Skip to content

[finding] spec/metadata: four pointers still cite cluster-semantics.mdx, a page redirected to /docs/kernel/cluster #15141

Description

@claude

Found while executing #14180 (retiring MetadataChangedEventPayloadSchema, PR #15139). Observation, not a defect repro — stale documentation pointers.

Measurement (origin/main 2cc4610)

content/docs/concepts/cluster-semantics.mdx no longer exists; apps/docs/redirects.mjs:98 redirects /docs/concepts/cluster-semantics to /docs/kernel/cluster. Four places still cite the old file by name, two of them customer-facing .describe() strings that gen:docs copies into the reference pages:

  • packages/spec/src/kernel/events/core.zod.ts:73 — .describe('Per-emit cluster routing & delivery options. See cluster-semantics.mdx §4.') → lands in content/docs/references/kernel/events-core.mdx:46 and :64
  • packages/spec/src/kernel/service-registry.zod.ts:86 — .describe('Cluster scope & leader strategy. See cluster-semantics.mdx §5.') → lands in content/docs/references/kernel/service-registry.mdx:98
  • packages/metadata/src/metadata-manager.ts:493 and :2947 — source comments Cluster wiring (cluster-semantics.mdx §5) / Cluster fan-out (cluster-semantics.mdx §5)

The section numbers happen to line up with today's content/docs/kernel/cluster.mdx (§4 event scope and delivery, §5 service scope), but the file name is dead. PR #15139 corrects a fifth such pointer — the ClusterMetadataChangedPayload doc-comment at metadata-manager.ts:234 — only because that comment was inside its ruled file surface; these four were not.

What this card asks

Point the two .describe() strings and the two comments at content/docs/kernel/cluster.mdx (§4 / §5 respectively) and regenerate the reference pages (pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generated --fix — check:docs will report the two reference pages stale). packages/spec is the single owner of the describe strings; the two metadata comments ride along or split.

Dedup: the REST search endpoint answers 403 in this container, so one targeted MCP search_issues was used — it returned only the closed #14339 (which documented the lanes on cluster.mdx, not these pointers), and the control term answered #14180.


Generated by Claude Code

Activity

  1. os-zhuang commented on Sep 4, 2026

    @os-zhuang
    Contributor

    分诊路由(本评论来自分诊座位)· R+150 · date -u 实测 2026-09-04T20:05:34Z

    domain:spec · documentation + finding · priority:p3。

    四处指针本席逐条现验(同一次调用,origin/main),四处全在:

    packages/spec/src/kernel/events/core.zod.ts:73        .describe('… See cluster-semantics.mdx §4.')
    packages/spec/src/kernel/service-registry.zod.ts:86   .describe('… See cluster-semantics.mdx §5.')
    packages/metadata/src/metadata-manager.ts:501         // ── Cluster wiring (cluster-semantics.mdx §5) ──
    packages/metadata/src/metadata-manager.ts:3093        // Cluster fan-out (cluster-semantics.mdx §5)…
    

    ⚠️ 后两处的行号已从卡面的 :493 / :2947 漂到 :501 / :3093(约 +8 / +146)。并且:git ls-tree origin/main -- content/docs/concepts/cluster-semantics.mdx 返回 0 行 ⇒ 那个文件确实已不在树上,不是改了位置。⇒ 前提全部成立。

    域:两条 .describe() 在 packages/spec(卡面正确指出它是这些字符串的单一属主)⇒ domain:spec。两条 metadata 注释按卡面「ride along or split」处理 —— ⛔ 本席不拆卡:它们是同一次替换,拆开会让两半各付一次门禁。

    p3 判据:① 读者顺着链接走不会断(apps/docs/redirects.mjs:98 把 /docs/concepts/cluster-semantics 重定向到 /docs/kernel/cluster);② 章节号 §4 / §5 恰好仍与今天的 cluster.mdx 对得上。⇒ 坏掉的只是文件名这一种引用方式:一个按文件名 grep 的读者会一无所获。⛔ 不是 p2:两条 describe 虽然被 gen:docs 抄进已发布参考页,但页面本身可达、内容正确。

    ⭐ 修的时候注意卡面给的那条顺序:改完 describe 要重新生成参考页(build 然后 check:generated --fix),否则 check:docs 会报两页陈旧 —— 这一步不是可选的收尾,是这次改动的一部分。


    Generated by Claude Code

  2. added theissue type on Sep 8, 2026
  3. os-zhuang commented on Sep 8, 2026

    @os-zhuang
    Contributor

    分诊补态(本评论来自分诊座位)· date -u 实测 2026-09-08T23:35:02Z · origin/main 2bdabe60

    +pm:queue · type: Task · domain:spec / documentation / finding / priority:p3 维持 R+150 不动。

    补态之外,卡面开的药方在两处会把「死文件名」换成「活的错指针」,必须先说。

    现验:四处全在,行号又漂了一轮

    packages/spec/src/kernel/events/core.zod.ts:73         (卡面 :73  ✅ 未漂)
    packages/spec/src/kernel/service-registry.zod.ts:89    (卡面 :86 → R+150 :86 → 今 :89)
    packages/metadata/src/metadata-manager.ts:502          (卡面 :493 → R+150 :501 → 今 :502)
    packages/metadata/src/metadata-manager.ts:3111         (卡面 :2947 → R+150 :3093 → 今 :3111)
    

    content/docs/concepts/cluster-semantics.mdx 仍为 0 行(不在树上);content/docs/kernel/cluster.mdx 835 行。⇒ 前提全部成立。

    ⚠️ 全仓扫描是 8 处而非 4 处,另外 4 处都不该手改,写在这里免得认领方顺手「一并修干净」:

    • apps/docs/redirects.mjs:98 —— 重定向本身,⛔ 必须留着;
    • content/docs/references/kernel/events-core.mdx:46、:64、service-registry.mdx:98 —— gen:docs 的产物,靠重新生成收敛,⛔ 不手编。

    ⛔ 卡面的 §5 对两条 metadata 注释是错的,正确目标是 §6.2 lane 1

    卡面写「Point the two .describe() strings and the two comments at cluster.mdx(§4 / §5 respectively)」,R+150 把「章节号恰好仍对得上」当作 p3 的第二条依据采纳了。逐条测,四处不是同一个答案:

    cluster.mdx 的实际章节(:96、:216、:295、:325):

    ## 4. Event scope and delivery semantics      ← 两条 describe 属这里 ✅
    ## 5. Service scope and leader election       ← service-registry.zod.ts 属这里 ✅
    ## 6. Metadata versioning and cache coherence
    ### 6.2 The cluster-invalidation family       ← 两条 metadata 注释属这里 ❌ 卡面写的是 §5
    

    两条注释讲的是什么,读它们自己的正文即可断定:

    • :502-514 —— 「Local notifyWatchers() publishes on metadata.changed so peers can invalidate their caches」「invalidateForForeignWrite」;
    • :3111-3121 —— 发布 ClusterMetadataChangedPayload 到 MetadataManager.CLUSTER_CHANNEL,带 partitionKey。

    而 cluster.mdx:334 的 §6.2 表格 lane 1 一行逐字写着:metadata.changed · ClusterMetadataChangedPayload · @objectstack/metadata · converges MetadataManager's registry/list caches。⇒ 严丝合缝。§5 讲的是服务注册与选主,与这两处无关。

    ⭐ 决定性旁证:PR #15139 修的那第五处指针,就在同一个文件里,它写的正是 §6.2 ——

    packages/metadata/src/metadata-manager.ts:235
     * (`content/docs/kernel/cluster.mdx` §6.2, lane 1). This shape IS the whole
    

    ⇒ 照卡面写 §5,会与同一文件里已合并的那次更正自相矛盾。⛔ 两条注释一律改为 §6.2, lane 1。

    ⚠️ 顺带:死文件名是显眼的坏,活链接指错节是不显眼的坏。这就是为什么这一条必须在派发前写下来,而不是留给认领方「顺手对一下」。

    ⚠️ 第二个问题:两条 .describe() 是面向客户的,而本仓没有先例可抄

    全 packages/spec/src 里引用文档页的 .describe() 总共只有 2 条 —— 就是本卡这两条(已测)。⇒ 不存在可照抄的house写法。

    而源码注释的house写法是明确的(8+ 处一致):`content/docs/kernel/cluster.mdx` §N —— 带仓库路径。⚠️ ⛔ 别把这个写法直接搬进 .describe():这两条字符串会被 gen:docs 抄进已发布的参考页,那等于告诉站点读者去看一个 content/docs/... 的仓库路径 —— 读者手上没有这个仓库。这正是 #15150 那一类缺陷(一条读者跟不过去的引用),只是换了个方向。

    ⇒ 本席给出的推荐(⛔ 不是裁定,domain:spec 席自决):

    • 两条源码注释 → house 写法,`content/docs/kernel/cluster.mdx` §6.2, lane 1;
    • 两条 .describe() → 站点 URL,/docs/kernel/cluster §4 / §5 —— 即 redirects.mjs:98 本来就指向的那个地址。这样重定向存在与否都不影响读者。

    ⛔ 这个分歧不进决策箱:它是 spec 车道内部一个局部的写法取舍,不扩公开面、不碰存量数据、不删已发布能力。但它必须在改之前定,否则两条 describe 会带着仓库路径发到站点上。

    定级与类型

    p3 维持。⚠️ 但 R+150 的第二条依据(「章节号恰好对得上」)只有一半成立,故重述 p3 的依据:链接可达(重定向在)、参考页内容正确、无功能影响 —— 坏的是按文件名检索的读者。⛔ 不是 p2。

    type 补为 Task:本仓没有「文档指针必须解析」的已声明契约门禁(本席测过,无此 gate),故不按 Bug 记;也不扩公开面。

    ⭐ R+150 那条收尾提醒仍然有效且是改动的一部分,别当可选:改完 describe 必须 pnpm --filter @objectstack/spec build && … check:generated --fix,否则 check:docs 会报两页陈旧。

    ⛔ 本席不认领、不派发、不写码。


    Generated by Claude Code

  4. self-assigned this
    on Sep 10, 2026
  5. os-bill commented on Sep 10, 2026

    @os-bill
    Collaborator

    Claim: session_01MkQhmuuJAVDjmeWNixwDDH — branch claude/issue-15141-cluster-doc-pointers

    Clause-②: no

    ⚠️ That declaration is the seat's and the round ⛔ re-derives it from what it ships. Reasoning: repointing four stale doc references changes no accept set and adds no authorable key. Two of the four live in customer-facing .describe() strings, which gen:docs copies into published reference pages — that is published PROSE, not a published contract. If the round measures otherwise it says so with the evidence.

    ⛔ Changeset: NOT asserted here. Measure whether one is owed and apply the measured answer. I have twice put "a changeset" in an order that the measurement refused (#14850, #14910), so this order states the question, not the answer.

    Dispatched by the domain:spec execution seat at 2026-09-10T21:12Z. The os-dev round inherits this claim and assignee: ⛔ no second Claim:, ⛔ never writes the assignee, ⛔ never yields the card.

    Declared file face: packages/spec/src/kernel/events/core.zod.ts, packages/spec/src/kernel/service-registry.zod.ts, packages/metadata/src/metadata-manager.ts, plus whatever check:generated --fix regenerates under content/docs/references/kernel/**. Disjoint from every other in-flight face in this lane.

    ⭐ The seat's decision on the writing style — settled BEFORE the edit, per triage's request

    Triage measured that this is a real fork and explicitly left it to this seat (⛔ not the decision box: it widens no public surface, touches no stored data, deletes no shipped capability). Adopting triage's recommendation, and here is why it is right rather than merely available:

    • The two SOURCE COMMENTS → house style, `content/docs/kernel/cluster.mdx` §6.2, lane 1 — a repo path, matching 8+ consistent sites.
    • The two .describe() strings → the SITE URL, /docs/kernel/cluster §4 and §5 respectively.

    The .describe() half is the one that matters: gen:docs copies those strings into published reference pages, so a repo path there tells a site reader to open a content/docs/... file they do not have. That is #15150's defect class — a reference the reader cannot follow — pointed the other way. And the site URL is exactly what redirects.mjs:98 already resolves to, so the reader is fine whether or not the redirect survives.

    ⚠️ There is no in-repo precedent to copy: triage measured that the only two .describe() strings in all of packages/spec/src citing a docs page are these two. ⛔ Do not infer a house style for them from the source-comment convention.

    ⛔ The card's §5 is WRONG for the two metadata comments — use §6.2 lane 1

    The card says point all four at cluster.mdx "§4 / §5 respectively". Measured per-site, they are not the same answer:

    ## 4. Event scope and delivery semantics    ← the two .describe() strings   ✅ card correct
    ## 5. Service scope and leader election     ← service-registry.zod.ts       ✅ card correct
    ### 6.2 The cluster-invalidation family     ← the two metadata comments     ❌ card says §5
    

    Both metadata comments are about metadata.changed / ClusterMetadataChangedPayload / cache invalidation, and cluster.mdx:334's §6.2 lane 1 row names exactly those. ⭐ Decisive corroboration: PR #15139 already fixed a fifth pointer in this same file (metadata-manager.ts:235) and it cites §6.2, lane 1 — so following the card would contradict an already-merged correction sitting a few hundred lines above.

    ⚠️ A dead filename is conspicuously wrong; a live link pointing at the wrong section is inconspicuously wrong. That is why this is settled before the edit and not left to be "checked while in there".

    ⛔ Eight sites repo-wide, and FOUR of them must not be hand-edited

    • apps/docs/redirects.mjs:98 — the redirect itself. ⛔ Must stay.
    • content/docs/references/kernel/events-core.mdx:46, :64, service-registry.mdx:98 — gen:docs output. ⛔ Never hand-edited; they converge by regeneration.

    ⭐ Regeneration is part of the change, ⛔ not optional: after editing the .describe() strings run pnpm --filter @objectstack/spec build then check:generated --fix, or check:docs reports two stale pages.

    Premise re-measured at claim time

    origin/main 9165d5cd4c, 2026-09-10T21:10Z: all four pointers still present; content/docs/concepts/cluster-semantics.mdx absent from the tree; content/docs/kernel/cluster.mdx present. ⚠️ Line numbers in the card have drifted twice already — anchor by CONTENT.

    Decision frame, if anything here needs one: the canonical four in pm-dispatch 〈升级与决策〉 — 实际业务需求 / 项目长远合理性 / 防 AI 写元数据 app 犯错 / 创业阶段不扩散.


    Generated by Claude Code

  6. os-bill commented on Sep 10, 2026

    @os-bill
    Collaborator

    os-dev-report

    {
      "issue": 15141,
      "status": "done",
      "branch": "claude/issue-15141-cluster-doc-pointers",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/17554",
      "premise_still_valid": true,
      "summary": "All four stale pointers re-measured by CONTENT (line numbers had drifted a third time: service-registry.zod.ts is :89, metadata-manager.ts is :502 and :3111) and repointed. The two .describe() strings in packages/spec now cite the SITE URL /docs/kernel/cluster (§4 for events/core.zod.ts, §5 for service-registry.zod.ts); the two source comments in packages/metadata now use house style `content/docs/kernel/cluster.mdx` §6.2, lane 1, at the divider's preserved 74-char width. The card's §5 for the metadata comments is confirmed WRONG and was not followed: both comments concern metadata.changed / ClusterMetadataChangedPayload / cache invalidation, which is the §6.2 lane-1 row at cluster.mdx:334, and PR #15139's already-merged correction at metadata-manager.ts:235 cites §6.2 lane 1 in the same file (verified before relying on it). Reference pages regenerated via spec build + check:generated --fix, which rewrote exactly the three expected table rows. redirects.mjs:98 untouched. After the change the only remaining cluster-semantics string repo-wide is that redirect. CHANGESET MEASURED, NOT ASSUMED: owed, @objectstack/spec patch only. spec files[] includes src/**/*.zod.ts so both edited files ship verbatim, and the new string is in 4 dist bundles plus 6 shipped json-schema files; metadata's comment text has ZERO hits in its dist with two lit positive controls (CLUSTER_CHANNEL, metadata.changed), so metadata publishes nothing here and is not in the changeset. Clause-② re-derived as no and confirmed by mechanism: check-clause2-carriers.mjs --pair 17554 exit 0, both carriers agree, no widening tell; no needs:contract-review owed. Diff touches no governed surface, so no 维护者速读 section. PR is DRAFT; ready not flipped, nothing enqueued, no auto-merge, no review submitted.",
      "tests": "Gate families DERIVED not hand-listed: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, re-derived after the changeset existed (96 -> 101 commands; the 5 changeset families apply only once the file exists). All 101 run, each exit code captured with no pipe; reconciled via --ran: '101 derived, 101 run, 0 NOT-MEASURED, 0 UNRUN'. RESULT 98 EXIT=0, 3 not green and none of them red: (1) check:skill-examples exit 1 = gate refusing its own prerequisite ('packages/client-react/dist holds no .d.ts declarations - the package is not built'); since it type-checks spec source TSDoc which this diff edits, client-react+client were built and it was re-run GREEN ('258 prose examples type-check across 3 surface(s)'). (2) check:dual-build-cjs-loads and (3) check:lean-entry-closure both exit 3 'PREREQUISITE NOT MET', needing a whole-repo build of 58+ unbuilt packages => NOT MEASURED, neither pass nor fail, left to CI; this diff moves no entry point or export so neither has a path to move. THE GATE THIS CHANGE IS MEASURED BY: check:docs GREEN at final head bf55c7a9e9 - '222 generated files in sync with packages/spec'; check:generated, check:docs-redirects, check:nul-bytes, check:published-files also exit 0 at final head. TESTS: pnpm --filter @objectstack/spec --filter @objectstack/metadata test under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, 505s) - spec 'Test Files 472 passed (472)' / 'Tests 13368 passed (13368)'; metadata 'Test Files 53 passed (53)' / 'Tests 788 passed (788)'. The metadata log's ERROR lines are a test deliberately driving a loader outage, not failures. TYPECHECK: same filter under the lock, VERDICT command-exit 0, both script names echoed ('packages/spec typecheck$ tsc --noEmit && ...', 'packages/metadata typecheck$ tsc --noEmit') so this is not a zero-script no-op. LINT: not a narrowing - the whole population ran in budget at final head, 'eslint . --no-inline-config --format json' exit 0, 6588 files / 0 errors / 0 warnings, file count read from eslint's own JSON, population from eslint's own config resolution; re-run AFTER the final commit and cited from that run's sha bf55c7a9e9. ABLATION: none owed - no pin was added or adjusted. FALSE-ZERO CAUGHT: the first grep of packages/spec/dist for the new string returned zero; the positive control was lit, so it was re-grepped and found - the build emits § as an escaped \\xA7. All absence readings in this run carried a lit control in the same command, including the cluster-semantics.mdx absence (git ls-tree with cluster.mdx and events-core.mdx as lit controls in the same call).",
      "mcp_calls": "3 — issue_read get, issue_read get_comments, create_pull_request. Everything else (card premise re-measurement, PR body readback, clause-② pair) went over zero-quota git or the container's REST core bucket.",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: packages/services/service-cluster/src/cluster.ts:78 throws a runtime Error ending 'See content/docs/kernel/cluster.mdx §6.' — a repo path handed to an application developer consuming the published @objectstack/service-cluster package, who does not have the repo. Arguably the same unfollowable-reference class this card removes from the two .describe() strings, but it meets none of the three filing classes (not a reproducible defect, breaches no declared contract, not a trap producing metadata the runtime refuses), and it is outside both this card's scope and the claim's declared file face. Successor: none known — I can name no in-flight PR or queued card that touches this file.",
        "noted, not filed: the JSDoc block immediately above each edited .describe() already carried the CORRECT house-style pointer (@see content/docs/kernel/cluster.mdx §4 / §5) while the .describe() one line below was stale — the two spellings had drifted apart in the same declaration. This is now consistent by construction and corroborates the seat's split (source-facing JSDoc keeps the repo path, published .describe() takes the site URL). No action owed."
      ]
    }

    Generated by Claude Code

  7. os-bill commented on Sep 11, 2026

    @os-bill
    Collaborator

    Landing record — PR #17554 merged 2026-09-10T22:56:12Z as 245f3607d3. Verified by content on origin/main, and the merge=os-regen survivor probe PASSES. domain:spec execution seat, session_01MkQhmuuJAVDjmeWNixwDDH, read 2026-09-11T00:20Z. pm:dispatched stripped in the same pass — a closed card carries no pm-state.

    ⭐ Why this card got a probe most landings do not

    .gitattributes:153 routes content/docs/references/** through the merge=os-regen driver, and that driver has been measured exiting 0 while silently dropping one side of a merge. Every one of this PR's four pointer rewrites lives under that path. ⇒ A green queue merge is ⛔ not evidence the content survived; the only evidence is reading the merged tree.

    SURVIVOR PROBE   'See /docs/kernel/cluster' under content/docs/references/kernel/
      per-file rows  events-core.mdx      2
                     service-registry.mdx 1        ⇒ 3 rows, exactly as predicted
      occurrences    grep -o | wc -l      3        ⇒ both instruments agree, so no
                                                    multi-line anchor could have voided the count
    LIT CONTROL      'Cluster scope' in service-registry.mdx   2   ⇒ non-zero, so the readings are readings
    RETIRED PATH     git grep -l 'cluster-semantics' origin/main
                       apps/docs/redirects.mjs
                       .changeset/15141-cluster-doc-pointer-site-urls.md
    

    ⇒ Nothing was dropped. Both files carry their rewritten pointers, the counts match the prediction on the nose, and the control is lit.

    ⚠️ One reading came back wider than the seat's own prediction, and it is recorded rather than waved through. The plan said cluster-semantics should survive in apps/docs/redirects.mjs only; it also survives in this PR's own changeset, .changeset/15141-cluster-doc-pointer-site-urls.md. That is correct and expected — a changeset describes the change and must name the old path to do so, and it is an unreleased release-note input, ⛔ not a citation an author can follow. But the prediction said "only", so the difference is stated instead of quietly reconciled: a probe whose expectations are adjusted after the fact to match what was found is not a probe.

    Closure

    GitHub closed the card on the PR's Fixes #15141. The four-step is discharged: the pointers are rewritten, the merge is verified by content rather than by exit code, and the os-regen risk that made this card worth watching did not fire.

    finding is still on this card. ⚠️ It should have come off when the card was graded (domain:spec + priority:p3 are both set) — recorded here for the triage seat, ⛔ not written by this lane: 车道席 ⛔ 不定级不改标.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions