Skip to content

docs(spec): align the skill.tools docblock with ADR-0109's authoring model - #10669

Merged
os-elon merged 2 commits into
mainfrom
claude/issue-10356-skill-tools-docblock
Aug 21, 2026
Merged

docs(spec): align the skill.tools docblock with ADR-0109's authoring model#10669
os-elon merged 2 commits into
mainfrom
claude/issue-10356-skill-tools-docblock

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Fixes#10356

The SkillSchema.tools docblock in packages/spec/src/ai/skill.zod.ts recommended the exact shape ADR-0109 rejected, in the highest-traffic place an AI author reads while writing skill.tools[]:

Tools should also be registered as first-class metadata (type: 'tool') unless they are dynamically materialised at runtime.

Two defects in one sentence. It advises authoring a tool record per exposed tool — ADR-0109's second rejected alternative, "a required tool record per exposed action", whose cost the ADR states precisely: "a second authoring step, a second namespace to keep consistent, and a second surface for AI authors to hallucinate into — for zero added capability." And it inverts the exemption, treating the materialised path as the exception when ADR-0109 makes it — together with the platform registry — the rule.

It was also actively misleading: per ADR-0109 Decision 4, a stack.tools record is the Phase-2 refinement layer with no runtime reader until that phase lands, so a reader who followed the advice authored inert metadata and believed it did something. Meanwhile the sibling docblock over stack.zod.ts's tools already stated the correct model — the package shipped two contradictory answers to the same question.

The change

Prose only. The replacement mirrors the resolution universe @objectstack/lint's validate-ai-tool-references actually implements (its header states the same ladder), rather than restating the ADR from memory:

  • a tool record is never required, and the default third-party path declares none;
  • a name resolves against stack.tools[] names ∪ PLATFORM_PROVIDED_TOOL_NAMES ∪ the materialised action_<name> family;
  • the action_<name> half is qualified as the validator qualifies it — ai.exposed + ai.description on a headless action type (ADR-0011), the follow-up item that keeps the rule from blessing references the agent could never call;
  • stack.tools is named as the optional Phase-2 refinement layer with no runtime reader, so a record authored today is inert.

Scope

Strictly the one docblock. Every changed line is a comment line — verified mechanically, not by eye:

git diff -U0 packages/spec/src/ai/skill.zod.ts | grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' | grep -vE '^[+-]\s*\*'
→ no output (every changed line matches a leading '*')

No schema shape, no .describe() text, no code path, and no authorable-surface movement — pnpm --filter @objectstack/spec check:generated reports all 14 generated artifacts up to date, check:authorable-surface among them. stack.zod.ts is read-only reference here (held by another in-flight card) and ToolSchema.outputSchema is untouched — a tracked ADR-0049 item the issue explicitly excludes.

One correction worth flagging

The changeset first claimed this text reaches consumers through the generated .d.ts. A real DTS build disproved it: property-level comments inside the z.object({ … }) literal are dropped from the emitted declarations (the built chunk carries a bare tools: z.ZodArray<z.ZodString>;). It ships instead through @objectstack/spec's files list, which publishes src/**/*.zod.ts — the docblock travels in the npm tarball as source. The changeset now states the measured fact; graded patch.

Verification — gate union pinned to 9259145cb (the final commit)

pnpm --filter @objectstack/spec test416 test files, 11097 tests passed, and pnpm --filter @objectstack/spec typecheck clean (check:test-typecheck: OK), both via the shared verify lock.

Gate families re-derived against the actual diff with node scripts/pm/dispatch-gates.mjs (which reads the change set from the merge base itself) — 19 families, all green:

check:changeset-gate-self-tests · check:cross-package-test-inputs (+ the ci.yml script form) · check:doc-formula-expressions · spec check:empty-state / check:liveness / check:strictness-ledger / check:variant-docs / check:generated · check:merge-driver · check:objectui-changeset · check:slot-lookup · check:spec-parsed-alias · check:test-source-alias · check:type-source-resolution · check-adr-0087-registration · check-changeset-no-major · check-empty-changeset · check-affected-docs · check-nul-bytes.

One declared narrowing:node scripts/check-dev-prereqs.mjs exits 1 with ✗ The workspace is not built — 1 unmet precondition, listing 65 of 67 packages with no dist/ on disk. That is an environment precondition needing a full pnpm build, not a property of this diff — it counted 67 before I built @objectstack/spec and @objectstack/formula and 65 after, exactly the two, and would fail identically on a clean origin/main checkout in this container. CI builds the workspace and runs it for real. Two other reds resolved to the same cause and are green above once the dependency was built: check:doc-formula-expressions (missing @objectstack/formula/dist) and check:generated's api-surface "stale" verdict — the generator refuses to write a baseline from an unbuilt dist, and after the build gen:api-surface regenerated to a no-op.

Related: #10355 — same ai-docs family (different surface: that one is docs, this one is spec source). No shared files.


Generated by Claude Code

…model
The `SkillSchema.tools` docblock told authors that tools "should also be
registered as first-class metadata (type: 'tool') unless they are dynamically
materialised at runtime" — the shape ADR-0109 explicitly rejected, with the
exemption inverted: it treated the materialised path as the exception when
ADR-0109 makes it, together with the platform registry, the rule. The sibling
docblock over `stack.zod.ts`'s `tools` already stated the correct model, so the
package shipped two contradictory answers to the same question.
The text now mirrors the resolution universe `validate-ai-tool-references`
implements: a `tool` record is never required, the default third-party path
declares none, and a name resolves against `stack.tools[]` names, the
`PLATFORM_PROVIDED_TOOL_NAMES` registry, or the materialised `action_<name>`
family. It also records that `stack.tools` is the optional Phase-2 refinement
layer with no runtime reader until that phase lands.
Prose only — no schema shape, no `.describe()` text, no code path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
Measured after a real DTS build: property-level comments inside the
`z.object({ ... })` literal do NOT survive into `dist/*.d.ts` (the emitted
declaration is a bare `tools: z.ZodArray<z.ZodString>;`). The text ships instead
through `@objectstack/spec`'s `files` list, which publishes `src/**/*.zod.ts`,
so the docblock travels in the npm tarball as source.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/ai/skills.mdx(via SkillSchema (symbol))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx(via SkillSchema (symbol))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 126 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 7bf3fb78cd17a7846c11c651f57dd77f9a998430packageMentionDocs.

Which tree this was computed on

This run read content/docs from c5da28b3b8e19d139d8e27e6489996bc9ac48ed8 — the merge of head 9259145cb80c68eeefdf5ff3ad19d90406cbc581 into base 7bf3fb78cd17a7846c11c651f57dd77f9a998430, 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 c5da28b3b8e19d139d8e27e6489996bc9ac48ed8 && git checkout c5da28b3b8e19d139d8e27e6489996bc9ac48ed8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7bf3fb78cd17a7846c11c651f57dd77f9a998430 9259145cb80c68eeefdf5ff3ad19d90406cbc581 && git checkout -B drift-repro 7bf3fb78cd17a7846c11c651f57dd77f9a998430 && git merge --no-ff 9259145cb80c68eeefdf5ff3ad19d90406cbc581
node scripts/docs-audit/affected-docs.mjs --json 7bf3fb78cd17a7846c11c651f57dd77f9a998430

⚠️ 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 7bf3fb78cd17a7846c11c651f57dd77f9a998430 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tooling protocol:ai labels Aug 21, 2026
@os-elon
os-elon marked this pull request as ready for review August 21, 2026 12:05
@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
@os-elon
os-elon added this pull request to the merge queueAug 21, 2026
Merged via the queue into main with commit 5f2e54cAug 21, 2026
33 checks passed
@os-elon
os-elon deleted the claude/issue-10356-skill-tools-docblock branch August 21, 2026 12:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:aisize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[spec] skill.zod.ts's tools docblock still tells authors to register a tool record — the opposite of ADR-0109's default path

2 participants

@os-elon@claude