Repository navigation
Commit 40bba68
docs(qa): approvals ledger — 19 checklist-accuracy edits from the 18.2.0 sweep (#26116)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi93z9uWCcha6ic7Xyx4VeoJa)_
Fixes #25838
Clause-②: no
Metadata-protocol: none
Applies the 19 `docs/qa/platform-checklist/areas/approvals.json` lines
of card #25838, the checklist-accuracy findings of the 18.2.0 release
sweep (wave anchor #25807), and respells the retired objectui pin flow
in the same file (#26052, #26035).
Only checklist text changes: one file, ten items. Each item takes one
`revision` bump and one `history` row, and each row names its run record
(QA run #25812, #25815 or #25817, all at framework `6679644a`) and this
card. No product code, test, pin, RUNNER.md or changeset changes.
`skip-changeset`: `docs/qa/**` ships in no package.
## How it was done
- Base `9753d163` (origin/main). `approvals.json` has no commit on main
since `73432299` (2026-10-21), so it is unchanged since the card was
filed and since the sweep's base `6679644a`. The card's "file unchanged
since filing" assumption holds.
- Every line was re-read against main before it was written: the pins
and their harnesses under `packages/`, the flows in
`examples/app-showcase`, and the console under `ui/` (the objectui paths
the items cite, re-read at their `ui/` location).
- **No line turned out to be a product defect.** All 19 are stale or
over-claiming text, so nothing goes to `out_of_scope_findings` as a
defect.
- A scripted span editor made every edit and never re-serialised the
file. It re-parses the result and asserts three things. The new document
equals the old one with the declared ops applied. Every other item and
top-level key is value-equal. The text outside the ten edited items is
byte-identical.
- The ten new history rows carry the placeholder ref `#PENDING-25838`.
The next commit on this branch swaps in this PR's number and changes
nothing else.
## Changes
| run | item (rev) | finding | change |
|:--|:--|:--|:--|
| #25812 | `account-app-entry` (4→5) | "drawer" in steps 6–7 and acc[1],
acc[3], acc[4]; two URL segments for acc[5] | steps[5]–[6],
acceptance[1], [3], [4]: the row click or `?request=` navigates to
`/apps/APP/sys_approval_request/record/ID` (objectui#12484;
`ui/apps/console/src/pages/system/ApprovalsInboxPage.tsx`
`requestPagePath`). acc[3]'s after-state is now "the decision actions
stop rendering": their `visible` predicates read
`record.viewer.can_act`, and no can_act=false explanation exists on the
request page. acc[5] names `com.objectstack.account` and the alias
`account` (`packages/apps/account/src/index.ts`). |
| #26052 | same item | retired pin flow | fixtures.requires[0],
steps[0], acceptance[0]: `pnpm console:build` from this tree replaces
`pnpm objectui:build` + `.objectui-sha`. With no dist stamp left, acc[0]
reads console:build's ready line at the run's framework SHA
(`packages/console/build-dist.sh`). The stale `check:console-sha` remark
is gone because that script no longer exists. acc[6]: AppHeader.tsx
under `ui/`, not "at the console pin". |
| #25815 | `decision-only-via-service` (2→3) | the ref is an engine
unit, and every oracle is the api | `automated` removed. knownGaps[0]
says what `resume-authority-gate.test.ts` asserts (AutomationEngine +
InMemorySuspendedRunStore, stand-in `gated_pause` node) and when
automated may return: an HTTP pin on the generic resume route plus the
approvals API. |
| #25815 | `email-action-token-door` (8→9) | a0, a1, a3 | a0: the
FORBIDDEN arm is pinned with `'stranger'` (not an approver at all), and
the at-rest read stays a run leg. a1: no pin asserts byte-identity of
the timeline or `consumed_at`. a3: no pin asserts that a failed decide
still burns the token, so it rests on source evidence only
(`redeemActionToken`'s order). automated.ref's "Full clauses" is
narrowed to acceptance[2] + negative[2], and knownGaps[0] is aligned. |
| #25815 | `request-page-reply-and-reader-visibility` (2→3) | a1, a2 |
a1.verify names the reply's HTTP legs: the row lands for the approver
and the submitter, and the member gets 403 FORBIDDEN
(`approvals-routes.ts` prefix map). The two service-layer pins are cited
as evidence of their own layer only. automated.ref's acc[2] note:
request-read-gate drives ObjectQL + `findData`/`getData` in-process,
never `/data` over HTTP. Also #26052: knownGaps[0]'s pin-skew block no
longer arises, because the console builds from this tree. |
| #25815 | `status-mirror-field` (3→4) | a2; a0/a1/a3/a4 | automated.ref
now scores negative[2] and acc[2] only, and states that acc[0], [1],
[3], [4] have no automation. a2.verify: the integration pin drives only
the dead-run sweep. For the SLA auto-decision half, the service-layer
pin `'SLA auto-decision: stays user-less — no human did it'` proves only
that the write is user-less. The live route is
approvals.sla-escalation's shim recipe. |
| #25817 | `approver-resolution-matrix` (6→7) | owner fallback does not
discriminate | steps[7]'s example is now a `{type:'user'}` fallback on a
non-administrator member. |
| #25817 | `decision-action-matrix` (8→9) | knownGaps[0]; N1 quote; A0
"exactly one" | knownGaps[0]: both remaining org-less candidates are
refuted. negative[1] quotes `'a reminder was already sent recently'`
(`approval-service.ts` remind). acc[0] carves out the maxRevisions
revise, which appends `revise` then `reject` (sendBack, audit-first). |
| #25817 | `per-group-signoff` (9→10) | A3; steps 3/4; N0 | steps[3]–[4]
use the request page and the "My Pending" tab. acc[3] reads the logged
`mark_approved` step (ExpenseSignoffFlow's approve edge is an
update_record). negative[0] names the unanimous invoice (admin×2) as the
stock contrast. |
| #25817 | `quorum-m-of-n` (6→7) | A2; unbinding | acc[2] reads the
`return_to_draft` step and the report's draft status.
fixtures.requires[1]: a fresh boot needs no unbinding. |
| #25817 | `inbox-metadata-actions` (5→6) | steps 1/2/5, A0; Record vs
Request cell | Same "drawer"/tab respelling, and the steps name the
Request cell (opens the request page) vs the Record cell (opens the
subject record, which also renders the bar). |
**Edits beyond the card's literal field list, made for same-item
consistency:** per-group-signoff acceptance[2] ("the drawer's group
chips"), and inbox-metadata-actions acceptance[1] plus
fixtures.requires[1]. They carry the same stale "drawer" or zh-only tab
wording as the fields the card names, and leaving them would make each
item contradict itself. Each history row says so.
## Acceptance notes
- **"drawer" persists in five items this card does not name:**
`quorum-m-of-n` (steps[8], acceptance[4]; #25817's A4 read the tally on
the record page as "0 of 2 · 3 eligible"),
`viewer-gating-submitter-side`, `notification-deep-link` (its title and
ten fields), `dynamic-approver-routing` steps[3], and
`inbox-keyboard-flow` (its title and thirteen fields). These are not
edited here. Carrier: wave anchor #25807.
- **`decision-action-matrix` acceptance[9].verify** still calls an
org-less row "the shape every schedule / time-relative / api-trigger run
mints". #25817 measured an api-hook approval on a tenancy-disabled
object stamping the default organization. Only knownGaps[0] was on the
card, so the parenthetical is left as it is. Carrier: #25807.
- **`per-group-signoff` steps[5]** still offers "via the documented
override path", which knownGaps[0] calls no longer acceptable. Not on
the card, and left as it is. Carrier: #25807.
- **`record-page-decisions` source[0]–[2]** cite objectui blobs "at the
console pin cf153eb". That is the provenance of a past read, not an
instruction to run the retired scripts, so it is left alone. Carrier:
#26035.
- History rows are append-only. Rows that quote the retired pin flow
stay as written.
## Gates (head `95fe453c`; the ref-swap commit changes only the ten
placeholder refs)
- `pnpm check:platform-checklist` → exit 0 (`check-platform-checklist:
OK — 16 areas, 329 items (324 active, 1 planned)`, both self-tests
pass).
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 13 families, and all 13 ran to exit
0. `check:doc-formula-expressions` first answered exit 3 (PREREQUISITE:
`@objectstack/formula` / `@objectstack/lint` unbuilt); it was re-run
after the prescribed `turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint` and answered exit 0. `--ran` gave `13
derived famil(ies) accounted for — 13 run, 0 NOT-MEASURED (a DERIVED
zero — all 13 recorded an exit code and none of them is 3)`.
- CI-only families (Test Core shards, the type-check lanes) are left to
this PR's checks.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01VwduP3gdrCAQyEyLmPtrUN)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 3a3751b commit 40bba68
1 file changed
Lines changed: 102 additions & 60 deletions
0 commit comments