Skip to content

Commit 7343229

Browse files
docs(qa): 18.0 sweep anchor round 18 — approvals.record-page-decisions re-derived against the shipped record page, the packaged-automation board's Activation cell and 22 own-port boot respellings (#25018)
Refs #24579 Clause-②: no Round 18 of the 18.0 sweep's wave anchor (claim `6304655523`, PM loop round 33). It applies round 17's three carried checklist-text findings, PR #25003's out-of-scope findings as its ACCEPT `6304389370` and landing record `6304641541` carry them: `approvals.record-page-decisions` re-derived against the record page objectui ships, `automation.setup-packaged-automation-board` `steps[3]`, and 22 other texts that named the own-port boot or its DB "isolated". Only checklist text changes. 19 items in 6 area files each take one `revision` bump and one `history` row, and each row names PR #25003 and its finding. There is no product code, test, pin, changeset text or `RUNNER.md` change, no new item, and every item id is kept. - Base `93e6c1a5f8`. `origin/main` `62bc129308` is merged in `1fa304d42f`. Its one commit touches `packages/cli` only: no `docs/qa/**` path and none of the sources these edits cite. - Every item was read at the base, and every objectui source at the console pin `cf153eb2e535` (`.objectui-sha` on `main`), with `git show` from a full scratch clone (`--is-shallow-repository` false). objectui `99782f961` (objectui#3055) is an ancestor of the pin: exit 0. Control, the pin as an ancestor of `99782f961`: exit 1. - A scripted span editor made every edit. It is round 17's editor, unchanged (same sha256), and it never re-serialises a file. For each area file it re-parses the result and asserts four things, and all six files passed: - A1: the new document equals the old one with the declared ops applied. - A2: every other item and top-level key is value-equal. - A3: each edited item's changed paths lie inside its declared fields (array edits by exact index), plus `revision` and the one new history row. - A4: the text outside the edited items is byte-identical. - A whole-branch check against the base agrees: 19 items changed, each with revision +1 and exactly one appended row, and no other file or path changed. - The new history rows carry the placeholder ref `#PENDING-24579R18`, because a row's `ref` names this PR and the PR had no number yet. The next commit on this branch swaps in this PR's number and changes nothing else. The gates are re-run on that head, and the report comment on #24579 records them. - The live verdicts at the pin are NOT MEASURED. The objectui and server sources were read, not run. ## 1. `approvals.record-page-decisions` (revision 3 → 4) At `cf153eb2e535` the record page draws no header Approve/Reject. `RecordDetailView.tsx` (blob `64a7c675e50d`) mounts `DeclaredActionsBar` (blob `f27b4ab0efff`) above the page body whenever the record has a pending request. The dispatch record is the pending `sys_approval_request` row, the location is `record_section`, and `approval_remind` is excluded (`recordApprovalActions.ts`, blob `f6aabe078ea0`). The bar reads `sys_approval_request`'s declared actions and evaluates each one's `visible` predicate, failing closed, against the row's `viewer` block. The server computes that block per caller (`ApprovalService.attachViewers`: `can_act`, `is_submitter`, `can_override`). `useRecordApprovals.ts` (blob `b65a91f4a9a4`) only reads, and holds no `canDecide`. The steps, numbered from 1 as the item's own text numbers them: | step | old | new | source read | |:--|:--|:--|:--| | 1 (`steps[0]`) | boot on its own port and DB; sign in as the dev admin | unchanged | — | | 2 (`steps[1]`) | PATCH budget 200000; the flow parks at manager_review, lockRecord:false | the same, plus: the admin is the request's submitter as well as its approver | `approval-node.ts` passes the run's trigger user as `submitterId`; `seed-approval-demo.ts` `ADMIN_APPROVAL_POSITIONS` | | 3 (`steps[2]`) | screenshot the header: Approve and Reject in the primary slot (`buildApprovalDecisionActions`, order -100 / -99, `record_header`) | screenshot the decision bar above the page body. For the admin: Approve (filled, declared `primary`), Reject (destructive, `danger`), Reassign, Send back, Request info, Recall and Reply, in declaration order, each with a `declared-action-` testid. Send reminder is excluded and Resubmit is hidden. Capture the approvals read and its `viewer` block | `RecordDetailView.tsx`, `DeclaredActionsBar.tsx`, `recordApprovalActions.ts`; `sys-approval-request.object.ts` (the declarations and their `visible`) | | 4 (`steps[3]`) | inline edit stays live under manager_review | the same, plus: the row reads `lock_record: false` | `approval-service.ts` (`lock_record` from the node's `lockRecord`); `useRecordApprovals.ts` `recordLockedByApproval` | | 5 (`steps[4]`) | click Reject: ONE dialog, titled by the Reject label, with the confirm question as its description | the same, named from the declaration: title Reject, description "Reject this request? A rejection is final for every approver.", Comment and Attachments inline, no confirm in front of it | `useConsoleActionRuntime.tsx` (title from `label`, description from `description`); `approval_reject` declares a `description` and no `confirmText` | | 6 (`steps[5]`) | Confirm; capture the POST to reject; re-read status=rejected | the same, plus: the POST's `:id` is the request's id; the comment is in the body and on the `GET …/:id/actions` row; the bar draws nothing once no request is pending | `approvals-routes.ts` (the decide and actions routes); `DeclaredActionsBar.tsx` (the row rides `params._rowRecord`) | | 7 (`steps[6]`) | approve path: Approve, one dialog, POST to approve, status approved | the same, with the declared title and description; at or under 500000 the run ends approved | `approval_approve` in `sys-approval-request.object.ts`; the flow's `needs_exec` edges | | 8 (`steps[7]`) | drive a budget above 500000 to exec_review; inline edit hidden | PATCH above 500000 and approve manager_review first: `needs_exec` routes to exec_review (position `exec`, held by the admin), a new request with `lock_record: true`; inline edit hidden, and the approval band shows its locked state | `BudgetApprovalFlow` (edge e4, `budget > 500000`); `resolveApprovalLock` (objectui `packages/react` `InlineEditContext.tsx`) | | 9 (`steps[8]`) | a non-approver member: the header offers no Approve/Reject (`approvals.canDecide` false); not the dev admin | the member's own approvals read serves no row for the record, so the page draws no decision action; a forged POST to approve answers 403 `FORBIDDEN`, and the request stays pending; not the dev admin, who holds the override | `request-read-gate.ts` header (participants and the override actor; the record-reader tier is off, and no showcase object declares `approvalsVisibleToReaders`); `ApprovalService.decide`; `approvals-routes.ts` (`FORBIDDEN` → 403) | | 10 (`steps[9]`, new) | — (step 9 named the override pair only as the reason not to use the admin) | the off-slate admin: reassign the step away and re-read `viewer` (`can_act` false, `can_override` true). The bar shows Override Approve / Override Reject / Override Reassign (amber, `data-override-decision`), no Send back or Request info, and a plain Recall and Reply | `DeclaredActionsBar.tsx` and `approvalOverride.ts` (`isOverrideDecision`); the declarations' `can_override` arms | The clauses: | clause | old | new | source read | |:--|:--|:--|:--| | `acceptance[0]` (screenshot) | Approve and Reject in the record header's primary slot | the pending approver is offered the declared decisions in the bar above the page body, not in the header. Each one is kept or dropped by its `visible` over the row's `viewer` block | `RecordDetailView.tsx` (its header-actions comment: no approval action is injected there); `DeclaredActionsBar.tsx` | | `acceptance[1]` (network) | Reject after ONE dialog, comment inline | the same clause, named from the declaration: the confirm question rides `description` and there is no `confirmText`, so the runtime chains no confirm | `useConsoleActionRuntime.tsx`; `sys-approval-request.object.ts` | | `acceptance[2]` (api) | the decision POSTs and re-reads; the header collected the comment (`actionParams`) | the POST goes to the REQUEST's id; the comment is the declared `comment` param and round-trips onto the `/actions` row | `approvals-routes.ts`; `DeclaredActionsBar.tsx` | | `acceptance[3]` (screenshot) | a locking approval hides inline edit (the clause is unchanged) | verify and evidence add each row's `lock_record` and the band's locked state | `approval-service.ts`; `RecordDetailView.tsx` (`resolveApprovalLock` gates `canEdit`) | | `acceptance[4]` (dom → api) | the header DOM of a non-approver has no approve_request/reject_request while `approvals.canDecide` is false | gated by the SERVER's viewer block: the member's approvals read serves no row, no `declared-action-*` button renders, and a forged POST answers 403 `FORBIDDEN`. The off-slate admin's Override framing is recorded separately | `ApprovalService.attachViewers` and `decide`; `request-read-gate.ts`; `DeclaredActionsBar.tsx` | The item's other fields: - `title`: "Approve/Reject render in the record header; …" → "Approve/Reject render on the record page from sys_approval_request's declared actions; …". The intent (deciding an approval from the record page), the id and the priority (P2) are kept. - `fixtures.requires[0]` now says that exec_review is reached only after manager_review approves, and that the admin holds `exec` and submits each request. `fixtures.requires[1]`: `buildApprovalDecisionActions` → `DeclaredActionsBar` over the pending row, `recordApprovalActions.ts`, and the server's declarations. - `fixtures.knownGaps[0]`, round 17's note about the retired text, is removed. It was the only entry, so `fixtures.knownGaps` goes. - `negative[2]`: "Approve/Reject rendered on the header for a viewer who is not a pending approver (canDecide false)" → a declared decision action rendered for a viewer whose `viewer` block admits none. - `source[0]` and `source[1]` now cite `RecordDetailView.tsx` and `DeclaredActionsBar.tsx` (with `useRecordApprovals.ts` and `useConsoleActionRuntime.tsx`) at the pin. `source[4]` (the declarations and `attachViewers`) and `source[5]` (the read gate and `decide`) are added. `source[2]` (the two pins) and `source[3]` (the flow) are unchanged. - No `automated` is added. The item had no `automated` field at the base: round 17 re-pointed `source[2]`. Neither pin holds a clause whole at its layer. objectui's `RecordDetailView.approvalDeclaredActions.test.tsx` (blob `9519ae70f743`) mounts the page in jsdom over a fetch double, with a hand-written `sys_approval_request` definition and the param dialog mocked. `sys-approval-request.object.test.ts` reads the declarations. ## 2. `automation.setup-packaged-automation-board` `steps[3]` (revision 6 → 7) Old: "confirm every switch reads On except a flow authored status: 'obsolete' (showcase_approver_bindings, …), whose switch correctly reads Off — 31 On / 1 Off at QA run #24545's subject". New: the Activation cell is read as two facts. The switch shows the activation flag. The badge beside it (`data-testid` `packaged-flow-activation`, with a `data-reading` attribute) reads 'On' for an enabled flow whose `_status` row carries no `unboundCause`, 'Not running here' with the switch still checked for one that carries one, and 'Off' with the switch unchecked for a disabled flow. On a default boot `showcase_scheduled_digest` and `showcase_task_due_reminder` carry `deployment_policy`. Sources at the pin: `packagedFlows.ts` `activationReading` (blob `a85c96d28e5a`) and `PackagedAutomationPage.tsx` (blob `f4aa90e11d32`). At the base: `describeUnboundReason` (service-automation `engine.ts`), `FLOW_UNBOUND_CAUSES` (spec `automation-service.ts`) and RUNNER.md's scheduled-work entry. The count is stated with its derivation from the 32 flows `allFlows` declares at `93e6c1a5f8` (`examples/app-showcase/src/automation/flows/index.ts`), each classified by its authored `status` and its start trigger: | flows | count | reads on a default boot | |:--|--:|:--| | record-triggered (18) and `api`-triggered (1), all of which bind | 19 | On | | no trigger: screen flows and invoked subflows | 10 | On | | `type: 'schedule'`: `showcase_scheduled_digest` (interval), `showcase_task_due_reminder` (time_relative) | 2 | Not running here | | `status: 'obsolete'`: `showcase_approver_bindings` | 1 | Off | That is 29 On / 2 Not running here / 1 Off, and 31 On / 1 Off with `OS_AUTOMATION_SCHEDULED_WORK_ENABLED` exported. The 19 that bind agree with RUNNER.md's measured banner, `32 flow(s) 19 bound` on a default boot and `21 bound` with the switch. Only `obsolete` and `invalid` disable a flow (`flowStatusDisabled` in the engine), so the 10 flows with no authored `status` (`draft`) are enabled. The step tells the run to reconcile against `_status` rather than these digits. ## 3. The 22 other "isolated" texts **22 respelled, in 21 strings across 17 items; 43 left.** At the base, 65 non-history occurrences of "isolated" remained after round 17. Each one was read in its item: - 22 name the own-port boot or its DB. They are respelled. - 41 name the `isolated` tenancy posture (`OS_TENANCY_POSTURE=isolated`, "group/isolated", walled boots). They are untouched. One of them sits in an edited item: `access-security.platform-owner-email-anchor` `steps[0]`, "a walled posture (group / isolated)". - 2 are other senses and are untouched: "Saved reports are owner-isolated" (`dashboards.saved-report-ownership`, its title) and "unroutable from the isolated env" (`automation.flow-error-handling`). At the head the scan reads 43 occurrences, which are those 41 and 2. | item | field | old | new | |:--|:--|:--|:--| | `automation.connector-dispatch-matrix` | `fixtures.requires[2]` | "so an isolated boot without it dials 3000" | "so a boot on its own port without it dials 3000" | | `automation.connector-dispatch-matrix` | `fixtures.knownGaps[1]` | "so a dogfood-§0 isolated boot on a non-3000 port" | "so a dogfood-§0 boot on its own non-3000 port" | | `automation.packaged-flow-subflow-disable-refusal` | `steps[6]` | "(or discard the isolated DB)" | "(or discard the run's own DB)" | | `automation.packaged-flow-clone-contract` | `steps[8]` | "(or discard the isolated DB)" | "(or discard the run's own DB)" | | `access-security.platform-owner-email-anchor` | `fixtures.knownGaps[0]` | "its own isolated scratch database" | "its own scratch database" | | `platform-core.boot-health` | `preconditions[0]` | "isolated run per dogfood-verification §0:" | "the run's own boot per dogfood-verification §0:" | | `platform-core.seed-integrity` | `fixtures.requires[0]` | "a FRESH isolated boot (new file DB)" | "a FRESH boot on its own port and a new file DB" | | `platform-core.interrupted-migration-boot-report` | `fixtures.requires[1]` | "an isolated persistent sqlite DB (own port," | "the run's own persistent sqlite DB (own port," | | `platform-core.manifest-install-contract` | `steps[9]` | "or simply discard the isolated file DB" | "or simply discard the run's own file DB" | | `platform-core.manifest-install-contract` | `steps[9]` | "the cheaper path an isolated boot makes free" | "the cheaper path a boot on its own port and DB makes free" | | `platform-core.activation-ledger-registration-home` | `fixtures.requires[0]` | "need nothing beyond an isolated stock boot" | "need nothing beyond a stock boot on its own port and DB" | | `platform-core.activation-ledger-registration-home` | `steps[1]` | "(stock `objectstack dev`, isolated port/DB)" | "(stock `objectstack dev`, on its own port and DB)" | | `platform-core.activation-ledger-row-contract` | `fixtures.requires[0]` | "an isolated boot on a file DB (-d" | "a boot on its own port and file DB (-d" | | `platform-core.activation-ledger-row-contract` | `steps[0]` | "boot isolated on the file DB;" | "boot on its own port with that file DB;" | | `platform-core.marketplace-install-local-lifecycle` | `fixtures.requires[0]` | "TWO isolated boots of the same app:" | "TWO boots of the same app, each on its own port and DB:" | | `platform-core.runtime-config-boot-read` | `fixtures.requires[0]` | "any stock isolated boot —" | "any stock boot on its own port and DB —" | | `platform-core.runtime-config-boot-read` | `steps[0]` | "on a stock isolated boot, with NO session" | "on a stock boot on its own port and DB, with NO session" | | `platform-core.lifecycle-retention-sweep` | `fixtures.requires[0]` | "an isolated boot on a file DB (-d" | "a boot on its own port and file DB (-d" | | `platform-core.seed-mode-matrix` | `steps[7]` | "or discard the isolated DB" | "or discard the run's own DB" | | `records-forms.field-group-visible-when` | `fixtures.requires[0]` | "and an isolated boot — the item" | "and a boot on its own port and DB — the item" | | `records-forms.field-unique-enforcement` | `fixtures.requires[0]` | "an isolated dev boot on the stock SQLite store" | "a dev boot on its own port and DB, on the stock SQLite store" | | `studio-authoring.metadata-diagnostics-sweep` | `fixtures.knownGaps[0]` | "ON AN ISOLATED SCRATCH FILE DB" | "ON THE RUN'S OWN SCRATCH FILE DB" | None of the 22 named RUNNER's canonical boot line, so each takes the "own port and DB" family of spellings, with "the run's own DB" where the text is about a teardown or a database. None of the 22 sits in a clause, and no posture text is touched. ## Acceptance notes - Some edits sit beyond the dispatch's field list, inside the item it names, to keep that item consistent with its own re-derivation: `title` (it said "render in the record header"), `fixtures.requires[0]` (exec_review is reached only after manager_review approves), `negative[2]` (it named `canDecide`), the new `steps[9]`, and `source[4]`–`[5]`. - The dispatch says to keep `automated.ref` only for clauses a pin asserts whole at their layer. The item carries no `automated` at the base, because round 17 re-pointed `source[2]`, and neither pin holds a clause whole at its layer. So none is added. - `acceptance[4]`'s oracle moves from `dom` to `api`. The clause is the server's gate, and RUNNER.md rule 4 makes the server the authority. The UI absence is still read, after a screenshot. - On a stock showcase the closed-side member reads no request at all. The approvals door serves requests to participants and to the override actor, and the record-reader tier is off. So the member's page draws no bar, rather than a bar with every button hidden, and the forged POST is the server-side proof. - The off-slate admin also keeps a plain Recall and Reply, because the admin submitted the request. `isOverrideDecision` leaves a lever the viewer holds through its submitter arm unframed (objectui#13225). ## Gates The derived list is `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`. It is computed on the actual change set: 6 paths against merge base `62bc12930`, at head `1fa304d42f`. It gives 13 commands. Each one ran at that head with its exit code captured before any pipe, and each exited 0: `node scripts/check-ci-filter-parity.mjs` · `node scripts/check-closing-keyword-parity.mjs` · `node scripts/check-closing-keyword-parity.mjs --self-test` · `node scripts/check-comment-mask-corpus.mjs` · `pnpm --filter @objectstack/lint run check:doc-formula-expressions` · `pnpm check:cross-package-test-inputs` · `pnpm check:doc-authoring` · `pnpm check:driver-memory-census` · `pnpm check:gitlink-declared` · `pnpm check:nul-bytes` · `pnpm check:platform-checklist` · `pnpm check:refd-timer-probe` · `pnpm check:watch-hint-literal` The same 13 were run before the merge, at `46f612b0a1`. There, the first run of `check:doc-formula-expressions` refused with exit 3, a prerequisite: `@objectstack/formula` and `@objectstack/lint` were not built. Both were built under the shared verify lock (`VERDICT command-exit 0`, held 11 s, waited 0 s), and the rerun exited 0. The `--ran` reconciliation reads "13 derived, 13 run, 0 NOT-MEASURED, 0 UNRUN" at both heads. No per-PR CI job runs `pnpm check:platform-checklist`, so this local reading is its evidence: | reading | base `93e6c1a5f8` | head `1fa304d42f` | |:--|:--|:--| | items | 16 areas, 328 items (323 active, 1 planned) | 16 areas, 328 items (323 active, 1 planned) | | line citations | 0 survive across 21 swept documents | 0 survive across 21 swept documents | | symbol anchors | 975/985 against 433 cited sources, 10 on the named residual (#16898), 18 file floors held | 979/989 against 434 cited sources, 10 on the named residual (#16898), 18 file floors held | The four new anchors all resolve: `seed-approval-demo.ts#ADMIN_APPROVAL_POSITIONS`, `sys-approval-request.object.ts#approval_reject`, `approval-service.ts#attachViewers` and `approval-service.ts#decide`. No line citation is added. `git merge-tree --write-tree origin/main HEAD` exited 0 against `62bc129308`. No open PR touches `docs/qa/platform-checklist/`: all 16 open PRs' file lists were read over REST, and the three with more than 100 files were read page by page, at 349, 367 and 235 files, each equal to its `changed_files`. So there was no second merge-tree to run. There is no ablation, because no tested behaviour changed. `skip-changeset` was re-measured: no `files[]` entry of the 73 non-private manifests (86 tracked `package.json` files) reaches outside its own package or names `docs/qa`, and the repo root is private. --- _Generated by [Claude Code](https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 094a39c commit 7343229

6 files changed

Lines changed: 171 additions & 77 deletions

File tree

‎docs/qa/platform-checklist/areas/access-security.json‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2919,7 +2919,7 @@
29192919
"title": "OS_PLATFORM_OWNER_EMAIL platform-admin anchor: only a VERIFIED stored-email match confers PLATFORM_ADMIN — unset, unverified match, non-match and malformed lists each confer nothing, fail closed",
29202920
"since": "v17",
29212921
"status": "active",
2922-
"revision": 6,
2922+
"revision": 7,
29232923
"priority": "P1",
29242924
"surface": "api",
29252925
"personas": [
@@ -2933,7 +2933,7 @@
29332933
"a manage_platform_settings-floor probe route: GET /api/v1/datasources answers 401/403 BEFORE any service resolution (packages/services/service-datasource/src/admin-routes.ts; DATASOURCE_ADMIN_CAPABILITY), and admin_full_access carries that capability (packages/spec/src/identity/eval-user.zod.ts) — so the probe discriminates platform-admin standing cleanly"
29342934
],
29352935
"knownGaps": [
2936-
"the ENTITLED live leg needs a VERIFIED second account, and no stock HTTP path verifies one: email_verified is statically readonly at the user-context API (stripped — the write-path-guards class), a fresh sign-up reads unverified, and the seeded dev admin — whose address IS stamped verified at seed time (#11343, auth-plugin.ts) — also holds the unscoped admin_full_access grant under single (the posture this item boots), so its 200s can never attribute to the config anchor. Verify M's address via a verification-enabled boot with mail capture (identity-auth.email-verification-loop's fixture, itself a knownGap there) or a system-context stamp through the same isSystem doorway the seed itself uses; without either, clause 5's live leg scores blocked(fixture) and the entitled side rests on the unit pin. Measured on main c91af8ed6: POST /api/v1/auth/admin/update-user {userId, data: {emailVerified: true}} answers the seeded dev admin 403 YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS, so the HTTP lane has no verifying door; the run stamped M's email_verified directly on its own isolated scratch database with the server stopped (a system-level write, never on a shared database) and records that it did"
2936+
"the ENTITLED live leg needs a VERIFIED second account, and no stock HTTP path verifies one: email_verified is statically readonly at the user-context API (stripped — the write-path-guards class), a fresh sign-up reads unverified, and the seeded dev admin — whose address IS stamped verified at seed time (#11343, auth-plugin.ts) — also holds the unscoped admin_full_access grant under single (the posture this item boots), so its 200s can never attribute to the config anchor. Verify M's address via a verification-enabled boot with mail capture (identity-auth.email-verification-loop's fixture, itself a knownGap there) or a system-context stamp through the same isSystem doorway the seed itself uses; without either, clause 5's live leg scores blocked(fixture) and the entitled side rests on the unit pin. Measured on main c91af8ed6: POST /api/v1/auth/admin/update-user {userId, data: {emailVerified: true}} answers the seeded dev admin 403 YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS, so the HTTP lane has no verifying door; the run stamped M's email_verified directly on its own scratch database with the server stopped (a system-level write, never on a shared database) and records that it did"
29372937
]
29382938
},
29392939
"steps": [
@@ -3052,6 +3052,12 @@
30523052
"date": "2026-10-20",
30533053
"change": "source[5] and source[7] re-pointed — the two comma-range line citations round 9 of wave anchor #24579 left, because PR #24667 held the item then (round 9's report 6283532816, out_of_scope_findings[0]). At the authoring commit 45397edf53, line 200 of env.ts was the resolvePlatformOwnerEmail declaration and line 261 of admin-routes.ts was the DATASOURCE_ADMIN_CAPABILITY declaration. At base 795ff708d8 source[5] anchors both PLATFORM_OWNER_EMAIL_ENV and resolvePlatformOwnerEmail in env.ts, and source[7] anchors the DATASOURCE_ADMIN_CAPABILITY declaration in admin-routes.ts instead of the string literal manage_platform_settings. All three resolve as declarations in the shared resolver. The ',NNN' residue is what #13786's strip left of a two-range list. No clause, step or other field changed",
30543054
"ref": "#24788"
3055+
},
3056+
{
3057+
"revision": 7,
3058+
"date": "2026-10-21",
3059+
"change": "Respelled — fixtures.knownGaps[0]: 'its own isolated scratch database' → 'its own scratch database'. PR #25003's out-of-scope finding (round 17 of wave anchor #24579; ACCEPT 6304389370, landing record 6304641541): 22 non-history texts in 17 items named the own-port boot or its DB with 'isolated' in other words, the phrasing round 12 found a runner reading as the `isolated` tenancy posture (OS_TENANCY_POSTURE=isolated). The new spelling names the boot or the DB by what it is, the run's own port and DB (RUNNER.md's trap-table counter: own port/DB, skill §0), and names no posture. Read at base 93e6c1a5f8. steps[0]'s 'a walled posture (group / isolated)' names the posture and is left as written. No clause changed",
3060+
"ref": "#25018"
30553061
}
30563062
]
30573063
},

0 commit comments

Comments
 (0)