Summary
@objectstack/spec carries three customer-facing prescriptions that state, in the present tense, that the runtime confirmation door has not shipped. PR #17486 (issue #15942) ships it. The moment that PR merges, all three are false — and they fail in the direction that matters: they tell an author the safety flag stops nothing, so an author who reads them concludes the gate is inert and arranges a human in the loop some other way, or stops setting the flag at all.
This is the mirror image of the defect #15942 was filed for. That card was "published prose promises a pause the runtime does not perform". This is "published prose denies a pause the runtime now performs". Same class, same surface, opposite sign.
The three sites
All three are the shipped text, not comments: the first reaches consumers as a parse-error prescription on a .strict() schema, the other two are what spec-changes.json, the upgrade guide and os migrate meta project.
1. packages/spec/src/ai/tool.zod.ts — the requiresConfirmation entry of the retired-key guidance map:
Read this before you rely on it: the declaration is the contract, not yet the behaviour — the runtime door that performs the refusal ships separately, and until it does, setting the flag does NOT stop an unconfirmed call. Do not try to verify the gate by invoking the operation without the member: until that door lands, such a call simply RUNS.
2. packages/spec/src/migrations/entries/semantic/17.tool-requires-confirmation-retired.ts, replacement:
The refusal is DECLARED, not yet performed — the runtime door lands in #15942, so until then the flag stops nothing on its own and the human in the loop is still yours to arrange
3. the same file, acceptanceCriteria:
Do NOT try to "prove the gate" by invoking the operation without the confirmation member: the runtime door that refuses lands in #15942, so before that ships the call is not refused, it RUNS the destructive operation. Until then the declaration is a contract and the human in the loop is still yours to arrange
Site 3 is the sharpest: after #17486 it instructs an author not to run the one experiment that would now demonstrate the gate working, on the grounds that the experiment would execute the destructive operation. It would not. It would be refused.
Why this is a card and not a rider on #17486
The spec half was declared read-only for the runtime half's executor, so #17486 does not touch packages/spec. That boundary is right — but the tense promotion was explicitly assigned to the landing change by the spec half itself. tool.zod.ts carries this comment immediately above site 1:
Tense follows enforcement: promote it in the change that lands the door, not before.
So the work is declared, the trigger has arrived, and the only thing missing is a carrier. Filing it rather than riding it also keeps the two regeneration gates (check:spec-changes, check:upgrade-guide) and the tombstone's own text on a diff a domain:spec seat owns.
What the fix is
Promote the tense at all three sites and drop the "not yet" hedges. The substance to keep, all of it already true after #17486:
- the refusal is
ACTION_CONFIRMATION_REQUIRED, 428, naming the action and the member to set;
- it is a GATE, not a queue — nothing is parked, nothing is held, and a refused call simply did not run;
- the enforced set is the doors that enforce
ai.exposed — today invokeBusinessAction via MCP run_action. REST /actions is not ai.exposed-gated and sits outside the gate, so an author relying on it for an API-key agent must still arrange their own human;
- only the author's declared
ai.requiresConfirmation === true refuses; the wider list_actions heuristic advises but never refuses;
confirm: true is an unverifiable caller claim. The gate makes forgetting loud; it does not prove a human.
Regenerate after the edit: pnpm --filter @objectstack/spec check:generated --fix covers check:spec-changes and check:upgrade-guide (site 1 is hand-written text with no generator).
Blocked-by: #17486 — the text may only be promoted once the door is on main, or it becomes false in the original direction instead.
Ordering hazard, restated
The domain:spec seat flagged the symmetric hazard on #15942 (comment 5567321513): a release cut between the two PRs. It repaired the wording to be contract-referential rather than relying on ordering, which is why nothing was broken while #15942 sat blocked. That repair is exactly what now needs its second half — the hedges it added are load-bearing only until the door lands, and permanent thereafter only as a defect.
Evidence
Read on origin/main at merge base c4d1759fa. Positive control on the same greps: requiresConfirmation lights 10 times across packages/spec/src, so the three readings above are a measurement and not a mis-aimed pattern.
Filed from the #15942 runtime half (PR #17486) as an out-of-scope finding: packages/spec was read-only for that delivery, and this seat did not edit it.
Blocked-by: #15942
Generated by Claude Code
Summary
@objectstack/speccarries three customer-facing prescriptions that state, in the present tense, that the runtime confirmation door has not shipped. PR #17486 (issue #15942) ships it. The moment that PR merges, all three are false — and they fail in the direction that matters: they tell an author the safety flag stops nothing, so an author who reads them concludes the gate is inert and arranges a human in the loop some other way, or stops setting the flag at all.This is the mirror image of the defect #15942 was filed for. That card was "published prose promises a pause the runtime does not perform". This is "published prose denies a pause the runtime now performs". Same class, same surface, opposite sign.
The three sites
All three are the shipped text, not comments: the first reaches consumers as a parse-error prescription on a
.strict()schema, the other two are whatspec-changes.json, the upgrade guide andos migrate metaproject.1.
packages/spec/src/ai/tool.zod.ts— therequiresConfirmationentry of the retired-key guidance map:2.
packages/spec/src/migrations/entries/semantic/17.tool-requires-confirmation-retired.ts,replacement:3. the same file,
acceptanceCriteria:Site 3 is the sharpest: after #17486 it instructs an author not to run the one experiment that would now demonstrate the gate working, on the grounds that the experiment would execute the destructive operation. It would not. It would be refused.
Why this is a card and not a rider on #17486
The spec half was declared read-only for the runtime half's executor, so #17486 does not touch
packages/spec. That boundary is right — but the tense promotion was explicitly assigned to the landing change by the spec half itself.tool.zod.tscarries this comment immediately above site 1:So the work is declared, the trigger has arrived, and the only thing missing is a carrier. Filing it rather than riding it also keeps the two regeneration gates (
check:spec-changes,check:upgrade-guide) and the tombstone's own text on a diff adomain:specseat owns.What the fix is
Promote the tense at all three sites and drop the "not yet" hedges. The substance to keep, all of it already true after #17486:
ACTION_CONFIRMATION_REQUIRED, 428, naming the action and the member to set;ai.exposed— todayinvokeBusinessActionvia MCPrun_action. REST/actionsis notai.exposed-gated and sits outside the gate, so an author relying on it for an API-key agent must still arrange their own human;ai.requiresConfirmation === truerefuses; the widerlist_actionsheuristic advises but never refuses;confirm: trueis an unverifiable caller claim. The gate makes forgetting loud; it does not prove a human.Regenerate after the edit:
pnpm --filter @objectstack/spec check:generated --fixcoverscheck:spec-changesandcheck:upgrade-guide(site 1 is hand-written text with no generator).Blocked-by:#17486 — the text may only be promoted once the door is onmain, or it becomes false in the original direction instead.Ordering hazard, restated
The
domain:specseat flagged the symmetric hazard on #15942 (comment 5567321513): a release cut between the two PRs. It repaired the wording to be contract-referential rather than relying on ordering, which is why nothing was broken while #15942 sat blocked. That repair is exactly what now needs its second half — the hedges it added are load-bearing only until the door lands, and permanent thereafter only as a defect.Evidence
Read on
origin/mainat merge basec4d1759fa. Positive control on the same greps:requiresConfirmationlights 10 times acrosspackages/spec/src, so the three readings above are a measurement and not a mis-aimed pattern.Filed from the #15942 runtime half (PR #17486) as an out-of-scope finding:
packages/specwas read-only for that delivery, and this seat did not edit it.Blocked-by: #15942
Generated by Claude Code