Skip to content

docs(spec): publish the solution-blueprint module header instead of its export list - #16992

Merged
zhuangjianguo merged 4 commits into
mainfrom
claude/issue-15263-solution-blueprint-module-header
Sep 9, 2026
Merged

docs(spec): publish the solution-blueprint module header instead of its export list#16992
zhuangjianguo merged 4 commits into
mainfrom
claude/issue-15263-solution-blueprint-module-header

Conversation

@claude

@claude claude Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #15263

Clause-②: no
The change makes an already-written module header visible to the header selector.
No accept set moves, no export is added, removed or renamed, no schema arm, bound or
behaviour changes. The published skill-index row gains the sentence the file already
contained. 拉回已声明契约 ⇒ 常规档.

Split from the original single PR. The pointer-row pin and its
vitest.repo-tests.json declaration moved to #16994, because a change under
packages/spec/scripts/ is a change to the generator that the #11705
generated-artifact exception has to trust — carried here, the recompute that certifies
the regenerated index would be running this PR's own generator and could not certify
it. The pin is not weakened or dropped; it lands separately, after this PR.

The defect, in the content of the published line

Before — a true statement about the file that says nothing about its subject, on the one
row whose job is to send an agent to this source for exact field shapes:

- `node_modules/@objectstack/spec/src/ai/solution-blueprint.zod.ts` — Exports: BlueprintConditionSchema, BlueprintSummaryOperationsSchema, BlueprintFieldSchema, BlueprintObjectSchema, BlueprintViewSchema

After:

- `node_modules/@objectstack/spec/src/ai/solution-blueprint.zod.ts` — Solution Blueprint Schema (ADR-0033 §4 — plan-first authoring)

The public reference page content/docs/references/ai/solution-blueprint.mdx gained the same
header as its opening paragraph — it had been printing none at all.

The mechanism, and why the fix is in the source

findModuleDocBlock() requires as its third condition that a header-zone block document
nothing
, and "immediately followed by a declaration" tolerates only blank lines between. One
blank line separated this file's module header from the snake_case regex constant, so TSDoc's
own attachment rule made the header that constant's documentation and the selector disqualified
it. Both generators then fell through to their export-list fallback.

The one-line doc that constant always deserved is a comment, not a declaration, so the preamble
now ends there and the header becomes the module's own block.

The selector is untouched. Under its own rule it was deciding correctly — this is the same
root cause as #14441 pointing the other way. There the wrong block was published; here the right
block was suppressed and a machine-generated list took its place. That fix swapped a block; this
one gives an existing block the separator it needed.

Census — one file, not a family

The card made the re-grade conditional on how many files lose a header the same way, so the
count was measured first, by driving the real selector rather than restating its rule.

reading count
.ts sources under packages/spec/src scanned 1314
module header selected today 451
header-zone block suppressed by a following declaration (this card's mechanism) 89
…of those, *.zod.ts — the only set either generator reads 19
…of those 19, a genuine misjudgement (block is a module header, not the symbol's doc) 1

The 89 is the mechanical shape; it is not 89 defects. In 18 of the 19 shipped files the block
plainly documents the symbol it sits against — Transport Protocol Enum against
export const TransportProtocol, Shared history for this file (#4001). against AGENT_HISTORY,
Field Widget Props Schema against FieldWidgetPropsSchema. That is the selector doing exactly
the job it was built for. Only here does a module header sit against a constant it says nothing
about. 1 is below the "more than 3" re-grade trigger, so this stays one file at p3.

Controls on the census: ai/solution-blueprint.zod.ts is PRESENT in the hit set (it fires), and
api/analytics.zod.ts — whose header survives today via the banner clause — is correctly absent.
A first pass conflated two suppression mechanisms and counted 53 shipped files; the corrected
oracle asks the selector on a synthetic source where the only branch that can return null is the
declaration check, which separates this card's mechanism from the #13263/#13334 import clause
(34 files sit in that second group and are out of scope here).

Why a generator-only check could not stand in for a real acceptance test

Triage's own words, kept because they are the acceptance bar:

⛔ 验收必须断言发布出来的那一行内容,而不是「生成器跑通了」。…生成器自己看不见这一类 —— 它们忠实地复现了选择器。

check:skill-refs and check:docs each compare an artifact against the generator that wrote it,
and the generator reproduced the selector faithfully — so both were green throughout the defect.
That is why the acceptance evidence above is the content of the published row, and why the
pin in #16994 asserts that content directly rather than asserting that the generators ran.

Governed-surface verdict

gen:skill-refs writes into skills/**, which is on the Prime Directive #14 register. Measured
on this PR's exact final file list:

node scripts/pm/check-governed-merges.mjs --test \
  .changeset/solution-blueprint-module-header.md \
  content/docs/references/ai/solution-blueprint.mdx \
  packages/spec/src/ai/solution-blueprint.zod.ts \
  skills/objectstack-ai/references/_index.md

governed-surface predicate: 0 of 4 path(s) hit the register after 1 generated-artifact lift(s)
  ✅  NOT governed — ordinary queue landing applies to a PR with exactly this file list.
  ℹ️  generated-surface exception (#11705): skills/objectstack-ai/references/_index.md is a PURE
      REGENERATION — byte-equal to `pnpm --filter @objectstack/spec gen:skill-refs` recomputed on
      THIS tree (never a stored baseline), so this path does not govern the PR by itself.

exit 0. The lift depends on this PR containing no generator edit, which is exactly why the pin
was split out.

Verification

  • pnpm --filter @objectstack/spec check:generated — ✓ all 15 generated artifacts up to date, after gen:skill-refs and gen:docs. git status clean. (A first run reported api-surface/ stale; that was a stale dist in a freshly recreated worktree — it reads the built .d.ts, not src/. Green after a rebuild, with nothing regenerated.)
  • pnpm --filter @objectstack/spec test — 467 files / 13100 tests passed; typecheck ✓ including the test layer.
  • pnpm lint — ✓ over the whole repository, so no narrowing is claimed.
  • Gates green by name: check:cross-package-test-inputs, check:test-source-alias, check:nul-bytes, check:published-files, check:skill-examples, check:skill-frame-sync, check:skill-compatibility, check:skill-identifier-liveness, check:doc-anchors, check:docs-single-h1, check:type-check-coverage, check:type-check-debt, check-empty-changeset, check-changeset-no-major, check-adr-0087-registration.
  • Gate families derived from the real change set with scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, not from a hand-written list.

Changeset — both halves

Half 1: packages/spec's files[] includes src/**/*.zod.ts, and the edited file is one.
Half 2, measured rather than assumed — npm pack --dry-run --json on the deciding tree, the
@objectstack/spec tarball: src/ai/solution-blueprint.zod.ts is shipped, with 206
src/**/*.zod.ts entries as the positive control, and scripts/, skills/ and content/ ship
0 entries each as discriminating negative controls. So the skill index and the reference page
publish nothing, while the edited source does. ⇒ a patch changeset, not skip-changeset.

Nothing under prompts/ or llms.txt moved, and neither carries a per-file index row for this
source.

skills/** readings

before after net
skills/objectstack-ai/references/_index.md 43 43 0
whole published bundle (sum of every SKILL.md) 6853 6853 0

The row was replaced one-for-one and no SKILL.md moved at all, so no budget is consumed.

维护者速读(草稿)

改了什么

packages/spec/src/ai/solution-blueprint.zod.ts 里的 snake_case 正则常量补了一行文档注释。
连带重新生成了两个产物:objectstack-ai 技能索引里的那一行,和该文件的公开参考页。加一条 changeset。
源码实际改动就是 1 行。配套的 pin 测试拆到 #16994

为什么改

这个文件本来就写了模块头(ADR-0033 §4,plan-first 授权、apply_blueprint 展开),但它和后面的常量
之间只隔一个空行,按 TSDoc 的归属规则就成了那个常量的文档,于是被选择器判掉。发布出去的那一行退化成
机器生成的导出清单 —— 陈述的是真事,却对这个文件是干什么的只字未提,而这行恰恰是引导 AI agent 去读
源码取字段形状的那一行。信息量的损失,不是错误指引。

风险与代价(含回滚)

风险很低:改的是注释,运行期零影响,没有任何导出、schema 或契约移动。发布面上动的只有 npm 包里那份
源码的字节。回滚就是还原这一行注释再跑一次两个生成器。本 PR 触到 skills/**,但 #11705 生成物例外
已实测解除(见上方判定,exit 0)—— 前提是本 PR 不含生成器改动,这正是把 pin 拆走的原因。

席位意见

(留空,待席位填写)

你要做的

确认两点:① 发布出去的那一行现在说的是这个文件的主题,而不是导出清单;② 普查结论 —— 同机制在已发布的
19 个 *.zod.ts 里只有这一处是误判,其余 18 处选择器判得对,所以本卡维持 p3、维持"修这个文件"而不是
"修选择器"。若你认为 18 处里有哪一处其实也是误判,那就是重判信号,请指出来。


Generated by Claude Code

`findModuleDocBlock()` rejects a header-zone doc block that is immediately
followed by a declaration — TSDoc's own attachment rule, read back: such a
block documents that symbol. In `ai/solution-blueprint.zod.ts` a single blank
line separated the module header from `const SNAKE_CASE`, so the header was
read as that regex constant's documentation and disqualified. The generators
then fell through to the export-list fallback, and the published skill-index
row stated a true fact about the file while saying nothing about its subject:

  - `.../ai/solution-blueprint.zod.ts` — Exports: BlueprintConditionSchema, ...

Give `SNAKE_CASE` the one-line doc it always deserved. That is a comment, not
a declaration, so the preamble now ends there and the header — plan-first
authoring, the `apply_blueprint` expansion — becomes the module's own block.

The selector is untouched: it was deciding correctly under its own rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
The published row and the reference page both regain the file's own sentence:

  - `.../ai/solution-blueprint.zod.ts` — Solution Blueprint Schema (ADR-0033 §4 — plan-first authoring)

Neither generator can see this class on its own — `check:skill-refs` and
`check:docs` compare the artifact against the generator, and the generator
reproduces the selector faithfully, so a generator-only check passes on the
defect. The pin asserts the fact the artifact must state instead.

Two legs that fail differently: the SOURCE leg reds the moment the separator
between the header and `SNAKE_CASE` goes away; the CORPUS leg stays green
through that and reds once an index is regenerated from a file whose header no
longer qualifies. Both directions measured.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
The pin reads `skills/*/references/_index.md` — outside its own package — so it
belongs in `vitest.repo-tests.json` and runs under the `repo` project, whose
hash moves with what it actually reads. Left undeclared it would run under
`@objectstack/spec#test`, invisible to both the affected-subset filter and the
turbo cache: green on every PR while red on main, which is the one failure this
pin exists to refuse.

`packages/spec` ships `src/**/*.zod.ts`, and `npm pack --dry-run` confirms the
edited source is in the tarball (206 such entries ship; scripts/, skills/ and
content/ ship none), so the change publishes and takes a patch changeset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
@github-actions github-actions Bot added the size/m label Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/ai/solution-blueprint.zod.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/ai/solution-blueprint.zod.ts) — pages documenting those are invisible to this run
  • 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 — 131 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 bb7d91f19f3fbcc212d90a50466995bf5333fd41packageMentionDocs.

The pin and its `vitest.repo-tests.json` declaration are a change to
`packages/spec/scripts/`, i.e. to the generator side of this repo. Carried here
they defeat the generated-artifact exception that would otherwise lift the
`skills/**` path on this PR: the recompute that certifies the regenerated index
would be running a tree that also modifies the generator it is trusting, so it
cannot certify itself.

What is left here is a pure `gen:skill-refs` / `gen:docs` regeneration beside
the one-line source fix. The pin lands separately and is not weakened.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
@github-actions github-actions Bot added size/s and removed size/m labels Sep 9, 2026
@zhuangjianguo
zhuangjianguo marked this pull request as ready for review September 9, 2026 00:51
@zhuangjianguo
zhuangjianguo added this pull request to the merge queue Sep 9, 2026
Merged via the queue into main with commit db76982 Sep 9, 2026
42 checks passed
@zhuangjianguo
zhuangjianguo deleted the claude/issue-15263-solution-blueprint-module-header branch September 9, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ai size/s tests tooling

Projects

None yet

2 participants