Skip to content

docs(getting-started): quick-reference 的 Kernel 计数按证据补回两行,并加一道计数校验门 (#6319) - #6357

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-6319-quick-reference-counts
Aug 7, 2026
Merged

docs(getting-started): quick-reference 的 Kernel 计数按证据补回两行,并加一道计数校验门 (#6319)#6357
hotlong merged 1 commit into
mainfrom
claude/issue-6319-quick-reference-counts

Conversation

@hotlong

@hotlong hotlong commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #6319

先说结论:issue 报的三处计数只有一处是真的,而那一处的真相在标题那一侧 —— 不是数字过时,是两行漂到了别的小节。另外两处是测量假象,已逐一复现其成因。

⚠️ 基线是 #6304 合并后main(a585374,含 5b103d6)。立单人的读数取自 #6304 之前,我在自己的基线上重新数过。

一、逐小节三数对照

"实测行数" 由脚本给出,不是手数(手数正是本单要防的错误来源,见第二节)。

小节 标题声明 实测行数(修前) 域内 .zod.ts 判定 修后
Data 17 17 30 相符 17
UI 11 11 17 相符 11
Kernel 17 15 32 表格漏了两行 17
System 18 18 37 相符(#6304 已同步) 18
AI 11 11 11 相符 11
API 17 17 28 相符(#6304 已同步) 17
Automation 5 5 14 相符 5
Security 3 3 4 相符 3
Identity 4 4 5 相符 4
Cloud 5 5 11 两行本属 Kernel 3
Integration 1 1 1 相符 1
Shared 5 5 13 相符 5
QA 1 1 1 相符 1

(N schemas) 的语义:是"本表行数",不是"该域 schema 数"

第 4 列存在的意义是证否它自己:13 个小节里,声明数与域内 .zod.ts一处都对不上(Data 声明 17 而 packages/spec/src/data/ 有 30 个 .zod.ts;System 18 对 37;API 17 对 28)。而声明数与表格行数在 13 个小节里对上了 12 个。所以这一页是一份精选索引,(N schemas) 说的是"这张表有几行",每行一个源文件。

这个判定是后面所有取舍的前提:"某个 schema 存在但不在表里" 本身不是缺陷,否则 Data 就该有 30 行。因此本 PR 不做索引扩编,只修计数确实对不上的那一处。

二、Cloud 5/6 与 Shared 5/12 是测量假象 —— 已复现成因

这两处在我的基线上不存在,在 #6304 之前的 main 上也不存在(两个版本上实测都是 5/5 与 5/5)。我用同样的错法把 issue 的三个数字逐一复现了:

立单人的扫描器把小节标题识别为复数 (N schemas),于是:

  • ## Integration Protocol (1 schema)## QA Protocol (1 schema) 两个单数标题不被识别,其行数被记到上一小节;
  • 小节只在"下一个被识别的标题"处结束,所以 Shared 一路吃到文末,把 ### Declarative Endpoints 那张规则表的 6 行(| **Path shape** | ... 等)也算了进去。

于是:

小节 复现出的数 拆解
Kernel 15 真实行数,这处是真的
Cloud 6 自身 5 + Integration(单数标题)1
Shared 12 自身 5 + QA(单数标题)1 + Declarative Endpoints 规则表 6

#6304 之前的 main 上跑该错法,输出与 issue 表格逐字一致(Kernel 17/15、Cloud 5/6、Shared 5/12)。

⚠️ 这不是挑立单人的错 —— 恰恰相反,它把本单最有价值的部分点出来了:这一页靠人工清点是不可靠的,而且错得很像真的。两个陷阱现在都被新门的 self-test 钉住(第五节 case 4/5)。

三、真实的那一处:两行漂到了别的小节,标题一侧是真相

Kernel 声明 17、表里 15,差 2 行。这两行没有丢,它们坐在 Cloud Protocol 小节里:

Source File 列 源文件实际位置 参考页实际位置
Plugin Registry plugin-registry.zod.ts packages/spec/src/kernel/ content/docs/references/kernel/plugin-registry.mdx
Plugin Security plugin-security.zod.ts packages/spec/src/kernel/ content/docs/references/kernel/plugin-security.mdx

三项机械证据全部指向 kernel:

  1. 源文件在 kernel —— packages/spec/src/cloud/没有同名文件(ls packages/spec/src/cloud/plugin-* 报 No such file);
  2. 参考页在 kernel —— 参考页由 build-docs.ts 按 spec 的目录结构生成,两页都生成在 references/kernel/,references/cloud/ 下没有 plugin-*;ci(check-links): 恢复 pull_request 断链门,仅检仓内链接、advisory-first (#6028) #6304 已经因为这个把 Plugin Security 的链接从 /docs/references/cloud/... 改成了 /docs/references/kernel/...,也就是说这两行的链接今天已经指向 kernel,只有它们所在的小节还留在 Cloud;
  3. 数字自洽 —— 15 + 2 = 17,不多不少正是 Kernel 标题声明的数。

所以判定 标题的 17 是真相,处置是"移",不是"改标题":

  • 两行移回 Kernel(按既有的 Plugin * 字母序插在 Plugin Loading 之后),Kernel 标题 17 保持不动;
  • Cloud 随之 5 → 3。

⛔ 我没有采用"把 17 改成 15"这个更省事的写法 —— 那会让声明去迁就现状,而现状恰恰是错的那一侧:两个 kernel schema 会继续挂在 "Cloud Protocol — Environments, marketplace, licensing, and multi-tenancy" 底下,而它们的 Source File 列和链接都写着 kernel。读者用 Source File 列去仓里找 schema,这一列必须和小节自洽。

一处连带改名:两行移回后,Kernel 里会出现两个都叫 "Plugin Security" 的条目(一个指 plugin-security.zod.ts,一个指 plugin-security-advanced.zod.ts)。把后者改名为 "Plugin Security Advanced" —— 与其源文件名、与其生成参考页的 title: 字段(实测就是 Plugin Security Advanced)、与邻座 "Plugin Lifecycle Advanced" 的约定三者一致。同名两行指向不同页面,是移动本身引入的缺陷,必须在同一次改动里消掉。

四、该页是手写的(范围 2 的判定)

判据 实测
文件头 AUTO-GENERATED 标记 无(content/docs/references/** 的生成物都有)
有没有生成器写它 没有。packages/spec/scripts/build-docs.ts 的输出根是 content/docs/references(第 62 行 DOCS_ROOT),只写这一棵树
全仓提到 quick-reference 的地方 4 处,没有一处是生成:build-docs.ts 的一句历史注释、其测试里同一句、content/docs/getting-started/meta.json 的导航项、apps/docs/redirects.mjs 的两条重定向
AGENTS.md 文档护栏表 content/docs/getting-started/ 一行明写 hand-written

手写。所以走范围 2 的第一条:加计数校验门。

五、新门:check:quick-reference-counts

落点与既有 check:* 家族同址:

  • scripts/check-quick-reference-counts.mjs(新)
  • package.json 一行:"check:quick-reference-counts": "... --self-test && ..."(与家族同写法)
  • .github/workflows/lint.ymlESLint job 一步,紧跟 check:role-word,与其余 docs 类守卫同址

门只做一件事:读该页每个 (N schemas) 标题,与其表格行数比对,不符即红,点名小节、声明数、实际行数、行号

两个抗错设计,都是从第二节的假象里学来的:

  1. 标题计数认 (N schema) (N schemas)(单复数都认);
  2. 小节在下一个 ## 标题处结束 —— 不论那个标题有没有计数。这条正是防止小节一路吃到文末、把无关表格算进来。

结构变化响亮报错,不静默放行。 一个悄悄不再认识自己页面的计数器会永远报绿,那正是这道门要防的失败模式,只是高了一层。所以以下都是 error 而不是 skip:一个小节都没找到;某个 ... Protocol 标题没有计数;某个小节没有表 / 有多张表 / 表里零行。

self-test 覆盖(9 组,--self-test)

# 断言 极性
1 好页面逐小节量出的三元组等于期望值 肯定式
1b 好页面零 finding 否定式(见下)
2 标题数字改错 ⇒ 恰好 1 条 count finding,且消息含小节名 + 声明数 + 实际数 肯定式
3 删掉一行表格 ⇒ 红,且消息含两个数字 肯定式
4 单数 (1 schema) 标题被当作真小节(把它的数字改错必须变红) 肯定式
5 末尾小节量出的行数 = 1(不吃下面那张规则表) 肯定式(断言实测值,不是断言"无 finding")
6 标题格式改掉 ⇒ structure finding 点名它 肯定式
7 小节的表没了 ⇒ structure finding 肯定式
8 整页认不出来 ⇒ structure finding(而不是零比较报绿) 肯定式
9 一个小节两张表 ⇒ structure finding(不静默相加) 肯定式

case 4 与 case 5 就是第二节两个陷阱的定桩:用复数-only 的正则,case 4 得到 0 条 finding;用"吃到文末"的作用域,case 5 量出 3 而不是 1。

六、反向验证(先申报,后执行)

声明 A(修正面)—— 成立

预期:改后每个小节的 (N schemas)、其表格行数、以及第一节判定的真相三者一致。实测 13/13 相符:

✓ content/docs/getting-started/quick-reference.mdx: 13 section(s), every "(N schemas)" heading matches its table.

逐小节三数见第一节表格(修后一列)。

声明 B(门会咬)—— 三段全部成立,另加一段更强的

预期在执行前写下:B1 改错标题数字 ⇒ 红且点名小节与两个数字;B2 删掉一行表格 ⇒ 红;B3 恢复 ⇒ 绿。另加 B4:把门跑在修复前的基线上 ⇒ 必须红,且点名 Kernel 17/15(如果 Cloud/Shared 真的也不符,这里会一起报出来 —— 这是对第二节结论的独立检验)。

操作 预期 实测
B1 Shared 标题 5 → 7 红,点名 EXIT=1 [count] section "Shared Protocol" declares 7 schema(s) but its table has 5 row(s)
B2 删掉 Cloud 的 Tenant 行 EXIT=1 [count] section "Cloud Protocol" declares 3 schema(s) but its table has 2 row(s)
B3 恢复 绿 EXIT=0,13 小节全绿
B4 跑在 origin/main#6304 之前的 main 上 红,只点 Kernel 两个版本都是 1 条 finding:[count] line 57: section "Kernel Protocol" declares 17 schema(s) but its table has 15 row(s)

B4 是本单结论的独立佐证:同一把尺子在两个历史版本上都只报 Kernel 一处,Cloud 与 Shared 一次都没报过。

探针未留残留:git diff --stat origin/main -- content/docs/getting-started/quick-reference.mdx4 insertions(+), 4 deletions(-),即本 PR 的意图改动本身。

断言极性(申报)

上表 B1/B2/B4 与 self-test 的 case 2–9 都是肯定式:门被删空或退化,它们会变红。

唯一的否定式断言是 self-test case 1b("好页面零 finding") —— 门若被掏空成"永远返回空",这一条会平凡成立、无法转红。所以我没有让它独自承担 case 1:同一组固件里用肯定式的 case 1 断言逐小节量出的 [标题, 声明, 实际] 三元组等于具体值,掏空的门在这里立刻失败。case 5 也是同样的处理 —— 本可以写成"不误报",改写成断言实测行数 = 1,于是它同样是肯定式。

声明 C(不造死链)—— 成立

预期:改后该页所有仓内链接目标在仓内存在。

本地无 lychee 二进制,故按 --offline --root-dir (workspace)/content --fallback-extensions mdx,md 的同一解析规则逐条判定(root-relative → content/... + {,.mdx,.md,/index.mdx,/index.md}):118 条仓内链接,0 条死链,1 条外链在 --offline 下被排除。

结构上也不可能造出死链:本 PR 没有新增任何链接目标 —— 两行是整行搬家(其 /docs/references/kernel/plugin-registry/docs/references/kernel/plugin-security 两个目标在 #6304 的绿跑里已被检过),改名那行只改可见文字、目标未动。

七、门禁 EXIT 表(均在 git add 之后跑)

命令 EXIT 输出
pnpm check:doc-authoring 0 365 files clean
pnpm check:role-word 0 OK (44 baselined file(s), no new occurrences)
pnpm check:nul-bytes 0 scanned 6003 tracked text file(s) ... no raw ASCII control bytes
pnpm check:docs-audit-scope 0 scope is in sync with content/docs/: 178 hand-written doc(s)
pnpm check:quick-reference-counts(新) 0 self-test 9 组 + 13 小节全绿
pnpm check:workflow-status-functions(动了 lint.yml) 0 scanned 22 workflow file(s), 41 job(s)
lint.yml YAML 解析 + 落位核对 0 解析通过;新步骤在 jobs.lint(name: ESLint)内,共 35 步
npx eslint scripts/check-quick-reference-counts.mjs 0 无输出
pnpm --filter @objectstack/spec check:generated --reconcile-only 0 见下

最后一条是特意跑的:check:generated 的元门会把 check:/gen: 脚本名与 package.json 双向对账,未分类者直接红。实测它读的是 packages/spec/package.json(pkgRoot = packages/spec,脚本第 39/407 行),而新门接在 package.json,故不在其对账面内 —— 已实跑确认绿(18 check: + 13 gen: scripts, all classified)。

另:控制字节自查(grep -naP 覆盖 NUL 之外的整个扫描面)对四个改动文件均无命中。

八、不在本 PR 里

  • connector-auth 参考页(issue 观察二):packages/spec/src/shared/connector-auth.zod.ts 存在,content/docs/references/shared/connector-auth.mdx 不存在;ci(check-links): 恢复 pull_request 断链门,仅检仓内链接、advisory-first (#6028) #6304 的处置是保留该行、去掉链接,所以今天读起来是"有这个 schema,但没有参考页可看"。补一整页是独立的写作工作量,按派单不在本单;本 PR 未新建该页,也未动那一行。需要补页请另立单。
  • 索引扩编:不给任何小节补"schema 存在但不在表里"的行(理由见第一节:这一页是精选索引,不是穷举;补了 Cloud 就得补 Data 的另外 13 个)。
  • required 集:未改动。新门与 ci(check-links): 恢复 pull_request 断链门,仅检仓内链接、advisory-first (#6028) #6304 同理走 advisory —— 它跑在 ESLint job 里,该 job 本身已是 required,故无需单独动 required 名单。
  • 未建 changeset:纯文档 + 一道仓内门禁,不发布任何包 ⇒ 走 skip-changeset 标签。
  • ⛔ 未动 packages/spec/**(只读清点)、content/docs/references/**.github/workflows/check-links.ymlcontent/docs/releases/**

Generated by Claude Code

…6319)

三处计数只有一处是真的。#6319 报的 Kernel 17/15 属实;Cloud 5/6 与
Shared 5/12 是测量假象 —— 立单人的扫描器只认复数 `(N schemas)`,于是
`## Integration Protocol (1 schema)` 与 `## QA Protocol (1 schema)` 两个
单数标题不被识别、其行数被记到上一小节;而且小节只在下一个"被识别的"
标题处结束,Shared 因此一路吃到文末,把 `### Declarative Endpoints`
规则表的 6 行也算了进去(5+1+6=12)。三个数字已用同样的错法逐一复现。

真实的那一处不是标题过时,而是两行漂到了别的小节:
plugin-registry.zod.ts 与 plugin-security.zod.ts 都在 packages/spec/src/kernel/
(cloud/ 下没有同名文件),参考页也生成在 content/docs/references/kernel/,
它们却列在 Cloud Protocol 小节里。15 + 2 = 17,正是 Kernel 标题声明的数。
故判定标题一侧为真:两行移回 Kernel(标题 17 不动),Cloud 随之 5 → 3。
移回后 Kernel 会出现两个同名 "Plugin Security",按源文件名与参考页标题
把指向 plugin-security-advanced 的那行改名为 "Plugin Security Advanced"
(与邻近的 "Plugin Lifecycle Advanced" 同一约定)。

该页是手写的 —— build-docs.ts 只写 content/docs/references/,AGENTS.md
的文档护栏表也把 content/docs/getting-started/ 列为手写 —— 所以按
declared = enforced 加一道计数门:每个 `(N schemas)` 标题与其表格行数
比对,不符即红并点名小节与两个数字。门对结构变化刻意响亮报错而不是静默
放行(零小节、无计数的 Protocol 标题、小节无表/多表/空表都是 error),
因为一个悄悄不再认识自己页面的计数器会永远报绿 —— 那正是这道门要防的
失败模式,只是高了一层。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 7, 2026 3:04pm

Request Review

@github-actions github-actions Bot added the size/m label Aug 7, 2026
@hotlong hotlong added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/m labels Aug 7, 2026 — with Claude
@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file labels Aug 7, 2026
@hotlong
hotlong marked this pull request as ready for review August 7, 2026 15:35
@hotlong
hotlong added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit bbd2d8d Aug 7, 2026
34 of 35 checks passed
@hotlong
hotlong deleted the claude/issue-6319-quick-reference-counts branch August 7, 2026 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] quick-reference.mdx 的协议索引与 packages/spec 现状漂移:三处小节计数不符 + connector-auth 有 schema 无参考页

2 participants