Skip to content

LLP corpus hygiene: illustrative @refs are marked and no gloss uses an em dash (#463 items 2 and 3) - #468

Merged
philcunliffe merged 4 commits into
masterfrom
fix/issue-463
Jul 30, 2026
Merged

LLP corpus hygiene: illustrative @refs are marked and no gloss uses an em dash (#463 items 2 and 3)#468
philcunliffe merged 4 commits into
masterfrom
fix/issue-463

Conversation

@philcunliffe

@philcunliffe philcunliffe commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What this fixes

Items 2 and 3 of #463: the placeholder refs that keep /ref-check from ever
exiting 0, and the em-dash @ref glosses. Item 1 (three duplicate LLP
numbers) is deliberately left alone, see below.

Verified counts, before and after

Measured on this branch with the checker added here, which indexes all three
anchor forms the corpus uses (heading slug, {#slug} on any line, inline
<a id>) exactly as PR #456 specifies:

before after
broken @ref annotations, total 25 16
of those, placeholder refs (this PR's slice) 9 0
of those, LLP 0103#cli and the wrapped LLP 0135 ref (PR #461's slice) 16 16
annotation lines carrying an em dash 164 0
LLP numbers claimed by two documents 3 3 (item 1, undecided)

Two counts in the issue needed correcting:

  • 9 placeholder refs, confirmed, six LLP-form and three path-form, and the
    issue is right that the distinction decides the fix: a rule keyed only on
    LLP NNNN#anchor silences six of nine and the tool still exits 1 forever.
  • 164 em-dash annotation lines, not 459. 132 put the dash in the separator
    slot; the rest carry one inside the gloss or in the prose of a line that also
    mentions @ref. All 164 are fixed. 459 does not reproduce under any reading I
    could construct: the repo has 2628 lines containing a U+2014 in total, so that
    figure looks like a count of em dashes at large rather than of annotations. The
    wider prose sweep is not attempted here.

Item 2: the nine placeholders are marked, not repointed

They live in .claude/skills/ref-check/SKILL.md and
.claude/skills/ref-story/SKILL.md and cite LLP 0042#token-strategy,
LLP 0074#focus-trap, LLP 0074#focus-management, LLP 0007#layout,
LLP 0042#anchor, path/to/doc.md, and docs/vendor/spec.md twice. They are
correct as documentation and wrong as data, so repointing them at a real section
was rejected: a ref invented to satisfy a checker teaches the reader something
false, and deleting the examples would gut the syntax reference.

Of the issue's three options (a skipped example block, a sentinel LLP number, an
ignore list) the marker is the only one that covers both citation forms, because
it is keyed on the line and not on the target:

  • ref-check:ignore anywhere on the line suppresses that line.
  • ref-check:ignore-start / ref-check:ignore-end suppress a region, written as
    HTML comments outside the fence so nothing renders inside the example.

Documented in the skill's extract step and in LLP 0001's conventions, with the
warning that the regions must stay tight because a suppressed annotation is
checked nowhere. A unit test pins that: a marked ref is skipped, an unmarked one
on the neighbouring line is not.

The illustrative annotations in ref-story also used the em dash separator, so
they were teaching the wrong spelling; they now show the colon.

Item 3: the em-dash sweep

Its own commit, separator-only where the dash stood where the colon belongs. The
21 sites where the dash sat inside prose instead got the punctuation the sentence
wanted (a comma, colon, semicolon, or parentheses), never a reworded rationale.
Every JS change is comment text: git diff over *.js outside the new test file
contains no non-comment line. The pre-existing em dash in matcher.js's
machine-local list doc comment, which the issue flagged as worth folding in, is
included.

Reproducing test

test/core/llp-ref-hygiene.test.js, the extractor plus resolver LLP 0001
§Tooling asked for and recorded as unbuilt.

On origin/master with only the test file added:

ok 1 - the scan finds the corpus and its annotations
not ok 2 - every @ref resolves to a live LLP document and one of its anchors
    9 broken @ref annotations:
not ok 3 - no @ref annotation separates its gloss with an em dash
    164 annotations carry an em dash:
# pass 2
# fail 2

On this branch all five pass (one skipped, see below). Full suite:
npm test, 2884 tests, 2874 pass, 8 fail, 1 skip. The 8 failures are
test/core/leave-command.test.js and reproduce identically on origin/master
(2879 tests, 2870 pass, the same 8 fail), so they predate this branch.
npm run typecheck is clean.

Left to PR #461

Left to a human: item 1, the three duplicate numbers

Not fixed here, and not fixed by #461 either (that PR's body asks for it to
be re-filed, which is how this issue exists). 0098, 0099, and 0111 are each
claimed by two documents. The issue offers renumbering the later claimant versus
adopting a filename-qualified citation form in LLP 0001, and says outright it is
worth deciding deliberately rather than by whoever touches it first. Renumbering
would sweep dozens of inbound refs and rewrite six documents' history; I am not
picking that in passing.

The invariant is written down and skipped in the test, with the reason in the
skip message, so unskipping it is the last step of whichever decision lands. The
resolver meanwhile resolves an anchor against either claimant, per #456, so
the collision does not manufacture false broken refs today.

Fixes #463

neutral-reconciler and others added 2 commits July 30, 2026 00:01
… item 3)

LLP 0000 and LLP 0001 spell the annotation `@ref LLP NNNN#anchor [relation]:
gloss`. 164 annotation lines separated the gloss with a U+2014 instead, which is
both a convention violation and a house-style one: CLAUDE.md forbids the
character in code, comments, and docs alike.

Mechanical and separator-only where the em dash stood where the colon belongs.
The 21 sites where the dash sat inside prose rather than in the separator slot
got the punctuation the sentence wanted instead (a comma, a colon, a semicolon,
or parentheses), never a reworded rationale. Comment text only: no runtime
string, and no non-comment line, is touched outside the LLP documents.

Also folds in the pre-existing em dash in `matcher.js`'s machine-local list
doc comment, named in the issue.

Co-Authored-By: Claude <noreply@anthropic.com>
…em 2)

Nine annotations cite deliberately fictional targets: six LLP-form
(`LLP 0042#token-strategy`, `LLP 0074#focus-trap`, `LLP 0074#focus-management`,
`LLP 0007#layout`, `LLP 0042#anchor`) and three path-form (`path/to/doc.md`,
`docs/vendor/spec.md` twice), all inside the `ref-check` and `ref-story` skill
docs. They are correct as documentation and wrong as data, which is why they are
marked rather than repointed at a real section: a ref invented to satisfy a
checker teaches the reader something false, and deleting the examples would gut
the syntax reference.

The marker is keyed on the line, not the target, so it covers both citation
forms (an exclusion rule keyed on `LLP NNNN#anchor` would silence six of nine and
the tool would still exit 1 forever). `ref-check:ignore` suppresses one line;
`ref-check:ignore-start` / `ref-check:ignore-end` suppress a region, written as
HTML comments outside the fence so nothing renders inside the example. Both are
documented in the skill's extract step and in LLP 0001's conventions, and the
regions stay tight because a suppressed annotation is checked nowhere.

`test/core/llp-ref-hygiene.test.js` is the extractor plus resolver LLP 0001
§Tooling asked for and marked unbuilt: it indexes all three anchor forms the
corpus uses (heading slug, `{#slug}` on any line, inline `<a id>`), resolves
every annotation, honors the markers, and fails on an em-dash gloss. The
`LLP 0103#cli` sites that PR #461 owns are tolerated by name, not asserted, so
that branch stays free to fix them. The duplicate-number invariant is written
down but skipped: 0098, 0099, and 0111 are each claimed by two documents, and
choosing between renumbering and a filename-qualified citation form is a corpus
decision, not a drive-by fix.

Co-Authored-By: Claude <noreply@anthropic.com>
@philcunliffe

Copy link
Copy Markdown
Contributor Author

🤖 neutral: this PR carries Fixes #463, but one third of #463's scope is deliberately not in it

Recording this so the scope gap is visible before the trailer closes the issue on merge.

Issue #463 named three things. This PR fixes two of them and leaves the third:

#463's scope Status here
9 placeholder refs Fixed, 9 → 0, via a documented ref-check:ignore marker rather than by inventing targets
em-dash @ref glosses Fixed, 164 → 0 on @ref lines
3 duplicate LLP numbers Not fixed, left as a skipped test with the reason in the skip message

The duplicate-number slice was left because it is a corpus-level convention choice (renumber the later claimant of 0098/0099/0111, versus adopt a filename-qualified citation form in LLP 0001), not a mechanical repair. That was the right call, and PR #461 does not fix it either, its own body asks for it to be re-filed.

Since both #463 and #457 name that slice and neither PR fixes it, merging both would have closed both issues with the work silently dropped. It is now tracked in #469, labelled neutral:fix and neutral:stuck with the decision fork spelled out for a human.

Also worth noting for review: the issue's headline count of "459 em-dash @ref glosses" does not reproduce. The verified figure is 164 @ref lines carrying a U+2014 (132 in the separator slot, 32 elsewhere). 2628 lines repo-wide contain one, which is likely where 459 came from, and that wider prose sweep is out of this PR's scope.

git merge-tree was verified clean against fix/issue-457, fix/issue-452 and fix/issue-453. It was not checked against fix/issue-464 or fix/issue-465, which did not exist yet when this branch was built and which both add a new LLP document. If this PR's ref-hygiene test and those branches disagree, it will surface as a normal CI failure on whichever lands second.

Review of #468 found the marker self-triggering: the prose in
`ref-check/SKILL.md` that documents `ref-check:ignore-start` matched the
plain `includes()` test, so lines 80-146 of that file (the whole "validate a
reference" section) were a suppressed region nobody asked for, and LLP
0001's bullet plus this test's own constants opened regions too. That is the
exact failure the marker is warned against, reached by writing it down.

A marker now counts only when it is written as a comment and outside an
inline code span, so documentation can name it without arming it. The gap
before the marker text is bounded rather than `*`-quantified so the pattern
does not match its own source line. Regions are now exactly the three the
author intended.

Suppression is also made reviewable, since a suppressed annotation is
checked nowhere: the gate fails on a region that is opened and never closed,
and on a marker in any file outside an enumerated list of the syntax
documentation, so a new suppression has to show up in a diff as an edit to
that list.

Co-Authored-By: Claude <noreply@anthropic.com>
@philcunliffe

Copy link
Copy Markdown
Contributor Author

Review round 1, head 68d21ef4

Verdict: findings. Two actionable, both fixed and pushed as 8a47ea6. The
substance of the PR holds up: the counts reproduce exactly, the gate is real, the
skip is honest, and the em-dash sweep is provably separator-only. The defects
were both in the new suppression mechanism, which is the right place to have
looked hardest.

1. Is ref-check:ignore an escape hatch that will rot the corpus?

Judgement: acceptable as designed, but it was unsafe as implemented, and it was
not reviewable. Both are now fixed.

The design choice is right. All nine suppressed refs are genuinely targets that
cannot exist: path/to/doc.md, docs/vendor/spec.md#tokens (twice),
LLP 0042#anchor, LLP 0042#token-strategy (twice), LLP 0074#focus-trap,
LLP 0074#focus-management, LLP 0007#layout. Six are syntax templates or
sample tool output inside ref-check/SKILL.md; three are annotations on a
fictional src/auth/tokens.rs and a fictional Modal in ref-story/SKILL.md.
None of the nine could have had a real target. There is no ref here that
someone gave up on, and repointing any of them would teach a reader something
false. Marking is the correct call, and keying on the line rather than the target
is the only option of the issue's three that covers the path form.

Two problems with the implementation as submitted:

FINDING 1 (medium, FIXED): the marker was self-triggering, so writing the
documentation armed it.
Detection was a bare
line.includes('ref-check:ignore-start'), so the prose that introduces the
marker matched it. At the reviewed head:

  • .claude/skills/ref-check/SKILL.md:80 (the bullet that documents
    ignore-start) opened a region that nothing closed until the unrelated
    ignore-end at line 146. Lines 80 to 146 were a suppressed region nobody
    asked for
    , covering the whole of section 4, "Validate each reference". The
    two deliberate per-line markers at 99 and 107 were inside it and doing nothing.
  • llp/0001-adopting-llp.plan.md:68 opened a region closed by line 69.
  • test/core/llp-ref-hygiene.test.js:40,41, the IGNORE_START / IGNORE_END
    constant declarations, suppressed themselves.

Nothing live is lost today, so this is not a correctness bug in the reported
counts. It is worse than that in kind: the PR's own warning ("keep the regions
tight, a suppressed annotation is checked nowhere") was violated by the act of
writing the warning down, and any @ref added to those 67 lines later would have
gone unchecked with no signal.

Fixed: a marker now counts only when written as a comment (<!-- -->, //,
/*, a JSDoc *) and not inside an inline code span. Documentation can name it
freely. The gap before the marker text is bounded rather than *-quantified so
the pattern does not match its own source line either. Regions are now exactly
the three the author intended: ref-check/SKILL.md 20-29 and 120-146 plus
per-line 99 and 107, ref-story/SKILL.md 77-180. llp/0001 now carries no
markers at all. A regression test pins it: documenting a marker does not activate it.

FINDING 2 (medium, FIXED): a future suppression was invisible. Nothing
checked that a region closes, so a dangling ignore-start silently unpoliced
every line after it, and nothing constrained where a marker may appear, so
// ref-check:ignore appended to a genuinely broken ref in any file would have
passed the suite with no diff signal. That is exactly the rot path.

Fixed with a new gate, suppression is confined to the syntax documentation and every region closes: markers are permitted only in an enumerated
MARKED_FILES set (the two skills plus this test's own fixture), and an unclosed
region fails. A new suppression now has to land as a visible edit to that list.
Documented in the skill and in LLP 0001 alongside the existing note.

Scoping otherwise was already correct: line and region, never file-wide, never
global, no config knob, no CLI flag.

2. Is the hygiene test a real gate?

Yes, verified directly rather than taken on trust. origin/master checked
out clean in a separate worktree with only the test file copied in:

ok 1 - the scan finds the corpus and its annotations
not ok 2 - every @ref resolves to a live LLP document and one of its anchors
    9 broken @ref annotations:
not ok 3 - no @ref annotation separates its gloss with an em dash
    164 annotations carry an em dash:
# pass 2  # fail 2  # skipped 1

Both claimed counts reproduce exactly: 9 and 164. On this branch all pass.
The gate still fails on pristine master after my fixes (same 9 and 164), so the
change did not weaken it.

The tolerance list is an explicit enumeration of 16 file:line strings, not a
regex.
Verified by emptying TOLERATED_BROKEN and re-running: exactly 16
sites, all of them LLP 0103#cli (15) plus the wrapped LLP 0011#interactive-
in llp/0135:959, which is precisely PR #461's slice. Tolerated and never
required, as claimed: nothing asserts a tolerated site is still broken, so #461
can fix them and pruning is a cleanup. A blanket pattern would have let new
0103#cli breakage in; this cannot.

3. The skipped test

Honest. no LLP number is claimed by two documents is skipped with the
reason in the skip message, and it is not hiding damage this PR caused: LLP 98,
99 and 111 are each claimed by the same two documents on origin/master and on
this branch, byte for byte. Unskipping it is a one-line change once the corpus
decision lands, and the resolver already resolves an anchor against either
claimant so the collision manufactures no false broken refs.

4. Correctness of the em-dash sweep

Sound. Spot-checked well past the requested eight, across
src/core/config/action_attach.js, src/core/daemon/runtime.js,
src/core/sinks/incremental.js, src/core/commands/purge.js,
src/core/remote/gateway_seed.js, src/core/cache/storage.js,
hypaware-core/plugins-workspace/codex/src/rollout-cwd.js,
test/plugins/s3-export-batch.test.js, llp/0073, llp/0080, llp/0052.
The 21 in-prose sites got the punctuation the sentence wanted and read correctly:
no configured-listen fallback - that's the manual path's became a semicolon;
the hyp purge verb - targeted, cache-only, confirmed, non-destructive marking left intact became parenthetical and is now less ambiguous than the original;
throws - the same fail-safe the store applies became "throws, matching the
fail-safe...". No rationale was reworded.

Two structural checks rather than eyeballing:

  • No annotation lost its attachment. Every blank-line addition in the diff is
    in .claude/skills/*/SKILL.md (around the HTML-comment markers) or
    llp/0001. Zero blank lines added or removed in any .js, .ts or .d.ts
    file, so no @ref was detached from the construct below it.
  • No ref target changed or was lost. Extracting every
    @ref LLP NNNN#anchor / @ref path.md#anchor prefix on both revisions and
    diffing the sorted multisets gives 9 additions and zero deletions and zero
    retargets
    . All 9 additions are the new test file's own header annotations and
    its fixture strings. The sweep provably touched separators only.

5. CLAUDE.md compliance of the diff

  • No em dash on any added line, in the PR or in my fix.
  • No semicolon-terminated statements added; the new test file is house style.
  • @ref format holds. The apparent exceptions are all backticked prose, LLP
    plan-line -- task separators, or the marked illustrative lines.
  • npm run typecheck clean. npm test 2886 tests, 2876 pass, 8 fail, 2 skip.
    The 8 are test/core/leave-command.test.js and reproduce identically on
    origin/master (2879 / 2870 / same 8), so they predate this branch.

Cross-PR merge check

git merge-tree is clean against fix/issue-464 and fix/issue-465 as well
as the three the PR body lists, and I ran the hygiene test on each merged tree
and on the tree with both merged at once. All pass. LLP 0143 and 0144 do not
exist on master, so neither new document collides, and neither carries a broken
or em-dash annotation. No cross-PR conflict to resolve.

Left, with reasons

Fixed and verified in 8a47ea6

file change
test/core/llp-ref-hygiene.test.js marker requires a comment context and no inline code span; unclosed-region and marker-location gate; two new tests
.claude/skills/ref-check/SKILL.md documents the comment requirement and that suppression is itself gated
llp/0001-adopting-llp.plan.md same, on the {#illustrative-refs} bullet

Verified landed with git diff 68d21ef4..origin/fix/issue-463: 3 files, 89
insertions, 10 deletions. Not merged, not marked ready, nothing pushed to
master.

Round 2 review of #468. The round-1 fix stopped the marker from arming itself
when named in prose, but it still accepted any comment opener in any file type,
so four ordinary ways of documenting the marker in Markdown still opened a real
suppressed region: a bullet (`* ref-check:ignore-start`), a heading
(`# ref-check:ignore-start`), a fenced code sample, and a four-space indented
one. Each was verified to hide a genuinely broken `@ref` with the suite green.

Markdown's only comment is `<!-- -->`; `*` is a bullet and `#` is a heading
there, so marker recognition is now per file type, and a marker inside a fenced
block or an indented block does not count, which is the multi-line form of the
inline-code-span exclusion already present. Extraction over the corpus is
unchanged: the same 1365 annotations, the same nine suppressed.

The gate now also rejects a region closed without being opened and a region
opened while another is open. Both previously passed silently, which meant the
author and the extractor could disagree about which lines were suppressed.

Also from the round-1 findings:

- `TOLERATED_BROKEN` is keyed on the file plus the target it cites, with a
  count, instead of on `file:line`. One blank line added to `policy.js` used to
  produce five spurious failures. The count keeps it from being a loosening: a
  seventh broken `LLP 0103#cli` in `clients.js` still fails.
- LLP 0001 no longer claims the colon separator is enforced. Only the em dash is
  gated; 27 annotations still open a gloss with a hyphen, and the doc now says
  so rather than describing a rule the checker does not apply.

Co-Authored-By: Claude <noreply@anthropic.com>
@philcunliffe

Copy link
Copy Markdown
Contributor Author

Review round 2 of 2, head 8a47ea67

Verdict: findings. One medium and two low, all three fixed and pushed as
ef04fcd. This was the final review round, so what is listed under "Left for
triage" below now goes to triage for classification, not repair.

The PR's substance holds. I re-derived the round-1 numbers rather than trusting
them: 1365 annotations extracted across the tracked corpus, all resolving; the
nine illustrative refs still suppressed; the em-dash gate still failing on
pristine master. The skip for issue #463 item 1 is still honest.

Both round-1 fixes were re-tested adversarially by execution. One of them was
incomplete, and the hole was the same hole in a new shape.

FINDING 1 (medium, FIXED): the marker still armed itself, four more ways

test/core/llp-ref-hygiene.test.js:48 (the marker pattern) at the reviewed head.

Round 1 stopped the marker from arming when named in prose, by requiring it to
sit behind a comment opener and outside an inline code span. But the opener set
was the union of every language's (<!--, //, /*, *, #) applied to every
file type, with no notion of a code sample. In Markdown, * is a bullet and #
is a heading. So the round-1 property held only for the one form round 1 tested.

I appended each of the following to .claude/skills/ref-check/SKILL.md (a file
the gate permits markers in), wrapped a genuinely broken @ref LLP 9999#nope
inside it, and ran the suite. Every one of these four opened a real suppressed
region, hid the broken ref, and left the suite green:

Form Result at 8a47ea67
* ref-check:ignore-start opens a region (Markdown bullet) armed, broken ref hidden, suite green
# ref-check:ignore-start (Markdown heading) armed, broken ref hidden, suite green
the marker shown inside a fenced code sample armed, broken ref hidden, suite green
the marker shown as a four-space indented code sample armed, broken ref hidden, suite green

Control: the same broken ref with no marker fails the suite
(LLP 9999 does not exist), so the suppression above was real and silent.

This matters because a bullet list and a fenced sample are precisely how the
ref-check skill documents everything else, and MARKED_FILES concentrates the
hazard in exactly the two files whose subject is the annotation syntax. Section 4
of that skill is a bullet list explaining these markers; it survives today only
because every mention in it happens to be backticked.

Two further forms escaped the gate without hiding anything by themselves:

Form Result at 8a47ea67
ignore-start, ignore-start, ignore-end (nested) inner end closed the outer region, gate silent
ignore-end with no ignore-start gate silent

Both mean the author and the extractor can disagree about which lines are
suppressed, which is the unreviewable state the round-1 gate exists to prevent.

Fixed. Marker recognition is now per file type: in Markdown only
<!-- ... -->, since that is Markdown's only comment. A marker does not count
inside a fenced block (the multi-line form of the inline-code-span exclusion
already present) or on a line indented four spaces or more (CommonMark's indented
code block). The extractor and the gate now read markers through one shared
per-file walker, markersFor, so what the gate polices is exactly what the
extractor obeys. The gate additionally rejects a region closed without being
opened and a region opened while another is open.

Verified:

  • All six attacks above now fail the suite. The four Markdown ones surface the
    hidden LLP 9999 ref; the two pairing ones report
    region closed without being opened and
    region opened while the one at 193 is still open.
  • Extraction over the corpus is unchanged: the same 1365 annotations at the
    same sites under the old and the new marker logic on the same tree, so the nine
    intended suppressions are intact and nothing live became suppressed. The change
    can only ever reduce suppression, never create it.
  • New regression test a marker shown as documentation or as a code sample does not activate it covers bullet, - bullet, heading, line-marker-in-a-bullet,
    fenced, ~~~ fenced, four-space indent and tab indent, plus positive cases
    that the intended forms still suppress in Markdown and in a source file. It
    fails when the pre-fix marker logic is restored.

Forms I tried that did not arm suppression, at 8a47ea67 and after:
inline code span, bare prose, uppercase REF-CHECK:IGNORE-START, mixed case
Ref-Check:Ignore-Start, a gap of nine or more spaces after the opener,
ignore-startle and ignore-starting (the word boundary holds), and a marker in
any file outside MARKED_FILES, which the gate catches. A dangling
ignore-start was already caught by the round-1 gate and still is.

FINDING 2 (low, FIXED): TOLERATED_BROKEN keyed on file:line

test/core/llp-ref-hygiene.test.js:86-103 at the reviewed head.

Round 1 left this as a maintenance note on the grounds that it fails loud. It
does, but the loud failure is a false one, and this PR itself edits both files
it lands on. Demonstrated: inserting one blank line near the top of
src/core/commands/policy.js produces five spurious failures at 8a47ea67.

Fixed without weakening it. The list is keyed on the file plus the target the
annotation cites, with a count spent as a ceiling:

['src/core/commands/clients.js  LLP 0103#cli', 6],

A line number is not a property of the defect, so shifts no longer matter, while
the count preserves the property round 1 correctly insisted on: a seventh broken
LLP 0103#cli in clients.js is new breakage and still fails, which a blanket
pattern would have let through. Counts are ceilings and never floors, so pruning
them when #461 lands is still a cleanup and not a failure. The 16 file:line
entries reduce to exactly 5 identities, verified against the resolver's own
output.

Verified: the one-blank-line shift above now passes, and a new unit test,
a tolerated reference is tolerated only as often as it is listed, pins the
ceiling, the per-anchor scoping and the per-file scoping.

FINDING 3 (low, FIXED): LLP 0001 described a rule the gate does not apply

llp/0001-adopting-llp.plan.md:62-64 said "A colon opens the gloss: no other
separator". The gate bans only the em dash, and 27 corpus annotations open a
gloss with a plain hyphen
(27 on master too, so this PR neither caused nor
changed it).

Of the three options, I took the honest minimum: align the doc to what is
enforced
, rather than widen the gate. Widening it means a third mechanical
sweep of 27 sites in files this PR does not touch, one of them inside the slice
PR #461 already owns, in the last review round. The doc now states that only the
em dash is gated, that the colon is convention the checker does not yet enforce,
how many sites diverge, and why the sweep is deliberately later. That is a
recorded gap instead of a false claim.

llp/0001 and the ref-check skill were also updated to describe the new marker
rule and the new pairing checks, in the same commit as the code, per the LLP
living-docs rule. The marker machinery now carries
@ref LLP 0001#illustrative-refs [implements].

Verification of the pushed change

  • npm run typecheck clean.
  • npm test: 2888 tests, 2878 pass, 8 fail, 2 skip. The 8 are
    test/core/leave-command.test.js, which round 1 confirmed fail identically on
    origin/master. The test delta is exactly the two tests added here.
  • git diff 8a47ea67..HEAD touches exactly three files:
    test/core/llp-ref-hygiene.test.js, .claude/skills/ref-check/SKILL.md,
    llp/0001-adopting-llp.plan.md.
  • CLAUDE.md compliance: zero em dashes on any added line, no
    semicolon-terminated statements added, JSDoc types only, no @typedef, no
    inline import() types.

Left for triage

No blocking objection to merging.

@philcunliffe

Copy link
Copy Markdown
Contributor Author

Neutral triage rung (LLP 0017), PR #468, head ef04fcd7. The 2-round
review budget is exhausted. Round 2's fix commits (8a47ea6, ef04fcd7) were
never reviewed by anyone before this - they are the current head, pushed by
the same round that found round 1's defects. I assessed them from scratch,
by execution rather than by reading the diff.

What I verified by running code, not by trusting claims

1. Tried to defeat round 2's marker-suppression fix with forms it did not
list.
All of the following were tested directly (a standalone
reimplementation of markerOn/markersFor/extractRefs for quick iteration,
then confirmed against the real test/core/llp-ref-hygiene.test.js with real
adversarial fixture files added to a tracked worktree):

  • Marker text inside an HTML comment nested in a fenced Markdown block: does
    not arm (fence swallows it); refs elsewhere in the same fence that are not
    covered by an active ignore-start/ignore-end region are still checked
    normally, which is correct.
  • Marker inside a Markdown table cell using real <!-- --> syntax: arms, but
    that is the documented syntax working as intended, not a bypass.
  • Marker after a trailing backslash / line continuation in a .js file: still
    recognized (a real // comment), not a bypass.
  • Marker as a real // comment in a .d.ts file: recognized, as expected for
    a real comment; .d.ts is scanned (.ts is in SCANNED_EXTENSIONS) and is
    not in MARKED_FILES.
  • Marker text embedded in a .json string value (not a real comment; JSON has
    none): the regex still matches it, because pattern recognition does not
    understand JSON syntax.
  • Marker with a CRLF line ending: still recognized (no bypass).
  • Marker inside a JSDoc block (* ref-check:ignore-start): recognized, as
    expected for a real comment.
  • Nested start/start/end and a lone ignore-end: extractRefs alone does
    not reject these (it just toggles a boolean), but the separate "suppression
    is confined to the syntax documentation and every region closes" test
    explicitly detects and fails on both nesting and unpaired regions when run
    against real tracked files.
  • The residual round 2 named itself: a Markdown-shaped <!-- ref-check: ignore-start --> embedded in a .js string (not a real comment) does arm,
    because CODE_MARKER (used for all non-.md files) also accepts a
    <!-- opener. I added a real file to the tracked worktree
    (src/core/__adv_attack1.js) with exactly this pattern hiding a genuinely
    broken @ref LLP 9999#nope, ran the real suite, and it failed loudly
    (suppression outside the syntax documentation, 2 offenders) rather than
    passing silently. MARKED_FILES has exactly one non-Markdown member
    (test/core/llp-ref-hygiene.test.js itself), confirmed by reading the
    source; any addition to that set is a one-line diff in a file ref-check
    itself has annotations on. The bound holds: the only place this residual
    form could hide something is inside the hygiene test's own source, and that
    file spells its literal marker text apart ('ref-check:' + 'ignore')
    specifically so its fixtures are not markers in their own file.

None of the forms I tried, including ones not on round 2's list, produced a
silent pass on a genuinely broken ref. Every marker that can suppress
something is read through the same markersFor the confinement test polices,
so nothing I found suppresses without also tripping that test outside the
three allow-listed files.

2. TOLERATED_BROKEN ceiling. src/core/commands/policy.js currently
carries exactly 5 broken LLP 0103#cli citations against a tolerance of 5
(fully spent). I added a 6th broken @ref LLP 0103#cli directly in that file
and reran the suite: it failed (LLP 103 defines no anchor #cli) because the
budget was exhausted, not silently tolerated. The ceiling correctly rejects
new breakage that shares an identity with an already-tolerated one; it does
not weaken the gate.

3. Duplicate-LLP-number skip. test/core/llp-ref-hygiene.test.js is new
in this PR (does not exist on master). Its one skipped test
(no LLP number is claimed by two documents, issue #463 item 1 / tracked
now in #469) is honest: running the assertion unskipped reproduces exactly
the three pre-existing collisions (0098, 0099, 0111), which predate this
branch and are not caused by it.

4. Em-dash gloss rewrites. Spot-checked 20+ sites across
graph_contract.js, control.js, sink.js, action_attach.js,
runtime.js, incremental.js, watermarks.js,
hypaware-plugin-kernel-types.d.ts, and several llp/*.md files. Every
change is a mechanical swap of the separator character in place
([relation] — gloss -> [relation]: gloss); no gloss text was reworded, and
no blank line was introduced between an @ref and the construct it attaches
to.

One thing I found that round 2 did not flag

The new "no @ref annotation separates its gloss with an em dash" test only
inspects the single line containing the @ref token. Three live em dashes
survive today in multi-line gloss continuations, invisible to that check:
hypaware-core/plugins-workspace/format-iceberg/src/table-format.js:456,
llp/0067-session-opt-out.design.md:114, and
src/core/runtime/activation.js:64. This is a real, narrow gap in the new
gate (a future multi-line gloss with an em dash on its continuation line would
also pass silently), but it is cosmetic (house style, not a functional
defect) and the same kind of gap as the repo-wide em-dash residual below, so
I am treating it as non-blocking and folding it into the same follow-up
rather than sending this back.

Non-blocking items, filed

  • Repo-wide em dash (U+2014) violates CLAUDE.md's house style outside @ref glosses #472: repo-wide em dash (U+2014) violates CLAUDE.md's house style
    outside @ref glosses. Verified counts against master at this triage:
    2459 lines across 193 tracked files carry at least one U+2014 (confirms the
    cited llp/0045 = 66, llp/0041 = 54, hypaware-plugin-kernel-types.d.ts
    = 36). Pre-existing on master, out of this PR's declared scope, and no
    other open or closed issue tracked it (searched em dash, em-dash,
    U+2014, and 463). Labeled neutral:fix, not neutral:stuck, since it
    concerns code already on master and is actionable today. Includes the
    three-site gloss-continuation gap above as a specific note for whoever picks
    it up.

Verdict

Everything above is non-blocking. Round 2's previously-unreviewed fix commit
holds up under direct adversarial testing, including forms it did not itself
enumerate. PR #468 can merge safely.

@philcunliffe
philcunliffe marked this pull request as ready for review July 30, 2026 01:56
@philcunliffe philcunliffe added the neutral:approved neutral reviewed this and holds it for a maintainer merge (own or adopted PR; LLP 0025/0030) label Jul 30, 2026
@philcunliffe
philcunliffe merged commit 183d38b into master Jul 30, 2026
8 checks passed
@philcunliffe
philcunliffe deleted the fix/issue-463 branch July 30, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

neutral:approved neutral reviewed this and holds it for a maintainer merge (own or adopted PR; LLP 0025/0030)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LLP corpus hygiene: 3 duplicate LLP numbers, 9 placeholder refs that keep /ref-check from ever exiting 0, and 459 em-dash @ref glosses

2 participants