Skip to content

docs(skills): teach MCP delete_record confirm: true in the objectstack-ai tools table - #31010

Merged
os-zhuang merged 1 commit into
mainfrom
claude/project-thread-vi5tee
Nov 6, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/project-thread-vi5tee

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Irene · project thread

Fixes #30863

Clause-②: no
Metadata-protocol: none

dispatch-gates mandates the contract-review model tier for skills/** (clause ①). The maintainer's standing order in this project (2026-11-03) waives it; this PR was developed below that tier, and the dispatching seat runs the contract review.

维护者速读

  • 改了什么:objectstack-ai 技能 MCP 工具表 L202 一行:delete_record 不带 confirm: true 会被 ACTION_CONFIRMATION_REQUIRED(428)拒绝,agent 须先问人再重试;另加一个 '@objectstack/skills': patch changeset。
  • 为什么改:PR feat(mcp)!: delete_record refuses a call without confirm: true #30861 让 MCP delete_record 必须带 confirm: true,旧行只写"annotated destructive",照技能调用的 agent 会被拒。
  • 风险与回滚:只改教学文字,无 schema、键或行为变化;回滚即 revert。
  • Token 账:SKILL.md 5539 → 5563(上限 6801,余量 1238),无需付账;skills/** 净增行 0。
  • 先看:skills/objectstack-ai/SKILL.md L202 与 packages/mcp/src/mcp-http-tools.ts 的 delete_record 注册(描述与 confirm 成员)。
  • 需要你做的:无;席位负责复审与落地。

What changes

  • skills/objectstack-ai/SKILL.md L202, the MCP tools-table row for create_record, update_record, delete_record: "writes; delete_record is annotated destructive" becomes "writes; delete_record is refused with ACTION_CONFIRMATION_REQUIRED (428) unless it sends confirm: true — ask the human first, then retry".
  • .changeset/30863-skills-ai-delete-record-confirm.md: '@objectstack/skills': patch, with Clause-②: no and Metadata-protocol: none. No skip-changeset.

Measured on main at 8ae0ab9b0 (PR #30861 landed as c77dd17f26):

  • packages/mcp/src/mcp-http-tools.ts registers delete_record with the description "A call is REFUSED unless it also sends confirm: true — ask the human first, then retry; nothing is deleted on a refused call." Its schema declares the member as [AI_ACTION_CONFIRMATION_MEMBER] (confirm), an optional boolean, and the handler answers recordDeleteConfirmationRefusal with code ACTION_CONFIRMATION_REQUIRED and HTTP status 428.
  • packages/runtime/src/action-execution.ts: ACTION_CONFIRMATION_REQUIRED_CODE = 'ACTION_CONFIRMATION_REQUIRED', ACTION_CONFIRMATION_REQUIRED_STATUS = 428, the same refusal run_action answers.
  • The row's wording follows the server's own run_action / delete_record description rule ("send confirm: true — ask the human first, then retry"); no key or code is introduced that main lacks.

Eval sweep: skills/objectstack-ai/evals/skills-tools-knowledge.json is this skill's only eval file, and no case mentions delete_record, confirm or destructive. Nothing to change there.

Token ratchet (node scripts/check-skills-token-ratchet.mjs): skills/objectstack-ai/SKILL.md before 5539 tokens, after 5563 tokens, ceiling 6801 (headroom 1238). No ceiling moves, no deletion needed. Net lines in skills/**: 0 (one row replaced in place).

Why

After PR #30861, an agent that followed the skill's tools table sent delete_record without confirm and was refused. The skill now teaches the call the server accepts.

Verification

Local gates on 54666fa: 45 derived, 45 run with exit 0, 0 NOT MEASURED (`dispatch-gates --ran`)
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 45 commands for the 2 changed files. All ran after pnpm install --frozen-lockfile and a full pnpm build (119/119 tasks, exit 0), each under a 900 s timeout with NODE_OPTIONS=--max-old-space-size=8192 (heap headroom for this cloud container) and its exit code captured before any pipe.
  • All 45 exited 0, among them check-skills-token-ratchet.mjs, check:skill-docs, check:skill-identifier-liveness, check:skill-compatibility, check:skill-frame-sync, check:doc-authoring, check-empty-changeset.mjs, check-changeset-no-major.mjs, check:dts-closure, check:dual-build-cjs-loads and check-comment-mask-corpus.mjs.
  • --ran printed: 45 derived famil(ies) accounted for — 45 run, 0 NOT-MEASURED (a DERIVED zero — all 45 recorded an exit code and none of them is 3).
  • node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD: exit 0 before the push.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (No schema touched.)
  • 2. Every name I added uses the casing for its kind. (No names added.)
  • 3. No formatter ran. (Markdown only: no lintable source touched, so pnpm lint was not run.)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec not touched; nothing to regenerate.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English. (The 维护者速读 section is Chinese, per the lane template.)
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J8VVAmcenQmYUv4fvtB68m

…ack-ai tools table

The MCP delete_record tool refuses a call without confirm: true and answers
ACTION_CONFIRMATION_REQUIRED (428). The objectstack-ai skill's tools table
said only that delete_record is annotated destructive, so an agent following
it sent no confirm and was refused. The row now names the member, the
refusal code, and the ask-the-human-first step.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAmcenQmYUv4fvtB68m
@github-actions github-actions Bot added the size/s label Nov 6, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract-review-claim: session_01J8VVAj7CvjkGXvgXzd6RZ7 · head 54666fa · 2026-11-06T17:08Z

The seat review runs at CONTRACT_REVIEW_TIER in an isolated subagent. No review seat (lanes/review.md) is seated.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 6, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 54666faed39647037682b61f6b6a510b044e6c64
Local-runs: none
Scope: skills/objectstack-ai/SKILL.md + packages/mcp/src/mcp-http-tools.ts (delete_record registration, recordDeleteConfirmationRefusal, codedErrorResult, run_action / resume_run descriptions), packages/runtime/src/action-execution.ts (ACTION_CONFIRMATION_REQUIRED_CODE / _STATUS, actionConfirmationRefusal), packages/spec/src/contracts/ai-service.ts (AI_ACTION_CONFIRMATION_MEMBER), packages/mcp/src/skill-md.ts (the served MCP skill's delete_record line), scripts/check-skills-token-ratchet.mjs (the skills/objectstack-ai/SKILL.md row), .changeset/30863-skills-ai-delete-record-confirm.md, packages/skills/package.json, skills/objectstack-ai/evals/skills-tools-knowledge.json, and the rest of skills/** (swept for delete_record teaching)

① Derived judgments

The net diff against main (merge base 8ae0ab9b0, which is origin/main itself) is two files. One row of skills/objectstack-ai/SKILL.md is replaced in place (L202), and one changeset is added. No schema, export, metadata key, accept-set or runtime behaviour changes. The only published surface that moves is the @objectstack/skills catalog text.

  • Teaching surface (L202): right. The tools-table row for create_record / update_record / delete_record used to say only "delete_record is annotated destructive". It now says that delete_record is refused with ACTION_CONFIRMATION_REQUIRED (428) unless it sends confirm: true, and that the agent asks the human first and then retries. The skill now agrees with the accept-set the server has had since PR feat(mcp)!: delete_record refuses a call without confirm: true #30861 (c77dd17f26). Before this change, an agent that followed the row sent no confirm and was refused.
  • Accept-set implied: none new. The row introduces no argument, code or value that main lacks. confirm is AI_ACTION_CONFIRMATION_MEMBER (packages/spec/src/contracts/ai-service.ts:242). It is declared on the delete_record input schema as an optional boolean (mcp-http-tools.ts:1593). The handler gate admits only the boolean true (mcp-http-tools.ts:564).
  • Eval surface: unchanged, and right to stay unchanged. skills/objectstack-ai/evals/skills-tools-knowledge.json is this skill's only eval file. No case in it mentions delete_record, confirm, destructive or MCP write tools; its cases grade query_records / get_record / action tools in defineSkill / defineAgent authoring. So no stale eval remains in this skill.
  • Sweep of the rest of skills/**: clean. The remaining delete_record mentions are the flow node type, not the MCP tool: skills/objectstack-automation/SKILL.md L89 and L203, and skills/objectstack-query/rules/filters.md L193. No file outside this row teaches the MCP delete_record call, so nothing sends it without confirm.

② Semver level

The changeset holds '@objectstack/skills': patch, Clause-②: no and Metadata-protocol: none, and the PR body carries the same two declaration lines. This matches what the diff publishes.

  • @objectstack/skills (packages/skills/package.json) is the package that ships the skills/** tree as its dist catalog. The edited row is the only published byte that changes.
  • Clause-②: no is right: no spec or accept-set changes, so there is neither a widening nor a narrowing.
  • Metadata-protocol: none is right: no key, value, metadata type, meaning or default that an author can write changes.
  • patch is the level that none takes. No skip-changeset is used, which is correct because the diff publishes text from a released package.
  • No other package bundles this file. packages/spec/scripts/check-skill-examples.ts reads skills/** but is a check script, not a published artifact.

③ Boundary flags

  • PR body: contract-review tier waived for the dev, under the maintainer's standing order (2026-11-03). Answered: this record is the contract-tier review the PR body says the dispatching seat owes.
  • PR body: "需要你做的:无". No open question is put to the maintainer, and none remains open for this record.
  • PR body: 45 local gates, exit 0. Not relied on; this record reads the head's check-runs instead. At 17:11Z, 38 check-runs were on the head. 29 had finished: 18 success and 11 skipped, with no failure. 9 were still in progress: Lint · docs & code guards, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · ESLint, Type Check · source gates, Type Check · consumer gates, Test Core (1/8), Test Core (2/8) and Live E2E UI. The skill gates (token ratchet, check:skill-docs, identifier liveness and frame sync) ride in the Lint jobs that are still running, so their verdicts are not in yet. Among those already green: Check Changeset, Build Core, Dogfood Regression Gate, Governed Surface Queue Guard, Lint · PM dispatch-gates, PR-body guards, Type Check · workspace and Check PR Size. Landing still waits for the in-progress runs.
  • Follow-up, escalated and outside this card's declared surface (L202 only): the adjacent list_actions, run_action row (L203). It does not teach run_action's own confirm: true for an action whose author set ai.requiresConfirmation. The rest of SKILL.md names ai.requiresConfirmation only as an author-side gate (L154, L317) and never names the caller's confirm member. Now that L202 teaches the member for delete_record, an agent may read its absence on L203 as "run_action never needs it". The server's own run_action description does teach it, so this is a gap in the skill, not a refusal the skill causes. It is a candidate for a separate card. It does not block this PR.

④ Design judgment

Each claim in the new row was checked against the code on main.

  • The code is ACTION_CONFIRMATION_REQUIRED: holds. recordDeleteConfirmationRefusal (mcp-http-tools.ts:558-579) returns code: ACTION_CONFIRMATION_REQUIRED, typed as the registered ErrorCode. This is the same literal as the runtime's ACTION_CONFIRMATION_REQUIRED_CODE (action-execution.ts:2869), which run_action's door answers with.
  • 428 on the MCP path: holds as written, no finding. On MCP there is no HTTP 428 at the transport. The handler returns codedErrorResult, which is a tool error (isError: true) whose text is the JSON { error: { code, message, status, details } }, and the JSON-RPC exchange itself succeeds. But status in that envelope is ACTION_CONFIRMATION_REQUIRED_HTTP_STATUS = 428 (mcp-http-tools.ts:526), mirrored from the runtime's ACTION_CONFIRMATION_REQUIRED_STATUS = 428 (action-execution.ts:2877), so the agent does see "status":428 in the tool result it reads. "(428)" after the code is also the house shorthand for this refusal (http-dispatcher.ts:1376 writes "ACTION_CONFIRMATION_REQUIRED, 428"). The row puts the code first, and the code is what an agent branches on. So the parenthetical is accurate and not misleading.
  • The argument is exactly confirm and the value exactly true: holds. The schema key is [AI_ACTION_CONFIRMATION_MEMBER] = 'confirm', z.boolean().optional(), and the gate tests === true. Absent or false gets the coded refusal. The input is strict (strictToolInput), so a string "true" fails schema validation as an input error, which is also a refusal with nothing deleted. The row's literal confirm: true is therefore the only call that passes, which is exactly what it teaches.
  • "ask the human first, then retry": holds. It is verbatim from the server's own delete_record description (mcp-http-tools.ts:1581-1582: "A call is REFUSED unless it also sends confirm: true — ask the human first, then retry"). It is also verbatim from the server's run_action description (L1696-1697), which is the wording rule the card asks for. The refusal message says the same thing in its own words ("confirm with the human in the loop, then retry this call with 'confirm': true"). The served MCP skill text (packages/mcp/src/skill-md.ts:288-291) teaches the same call, so the two skill surfaces agree.
  • Scope of the claim: holds. The gate runs before the bridge for every object (mcp-http-tools.ts:1610, "no object is exempt"). The only earlier refusal is the system-object guard, which the row does not contradict. create_record / update_record stay ungated, as the row implies by naming only delete_record. The row drops the words "annotated destructive", but destructiveHint: true still stands on the tool and the new sentence subsumes it for an agent, so no fact the agent needs is lost.
  • Token and line budget: holds. The ratchet counts ceil(utf8 bytes / 4) after excluding the release-stamp line ( version: "18.3.0" plus newline, 20 bytes).
    • Head blob: 22272 bytes, 20 stamp bytes excluded, which is 5563 tokens.
    • main blob: 22176 bytes, 20 stamp bytes excluded, which is 5539 tokens.
    • Row ceiling: 6801 (scripts/check-skills-token-ratchet.mjs:369, unchanged by this PR). Headroom is 1238.
    • This matches the PR body's 5539 → 5563 / 6801 / 1238 exactly. The raw count without the exclusion is 5544 → 5568.
    • Lines: 421 → 421, so the net line change in skills/** is 0.

No must-fix and no should-fix. The new row holds against main's code and against the card's expected behaviour.

Implemented-by: claude/project-thread-vi5tee
Reviewed-by: session_01J8VVAj7CvjkGXvgXzd6RZ7

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

维护者速读

  • 改了什么: objectstack-ai 技能 MCP 工具表 L202 一行:delete_record 不带 confirm: true 会被 ACTION_CONFIRMATION_REQUIRED(428)拒绝,agent 须先问人再重试;另加 '@objectstack/skills': patch changeset。2 个文件,+9/−1。
  • 为什么改: PR feat(mcp)!: delete_record refuses a call without confirm: true #30861 让 MCP delete_record 必须带 confirm: true,旧行只写「annotated destructive」,照技能调用的 agent 会被拒(skills: objectstack-ai SKILL.md should teach delete_record confirm: true (after #30813) #30863)。
  • 风险与回滚: 只改教学文字,无 schema、键或行为变化;SKILL.md 5539 → 5563 token(上限 6801 不动),行数不变。可整 PR revert。
  • 先看: skills/objectstack-ai/SKILL.md L202。
  • 两分钟验证: grep -n "delete_record" skills/objectstack-ai/SKILL.md 只出一行,且含 confirm: true。
  • 需要你做的: skills/** 是受治理 Tier H 面,需 os-zhuang 或 hotlong 在 GitHub 上 Approve 一次。达档复核 6690778934 PASS,Head-sha 54666faed39647037682b61f6b6a510b044e6c64。批准后本席落地。
  • 席位意见: ACCEPT。

席位核对 — ACCEPT · domain:skills#3 · head 54666faed39647037682b61f6b6a510b044e6c64

  1. 形态: draft → main,Fixes #30863;'@objectstack/skills': patch,Clause-②: no,Metadata-protocol: none;作者 objectstack-fleet[bot];2 个文件,在认领面之内。
  2. 前提实测: 复核读 main 代码确认:delete_record 的拒绝码与 run_action 同为 ACTION_CONFIRMATION_REQUIRED,错误 JSON 带 status: 428;参数名就是 confirm,只收布尔 true;「ask the human first, then retry」与服务端工具描述逐字一致。该技能唯一 eval 文件不涉及 delete_record。
  3. 门禁(钉 head 54666fae): dev 派生门禁 45 条全部 exit 0;check-runs 41 条全部完成,28 success、13 skip、0 failure。check-governed-merges:Tier H(skills/**)。
  4. 接受的偏差: PR 正文速读「需要你做的:无」不对,本 PR 碰 skills/**,属 Tier H,以本评论为准。
  5. 范围外去向: 同表 L203 没说 run_action 对设了 ai.requiresConfirmation 的动作同样要 confirm: true,另立卡跟进(本卡面只到 L202)。Skills-docs: self。
  6. 落地条款: Tier H。保持 draft,挂 pm:awaiting-merge,请 os-zhuang、hotlong 审批;获批后本席过落地前检、清标、ready、auto-merge;维护者直合亦可。

@objectstack-fleet objectstack-fleet Bot added the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 6, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review November 6, 2026 17:38
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 6, 2026
@objectstack-fleet objectstack-fleet Bot removed the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 6, 2026
Merged via the queue into main with commit dc3adf3 Nov 6, 2026
50 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-vi5tee branch November 6, 2026 18:08
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 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.

skills: objectstack-ai SKILL.md should teach delete_record confirm: true (after #30813)

2 participants