Skip to content

docs(ai): add the tool-record guide, routing readers to skills and materialised action tools first - #10358

Merged
os-elon merged 1 commit into
mainfrom
claude/issue-10222-tool-metadata
Aug 21, 2026
Merged

docs(ai): add the tool-record guide, routing readers to skills and materialised action tools first#10358
os-elon merged 1 commit into
mainfrom
claude/issue-10222-tool-metadata

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Fixes#10222

Notation note. This body writes the materialised tool name as action_{name} in braces, because this repo's body sanitizer eats angle-bracket-delimited text (it silently truncated the issue body when the card was filed). The real spelling in the source and in the page itself uses angle brackets.

What this adds

tool is an authorable metadata kind with a generated field reference (/docs/references/ai/tool) and no guide. The gap was not "a page is missing" — it was that a developer asking "how do I give my agent a new tool" had nowhere that told them the answer is usually not a tool record.

So content/docs/ai/tools.mdx opens with the decision rather than the shape:

  1. Three ways a capability reaches an agent — a platform tool named by a skill, an Action materialised as an action_{name} tool, or an authored tool record — with a table saying which is right and answering "tool record?" with No for the first three.
  2. Why the default path is the default — AI capability equals application capability (same executable, same permission checks, same audit), one less namespace to hallucinate into, and ai-skill-tool-unresolved catching fictional names at authoring time.
  3. When a tool record is the right answer — the five refinement cases ADR-0109 names, and nothing beyond them.
  4. What a tool record does today — an honest warn callout: ToolSchema has no implementation/handler, ADR-0109 Phase 2 has not landed, stack.tools has no runtime reader. What it does do is narrower and stated exactly.
  5. The declaration shape and how the name is resolved, for the reader who has established they need it.

A reader who leaves after section 1 is on the correct path. That was the acceptance criterion, and it is why the shape is deliberately the fifth thing on the page rather than the first.

Files

FileChange
content/docs/ai/tools.mdxnew page
content/docs/ai/index.mdxmodule list gains the page; the sentence that presented defineAgent / defineTool / defineSkill as coequal now places them per ADR-0063 §2 + ADR-0109
content/docs/ai/meta.jsonone sidebar entry, after actions-as-tools
.claude/workflows/docs-accuracy-audit.jsone line — declared deviation, see below

actions-as-tools.mdx and skills.mdx are cross-linked, not edited. Nothing under packages/spec/** is touched.

Declared deviation — and why this PR is human-merge-only

Adding a hand-written page turns check:docs-audit-scope RED until the audit scope is regenerated. The one added line is exactly what node scripts/docs-audit/check-audit-scope.mjs --write produced (+1 added, -0 removed) and was not hand-edited:

 "content/docs/ai/skills.mdx",
+ "content/docs/ai/tools.mdx",
"content/docs/api/client-sdk.mdx",

.claude/** is a governed surface (#9866), so this PR is human-merge-only. It stays draft: not flipped ready, not enqueued, no auto-merge.

Verification — all at 8b81c40c, the final commit

Gate set re-derived from the actual diff with node scripts/pm/dispatch-gates.mjs (no path arguments — it reads the change set from the merge base itself). That added three families the dispatch list did not name (check:doc-formula-expressions, check:pm-governed-merges, check:skill-frame-sync), and all three were run.

GateVerdict line
check:doc-anchors253 internal #fragment link(s) across 400 source file(s) all resolve to a real heading
check:doc-authoring381 files clean — no bare metadata literals
check:docs-audit-scopescope is in sync with content/docs/: 181 hand-written doc(s)
check:docs-redirectsOK (92 entries -- 89 page destination(s) resolved ...)
check:published-readme-links152 outbound link(s) across 60 published markdown file(s)
check:role-wordOK, no new occurrences of the reserved word
check:cross-package-test-inputsOK: 12 package(s) read outside themselves, all declared
check:nul-bytesOK (scanned 6096 text file(s) ... no raw ASCII control bytes)
check:empty-stateall classified (1 closed, 2 open, 4 output, 9 scope)
check:livenessevery governed-type property ... is classified
check:strictness-ledger61 file(s) across 5 triaged director(ies)
check:variant-docs18 discriminated union(s) — 8 governed, 10 exempt
check:skill-examples211 prose examples type-check against @objectstack/spec
check:doc-formula-expressions22 record-scoped formula example(s) across 407 files / 1417 TS blocks judged clean
check:pm-governed-mergesself-test: 81 assertions
check:skill-frame-sync4 copies of the decision frame are structurally isomorphic

Both TypeScript examples are type-checked, and that was falsified before it was trusted

The page's two code blocks carry {/* os:check */}, so check:skill-examples compiles them against the built @objectstack/spec declarations. The run names them: content/docs/ai/tools.mdx:55 and :161.

To confirm the green is a measurement and not a vacancy, the second block was mutated (label: 'Summarise Case',label: 42,; confirmed on disk — the removed text went 1 → 0 occurrences and the injected text 0 → 1) and the gate re-run. It went red on the mutated line:

content/docs/ai/tools.mdx:167:3
error TS2322: Type 'number' is not assignable to type 'string'.

The block was then restored from the commit (counts confirmed back at 1 / 0) and the gate re-run green. No spec source changed in either leg, so the built dist under test is the same tree throughout.

Two premises checked before writing, both reported rather than fixed here

  • ai/actions-as-tools.mdx covers only the automatic materialisation path — confirmed. It documents list_actions / run_action over MCP and the ObjectOS action_{name} runtime, and says nothing about authoring a tool record. The card's shape holds.
  • ai/agents.mdx already states that agent is platform-internal (ADR-0063 §2) — confirmed, in three places: the heading "You extend the platform with skills, not agents", the allowRuntimeCreate:false, allowOrgOverride:false sentence citing §2, and the closing warning callout. No fix needed.

Two accuracy defects were found in files this card must not touch, and are filed rather than fixed here:

Neither is addressed here, and neither is assigned.

Part of #10206


Generated by Claude Code

…10222)
`tool` is an authorable metadata kind with a generated field reference and
no guide. The gap is not "a page is missing" — it is that a reader asking
"how do I give my agent a new tool" had nowhere that told them the answer
is usually NOT a tool record.
So content/docs/ai/tools.mdx opens with the decision, not the shape: the
three ways a capability reaches an agent (a platform tool named by a skill,
an Action materialised as `action_<name>`, an authored `tool` record) and
when each is right. The declaration shape is below that, for the reader who
has established they need it, together with an honest statement of what a
record does today — ADR-0109 Phase 2 has not landed, so `stack.tools` has
no runtime reader and authoring one makes nothing runnable.
- ai/index.mdx: the module list gains the page, and the sentence that
presented `defineAgent` / `defineTool` / `defineSkill` as coequal now
places them per ADR-0063 §2 + ADR-0109 (skills are the primitive; agents
are platform-owned; a tool record is an optional refinement layer).
- ai/meta.json: one sidebar entry, after actions-as-tools.
- .claude/workflows/docs-accuracy-audit.js: one line, produced by
`node scripts/docs-audit/check-audit-scope.mjs --write` — a new
hand-written page must enter the audit scope or the gate goes red.
Both TypeScript examples carry `{/* os:check */}` and type-check against
the built @objectstack/spec.
Part of #10206
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GawRwpD44VwBDVy3hs77AX
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Aug 20, 2026
@os-elonos-elon added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 20, 2026 — with Claude
@os-elon
os-elon requested a review from os-zhuangAugust 20, 2026 16:34
@os-elon
os-elon marked this pull request as ready for review August 21, 2026 00:55
@os-elon
os-elon added this pull request to the merge queueAug 21, 2026
@os-elonClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landed by the PM under the maintainer's explicit instruction — audit record.

This is a governed PR (.claude/workflows/docs-accuracy-audit.js, the #9866 collision), so under the standing regime the merge is the maintainer's own act and their click is the review record. It is being landed by an agent instead, on instruction. This comment exists so the entry is legible in check-governed-merges rather than reading as an unexplained agent merge.

Provenance — who, what, where. The repository owner, in the PM session driving epic #10206, 2026-08-20. Authorization verbatim: 「你帮我处理合并吧」. Their per-PR verdict on this one, verbatim: 「可合」 — 「以「先别写 tool record」分流开篇,与 ADR-0109 现状诚实对齐」.

PM position, recorded rather than dropped. I stated before acting that an agent-executed governed merge removes the human click the fence treats as the review record, and that the audit would flag it. The maintainer instructed anyway. The decision is theirs; this is the record of it, not a silent reinterpretation of the rule.

Landing route. A direct merge is refused by repository rules (405 Changes must be made through the merge queue), so this PR was flipped ready and entered the merge queue via auto-merge — the only sanctioned path. It lands only if the full-suite queue build passes; the queue has been dequeuing on an unrelated @objectstack/example-showcase failure tonight (see #10253), so a dequeue here would not be this PR's defect.


Generated by Claude Code

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Aug 21, 2026
@os-elon
os-elon added this pull request to the merge queueAug 21, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 32441044667 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (4/6) — 失败步骤: Run this shard's tests

    × still ignores dot entries UNDER the root, including its own bookkeeping 1167ms
    FAIL test/watch-dot-root.test.ts > FileSystemRepository watcher — dot-rooted watch root (#7150) > still ignores dot entries UNDER the root, including its own bookkeeping
    ↳ 失败原因: AssertionError: expected [ 'view/seed', 'view/closer', …(2) ] to have a length of 3 but got 4
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

跨 PR 相同签名(24h,按失败测试文件聚合):

  • test/watch-dot-root.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 38 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-elonClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge-queue dequeue triage (PM, epic #10206). Verdict: not this PR's failure — requeued once, and only once.

The signature, and why it is not branch 1

FAIL test/watch-dot-root.test.ts > FileSystemRepository watcher — dot-rooted watch root (#7150)
> still ignores dot entries UNDER the root, including its own bookkeeping
↳ AssertionError: expected [ 'view/seed', 'view/closer', …(2) ] to have a length of 3 but got 4

The test lives in packages/metadata-fs. This PR's diff is four files — content/docs/ai/tools.mdx, content/docs/ai/index.mdx, content/docs/ai/meta.json, and the mechanical .claude/workflows/docs-accuracy-audit.js regeneration. There is no path by which a docs page reaches a FileSystemRepository watcher test, so triage branch 1 is ruled out by construction rather than by judgement.

An AssertionError rather than a timeout is normally the signature that does indicate a real behaviour change — which is why this is being written up rather than waved through as "probably flaky".

Hypothesis about the mechanism — stated as a hypothesis, not proven

The failure direction is one event too many (expected 3, got 4), not one too few. Reading the test source: the put() that seeds the case is followed by await sleep(400), commented as "past the 200ms self-write suppression window of the put above". If that self-write event is delivered later than 400 ms — which a saturated queue runner running the full six-shard suite can plausibly cause — it lands inside the collection window and becomes a fourth event.

That is consistent with the observed assertion, but I did not prove it: I have not reproduced it, and the file's own docblock argues at length that the case is not a function of runner speed ("it brackets the noise between an opener … and two closers … so nothing about it is a function of how fast the runner happens to be"). If that claim is right, my hypothesis is wrong and something else produced the extra entry. Either way it is not reachable from this diff.

Worth flagging for whoever owns metadata-fs: a case that documents itself as load-independent just failed under the heaviest load in the repo. That is either a real defect in the watcher or a gap in the bracket argument, and both are worth more than a re-run. Not filing a card — the triage workflow says test/watch-dot-root.test.ts has only been hit by this PR in 24h and will auto-open an aggregate issue on a second, different PR hitting it, so a hand-filed card now would race that mechanism.

Action

Requeued once. The discipline allows exactly one confirming re-run for a failure naming a surface the diff does not touch, and the triage comment's own history line confirms this is this PR's first queue failure in 24h. ⛔ A second failure will not be requeued — it would be real by definition, and I will stop and diagnose rather than burn another full-queue rebuild. The queue logged 38 other failed builds in the preceding 24h, so re-queuing is not free for anything behind it.


Generated by Claude Code

@os-elon
os-elon added this pull request to the merge queueAug 21, 2026
Merged via the queue into main with commit b34ef8dAug 21, 2026
24 checks passed
@os-elon
os-elon deleted the claude/issue-10222-tool-metadata branch August 21, 2026 03:57
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(ai): tool metadata has a reference and no guide — and the guide has to say when NOT to author one (ADR-0109)

1 participant

@os-elon