Skip to content

feat(spec)!: remove tool.requiresConfirmation — a safety flag nothing enforced (#3715, ADR-0033 §2) - #3876

Merged
os-zhuang merged 2 commits into
mainfrom
claude/action-undoable-experimental-j8g24i
Jul 28, 2026
Merged

feat(spec)!: remove tool.requiresConfirmation — a safety flag nothing enforced (#3715, ADR-0033 §2)#3876
os-zhuang merged 2 commits into
mainfrom
claude/action-undoable-experimental-j8g24i

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

The enforce-or-remove the ledger flagged as its most severe entry.

The property

ToolSchema.requiresConfirmation accepted true and no execution path ever read it:

Path What it does with the flag
LLM tool set (vercel-adapter.ts) nothing — a tool reaches the model as name/description/parameters only
ToolRegistry.execute nothing
POST /ai/tools/:name/execute nothing
MCP bridge (mcp-server-runtime.ts) nothing — derives destructiveHint from a hardcoded name list

For an ordinary dead property that's untidy. For a safety property it's false compliance — the case ADR-0049 exists for. An author gates a destructive tool, sees the flag accepted, and ships believing a human is in the loop.

What makes it worse than most dead surface is the near-miss: action.ai.requiresConfirmation has the same name and does work (runtime/src/action-execution.ts:264). So the mistake reads as correct in review, and every grep for the identifier lands on hits that are genuinely live. ADR-0033 §2 already resolved this: "We delete the unenforced requiresConfirmation placeholder on metadata tools."

Migration

  • FROM requiresConfirmation: true on a tool definition
  • TO put the operation behind an action and set ai.requiresConfirmation: true there — the flag the HITL approval queue reads, and the only path that actually stops execution.
  • For AI metadata mutations, nothing to migrate: the ADR-0033 draft/publish workspace is the gate — nothing is live until a human publishes.

ToolSchema is now .strict() — this is the load-bearing part

Removing a key from a non-strict schema swaps one silent no-op for another: zod strips it wordlessly, the author keeps writing requiresConfirmation: true, and the safety flag goes on meaning nothing. That's the "silent strip" ADR-0032 / #1535 closed for objects, and it would have made this PR a rename of the problem rather than a fix.

So the retired key now rejects, carrying the FROM → TO above — a parse error is the one channel every consumer bumping @objectstack/spec is guaranteed to hit. This follows the house pattern (ObjectCapabilities, tenancy): the tombstone lives in a TOOL_RETIRED_KEY_GUIDANCE map wired through a $ZodErrorMap.

Side benefit: a typo on a tool definition (buildIn, catagory) is now a located parse error instead of a silently dropped field.

Also removed

  • the Studio form row (tool.form.ts);
  • its four generated locale bundlesen/zh-CN/ja-JP/es-ES still carried "Ask user to approve before executing (for destructive actions)", a translated false promise. The vocabulary test only checks retired groups, not fields, so nothing would have caught this;
  • the liveness-ledger entry (tool dead 5 → 4);
  • the generated reference-doc row (pnpm gen:docs — exactly one line, no churn).

Verification

  • pnpm --filter @objectstack/spec test — 262 files, 6828 passed. Three new tests pin the behaviour: the key rejects rather than being stripped, the message names ai.requiresConfirmation and #3715, and an unrelated unknown key rejects too (strictness isn't special-cased to the tombstone).
  • check:liveness — exits 0, zero warnings, tool now 18 classified (13 live / 4 dead / 1 experimental).
  • Strictness blast radius checked: no defineTool / ToolSchema.parse call sites exist outside spec itself, so nothing in-repo constructs a Tool with extra keys.
  • @objectstack/mcp and some platform-objects suites fail in this sandbox on unbuilt workspace deps (@objectstack/core, @objectstack/formula). Confirmed pre-existing by re-running them on a stashed tree — identical failure. CI builds first.

Follow-up in objectui

ToolPreview.tsx reads the field as !!d.requiresConfirmation, so it degrades to "badge not shown" with no error — no break, but removing that badge (and the preview-samples.ts entry) is a follow-up in that repo. Worth noting it was a preview renderer, which by this ledger's own doctrine was never evidence of a consumer in the first place.


Generated by Claude Code

… enforced (#3715, ADR-0033 §2)

ToolSchema.requiresConfirmation accepted `true` and no execution path ever read
it: not the LLM tool set (a tool reaches the model as name/description/parameters
only), not ToolRegistry.execute, not POST /ai/tools/:name/execute, and not the MCP
bridge, which derives destructiveHint from a hardcoded name list. Setting it on a
destructive tool produced NO pause.

For an ordinary dead property that is untidy. For a SAFETY property it is false
compliance — the case ADR-0049 exists for: an author gates a destructive tool,
sees the flag accepted, and ships believing a human is in the loop. Made worse by
the near-miss: action.ai.requiresConfirmation has the same name and DOES work, so
the mistake reads as correct in review. ADR-0033 §2 already resolved to delete it.

Migration — FROM `requiresConfirmation: true` on a tool, TO an action carrying
`ai.requiresConfirmation: true`, the flag the HITL approval queue actually reads
(runtime/src/action-execution.ts). For AI metadata mutations nothing changes: the
ADR-0033 draft/publish workspace is the gate.

ToolSchema is now .strict(), which is load-bearing rather than tidying. Removing a
key from a non-strict schema swaps one silent no-op for another — zod strips it
wordlessly, the author keeps writing it, and the safety flag goes on meaning
nothing (the silent strip ADR-0032/#1535 closed for objects). The retired key now
REJECTS with the FROM -> TO prescription attached, because a parse error is the
one channel every consumer bumping @objectstack/spec is guaranteed to hit. Typos
on a tool definition are now located parse errors too.

Also removed: the Studio form row; its four generated locale bundles, which still
promised "Ask user to approve before executing (for destructive actions)" — a
translated false promise; the liveness-ledger entry (tool dead 5 -> 4); and the
generated reference-doc row.

objectui's ToolPreview reads it as `!!d.requiresConfirmation`, so it degrades to
"not shown" with no error; removing that badge is a follow-up in that repo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ajwvrmd1hDC9RBofYBhGuR
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 28, 2026 1:44pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ai size/m tooling labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/platform-objects, @objectstack/spec.

104 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/platform-objects, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/platform-objects, @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

… skill example + clarify the MCP namesake

CI's check:skill-examples caught what my own sweep missed: skills/objectstack-ai
SKILL.md:326 authored `requiresConfirmation: true` in a `defineTool` example —
one an AI copies verbatim — so removing the field left the skill teaching a call
that now fails to parse. Fixed the example, the property list above it (which
also promised `default false`), and the guardrails note, which now says the field
was removed rather than merely unenforced.

I missed it because my grep ended in `| head -10` and the skills/ hit was #11 —
the exact failure the liveness README documents under "How to verify a claim
without fooling yourself" (`… | head -3` hid the real hit further down the list).
Re-swept untruncated.

Also clarified the two MCP docs whose `requiresConfirmation` is a DIFFERENT,
still-live thing — the MCP capability descriptor hint surfaced in list_actions
(shape: handler + sideEffects + array params, not ToolSchema). Their notes cited
#3715 as "declared but unenforced", which after the removal reads as if THIS is
the field that went away. They now name which is which.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ajwvrmd1hDC9RBofYBhGuR
@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 14:06
@os-zhuang
os-zhuang merged commit 4d00b13 into main Jul 28, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/action-undoable-experimental-j8g24i branch July 28, 2026 14:07
os-zhuang added a commit that referenced this pull request Jul 28, 2026
…just skills/ (#3882)

check:skill-examples compiles the TypeScript in prose against the built spec, so
an example that stops compiling fails CI instead of quietly teaching code that no
longer works. It does its job — it caught the broken defineTool example in #3876.
But it only ever walked skills/:

    skills/          9 files with ts blocks,   9 compiled
    content/docs/  124 files with ts blocks,   0 compiled

The identical break in a docs page ships. Docs examples are copied verbatim by
humans and AI exactly like skill examples — the same shape as the stale-evidence
and orphan-proof warnings fixed in #3857 / #3868: a gate covering a fraction of
the surface it appears to cover reads as coverage.

The walker is now a SOURCE_ROOTS list, and this lands the first batch: 164 docs
blocks across 63 pages, taking the gate from 32 to 196 checked examples.
content/docs/references/ is excluded — build-docs.ts regenerates it from the
schemas, so it cannot drift independently.

THE MARKER IS NOW PER-FORMAT. MDX has no HTML comments: `<!-- os:check -->` in a
.mdx does not degrade, it fails the fumadocs build outright ("Unexpected
character `!`… to create a comment in MDX, use `{/* text */}`"). Found by building
the docs site after the first attempt broke 60+ pages; nothing in the type-check
gate would have said so, because block extraction is regex-level and never parses
MDX. skills/**/*.md keeps `<!-- os:check -->`; content/docs/**/*.mdx uses
`{/* os:check */}`. Both spellings are recognised for ORPHAN detection, so a
wrong-format marker fails loudly instead of silently checking nothing.

The batch was measured, not guessed: marking all 780 docs blocks and compiling
showed which are self-contained. One subtlety — a block that "passes" inside a
780-file program can be leaning on globals declared by OTHER blocks (a file with
no import/export is a global script), so the first pass's 564 "passing" blocks
collapsed to 164 once compiled as the real, smaller set. Converged by recompiling
and dropping newly-failing blocks until green.

The remaining blocks are mostly fragments, which the opt-in design anticipates.
Whether any are genuine rot is now answerable for the first time.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ai size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants