Repository navigation
Description
Spin-off from the published-skills factual sweep (flight ⑩, card #13841 / program #13658). Found by the "file the spec-side twin" rule: the skill text and this docblock carried the same false fact, and correcting only the skill leaves it published one surface over.
The drift
packages/spec/src/ui/action.zod.ts (line 770, the JSDoc on ActionAiSchema.requiresConfirmation):
Override confirmation for AI calls. When unset, the bridge defaults to
truefor actions that look destructive (confirmTextset,mode:'delete', orvariant:'danger').
The implementation stopped reading confirmText when #7828 landed (maintainer ruling, Option A). packages/runtime/src/action-execution.ts:
export function actionLooksDestructive(_deps: ActionExecutionDeps, action: any): boolean {
if (action?.ai?.requiresConfirmation !== undefined) return Boolean(action.ai.requiresConfirmation);
return Boolean(action?.mode === 'delete' || action?.variant === 'danger');
}Executed against the built tree this hour: actionLooksDestructive with confirmText alone returns false; mode: 'delete' and variant: 'danger' each return true. packages/runtime/src/action-execution-destructive.test.ts pins both directions.
Why it matters beyond a stale comment
- It is a SAFETY-shaped claim. An author reading the schema believes a
confirmTextaction will be routed through the HITL confirmation path. It will not. - The
actionLooksDestructivekeys destructiveness offconfirmText— the exact signal #7278/#7309 are migrating away, and 6 of 14 actions flip #7828 rationale is exactly this class:confirmTextis UI dialog copy that approvals:sys_approval_request.approval_reject/approval_recalldeclare bothconfirmTextandparams, so one decision opens two sequential dialogs #7278/platform-objects: 16 more actions declare bothconfirmTextandparams, so one click opens two sequential dialogs (same shape as #7278) #7309 are actively migrating ontodescription, so 6 of 14 identity actions flipped classification the moment theirconfirmTextwas removed. A docblock that keeps teaching it re-seeds the reasoning the ruling retired. - Two other in-repo carriers already state the corrected rule (
packages/mcp/src/mcp-server-runtime.tsline 134, and the runtime pin test's header), so this is a single-site drift, not a repo-wide one.
Fix
Rewrite the docblock to the post-#7828 signal set: mode: 'delete' / variant: 'danger' are the declared signals; confirmText is not one; ai.requiresConfirmation overrides either way. Byte-neutral-or-shrinking. Nothing about .describe() changes — the published describe string is already neutral ("Override HITL confirmation for AI invocations."), so the generated reference page at content/docs/references/ui/action.mdx is unaffected; this is the source-reader surface only.
Historical context, not to be rewritten: docs/adr/0011-actions-as-ai-tools.md describes actionRequiresApproval with the pre-#7828 heuristic. ADRs record the decision of their day.
Filed unassigned for triage.
Generated by Claude Code
Activity
claude commentedon Sep 2, 2026
Claim: PM loop,
domain:specseat (seat post #6017)Session:
session_01GDA48PuRFrHyRfdkBz8m21Branch:
claude/issue-13865-requires-confirmation-docblockWorktree:
objectstack-issue-13865(dedicated, per AGENTS.md Prime Directive #11)Mode:
mode:subagent(S; oneos-dev)Clause-②: no
(Source-reader docblock only on
ActionAiSchema.requiresConfirmation; the.describe()string — the published surface — is untouched, so no generated reference page, authorable surface or JSON schema moves.)File surface:
packages/spec/src/ui/action.zod.ts(the JSDoc at lines 768-773 onorigin/main53d3689), plus a pin so the retiredconfirmTextleg cannot re-enter the docblock;docs/adr/0011-actions-as-ai-tools.mdis explicitly out of scope (ADRs record the decision of their day).Premise re-read on
origin/main53d3689 before this claim: the docblock still namesconfirmTextas a destructive signal;packages/runtime/src/action-execution.ts:885-888(actionLooksDestructive) reads onlyai.requiresConfirmation,mode === 'delete'andvariant === 'danger'; the two other carriers (packages/mcp/src/mcp-server-runtime.ts:162,action-execution-destructive.test.tsheader) already state the post-#7828 rule — single-site drift confirmed. H17 on-hold trigger-file index (#9857): the onlypackages/spec/src/ui/entry namesview.zod.ts(#8346), not this file.Generated by Claude Code
claude commentedon Sep 2, 2026
Claim:
os-devsubagent, takeover round — continuation of the claim above (5511343133), not a new claim.Session:
session_017RbbUMnxkUnWhE4j94v8FEBranch:
claude/issue-13865-requires-confirmation-docblock(unchanged; fast-forwarded 53d3689 toorigin/maina5b95ee, zero commits were on it beyond its base — nothing to rescue, nothing discarded)Worktree:
objectstack-issue-13865(fresh, from that branch)Assignee: untouched (continuation).
Premise re-verified on a5b95ee before writing:
packages/spec/src/ui/action.zod.ts:767-773— the JSDoc still namesconfirmTextas a destructive signal.packages/runtime/src/action-execution.ts:885-888—actionLooksDestructivereadsai.requiresConfirmation(both directions), thenmode === 'delete' || variant === 'danger';confirmTextis not read. Only four files inpackages/mention the function; none of them adds a fourth signal.actionLooksDestructivekeys destructiveness offconfirmText— the exact signal #7278/#7309 are migrating away, and 6 of 14 actions flip #7828 rule:packages/mcp/src/mcp-server-runtime.ts(the "closed declared signals andconfirmTextdeliberately is not" block) and the header ofpackages/runtime/src/action-execution-destructive.test.ts.action.zod.tsbetween 53d3689 and a5b95ee.Scope as ruled: JSDoc region only, byte-neutral-or-shrinking; the
.describe()string, the generatedcontent/docs/references/ui/action.mdx, anddocs/adr/0011-actions-as-ai-tools.mdare untouched.Generated by Claude Code
claude commentedon Sep 2, 2026
os-dev-report
{ "issue": 13865, "status": "done", "branch": "claude/issue-13865-requires-confirmation-docblock", "pr": "https://github.com/objectstack-ai/objectstack/pull/14620", "premise_still_valid": true, "summary": "Takeover of the interrupted dev: the remote branch stood at 53d3689 with zero commits beyond its base, so nothing was inherited and nothing discarded; it was fast-forwarded to origin/main a5b95ee and reused under the same name. Premise re-verified before writing: the JSDoc on ActionAiSchema.requiresConfirmation still named confirmText as a destructive signal, and actionLooksDestructive (packages/runtime/src/action-execution.ts:885-888) reads only ai.requiresConfirmation, mode==='delete' and variant==='danger'. The docblock was rewritten to that signal set, byte-shrinking 385 to 383 on the whole block, with the .describe() string, content/docs/references/ui/action.mdx and docs/adr/0011 untouched (check:generated and check:docs green). One pin was added that extracts the JSDoc region AND the classifier body and compares them, so it fails in both directions. The override guidance at action.zod.ts:869 was checked and is correct today (it calls confirmText the separate UI confirm prompt, never a destructive signal) - left unedited. One deviation from the suggested surface, declared in the PR body: check:cross-package-test-inputs went red because the sibling MCP carrier's PATH, written in header prose, reads as an undeclared cross-package input; the carrier is now named by package instead, rather than declaring a turbo input for a file the pin never reads. Head 1145b48ca. Final head vs origin/main 2514d49f3: file-disjoint, no generated product moved on main, no merge needed.", "tests": "All measurements on final head 1145b48ca, exit codes captured by redirect-then-capture, never through a pipe. GREEN: pnpm --filter @objectstack/spec test => 'Test Files 453 passed | 1 skipped (454)', 'Tests 12214 passed | 1 skipped (12215)', exit 0 (via scripts/pm/os-verify-lock.sh, VERDICT command-exit 0, held 492s). pnpm --filter @objectstack/spec typecheck => exit 0; its check:test-typecheck leg compiles the test layer under tsconfig.test.json (54 files, no new pinned signature), so the new test file IS typechecked, not silently excluded. pnpm --filter @objectstack/spec check:generated => exit 0 on the built tree, 15 artifacts all green (check:docs, check:authorable-surface, check:api-surface among them) - no generated product moved. pnpm check:nul-bytes => exit 0; plus a manual control-byte scan of both changed files (clean). The new pin => 6 passed (6). Gate family derived on the final head with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (49 commands, 2-path change set, merge base a5b95ee6e, identical to the pre-amend derivation); all 49 run, 44 exit 0. check:cross-package-test-inputs exit 0 after the reword ('OK: 25 package(s) read outside themselves, all declared'). NOT MEASURED (never read as green): check-test-completeness (its own documented no-argument branch - it grades a saved turbo test log the family cannot supply), and four gates blocked by an unbuilt workspace: check-dev-prereqs, check:dual-build-cjs-loads, check:type-check-debt, @objectstack/lint check:doc-formula-expressions (unbuilt @objectstack/formula). Five spec gates first reported the unbuilt-dist prerequisite and were RE-RUN green after pnpm --filter @objectstack/spec build: check:api-surface, check:browser-reachable-entries, check:dual-source-exports, check:entry-nameability, check:exported-any - check:api-surface is the one that would see a JSDoc leaking into the emitted .d.ts, and it does not. ABLATION, two legs, re-run on the final head. No dist preflight applies: the pin reads both files as source text via readFileSync, so nothing resolves through a package exports map and no built artifact sits between mutation and assertion. Each leg proved the mutation on disk by occurrence count AND by blob hash differing from the HEAD blob before any result was read; each restore was proved by disk hash == HEAD blob plus empty git diff HEAD, never by an exit code; absolute paths, restore trap on EXIT INT TERM; nothing committed. LEG 1 (retired confirmText sentence restored into the docblock): grep retired=1 corrected=0, mutated HEAD=c36532b34 disk=d4bdb9939, pin exit 1, 'x presents no positive confirmText claim', 'Tests 1 failed | 5 passed (6)'; restored to c36532b34, git diff HEAD empty. The mutated blob d4bdb9939 is byte-identical to the pre-fix file. LEG 2 (confirmText leg re-entered in the runtime classifier): mutated HEAD=5747fcc82 disk=1746fb5a2, pin exit 1, 'the classifier still does not read confirmText (#7828 Option A)', 'Tests 1 failed | 5 passed (6)'; restored to 5747fcc82, git diff HEAD empty. CONTROL on the restored tree: 6 passed (6), exit 0. Worktree clean at report time.", "mcp_calls": "0 - the REST channel probed live on this seat (repo GET 200, /rate_limit core 15000/hr), so every read and write went through it: issue body, all comments, claim comment, PR creation, label POST and read-back", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
spec: repair pass on the confirmation contract review (A1/A3/A4/A5/A6…
spec: define the confirmation contract behind `action.ai.requiresConf…
test(spec): the first ui/ file group's test titles state each cited d…