Skip to content

fix(scripts): the citation extractor reads hyphen-suffixed, slash-joined and URL-spelled citations, pinned in one spelling table - #20989

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20636-citation-extractor-closeout
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20636-citation-extractor-closeout

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #20636
Clause-②: no

The family closeout for the citation extractor in scripts/check-issue-citations.mjs: every spelling a seat measured as invisible to the diff gate and the census is now read, every exclusion keeps only the shapes it was measured protecting, and the self-test carries the one enumeration table the triage asked for (48 spellings, each "extracted as" or "not a citation, because"). Landing site: scripts/check-issue-citations.mjs only. scripts/check-doc-authoring.mjs is untouched; H2 below says why.

What changed

  • Hyphen after the number. The lookahead no longer refuses a -, so #13398-class, #5347-A and ui#6206-B read as citations of their number. It still refuses a word character, so a hex colour stays out.
  • Slash before the #. A / is now valid context before a bare #. It stays refused only before a qualifier candidate, so a URL path fragment such as https://example.com/docs/page#12 never reads page as prose. A / right after a digit is the exception, so #3076/objectui#2614 still reads its own qualifier.
  • Slash-joined continuation. In #A/#B, the second number takes the chain head's reading: bare after a bare or prose head, the head's repository after a qualified head, and an ordinal after an ordinal. Only a joined / continues a chain. objectui#1 / #2 and objectui#1 + #2 stay two separate readings.
  • URL spelling. https://github.com/OWNER/REPO/issues/N and .../pull/N are now citations, qualified by their own OWNER/REPO. That puts objectstack-ai/framework in this repository and makes every other repository's URL cross-repo, never a finding. Inside a markdown link [#N](URL), the citation counts once.
  • Head rows. I retired re-charter, clause and option (named in the thread), plus acceptance and the section mark (found by the same measurement). Each protects 0 sites repo-wide at two or more digits and hides board citations. The grammar's two-digit floor already keeps one-digit ordinals out. I added a ]( row, the narrower guard the hyphen exclusion leaves behind for markdown in-page heading anchors.
  • Self-test.
    • A new spellings battery (56 cases) reads the table row by row.
    • Every head row must excuse at least one table row, and every required spelling (the card's list plus the thread's) must be present.
    • The live-corpus battery gains three floors, one per new arm, each counting only a number spelled once on its line.
    • The roster floor rises from 6 to 9 batteries.
    • Total: 114 cases in 8 batteries became 173 in 9.
  • I edited the header in place and kept its line count, because scripts/pm/dispatch-gates.mjs's self-test pins scripts/check-issue-citations.mjs:204 local-env. The marker is still on line 204, and that pin passes (see Gates).

H1: what each exclusion protected and hid

Measured on 3693a1b50 over the declared surfaces. Every arm was judged against one enumerated board: 188 pages, frontier 20959, 2026-09-30T22:55Z. The instrument mirrors the gate's extractor and matched it file for file on all 2,640 files (0 mismatches).

exclusion hid (sites, dead) protected in the declared surfaces disposition
hyphen after the number 58, 1 dead (8 cross-repo) 0: no numeric range, slug or hex-like token. Repo-wide: in-page heading anchors, 16 lines in docs/design/** and skills/**, plus one range, docs/audits/...md, whose first number is a citation dropped; the ]( row keeps the anchor out
/ before the # 528, 10 dead: 523 #A/#B second numbers and 5 TOKEN/#N such as ADR-0049/#1888 0 paths and 0 URL fragments narrowed to the candidate arm; continuations read as their chain
head re-charter 0 left on this tree (the 26 dead re-charter #13135 were rewritten by PR #20750) 0; only the gate's own fixtures used it retired
head clause 0 in the surfaces; 4 in deferred test files, all board citations 0 retired
head option 1 (option #14088, live); 1 more under scripts/** 0 retired
head acceptance 1 (the silent acceptance #6132 closed, live) 0 at two or more digits retired (in-place, below)
head section mark 0 in the surfaces; 4 in test files (§6 #11176's decisions) 0 at two or more digits retired (in-place, below)
heads kept none measured hiding a citation directive 203 (max 13), PD 85 (max 13), batch 276 (69 distinct, 11 to 227), OQ 10, PKCS 1 kept

The 8 slash chains headed by another repository are not a case where the two populations cannot be told apart. The 5 on objectui's public board each name objectui's record, the issue and then the pull request that fixed it, read one by one against both boards:

  • objectui#2715/#2717
  • #2711/#2722
  • #2725/#2732
  • #2967/#2904
  • #4648/#4901

This repository's records with the same numbers are unrelated. The other 3 (cloud, hotcrm-heimao) are boards one credential cannot read, so they stay unjudged, as they were before.

H2: where the URL spelling belongs

The extractor. At 3693a1b50, 57 URL sites sit in the gate's projection: 56 in package comments and 1 link on a release page. 4 of them are dead. Only 2 URL sites in package sources are inside string literals, both internal note: strings in packages/runtime/src/route-ledger.ts.

check:doc-authoring asks a different question: may a runtime string carry a tracker reference at all? It reads string literals, skills and spec refusal messages. The two projections are disjoint, so adding the URL to the extractor double-counts nothing there. Inside the extractor, the one double-spelled site ([#15325](.../issues/15325) on v17/17-3.mdx) counts once. check-doc-authoring.mjs is not touched.

H3: open PRs' added lines

All 13 open PRs at 2026-09-30T23:2xZ: their heads were fetched into a private ref namespace (deleted afterwards). For each, the BASE extractor and this one were run over the lines it adds, against its merge base. Result: 85 added-line citations under both extractors, 0 newly extracted, 0 lost. No PR's verdict changes. Lines a PR does not add are never judged, which is unchanged and pinned in the diff-scope battery.

Census, before and after

The gate's own --census --json, once with the 3693a1b50 script and once with this one, over the same tree:

judged resolves resolves as PR cross-repo allocated-but-absent
before (frontier 20965) 37,152 33,403 1,985 1,024 740
after (frontier 20966) 37,796 33,917 2,083 1,041 755

That is 644 more judged sites and 15 more dead ones, with 0 findings lost. By arm: hyphen 1 dead, slash 10 dead, URL 4 dead.

Newly visible dead sites per lane. I rewrote none of them; they belong to the lane cards:

This census was run on the PR head's tree, not after landing. A re-run after landing reads the same corpus plus whatever main has gained by then.

In-place fixes beyond the three named rows

I retired the acceptance and section-mark rows here rather than filing them. All four conditions hold:

  1. They are the same defect class as option and clause: a head row hiding a board citation.
  2. The fix is mechanical, and the shape is pinned by the table.
  3. The file is this claim's own surface (NON_CITATION_HEADS).
  4. The same gate's self-test covers them, so no new verification surface is added.

Evidence: acceptance #6132 (live) in packages/formula/src/cel-pushdown-limits.ts:82, and the section-mark sites in deferred test files. Neither row has any two-or-more-digit ordinal anywhere in the repository.

Gates (final head 25d96fcc0)

I re-derived the list with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, which gave 32 commands. The same derivation on a throwaway tree at origin/main (05be35259) with this diff applied gave an identical list. I ran all 32, plus pnpm check:doc-authoring and the self-test, and every one exited 0. Reconciliation verdict line:

✓ dispatch-gates --ran: 32 derived famil(ies) accounted for — 32 run, 0 NOT-MEASURED (a DERIVED zero — all 32 recorded an exit code and none of them is 3).

Verdict lines worth quoting:

  • node scripts/check-issue-citations.mjs --self-test: ✅ ... every spelling enumerated ... (173 cases, 9 batteries). It also passes on origin/main 05be35259 with this diff applied.
  • node scripts/check-issue-citations.mjs (diff mode): ✅ check-issue-citations: no issue citations added against 3693a1b50 (0 file(s) read). The script lives in the deferred scripts/** surface.
  • pnpm check:pm-dispatch-gates: ✓ dispatch-gates self-test: 1976 cases pass. (1237.9s). Its pin scripts/check-issue-citations.mjs:204 local-env holds.
  • pnpm check:doc-authoring: ✓ doc authoring guard: ... hold the baseline — 549 pinned site(s).
  • pnpm check:nul-bytes: check-nul-bytes: OK (scanned 9582 text file(s) ...).
  • node scripts/check-scripts-symbol-anchors.mjs: ✅ ... 3706 anchors across 282 scripts resolve.

Ablations (one-shot, from committed 25d96fcc0, via scripts/ablation-replace.mjs)

Every leg was expected to go red, and every leg did. Each one restored to blob c732ce2e21c7 (equal to HEAD) with an empty git diff HEAD. No permanent ablation file is left.

mutation first red
hyphen refused again after the number the live corpus must yield a #N-word citation
/ refused again before the # the live corpus must yield a slash-joined #A/#B second number
URL arm disabled the live corpus must yield a URL-spelled citation
option head row restored spelling option #N in "the option #14088 gave" must read #14088; got nothing
continuation disabled spelling repo#A/#B ... must read objectstack-ai/objectui#2711, objectstack-ai/objectui#2722
]( row disabled a markdown link's in-page heading anchor is not a citation
link de-duplication disabled spelling [#N](URL) ... must read #15325

The first slash ablation, run before the floors were tightened, went red in the table but left the live-corpus continuation floor green. A line citing the same number twice let a plain citation stand in for the slash-joined one. Commit 25d96fcc0 makes each new floor count only a number spelled once on its line. The re-run above is red at that floor.

Acceptance notes

  • URL spelling in runtime strings. check:doc-authoring does not read it. At 3693a1b50 the population is 2 internal route-ledger note: strings (packages/runtime/src/route-ledger.ts:459, :465), and no author-facing door shows them. Noted, not filed; carrier: none.
  • Range second numbers. In a range such as #712-714, the second number carries no # and is not read. There is 1 site repo-wide, in docs/audits/**, outside the declared surfaces. Pinned in the table with its reason.
  • Dormant test-file citations. The 8 test-file citations the retired clause and section-mark rows hid are in the deferred test surface. They become visible only when that surface is swept.
  • Line-number pin. dispatch-gates.mjs pins this file's local-env marker by line number (:204), so any future header growth above it has to move that pin in the same PR.

No changeset: a root scripts/ file publishes nothing (the root package.json is private, and no package's files ships scripts/), so this PR takes skip-changeset.


Generated by Claude Code

…spelling, and pins them in one table

The extractor hid real citations behind exclusions wider than anything they
protected. Measured over the declared surfaces at 3693a1b against one
enumerated board:

- a hyphen after the number hid 58 `#N-word` sites and protected none; the one
  non-citation shape it covered repo-wide (a markdown in-page heading anchor)
  keeps a narrower `](` head row;
- a `/` before the `#` hid 528 sites (523 slash-joined second numbers); it
  stays only on the candidate arm, where it keeps URL path fragments out, and a
  slash-joined continuation reads as its chain (qualifier or ordinal);
- the `re-charter`, `acceptance`, `clause`, `option` and section-mark head rows
  each protected 0 sites repo-wide and hid board citations; retired;
- the URL spelling (`https://github.com/OWNER/REPO/issues/N`, `/pull/N`) is
  now a citation, qualified by its own OWNER/REPO, counted once inside a
  `[#N](URL)` link.

The self-test gains the one enumeration table (48 spellings, each "extracted
as" or "not a citation, because"), completeness rules over the head rows and
the required spellings, and three live-corpus floors for the new arms.

Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU
Co-authored-by: Claude <noreply@anthropic.com>
…a number spelled once on its line

An ablation that restored the slash exclusion left the continuation floor
green: a line citing the same number twice let a plain citation stand in for
the slash-joined one. Each floor now counts only a row whose number appears
once in its line.

Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the size/m label Oct 1, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 1, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 00:54
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 1, 2026 00:54
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit a5bce40 Oct 1, 2026
38 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20636-citation-extractor-closeout branch October 1, 2026 01:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants