Found while writing content/docs/ai/tools.mdx (#10222). That card must not touch packages/spec/**, so filed rather than fixed there.
The defect
packages/spec/src/ai/skill.zod.ts, in the docblock over the tools field, says:
Tools should also be registered as first-class metadata (type: 'tool') unless they are dynamically materialised at runtime.
ADR-0109 decided the opposite, and the sibling docblock in the same package says so. packages/spec/src/stack.zod.ts, over tools:
tools: OPTIONAL refinement layer, never required (ADR-0109). The default third-party path declares no tool records: a skill's tools[] names a platform-registered tool (PLATFORM_PROVIDED_TOOL_NAMES) or a tool the runtime materialises from the app's own declarative actions […] A stack.tools record exists only for AI-presentation refinement (Phase 2 […]) and has no runtime reader until that lands.
So one docblock says a skill's tools "should also" be registered as tool records, and the other says the default path declares none. The first also inverts the exemption: it treats the materialised path as the exception, when ADR-0109 makes it (together with the platform registry) the rule.
Why it matters
This is the docblock an AI author reads while writing skill.tools[] — the exact surface ADR-0109 was written to keep clean. ADR-0109's rejected alternative "a required tool record per exposed action" names the cost 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. This sentence recommends that shape in the highest-traffic place.
It is also actively misleading now: a stack.tools record has no runtime reader until Phase 2 lands, so a reader who follows the advice authors inert metadata and believes it did something.
Suggested fix
Rewrite the sentence to match ADR-0109 and the stack.zod.ts docblock — the resolution universe for skill.tools[] (stack tool names, the platform registry, the materialised action family) is already stated correctly in packages/lint/src/validate-ai-tool-references.ts and can be mirrored. Prose-only; no schema change and no authorable-surface impact.
Also checked, and NOT part of this
ToolSchema.outputSchema is declared [EXPERIMENTAL — not enforced] with liveness references (#1878/#1893) and is classified in the liveness ledger — pnpm --filter @objectstack/spec check:liveness passes. So it is a tracked ADR-0049 item, not an undeclared inert key, and is deliberately not bundled here.
Evidence
packages/spec/src/ai/skill.zod.ts — the tools field docblock.packages/spec/src/stack.zod.ts — the tools entry in the ObjectAI docblock, and the field's own .describe(...).docs/adr/0109-ai-tool-authoring-model.md — TL;DR, Decision 1 and 4, and the second rejected alternative.
Found while writing
content/docs/ai/tools.mdx(#10222). That card must not touchpackages/spec/**, so filed rather than fixed there.The defect
packages/spec/src/ai/skill.zod.ts, in the docblock over thetoolsfield, says:ADR-0109 decided the opposite, and the sibling docblock in the same package says so.
packages/spec/src/stack.zod.ts, overtools:So one docblock says a skill's tools "should also" be registered as
toolrecords, and the other says the default path declares none. The first also inverts the exemption: it treats the materialised path as the exception, when ADR-0109 makes it (together with the platform registry) the rule.Why it matters
This is the docblock an AI author reads while writing
skill.tools[]— the exact surface ADR-0109 was written to keep clean. ADR-0109's rejected alternative "a required tool record per exposed action" names the cost 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. This sentence recommends that shape in the highest-traffic place.It is also actively misleading now: a
stack.toolsrecord has no runtime reader until Phase 2 lands, so a reader who follows the advice authors inert metadata and believes it did something.Suggested fix
Rewrite the sentence to match ADR-0109 and the
stack.zod.tsdocblock — the resolution universe forskill.tools[](stack tool names, the platform registry, the materialised action family) is already stated correctly inpackages/lint/src/validate-ai-tool-references.tsand can be mirrored. Prose-only; no schema change and no authorable-surface impact.Also checked, and NOT part of this
ToolSchema.outputSchemais declared[EXPERIMENTAL — not enforced]with liveness references (#1878/#1893) and is classified in the liveness ledger —pnpm --filter @objectstack/spec check:livenesspasses. So it is a tracked ADR-0049 item, not an undeclared inert key, and is deliberately not bundled here.Evidence
packages/spec/src/ai/skill.zod.ts— thetoolsfield docblock.packages/spec/src/stack.zod.ts— thetoolsentry in the ObjectAI docblock, and the field's own.describe(...).docs/adr/0109-ai-tool-authoring-model.md— TL;DR, Decision 1 and 4, and the second rejected alternative.