Skip to content

fix(spec): the tool.requiresConfirmation guidance names the one pair REST /actions asks ai.exposed of - #33056

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-4nuxb0
Nov 13, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-4nuxb0

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Part of #32924 (item 1 only; item 2, whether REST also asks confirm: true, stays with the maintainer and keeps the card open)

Clause-②: no
Metadata-protocol: none
Skills-docs: none owed — skills/** never says REST /actions is not ai.exposed-gated (its requiresConfirmation hits teach the action-level flag and the tool-level removal, both still true); in content/docs/** the only stale spot was permissions/system-context.mdx row 57, fixed here. ui/actions.mdx (the 'rest' posture row), ai/actions-as-tools.mdx and ai/connect-mcp.mdx already state the AI-service-account rules correctly.

维护者速读

  • 改了什么:工具上写 requiresConfirmation 时报错里给作者的那段说明,原来说「REST /actions 不读 ai.exposed」;现在改为「只在一种情况下读:AI 服务账号经 type: 'flow' 动作启动 runAs: 'system' 的流程,此时只要求作者的 AI 授权,不要求确认」,其余照旧。权限文档 system-context 第 57 行补上这条 AI 服务账号规则;os migrate meta 对已删除的工具 requiresConfirmation 给出的升级说明(17 版语义迁移条目)同样改正,并说明代表某个人的调用仍需作者自己把关、AI 服务账号自身在每道动作门都被拒。
  • 为什么改:PR fix(runtime, service-automation)!: an AI service account starts a flow that runs as the system only through an action its author opted into AI #32912(已合并,随 18.4.0 发版)让 REST 动作路由对这一组合读 ai.exposed,原句随版本发出会误导写 AI 动作的作者。
  • 风险与回滚:只改文字,schema 接受与拒绝的内容不变(Clause-②: no);回滚即 revert。
  • 先看:packages/spec/src/ai/tool.zod.ts(requiresConfirmation 那段说明的第 (1) 条边界);packages/spec/src/migrations/entries/semantic/17.tool-requires-confirmation-retired.ts(replacement 与验收说明两句);content/docs/permissions/system-context.mdx 第 57 行。
  • 两分钟验证:pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/ai/tool-confirmation-prescription-tense.pin.test.ts,应见全部通过。
  • 需要你做的:无,席位落地。第 2 项(REST 是否也要求 confirm: true)仍待你在卡上定夺。

What changes

The prescription. TOOL_RETIRED_KEY_GUIDANCE.requiresConfirmation in packages/spec/src/ai/tool.zod.ts is the message an author meets when a tool still declares requiresConfirmation (the .strict() ToolSchema rejects it). Its first bound said REST /actions "is NOT ai.exposed-gated". Since #32912 that is untrue for one pair. The bound now reads:

(1) The enforced set is the doors that ask every action for the author's ai.exposed opt-in — today the action door reached from the MCP run_action tool. REST /actions reads ai.exposed in one case only (an AI service account starting a type: 'flow' action whose flow declares runAs: 'system' needs the opt-in there), and that check asks for the opt-in alone, not a confirmation; for a person's call REST /actions sits OUTSIDE this gate, so a human in front of that route is still yours to arrange.

The account rule that follows it (an AI service account never confirms for itself and is refused at every action door, REST included) is unchanged. The source comment above the entry names the runtime rule it now describes.

Both halves read off the code at c54a23d2:

  • REST reads the opt-in for that pair: packages/runtime/src/flow-start-admission.ts#flowStartRefusal rule 2, flow.runAs === 'system' && isAiServiceAccount(executionContext) && door.aiExposed !== true → 403 PERMISSION_DENIED; dispatchFlowAction (action-execution.ts, shared by the REST actions route and MCP run_action) hands { aiExposed: actionIsAiExposed(action) }.
  • The confirmation gate is still not on REST for a person: domains/actions.ts runs the admitted sequence under posture: 'rest', and admitResolvedAction asks actionConfirmationRefusal only under 'ai' or for an AI service account. The pin ai/tool-confirmation-prescription-tense.pin.test.ts asserts that relation and stays green.

Docs. content/docs/permissions/system-context.mdx row 57 (what the system principal bypasses at a flow start) gains the AI-service-account rule beside the elevated self-triggered start and requiredPermissions (comment 6839421711). Prose only; the row's anchors and function list are unchanged.

Generated artefacts. The describe string feeds none: it is a parse-error message, not a .describe() on a schema field, so no JSON schema, content/docs/references/** page or authorable-surface shard carries it. check:generated against a dist built from this head: all 14 artefacts up to date.

The upgrade guidance (seat rework, file surface amended in comment 6846193569). packages/spec/src/migrations/entries/semantic/17.tool-requires-confirmation-retired.ts, the entry os migrate meta reports for a tool still carrying the key, carried the same sentence twice. Its replacement (printed alone by os migrate meta and the upgrade guide) now says REST /actions reads ai.exposed only of an AI service account starting a runAs: 'system' flow action (asking for the opt-in, not a confirmation) and sits outside the gate for a person's call or one an agent makes on a person's behalf, while an AI service account acting for itself is refused a gated action at every action door, REST /actions included (contract review S1, record 6846718400). Its acceptance text says the same, and replaces "an agent holding an API key on that route is still yours to put a human in front of" with: the confirmation gate is not enforced there, so a person or an agent acting on a person's behalf on that route is still yours to put a human in front of (an AI service account acting for itself is refused a gated action at every action door; review N2 dropped "with an API key", since that principal is the OAuth-delegated client). No generated artefact carries the entry's text (check:generated, check:spec-changes, check:upgrade-guide green).

Changeset. .changeset/32924-requires-confirmation-describe.md, @objectstack/spec patch.

Tests

No new test. The existing pin already ties this text to the runtime door in both directions, and it keeps passing with the rewording (ai.exposed bound, REST /actions carve-out, account rule, REST \/actions` included, unverifiable caller claim`). Prose only, so no ablation.

Acceptance notes

  • Ruling surface grep (ruling|Ruling-ref|maintainer|⛔): tool.zod.ts 0 hits; system-context.mdx hits at rows 6b, 21, 23f, 31, 57, 62 and the ruling sections (L285, L316–337, L369, L446). Only row 57 is edited, and it cites ruling 6812617393 letter C, the ruling the new sentence restates.
  • The 16→17 semantic migration entry was first left out of scope and reported; the seat amended the file surface (comment 6846193569) and it is fixed here, see What changes.
  • The two reds on the first head 4379cff9 (Test UI, TypeScript Type Check) were aggregator jobs reading cancelled shards and lanes after the rework push superseded that head; neither log holds a test or tsc failure.
  • PR feat(runtime): a script body sends mail and adds a timeline entry through platform doors that stamp who sent it #32966 (Tier H) also edits system-context.mdx; this PR touches row 57's text only.
  • Item 2 of the card (S2: whether REST asks confirm: true of an AI service account) is not implemented in either direction.

Verification

All readings at head a8f3da02.

  • pnpm --filter @objectstack/spec build (lock VERDICT command-exit 0), then pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/migrations/ src/ai/: Test Files 21 passed (21), Tests 604 passed (604), including tool-confirmation-prescription-tense.pin.test.ts and semantic-relevance.test.ts.
  • Consumer of the migration entry: pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 test/migrate-meta-engine-guidance.test.ts (after pnpm --filter @objectstack/cli build): Test Files 1 passed (1), Tests 3 passed (3).
  • pnpm --filter @objectstack/spec run check:generated (against the dist built from this change): all 14 generated artefacts up to date; the working tree stayed clean.
  • Derived gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack → 103 families (the entry adds check:spec-changes, check:upgrade-guide, check:cli-command-ids, check:query-options-erasure), all run with </dev/null after a pnpm turbo run build --concurrency=2 --filter='./packages/**' (81/81), exit codes captured before any pipe; every one exit 0. Reconciliation --ran: 103 derived famil(ies) accounted for — 103 run, 0 NOT-MEASURED (a DERIVED zero — all 103 recorded an exit code and none of them is 3).
  • Consumers: the text is a parse-error message of ToolSchema; no package parses its wording (the runtime pin reads the runtime, not this string), so no consumer test is owed beyond the spec pin. test:repo and the full farm are CI's.
Gate record (103 lines)
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-adr-links.mjs :: exit 0
node scripts/check-adr-links.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dev-prereqs.mjs --self-test :: exit 0
node scripts/check-doc-route-spelling.mjs --advisory :: exit 0
node scripts/check-doc-route-spelling.mjs --self-test :: exit 0
node scripts/check-docs-section-name.mjs :: exit 0
node scripts/check-docs-section-name.mjs --self-test :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-section-landing-index.mjs :: exit 0
node scripts/check-section-landing-index.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/ci/snapshot-next.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0
pnpm --filter @objectstack/spec run check:api-surface :: exit 0
pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0
pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0
pnpm --filter @objectstack/spec run check:docs :: exit 0
pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm --filter @objectstack/spec run check:empty-state :: exit 0
pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0
pnpm --filter @objectstack/spec run check:export-origins :: exit 0
pnpm --filter @objectstack/spec run check:exported-any :: exit 0
pnpm --filter @objectstack/spec run check:liveness :: exit 0
pnpm --filter @objectstack/spec run check:llms-txt :: exit 0
pnpm --filter @objectstack/spec run check:skill-examples :: exit 0
pnpm --filter @objectstack/spec run check:skill-refs :: exit 0
pnpm --filter @objectstack/spec run check:spec-changes :: exit 0
pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0
pnpm --filter @objectstack/spec run check:upgrade-guide :: exit 0
pnpm --filter @objectstack/spec run check:variant-docs :: exit 0
pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:cli-command-ids :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:corpus-claim-drift :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-anchors :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:doc-frontmatter :: exit 0
pnpm check:docs-audit-scope :: exit 0
pnpm check:docs-redirects :: exit 0
pnpm check:docs-single-h1 :: exit 0
pnpm check:docs-spec-enumerations :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:merge-driver :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:object-def-param-keys :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-prior-rulings :: exit 0
pnpm check:published-files :: exit 0
pnpm check:published-readme-links :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:react-page-adapter-contract :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:role-word :: exit 0
pnpm check:settings-bind-window :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:spec-docblock-symbol-anchors :: exit 0
pnpm check:spec-parsed-alias :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:vendor-version-stamps :: exit 0
pnpm check:vouch-for-reference-callers :: exit 0
pnpm check:watch-hint-literal :: exit 0

🤖 Generated with Claude Code

https://claude.ai/code/session_01L9qibyxGyX6L3NhaTF6yiA

…REST /actions asks ai.exposed of

The rejection prescription said REST /actions is never ai.exposed-gated.
That route now asks the opt-in of an AI service account starting a
runAs: 'system' flow action, with no confirmation asked there; the bound
now says so. The permissions system-context table's flow-start row names
the same rule. Text only; no accept-set change.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibyxGyX6L3NhaTF6yiA
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation protocol:ai tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 13, 2026
@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 3 documentable anchor(s).

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/actions-as-tools.mdx (via requiresConfirmation (symbol, a field of const object TOOL_RETIRED_KEY_GUIDANCE))
  • content/docs/ai/tools.mdx (via requiresConfirmation (symbol, a field of const object TOOL_RETIRED_KEY_GUIDANCE))
  • content/docs/protocol/objectui/actions.mdx (via requiresConfirmation (symbol, a field of const object TOOL_RETIRED_KEY_GUIDANCE))
What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 65 of 247 client-bound route-ledger rows — the other 182 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 182: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 59 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 123 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 171 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 157051ceb48d2d9ed289f21f7018a39ce7249178 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from d67edaf6ddbffc0c6bc4a64ed09c107e4895d6f3 — the merge of head a8f3da02c48a08873d486c6bfbfaae2e940789f6 into base 157051ceb48d2d9ed289f21f7018a39ce7249178, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d67edaf6ddbffc0c6bc4a64ed09c107e4895d6f3 && git checkout d67edaf6ddbffc0c6bc4a64ed09c107e4895d6f3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 157051ceb48d2d9ed289f21f7018a39ce7249178 a8f3da02c48a08873d486c6bfbfaae2e940789f6 && git checkout -B drift-repro 157051ceb48d2d9ed289f21f7018a39ce7249178 && git merge --no-ff a8f3da02c48a08873d486c6bfbfaae2e940789f6

node scripts/docs-audit/affected-docs.mjs --json 157051ceb48d2d9ed289f21f7018a39ce7249178

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 157051ceb48d2d9ed289f21f7018a39ce7249178 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…ames the REST ai.exposed pair

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibyxGyX6L3NhaTF6yiA
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#2 · os-tesla · session_01L9qibvfX5bawZKtfa6u7j9

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d086c4cf0948b148218155d504dfc836b635c6d9
Local-runs: none
Scope: packages/spec/src/ai/tool.zod.ts, packages/spec/src/migrations/entries/semantic/17.tool-requires-confirmation-retired.ts (hits) + in-diff content/docs/permissions/system-context.mdx row 57; read, not reviewed: packages/runtime/src/{flow-start-admission,action-dispatch,action-execution}.ts, domains/{actions,mcp,automation}.ts, endpoint-executor.ts, packages/core/src/security/assemble-execution-context.ts, packages/cli/src/commands/migrate/meta.ts, packages/spec/scripts/build-upgrade-guide.ts, the pin ai/tool-confirmation-prescription-tense.pin.test.ts

① Derived judgments

  • Accept sets: none moved — right. Both hits change string literals only (a ToolSchema rejection message and a SemanticMigration's replacement / acceptanceCriteria); no schema shape, key, enum or export changes.
  • Public surface: the two published strings change wording — right, and intended.
  • Generated artefacts: none carries either string; consistent with Lint & Repo Gates, Lint · PM dispatch-gates, Spec property liveness, Build Docs success.
  • Check-runs at review time: all completed runs success or skipped; Test Core (7/8) and (8/8) still in progress.

② Semver level

@objectstack/spec patch matches a text-only change. Clause-②: no is present in the changeset and the PR body, and is correct. Check Changeset success.

③ Boundary flags

  • open_questions: [].
  • Dev flag (migration entry text): answered by the file-surface amendment 6846193569 and fixed in this diff — in one of the two fields only, see S1.
  • Item 2 of the card: not implemented or decided here — right. Escalated to the maintainer: item 2's premise ("ai.requiresConfirmation is asked only on MCP") no longer matches the tree. admitResolvedAction (packages/runtime/src/action-dispatch.ts:460, the posture === 'ai' || isAiServiceAccountCaller(wiring.ec) branch) already refuses an AI service account 428 at REST /actions (ADR-0151 D3 rule 3), which is stricter than option B.

④ Design judgment

packages/spec/src/ai/tool.zod.ts — holds, sentence by sentence: under posture: 'rest' the only ai.exposed reader is dispatchFlowAction → flowStartRefusal(…, { aiExposed }) rule 2 (opt-in alone, no confirmation); a person's REST call skips the gate; an AI service account is refused a gated action at every action door, REST included. nit N1 (unchanged text): "today the action door reached from the MCP run_action tool" omits MCP resume_run, which asks the same gate; it understates enforcement.

17.tool-requires-confirmation-retired.ts

  • acceptanceCriteria — holds.
  • should-fix S1 — replacement: "REST /actions reads ai.exposed only of an AI service account starting a runAs: 'system' flow action (asking for the opt-in, not a confirmation) and otherwise sits outside the gate" is false for an AI service account, which the gate refuses 428 at REST for any action declaring ai.requiresConfirmation: true. replacement is printed alone by os migrate meta (meta.ts, ${surface} → ${replacement}) and by the upgrade guide (build-upgrade-guide.ts), so acceptanceCriteria does not cover it. Fix: scope the carve-out to a person's or on-behalf-of call and add the account rule, as tool.zod.ts and acceptanceCriteria do.
  • nit N2: "an agent acting on a person's behalf with an API key" — the onBehalfOf principal is the OAuth-delegated client (assemble-execution-context.ts), not an API-key holder. The gating claim is unaffected.

Docs row 57 — agrees with the tree (flowStartRefusal returns null for isSystem before rule 2; trigger route and declared endpoint pass { aiExposed: false }).

Against ruling C (6812617393), ADR-0151 and PR #32912: no sentence contradicts them; nothing widens or narrows a door.

Implemented-by: claude/project-thread-4nuxb0
Reviewed-by: subagent of session_01L9qibvfX5bawZKtfa6u7j9

VERDICT: PASS — one should-fix (S1) earns a delta review of its fix; two nits; conditional on Test Core (7/8) and (8/8) completing green.

… a person's or on-behalf call

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibyxGyX6L3NhaTF6yiA
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#2 · os-tesla · session_01L9qibvfX5bawZKtfa6u7j9

Contract review

Delta round of record 6846718400

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a8f3da02c48a08873d486c6bfbfaae2e940789f6
Local-runs: none
Scope: delta d086c4cf..a8f3da02 — one commit, one file: packages/spec/src/migrations/entries/semantic/17.tool-requires-confirmation-retired.ts (hit; replacement and acceptanceCriteria strings). Read, not reviewed: packages/runtime/src/action-dispatch.ts, action-execution.ts, flow-start-admission.ts, domains/{actions,mcp}.ts, packages/core/src/security/assemble-execution-context.ts. tool.zod.ts, docs row 57 and the changeset are untouched by the delta.

① Derived judgments

  • Accept sets: none moved — right. Two string literals in one SemanticMigration entry; no schema, key or export changes.
  • Public surface: the wording of the published replacement and acceptanceCriteria changes — right, and intended.
  • Check-runs on a8f3da02 at review time: 59 success, 12 skipped, 1 in progress (one Check Changeset run; the other is success). Test Core 1–8/8 and its aggregate, Build Core, Lint & Repo Gates, TypeScript Type Check success. No failure.

② Semver level

Unchanged: @objectstack/spec patch, Clause-②: no. The changeset's sentence that the migration guidance now says an account is already refused is now true of both fields.

③ Boundary flags

Nothing new. Item 2 of the card is neither implemented nor decided. The escalation in record 6846718400 stands: item 2's premise is overtaken by the tree, which already refuses an AI service account 428 at REST /actions.

④ Design judgment

17.tool-requires-confirmation-retired.ts

  • S1 — resolved. replacement now says REST /actions "sits outside the gate for a person's call or one an agent makes on a person's behalf, while an AI service account acting for itself is refused a gated action at every action door, REST /actions included". Person or on-behalf-of call outside the gate on REST: true (posture: 'rest'; admitResolvedAction asks actionConfirmationRefusal only under 'ai' or for isAiServiceAccountCaller). Account refused at every action door: true. The field now stands on its own where os migrate meta and the upgrade guide print it alone. The retained first clause still holds (flowStartRefusal rule 2).
  • N2 — resolved. acceptanceCriteria drops "with an API key"; it matches the code for both caller kinds.
  • The two fields agree with each other and with tool.zod.ts. N1 (MCP resume_run not named) is carried as a nit.

No must-fix, no should-fix in the delta.

Implemented-by: claude/project-thread-4nuxb0
Reviewed-by: subagent of session_01L9qibvfX5bawZKtfa6u7j9

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#2 · os-tesla · session_01L9qibvfX5bawZKtfa6u7j9

维护者速读

  • 改了什么:ai.requiresConfirmation 的说明文字和 17 版语义迁移条目(os migrate meta 与升级指南打印的那段)改正:REST /actions 只在「AI 服务账号经 flow 动作启动 runAs: 'system' 流程」时读 ai.exposed(只要授权、不要确认);代表某个人的调用在 REST 上仍不过确认闸,AI 服务账号自身在每道动作门都会被拒。权限文档 system-context 第 57 行补上同一规则。
  • 为什么改:PR fix(runtime, service-automation)!: an AI service account starts a flow that runs as the system only through an action its author opted into AI #32912 已随 18.4.0 改了 REST 行为,原句发出去会误导写 AI 动作的作者和 AI。
  • 风险与回滚:只改文字,接受/拒绝集不变(Clause-②: no),spec patch;回滚即 revert。
  • 先看:packages/spec/src/migrations/entries/semantic/17.tool-requires-confirmation-retired.ts 的 replacement 一句。
  • 两分钟验证:pnpm --filter @objectstack/spec exec vitest run src/ai/tool-confirmation-prescription-tense.pin.test.ts,预期全绿。
  • 需要你做的:本 PR 无。卡上第 2 项(REST 是否也要 confirm: true)待你定夺;复审发现代码其实已经在 REST 上拒绝 AI 服务账号(428),比选项 B 更严,见记录 6846718400 ③。

席位意见:可以落地。

席位核对

ACCEPT · seat domain:spec#2 · head a8f3da02 · 2026-11-13T07:46Z

  1. Shape: Part of #32924 (item 1 only; no closing keyword anywhere in the body), draft, base main, 4 files, +36/−11; changeset .changeset/32924-requires-confirmation-describe.md (@objectstack/spec patch). Clause-②: no, Metadata-protocol: none, matching claim 6845498057 and surface amendment 6846193569.
  2. Premise: PR fix(runtime, service-automation)!: an AI service account starts a flow that runs as the system only through an action its author opted into AI #32912 merged; REST /actions reads ai.exposed for the AI-service-account runAs: 'system' flow pair (flowStartRefusal rule 2), and admitResolvedAction (packages/runtime/src/action-dispatch.ts:460) refuses an AI service account a gated action at REST (seat re-read on origin/main).
  3. Contract review: owed at CONTRACT_REVIEW_TIER (packages/spec/src/** non-test). Record 6846718400 on d086c4cf PASS with S1 + N1, N2; delta 6847209749 on a8f3da02 PASS (S1, N2 resolved). Both by an isolated subagent; the seat spot-checked the reworded replacement (lines 18–21) and acceptanceCriteria (line 67) at this head. Prose faces checked against the diff: the changeset's claim that the guidance now says an account is refused (true of both fields at this head); docs row 57's AI-service-account sentence (agrees with flowStartRefusal, per 6846718400).
  4. Gates / check-runs: dev: 103 derived gates exit 0 at a8f3da02 (out/32924-gates-ran-a8f3da02.list). On head a8f3da02: 72 runs, 60 success, 12 skipped, 0 failed, 0 running; mergeable_state clean. The red Test UI / Type Check on 4379cff9 were concurrency cancellations (dev logs), not failures. K = 7 since base c54a23d2: under 15, no new locale pack.
  5. Accepted deviations and out-of-scope findings: N1 (MCP resume_run not named among enforcing doors; understates enforcement) | Acceptance notes. The migration entry flagged by the dev | fixed here under amendment 6846193569. Item 2 of spec: ai.requiresConfirmation's describe says REST /actions reads no ai.exposed opt-in, which PR #32912 makes untrue for an AI service account starting a runAs:system flow action #32924 | stays open on the card for the maintainer, with record 6846718400 ③'s finding that its premise is overtaken.
  6. Landing: not governed (check-governed-merges --pr 33056: 0 of 4 paths), 47 changed lines. The seat readies the PR and arms auto-merge under the maintainer's standing words 「车道经理应该使用 pm-dispatch 技能审核和落地pr」 and 「你应该是使用 pm-dispatch 技能自主审核和落地 pr」. On MERGED: spec: ai.requiresConfirmation's describe says REST /actions reads no ai.exposed opt-in, which PR #32912 makes untrue for an AI service account starting a runAs:system flow action #32924 stays open (Part of); pm:dispatched comes off with a note handing item 2 to the maintainer.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 13, 2026 07:46
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 13, 2026
Merged via the queue into main with commit ccaee99 Nov 13, 2026
73 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-4nuxb0 branch November 13, 2026 08:09
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/s tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants