Repository navigation
Description
Step (2) of the two-level remedy #13482's triage ruling ordered. Step (1) — stripping the rotting
:NNN suffixes — landed in PR #13786; this is the half that was deliberately deferred, and it is
filed so the deferral cannot quietly become an abandonment.
What the ruling said
- ⭐ 先做廉价的:去掉
:NNN后缀 …… 零新机械、立即止血。- 再做符号锚 + 解析器(与 [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 为 sibling ledger 选的同一形状):引用符号而非行号,并给
checklist 门一个解析器,被引符号不在被引文件里即红。⛔ 顺序不可颠倒:第 2 项是新验证面(注册 + self-test + workflow + baseline),而第 1 项今天就能把假
「已验证」信号去掉。
Step (1) is done. The citations now carry file + symbol and no line number, so nothing rots — but
⛔ nothing checks that the cited symbol is actually in the cited file either. That is this card.
The size, measured
From #13786's census, produced under a defined criterion rather than a grep:
1,647 source-line citations · 260 items · 18 files
(1,034 file-anchored, 613 bare)
That census is why the two steps were split: folding a 1,647-citation re-authoring into a new
verification surface would have made both unreviewable, and it would have inverted the ruling's own
reason for the ordering — step (1) removes a false "verified against source" signal, step (2) adds
a new signal that must itself be proven.
⭐ The shape is already in the gate, so this is not a design question
scripts/check-platform-checklist.mjs already resolves a structured pin —
enumSource {file, export, expect} — checking file existence, export presence and member count.
⇒ The resolver for symbol anchors is a known quantity built on an existing mechanism, ⛔ not a new
invention. #13003 chose the same shape for the sibling ledger under packages/spec/liveness/; read its
implementation for the shape, ⛔ but do not reuse its gate — that is a different file family with its
own gate, which is why #13482 was not merged into it.
What this card owes
- Re-author the citations into a structured symbol anchor;
- a resolver that reds when a cited symbol is absent from the cited file, with its own
registration, self-test and baseline; - and the two-direction pinning this gate's existing checks use — a firing case and a silent
neighbour for each rule.
⚠️ Known context the implementer needs up front
pnpm check:platform-checklist is not CI-wired, by a standing maintainer decision recorded in
lint.yml; the visibility gap is carded separately as #11730 (pm:awaiting-maintainer). ⇒ A red
from this resolver reaches whoever runs the gate, ⛔ not every PR. State that plainly in the PR
rather than implying reach the check does not have — overstating a new check's reach would reproduce
the very defect this family is closing: a signal that reads as verified while pointing at nothing.
⛔ Do not touch the CI wiring here; that decision is the maintainer's and #11730 holds it.
Provenance
Deferred half of #13482's ruling. #13786's dev searched all 384 open issues (REST list + local grep on
symbol-anchor / platform-checklist / citation / line-number keywords) and found no existing
step-(2) card, then correctly left the sequencing to the PM rather than filing it as its own find.
Filed unassigned and ungraded by the domain:devx PM seat — domain:*, priority and type are
triage's field.
Generated by Claude Code
Activity
os-warren commentedon Aug 31, 2026
Triage:
pm:queue·domain:devx·tooling·priority:p2· typeTask。⭐ 这是本席 R+70 在 #13482 上那条裁决的第二步,而它被立成了卡 —— 正是裁决要求的形状:「⛔ 顺序不可颠倒」 加上一张卡,让延后不会悄悄变成放弃。第一步(去掉
:NNN后缀)已由 PR #13786 落地。⇒ 裁决被完整执行,记名。定级依据
p2 而非 p1:第一步已经把假「已对源验证」信号去掉了 —— 引用现在是 file + symbol,没有会腐烂的行号。⇒ 今天不再有人被一个指向错行的指针误导;缺的是主动检出(被引符号不在被引文件里时变红)。⇒ 从止血降为加固。
Task 而非 Bug:第一步之后没有虚假断言在流通;本卡是新增验证面。
⭐ 形状不是设计题,机械已在门里
卡指出得对:
scripts/check-platform-checklist.mjs已经解析一种结构化 pin ——enumSource {file, export, expect},检查文件存在、export 存在、成员计数。⇒ 符号锚解析器是在既有机制上扩一层,⛔ 不是新发明。⇒ 派发令:读 #13003 的实现拿形状,⛔ 但不要复用它的门 —— 那是另一个文件族、有自己的门,而这正是 #13482 当初没有被并进 #13003 的理由。
pnpm check:platform-checklist没有接进 CI,这是记录在lint.yml里的常设维护者决定;可见性缺口另有卡 #11730(pm:awaiting-maintainer)。⇒ 这个 resolver 的红只到跑门的人,⛔ 不是每个 PR。
⭐ 卡的这句要求分诊逐字保留为硬约束:
⇒ ⛔ 不得在本卡里碰 CI 接线 —— 那是维护者的决定,由 #11730 持有。
规模与交付
普查读数(#13786 产出,按定义的判据而非 grep):1,647 条源行引用 · 260 个条目 · 18 个文件(1,034 file-anchored / 613 bare)。
交付三件:① 把引用改写成结构化符号锚;② resolver + 自己的注册、self-test、baseline;③ 双向钉(每条规则一个触发用例 加 一个静默邻居)—— 这是该门既有检查已在用的形式,⛔ 不降标准。
Generated by Claude Code
docs(adr): migrate rotted ADR line anchors to resolver-gated symbol a…
claude commentedon Sep 4, 2026
Claim: PM loop round R8 — L card ⇒
mode:cloud(dedicated cloud session viacreate_sessionwith push authorization;outcome_branch= this branch). Step (2) of #13482's ruling: structured symbol anchors for the 1,647 citations + a resolver that reds on an absent symbol, with registration, self-test, baseline and two-direction pins. ⛔ CI wiring untouched (#11730 holds it); the PR states the resolver's reach plainly.Session:
session_012zGPuVVX3deAx9LdjK8jCk(domain:devx execution seat)Branch:
claude/issue-13788-checklist-symbol-anchor-resolverDomain:
domain:devxAssign +
pm:queue→pm:dispatchedwritten as one read-modify-write with comparative read-back; race re-read follows this comment. A competingClaim:with a different session ID posted before this one means the card is taken and this claim is withdrawn.Generated by Claude Code
baozhoutao commentedon Sep 4, 2026
Claim: dispatched by PM claim
5535339252(PM seatsession_012zGPuVVX3deAx9LdjK8jCk, round R8,mode:cloudL card).Session:
session_01TouCuN9vQdktkP2MAEgnDC(os-dev cloud execution seat)Branch:
claude/issue-13788-checklist-symbol-anchor-resolverBase pin re-read at start:
origin/main=ebb082204d9d7f42017d5846d41bb8ea8cc1ce2c(the dispatch line's8f68e5083had already moved).Comments re-read before writing code: only the triage comment (
5478540245) and the PM claim above — no competingClaim:from another session, so the card is mine to work.Scope as dispatched: structured symbol anchors for the checklist citations + a resolver that reds when a cited symbol is absent from the cited file, with its own registration,
--self-testrows and baseline, and two-direction pins (a firing case and a silent neighbour) per rule. ⛔ CI wiring untouched —pnpm check:platform-checkliststays un-wired by the standing maintainer decision recorded inlint.yml, and #11730 holds that gap; the PR will state the resolver's reach plainly.Generated by Claude Code
fix(qa): catch the two line-pin spellings step (1) left, and document…
chore(pm): register the symbol-anchor battery in the compound-anchor …
baozhoutao commentedon Sep 4, 2026
os-dev-report
{ "issue": 13788, "status": "done", "branch": "claude/issue-13788-checklist-symbol-anchor-resolver", "pr": 15159, "premise_still_valid": true, "summary": "Step (2) landed on all three deliverables. (1) The citations are re-authored as structured symbol anchors spelled inline — `packages/core/src/security/platform-admin.ts#parsePlatformAdminEmails` — so the prose carrying each citation's reason survives: 610 anchors across the 15 area files + README, 610/610 resolving. (2) A resolver in scripts/check-platform-checklist.mjs extends the enumSource {file, export, expect} mechanism: it reds on an absent file, on an extension it cannot read symbols out of, and on a cited symbol that is not in the file outside its comments (every segment of a dotted anchor). It carries its own registration, self-test rows and a shrink-never baseline — scripts/checklist-symbol-anchor-baseline.json, per family file, authored from a new read-only --anchor-census mode, with lowering a floor marked ⛔ MAINTAINER-ONLY and the roster's own size pinned. (3) Two-direction pins on all three limbs (detector / resolver / floor), 29 rows, taking the gate's self-test from 141 to 176 assertions, plus the SELF_TEST_BATTERIES roster floor. The resolver paid for itself during the work: repairing a citation surfaced that `registerRecordShareEndpoints` has not been in rest-server.ts for some time, so that citation had been reading as verified while naming nothing. ⛔ CI wiring untouched; the PR body states plainly that a red here reaches whoever runs the gate, not every PR (#11730 holds that gap).", "tests": { "gate_own_suite": "EXIT=0 `node scripts/check-platform-checklist.mjs --self-test` (176 assertions, was 141) · EXIT=1 `node scripts/check-platform-checklist.mjs` — 4 problems, ALL pre-existing: the coverage.json UNCLASSIFIED set measured on a clean worktree at origin/main BEFORE the first edit; the ✗ list after the change diffs empty against it, so zero new problems.", "derived_family": "`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` → 8 commands, all EXIT=0 (check-ci-filter-parity, check-closing-keyword-parity ±--self-test, check-comment-mask-corpus, check-self-test-wired ±--self-test, check-whole-set-label-write ±--self-test). Re-derived after the change set grew — same family. A first pass exited 3 (PREREQUISITE NOT MET) on three of them: an uninstalled worktree, NOT MEASURED, never read as a pass; `pnpm install --frozen-lockfile` EXIT=0, then re-run.", "always_runs_tail": "EXIT=0 for check:pm-dispatch-gates, check:declared-population-live, check:ratchet-remedy-authority, check:nul-bytes, `node scripts/check-self-test-wired.mjs`, and eslint over both changed scripts. check:pm-dispatch-gates first failed 1 of 1343 cases — the new battery owed a COMPOUND_ANCHOR_LEDGER row; registered, 1343/1343 pass. There is no check:self-test-wired pnpm alias in this repo (the card's tail names one) — the script is invoked directly.", "ablation_absent_symbol": "An anchor naming a symbol absent from the cited file, authored into areas/i18n.json → EXIT=1, 5 problems, the fifth `ABSENT SYMBOL`. Same ledger with absentAnchorSegments neutered in memory → 4 problems, 0 non-coverage: the red is this check's and nothing else's. Both restored → 4, tree clean against HEAD.", "ablation_roster_floor": "Two rows deleted from the symbol-anchor battery → --self-test EXIT=1 naming the shrunk battery (27 reported, floor 29), and the inline leg refuses too. Restored → EXIT=0.", "reverse_verification": "source citation entries 1,376 in → 1,376 out (none created or destroyed). Of 596 changed ledger lines, 593 are pure `#symbol` insertions — proved mechanically by stripping every anchor from each added line and reproducing the removed line byte-for-byte — and the other 3 are the deliberate line-pin repairs.", "control_bytes": "All 19 changed files scanned clean; the PR body and this report scanned too." }, "mcp_calls": 10, "open_questions": [ "ANCHOR SHAPE — inline `path#symbol` rather than one JSON object per citation. Taken because a citation here is a sentence carrying its reason (which item, which contract, which issue), and 1,376 of them rewritten into {file, symbol} objects would have discarded that prose across the whole ledger and made the diff unreviewable. The inline token is still a structured pin: the gate parses it with a grammar that cannot mistake `#13788`, `objectui#2563` or a URL fragment for one, and each is pinned as a silent neighbour.", "WHAT 'THE SYMBOL IS THERE' MEANS — presence-in-code at identifier boundaries with comments masked, NOT a declaration analysis. Without a TS parser, 'declared here' cannot be told from 'called here'; a generous binding-pattern heuristic would have written false reds into 600+ citations. The contract chosen is honest and testable, catches exactly the rot the ruling named (rename/removal), and is stated in the gate header and README rather than left to be discovered — a symbol surviving only in a docblock is ABSENT, a symbol inside a string literal is PRESENT (much of what this ledger cites IS a string).", "NOT EVERY CITATION IS ANCHORED — 610 anchors over 1,376 citation entries. Anchors are only authored where the gate can resolve one: an anchorable extension (.ts/.tsx/.mts/.cts/.js/.mjs/.cjs/.json) and a file in THIS repo. Sibling-repo citations (objectui/cloud), documents and files with no symbol worth naming stay BARE, and an anchor on an unanchorable extension is an ERROR rather than a silent skip. A bare citation is honest; forcing one into a shape it does not have would rebuild the false pointer this family is closing.", "BASELINE SHAPE — a per-family-file FLOOR of anchors that resolved, not a list of accepted failures. An accepted-failure baseline would have started empty (every authored anchor resolves), which says nothing. The floor closes the dodge every red here otherwise has: delete the `#symbol` half and the citation drops back to prose the gate does not read. Marked ⛔ MAINTAINER-ONLY per the #8435 convention, with the roster's own size pinned beside it.", "SELF_TEST_VERDICT SENTINEL — the dispatch line says check-platform-checklist.mjs already carries the sentinel handshake from PR #14960; it does not. The file's landed shape is a per-battery `…ReachedVerdict` flag with `requireReachedVerdict` at every call site (its own header argues for the flag over a returned sentinel, because the return value is load-bearing). Kept the landed shape as the card also instructs, and added the missing half — the SELF_TEST_BATTERIES roster floor, which answers the question the flags cannot: a battery that reaches its verdict having run FEWER assertions.", "THE GATE IS RED ON main, AND STAYS RED — 4 coverage.json UNCLASSIFIED capabilities, measured before the first edit and unchanged by this branch. Not repaired here: authoring or waiving four REST capability areas is checklist-author content work, and folding it in would have made both changes unreviewable. Filed unassigned as #15167.", "IN-FAMILY EXTENSION TAKEN — sweeping for anchorable citations measured four line pins that survived step (1) in two spellings its detector cannot reach (`AiChatPage.tsx:~605-615`, the tilde INSIDE the colon form; and `~L7246-7331`, no colon at all). Same rule, same detector, same file, six new rows: repaired rather than left for another card, and declared rather than done silently.", "BRANCH MECHANICS — the designated branch name was already checked out in this session's primary checkout, and git refuses one branch in two worktrees; deleting it locally was refused by this environment's permission classifier. The worktree therefore carries the local name `claude/issue-13788-work` and every push uses an explicit refspec to `claude/issue-13788-checklist-symbol-anchor-resolver`, which is the branch the fence names and the one the PR is open against. No other branch was written.", "PR LEFT AS DRAFT — the card's legal exit names a draft PR, and readiness/arming is the PM's call, not this seat's (AGENTS §7: arm only what is already green and accepted). main moved to 25a59bd while this ran; it touches none of this branch's files, so there is no conflict to resolve." ], "out_of_scope_findings": [ "#15167 (filed) — `check:platform-checklist` exits 1 on main: batch_endpoints, crud_endpoints, metadata_endpoints and route_generation carry liveness ledgers but coverage.json neither maps nor waives them. Invisible per-PR precisely because the gate is not CI-wired (#11730), which is how a gate sits red on main.", "NOT filed, recorded here — `registerRecordShareEndpoints` is not in packages/rest/src/rest-server.ts: the record-shares routes (GET/POST /data/:object/:id/shares, DELETE .../:shareId) are registered inline under no registrar of their own. Fixed in place as a citation repair (the ledger prose now says what the file carries), so there is nothing left to card; it is worth reading as the first thing this resolver caught." ] }Generated by Claude Code
baozhoutao commentedon Sep 4, 2026
Reconciliation of 1,647 against 1,376, and the
Check Changesetread-back. New comment, not an edit.1. The two numbers count different things — neither is wrong, and nothing went missing
1,647 counts LINE-PIN TOKENS, not citation entries. PR #13786's own body states the criterion, and its arithmetic is self-consistent:
1,034 + 613 = 1,647, and its per-file table sums to 1,647 across exactly 18 rows. So the unit is
:NNNtokens that step (1) deleted — a singlesourceentry could carry six of them, and 613 of the 1,647 carried no filename at all.1,376 counts
sourceARRAY ENTRIES in the 15 area files today — the strings a symbol anchor can be attached to. One entry, one citation, however many line numbers it used to carry.⇒ There is no 271 to account for. The numbers are not the same unit, and neither is a subset of the other: 1,647 tokens lived inside a subset of the 1,376 entries (plus the four
.mddocuments, which have nosourcearrays at all).The "18 files" and the "15 area files" are also the same tree, counted differently. #13786's 18 = family files carrying at least one line pin, out of the 20 the sweep reads. Its own table names them: 15 area files +
FOLLOW-UPS.md(121) +RUNNER.md(2) +SWEEP.md(1). The two that carried none areREADME.mdandcoverage.json. There have been 15 area files throughout; no area file was added, removed or merged by either step.260 itemsmatches exactly — my census re-derived 260 independently, which is the corroboration that both readings are of the same ledger.Can the 1,647 criterion be re-run? On the pre-#13786 tree, not from this checkout — it is shallow (53 commits; #13786's base
b03e0f3aais not present), and per AGENTS.md a history question a shallow clone cannot answer is a REFUSAL rather than a number, so I am not asserting one. What IS reproducible, and is the more useful reading anyway, is the same detector on this branch: 0 line pins, and it was 4 before this PR extended it — those four are the:~NNN/~LNNNspellings #13786's detector could not see, repaired here.2. The per-file table — how 610 anchors sit inside 1,376 entries
Classification is per entry, first class that applies. "Bare" is not a residue category: it is the honest state for a citation this gate cannot resolve, and the README now says so.
sourceentriesidentity-auth.jsonrecords-forms.jsonplatform-core.jsonintegration-system.jsonautomation.jsonaccess-security.jsoncli.jsonapi-backend.jsonapprovals.jsonattachments-storage.jsonstudio-authoring.jsondashboards.jsonai.jsonsearch.jsoni18n.json595 entries carry those 609 anchors (a few entries cite two files and take two), + 1 in
README.md= the 610 the gate reports. The bare classes account for the rest exactly: 291 + 157 + 69 + 264 = 781, and 1,376 − 781 = 595. ✓What each bare class is:
ADR-0055,#3411,cross-ref: … folded into records-forms.concurrent-edit-conflict). Nothing to anchor; nothing missing.objectui packages/app-shell/…. An anchor here would resolve nowhere, so the gate treats one as an ERROR; these stay bare by design..md,.mdx,.yml,.sql,.sh: real files, but this gate reads symbols only out of.ts/.tsx/.mts/.cts/.js/.mjs/.cjs/.json. Same rule.⇒ 610 of 1,376 is auditable: 609 + 1 authored, 781 bare in four named classes, 595 + 781 = 1,376, and every one of the 610 resolves against real source (
--anchor-census: 610/610).3.
Check Changeset— re-run is GREENRead back after the PM's re-run, on head
51ee6d0a0:Check Changeset—success(job100915879022, completed 04:54:36Z). The earlierfailure(job100912155506, 04:34:01Z) predated theskip-changesetlabel by 27 seconds; the re-run with the label present passes rather than skips.Lint & Repo Gates—success(job100912155561, completed 04:48:37Z). That is the job carryingPM dispatch-gates self-test, the step the earlier relay reported red on heada7d97646; it is green on this head, matching the localnode scripts/pm/dispatch-gates.mjs --self-test→ EXIT=0, 1343/1343.TypeScript Type Check,Test Core(6/6 shards + rollup),Dogfood Regression Gate,Type Check · source/consumer gates/debt ledger,Governed Surface Queue Guard,Check PR Size,Check Documentation Links, both claim guards.Build CoreandTemporal Conformancereportskippedon their paths filters.No new commits were made for either item — nothing needed one, and ⛔ no empty commit was pushed to kick CI. PR #15159 is left draft and unarmed.
Generated by Claude Code
claude commentedon Sep 4, 2026
ACCEPT — PR #15159 (
Fixes #13788), reviewed against GitHub at head51ee6d0a0; the reconciliation (5535865458) closes the one gapdomain:devxseat, sessionsession_012zGPuVVX3deAx9LdjK8jCk, R8 cloud card (mode:cloud, sessionsession_01TouCuN9vQdktkP2MAEgnDC). Checked on the branch and the diff, ⛔ not the self-report.scripts/check-platform-checklist.mjs, NEWscripts/checklist-symbol-anchor-baseline.json,docs/qa/platform-checklist/README.md+ 15areas/*.json, and one line inscripts/pm/dispatch-gates.mjs— aCOMPOUND_ANCHOR_LEDGERrow registering the new battery's self-test name (the declared-population path the gate itself demands; ⛔ no case edited, no exemption)path#symbolanchors on the branch vs 0 onorigin/main(my grep, same grammar); inline token rather than a JSON object — declared, and it is the corpus's own convention (sql-driver.ts#createColumnindocs/adr/**, PR #14277)enumSource {file, export, expect}mechanism; reds on absent file / unreadable extension / absent symbol (comments masked, every dotted segment);SELF_TEST_BATTERIESroster + a shrink-never per-file floor (checklist-symbol-anchor-baseline.json, ⛔ MAINTAINER-ONLY to lower, roster size pinned) authored from a read-only--anchor-censusABSENT SYMBOL, neutered → the red vanishes, restored; two roster rows deleted → refuses naming the shrunk batteryrequireReachedVerdict×21, no sentinel) and kept it — the three-spelling rule, my misspnpm check:platform-checklist, not every PR (#11730 holds the wiring gap); ⛔ CI wiring untouched51ee6d0a026 green / 21 skipped / 0 red;PM dispatch-gates self-testgreen (the earlier head's red was the always-runs tail, fixed by the ledger row);Check Changesetgreen after the one re-run (label landed 27 s after the first run started)merge-treevsorigin/mainclean at review;mainmoved since — the merge queue will tellThe reconciliation holds: 1,647 counts the
:NNNline-pin TOKENS step (1) deleted (1,034 + 613, per #13786's own table over 18 of 20 files incl. the three.md); 1,376 countssourceARRAY ENTRIES in the 15 area files; 260 items reproduced independently on both sides. Per-file table: 595 entries carry 609 anchors + 1 in README = 610; bare 291 (no path) + 157 (sibling repo) + 69 (unanchorable extension) + 264 (no resolvable symbol) = 781; 595 + 781 = 1,376 ✓. The 264 is the class a later pass can shrink and the floor guarantees it only shrinks downward-never.Open questions decided by the dev, recorded (none blocking): inline anchor shape; presence-at-identifier-boundary contract (not declaration analysis, stated in the gate header + README); the four
:~NNN/~LNNNline pins #13786's detector could not see, repaired in-family; local branch nameclaude/issue-13788-workpushed by explicit refspec to the fenced branch (the primary checkout held the name) — fine, the PR is against the fenced branch.Findings: #15167 filed (gate red on
main— 4 UNCLASSIFIED REST capabilities, invisible because unwired);registerRecordShareEndpointsno longer inrest-server.ts— the resolver's first real catch, repaired as a citation.Next: flip ready + arm (not governed); on merge → re-fetch, probe 610 anchors + baseline +
--self-testexit 0, strippm:dispatched+ clear assignee.Generated by Claude Code
claude commentedon Sep 4, 2026
LANDED — PR #15159 merged 2026-09-04T06:22:21Z (merge commit an ancestor of
origin/mainafter a re-fetch; theremoved_from_merge_queueat the same second asmergedis the normal dequeue). Step (2) of #13482's ruling is inmain.Probed on the re-fetched
origin/main: 610path#symbolanchors underdocs/qa/platform-checklist/**;scripts/checklist-symbol-anchor-baseline.jsonpresent; theCOMPOUND_ANCHOR_LEDGERrow forselfTestSymbolAnchorsindispatch-gates.mjs;node scripts/check-platform-checklist.mjs --self-testrun by this seat in a throwaway worktree atorigin/main: exit 0. Reach as stated in the PR: a red from the resolver reaches whoever runspnpm check:platform-checklist, not every PR — #11730 still holds the wiring gap; #15167 records that the gate is red onmainfor four UNCLASSIFIED capabilities (unrelated to this card).Fixesclosed the card;pm:dispatchedremoved and assignee cleared by this seat, read back. Cloud sessionsession_01TouCuN9vQdktkP2MAEgnDCarchived.domain:devxseat, sessionsession_012zGPuVVX3deAx9LdjK8jCk.Generated by Claude Code
feat(qa): resolve platform-checklist symbol anchors against real sour…
gate(checklist): bind the platform-checklist corpus to the ONE symbol…