|
2099 | 2099 | "title": "Activation-ledger write authority (ADR-0126 §5): the manage_metadata tier refuses first on every posture, and the shared operator gate — INERT on stock `single` — demands the platform_admin POSITION in walled postures, at both the flow and action doors", |
2100 | 2100 | "since": "v17", |
2101 | 2101 | "status": "active", |
2102 | | - "revision": 1, |
| 2102 | + "revision": 2, |
2103 | 2103 | "priority": "P1", |
2104 | 2104 | "surface": "api", |
2105 | 2105 | "personas": [ |
|
2128 | 2128 | "as the plain member again: repeat the flow toggle with a deliberately INVALID body (e.g. {\"enabled\": \"false\"}) — the refusal must still be the 403, not a 400 body-validation answer (the gate runs before body validation, automation.ts; activation-gate.ts)", |
2129 | 2129 | "GET /api/v1/data/sys_metadata_activation (reads are open — sys-metadata-activation.object.ts) and confirm no row for either artifact was written by the refusals", |
2130 | 2130 | "author the scratch manage_metadata permission set, grant it to a fresh member, and prove server-side that the member's positions do NOT include platform_admin (session row / GET /auth/get-session — never the grant gesture)", |
2131 | | - "as that member: POST /api/v1/automation/showcase_urgent_task_alert/toggle {\"enabled\": false} — expect 200; read the ledger row back (metadata_type 'flow', active false, organization_id NULL); then re-enable ({\"enabled\": true}) to restore the fixture", |
| 2131 | + "as that member: POST /api/v1/automation/showcase_urgent_task_alert/toggle {\"enabled\": false} — expect 200; read the ledger row back (metadata_type 'flow', active false, and NO organization_id key on the row — the ledger carries no tenant column at all, #15024 / ADR-0131 D7); then re-enable ({\"enabled\": true}) to restore the fixture", |
2132 | 2132 | "as the same member: POST /api/v1/actions/_activation/showcase_task/showcase_mark_done {\"enabled\": false} — expect 200 + the metadata_type 'action' row; re-enable to restore", |
2133 | 2133 | "walled-posture legs: BLOCKED on stock fixtures (see knownGaps) — the group/isolated matrix (org admin with manage_metadata refused naming the posture and the per-artifact remedy; platform_admin POSITION admitted; no row on refusal) is pinned at unit level by automated.ref" |
2134 | 2134 | ], |
|
2152 | 2152 | "evidence": "the ledger reads + the invalid-body trace" |
2153 | 2153 | }, |
2154 | 2154 | { |
2155 | | - "clause": "the §5 gate is INERT on `single`, not satisfied: a caller holding manage_metadata but NOT the platform_admin POSITION succeeds at BOTH doors, and each write lands one install-level ledger row (organization_id NULL) — install-level and org-level are the same scope on one logical tenant (activation-gate.ts,141-147)", |
| 2155 | + "clause": "the §5 gate is INERT on `single`, not satisfied: a caller holding manage_metadata but NOT the platform_admin POSITION succeeds at BOTH doors, and each write lands one deployment-level ledger row — install-level and org-level are the same scope on one logical tenant (activation-gate.ts,141-147), and the ledger carries no tenant column to say otherwise", |
2156 | 2156 | "oracle": "api", |
2157 | | - "verify": "the scratch-set member's positions are proven server-side to exclude platform_admin (BUILTIN_IDENTITY_PLATFORM_ADMIN = 'platform_admin', packages/spec/src/identity/eval-user.zod.ts), then both toggles answer 200 and GET /api/v1/data/sys_metadata_activation shows the metadata_type 'flow' and 'action' rows with active false, organization_id NULL; re-enable restores active true", |
| 2157 | + "verify": "the scratch-set member's positions are proven server-side to exclude platform_admin (BUILTIN_IDENTITY_PLATFORM_ADMIN = 'platform_admin', packages/spec/src/identity/eval-user.zod.ts), then both toggles answer 200 and GET /api/v1/data/sys_metadata_activation shows the metadata_type 'flow' and 'action' rows with active false and no organization_id key at all; re-enable restores active true. ⛔ Do not score the tenant half from a VALUE read — a check of the shape `row.organization_id ?? null` is `null` for a column that does not exist, so it passes while measuring nothing; read the key set (platform-core.activation-ledger-row-contract owns that probe)", |
2158 | 2158 | "evidence": "the position proof + both 200 traces + the ledger rows" |
2159 | 2159 | }, |
2160 | 2160 | { |
|
2195 | 2195 | "packages/runtime/src/domains/automation.ts#FLOW_ENABLEMENT_DENY_MESSAGE (FLOW_ENABLEMENT_DENY_MESSAGE), (isFlowActivationWrite), (gate ordering ahead of service probe and body checks)", |
2196 | 2196 | "packages/runtime/src/domains/actions.ts (both tiers at the action door)", |
2197 | 2197 | "packages/spec/src/security/tenancy-posture.ts#TenancyPostureSchema (TenancyPostureSchema — the variants source; postureEnforcesWall; the ADR-0105 D12 entitlement note)", |
2198 | | - "docs/adr/0126-packaged-metadata-customization-model.md §5 (D3: install-level rows, operator-gated in multi-org postures)", |
2199 | | - "#12438 (the sweep), #12159 (ADR-0126 flow legs), #10243 (the measured incident the gate makes durable), #12157 / #12160 (the two door cards)" |
| 2198 | + "docs/adr/0126-packaged-metadata-customization-model.md §5 (D3: install-level rows, operator-gated in multi-org postures) — ⚠️ the operator gate this item owns STANDS; only §5's separate 'the org column is reserved' bullet was withdrawn (ADR-0131 D7), which is why the ledger read-backs here name no organization_id", |
| 2199 | + "#12438 (the sweep), #12159 (ADR-0126 flow legs), #10243 (the measured incident the gate makes durable), #12157 / #12160 (the two door cards), #15154 (the ledger read-back detail re-grounded after #15024 dropped the tenant column — the gate itself and its pins are unchanged)" |
2200 | 2200 | ], |
2201 | 2201 | "history": [ |
2202 | 2202 | { |
2203 | 2203 | "revision": 1, |
2204 | 2204 | "date": "2026-08-26", |
2205 | 2205 | "change": "new — ADR-0126 §5 write authority had no checklist coverage. Authored with the stock-posture truth stated up front (the §5 gate is INERT on `single`; the D1 refusal is the manage_metadata tier) so a runner never scores the wrong gate, and with the group/isolated legs blocked(fixture) on the enterprise-posture gap (#9334 precedent) instead of faked. Variants pinned to TenancyPostureSchema (verified extractable by the validator's enumSource extractor)", |
2206 | 2206 | "ref": "#12438" |
| 2207 | + }, |
| 2208 | + { |
| 2209 | + "revision": 2, |
| 2210 | + "date": "2026-09-04", |
| 2211 | + "change": "read-back detail only: #15024 dropped sys_metadata_activation's reserved organization_id column, so the two ledger read-backs (step 7 and the inert-gate clause) asked the runner to see a column that is gone. They now read the row's KEY SET instead — a value check of the shape `row.organization_id ?? null` is `null` for a column that does not exist, so carrying the old spelling forward would have left a leg that passes while measuring nothing. ⚠️ The gate itself is UNCHANGED: ADR-0126 §5's operator-gate half stands and its unit pins pass unmodified; only §5's separate 'the org column is reserved' bullet was withdrawn (ADR-0131 D7). The source citation now says so, so the next reader does not infer the gate moved too", |
| 2212 | + "ref": "#15154" |
2207 | 2213 | } |
2208 | 2214 | ] |
2209 | 2215 | }, |
|
0 commit comments