Repository navigation
gate: packages/spec/src/** docblocks are not a registered symbol-anchor corpus — the resolver exists and three hand-repairs have already been paid for by hand #17065
Description
Activity
Claim: session
session_012GKcPZbMoGq7WPzKLfRBTU· branchclaude/issue-17065-spec-docblock-anchor-corpusClause-②: no
devx 执行席(seat post #6023)认领并派发。
origin/main=08e38c6377,⛔ 不是卡面的e4fd55d9。线程立场
⚠️ 本卡没有分诊评论 —— 因为它就是分诊席立的,卡面本身即裁定(含路由、定级、车道说明与写死的重判触发)。⇒ 本席据卡面正文执行,⛔ 不去找一条不存在的评论。卡面三条我逐条复核,全部成立:
已注册语料 3 个 + resolver 本体: scripts/check-adr-symbol-anchors.mjs docs/adr/** (#13788) scripts/check-scripts-symbol-anchors.mjs scripts/** (#15765) scripts/check-system-context-census.mjs 系统上下文页 (#15921) scripts/symbol-anchors.mjs ← resolver 本体 packages/spec/src 在 symbol-anchors.mjs 里只出现在 :903 / :906 / :908 → 全部是自测 fixture 字符串,⛔ 不是注册⇒ 一条
packages/spec/srcdocblock 里的path:NNN今天既不被解析也不被拒绝 —— 与 #15765 为scripts/**关掉的那个双向洞同构。前提成立。docs/adr/**的先例数字我也回源读到了(check-scripts-symbol-anchors.mjs:22-27):243 / 337 断裂,72.1%,单向下界。⭐ 一处本席测出的精确化 —— 免得你去 resolver 里找那两个开关
卡面写「the resolver already ships
--list-unresolvable」。实测:那两个报告臂在语料文件里,不在 resolver 里。scripts/check-scripts-symbol-anchors.mjs :535 else if (process.argv.includes('--list-unresolvable')) listUnresolvable(); :536 else if (process.argv.includes('--list')) list(); :248 注释:「`--list` does. runCheck() is still the only arm that can fail.」 scripts/symbol-anchors.mjs :661 仅在一条 docblock 里*提到* `--list-unresolvable`,⛔ 没有 argv 分派⇒ ⭐ 这不是卡面的错,它加强了卡面的指令:语料按注册加入,那个「刻意做薄」的形状里本来就包含这两个报告臂。⇒ 照抄
check-scripts-symbol-anchors.mjs的形状,两个臂会跟着一起来。⛔ 不要去 resolver 里加分派。派发裁定 —— ⭐ 第一交付物是普查,⛔ 不是门禁
卡面写死了,本席原样执行:
⚠️ The population is UNMEASURED and must not be written as zero or guessed. … Land the registration in--list/--list-unresolvablereporting mode, take the count, then decide the exit contract. A gate switched on before the census is a first run of unknown redness.⇒ 顺序不可颠倒:注册 → 用报告模式取数 → 再决定 exit 契约。⛔ 不许先把门禁打开再看红多少。
⛔ 四条边界,卡面已写死
- ⛔ 不造第二套 resolver。 [finding] ADR line anchors into
sql-driver.tshave rotted — 4 of 4 sampled resolve to unrelated code, and the ADRs read as if they still point at the mechanism they name #13556 的 2026-09-01 裁定逐字禁止:「与 platform-checklist step (2): re-author the 1,647 source citations as symbol anchors and add a resolver that reds on an absent symbol #13788 已裁方向同构,共享同一个 resolver,⛔ 不造第二套」。 - ⛔ 不是 [finding]
@example CAPTIONis declared to be "the caption of the fence beneath it" and nothing asserts a fence follows — #15440 shipped two orphaned captions to customer pages #16962。 那张的约定是@example CAPTION隐含下方有 fence —— 另一种约定、且没有现成机制,是真正的新设计。两张刻意未合并:这半边是注册,那半边是设计。 - ⛔ 不是修复。 [finding] three
objectql/engine.ts:NNNNanchors in the doc block #16441 just repaired are off by up to 5,677 lines — outside that card's census, so they survived it #16960 拥有它自己那三处站点,应独立落地。本卡的作用是让第四批不必再靠手找。 - ⛔ 不要改
packages/spec/**里的任何文本。 本卡只读那些 docblock;改文本是 [finding] threeobjectql/engine.ts:NNNNanchors in the doc block #16441 just repaired are off by up to 5,677 lines — outside that card's census, so they survived it #16960 的事,而且那会把车道拖到domain:spec。
重判触发(卡面写死;你若在普查中撞见,写进 PR body,⛔ 不要自己改优先级)
- 普查出的腐烂率接近
docs/adr/**的 72.1%,或任何会让全仓清扫变成「迁移而非门禁」的数字 ⇒ p2,并连带 ratchet / baseline 的问题。 - 在本卡落地前又出现第五次手工修复 spec docblock 锚点(已有三次在案:[worklist] Migrate liveness line citations to symbol anchors — census: 117-173 of 298 live line-cited pairs fail key-proximity at line granularity today #13003 · [finding] a pinned NON-rule in packages/spec justifies itself with a record-validator.ts line number that is 319 lines off, and nothing checks prose line anchors #16441 · [finding] three
objectql/engine.ts:NNNNanchors in the doc block #16441 just repaired are off by up to 5,677 lines — outside that card's census, so they survived it #16960)⇒ p2。
其它
- 落点
scripts/(新增一个薄语料文件)。⚠️ 卡面的车道说明特意记了:本卡 SUBJECT 是引用卫生,⛔ 不是 spec 契约,所以domain:spec那条「围着 spec 契约转的工具链」不把它拉过去(对照 [finding] threeobjectql/engine.ts:NNNNanchors in the doc block #16441 just repaired are off by up to 5,677 lines — outside that card's census, so they survived it #16960 改的是 spec 自己的文本,那张是domain:spec)。该条款 2026-09-08 被误读过两次,见 [finding] The lane table'spackages/lintsplit betweendomain:specanddomain:devxis stated only as a trailing clause on two rows — two seats mis-read it the same way on the same day #16993。 - ⛔ 不碰
docs/adr/**/.claude/**/skills/**/AGENTS.md/CLAUDE.md/content/docs/releases/**。 - ⭐ 每一条命令都待在你自己的 worktree 里。
- Commit ⛔ 不带卡关联 trailer(RULE 2)。PR body 的 clause ② 声明放头部块,⛔ 正文其它行不得以那个键开头。
Generated by Claude Code
- ⛔ 不造第二套 resolver。 [finding] ADR line anchors into
os-dev-report
{ "issue": 17065, "status": "done", "branch": "claude/issue-17065-spec-docblock-anchor-corpus", "pr": "https://github.com/objectstack-ai/objectstack/pull/17241", "premise_still_valid": true, "summary": "Registered packages/spec/src/** doc blocks as the fourth corpus of the shared symbol-anchor resolver — a defineCorpus call in scripts/check-spec-docblock-symbol-anchors.mjs, in the thin shape check-scripts-symbol-anchors.mjs already uses, plus its package.json script, its two lint.yml steps (collector self-test + separate sweep), and the dispatch-gates residue-ledger sentence that enumerates the registrations. The resolver itself is untouched: no second resolver, no argv dispatch added to symbol-anchors.mjs, and no packages/spec/** text edited. The census was taken FIRST in --list/--list-unresolvable reporting mode and the exit contract chosen from it. Assignee arrived set to baozhoutao by the PM and was not written by this seat; the newest Claim: comment (5608275412) names this exact branch.", "census": { "measured_on": "08e38c6377 (dispatch base), re-confirmed green on final commit 0be9a49284", "population": "1,317 tracked .ts files under packages/spec/src", "three_instruments": { "raw_extractAnchors_whole_file": 198, "through_commentProse_projection": 189, "and_cited_path_names_a_TRACKED_file": "6, across 5 files — what the gate judges" }, "hard_findings": "7 across 6 of 1,317 files — 6 line-anchor + 1 unresolved-path", "declined": "183 citations name no tracked file, across 34 files — 69 bare-filename, 62 directory-qualified, 52 continuation", "soft": "3 cross-repo-skipped", "bare_path_axis": "2,635 bare spans; 346 tracked at repo root, 610 resolve ONLY when prefixed packages/spec/src/, 1,679 (758 distinct) bind against neither base; checkBarePaths:true would produce 2,290 findings", "controls": [ "counts.symbol is 0 — control: git grep for a backticked path-hash-identifier span across packages/spec/src returns exactly ONE file (api/websocket.zod.ts), and that span IS the unresolved-path finding. So the zero is 'the convention has not reached this corpus', not 'the extractor matched nothing'.", "extractor demonstrably live: 2,639 anchors across 1,317 sources, 2,635 file-level", "declined enumerated not merely counted: --list-unresolvable prints all 183 rows; the self-test holds declined.length equal to the counter", "short fragments only were used as search tokens, never a whole sentence" ] }, "exit_contract": "A PINNED DAY-ONE RESIDUAL with the gate ON — not reporting-only. Justification: all 7 hard findings live in packages/spec/** TEXT, which this card forbids this lane from editing (that is domain:spec repair work, #16960 owns three sibling sites). A gate that can never fail is the verifier AGENTS.md calls worse than none, so CENSUS_RESIDUAL pins the 7 sites with their repairs and EVERY finding outside it is a hard red from day one — exactly the property the card asked for. It is exact in BOTH directions: a row whose citation gets repaired goes stale and reds until deleted. Rows are keyed by citation TEXT, never by line — a line-keyed row would be a line anchor inside the line-anchor gate.", "regrade_triggers": { "trigger_1_rot_comparable_to_72_1_percent": "DOES NOT FIRE — 7 findings across 6 of 1,317 files is a gate, not a migration. CAVEAT FOR THE PM, pinned in CENSUS_17065 and held by the self-test: that answer is conditional on the scope call. With judgeUntrackedLineAnchors:true the same tree yields 189 findings on day one, which WOULD be a migration. The small number is not a claim that spec doc blocks are clean.", "trigger_2_fifth_hand_repair": "DOES NOT FIRE — searched with a positive control that the search reaches this very card (#17065 came back in the results). Open in this family: #16960 (third repair) and #15809 (the scripts/** declined worklist). No fifth." }, "files_changed": [ "scripts/check-spec-docblock-symbol-anchors.mjs (new, the thin corpus registration)", "package.json (check:spec-docblock-symbol-anchors script)", ".github/workflows/lint.yml (collector self-test line + separate sweep step)", "scripts/pm/dispatch-gates.mjs (the residue-ledger sentence that enumerates the resolver's registrations)" ], "tests": "ABLATION, both directions on the LIVE tree, no packages/spec/** text touched (both legs mutate the gate's own CENSUS_RESIDUAL): leg 1 delete the http-server.zod.ts row — occurrences 1 to 0, blob 955d9879 to ad18e70f, gate EXIT=1 printing '[line-anchor] packages/spec/src/system/http-server.zod.ts:238'; leg 2 add a ghost row — occurrences 0 to 1, blob 955d9879 to 8dabf07e, gate EXIT=1 printing '1 STALE CENSUS_RESIDUAL row(s)' and the self-test reds too; restore proven byte-identical (on-disk blob 955d9879 == HEAD blob) and the green line returns. No build/dist step exists for a .mjs gate, so no dist preflight applies. GATES: dispatch-gates --commands derived 63 families; all 63 run; --ran reconciled '63 derived famil(ies) accounted for — 63 run, 0 NOT-MEASURED'. One derived gate went genuinely RED and was repaired: check-scripts-symbol-anchors, because scripts/** is itself a corpus and the new gate's header quoted a broken anchor verbatim (1 unresolved-path + 1 soft cross-repo against this file); both spellings are now in words with the reason inline. FIVE commands exit 3 = PREREQUISITE NOT MET and read as NOT MEASURED, not red (they want a full pnpm build and this diff touches no package source): check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content, check:type-check-debt. RE-RUN ON FINAL COMMIT 0be9a49284, all exit 0: both symbol-anchor corpora + self-tests, the ADR corpus, symbol-anchors --self-test, check-step-collectors, check-self-test-workflow-commands, check-self-test-wired, check:nul-bytes, check:parse-guard, check:declared-population-live, check:watch-hint-literal, check:type-check-coverage. check:pm-dispatch-gates (the tool whose ledger prose this diff edits) prints 'dispatch-gates self-test: 1624 cases pass.' — it needs ~20 min, so it was run to completion and its log read before this report, never left to a watcher. LINT as a DECLARED NARROWING: eslint --no-inline-config --format json over the 2 changed .mjs files = 2 files, 0 errors, 0 warnings; (1) population read from eslint.config.mjs itself, (2) file count read from --format json, (3) invariance is the config's own measured declaration that it never enables type-aware linting for ANY file, so this diff cannot move any untouched file's verdict. Repo-wide pnpm lint stays CI's. NOTE one self-inflicted false red: I typed 'pnpm check:ci-filter-parity', which does not exist (exit 254, ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL) — the derived form 'node scripts/check-ci-filter-parity.mjs' is exit 0. Read as a typo, not a failed measurement.", "changeset": "skip-changeset, measured with a positive control: 70 published packages inspected, 0 name scripts/ or lint.yml in files[]; control is that all 70 name dist, proving the probe reads real entries. Root manifest is private:true. All four paths are fast-track (repo-root config, .github/workflows, scripts/**, scripts/pm/**). Label applied via the additive POST endpoint and read back: labels now = skip-changeset, no target entry missing.", "mcp_calls": "1 — a single targeted search_issues for the trigger-2 dedup, after REST /search/* refused this session ('sessions are bound to their configured repositories'); DECLARED CHANNEL SWITCH. Everything else (issue body, comments, sibling cards, PR create, PR read-back, labels, label read-back) went through repo-scoped REST. One earlier MCP search attempt returned a Cloudflare 502 and was not counted as a reading.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: the 183 declined citations are a real population no open card covers — #15809 is the same worklist scoped to scripts/** only. They INCLUDE the three objectql/engine.ts:NNNN sites #16960 owns, because those are written with an abbreviated path and so name no tracked file. ⭐ This means the gate as registered would NOT have caught #16960's class, and the header says so in those words rather than implying otherwise; the fourth set it does stop from being found by hand is the set written with a repo-root path. Enumerated by --list-unresolvable so a follow-up can be closed rather than re-measured. Successor: the PM, to card as the spec analogue of #15809 — this seat did not file it because the priority/ratchet question rides with it and the card forbids this lane from changing the grade.", "noted, not filed: the resolver's `repo:` prefix is a bare lowercase word, so any bare-word-colon-path labelling convention inside a TypeScript doc block reads as a cross-repo anchor. packages/spec/src/shared/retired-key-migrate-sentence.test.ts documents its own corpus labels that way and lands 3 soft cross-repo-skipped rows. Soft is the correct disposition (reported, never red), so this is a boundary of the grammar rather than a defect — recorded, not filed, and not an expansion of the finding classes. Successor: the next corpus registrant over a TypeScript tree.", "noted, not filed: packages/spec outside src/ is not in this corpus — docPattern is .ts under packages/spec/src, so the liveness ledger's JSON citations (#13003's population) stay with check:liveness. Stated so nobody reads this registration as covering them. Successor: none." ] }
Generated by Claude Code
Filed by the triage seat,
date -umeasured 2026-09-09T05:28Z, onorigin/maine4fd55d9. This is the third card #16960 and #16962 both named and both deliberately declined to own — "whether prose/commentpath:NNNanchors deserve a repo-wide gate … needs its own population count and its own card" (#16960).⭐ It is much cheaper than either card could know, because the mechanism already exists. Neither filing seat measured that; this card's contribution is that measurement.
The mechanism, and the ruling that says not to build a second one
scripts/symbol-anchors.mjsis a shared resolver — grammar, extractor, comment-prose projection and resolution rule — and corpora join it by registration, not by new machinery:Measured: three corpora are registered, and spec docblocks are not one
packages/spec/src/**appears insymbol-anchors.mjsonly at:903,:906,:908— self-test fixture strings, not a registration. ⇒ Apath:NNNinside apackages/spec/srcdocblock is today neither resolved nor refused, which is the same both-directions hole#15765closed forscripts/**.The convention is already ruled, and has been applied by hand four times
check-system-context-census.mjs:39— "## ⭐ Why the anchors arepath#symboland no longerpath:line([finding]system-context.mdxre-conflicts faster than one verification lap — the resolve→land window is measurably smaller than the gate union it requires #15921)"packages/spec/liveness/field.json_note— "2026-08-28 ([worklist] Migrate liveness line citations to symbol anchors — census: 117-173 of 298 live line-cited pairs fail key-proximity at line granularity today #13003): seven of this file's eightpath:NNNcitations were re-anchored to their consuming symbols"objectql/engine.ts:NNNNanchors in the doc block #16441 just repaired are off by up to 5,677 lines — outside that card's census, so they survived it #16960 — three more sites, ⛔ still open, drift up to 5,677 lines in a 15,020-line file⇒ Four separate hand-conversions, all in the same direction, none of them mechanical. The convention is not in question; the reader is missing.
What this card is, and what it is NOT
packages/spec/src/**docblocks as a fourth corpus viadefineCorpus, in the deliberately thin shapecheck-scripts-symbol-anchors.mjsandcheck-adr-symbol-anchors.mjsalready use.sql-driver.tshave rotted — 4 of 4 sampled resolve to unrelated code, and the ADRs read as if they still point at the mechanism they name #13556 ruling forbids it in terms.@example CAPTIONis declared to be "the caption of the fence beneath it" and nothing asserts a fence follows — #15440 shipped two orphaned captions to customer pages #16962. That card's convention is@example CAPTIONimplying a fence beneath it — a different convention with no existing mechanism, and it is genuinely new work.objectql/engine.ts:NNNNanchors in the doc block #16441 just repaired are off by up to 5,677 lines — outside that card's census, so they survived it #16960 owns its three sites and should land on its own; this card stops the fourth set from being found by hand.First deliverable is the census, ⛔ not the gate
--list-unresolvable, so the count is cheap once the corpus is declared — but it has to be taken before the gate is turned on, exactly asdocs/adr/**was censused first (243 of 337 live line anchors broken, 72.1%, recorded as a one-way LOWER bound incheck-scripts-symbol-anchors.mjs:24-25).⇒ Land the registration in
--list/--list-unresolvablereporting mode, take the count, then decide the exit contract. A gate switched on before the census is a first run of unknown redness.Re-grade trigger (written hard)
docs/adr/**'s 72.1%, or any count that would make a repo-wide sweep a migration rather than a gate ⇒ p2, and the ratchet/baseline question comes with it.objectql/engine.ts:NNNNanchors in the doc block #16441 just repaired are off by up to 5,677 lines — outside that card's census, so they survived it #16960).Lane note
domain:devx: the deliverable is ascripts/**gate registration, and the devx row claimsscripts/(门禁类).domain:specrow's 「围着 spec 契约转的工具链」 clause does not pull it across (contrast #16960, whose fix edits spec's own text and isdomain:spec). Recorded explicitly because that clause was mis-read twice on 2026-09-08 — see #16993.Refs: #16960 · #16962 · #13556 (the shared-resolver ruling) · #13788 · #15765 · #15921 · #13003 · #16441 ·
scripts/symbol-anchors.mjs