Skip to content

Commit 8acbfab

Browse files
docs(skills): objectstack-ai MCP tools table teaches run_action/resume_run's confirmation gate and delete_records (#31088)
<!-- claude-projects-attribution: {"github_login":"huangyiirene"} --> _Requested by **Irene** · [project thread](https://claude.ai/code/project/chan_01TJXtQ1tVD26ecLDZxFS67x?thread=cmsg_01TJXtQ1tVD26ecLDZxFS67xVR81HtXEk46FDF6MRi3Qfk)_ Fixes #31080 Clause-②: no Metadata-protocol: none `dispatch-gates --tier` reads a MANDATORY model tier for `skills/**` (clause ① of the model-tiering ruling); it is waived by the standing order of the maintainer in this project (2026-11-03), and this card ran on the project's default model. The dispatching seat runs the contract review. ## 维护者速读 - 改了什么:`skills/objectstack-ai/SKILL.md` 的 MCP 工具表:动作行补上 `resume_run` 和确认门(带 `ai.requiresConfirmation` 的动作不带 `confirm: true` 即 428 拒绝、不执行);新增一行 `delete_records`(单对象、`where` 或 `recordIds` 二选一、每次至多 200 条、未确认的调用即预览不删除、OAuth 需 `data:read` + `data:write`)。 - 为什么改:表里只教了 `delete_record` 的确认门,读表的 agent 不知道 `run_action` / `resume_run` 也会被拒,也不知道已合入的 `delete_records`(#30891, PR #31024)。 - 风险与回滚:只改技能文字,净增 1 行,token 5563 → 5776(上限 6801 不动);发 `@objectstack/skills` patch;回滚即 revert。 - 先看:`skills/objectstack-ai/SKILL.md` L202–204,对照下文「Code sites」各行。 - 两分钟验证:`node scripts/check-skills-token-ratchet.mjs`,预期退出 0,`skills/objectstack-ai/SKILL.md is 5776 tokens (ceiling 6801)`。 - 需要你做的:`skills/**` 属 Tier H(人合),需要 `os-zhuang` 或 `hotlong` 的一次 approval,之后席位落地。 ## What changes `skills/objectstack-ai/SKILL.md`, MCP tools table. L202 (`create_record`, `update_record`, `delete_record`) is byte-identical to `main`. ```diff @@ -202,3 +202,4 @@ permissions and row-level security: | `create_record`, `update_record`, `delete_record` | writes; `delete_record` is refused with `ACTION_CONFIRMATION_REQUIRED` (428) unless it sends `confirm: true` — ask the human first, then retry | -| `list_actions`, `run_action` | your AI-exposed business Actions — the same `ai.exposed` opt-in as above | +| `delete_records` | up to 200 records of one object per call (fewer under a smaller `OS_DATA_MAX_PAGE_SIZE` or `OS_DATA_QUERY_ROW_BUDGET`), by `where` or `recordIds`, not both; a call without `confirm: true` is the preview (match count, sample ids) and deletes nothing — show it to the human, and send `confirm: true` only once they approve; over OAuth it needs `data:read` and `data:write` | +| `list_actions`, `run_action`, `resume_run` | your AI-exposed business Actions — the same `ai.exposed` opt-in as above; registered only when the bridge implements `listActions` and `runAction` (the stdio bridge does not, so it serves none), `resume_run` only when it also implements `resumeRun`. An action flagged `ai.requiresConfirmation` (`list_actions` reports it) is refused with the same 428 unless the call sends `confirm: true`, and nothing runs — ask the human first, then retry; `resume_run` submits a paused screen flow's form under the same gates | ``` The "ask the human first, then retry" stance stays on L202 and is repeated on both new rows; they add no gate the code lacks. Plus `.changeset/mcp-tools-table-confirmation-gate.md`: `'@objectstack/skills': patch`. ## Code sites (each taught fact, measured on `main` 24bc259) | Taught fact | Code site | |---|---| | The member is spelled `confirm` | `packages/spec/src/contracts/ai-service.ts:242` `AI_ACTION_CONFIRMATION_MEMBER = 'confirm'` | | `run_action`: an action with `ai.requiresConfirmation: true` is refused with `428 ACTION_CONFIRMATION_REQUIRED` unless the request carries `confirm: true` | `packages/runtime/src/action-execution.ts:2911` `actionConfirmationRefusal` (code and status at :2893, :2901) | | Nothing runs on a refused call | `packages/runtime/src/action-execution.ts:4513`: the gate is the last pre-dispatch check, before the subject record is read; ask the human first: tool description `mcp-http-tools.ts:2255-2256`, `packages/mcp/src/skill-md.ts` L294–317 | | The MCP tool declares and forwards the member | `packages/mcp/src/mcp-http-tools.ts:2309` (schema key), `:2362` (forwarded to `bridge.runAction`) | | `list_actions` reports `requiresConfirmation` | `packages/mcp/src/mcp-http-tools.ts:2255`, `:2313-2314` (tool descriptions) | | The action tools are registered only on a bridge with `listActions` and `runAction`; the stdio bridge has neither | `packages/mcp/src/mcp-http-tools.ts:1302` (`wireBridgeTools`); `packages/mcp/src/stdio-data-bridge.ts` implements no `listActions` / `runAction` | | `resume_run` is in the `registerActionTools` family, registered only when the bridge implements `resumeRun` | `packages/mcp/src/mcp-http-tools.ts:343-344` (interface: optional), `:2218-2220`, `:2382`; the runtime bridge implements it at `packages/runtime/src/domains/mcp.ts:1027` | | `resume_run` carries the same gate | `packages/runtime/src/domains/mcp.ts:1465` `resumeAdmissionRefusal` calls `actionConfirmationRefusal`; the tool's description (`mcp-http-tools.ts:2394-2396`) says a refused call leaves the run paused | | `delete_records` is registered by `registerObjectTools`, under `data:write` and `data:read` | `packages/mcp/src/mcp-http-tools.ts:1924` (`if (canWrite)`), `:2033` (`if (canRead)`), `:2057` | | One object, `where` or `recordIds`, not both | `packages/mcp/src/mcp-http-tools.ts:2120-2121` | | At most 200 per call, fewer under a smaller `OS_DATA_MAX_PAGE_SIZE` or `OS_DATA_QUERY_ROW_BUDGET` | `packages/mcp/src/mcp-http-tools.ts:632` `DELETE_RECORDS_MAX_PER_CALL = 200`, `:635-648` `deleteRecordsCap` | | The unconfirmed call is the preview (count, sample ids) and deletes nothing; show it to the human and confirm only once they approve | `packages/mcp/src/mcp-http-tools.ts:2062`, `:916`, `:925-933`; `.changeset/30891-mcp-delete-records.md` | ## Tests None: the diff is skill teaching text with no runtime behaviour to test. The gates below are the checks. ## Acceptance notes ### Token budget (`node scripts/check-skills-token-ratchet.mjs`, exit 0) | File | `main` | head | Ceiling | |---|---|---|---| | `skills/objectstack-ai/SKILL.md` | 5563 | 5776 | 6801 (unchanged) | Net lines +1 (+2 / −1) across both commits. The second commit (review fixes) rewrites only the two new rows. No ceiling moves and no payment was needed (headroom 1238 before, 1025 after). `main` moved 4 commits after the branch point; none touches this file or the ratchet. ### Eval sweep - `skills/objectstack-ai/evals/skills-tools-knowledge.json`: no case names `run_action`, `resume_run`, `delete_record(s)`, `confirm`, `requiresConfirmation` or the MCP tools list. Nothing to fix. - Other copies of the MCP tools list in `skills/**`: none. The `delete_record` hits in `skills/objectstack-automation/SKILL.md` L87 and `skills/objectstack-query/rules/filters.md` L193 are flow node types, not MCP tools. Left. ## Verification <details><summary>Local gates on 6a31227: 45 derived, 45 run with exit 0, 0 NOT MEASURED (`dispatch-gates --ran`)</summary> - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 45 commands (26 pnpm, 19 direct node) for the 2 changed paths. Each ran under a 900 s timeout with `NODE_OPTIONS=--max-old-space-size=8192`, its exit code captured before any pipe, after a full `pnpm build` (exit 0). All 45 exited 0 on the first run. - They include `check-skills-token-ratchet.mjs`, `check:skill-docs`, `check:skill-compatibility`, `check:skill-frame-sync`, `check:skill-identifier-liveness`, `check:doc-authoring`, `check:dts-closure`, `check:dual-build-cjs-loads`, `check-changeset-no-major.mjs`, `check-empty-changeset.mjs` 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)`. - Also run, outside the derived list: `pnpm check:changeset-fixed` (exit 0), the one artifact-roster family whose roster sits under `.changeset`. </details> - `node scripts/pm/check-governed-merges.mjs --branch claude/project-thread-qmr8o9`: GOVERNED, Tier H (人合): `skills/**` ×1; 13 changed lines (+12 / −1), under 3000. - `node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD`: exit 0 before every push. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (No schema touched.) - [x] 2. Every name I added uses the casing for its kind. (No names added.) - [x] 3. No formatter ran. (Markdown only.) - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. `skills/**` publishes `@objectstack/skills`, so the PR carries a `'@objectstack/skills': patch` changeset. Both declaration lines are filled in above. - [x] 6. `packages/spec` not touched; nothing to regenerate. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. (The 维护者速读 section is Chinese, per the lane template.) - [x] The *Before Submitting* list in CONTRIBUTING.md is done. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01J8VVAkL29APmM7okwgntB6 <!-- signed: domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7 --> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3c1eb63 commit 8acbfab

2 files changed

Lines changed: 12 additions & 1 deletion

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): objectstack-ai's MCP tools table teaches `run_action` / `resume_run`'s confirmation gate and the `delete_records` tool
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The table taught the `confirm: true` gate for `delete_record` only. It now says that an action flagged `ai.requiresConfirmation` is refused by `run_action` and `resume_run` with `428 ACTION_CONFIRMATION_REQUIRED` unless the call sends `confirm: true`, and lists `delete_records`: up to 200 records of one object per call, by `where` or `recordIds`, whose unconfirmed call is the preview and deletes nothing, and which needs `data:read` and `data:write` over OAuth.

‎skills/objectstack-ai/SKILL.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,8 @@ permissions and row-level security:
200200
| `aggregate_records` | GROUP BY totals; registered only when the data bridge implements `aggregate` |
201201
| `validate_expression` | build-accurate CEL check before a formula is authored into metadata |
202202
| `create_record`, `update_record`, `delete_record` | writes; `delete_record` is refused with `ACTION_CONFIRMATION_REQUIRED` (428) unless it sends `confirm: true` — ask the human first, then retry |
203-
| `list_actions`, `run_action` | your AI-exposed business Actions — the same `ai.exposed` opt-in as above |
203+
| `delete_records` | up to 200 records of one object per call (fewer under a smaller `OS_DATA_MAX_PAGE_SIZE` or `OS_DATA_QUERY_ROW_BUDGET`), by `where` or `recordIds`, not both; a call without `confirm: true` is the preview (match count, sample ids) and deletes nothing — show it to the human, and send `confirm: true` only once they approve; over OAuth it needs `data:read` and `data:write` |
204+
| `list_actions`, `run_action`, `resume_run` | your AI-exposed business Actions — the same `ai.exposed` opt-in as above; registered only when the bridge implements `listActions` and `runAction` (the stdio bridge does not, so it serves none), `resume_run` only when it also implements `resumeRun`. An action flagged `ai.requiresConfirmation` (`list_actions` reports it) is refused with the same 428 unless the call sends `confirm: true`, and nothing runs — ask the human first, then retry; `resume_run` submits a paused screen flow's form under the same gates |
204205

205206
---
206207

0 commit comments

Comments
 (0)