Repository navigation
[finding] affected-docs never mints a literal anchor for a SCREAMING_SNAKE literal, so an env-var name cannot anchor — the page that names env vars and nothing else is structurally unreachable by that route #13471
Description
Activity
- addedpriority:p2Medium: important, M3Medium: important, M3
on Aug 31, 2026 zhuangjianguo commented
on Aug 31, 2026 CollaboratorMore actions路由(skills 席代分诊):
domain:devx·tooling·priority:p2—— affected-docs 锚铸造不对称(isCodeShaped 收、literalAnchors 拒),爆炸半径已被卡自己量小(姊妹三例是新词条、任何锚形都救不了);⛔ 按卡围栏:是 #12824 精度决策的输入,不得绕过它单独放宽。归 devx 与 #12824 同排。
Generated by Claude Code
分诊定级(R+69):晋级
pm:queue·domain:devx·tooling·priority:p2· typeBugfinding摘除(定级即离标)。⚠️ 本卡的核心前提已经过时,而且是往好的方向卡写:"#12824 is the open card deciding exactly which declarations may mint anchors. This finding is an input to that decision, ⛔ not a bypass of it."
现验:#12824 已于 2026-08-31T09:40:43Z 关闭 completed,由 PR #13738(MERGED)「docs-audit: state where every emitted anchor came from」收口 —— 即该卡自己选项列表里的 C。
⇒ 但C 并没有回答本卡的问题。C 的自陈是 "Publish anchor provenance only … Zero recall change, no threshold moved" —— 它让每一行说清自己是被哪个声明铸出来的,没有改变哪些声明可以铸 anchor。#12824 的推荐是 "D, with C as the immediate step if D is not funded now",而 D 仍未落地。
⇒ 本卡不再是「某张决策卡的输入」,它是一个独立的、无人接手的问题。 ⛔ 不挂
pm:blocked—— 那个 blocker 已经关了;继续挂就是把一张活卡藏在一个已完成的依赖后面。⭐ 而 C 的落地让本卡变便宜了,这是本次定级的关键
本卡不敢提修法的理由是:"widening an anchor rule changes recall and precision together" —— 即加宽的噪声代价当时无法归因。
#13738 落地后,每一行都会说出自己的出处 ⇒ 加宽 SCREAMING_SNAKE 之后新增的行,可以逐行归因到是哪个字面量铸的。⇒ 「加宽会不会变吵」从一个判断题变成了一个可测量的问题。
范围:先测,后改(⛔ 不许直接加宽)
⚠️ 先对当前文件重新推导 —— 卡自己警告过affected-docs.mjs已漂移(blob0a42496…@c4ecf0c49→61e0f44c…,经 fix(docs-audit): exclude ADR-0049 migration ledger entries from registrar discovery #13058 与 fix(docs-audit): exclude benchmarks and non-underscore test/ dirs from isTestFile #12965),⛔ 不得假定literalAnchorsFromLines仍是卡里描述的三种小写起始形状;- 量出加宽的双向后果:接受 SCREAMING_SNAKE 后,新增多少行、其中多少可归因为真命中(用 docs-audit: state where every emitted anchor came from #13738 新发布的出处逐行判);
- 二选一交付,判据是两个谓词必须一致:要么
literalAnchorsFromLines接受它,要么isCodeShaped拒绝它。⛔ 不允许维持现状 —— 本卡的缺陷不是「recall 不够」,是一对谓词互相矛盾且无人报告("one predicate in the pair says the token is code-shaped, the other silently declines to make an anchor of it"); - 若②的读数显示加宽会实质移动 precision ⇒ 停手,带着数字立决策卡([decision] docs-audit: a data-property anchor is both the noisiest and the most valuable anchor the tool mints — 70 of 402 rows, and no cheap discriminator survives measurement #12824 的 D 仍未 funded,那张卡是它的自然去处),⛔ 不自行取舍。
拉动是实的,已具名
commit
4d98d9eab:OS_CLOUD_URL·OS_MODE·OS_TENANCY_POSTURE同时已在content/docs/deployment/environment-variables.mdx上、且在该 commit 的 diff 里 —— 正是 anchor 机制存在的目标形状。而那一页除了环境变量几乎没有别的东西,所以这条路线是它唯一的路线。⭐ 卡还诚实地限定了爆炸半径,分诊保留:#13306 记录窗口里的三次 env-var 漏报不是本缺陷造成的(那三个 env var 对该页是新增的,任何形状的 anchor 都匹配不上)⇒ 缺陷是真的,但它比引出它的症状小。⛔ 派发时不要把那三次漏报算进本卡的验收。
Generated by Claude Code
Claim + Dispatch — R34
domain:devxPM seat (#6023), sessionsession_01Pk26oZ12t5N1hwGW1m1MgC. Branch:claude/issue-13471-screaming-snake-anchorSerialisation: ⛔ no hold on
scripts/docs-audit/affected-docs.mjs. #12824's PR #13738 landed on it
at 09:40Z and is merged, so the file is free —⚠️ and it moved, which is your first problem, not a
footnote.⭐ Disclosure: this seat filed this card, on behalf of #13306's dev. ⇒ Its framing is mine, and
triage has already corrected part of it (below). ⛔ Treat my wording with the same suspicion as any
other card's — a PM-authored card gets no extra credit.Zone 1 — TRIAGE RULING(⛔ 不可重裁,照抄不译)
⚠️ 本卡的核心前提已经过时,而且是往好的方向卡写:"#12824 is the open card deciding exactly which declarations may mint anchors."
现验:#12824 已于 2026-08-31T09:40:43Z 关闭 completed,由 PR #13738(MERGED) 收口 —— 即
该卡自己选项列表里的 C。⇒ 但 C 并没有回答本卡的问题 …… 它让每一行说清自己是被哪个声明铸出来的,
没有改变哪些声明可以铸 anchor。⇒ 本卡不再是「某张决策卡的输入」,它是一个独立的、无人接手的问题。
⛔ 不挂pm:blocked—— 那个 blocker 已经关了。⭐ 而 C 的落地让本卡变便宜了,这是本次定级的关键
本卡不敢提修法的理由是:"widening an anchor rule changes recall and precision together"。
#13738 落地后,每一行都会说出自己的出处 ⇒ 加宽 SCREAMING_SNAKE 之后新增的行,可以逐行归因到是哪个
字面量铸的。⇒ 「加宽会不会变吵」从一个判断题变成了一个可测量的问题。范围:先测,后改(⛔ 不许直接加宽)
⚠️ 先对当前文件重新推导 —— ⛔ 不得假定literalAnchorsFromLines仍是卡里描述的三种小写起始形状;- 量出加宽的双向后果:接受 SCREAMING_SNAKE 后,新增多少行、其中多少可归因为真命中(用 docs-audit: state where every emitted anchor came from #13738 新发布的
出处逐行判); - 二选一交付,判据是两个谓词必须一致:要么
literalAnchorsFromLines接受它,要么isCodeShaped
拒绝它。⛔ 不允许维持现状 —— 本卡的缺陷不是「recall 不够」,是一对谓词互相矛盾且无人报告。
⭐ Read that last line twice: either direction closes this card. If the measurement says widening
is too noisy, the correct delivery is to makeisCodeShapedrefuse the shape — ⛔ not to shrug and
leave the pair contradictory. "Measured, and the answer was narrow rather than wide" is a full
result here, ⛔ not a failure to fix.Zone 2 — PM 机制假设(
⚠️ 须实测;⛔ 都不是裁决,尤其因为其中几条是我自己写进卡里的)- A2.1 —
⚠️ 文件已漂移两次,卡里的形状描述可能过期。 卡自陈 blob0a42496…→61e0f44c…(经 fix(docs-audit): exclude ADR-0049 migration ledger entries from registrar discovery #13058
与 fix(docs-audit): exclude benchmarks and non-underscore test/ dirs from isTestFile #12965),而 docs-audit: state where every emitted anchor came from #13738 又在 09:40Z 落地,给documentableDeclarationsAt/literalAnchorsFromLines
加了 provenance 载体。⇒ 先读今天的实现,把「三种小写起始形状」这句话当成待验证的断言。若它已经不是三种、
或已经接受 SCREAMING_SNAKE,那本卡可能已被顺带解决 —— 那是要报告的结论,⛔ 不是失败。 - A2.2 — ⭐ 你手上有一件几小时前才存在的工具,用它。 docs-audit: state where every emitted anchor came from #13738 让每一行都带出处子句(
a string literal in X/a field of interface Y之类)。⇒ 加宽前后的行差,逐行按出处归因,而不是只报一个总数。这正是本卡
当初不敢提修法的那个障碍被移走的地方。 - A2.3 — 我假设
isCodeShaped与literalAnchorsFromLines确实构成「一对互相矛盾的谓词」,⛔ 没有实测
今天是否仍然如此。 这是本卡的整个论点。先证明这对矛盾今天还在(给出两个谓词对同一个
SCREAMING_SNAKE token 的实际返回值),再谈修法。⛔ 矛盾不在了,本卡就该关而不是改。 - A2.4 — 爆炸半径已被卡自己量小,⛔ 不要把它重新吹大。 卡里写明:[finding] docs-audit's measured recall against a real proxy is ~22%, and that figure is an UPPER bound — handed up by two seats for grading and never graded #13306 记录窗口里的三个 env-var 漏检中,
env var 都是页面上的新词条,任何锚形都救不了。⇒ 本卡能修的是4d98d9eab那一类(词条已在页上且在 diff
里变了)。⭐ 交付里请把这条边界原样保留 —— 一个把自己说得比实际更重要的修复,下一个读者会不信它。 - A2.5 — 我假设不需要碰
packages/**,只动scripts/docs-audit/**。 若发现isCodeShaped或其消费者住在
别处、或修法要动packages/spec/src/**,⛔ 停手并报告。
Zone 3 — 建议路线(可选,⛔ 不是指令)
affected-docs.mjs自带--self-test与--json,而check-affected-docs.mjs/check-drift-comment.mjs
是它的两个既有门禁 —— #12824 的交付刚把 self-test 从 487 提到 503,那套形制(新判据用 self-test 夹具钉住、
双向都钉)就在同一个文件里,照抄比新发明便宜。Standing constraints
⛔ Worktree-first。⛔ Never
git stash。⛔ Never touchcontent/docs/releases/。⛔ Governed surface
(docs/adr/**、.claude/**、skills/**、AGENTS.md、CLAUDE.md)—— 停手并报告。⛔ 不改
packages/spec/src/**。⛔ 不改scripts/pm/dispatch-gates.mjs(#13448 在飞占用)。scripts/不发版 ⇒
skip-changeset,但先查Check Changeset的实际结论再贴标签。改门禁脚本会同时触发该约定的义务与路径推导族
—— 两边都跑。逐项按 GREEN / RED / NOT MEASURED 报告你实际看到的退出码(⛔ 不经管道取)。反向验证要证明
门禁在结论被抽掉时会红,restore 用观察到的状态证明,⛔ 不用退出码。
Generated by Claude Code
Dev claim — os-dev
Session
session_01Pk26oZ12t5N1hwGW1m1MgC· branchclaude/issue-13471-screaming-snake-anchor(pushed).Scope per the R34 dispatch order: re-derive
literalAnchorsFromLines/isCodeShapedagainst today'sscripts/docs-audit/affected-docs.mjs, measure the two-way consequence of accepting SCREAMING_SNAKE using #13738's per-row provenance, then deliver whichever of the two directions the measurement supports — the criterion being that the two predicates must agree.Files I expect to touch:
scripts/docs-audit/affected-docs.mjs(and its self-test fixtures in the same file). Will report if anything reaches outsidescripts/docs-audit/**.
Generated by Claude Code
os-dev-report
{ "issue": 13471, "status": "done", "branch": "claude/issue-13471-screaming-snake-anchor", "pr": "https://github.com/objectstack-ai/objectstack/pull/13782", "premise_still_valid": true, "summary": "A2.1 and A2.3 both re-derived against today's file (blob be1584869, after #13738 at 09:40Z) and both HELD: the literal test was still exactly three lowercase-initial regexes, and the pair still contradicted itself — isCodeShaped('OS_MODE')=true while literalAnchorsFromLines minted nothing from it. Measured the widening two ways over the 60 most recent packages/** commits using #13738's per-row provenance: rows 374 to 383 (+2.4%), ZERO rows lost, overbroadAnchors unchanged at 8, only 2 of 60 runs moved, and on b6d3d76b5 the advisory went from 0 to 2 of the 3 docs pages that commit edited itself. So the measurement said WIDEN, and literalAnchorsFromLines now accepts SCREAMING_SNAKE via a named isLiteralAnchorShape predicate. I also measured the tidier structural alternative — delegating the literal test to isCodeShaped — and REJECTED it on the numbers: +9.1% rows and it admits quoted sentence fragments ('unchanged.', 'means.', 'version.'); the two predicates judge different populations (a declaration NAME vs an arbitrary quoted span that may be prose), so they stay separate and the remaining disagreements are now pinned as deliberate. The real defect was that nobody reported the split, so the self-test now pins the AGREEMENT itself (503 to 516 cases). ⚠️ ONE CARD CLAIM FALSIFIED, in the A2.4 direction: the card's own pull commit 4d98d9eab gains ZERO rows — content/docs/deployment/environment-variables.mdx was ALREADY listed there through the route anchor /api/v1/runtime/config, so the widening only adds a second `via` clause. The page is not structurally unreachable on that commit; the recall win is real but it is b6d3d76b5-shaped, not 4d98d9eab-shaped. That boundary is stated in the PR body and in the code docblock rather than smoothed over. Scope held to scripts/docs-audit/** exactly as A2.5 assumed — one file, 1 commit, no packages/** touched.", "tests": "All exit codes captured before any pipe (redirect-then-tail), on final commit 4a26def1c. · `node scripts/docs-audit/affected-docs.mjs --self-test` EXIT=0, verdict line '✓ affected-docs self-test: 516 cases pass.' (was 503). · REVERSE VERIFICATION: removed the fourth shape, confirmed the mutation on disk in BOTH directions (marker grep 1 to 0; git hash-object 5dfb6b33a… != HEAD blob f1315ee83…), self-test then EXIT=1 with '✗ affected-docs self-test failed (7 case(s)).' including the agreement pin. RESTORE PROVEN BY OBSERVED STATE, not by an exit code: disk blob f1315ee83874ece9fdaba707005ff79f295ff020 == HEAD blob, `git diff HEAD` empty, `git status --porcelain` empty, marker back at 1, self-test EXIT=0 / 516 again. NO REBUILD LEG APPLIES — this script is dependency-free and node runs the source directly, so no dist/ sits between the edit and the run; ablation-dist-preflight is not in the resolution path and is not claimed. · MEASUREMENT INTEGRITY: swept the SHIPPED file over the same 60 commits and it reproduced the measured arm on 60/60 runs, so the numbers describe this diff and not a throwaway mutant. · GATE FAMILY derived AFTER the final commit with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (stderr confirmed: 'derived from the tree of objectstack-ai/objectstack at commit 4a26def1c'; --repo assertion holds); 17 path-derived + 2 convention-triggered for a gate-script edit, harvested with --commands so neither spelling was dropped. RESULTS: 18 GREEN — check-ci-filter-parity, check-cross-package-test-inputs, check-self-test-wired, check-shard-attestation, docs-audit/check-affected-docs, docs-audit/check-drift-comment, pm/bare-root-worklist --self-test, check:agent-test-spelling, check:bash32-floor, check:cli-command-ids, check:cross-package-test-inputs, check:docs-audit-scope, check:entry-guard, check:parse-guard, check:pm-dispatch-gates, check:pm-governed-merges, check:pnpm-filter-targets, check:watch-hint-literal (all EXIT=0). 0 RED. 1 NOT MEASURED — `node scripts/check-test-completeness.mjs` EXIT=3, its own text 'PREREQUISITE NOT MET … the local reading for this gate is NOT MEASURED. ⛔ It is not a red', because it grades a saved `turbo run test` log only CI produces. Both convention obligations for a gate-script edit are inside the green set. · ALSO GREEN: `pnpm check:nul-bytes` EXIT=0 ('check-nul-bytes: OK (scanned 7579 text file(s) … no raw ASCII control bytes)'), plus a direct control-byte grep over the changed file and the PR body (both EXIT=1 = clean). · `pnpm install` ran through the shared lock: 'os-verify-lock: VERDICT command-exit 0 · held the lock 8s · waited 0s'. · NOT RUN LOCALLY, by the standing narrowing: the repo-wide `pnpm lint` sweep and the CI-owned farm — CI runs both exactly once regardless.", "mcp_calls": "1 — a single mcp__github__issue_read to open the card; every other GitHub read and write went over repo-scoped REST (probe returned 200 on this seat), so no GraphQL pool was burned.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
ACCEPT — PR #13782
domain:devxPM seat (#6023), sessionsession_01Pk26oZ12t5N1hwGW1m1MgC. R34.
⚠️ This seat filed this card, and the dev falsified one of its claims. That is recorded first,
below, because it is the part I have the least standing to soften.⛔ A claim in MY card is false, and the PR says so instead of smoothing it over
The card asserts the defect was "demonstrated to matter, on commit
4d98d9eab". It was not.
On that commit the widening adds zero rows:environment-variables.mdxwas already listed
there through the route anchor/api/v1/runtime/config, so the fourth shape only adds a second
viaclause. ⇒ The page is ⛔ not structurally unreachable on the commit the card used to prove
it was.⭐ The recall win is real and it is
b6d3d76b5-shaped, not4d98d9eab-shaped: on that commit
the advisory went from 0 of the 3 docs pages the commit itself edited to 2 of 3
(api/client-sdk.mdx,automation/flows.mdx, both minted fromFlowRefusalCode). ⇒ The card's
conclusion survives; its exhibit does not. And the correction is written into the PR body and
the code docblock — ⛔ not just into a report that nobody reads twice.A2.1 / A2.3 re-derived and HELD — which is the branch that had to be checked first
Against today's file (post-#13738): the literal test was still exactly the three lowercase-initial
regexes — I readmain:1271and confirmed — and the pair still contradicted itself:
isCodeShaped('OS_MODE') = truewhileliteralAnchorsFromLinesminted nothing from it. ⇒ ⛔ Not
absorbed in passing, so the card earned a PR rather than a close.⭐⭐ The measurement said WIDEN — and the tidier alternative was measured and REJECTED on numbers
Over the 60 most recent
packages/**commits, using #12824's per-row provenance so each added row
is attributed to the declaration that minted it ⛔ rather than counted in a lump:rows 374 → 383 (+9, +2.4%) rows lost zero overbroadAnchors8 → 8 — the corpus-share guard caught no new hub term runs moved 2 of 60 the four vendor codes in the window ( ER_DUP_KEYNAME&c.)minted anchors, matched no page ⇒ ⭐ "an anchor no doc names is not a row" ⭐ And the structurally tidier option — delegate the literal test to
isCodeShaped— was measured,
not hand-waved: rows 374 → 408 (+9.1%), and it admits'unchanged.','means.','version.'
— sentence fragments reachingisCodeShaped's.arm. ⇒ Rejected, because the two predicates run
over different populations:isCodeShapedjudges a token already known to be a declaration
name; this one judges an arbitrary quoted span, which may be prose someone quoted. ⛔ They look
like the same question and are not.⭐ This is a better reading of the card than the card had. Zone 1 said the two predicates must
agree. The dev's answer: they now agree on this shape, and every remaining disagreement is
pinned as deliberate —'unchanged.'/'means.'/'version.'are self-test non-anchors so
the collapse goes red. ⇒ The defect was never "the predicates disagree", it was ⭐ "nobody
reported the split", and the self-test now pins the agreement itself (503 → 516 cases).
⛔ A literal reading of the ruling would have forced the full delegation — the option the evidence
rejects.The new shape is also multi-segment by construction (
/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/— the
_is required), so a bare all-caps word is ⛔ not admitted. "Prose does not shout in underscores."Verification
Reverse verification removed the fourth shape, proved the mutation on disk both ways (marker
1→0;git hash-object5dfb6b33a…≠ HEAD'sf1315ee83…), self-test thenEXIT=1with 7 failing
cases including the agreement pin. Restore proven by observed state — blob back to
f1315ee83…,git diff HEADempty,git status --porcelainempty, marker back at 1, self-test
EXIT=0/516. ⛔ No rebuild leg applies (dependency-free script, node runs the source) and none is
claimed.⭐ Measurement integrity, which is the step most reports skip: the shipped file was swept over
the same 60 commits and reproduced the measured arm on 60/60 runs. ⇒ The numbers describe this
diff, ⛔ not a throwaway mutant that never shipped.Gates
18 GREEN / 0 RED / 1 NOT MEASURED, family derived after the final commit (stderr confirms the
--repoassertion resolved to commit4a26def1c), 17 path-derived + 2 convention-triggered,
harvested with--commands. Both gate-script convention obligations are inside the green set. The
NOT MEASURED ischeck-test-completenessatEXIT=3per its own text.check:nul-bytesgreen over
7579 files, plus a direct control-byte grep over the changed file and the PR body. The standing
narrowing (repo-widepnpm lintand the CI-owned farm) is declared, ⛔ not silently skipped.
1 MCP call for the whole task.Disposition
⛔ Not governed surface (one
scripts/docs-audit/**file), no clause ② limb, scope held exactly to
A2.5's assumption.skip-changesetcorrect. Arming once its checks settle green;Fixes #13471
closes this card on merge.
Generated by Claude Code
Filed by the
domain:devxPM seat (#6023), sessionsession_01Pk26oZ12t5N1hwGW1m1MgC, on behalf of #13306's dev, which measured it and deliberately did not ship it — that dispatch reservedaffected-docsbehaviour changes. ⛔ Ungraded and unrouted —domain:*, priority and type are triage's. Filed unassigned.Measured
scripts/docs-audit/affected-docs.mjs—literalAnchorsFromLinesaccepts three quoted-literal shapes, and all three are lowercase-initial. ⇒ aSCREAMING_SNAKEliteral is never minted as a literal anchor, even thoughisCodeShapedaccepts it.Demonstrated to matter, on commit
4d98d9eab:OS_CLOUD_URL,OS_MODEandOS_TENANCY_POSTUREwere already present oncontent/docs/deployment/environment-variables.mdxand changed in that commit's diff — the exact shape the anchor mechanism exists to catch. That page names env vars and essentially nothing else, so this route is the only one it has.⭐ And demonstrated NOT to explain the sibling cases, which is why this is one finding rather than a theory: in the three env-var misses inside #13306's record window, the env var is new to the page, so no anchor of any shape could have matched. ⛔ The defect is real and its blast radius is smaller than the symptom that led to it.
⛔ Not claimed here
Dedup
domain:devxissues with a must-hit control — the listing was verified to return #13306 itself before its zero was read as a zero. ⭐ Recorded because the first attempt returned an empty list and failed its control (thedomain:devxlabel needed URL-encoding); that empty list was discarded rather than read as a clean sweep. No existing card found. #12824 is the precision card on data-property anchors — a different mechanism, not a duplicate.Re-check
The measurement commands are in the⚠️
os-dev-reportcomment on #13306.affected-docs.mjshas drifted since the record window (blob0a42496…atc4ecf0c49→61e0f44c…on today'smain, via #13058 and #12965) — ⛔ re-derive against the current file, do not assume the predicate still reads as described.Refs