You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(runtime): keep tracker ids out of ledger prose; record the new elevation read
`check:doc-authoring` refuses tracker ids inside runtime prose strings — a route
ledger `note` reaches operators and generated surfaces, none of whom can resolve
`#NNNN`. The ids move to adjacent `//` comments, where the reader who can resolve
them already is.
`check:check-system-context-census` requires every `isSystem` read to carry an
anchor on the census page. The new gate's `isSystem` bypass is anchored on the
automation row and the page's seven census-derived counts move with it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zTkyNHJ7TkuN2oXtP5x37
| 55 | Package domain capability gates bypassed | runtime | Get: package management and package-inventory reads without the capability |`packages/runtime/src/domains/packages.ts#requireManageMetadata`, `#requireReadCapability`|
174
174
| 56 | Activation write / authoring refusals do not fire | runtime | Get: activation artifacts writable and authorable without the activation-authoring capability |`packages/runtime/src/domains/activation-gate.ts#refuseUngrantedActivationWrite`, `#refuseUngrantedActivationAuthoring`|
175
-
| 57 | Automation run-state read, flow-authoring write and unrelated-screen read all pass | runtime | Get: run state, flow writes and screen reads with no grant |`packages/runtime/src/domains/automation.ts#mayReadRunState`, `#refuseUngrantedFlowWrite`, `#refuseUnrelatedScreenRead`|
175
+
| 57 | Automation run-state read, flow-authoring write, unrelated-screen read and the two operator run-lifecycle writes all pass | runtime | Get: run state, flow writes, screen reads with no grant — and cancelling or restoring a suspension without the platform-operator rung. The lifecycle bypass is the in-process owner's door: plugin-approvals' revise-window recall (ADR-0044) cancels on behalf of a decision it already authorized and recorded |`packages/runtime/src/domains/automation.ts#mayReadRunState`, `#refuseUngrantedFlowWrite`, `#refuseUnrelatedScreenRead`, `#refuseUngrantedRunLifecycleWrite`|
176
176
| 58 | Audience-binding suggestion recording skipped | plugin-security | Lose: install-time suggestions are not recorded for system callers |`packages/plugins/plugin-security/src/suggested-audience-bindings.ts#assertTenantAdmin`|
177
177
| 59 | Email-template / webhook provenance stamps skipped | plugin-email, plugin-webhooks | Lose: the row is not marked as an admin customization |`packages/plugins/plugin-email/src/email-template-provenance.ts#bindEmailTemplateProvenanceStamp`, `packages/plugins/plugin-webhooks/src/webhook-provenance.ts#bindWebhookProvenanceStamp`|
178
178
| 60 |**Automation flow data nodes re-add the `owner_id` stamp** (the one place row 2's gap is compensated inline) | service-automation | Get: a flow-authored INSERT under system elevation still lands owned, when the run resolved a user. Fill-only — flow-authored values win |`packages/services/service-automation/src/runtime-identity.ts#stampSystemInsertOwner`, called from `packages/services/service-automation/src/builtin/crud-nodes.ts#registerCrudNodes`|
@@ -277,7 +277,7 @@ Ownership injection, `readonly` bypass and sharing materialisation are
277
277
independent decisions, and a seed loader plausibly wants the first two but not
278
278
the third. The concept is nevertheless **staying as one boolean**:
279
279
280
-
-**Shipped semantics.**`isSystem` is a published contract with 106 read sites
280
+
-**Shipped semantics.**`isSystem` is a published contract with 107 read sites
281
281
in 20 packages. Splitting it is a breaking contract change across all of them.
282
282
(The ruling was taken when the census read 80 sites in 18 packages; the count
283
283
has grown, which strengthens rather than weakens the argument.)
@@ -334,16 +334,16 @@ still holds equal to the census on every pull request:
334
334
| Appearances of the bare identifier `isSystem` in non-test sources | 813 | — |
335
335
| — parsed as a declaration | 22 | ✅ |
336
336
| — parsed as an object-literal / type key (producers and option objects) | 310 | — |
337
-
| — parsed as a property **read**|112| ✅ |
337
+
| — parsed as a property **read**|113| ✅ |
338
338
| — parsed in some other syntactic position (a local, a cast, a conditional) | 9 | ✅ |
339
339
| — the remainder: text inside comments and string literals | 358 | — |
340
340
| Of those reads: reads of one of the unrelated metadata fields | 6 | ✅ |
341
-
| Of those reads: reads of `ExecutionContext.isSystem`|**106**| ✅ |
342
-
| — behaviour-bearing (rows 1–62 above) |102| ✅ |
341
+
| Of those reads: reads of `ExecutionContext.isSystem`|**107**| ✅ |
342
+
| — behaviour-bearing (rows 1–62 above) |103| ✅ |
343
343
| — carry the flag onward only (rows 63–66 above) | 4 | ✅ |
344
344
| Packages containing at least one elevation read |**20**| ✅ |
345
345
| Files containing at least one elevation read | 45 | ✅ |
346
-
| — the distinct symbols those reads live in — what this page anchors |89| ✅ |
346
+
| — the distinct symbols those reads live in — what this page anchors |90| ✅ |
347
347
| — of those files, the ones holding more than one read in one symbol | 9 | ✅ |
348
348
349
349
The six rows marked — are a **dated decomposition, not a live claim**: they were
note: "generic, so the SUSPENDED NODE gates it (#3801): a pause whose descriptor declares resumeAuthority:'service' — today `approval` / `approval_revise` — answers 403 here and continues only through its owning service (ApprovalService.decide), which authorizes and records the decision first. A node type that declares NO resumeAuthority answers 403 too, fail-closed since #5561: this door is an opt-in a descriptor states with 'any'. Screen/wait pauses are unaffected because they declare it; this route is the screen-flow runner's door"},
note: "[#13953] Cancel a suspended run (ADR-0044) — the maintainer ruling of 2026-09-05 (option A) gave the engine's two operator run-lifecycle verbs a door, because until #16563 neither was reachable by an operator at all: no REST route, no CLI command, and not on `IAutomationService`. Body is the closed `{ reason? }` envelope, relayed VERBATIM to the engine, which lands it on the terminal `cancelled` log's `error`. ⚑ ONE authority tier and it is the strictest one this domain has: the ADR-0095 D2/D3 posture RUNG (`posture === 'PLATFORM_ADMIN'`, #15981 — NEVER `positions.includes('platform_admin')`, which `sys_user_position` lets a tenant mint), required UNCONDITIONALLY. ⛔ Not posture-conditional like the ADR-0126 §5 activation gate: that one falls open under `single` because `manage_metadata` still gates it there, and this door has no capability tier in front of it, so the same conditionality would open an operator verb to any authenticated caller on every single-organization deployment — looser than `resume`, whose `resumeAuthority` gate is fail-closed on every deployment. Fail-closed by construction: an absent executionContext, an absent posture or any other rung all reach the refusal, 403 `PERMISSION_DENIED` (ADR-0112); only `isSystem` bypasses, which is how plugin-approvals' in-process revise-window recall keeps working. The #5519 anonymous floor answers an unidentified caller 401 first. WHICH routes is one predicate, `isRunLifecycleWrite` in `domains/automation.ts`, read by the gate AND by both route arms so they cannot drift; it excludes `parts[0] === 'trigger'` so a flow literally NAMED `runs` keeps its legacy execution door. Answers 200 both ways — `cancelled: false` is idempotent success per the contract — but ⛔ never a BARE success: `false` is also what an UNREADABLE durable store answers, so the response carries a `notice` naming both readings (#13909's posture: never a door that returns success while hiding the condition). The `true` notice states that `true` is NOT exclusive — the engine has no cancel-side compare-and-set, so overlapping cancels each answer `true` and each record the terminal log; this door keys no once-only side effect off it and says so on the wire. A service not declaring `cancelRun` (an OPTIONAL member) answers 501 `NOT_IMPLEMENTED`, ⛔ never a 200 and ⛔ never `{ handled: false }`. NOT JS-SDK surface on this leg, and that is stated rather than left as an open gap: the ruling charters a REST door for a platform operator holding only HTTP and explicitly declines a CLI command for want of pull, so this card declares no client method and implies none; adding one reclassifies this row to `sdk`. Pinned in `domains/automation-run-lifecycle-door.test.ts`"},
397
+
note: "Cancel a suspended run (ADR-0044) — the maintainer ruling of 2026-09-05 (option A) gave the engine's two operator run-lifecycle verbs a door, because until the contract half landed neither was reachable by an operator at all: no REST route, no CLI command, and not on `IAutomationService`. Body is the closed `{ reason? }` envelope, relayed VERBATIM to the engine, which lands it on the terminal `cancelled` log's `error`. ⚑ ONE authority tier and it is the strictest one this domain has: the ADR-0095 D2/D3 posture RUNG (`posture === 'PLATFORM_ADMIN'` — NEVER `positions.includes('platform_admin')`, which `sys_user_position` lets a tenant mint), required UNCONDITIONALLY. ⛔ Not posture-conditional like the ADR-0126 §5 activation gate: that one falls open under `single` because `manage_metadata` still gates it there, and this door has no capability tier in front of it, so the same conditionality would open an operator verb to any authenticated caller on every single-organization deployment — looser than `resume`, whose `resumeAuthority` gate is fail-closed on every deployment. Fail-closed by construction: an absent executionContext, an absent posture or any other rung all reach the refusal, 403 `PERMISSION_DENIED` (ADR-0112); only `isSystem` bypasses, which is how plugin-approvals' in-process revise-window recall keeps working. The anonymous floor answers an unidentified caller 401 first. WHICH routes is one predicate, `isRunLifecycleWrite` in `domains/automation.ts`, read by the gate AND by both route arms so they cannot drift; it excludes `parts[0] === 'trigger'` so a flow literally NAMED `runs` keeps its legacy execution door. Answers 200 both ways — `cancelled: false` is idempotent success per the contract — but ⛔ never a BARE success: `false` is also what an UNREADABLE durable store answers, so the response carries a `notice` naming both readings (never a door that returns success while hiding the condition). The `true` notice states that `true` is NOT exclusive — the engine has no cancel-side compare-and-set, so overlapping cancels each answer `true` and each record the terminal log; this door keys no once-only side effect off it and says so on the wire. A service not declaring `cancelRun` (an OPTIONAL member) answers 501 `NOT_IMPLEMENTED`, ⛔ never a 200 and ⛔ never `{ handled: false }`. NOT JS-SDK surface on this leg, and that is stated rather than left as an open gap: the ruling charters a REST door for a platform operator holding only HTTP and explicitly declines a CLI command for want of pull, so this card declares no client method and implies none; adding one reclassifies this row to `sdk`. Pinned in `domains/automation-run-lifecycle-door.test.ts`"},
398
+
// [#13953] The repair verb's door — the exit from the `'stranded'` state #13937
399
+
// shape 4 named and #13909 exists to measure. Its result is the inline structural
400
+
// type PR #16563 landed in spec (card #16495 route (i)), whose `refusal?: string` is
401
+
// the covariant widening the non-exhaustive status switch is a consequence of.
note: "[#13953] Put back the suspension a failed resume consumed (#13909) — the repair verb for `AutomationResult.status: 'stranded'`, re-arming a run the platform recorded as terminally failed. Same gate, same predicate and same fail-closed absent-member 501 as the cancel row above; see it for the authority and why the rung is unconditional. The card's own reason this needed a permission model rather than a line of routing: a repair verb re-arms a terminally-failed run, so 'who may do this' is a real question and NOT the same answer as 'who may resume'. Body is the closed `{ reason? }` envelope; ⭐ `requestedBy` is filled from the AUTHENTICATED CALLER and is refused BY NAME in the body, so no operator can write another's name into the trace that records who re-armed the run. Refusals are answered as refusals — `RUN_NOT_FOUND` 404, `STORE_UNAVAILABLE` 503, and the five run-state conflicts (`RESUME_IN_PROGRESS`, `RESTORE_IN_PROGRESS`, `RUN_SUSPENDED`, `RUN_COMPLETED`, `RUN_CANCELLED`, `NO_CONSUMED_SUSPENSION`) 409 — matching the statuses this same door already answers those conditions with on `resume`, ⛔ never a 200 carrying `restored: false`. ⚠️ The contract types the refusal as `refusal?: string`, a covariant widening of the engine's closed eight-member union (#16495 route (i)), so that mapping is a NON-EXHAUSTIVE string switch by construction: an unrecognised code — or a `restored: false` carrying none — answers 500, ⛔ deliberately not one of the 409s, which would claim a diagnosis this door did not make. ⛔ The vocabulary is neither narrowed nor extended at this call site; closing it is a `packages/spec` card. The engine's code rides `details.refusal`, ⛔ never `details.code`, which would promote an unregistered member into the ADR-0112-closed `error.code`. NOT JS-SDK surface on this leg, for the cancel row's reason; adding a client method reclassifies this row to `sdk`. Pinned in `domains/automation-run-lifecycle-door.test.ts`"},
403
+
note: "Put back the suspension a failed resume consumed — the repair verb for `AutomationResult.status: 'stranded'`, re-arming a run the platform recorded as terminally failed. Same gate, same predicate and same fail-closed absent-member 501 as the cancel row above; see it for the authority and why the rung is unconditional. The card's own reason this needed a permission model rather than a line of routing: a repair verb re-arms a terminally-failed run, so 'who may do this' is a real question and NOT the same answer as 'who may resume'. Body is the closed `{ reason? }` envelope; ⭐ `requestedBy` is filled from the AUTHENTICATED CALLER and is refused BY NAME in the body, so no operator can write another's name into the trace that records who re-armed the run. Refusals are answered as refusals — `RUN_NOT_FOUND` 404, `STORE_UNAVAILABLE` 503, and the run-state conflicts (`RESUME_IN_PROGRESS`, `RESTORE_IN_PROGRESS`, `RUN_SUSPENDED`, `RUN_COMPLETED`, `RUN_CANCELLED`, `NO_CONSUMED_SUSPENSION`) 409 — matching the statuses this same door already answers those conditions with on `resume`, ⛔ never a 200 carrying `restored: false`. ⚠️ The contract types the refusal as `refusal?: string`, a covariant widening of the engine's closed eight-member union, so that mapping is a NON-EXHAUSTIVE string switch by construction: an unrecognised code — or a `restored: false` carrying none — answers 500, ⛔ deliberately not one of the 409s, which would claim a diagnosis this door did not make. ⛔ The vocabulary is neither narrowed nor extended at this call site; closing it is a `packages/spec` card. The engine's code rides `details.refusal`, ⛔ never `details.code`, which would promote an unregistered member into the ADR-0112-closed `error.code`. NOT JS-SDK surface on this leg, for the cancel row's reason; adding a client method reclassifies this row to `sdk`. Pinned in `domains/automation-run-lifecycle-door.test.ts`"},
0 commit comments