Skip to content

fix(check-adr-symbol-anchors): the verdict may not claim a census wider than the vocabulary it read - #18925

Merged
os-zhuang merged 4 commits into
mainfrom
claude/issue-18899-adr-symbol-anchors-blind-forms
Sep 18, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
claude/issue-18899-adr-symbol-anchors-blind-forms

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes #18899

Clause-②: no

check-adr-symbol-anchors printed 0 line anchors survive. and that sentence
was false. This change deletes the surviving pointers inside the declared file
surface — every one of them sampled stale before it was deleted — and makes the
verdict stop claiming a census it does not take.

Caliber first, then the count

⛔ No count is inherited here, mine included. A line pointer is defined as a
token in an authored document that designates a position in a source file by
LINE NUMBER. The class was then enumerated by SPELLING — six of them — and each
regex was lit on a literal it must see before any corpus was read; the two
path-less spellings additionally got a negative control on the neighbours they
must refuse (an HTTP status, a URL port, a clock time, a lane label, an
identifier ending in a capital L before digits). Fenced blocks are skipped,
exactly as the extractor skips them, so quoted material is not counted as
authored.

docs/adr/** at the branch point, 139 records:

spelling hits records this gate reads it?
A path-anchored (a path, a colon, digits) 6 3 yes — and all 6 carry anchor-exempt markers, which is why the gate was green
B backticked bare continuation 0 0 yes
C tilde-backticked number 0 0 yes
D path-less bare colon continuation 5 2 ⛔ no
E the L pin as a markdown link-target fragment 2 1 ⛔ no
F the L pin bare in prose (E is a subset of F) 12 4 ⛔ no

Two of D's five are ADR-0047's dev-server ports, which are not line pointers at
all, so the caliber's verdict is: 15 real line pointers across 5 records were
invisible to this gate
— not 5, and not 2.

The card's 「未復現」 section, reproduced

The handed-over report claimed 12 fragments across ADR-0020×6, ADR-0021×2,
ADR-0034×2, ADR-0077×1, and the filing seat could neither reproduce the 12 nor
make the parts sum to it. At spelling F the measurement is 12 across exactly
those four records, distributed 0020×6 · 0021×2 · 0034×3 · 0077×1. So the
total was right, the 0034 subtotal was one short, and the class it
counted was the L pin rather than the narrower #L fragment the filing seat
measured at 2. ⛔ Neither reading was wrong; they were two calibers. Both are
reproduced above, from one instrument, with the light on.

The reading that decided deletion, not correction

#13556 ruled this class deleted with no transition period, on a 4/4 sample of
pointers landing on unrelated code. The card states plainly that it did not take
that sample. Taken here, on every pointer the caliber found — 15/15 stale:

pointer claims to point at line N is, today
security-plugin.ts 626 (ADR-0096 ×3) the getReadFilter read-filter fall-open a JSDoc closing line inside the SecurityReportSink logging-sink interface
record-validator.ts 198 (ADR-0020 ×2) validateRecord a comment inside the ValidationError constructor; validateRecord is declared at 1185 — 987 lines away
object.zod.ts 354 / 371 / 410 / 765 (ADR-0020 ×4) "managed-by buckets and toolbar lifecycle actions" a closed-batch comment, a where-guidance comment, const nearMiss =, an author-time-rejection comment. The file's lifecycle block now sits at 818-1010 as an ADR-0057 retention/ttl/storage/archive policy
engine.ts 806 + 961 (ADR-0021 ×2) the two metadataArrayKeys lists a blank docblank line and a comment about answering one mistake two ways. metadataArrayKeys is discussed at 2327, where a comment says it USED to be two lists
engine.ts 1811, sql-driver.ts 561-563, sql-driver.ts 548-600 (ADR-0034 ×3) a deadlock warning, a savepoint, the sequences table a docblock tail, a datetime-canonicalisation docblock, a presentation-convergence comment. SEQUENCES_TABLE is at 181
engine.ts 3217 (ADR-0077 ×1) the sandbox-runner ALS limitation a comment about callers probing a private guard. The setImmediate discussion is at 4572 / 15700 / 15866

The structural clue the card asked to be verified holds: ADR-0096's inherited
path does sit in the same table row as the bare number, in a different cell. That
is why the path-anchored pass cannot see it — there is no path adjacent to the
number to anchor on.

What this change does, and what it deliberately does not

Three deletion commits, one per distinct stale target, each carrying its own
sample in the message; then one commit on the gate.

ADR-0096 — deleting a path-less number leaves the sentence with no referent,
so two of the three take the symbol anchor the record's own table row already
uses for that site, and the third points at the row above it. Both substitutions
resolve: the gate's anchor count rises 2093 to 2095 and 2 of those are new
CHECKED pointers rather than merely shorter prose.

ADR-0020 — the path half of the links is correct and survives, so these are
pure fragment deletions dropping to the file-level target the gate's own remedy
text names as its second exit. ⛔ The prose is not repaired: whether ADR-0020's
parenthetical still describes object.zod.ts is a separate defect and takes its
own card, exactly as this gate's header instructs.

The gate — summaryLine() now names the vocabulary it judged and states that
the two path-less spellings are opt-in and off here. The self-test holds the
claim in three parts, and the middle one is the control that makes the battery
worth running: the corpus DECLARES the narrower vocabulary; the shared grammar
really does see the wider one on the same text when asked; and the sentence names
both halves and can no longer end in the unqualified form. Battery floor 17 to 22.

⛔ Route (c), widening the vocabulary, is MEASURED and NOT taken

pathlessLineCitations: true on this corpus yields 17 findings across 6
records
. Two of them are ADR-0047's dev-server ports: correct prose no author
can repair, whose only remedy is the maintainer-only exemption marker. Four of
the six records are outside this card's declared file surface. The alternative
route is worse: unspannedAnchors: true yields 8 findings, 6 of them
fabrications
— correct relative ADR-to-ADR markdown links reported as missing
FILES because a heading fragment reads as a symbol.

So the price tag is real and the decision is not this sentence's to make. What
the sentence owed was to stop reading as though the decision had already gone the
other way. ⛔ It also cannot be taken inside this fence at all: the vocabulary
lives in scripts/symbol-anchors.mjs, shared by five corpora, and that file is
outside the declared surface. This change does lower the bill: the 17 becomes
8 once these nine pointers are gone.

Evidence

Gates — dispatch-gates --repo objectstack-ai/objectstack --commands derived
37 families from the real three-path change set; all 37 run, all exit 0,
reconciled with --ran carrying exit codes: "37 derived families accounted for —
37 run, 0 NOT-MEASURED (a DERIVED zero — all 37 recorded an exit code and none of
them is 3)"
. One needed a second pass:
@objectstack/lint check:doc-formula-expressions first exited 3 —
PREREQUISITE NOT MET
, which its own text declares is not a finding; after
turbo run build --filter=@objectstack/formula --filter=@objectstack/lint it
exits 0.

Ablation, 4 legs, all on the final tree at c38eead. Every leg proved its
mutation reached disk (anchor count dropped, blob hash moved) before the run was
read, and every restore was verified by blob-equals-HEAD plus an empty
git diff HEAD — not by an exit code:

leg mutation the case that fired
a corpus declaration flipped to pathlessLineCitations: true the narrower vocabulary must be this corpus's DECLARATION
b the probe literal stripped of both spellings the shared grammar must SEE both path-less spellings when asked … got 0
c1 the unqualified sentence restored wholesale the verdict must name BOTH the vocabulary it judged and the one it did not
c2 both halves named, the unqualified claim re-appended the verdict must not end in the unqualified claim

Leg b is the one that matters: without it, legs a and c would hold just as
well on a day when the wider grammar had stopped matching anything, and the new
caveat would be documenting a dead rule.

Lint, a declared narrowing rather than the farm run. Three readings, all at
c38eead: ① the examined population comes from eslint's own config, not a
guess — ESLint#isPathIgnored answers true for both docs/adr/** records
(they are outside eslint's population entirely) and false for the script;
② --format json reports 1 file linted, 0 errors, 0 warnings; ③ invariance —
eslint.config.mjs states in its own words that this repo "runs one
eslint.config.mjs, which never enables type-aware linting (no
parserOptions.project, no typed @typescript-eslint rules) for ANY file"
, and
a grep for that configuration finds only the comment saying it is absent, so no
verdict on an untouched file can move as a function of this diff. The repo-wide
eslint . --no-inline-config remains CI's run.

Control bytes — check:nul-bytes green, plus a direct scan of the touched
files for the non-NUL control bytes it does not cover: no matches.

Changeset: skip-changeset, measured

Nothing published moves. The root package is private: true, and 0 of the 70
public packages
has a files[] entry that escapes its own directory or names
docs/adr. Measured at the byte level against the built output with a matched
positive control: a grep for the exact text this diff changed
(record-validator.ts plus the fragment, the four-pin list, the two :626
sentences, the old verdict phrase) across every built dist/ exits 1, while
the same grep shape fires on 168 files for a symbol that is published. One
near-miss worth naming: published .d.ts files DO cite ADR-0020 by URL, so the
record's filename ships — its text does not.

Acceptance notes

⛔ Out of the declared file surface, so measured and reported rather than touched:

维护者速读(草稿)

改了什么. 两件事。一是删掉九个指向源码行号的旧指针——三个在 ADR-0096、六个在
ADR-0020,删前逐个抽样,九个全部指向无关代码(最离谱的一个偏了 987 行,落在一个错误类的
构造函数里)。二是把门禁那句结语从「0 line anchors survive.」改成说清它究竟量了哪几种
写法、哪两种没量。门禁逻辑一行没动,只动了它对外的说法,以及三条防止这句话退回去的自测。

为什么改. #13556 已裁决「ADR 行号锚」这一类无过渡期删除,依据是 4/4 抽样全烂。但门禁
的词汇表比那一类窄,于是两种写法活了下来:一种把行号藏在 markdown 链接的 fragment 里,
一种把行号与路径拆在同一表格行的不同单元格里。门禁读不到它们,却仍然印「0 … survive」。
读数是真的,那句话不是——这就是本卡判据 (b)。一句印在绿灯里的假零,下一个人会据它认定
这一类已经清完。

风险与代价(含回滚). 风险很低。① 不发布任何东西:根包是私有包,70 个公开包里没有一个
的 files[] 能伸到 docs/adr 或根 scripts/,已用字节级 grep 配正控实测,故声明
skip-changeset。② 本卡故意没有去扩宽门禁词汇表,因为实测代价摆在这里:打开那个开关
会在 6 份 ADR 上红 17 条,其中 2 条是 ADR-0047 的开发服务器端口号——那是完全正确的散文,
作者无法修,唯一出路是维护者专属的豁免标记;另有 4 份 ADR 在本卡申报的文件面之外。换一条
路更糟:unspannedAnchors 会红 8 条,其中 6 条是把正确的 ADR 互链误报成「文件不存在」。
⇒ 扩词汇表是一个带价签的决定,不该由一句结语替您做。 本卡把账单从 17 降到 8。
③ 回滚成本近乎零:四个 commit 各自独立,git revert 任意一个都不影响其余,删 ADR 指针
那三个纯属文本删除。④ 唯一需要您过目的判断:ADR-0096 有两句删掉数字后会没有指代对象,
本卡让它们改用该 ADR 自己那张表里已有的符号锚(#getReadFilter),没有新造事实;
这两处因此从「不可检查的数字」变成了「门禁会检查的锚点」。

席位意见.

你要做的. ① 这是受管面(docs/adr/**),CI 绿也不由 AI 座位合并——需要您亲手确认与
合并。② 请重点看 ADR-0096 那两句的指代替换读起来对不对(是否仍然说的是同一个站点);其余
七处是纯删除,不改变任何论断。③ 如果您认为剩下那三份 ADR(0021 / 0034 / 0077)的六个指针
应当一并清掉,或者愿意为扩宽门禁词汇表付那 8 条红灯的账,请说一声——两件都已量好,另立卡
即可动手。


Generated by Claude Code

The #13556 ruling deleted the ADR line-anchor class with no transition
period. Three pointers to `security-plugin.ts` line 626 survived it as a
bare `:NNN` continuation -- the path and the number split across
different table cells, a shape the gate's vocabulary does not read.

Sampled before deleting, which is what #13556's own basis was:
`packages/plugins/plugin-security/src/security-plugin.ts` line 626 is
today a JSDoc closing line inside the `SecurityReportSink` interface --
a logging sink type, not the read-filter fall-open the three sentences
describe. The pointer is stale.

Deletion alone would leave two of the three sentences without a
referent, so those two take the symbol anchor the ADR's own table row
already uses for that site (`#getReadFilter`, first cell of the same
row); the third points at the row above it instead. Both substitutions
resolve, so the gate's anchor count rises 2093 -> 2095 and the two new
pointers are checked rather than merely shorter.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-authored-by: Claude <noreply@anthropic.com>
The second shape that survived #13556's no-transition deletion: the line
number written as a markdown link-target fragment, `<path>.ts#L198`,
where the gate's grammar reads only a backticked span and so sees
neither the path nor the number.

Sampled before deleting. Both fragments claim to point at
`validateRecord` in
`packages/objectql/src/validation/record-validator.ts`. Line 198 of that
file today is a comment inside the `ValidationError` constructor;
`validateRecord` is declared at line 1185. The pointer is 987 lines off
and lands on unrelated code -- the same failure shape as the 4/4 sample
#13556 ruled on.

The path half of both links is correct and survives, so this is a pure
deletion of the fragment: the links drop to the file-level target the
gate's remedy text names as the second exit. Upgrading them to checked
`path#symbol` anchors -- the idiom this same record already uses at :57
-- would be a prose change the ruling does not call for and is left to
the maintainer.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-authored-by: Claude <noreply@anthropic.com>
A third spelling of the same class, found by widening the caliber past
the two literals the card measured: `L354/L371/L410/L765` written as
bare prose beside a file-level link, carrying no path of its own.

Sampled before deleting -- 4/4 stale. Those four lines of
`packages/spec/src/data/object.zod.ts` are today a comment about a
closed batch site, a comment about `where` guidance, `const nearMiss =`,
and a comment about author-time rejection. None is the "managed-by
buckets and toolbar lifecycle actions" the sentence cites them for; the
file's `lifecycle` block now sits at 818-1010 as an ADR-0057
retention/ttl/storage/archive policy.

The file-level link was already there and is kept, so the sentence's
claim that `lifecycle` is overloaded in that file stays checked and
stays true. ⛔ The prose is NOT repaired: whether the parenthetical's
specifics ("managed-by buckets", "toolbar lifecycle actions") still
describe that file is a separate defect and takes its own card, exactly
as this gate's header instructs.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-authored-by: Claude <noreply@anthropic.com>
…er than the vocabulary it read

The green line ended `0 line anchors survive.` -- unqualified, and
measured false while it printed. Nine pointers of the class the #13556
ruling deleted were live in `docs/adr/**` in two spellings this corpus
does not judge: the path-less bare colon continuation (three in one
record, the path in a different table cell from the number) and the `L`
pin, bare in prose and as a markdown link-target fragment (six across
two records). The reading was right; the sentence spoke for the whole
class instead of for what it read.

So the repair is the CLAIM. `summaryLine()` now names the vocabulary it
judged and states that the two path-less spellings are opt-in and off
here. Widening instead was MEASURED and is not taken in this change:
`pathlessLineCitations: true` on this corpus yields 17 findings across 6
records, 2 of them an ADR's dev-server ports -- correct prose whose only
remedy is the maintainer-only exemption marker.

The self-test's new battery holds the claim in three parts, and the
middle one is the light-on-a-literal control: the corpus DECLARES the
narrower vocabulary; the shared grammar really does see the wider one on
the same text when asked (so the caveat is a measurement, not a hedge
over a rule that matches nothing); and the sentence names both halves
and can no longer end in the unqualified form. Flipping the declaration
now forces the verdict to be re-stated in the same edit. Battery floor
17 -> 22.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

维护者速读(终稿)

席位 domain:spec#4 · 座位贴 #18917 · 2026-09-18T06:30Z。这是终稿:我对照自己读的 diff 校正了 PR 正文里 dev 的草稿并填了席位意见。你只读这一条即可,⛔ 不必读草稿。

改了什么

两件事,3 个文件、+83/−13。

一是删掉 9 个指向源码行号的旧指针,我从 diff 里重新数过:ADR-0020 6 个(:45 与 :108 的两个 #L198 链接片段,加 :61 一行里的 4 个 L354/L371/L410/L765),ADR-0096 3 个(裸 :626)。ADR-0020 的 6 个是纯删除;ADR-0096 的 3 个必须做指代替换,因为一个不带路径的裸数字删掉后句子就没有指代对象了 —— 其中 2 个改用该 ADR 自己那张表里已有的符号锚 …security-plugin.ts#getReadFilter,1 个改成散文回指。⇒ 那 2 处因此从「不可检查的数字」升级成「门禁会检查的锚点」。

二是把门禁结语从「0 line anchors survive.」改成说清它究竟量了哪几种写法、哪两种没量。 门禁的判定逻辑一行没动,只动了它对外的说法,外加自测。

为什么改

#13556 已裁决「ADR 行号锚」这一类无过渡期删除,依据是 4/4 抽样全部指向无关代码。但门禁的词汇表比那一类窄,于是两种写法活了下来:一种把行号藏在 markdown 链接的 fragment 里,一种把行号与路径拆在同一表格行的不同单元格里。门禁读不到它们,却仍然印「0 … survive」。

读数是真的,那句话不是 —— 这就是本卡的判据 (b)。一句印在绿灯里的假零,下一个人会据它认定这一类已经清完。

⭐ 我自核了那个「烂化」是真的,没采信报告:record-validator.ts:198 今天是 ValidationError 构造器里的一行注释,而 validateRecord 实际在 :1185(相差 987 行);security-plugin.ts:626 今天是一个日志 sink 接口里的 JSDoc 收尾 */,getReadFilter 在 169/371/943/1225/1492,没有一处靠近 626。⇒ 处方是删除而非「改对行号」,与 #13556 一致。

风险与代价(含回滚)

很低。

  1. 不发布任何东西:根包私有,70 个公开包没有一个的 files[] 伸得到 docs/adr 或根 scripts/,已用字节级 grep 配正控实测 ⇒ skip-changeset 成立。
  2. 本 PR 故意没有扩宽门禁词汇表,而这是带价签的克制:打开那个开关会在 6 份 ADR 上红 17 条,其中 2 条是 ADR-0047 的开发服务器端口号(:3000 / :5180)—— 那是完全正确的散文,作者无法修,唯一出路是仅维护者可用的豁免标记;另有 4 份 ADR 在本 PR 申报的文件面之外。换另一条路更糟:unspannedAnchors 会红 8 条,其中 6 条是把正确的 ADR 互链误报成「文件不存在」。⇒ 本 PR 把这张账单从 17 降到 8。
  3. 回滚近乎零成本:commit 各自独立,git revert 任一个不影响其余;ADR 那两个文件的改动是纯文本。
  4. 自测:floor 从 17 抬到 22(+5 条断言,表达代码注释自己命名的 3 个 case)。⭐ 其中 (b) 是「拿字面量点亮仪器」的控制腿 —— 没有它,另外两条在「那条更宽的语法哪天什么都匹配不到」的日子里照样全绿,而那句 caveat 就变成在描述一条死规则。这条设计是对的。

席位意见

我判 ACCEPT,并且认为这张 PR 的质量高于它的体量。 三个具体理由:

  1. 它修的是「主张」而不是「读数」。 面对「一个真测量配一句假结论」,最省事的做法是把结论删掉或含糊过去;它选择让结语自陈边界 —— 印出它判了哪四种拼写、明说这不是对被删那一类的普查。这是把假零变成真话的唯一正确改法。
  2. 它拒绝继承任何人的计数,包括我的。 立卡席量到 2、递过来的报告说 12、我派发时量到 5 —— 它先定口径、每条正则先拿字面量点亮、给两种无路径拼写配负控制,最后量到 15,并且让那个 12 在某一种拼写下精确复现(0034 的分项少了 1)。⛔ 没有一个前人的数字被照抄。
  3. 它在我划的停手线上停住了。 我在 Zone 2b 写明「扩词汇表若代价大就停下回报,别合并后才发现」——它测了 17 条红、发现其中 2 条作者根本修不了,于是没做,把决定连价签一起交上来。⛔ 这不是没干完,这是按指令交付了一个决定。

⚠️ 两点我要你知道的保留:① CI 尚未收敛 —— 我 06:27Z 读:17 绿 · 11 skipped · 5 条仍在跑(含必查的 Lint & Repo Gates 与三条 Type Check)· 0 条红。⛔ 请在全绿之后再合。② 本 PR 只清了它申报文件面内的 9 个;同一类还有 6 个活在 ADR-0021/0034/0077,我已按自己的重新测量另立 #18932(6/6 抽样已做完,承接是机械的)。

你要做的

一个动作:确认并合并 #18925(等 CI 全绿)。

看的时候请重点只看一处:ADR-0096 那两句的指代替换读起来是否仍指同一个站点(把 :626 换成 …security-plugin.ts#getReadFilter)。其余七处是纯删除,不改变任何论断。

⛔ 这是受管面(docs/adr/**),CI 绿也不由 AI 座位合并。我已向 os-zhuang 与 hotlong 两个授权账户请求复审。若你希望把 ADR-0021/0034/0077 那 6 个一并清掉,#18932 已就位,说一声即可派发。


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

CI 现已全绿 —— 速读里那句「等全绿之后再合」的条件已满足。 席位 domain:spec#4,2026-09-18T06:56Z 读数。

06:30Z(贴速读时) 06:56Z(现在)
success 17 24
skipped 11 15
仍在跑 5(含必查 Lint & Repo Gates) 0
非绿 0 0

⇒ 本 PR 现在没有任何未完成或非绿的 check。⛔ 我仍然不合它 —— 受管面(docs/adr/**)的合并是你的动作,这条只是把我自己设的那个前置条件报销掉,免得你为一个已经不存在的理由等着。

hotlong 与 os-zhuang 的复审请求仍在。速读(终稿)在本 PR 上一条评论,看的时候重点仍只有一处:ADR-0096 那两句把 :626 换成 …security-plugin.ts#getReadFilter 之后是否仍指同一个站点。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 18, 2026 11:24
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit 64de4c5 Sep 18, 2026
44 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-18899-adr-symbol-anchors-blind-forms branch September 18, 2026 11:53
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…stack-ai#13556 fence (objectstack-ai#19022)

Fixes objectstack-ai#18932

Clause-②: no

Deletes the six stale line pointers that ADR-0021, ADR-0034 and ADR-0077
still carried after the objectstack-ai#13556 class removal — six pointers across five
lines in three records. The prescription is **deletion**, not
renumbering: the ruling retired this *class* of pointer, and all six
were sampled as naming code that has nothing to do with the prose citing
them.

Note on spelling in this body: this repository's body sanitizer eats
angle-bracket-shaped fragments, so every such fragment below is written
in placeholder words. One substitution is load-bearing and is flagged
inline. The byte-exact text is the diff.

## The five sites, each judged on its own

Each pointer was removed so that the surrounding prose keeps every other
fact it was carrying — in particular the referent (a path or a symbol
name) survives at every site, so nothing needed a replacement referent.

**1. ADR-0021 line 372** — removed `, L806 + L961`; `(**two** lists)`
deliberately kept.

```
- **Registration touches 4 surfaces**, not 1: `kernel/metadata-type-schemas.ts`, `kernel/metadata-plugin.zod.ts` (enum + `DEFAULT_METADATA_TYPE_REGISTRY`, `loadOrder` LESS-THAN report/dashboard), `shared/metadata-collection.zod.ts` (`MAP_SUPPORTED_FIELDS` + `PLURAL_TO_SINGULAR`), `objectql/engine.ts` `metadataArrayKeys` (**two** lists).
```

⚠️ `LESS-THAN` above stands for a single less-than character that is
pre-existing, untouched prose. It is spelled out only to survive this
body; the file itself is unchanged there.

**2. ADR-0034 lines 42-43** — removed ` around L1811`.

```
`engine.transaction()` threads the open transaction into the driver options of
the *top-level* write via `buildDriverOptions` (`engine.ts`, which even warns
about this deadlock). But internal reads/writes performed **during**
```

**3. ADR-0034 line 57** — removed ` L561-563`.

```
  savepoint on the parent transaction (`sql-driver.ts`). The gap is
```

**4. ADR-0034 lines 59-61** — removed `L548-600` and the comma that
introduced it; the bullet keeps all four of its symbol referents.

```
- `driver-sql` already implements a **persistent, atomic sequence**
  (`SEQUENCES_TABLE` + `getNextSequenceValue` with `forUpdate` + seed-from-max).
  See "Autonumber (objectstack-ai#1603)" below — this is mostly already solved at
  the driver level.
```

**5. ADR-0077 line 51** — removed `, ~L3217`.

```
2. **The flow path does not thread the transaction explicitly**, and ALS is fragile here: it does **not** survive `setImmediate`/deferred-promise boundaries (the documented sandbox-runner limitation in `engine.ts`). Any atomicity would be *accidental* (inline before-path only) and *untested* — not a guarantee an author may rely on.
```

## Evidence

Base for every reading below: `origin/main` = `64de4c5764` (the merge of
objectstack-ai#18925), merged into this branch. The dispatch order was written against
`43f4766889` and predicted "4 files before, 1 after (ADR-0020
remaining)"; objectstack-ai#18925 landed at 11:53:36Z and cleared ADR-0020, so the
correct expectation is **3 files before, 0 after**. Both the corrected
expectation and the reading below were taken first-hand on this tree.

The probe is the shared grammar's own `PATHLESS_L_CITATION` regular
expression, copied verbatim from `scripts/symbol-anchors.mjs` — the
path-less L-pin spelling (optional tilde, the letter L, two or more
digits, optional dash-range, word-boundary on both sides). It is not
re-invented here.

| leg | reading |
|---|---|
| **BEFORE** — base tree, whole `docs/adr/**` | **3 files / 6 matches**:
ADR-0021 ×2, ADR-0034 ×3, ADR-0077 ×1 |
| **AFTER** — this tree, whole `docs/adr/**` | **0 files / 0 matches** |
| **LIGHT-ON** (same probe, same run, corpora this diff never touches) |
`docs/audits/2026-08-partial-retirement-annotation-signal.md` = **6**,
`docs/launch-readiness.md` = **3** |
| **FORGED NEEDLE** | a fabricated pin reads **0** across `docs/adr/**`,
and **1** in a scratch file where it was planted with the same probe |
| **DARK** — `docs/adr/0047-object-ui-run-modes.md` | blob `b50b52863d`
on `origin/main` and `b50b52863d` in this worktree — byte-identical, not
in the diff |

The two light-on corpora and the planted needle are what make the zero a
reading rather than a non-measurement: the same probe, in the same run,
read non-zero in three named places.

### Instrument reach radius, and a known target outside it

The AFTER reading is a **zero**, so the radius is stated by form and a
known target outside it is named — both measured, not asserted.

**Inside the radius.** Content layer: the raw line text of every file
under `docs/adr/`, read line by line. Form: an optional tilde, the
letter `L`, two or more digits, an optional dash-range, with a non-word
character required on both sides. Measured, this radius is *wider* than
the gate's in two ways worth naming: an L-pin written inside a markdown
link target (the hash-L-digits form that objectstack-ai#18925 dealt with) **is** read
— measured, 1 match on a planted sample — and so is one written inside a
code fence, which the gate deliberately skips as quoted material. So
neither of those is a valid out-of-radius target here.

**Outside the radius, by form.** The path-anchored colon spelling (a
filename, a colon, digits), the path-less colon continuation (a bare
colon and digits continuing a filename named earlier), and any
single-digit L pin. Also outside, by population: every corpus that is
not under `docs/adr/`.

**The known target.** `docs/adr/0065-sdui-styling-model.md` line 96
carries a live path-anchored colon anchor into
`packages/sdks/src/types/builder-block.ts` at line 42, tagged with the
maintainer-only exemption marker. It is in the same corpus, the same
file type and the same content layer as everything this PR measured — it
differs from the deleted pointers **only in form**. Measured on that
exact line: the L-probe reads **0**, the colon probe reads **1**. Across
`scripts/*.mjs` the same asymmetry holds at scale — 127 colon-form
targets present, the L-probe reads 0 on all 127.

⇒ The zero above is therefore a reading about the L-pin form in
`docs/adr/**`, and ⛔ not a claim that no line pointer of any spelling
remains anywhere. The colon form is judged by
`check-adr-symbol-anchors`, which is green.

**What the gate says, and why this diff is invisible to it.** The ADR
corpus declares `pathlessLineCitations: false`, so
`check-adr-symbol-anchors` never judged this spelling — before or after.
Measured: the gate-visible colon form reads **0 before and 0 after** in
all three records, and the gate is green on both trees. objectstack-ai#18925 (already
landed) is what stopped its verdict line from *claiming* a census it had
not taken; this PR removes the pointers that claim was measured false
against. No gate script, corpus vocabulary or `pathlessLineCitations`
switch is touched here.

**Gates.** `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 18 families on the merged tree; all
18 run, all exit 0, exit codes captured before any pipe and reconciled
with `--ran`: *18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN*. One family
(`check:doc-formula-expressions`) first exited **3 — PREREQUISITE NOT
MET**, which is a refusal to measure and not a finding; after building
`@objectstack/formula` and `@objectstack/lint` as its own output
instructs, it exits 0.

**Changeset — `skip-changeset`.** Per-path reading over the 70
non-private packages that declare a `files[]`: all three diff paths are
`docs/adr/**`, none sits inside a published package directory, and none
matches any `files[]` entry. Nothing this PR changes is shipped by any
package.

## Acceptance notes

Observed while working, deliberately **not** filed and **not** fixed
here:

- The blocker the card records is confirmed present and untouched:
`docs/adr/0047-object-ui-run-modes.md` lines 48-49 carry two dev-server
ports that the shared `pathlessLineCitations` grammar would read as line
citations. That file is byte-identical here. Anyone widening the ADR
corpus vocabulary still has to settle those two first. Successor: that
future vocabulary-widening PR — it cannot avoid this file.
- `docs/audits/2026-08-partial-retirement-annotation-signal.md` (6) and
`docs/launch-readiness.md` (3) carry the same path-less L-pin shape
outside `docs/adr/**`. Whether other corpora should be swept is a
scoping question no one has asked; they are used here only as light-on
controls. Successor: none today.

## Governance

`docs/adr/**` is a governed surface. This PR stays **draft**; it is not
merged, not queued, and auto-merge is not armed. Landing waits for the
maintainer's approval.

## 维护者速读(草稿)

**改了什么** —— 三份 ADR(0021 / 0034 / 0077)的散文里删掉 6 个指向源码行号的指针,分布在 5
行上。只删数字,每句话的路径名或符号名都留着,句子读起来完整。

**为什么改** —— objectstack-ai#13556 已经裁定「ADR 行锚点」这一类整体删除、无过渡期。objectstack-ai#18925 清掉了 ADR-0020 与
ADR-0096 里的,这 6 个当时在那张卡申报的文件面之外,所以留了下来。这 6 个都被抽样核过,指向的是今天完全无关的代码 ——
留着它们会把读 ADR 的人(和 AI)引到错的位置。

**风险与代价(含回滚)** —— 风险很低:纯散文删字,不动任何代码、门禁脚本或开关,任何包的发布内容都没有变(所以
`skip-changeset`)。回滚就是 revert 这一个 PR,没有数据迁移、没有生成物、没有下游依赖。唯一的信息代价是读者不再能从
ADR 直接跳到某一行 —— 而那正是本次要消除的东西,因为那些行号早就指错了。

**席位意见** ——

**你要做的** —— `docs/adr/**` 是受管面,需要你本人的 approval
才能落地。请看一眼上面五处改后的整句,确认散文没有丢信息(尤其 ADR-0021 那句要保住「**two**
lists」这个事实),然后决定是否 approve。

---
_Generated by [Claude
Code](https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3)_

Co-authored-by: Claude <noreply@anthropic.com>
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 size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants