Skip to content

Commit fe81662

Browse files
committed
docs(qa): re-word three v18 checklist clauses the 18.0 QA sweep found mis-asserted
platform-core.v18-boot-refusals acceptance[2]: the refused boot wrote no row and ran no D10 step, read as a row-level SQLite diff that includes the -wal, not a file checksum (the additive schema sync before the guard is expected). steps[0] stops passing -d to os serve. access-security.security-catalog-one-holder-and-boot-report acceptance[1] and title: an inline-manifest install-local answers 422 PLUGIN_REGISTER_FAILED whose message names each conflicting name and its holder, and nothing is installed; no conflicts[] on that door. approvals.act-door-and-admin-rescue-slate acceptance[1] and title: a PUT neither wire door claims is the door's ordinary 404 miss with no Allow header; 405 + Allow: GET, POST is the in-process member's answer only. Each item: revision 1 -> 2, one history entry naming its QA source. Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp Co-authored-by: Claude <noreply@anthropic.com>
1 parent de43524 commit fe81662

3 files changed

Lines changed: 33 additions & 15 deletions

File tree

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3501,10 +3501,10 @@
35013501
},
35023502
{
35033503
"id": "access-security.security-catalog-one-holder-and-boot-report",
3504-
"title": "The security catalog has exactly one holder per name: two packages declaring one permission-set / position name refuse boot (422 NAMESPACE_CONFLICT on install-local), a body packageId that disagrees with its package is refused, `os migrate security-catalog-overlays` clears environment shadows, and boot reports grants naming undefined catalog entries",
3504+
"title": "The security catalog has exactly one holder per name: two packages declaring one permission-set / position name refuse boot (on install-local, 422 PLUGIN_REGISTER_FAILED naming each name and its holder), a body packageId that disagrees with its package is refused, `os migrate security-catalog-overlays` clears environment shadows, and boot reports grants naming undefined catalog entries",
35053505
"since": "v18",
35063506
"status": "active",
3507-
"revision": 1,
3507+
"revision": 2,
35083508
"priority": "P1",
35093509
"surface": "mixed",
35103510
"personas": [
@@ -3532,10 +3532,10 @@
35323532
"evidence": "the vitest summary line per pin file, with the package name and the subject sha"
35333533
},
35343534
{
3535-
"clause": "install-local of a package redeclaring a catalog name → 422 NAMESPACE_CONFLICT (or PLUGIN_REGISTER_FAILED) with conflicts[], and nothing is recorded as installed",
3535+
"clause": "install-local of an inline manifest (a `{ manifest }` body) whose package redeclares a catalog name → 422 PLUGIN_REGISTER_FAILED whose message names each conflicting name and its holder, and nothing is recorded as installed",
35363536
"oracle": "api",
3537-
"verify": "status + code + conflicts; package list unchanged",
3538-
"evidence": "refusal body + the list"
3537+
"verify": "status + error.code + error.message: every redeclared permission-set / position name appears in the message with the package or environment that already holds it; GET /api/v1/packages lists the same packages before and after. ⛔ No `conflicts[]` is expected on this door: it answers its own registered PLUGIN_REGISTER_FAILED and interpolates only the register error's message (that route's entry in packages/spec/src/api/error-code-ledger.zod.ts); the structured `conflicts` ride the thrown SecurityCatalogNameConflictError, which the boot-refusal pins in automated.ref assert",
3538+
"evidence": "the refusal body + the package list before and after"
35393539
},
35403540
{
35413541
"clause": "a permission-set save whose body packageId disagrees with the target package → 400 VALIDATION_ERROR at packageId",
@@ -3576,6 +3576,12 @@
35763576
"date": "2026-10-16",
35773577
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it",
35783578
"ref": "#23879"
3579+
},
3580+
{
3581+
"revision": 2,
3582+
"date": "2026-10-19",
3583+
"change": "acceptance[1] (clause, verify, evidence) and the title's install-local parenthetical re-pointed — an assertion defect found by the 18.0 QA sweep (QA run #24558, access-security.security-catalog-one-holder-and-boot-report acceptance[1]: FAIL, confirmed by an independent RUNNER rule 7 re-derivation; card #24567 row 2, folded from #24568). The product is right and the clause was wrong: on an inline manifest the install-local door (packages/cloud-connection/src/marketplace-install-local-plugin.ts) answers its OWN registered 422 PLUGIN_REGISTER_FAILED and interpolates only the register error's message — that route's documented contract in packages/spec/src/api/error-code-ledger.zod.ts — so it never carried NAMESPACE_CONFLICT or a `conflicts[]`, and no pin in automated.ref asserts either at this door. The run measured the refusal twice: the names and their holders inside the message, nothing installed (27 packages before and after). The clause now asserts that; a structured `conflicts[]` on this door would be a separate enhancement decision, not taken here. The other four clauses are unchanged",
3584+
"ref": "#24567"
35793585
}
35803586
]
35813587
},

‎docs/qa/platform-checklist/areas/approvals.json‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,10 +1556,10 @@
15561556
},
15571557
{
15581558
"id": "approvals.act-door-and-admin-rescue-slate",
1559-
"title": "Approvals on v18: the email action door /approvals/act works through the dispatcher exactly as the self-hosted mount (GET renders, POST redeems, PUT 405 Allow: GET, POST, a dead link a 200 page, a policy-held session not 403'd before the token), and an approval over an unstaffed position opens on the org administrators as ordinary approvers",
1559+
"title": "Approvals on v18: the email action door /approvals/act works through the dispatcher exactly as the self-hosted mount (GET renders, POST redeems, a PUT neither door claims an ordinary 404 miss with no Allow header, a dead link a 200 page, a policy-held session not 403'd before the token), and an approval over an unstaffed position opens on the org administrators as ordinary approvers",
15601560
"since": "v18",
15611561
"status": "active",
1562-
"revision": 1,
1562+
"revision": 2,
15631563
"priority": "P1",
15641564
"surface": "api",
15651565
"personas": [
@@ -1586,10 +1586,10 @@
15861586
"evidence": "the vitest summary line per pin file, with the package name and the subject sha"
15871587
},
15881588
{
1589-
"clause": "GET renders the action page, POST redeems, PUT → 405 with Allow: GET, POST, a dead token → a 200 page (not an error envelope)",
1589+
"clause": "GET renders the action page, POST redeems, a dead token → a 200 page (not an error envelope), and PUT — a method neither wire door claims — is that door's ordinary miss: 404 with no Allow header, never reaching the member (ENDPOINT_NOT_FOUND on the self-hosted `os dev` boot, whose raw-app mount registers only GET and POST; ROUTE_NOT_FOUND on the hosted dispatcher door, whose `/approvals/act` domain claims only GET, HEAD and POST). 405 with Allow: GET, POST is the in-process ApprovalService.handleActionPage member's answer only, which no wire request reaches with a PUT",
15901590
"oracle": "api",
1591-
"verify": "status + headers per verb",
1592-
"evidence": "four lines"
1591+
"verify": "status + headers per verb; for the PUT also error.code and the absence of an Allow header. The member's 405 is not a wire leg: it is pinned in-process by packages/plugins/plugin-approvals/src/action-page-member.integration.test.ts, and the dispatcher's 404 miss by packages/runtime/src/domains/approvals-act.test.ts (both in automated.ref)",
1592+
"evidence": "four lines (the PUT's with its error.code and its headers)"
15931593
},
15941594
{
15951595
"clause": "the unstaffed request appears in each org admin's pending list and the decision is recorded via_override:false",
@@ -1614,6 +1614,12 @@
16141614
"date": "2026-10-16",
16151615
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it; approvals.approver-resolution-matrix still says admin_rescue is decidable only via the privileged override — stale against 22824 (reported, not edited here)",
16161616
"ref": "#23879"
1617+
},
1618+
{
1619+
"revision": 2,
1620+
"date": "2026-10-19",
1621+
"change": "acceptance[1] (clause, verify, evidence) and the title's PUT leg re-worded — an assertion defect found by the 18.0 QA sweep (QA run #24542, approvals.act-door-and-admin-rescue-slate clause 1: FAIL; card #24543). The product is right and the clause was wrong: on a stock `os dev` showcase boot `PUT /api/v1/approvals/act?token=…` answered 404 ENDPOINT_NOT_FOUND with no Allow header (twice; DELETE and PATCH the same), which is the design — the self-hosted mount registers only GET and POST (changeset 22578), the dispatcher domain claims only GET, HEAD and POST and lets every other method fall through (packages/runtime/src/domains/approvals.ts), and the item's own pin packages/runtime/src/domains/approvals-act.test.ts asserts that miss (404 ROUTE_NOT_FOUND) with the member never run. 405 with Allow: GET, POST belongs to the in-process member ApprovalService.handleActionPage alone (packages/plugins/plugin-approvals/src/action-page-member.integration.test.ts). The GET, POST and dead-token legs held and are unchanged, as are the other two clauses",
1622+
"ref": "#24543"
16171623
}
16181624
]
16191625
}

‎docs/qa/platform-checklist/areas/platform-core.json‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2977,7 +2977,7 @@
29772977
"title": "A v18 server refuses to boot rather than serve wrongly: against a 17.x-shaped database (a physical organization_id on a non-tenant table, no verified ADR-0131 ceremony marker) it stops before listening with ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED naming the tables and the command, and with security on but no identity objects it stops with AuthzIdentityObjectsMissingError",
29782978
"since": "v18",
29792979
"status": "active",
2980-
"revision": 1,
2980+
"revision": 2,
29812981
"priority": "P0",
29822982
"surface": "cli",
29832983
"personas": [
@@ -2991,7 +2991,7 @@
29912991
]
29922992
},
29932993
"steps": [
2994-
"checksum the v17 DB; run `os serve` (v18) against it with -d file:COPY.db; read the output and exit code; checksum again",
2994+
"snapshot the v17 copy through SQLite — its schema (`sqlite_master`, `pragma_table_info` per table) and every table's rows, read with the sqlite3 CLI or node:sqlite so a `-wal` file is included (copy the v17 DB together with its `-wal`, or checkpoint the original before copying); run `os serve` (v18) against the copy with `OS_DATABASE_URL=file:COPY.db` (`os serve` has no `-d` flag); read the output and exit code; snapshot again and diff the two",
29952995
"run `os dev` on a scratch config with services:{auth:false} and security on"
29962996
],
29972997
"acceptance": [
@@ -3008,10 +3008,10 @@
30083008
"evidence": "the refusal excerpt"
30093009
},
30103010
{
3011-
"clause": "the refused boot did not alter the database",
3011+
"clause": "the refused boot wrote no row and ran no D10 step: every table the v17 copy held keeps every row and every value in the columns it held (no row added, deleted or changed — so no column dropped, no mirror row deleted, no organization_id attributed), every table the boot created is empty, and `sys_migration` holds no `adr-0131-organization-ownership` marker. The additive schema sync the engine runs before the guard judges (new tables, new columns, new indexes) is expected and is not a failure",
30123012
"oracle": "build",
3013-
"verify": "checksum before == after",
3014-
"evidence": "the two checksums"
3013+
"verify": "a row-level diff of the two SQLite snapshots from steps[0], read through SQLite so the `-wal` is included: for each table present before, its pre-existing columns compared row by row (ordered by primary key) — no difference; for each table present only after, its row count — 0; `SELECT id FROM sys_migration WHERE id = 'adr-0131-organization-ownership'` — no row. ⛔ Not a file checksum: in WAL mode the boot's DDL can sit in COPY.db-wal with the main file's hash unchanged, and the additive sync changes the hash with no row written, so a checksum answers neither question",
3014+
"evidence": "per-table row counts before and after, the (empty) row diff over the pre-existing columns, the tables the boot created with their row counts, and the sys_migration read"
30153015
},
30163016
{
30173017
"clause": "the auth-less security boot is refused naming createIdentityObjectsPlugin(), not served with 503s",
@@ -3043,6 +3043,12 @@
30433043
"date": "2026-10-16",
30443044
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it",
30453045
"ref": "#23879"
3046+
},
3047+
{
3048+
"revision": 2,
3049+
"date": "2026-10-19",
3050+
"change": "acceptance[2] (clause, verify, evidence) and steps[0] re-worded — an assertion defect found by the 18.0 QA sweep (QA run #24556, platform-core.v18-boot-refusals acceptance[2]: FAIL, confirmed by an independent RUNNER rule 7 re-derivation; card #24567 row 1). The product is right and the clause was wrong: the guard judges in its own start(), after the engine (whose start() runs the additive schema sync the ceremony waits for) and ahead of every plugin that writes a row (packages/runtime/src/organization-ownership-boot-guard.ts), and ADR-0131 D10 item 5 says the refusing runtime never auto-migrates (the ceremony's fates); nothing in it forbids additive schema sync. So a refused boot legitimately adds tables, columns and indexes, and 'did not alter the database' (checksum before == after) failed on a correct product. The clause now asserts what the guard promises — no row written, no D10 step run — through a row-level SQLite diff that includes the `-wal` (the run's second boot left the main file's hash unchanged with its DDL in the `-wal`). steps[0] also stops passing `-d` to `os serve`, which declares no such flag (the same run's checklist-accuracy finding); `OS_DATABASE_URL` is what the run booted with. The other three clauses are unchanged",
3051+
"ref": "#24567"
30463052
}
30473053
]
30483054
},

0 commit comments

Comments
 (0)