Skip to content

docs(qa): approvals checklist — opening notifies approvers (approval.requested); the token-door item re-pointed after sys_approval_token left the data API (#22631, QA half) - #22656

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-22631-qa-checklist-approval-requested
Oct 10, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-22631-qa-checklist-approval-requested

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #22631
Clause-②: no

The non-governed half of #22631: docs/qa/platform-checklist/areas/approvals.json only. Two items move. The deep-link item stops saying that opening a request notifies nobody, because openNodeRequest publishes approval.requested to each concrete approver on the slate the request opens on since PR #22625 (e8c6666870). The token-door item is re-pointed after PR #22633 (86da194919, #22616) closed sys_approval_token to the automatic API. The Tier H half (ADR-0043 and the automation skill) is PR #22652. #22631 remains open until both PRs land.

Dispatched by the domain:skills seat PM, session session_01RdnZdPZH9ByduzPRWuH9tN. Claim 6095805515, triage 6095381775, token-door note 6095140961, remedy 6095111901 on #22616. docs/qa/** is domain:devx's surface, carried here under the cross-domain exception path the triage named.

Measured first

  • At origin/main 3d0eeefa the file read as the card quotes: notifies NOBODY 1 hit (:341), has no approval.opened member 1 hit (:394), approval.opened 3 hits (:341, :394, and the revision-4 history row at :400); control approval.reminder 3 hits.
  • packages/plugins/plugin-approvals/src/approval-service.ts: 'approval.requested' is published at :3349 inside openNodeRequest (:2999), one this.notify() per concrete approver on openedOn; issueActionTokens has one caller, :4815 inside remind().
  • sys-approval-token.object.ts :115–:116 declares apiEnabled: false and apiMethods: []. PR fix(plugin-approvals)!: sys_approval_token, the action-link tokens, is no longer exposed through the automatic API (#22616) #22633's changeset says every data-API verb answers 404 OBJECT_API_DISABLED to every caller, the administrator included, and that "there is no replacement read, by design: a link's state is what its confirm page answers, and a decision taken through a link is recorded in the request's action history". So the remedy in 6095111901 holds in full, and nothing in the token-door item needed less than it says.
  • The expiry leg: ACTION_TOKEN_TTL_MS = 72h (:815); ttlMs is an option of issueActionTokens only (:4857), which remind() calls without it; the service clock is opts.clock on the constructor (:1223, :1376), and approvals-plugin.ts constructs ApprovalService without one (:207 ff.); the plugin's source reads no OS_TEST_* and no process.env. Nothing drives expiry through a public door. The expired reason is pinned by approval-service.test.ts ("redeem: dead tokens — invalid, expired, decided request, reassigned slot": issueActionTokens(…, { ttlMs: 1 }) under the fake clock), and RESULT_COPY.expired in action-link-pages.ts is the page that reason renders.
  • The token-capture seam the item rests on survives: sys_notification still declares apiMethods: ['get', 'list'] (packages/platform-objects/src/audit/sys-notification.object.ts :187), so the raw links are still read from the persisted reminder payload.
  • The action history is GET /api/v1/approvals/requests/:id/actions (packages/rest/src/rest-server.ts :12762; rest-route-ledger.ts :444), which the item already used.

What changed (17 anchored edits; the JSON re-parsed; no key reordered)

approvals.notification-deep-link, revision 4 → 5

  • The knownGaps entry ("opening a request notifies NOBODY on this build — there is no approval.opened topic …") is removed. Six of the file's 18 items carry no knownGaps key, so the key goes rather than an empty array.
  • The source note on approval-service.ts#ApprovalService names approval.requested on open (PR feat(plugin-approvals): opening an approval step tells each resolved approver (approval.requested) #22625) beside the nine sibling topics, every member inheriting the deep link.
  • Step 2 and the fixture note no longer say "opening a request emits nothing": the remind/reassign path notifies on demand, and a freshly opened request is a second source. The fixtures stay on remind/reassign.
  • The revision-4 history row stands as history; a revision-5 row records the change (ref #22631).

approvals.email-action-token-door, revision 1 → 2

  • Step 2 (mint): the GET /api/v1/data/sys_approval_token read becomes the remind response's notified count and the new remind row on /:id/actions, with the instruction not to read the object through the data API and that its 404 OBJECT_API_DISABLED is the designed answer, not a finding.
  • Step 4 (prefetch safety): the "token row, consumed_at still null" re-read becomes the second GET rendering the confirm page rather than the "Already used" page, beside the /:id and /:id/actions re-reads.
  • Step 9 (expiry): no live door; cite the pinned expired reason and the RESULT_COPY.expired copy, and record the leg as blocked(fixture).
  • Acceptance 1 (mint, hashed at rest): oracle api → test, pointing at approval-token-internal-hash.integration.test.ts and sys-approval-token-generic-door.integration.test.ts; the live half keeps the sys_notification payload read.
  • Acceptance 2, 4 and 5: the token-row reads move to the act door's outcome pages and /:id/actions; the five-shape capture becomes four live shapes plus the pinned expired reason.
  • Negative 4: the hashed-at-rest FAIL is re-anchored on storage (the engine's system read in the internal-hash test), and reading the data API's 404 as a defect is named a FAIL of the method.
  • knownGaps[1] (expiry) is rewritten with the exact reason above; source gains the token object's closed declaration and the three test files; a revision-2 history row records all of it, including the two steps that had contradicted the door before PR fix(plugin-approvals)!: sys_approval_token, the action-link tokens, is no longer exposed through the automatic API (#22616) #22633 (the digest withheld since [security] The compliance ledger stores a JWT signing-key row's key material in its create snapshot, and an admin is served it through the ledger's by-id door while the key object itself declares no API door #21197; update never whitelisted).
  • Every clause keeps the file's oracle / verify / evidence shape, and every oracle value is in the gate's set.

Verification (all at 3f630d4dc)

  • PM-named: pnpm check:platform-checklist exit 1 (below); node scripts/check-doc-route-spelling.mjs --advisory exit 0; pnpm check:corpus-claim-drift exit 0.
  • check:platform-checklist is red on origin/main independently of this diff. Its 7 problems are 5 in areas/access-security.json (ABSENT SYMBOL: protocol.ts#anonymousFormIntakeOrgScopeRefusal, #anonymousFormIntakeReopenRefusal, #envWideRawViewRows) and 2 in areas/attachments-storage.json (ABSENT SYMBOL attachment-access-hooks.ts#canEdit; SYMBOL ANCHORS LOST, 27 against a floor of 28). The same 7 problems come back verbatim on a tree whose approvals.json is byte-identical to 3d0eeefa (the sibling worktree at af35cd5be), and no problem names approvals.json. Origin: b389e4355 (feat(metadata-protocol,runtime,service-automation,spec)!: the protocol refuses every organization-scoped write; an uninstall is environment-wide (ADR-0131 D6/D12) #22515) removed the two protocol.ts symbols, and ce3d0ad41 (fix(service-storage,plugin-audit,plugin-security)!: the attachment and comment parent gates judge a controlled_by_parent parent through its master #22513) reshaped canEdit. Both files are outside this card's surface: reported to the seat, not touched here. Until they are repaired, Lint & Repo Gates stays red on this PR for a reason this diff does not own.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; the change set is taken from the merge base) derived 13 commands. All 13 ran in the foreground, each exit captured before any pipe; --ran reconciliation: "13 derived, 13 run, 0 NOT-MEASURED, 0 UNRUN". 12 exit 0: check-ci-filter-parity, check-closing-keyword-parity (+ self-test), check-comment-mask-corpus, check:doc-formula-expressions (after its named prerequisite build through the verify lock, 4 of 4 tasks cached), check:cross-package-test-inputs, check:doc-authoring, check:driver-memory-census, check:gitlink-declared, check:nul-bytes, check:refd-timer-probe, check:watch-hint-literal. 1 exit 1: check:platform-checklist, above.
  • Not run locally: the repo-wide pnpm lint (no lintable source touched) and package suites (no package touched).

Changeset

Docs-only; docs/qa/** ships in no package's files[]. skip-changeset is applied through label-write.

Acceptance notes

  • check:platform-checklist red on main (above). Class (b): the checklist's symbol-anchor contract ("every path#symbol citation resolves; the per-file anchor count is shrink-never") is violated by two area files. Reach: pnpm check:platform-checklist on origin/main 3d0eeefa answers exit 1 with 7 problems. Carrier: the seat. Dedupe words: access-security.json ABSENT SYMBOL anonymousFormIntakeOrgScopeRefusal, attachments-storage.json canEdit anchor floor 28.
  • The source entries added for the three test files are bare paths (no #symbol half), so the item's resolvable-anchor count does not shrink; its six existing anchors are kept.

Generated by Claude Code

…requested); token-door item re-pointed after sys_approval_token left the data API

approvals.notification-deep-link (revision 5): the knownGaps entry that
said opening a request notifies nobody is removed, the source note names
approval.requested in the emitting topic set, step 2 and the fixture note
say the opening is a second notification source; the fixtures stay on
remind/reassign and the revision-4 row stands as history.

approvals.email-action-token-door (revision 2): sys_approval_token answers
404 OBJECT_API_DISABLED to every caller on every data-API verb, so the
token-row oracles move to the act door's outcome pages, the request's
action history and the two pinned integration tests; the mint clause is
scored by test. The expiry leg's only recipe was backdating expires_at
over the data API; nothing else drives expiry (ttlMs is not REST-reachable
and the plugin threads no clock), so it is recorded in knownGaps as
blocked(fixture) with the pinned 'expired' reason as its oracle.

Co-authored-by: Claude <[email protected]>
Claude-Session: https://claude.ai/code/session_01RdnZdPZH9ByduzPRWuH9tN
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 10, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 10, 2026 09:46
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 10, 2026 09:46
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 10, 2026
Merged via the queue into main with commit 65f4756 Oct 10, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22631-qa-checklist-approval-requested branch October 10, 2026 10:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants