Repository navigation
Commit 8acbfab
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
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
200 | 200 | | |
201 | 201 | | |
202 | 202 | | |
203 | | - | |
| 203 | + | |
| 204 | + | |
204 | 205 | | |
205 | 206 | | |
206 | 207 | | |
| |||
0 commit comments