Skip to content

Commit a623a15

Browse files
hotlongclaude
andauthored
docs(qa): invert four more activation-ledger read-backs to key-set assertions (#15359)
`sys_metadata_activation` dropped its reserved `organization_id` column before it ever shipped (#15024): the object declares `systemFields: { tenant: false }`, the opt-out that REMOVES the column rather than leaving it unwritten. PR #15251 re-grounded the two items #15154 named. Four siblings still read that column back and asserted it NULL — a VACUOUS leg, not merely a wrong value: once the column is gone, `row.organization_id ?? null` answers `null` either way, so the read passes rather than failing and a runner sees green for exactly the reason it should be red. Each is inverted into a KEY-SET assertion, the shape #15024 used on the dogfood pin and PR #15251 carried into step 2. - platform-core.activation-ledger-registration-home — step 2's row capture and the no-automation clause's "ONE install-level row (organization_id NULL)". The row is re-said as DEPLOYMENT-level: with no tenant column there is no second scope for install-level to be distinguished from. The schema-side probe stays with the sibling item that owns it. - api-backend.packaged-action-disabled-dispatch — the disable step's row confirmation, plus `automated.ref`, which described the dogfood pin as asserting "ONE install-level row with organization_id NULL". That pin has been a key-set reading named "writes ONE deployment-level row, and the TABLE has no tenant column at all" since #15024; the ref now quotes its live name. - api-backend.action-activation-door-contract — the happy-path step, its acceptance clause and `automated.ref`. The clause also glossed the column as "the per-org dimension is reserved and unwritten": ADR-0131 D7 WITHDREW that ADR-0126 §5 D3 bullet ("a reserved nullable tenant column is the shape D1 forbids") and names this table as reverted before 17.3 and not returning, so the gloss told a runner the tenant dimension was coming back. The source list now cites ADR-0131 D7 and marks which half of §5 survives. - automation.packaged-flow-disable-durable — the ledger read step, the row clause and its "§5 reserves the column" gloss, the verify line's NULL-collapsed 'organization' index (it is a plain `unique: 'global'` over two required key parts; with no tenant column `normalizeDeclaredIndex` has no part to prepend), and the `source` line still citing "org-carrying rows skipped on read" and "organization_id never written". The store citation splits so `#list` carries the read-path correction where the read path lives. Each item bumps `revision` and appends a `history` entry, per the checklist's change lifecycle. No item outside this defect class is touched, and no clause of the four contracts themselves moved — only what a runner records about the row. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent a727043 commit a623a15

3 files changed

Lines changed: 48 additions & 20 deletions

File tree

docs/qa/platform-checklist/areas/api-backend.json

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,7 +1355,7 @@
13551355
"title": "A disabled packaged action is refused 409 ACTION_DISABLED at BOTH dispatch doors — after the capability gate (a 403 is never an oracle for the switch), before the param contract and the record load, durable across restart",
13561356
"since": "v17",
13571357
"status": "active",
1358-
"revision": 1,
1358+
"revision": 2,
13591359
"priority": "P0",
13601360
"surface": "api",
13611361
"personas": [
@@ -1377,7 +1377,7 @@
13771377
},
13781378
"steps": [
13791379
"boot showcase isolated on the real HTTP stack (os dev); sign in as admin",
1380-
"disable the probe: POST /api/v1/actions/_activation/showcase_task/showcase_mark_done {\"enabled\": false} → 200 (the door's own arms are api-backend.action-activation-door-contract's item — do not re-prove them here); confirm the row via GET /api/v1/data/sys_metadata_activation (metadata_type 'action', name showcase_mark_done, organization_id NULL, active false)",
1380+
"disable the probe: POST /api/v1/actions/_activation/showcase_task/showcase_mark_done {\"enabled\": false} → 200 (the door's own arms are api-backend.action-activation-door-contract's item — do not re-prove them here); confirm the row via GET /api/v1/data/sys_metadata_activation (metadata_type 'action', name showcase_mark_done, active false) and confirm the row's KEY SET carries NO organization_id at all — the ledger has no tenant column (#15024 / ADR-0131 D7). ⛔ Never confirm the tenant half by reading the value: a check of the shape `row.organization_id ?? null` answers `null` for a column that does not exist, so it passes while measuring nothing",
13811381
"REST door: POST /api/v1/actions/showcase_task/showcase_mark_done with {\"recordId\": \"no-such-record-0000\"}; capture status + body; then re-read a REAL seeded task and confirm no done/progress write happened anywhere",
13821382
"param-ordering discriminator: disable showcase_action_param_gallery (POST /actions/_activation/showcase_field_zoo/showcase_action_param_gallery {\"enabled\":false}), then invoke POST /actions/showcase_field_zoo/showcase_action_param_gallery with params: {\"bogus_key\": 1} (missing required p_text AND an unknown key) plus a nonexistent recordId; capture. Re-enable and repeat the IDENTICAL request; capture the contrast",
13831383
"403-oracle: POST /actions/showcase_field_zoo/showcase_zoo_perm_missing while it is ENABLED; capture the 403 body. Disable it via /actions/_activation/showcase_field_zoo/showcase_zoo_perm_missing, repeat the identical invocation, capture again; byte-compare the two refusals",
@@ -1442,7 +1442,7 @@
14421442
],
14431443
"automated": {
14441444
"kind": "dogfood",
1445-
"ref": "packages/qa/dogfood/test/packaged-activation-ledger-reach.dogfood.test.ts (live boots, both compositions: the flip lands 200, ONE install-level row with organization_id NULL, REST-door dispatch 409, re-enable UPDATES the row and re-arms, flows+actions share one table without touching each other's rows) + packages/runtime/src/domains/action-activation-dispatch.test.ts (unit: both doors incl. the MCP envelope, the 403-not-an-oracle leg, the flow-type-refused-by-its-own-switch leg, absence-means-active). ⚠️ Neither pin covers the RESTART leg, the param-ordering contrast, or a LIVE MCP transport call — those stay manual"
1445+
"ref": "packages/qa/dogfood/test/packaged-activation-ledger-reach.dogfood.test.ts (live boots, both compositions: the flip lands 200, ONE deployment-level row on a table with no tenant column at all — 'writes ONE deployment-level row, and the TABLE has no tenant column at all', a KEY-SET reading off the driver's own SELECT rather than a value read — REST-door dispatch 409, re-enable UPDATES the row and re-arms, flows+actions share one table without touching each other's rows) + packages/runtime/src/domains/action-activation-dispatch.test.ts (unit: both doors incl. the MCP envelope, the 403-not-an-oracle leg, the flow-type-refused-by-its-own-switch leg, absence-means-active). ⚠️ Neither pin covers the RESTART leg, the param-ordering contrast, or a LIVE MCP transport call — those stay manual"
14461446
},
14471447
"traps": [
14481448
"dispatcher-vs-hono-route",
@@ -1466,6 +1466,12 @@
14661466
"date": "2026-08-26",
14671467
"change": "new — ADR-0126 §8 item 2 landed the packaged-action kill switch with a two-door consult and nothing in the ledger asserted the dispatch-side contract: the 409, its position in the gate order (after the D4 403, before params/record), the MCP twin, or restart durability. Register corrections folded in during source verification: the param-ordering discriminator must be showcase_action_param_gallery (showcase_mark_done declares no params, so enforceActionParams is a no-op for it and its enabled control proves nothing); the 403 leg must be an action with requiredPermissions (showcase_zoo_perm_missing — mark_done has none, every authed caller passes its gate); the live MCP leg must be showcase_portfolio_snapshot (the only ai.exposed stock action — the exposure gate refuses everything else ahead of the consult); and the record-load leg has no contrasting 404 because the load is best-effort by design",
14681468
"ref": "#12438"
1469+
},
1470+
{
1471+
"revision": 2,
1472+
"date": "2026-09-04",
1473+
"change": "ledger read-back detail only — the dispatch contract itself is UNCHANGED and no clause moved. #15024 dropped sys_metadata_activation's reserved organization_id column before it ever shipped (systemFields { tenant: false } removes it rather than leaving it unwritten), so step 2's row confirmation asked a runner to read a column that is not there. It now asserts the row's KEY SET carries no organization_id, because the value spelling passes VACUOUSLY once the column is gone (`row.organization_id ?? null` is `null` either way) — green for exactly the reason it should be red. automated.ref was stale in the same direction: it described the dogfood pin as asserting 'ONE install-level row with organization_id NULL', and that pin has been a key-set reading named 'writes ONE deployment-level row, and the TABLE has no tenant column at all' since #15024; the ref now quotes the pin's live name so a runner who opens the file finds it",
1474+
"ref": "#15253"
14691475
}
14701476
]
14711477
},
@@ -1474,7 +1480,7 @@
14741480
"title": "POST /actions/_activation/:object/:action — the activation door's ordered arms: authority before body and lookup, the exact 400 shapes, 404 undeclared, 409 ambiguity, 501/503 honesty, and the reserved segment surviving the real mount",
14751481
"since": "v17",
14761482
"status": "active",
1477-
"revision": 1,
1483+
"revision": 2,
14781484
"priority": "P2",
14791485
"surface": "api",
14801486
"personas": [
@@ -1500,7 +1506,7 @@
15001506
"shape arms, as admin: POST /api/v1/actions/_activation/showcase_mark_done {} (2-segment); POST /api/v1/actions/_activation/showcase_task/showcase_mark_done/extra {} (4-segment); capture both",
15011507
"body arms, as admin, against the declared action: {\"enable\": false} (one letter off); {\"enabled\": \"false\"} (string, not boolean); [] (non-object body); {} (empty); capture each with full envelope",
15021508
"declaration arm: POST /actions/_activation/showcase_task/no_such_action {\"enabled\": false} as admin; capture; then GET /api/v1/data/sys_metadata_activation and confirm NO row was written by any refused probe so far",
1503-
"happy path: POST /actions/_activation/showcase_task/showcase_mark_done {\"enabled\": false} → capture the 200 body; read the ledger row (metadata_type 'action', name, organization_id NULL, active false); POST again {\"enabled\": true} → re-read: the SAME row updated in place, active true — never deleted, never duplicated",
1509+
"happy path: POST /actions/_activation/showcase_task/showcase_mark_done {\"enabled\": false} → capture the 200 body; read the ledger row (metadata_type 'action', name, active false) and capture its KEY SET — organization_id is ABSENT from it, because the table carries no tenant column (#15024 / ADR-0131 D7). ⛔ Do not record the tenant half as a value: `row.organization_id ?? null` is `null` for a column that does not exist, so a value read passes while measuring nothing; POST again {\"enabled\": true} → re-read: the SAME row updated in place, active true — never deleted, never duplicated",
15041510
"record the ambiguity / 501 / 503 arms against their unit pins (knownGaps above) — run the pins and cite their output rather than skipping the clauses silently"
15051511
],
15061512
"acceptance": [
@@ -1535,10 +1541,10 @@
15351541
"evidence": "the 404 trace + the ledger read"
15361542
},
15371543
{
1538-
"clause": "the happy path writes exactly ONE install-level row and re-enable UPDATES it: 200 {name, objectName, enabled}; the row carries metadata_type 'action', the declarative name, organization_id NULL (the per-org dimension is reserved and unwritten); flipping back answers 200 and the same row flips active — never a second row, never a delete (§6 wall 3: the ledger records the choice)",
1544+
"clause": "the happy path writes exactly ONE DEPLOYMENT-level row and re-enable UPDATES it: 200 {name, objectName, enabled}; the row carries metadata_type 'action', the declarative name and active — and NO organization_id key, because the ledger has no tenant column to carry one (ADR-0131 D7 WITHDREW ADR-0126 §5 D3's reserved-and-never-written column before 17.3, and names this table as not getting it back — so the per-org dimension is not 'reserved', it is gone); flipping back answers 200 and the same row flips active — never a second row, never a delete (§6 wall 3: the ledger records the choice)",
15391545
"oracle": "api",
1540-
"verify": "step-6: the 200 body + before/after ledger reads (one row throughout, active false then true)",
1541-
"evidence": "the traces + both row reads"
1546+
"verify": "step-6: the 200 body + before/after ledger reads (one row throughout, active false then true), each read scored on the row's KEY SET for the tenant half. ⛔ Never score it from a value: a check of the shape `row.organization_id ?? null` answers `null` for a column that does not exist, so it passes while measuring nothing (platform-core.activation-ledger-row-contract owns the schema-side probe)",
1547+
"evidence": "the traces + both row reads, key sets included"
15421548
},
15431549
{
15441550
"clause": "the ambiguity arm refuses instead of switching off artifacts the caller did not name: a name declared on 2+ objects answers 409 RESOURCE_CONFLICT (standard catalog — no new code minted) naming every owner object, and writes nothing",
@@ -1561,7 +1567,7 @@
15611567
],
15621568
"automated": {
15631569
"kind": "unit",
1564-
"ref": "packages/runtime/src/domains/action-activation-posture-gate.test.ts (the door's own contract: both authority tiers across all three postures, unknown-key/non-boolean 400s, empty-body-enables, undeclared 404 + no row, ambiguity 409 + no write, 501 no-projection, 503 non-durable) + packages/qa/dogfood/test/action-params-contract.dogfood.test.ts ('[#12160] the reserved `_activation` segment survives the real mount' — the 2-segment 400 and undeclared 404 measured through the live dispatcher reconstruction) + packages/qa/dogfood/test/packaged-activation-ledger-reach.dogfood.test.ts (the live happy path: 200 flip, one org-NULL row, re-enable updates). The live-server body/authority arm sweep as a whole stays manual"
1570+
"ref": "packages/runtime/src/domains/action-activation-posture-gate.test.ts (the door's own contract: both authority tiers across all three postures, unknown-key/non-boolean 400s, empty-body-enables, undeclared 404 + no row, ambiguity 409 + no write, 501 no-projection, 503 non-durable) + packages/qa/dogfood/test/action-params-contract.dogfood.test.ts ('[#12160] the reserved `_activation` segment survives the real mount' — the 2-segment 400 and undeclared 404 measured through the live dispatcher reconstruction) + packages/qa/dogfood/test/packaged-activation-ledger-reach.dogfood.test.ts (the live happy path: 200 flip, then 'writes ONE deployment-level row, and the TABLE has no tenant column at all' — a key-set reading off the driver's own SELECT, not a value read — and re-enable updates). The live-server body/authority arm sweep as a whole stays manual"
15651571
},
15661572
"traps": [
15671573
"dispatcher-vs-hono-route",
@@ -1574,7 +1580,8 @@
15741580
"packages/runtime/src/route-ledger.ts#servedBy (the #12160 server-only row: servedBy reconstruction through /actions/:object/:action/:recordId, the 4-segment router-404, and why the segment is reserved)",
15751581
"packages/objectql/src/engine.ts#setActionActive (setActionActive — durable row first, projection second; the 503-on-no-ledger contract)",
15761582
"sibling item api-backend.packaged-action-disabled-dispatch (what DISPATCH does with the row this door writes) · access-security.activation-write-operator-gate owns the gate's posture depths",
1577-
"docs/adr/0126-packaged-metadata-customization-model.md (§4, §5, §6 walls, §8 item 2)",
1583+
"docs/adr/0126-packaged-metadata-customization-model.md (§4, §5, §6 walls, §8 item 2) — ⚠️ §5's operator-gate half STANDS (access-security.activation-write-operator-gate owns its depths); only §5 D3's separate 'the org column is reserved, written NULL' bullet was WITHDRAWN, which is why the happy path's row read names no organization_id",
1584+
"docs/adr/0131-total-organization-ownership-no-null-organization-id.md D7 (deployment-level state has no organization column — sys_metadata_activation is named there as reverted before 17.3 and not returning; a reserved nullable tenant column is the shape D1 forbids)",
15781585
"#12438",
15791586
"Epic #12150"
15801587
],
@@ -1584,6 +1591,12 @@
15841591
"date": "2026-08-26",
15851592
"change": "new — the ADR-0126 §8 activation door is the only non-invocation shape /actions serves, is absent from every REST-ledger sweep (server-only, servedBy reconstruction), and none of its arms were in the ledger. Register corrections folded in during source verification: the 400-shape message carries a '(use `global` for an object-less action)' tail; a 4-segment path 404s at the ROUTER rather than reaching the door's 400 (no mount pattern matches); the authority gates run ahead of even the path-shape check, not just body/lookup; an empty body ENABLES by contract (a trap for a runner reading it as a refusal); and the ambiguity arm's no-stock-fixture status was verified across both showcase action files rather than assumed",
15861593
"ref": "#12438"
1594+
},
1595+
{
1596+
"revision": 2,
1597+
"date": "2026-09-04",
1598+
"change": "ledger read-back detail and one citation — the door's ordered arms are UNCHANGED and no arm moved. Two corrections, both downstream of #15024 dropping sys_metadata_activation's reserved organization_id column before it ever shipped. (1) VACUITY: step 6 and the happy-path clause read the column back and asserted it NULL. With the column gone that read passes rather than failing (`row.organization_id ?? null` is `null` either way), so the leg was green for the wrong reason; both now score the row's KEY SET, the same inversion #15024 applied to the dogfood pin. automated.ref carried the same staleness as 'one org-NULL row' and now quotes the pin's live name. (2) DECISION: the clause glossed the column as 'the per-org dimension is reserved and unwritten', citing ADR-0126 §5 D3 — a bullet ADR-0131 D7 WITHDREW ('a reserved nullable tenant column is the shape D1 forbids'), naming this table as reverted before 17.3 and not returning. Left as it stood it told a runner the tenant dimension was coming back. The source list now carries ADR-0131 D7 and marks which half of §5 survives: the operator gate stands, the reserved column does not",
1599+
"ref": "#15253"
15871600
}
15881601
]
15891602
},

0 commit comments

Comments
 (0)