Skip to content

fix(spec): publish each skill reference's own module doc block, not the first block in the file - #12202

Merged
os-trump merged 2 commits into
mainfrom
claude/issue-12094-skill-refs-module-docblock
Aug 25, 2026
Merged

fix(spec): publish each skill reference's own module doc block, not the first block in the file#12202
os-trump merged 2 commits into
mainfrom
claude/issue-12094-skill-refs-module-docblock

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12094

build-skill-references.ts derived every _index.md pointer description from the first doc block anywhere in the source file. That is a rule about ORDERING, not about descriptions: whichever declaration happened to sit nearest the top of a .zod.ts donated its comment to a customer-facing page, and moving a helper up a file silently rewrote published text.

This is the defect the docs-site generator already fixed by converging on findModuleDocBlock() — the block must start at column 0, precede the first declaration, and document no symbol. The skill-references generator was never converted, so the two generators disagreed about the same sources. It now imports that selector rather than restating it: one selector, two consumers, no second port of the rule. The diff in the generator is the import plus the two lines that call it; everything else is the docblock explaining why.

skills/** is loaded whole into customer agent context windows, so it was paying the higher price for the same defect.

Census — every changed _index.md line, read line by line

13 pointer rows across 7 skills, covering 11 distinct source files (data/hook-body.zod.ts appears in 3 skills). No pointer row was added or removed and no ids reflowed — this diff is description text only.

Corrected: wrong block → Exports: fallback (12 rows, 10 files)

SkillSource fileWas published (the wrong block)Now
aiai/tool.zod.ts"Retired ToolSchema keys — the rejection carries the upgrade prescription,"Exports: ToolSchema
apiapi/realtime.zod.ts"Transport Protocol Enum" — one enum's doc on a 5-schema fileExports: TransportProtocol, RealtimeEventType, …
apiapi/contract.zod.ts"Machine-readable semantic code (ADR-0112): a StandardErrorCode member or" — a doc nested insideApiErrorSchemaExports: ApiErrorSchema, BaseResponseSchema, …
automationautomation/approval.zod.ts"Approval Step Approver Type"Exports: ApproverType, DEPRECATED_APPROVER_TYPES, …
data, platform, uidata/hook-body.zod.ts (×3)"Capability tokens a script body may request." — documents HookBodyCapabilityExports: HookBodyCapability, ExpressionBodySchema, …
i18nsystem/translation.zod.ts"Shared history sentence for every shape in this file." — the comment on the private TRANSLATION_HISTORY constExports: LocaleSchema, FieldTranslationSchema, …
platformkernel/manifest.zod.ts"Structured permission grants requested by a plugin (ADR-0025 §3.2)."Exports: PluginPermissionsSchema, …
platformkernel/plugin.zod.ts"Shared Plugin Types"Exports: PluginContextSchema, CORE_PLUGIN_TYPES, …
uiui/widget.zod.ts"Field Widget Props Schema"Exports: FieldWidgetPropsSchema
uiui/responsive.zod.ts"Style Map Schema (ADR-0065)"Exports: StyleMapSchema, ResponsiveStylesSchema

The i18n row is the live victim named on the issue, and it is the cleanest case: the sentence described an internal string constant used to build .describe() text, not the Translation protocol.

Gained: Exports: → a real module description (1 row)

SkillSource fileWasNow
platformshared/metadata-types.zod.tsExports: MetadataFormatSchema, BaseMetadataRecordSchema"Supported metadata file formats"

Its block was previously invisible to the old selector, which required a newline immediately after the opening delimiter and so never matched a one-line block. Worth noting for review: the shared selector accepts it because an import line sits between the block and the schema, and imports do not close the header zone. That is the shared rule's existing, documented behaviour and the docs site already publishes this exact sentence for the same file (content/docs/references/shared/metadata-types.mdx), so this row makes the two surfaces agree rather than inventing a third answer.

Census completeness — cross-checked, not asserted

An independent reimplementation of the new selection was run across all 208 .zod.ts sources before the change and predicted 17 files would change repo-wide. 11 of those are reachable from SKILL_MAP and all 11 changed here; the other 6 (ai/solution-blueprint, api/protocol, api/router, data/mapping, shared/error-map, system/notification) were each verified to appear in zero skill indexes. Nothing changed that was not predicted, and nothing predicted was silently missed.

The fallback-vs-loud-refusal question (issue scope item 3) — priced

This PR keeps the Exports: fallback and does not introduce a loud refusal.

The reasoning is the shared lib's own principle, 宁可缺,不要错: an export list states a true fact about the file, where the wrong block asserted a false one about its subject. Every one of the 12 corrected rows is therefore a net improvement even though 12 rows lose prose and only 1 gains it — the prose they lost was wrong.

A loud refusal was considered and rejected for this card: it would make "write a module doc block" an implicit prerequisite of a generator fix, red the gate on ~9 source files, and convert a contained repair into an authoring sweep. Whether a .zod.ts on this surface should be required to carry a module doc block is a real question and a separate decision — the census above names every file that reaches the fallback, which is exactly the input that decision needs.

One measured caveat on the fallback, filed rather than fixed here: its export list ranks by source order with no notion of public authorable surface, so DEPRECATED_APPROVER_TYPES, NON_AUTHORABLE_APPROVER_TYPES and LEGACY_OBJECT_FIRST_KEYS now appear as headline content on an authoring surface. That is honest but badly ranked. Filed as #12201 (observation, unassigned) — out of scope here, and #12094 anticipated it as its own follow-up.

Whole-package value density (governed surface)

Line counts are unchanged everywhere — this is a 1:1 description-line replacement (13 insertions, 13 deletions).

Readingbefore → afterdelta
Changed _index.md files — lines363 → 363+0
Changed _index.md files — tokens6,344 → 6,467+123
All 11 published SKILL.md (the check:skills-token-ratchet surface) — lines10,516 → 10,516+0
All 11 published SKILL.md — tokens117,719 → 117,719+0
Entire published catalog skills/** — lines17,899 → 17,899+0
Entire published catalog skills/** — tokens187,941 → 188,064+123 (+0.07%)

Tokens counted as ceil(utf8 bytes / 4), the convention scripts/check-skills-token-ratchet.mjs defines. The +123 tokens buys the removal of 12 false statements from a surface loaded whole into every customer session; the ratchet's own surface (SKILL.md) is untouched.

Per-file: ai −14, api +30, automation +25, data +10, i18n +21, platform +35, ui +16 tokens.

Verification

All gates below were run on the final commit, 4013d77, and each verdict is the gate's own printed line (exit codes captured before any pipe).

Gate family re-derived from the actual changed paths with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (which reads the change set from the merge base itself), not from the dispatch list.

✅ check:skill-refs 9 generated files in sync with packages/spec
✓ check:generated All 14 generated artifacts are up to date
✓ check:scripts-typecheck tsc --noEmit -p tsconfig.scripts.json — exit 0
✓ check:doc-authoring 390 files clean; 48 published skill files clean — no internal issue-id references
✓ check:skills-token-ratchet 11 published SKILL.md within their ceilings
✓ check:skill-compatibility 11 SKILL.md reconciled against 78 workspace packages
✓ check:skill-frame-sync 4 copies of the decision frame structurally isomorphic across 3 files
✓ check:agent-test-spelling 0 violations — 377 files
✓ check:role-word no new occurrences of the reserved word
✓ check-empty-changeset 1 declaring changeset added
✓ check-changeset-no-major introduces no `major` bump
✓ check-adr-0087-registration no declared-breaking changeset
✓ check:objectui-changeset / check:changeset-gate-self-tests self-tests pass

check:docs passing inside check:generated is the load-bearing negative result: the shared lib/file-description.ts is not modified, so the docs side is provably unperturbed by this convergence.

Declared narrowing: repo-wide scans that matched only because a file under packages/spec/** was touched (check:liveness, check:empty-state, check:strictness-ledger, check:variant-docs, check:slot-lookup, check:published-files, check:type-source-resolution, check:test-source-alias, check:merge-driver, check:dev-prereqs, check:plugin-teardown-shape, the docs-audit pair) were not run locally — the change is to a build script, not to spec source, and CI runs the farm in full regardless.

Notes for review


Generated by Claude Code

build-skill-references.ts derived every _index.md pointer description from
the first doc block anywhere in the source file — a rule about ORDERING, not
about descriptions. Whichever declaration happened to sit nearest the top of a
.zod.ts donated its comment to a customer-facing page.
Converge on the shared findModuleDocBlock() selector that build-docs.ts
already uses, imported rather than restated: one selector, two consumers. The
column-0 / precedes-first-declaration / documents-no-symbol rule now governs
both surfaces, so the two generators stop disagreeing about the same sources.
Thirteen pointer rows across seven skills change; skills/** artifacts are
regenerated, not hand-edited. The docblock-less fallback to the existing
"Exports: ..." line is kept deliberately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV
@github-actions

github-actionsBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 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 2c4c59ed20caf4f6cde3c979ae9205ee69e04256packageMentionDocs.

@os-trump
os-trump marked this pull request as ready for review August 25, 2026 15:25
@os-trump
os-trump added this pull request to the merge queueAug 25, 2026
Merged via the queue into main with commit 18c432eAug 25, 2026
35 checks passed
@os-trump
os-trump deleted the claude/issue-12094-skill-refs-module-docblock branch August 25, 2026 16:05
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

3 participants

@os-litant@os-trump@claude