Skip to content

LLP 0103 names its CLI and reporting sections; the usage-policy fail-safe refs cite a live doc - #461

Merged
philcunliffe merged 3 commits into
masterfrom
fix/issue-457
Jul 30, 2026
Merged

LLP 0103 names its CLI and reporting sections; the usage-policy fail-safe refs cite a live doc#461
philcunliffe merged 3 commits into
masterfrom
fix/issue-457

Conversation

@philcunliffe

@philcunliffe philcunliffe commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What this fixes

Items 1 and 2 of #457. Items 3 and 4 are deliberately left alone (see below).

Item 1: @ref LLP 0103#cli, 15 sites, anchor never defined

The issue counted 14; a full pass finds 15 (llp/0112:43 carries the ref too,
on the T2 task line next to the T1 one the issue cited). llp/0103 defined no
cli anchor in any of the three forms.

Chose (a), add the anchor, not (b), repoint. The referent is unambiguous:

  • llp/0111-hyp-policy-verb.design.md:21 says outright @ref LLP 0103#cli [constrained-by] - extends its CLI paragraph.
  • All six clients.js sites and all five policy.js sites gloss the
    --private / --local-only / --sync machine-local marking flags, which is
    exactly what that paragraph mints ("Machine-local writes get flags on the same
    verb...", plus the Extended-by: LLP 0110 note the policy verb rests on).

So the paragraph gets the name the refs already agree on, - **CLI** {#cli}:, in
the list-item form 8 other docs in this corpus already use (LLP 0101, 0105, 0106,
0107, 0138, ...). One anchor, 14 refs fixed, intent preserved.

One of the fifteen meant a different paragraph. runIgnoreCheck
(clients.js:1074) glosses "--check names which source governs (dotfile vs
machine-local entry)", which is verbatim the Consequences bullet, not the CLI
paragraph. Naming that bullet {#reporting} and repointing that one ref keeps
the rule the issue and CLAUDE.md both ask for: the anchor has to land on the
prose the gloss actually quotes. The other 14 are untouched, so the prose in
LLP 0111/0112 that talks about "the @ref LLP 0103#cli annotations" stays true.

Item 2: two refs into a Superseded LLP

src/core/usage-policy/local_only.js and matcher.js cited
LLP 0080#fail-safe. LLP 0080 is Status: Superseded.

Its formal Superseded-by is LLP 0102 (skill-replaces-enrollment-picker),
and repointing there mechanically would be wrong: 0102 retires the in-login
picker and contains no fail-safe content at all. A ref at the wrong live doc is
worse than one at a superseded doc.

LLP 0080's own supersede note scopes the supersession and says where the rest
lives: "The non-picker substrate (machine-local list, export-seam withholding,
durable CLI) lives on under LLP 0069/0070/0071."
The corrupt-list rule is that
substrate, and 0080 §fail-safe derives it explicitly from
LLP 0049#fail-safe ("forbidden by [LLP 0049 §fail-safe]") - Accepted, same
anchor name, and already the citation carried by
src/core/usage-policy/format.js:30 for the same polarity. Both refs now cite it
as [constrained-by], with the throw-not-empty mechanics credited to LLP 0080
§fail-safe in prose so the trail survives without a ref into a superseded doc.

Nothing else in the corpus restates the corrupt-list rule (checked
0049/0069/0070/0071/0081/0102/0103/0108), which is why it is a repoint to the
invariant rather than to a replacement section.

Bonus, explicitly allowed by scope

llp/0135:959 quoted @ref LLP 0011#interactive-walkthrough across a line wrap,
so the trailing hyphen read as an anchor. Rewrapped. LLP 0011 does have an
## Interactive walkthrough heading, so the unwrapped ref resolves.

Ref-check before / after

Validated with the corrected §3 algorithm from #456 (all three anchor forms;
{#slug} indexed on any line, not just headings; heading slugs with one -
per whitespace character and no collapsing; <a>/</a> stripped off heading
lines and nothing else). The version of §3 on master is still the
heading-slugs-only one and reports hundreds of resolvable refs as broken, so it
was not used.

Over 1364 refs in 359 files, 133 LLP docs:

before after
BROKEN 22 6
WARNING (ref targets a superseded/tombstoned doc) 4 2

The 22 before: 15 x LLP 0103#cli, 6 placeholder examples in the skill docs, 1
wrap artifact in llp/0135. The 6 after are exactly the skill-doc placeholders
(LLP 0042#token-strategy x2, LLP 0042#anchor, LLP 0074#focus-trap,
LLP 0074#focus-management, LLP 0007#layout) - item 4, untouched. The 2
remaining WARNINGs are llp/0101:21 and llp/0102:18 citing the very documents
they supersede, which is correct as written.

Per-site proof:

BEFORE (origin/master)                      AFTER
LLP 0103#cli                                LLP 0103#cli
  BROKEN llp/0111-...design.md:21             OK llp/0111-...design.md:21
  BROKEN llp/0111-...design.md:247            OK llp/0111-...design.md:247
  BROKEN llp/0112-...plan.md:42               OK llp/0112-...plan.md:42
  BROKEN llp/0112-...plan.md:43               OK llp/0112-...plan.md:43
  BROKEN src/core/commands/clients.js:710     OK src/core/commands/clients.js:710
  BROKEN src/core/commands/clients.js:755     OK src/core/commands/clients.js:755
  BROKEN src/core/commands/clients.js:891     OK src/core/commands/clients.js:891
  BROKEN src/core/commands/clients.js:938     OK src/core/commands/clients.js:938
  BROKEN src/core/commands/clients.js:1008    OK src/core/commands/clients.js:1008
  BROKEN src/core/commands/clients.js:1074  -> LLP 0103#reporting: OK clients.js:1074
  BROKEN src/core/commands/policy.js:33       OK src/core/commands/policy.js:33
  BROKEN src/core/commands/policy.js:218      OK src/core/commands/policy.js:218
  BROKEN src/core/commands/policy.js:254      OK src/core/commands/policy.js:254
  BROKEN src/core/commands/policy.js:291      OK src/core/commands/policy.js:291
  BROKEN src/core/commands/policy.js:338      OK src/core/commands/policy.js:338

LLP 0080#fail-safe                          LLP 0049#fail-safe
  WARNING local_only.js:28 (superseded)       OK src/core/usage-policy/local_only.js:33
  WARNING matcher.js:215  (superseded)        OK src/core/usage-policy/matcher.js:215
  (0 refs to LLP 0080#fail-safe remain)

LLP 0011#interactive-walkthrough
  BROKEN llp/0135-...design.md:959 (#interactive-)   OK llp/0135-...design.md:960

Checks

  • npm test: 2846 pass, 8 fail, all 8 in test/core/leave-command.test.js
    and all 8 present on origin/master before this change. Baseline, no
    regression.
  • npm run typecheck: clean.
  • No runtime behaviour changes: annotations, JSDoc prose, and two anchor names in
    one design doc. Which is why the gate here is the corrected ref-check rather
    than a new unit test.

Held-PR overlap

None. Checked git diff --stat origin/master...origin/<branch> for
fix/issue-448 (#449), fix/issue-442 (#450), fix/issue-452 (#456),
fix/issue-453 (#458), claude-desktop-consent-and-entrypoint-gate (#422), and
integration/bounded-query-execution (#221). None of them touch any of the five
files here (llp/0103-machine-local-policy-classes.decision.md,
llp/0135-install-experience-overhaul.design.md,
src/core/commands/clients.js, src/core/usage-policy/local_only.js,
src/core/usage-policy/matcher.js), so no merge-tree reconciliation is needed.
Note the validation here depends on #456's corrected §3 wording but does not
edit that file, so the two do not conflict.

Merging this closes #457, and items 3 and 4 are NOT fixed

They still need their own issues. Please file:

  1. Three LLP numbers are each claimed by two documents - 0098
    (inactive-not-unknown-dispatch-miss / scancolumn-where-pushdown), 0099
    (codex-attach-auth-route / dispatch-miss-repair-by-cause), 0111
    (hyp-policy-verb.design / report-cli.decision). Every bare
    @ref LLP 0111#surface is formally ambiguous, and a checker that keeps only
    the last file it walked reports refs aimed at the other claimant as broken.
    Corpus-wide decision: renumber the later claimant and sweep its refs, or adopt
    a filename-qualified citation form documented in LLP 0001. Not fixable in
    passing.
  2. /ref-check can never exit 0 on this repo - 6 refs inside
    .claude/skills/ref-check/SKILL.md (88, 114, 122) and
    .claude/skills/ref-story/SKILL.md (101, 116, 135) are illustrative
    placeholders (LLP 0042#token-strategy, LLP 0074#focus-trap,
    LLP 0007#layout). Harmless as prose, fatal if anyone wires the skill into
    CI. Needs an example-block marker or an exclusion rule.
  3. 459 @ref ... [relation] - gloss HINTs using an em dash where LLP 0000
    specifies :, which is also a CLAUDE.md house-style violation. Mechanical but
    a large diff, deliberately not swept here.

Fixes #457

neutral-loop and others added 2 commits July 29, 2026 23:07
…safe refs cite a live doc (#457)

Fourteen `@ref LLP 0103#cli` annotations, plus the prose in LLP 0111/0112 that
describes them, pointed at an anchor LLP 0103 never defined in any of the three
forms. The referent is unambiguous: LLP 0111 says "extends its CLI paragraph",
and every code site glosses the `--private` / `--local-only` / `--sync`
machine-local marking flags that paragraph mints. So the paragraph gets the name
the refs already agree on, `{#cli}`, in the corpus's list-item form.

One of the fifteen sites meant a different paragraph: `runIgnoreCheck`'s gloss
("`--check` names which source governs") is verbatim the Consequences bullet, not
the CLI paragraph. That bullet gets `{#reporting}` and the ref is repointed, so
both anchors land on the prose their glosses actually quote.

`src/core/usage-policy/{local_only,matcher}.js` cited `LLP 0080#fail-safe`, a
Superseded document. Its formal Superseded-by, LLP 0102, retires the in-login
picker and carries no fail-safe content, so pointing there would be worse than
leaving it. LLP 0080's own note says the machine-local list substrate lives on,
and its `#fail-safe` section derives the rule from `LLP 0049#fail-safe`
(Accepted, and already the citation on `src/core/usage-policy/format.js`). Both
refs now cite that invariant as `[constrained-by]`, with the throw-not-empty
mechanics credited to LLP 0080 in prose so the trail survives.

Also unwraps the line-wrapped `@ref LLP 0011#interactive-walkthrough` quoted in
llp/0135, whose trailing hyphen made a wrap artifact look like an anchor.

Corrected ref-check (all three anchor forms, `{#slug}` on any line, one `-` per
space): BROKEN 22 -> 6, superseded-target WARNING 4 -> 2. The remaining 6 are
the placeholder examples inside the ref-check / ref-story skill docs and the
remaining 2 WARNINGs are LLP 0101/0102 citing the docs they supersede, both
correct as they stand.

Co-Authored-By: Claude <noreply@anthropic.com>
…o LLP 0080

Review follow-ups on the LLP 0103 anchor split:

- `runPolicyShow` (`policy.js`) carried the same "names the governing
  source (dotfile vs machine-local)" gloss that justified moving
  `runIgnoreCheck` to `LLP 0103#reporting`, but stayed on `#cli`. Split it
  the same way: `#reporting` for the source-and-class report, `#cli` for
  the store/resolver/lattice-unchanged claim its four sibling glosses make.
- `LOCAL_ONLY_LIST_UNREADABLE_ERROR_KIND`'s one-line prose credit moves
  back to LLP 0080 §fail-safe. LLP 0049 §fail-safe states the invariant
  (suppress more, never expose more) and is the right `@ref` target, but it
  says nothing about throwing or about a structured error naming the file;
  the `error_kind` mechanic is LLP 0080's (spelled out in LLP 0081 T1).

Annotation and JSDoc prose only. `npm test` at the 8-failure
`leave-command.test.js` baseline; typecheck clean.
@philcunliffe

Copy link
Copy Markdown
Contributor Author

neutral review round - PR #461 @ b375535

Annotation/doc-correctness review. Reproduced the corrected ref-check algorithm from
scratch (own implementation, not the skill text), re-adjudicated both judgement calls
plus the {#reporting} split, checked held-branch overlap, house style, tests and
typecheck. Two findings, both fixed and pushed (f0f603e).

1. Ref-check, measured independently

Own implementation of the corrected §3: heading slugs (lowercase, punctuation stripped
keeping -/_, each whitespace char to one - with no collapsing, <a …>/</a>
stripped off the heading line and nothing else, -1/-2 disambiguation), {#slug} on
any line, and inline <a id=…>/<a name=…>. Anchors resolve against any
claimant of a duplicated number. Fence-sensitivity checked both ways - identical results.

origin/master (f9b9667) b375535
refs / files-with-refs / LLP numbers / LLP files 1367 / 359 / 130 / 133 1367 / 359 / 130 / 133
BROKEN (all forms) 25 9
BROKEN (LLP NNNN form only) 22 6
WARNING (superseded/tombstoned target) 4 2
duplicate LLP numbers 3 (0098, 0099, 0111) 3

The PR's 22 -> 6 and 4 -> 2 reproduce exactly, and the small difference in the other
two numbers is fully explained, not a disagreement:

  • My refs is 1367, the PR's is 1364. The delta is exactly the 3 path-form refs
    (@ref path/to/doc.md#anchor, @ref docs/vendor/spec.md#tokens x2) in
    .claude/skills/ref-check/SKILL.md:24,96,115, which the PR's count excludes.
  • Same 3 refs explain BROKEN 25 vs 22 and 9 vs 6. They are the same class of
    illustrative placeholder as the 6 the PR defers to item 4 - so the residue is
    9 placeholder refs, not 6 (see finding N1 below).

The 6 remaining LLP-form BROKEN are exactly the ones the PR names
(ref-check/SKILL.md:88,114,122, ref-story/SKILL.md:101,116,135 ->
LLP 0042#anchor, LLP 0042#token-strategy x2, LLP 0074#focus-trap,
LLP 0074#focus-management, LLP 0007#layout). The 2 remaining WARNINGs are
llp/0101:21 -> LLP 0093 and llp/0102:18 -> LLP 0094, i.e. each decision citing the
document it supersedes. Correct as written; nothing to do.

All 15 LLP 0103#* sites resolve (14 #cli + 1 #reporting):
llp/0111:21, llp/0111:247, llp/0112:42, llp/0112:43,
clients.js:710,755,891,938,1008, clients.js:1074 (#reporting),
policy.js:33,218,254,291,338. The PR's 15th site (llp/0112:43) is real - the issue
counted 14 because it missed the T2 task line. llp/0112:40 also says
LLP 0103#cli in prose but is not an @ref, so it is correctly not counted.

Both LLP 0049#fail-safe repoints resolve, alongside the pre-existing
format.js:30. Zero @ref into LLP 0080#fail-safe remain.

llp/0135: on master the wrap produced anchor interactive- (BROKEN); at
b375535 line 960 resolves against LLP 0011's ## Interactive walkthrough
heading slug. Verified the unwrap is text-identical, only re-wrapped.

2. Judgement call: is {#cli} on the Decision bullet the real referent?

Verdict: yes for 14 of the 15, and adding the anchor was the right branch over
repointing
- with one narrow correction, filed as finding A1 below.

Evidence for the anchor landing on the prose the glosses quote:

  • llp/0111:21 is decisive and explicit: @ref LLP 0103#cli [constrained-by] - extends its CLI paragraph. The bullet at llp/0103:63-72 is that paragraph.
  • The 5 remaining clients.js sites gloss the marking flags the bullet mints. The
    bullet says "Machine-local writes get flags on the same verb: the existing
    --local-only, a new --private … and an explicit-sync marking"; the glosses at
    clients.js:710 (--private/--sync parsing), :755 and :938 (dispatch),
    :891 (the shared marking verb) and :1008 ("symmetric class-scoped removal", against
    the bullet's "hyp unignore grows symmetric removal") are direct hits. The
    surrounding JSDoc prose at clients.js:705,751,934 already writes
    (LLP 0103 #cli) in plain text, so the author's intent is on record.
  • The 5 policy.js sites are a weaker but still correct fit, and here the PR body's
    justification is inaccurate: it claims "all five policy.js sites gloss the
    --private / --local-only / --sync machine-local marking flags". They do not -
    four of them gloss "the store, resolver, and class lattice are unchanged; only the
    verb spelling is new"
    (policy.js:33,218,291,338), and :338 is about the version-2
    class-per-entry store. That content sits in the Decision preamble and the class
    bullets, not the CLI paragraph. The anchor is still right, for a reason the PR
    body does not give: the {#cli} bullet is the only place in LLP 0103 that mentions
    hyp policy at all - its Extended-by: LLP 0110 note (llp/0103:70-72) says
    literally "the flag-on-ignore spelling this paragraph left open is retired in favor of
    a class-neutral hyp policy verb; the class taxonomy above is unchanged", which is
    what those four glosses paraphrase. And llp/0111:247 / llp/0112 T2 both mandate
    this exact citation ("the @ref LLP 0103#cli annotations … keep citing 0103 for the
    store-and-classes rationale"), so repointing them elsewhere would falsify live plan
    text. Conclusion: keep #cli; the PR body's reasoning for those five is wrong even
    though its choice is right. Body is not editable by this review - noting it here.
  • Repointing (option b) was correctly rejected: 15 independent sites agreeing on a name,
    one of them saying "extends its CLI paragraph", is about as unambiguous as intent gets.
  • - **CLI** {#cli}: matches the corpus form exactly (- **Label** {#anchor}:), used by
    LLP 0101, 0105, 0106, 0107, 0138 and others. No slug collision introduced: LLP 0103's
    anchor set goes from {consequences, context, decision, llp-0103-…, the-surface-matrix-settled}
    to that plus cli and reporting. No markdown link anywhere targets
    0103-machine-local-policy-classes.decision.md#…, so nothing depended on the old
    rendering.

3. Judgement call: LLP 0080#fail-safe -> LLP 0049#fail-safe

Verdict: right, and the relation downgrade to [constrained-by] is what makes it
right.
Verified independently:

  • llp/0049-hypignore-usage-policy.spec.md is **Status:** Accepted, and
    ### Fail-safe for unimplemented classes {#fail-safe} (line 96) states the invariant
    the two code sites rest on: "the safe failure for a privacy control is 'suppress
    more,' never 'record-and-export something the user flagged.'"
    R3 (line 185) restates it
    normatively.
  • The derivation chain is in the corpus, not invented here: llp/0080:82-84 says the
    empty-list reading is "forbidden by [LLP 0049 §fail-safe]". LLP 0049 is literally
    0080's own cited authority for this rule.
  • The formal supersessor is genuinely unusable. llp/0102 is Accepted and contains no
    fail-safe content; llp/0080's note scopes the supersession to the in-login picker.
    Repointing to 0102 would have been the mis-file the PR describes.
  • I checked the alternative homes myself, not just on the PR's word: llp/0069,
    llp/0070, llp/0071 are all Accepted and none has a fail-safe section or
    anchor
    , so 0080's supersede note ("the non-picker substrate … lives on under LLP
    0069/0070/0071") does not in fact give the fail-safe mechanics a live home. A
    corpus-wide grep for unparseable|unreadable|corrupt finds the rule restated in only
    two other places, neither a candidate target: llp/0081:20 (T1, a plan line that
    itself cites "the LLP 0080 #fail-safe") and llp/0093:63 (itself Superseded). So the
    PR's claim that nothing else restates the rule holds, and the repoint-to-the-invariant
    choice is correct.
  • On the "fix Superseded-by instead" option: the honest reading is that LLP 0080 is
    correctly Superseded (the picker it designs really is gone) while one section of it
    remains the only written record of a live mechanic. That is not a Superseded-by
    error, so correcting Superseded-by would not fix anything; the repo has no
    "partially superseded" status to reach for. Crediting 0080 §fail-safe in JSDoc prose
    while @ref-ing the live invariant is the right trade: the trail survives for a human
    reader without a machine-visible ref into a superseded doc. It does mean the mechanics
    are no longer reachable by tooling from code - worth a future llp/0069-or-0071
    §fail-safe section, but that is new doc work, not this PR's job.

Relation calibration is worth calling out as correct, since it is the subtle part:
0049 §fail-safe's literal mechanic is "unknown class token resolves to ignore", not
"throw". The code throws. [constrained-by] + the gloss "an uninterpretable privacy
signal resolves to 'suppress more', never to 'no exclusions'" claims exactly the
invariant and not the mechanic, which is accurate. [implements] here would have been
wrong.

4. Judgement call: the {#reporting} split

Verdict: correct, and under-applied by exactly one site (fixed below).

clients.js:1074's gloss - "--check names which source governs (dotfile vs
machine-local entry) and the entry's class" - is a near-verbatim quote of the
Consequences bullet at llp/0103:88-90 ("hyp ignore --check and hyp status
reporting must name which source governs (dotfile vs machine-local entry) or debugging
gets harder"). Nothing in the {#cli} bullet is about --check reporting at all;
--check is LLP 0049's flag, and the same function already carries
@ref LLP 0049#prospective-only. So the split is right, and the bullet rewrite is
content-identical - only a bold label and the marker were added.

Findings

A1 (minor, fixed) - runPolicyShow carries the same reporting gloss but stayed on #cli

src/core/commands/policy.js:254 read:

@ref LLP 0103#cli [constrained-by]: names the governing source (dotfile vs machine-local) and class; store/resolver unchanged

That first clause is the same {#reporting} bullet that justified moving
clients.js:1074, and policy show is hyp ignore --check's successor delegating to
the very function that now carries #reporting. Applying the PR's own rule to one of
the 15 sites and not the other silently mis-files it - and once it resolves, nothing
will ever flag it again. Split it the same way, which also makes the remaining four
policy.js glosses uniform:

@ref LLP 0103#reporting [constrained-by]: the report names which source governs (dotfile vs machine-local entry) and the class
@ref LLP 0103#cli [constrained-by]: the store, resolver, and class lattice are unchanged; only the verb spelling is new

A2 (minor, fixed) - the error_kind prose credit moved to a doc that does not contain it

src/core/usage-policy/local_only.js:18 was changed from (LLP 0080 #fail-safe) to
(LLP 0049 #fail-safe). LLP 0049 §fail-safe mentions no throw, no structured error and
no error_kind; LLP 0080 §fail-safe is where "the failure is a structured error naming
the file"
is written, and llp/0081:20 (T1) names the literal string
local_only_list_unreadable as "the LLP 0080 #fail-safe". This is a bare prose mention,
not an @ref, so it never affected the WARNING count - the change bought nothing and
cost accuracy for a reader asking where that error_kind comes from. Reverted to
LLP 0080 #fail-safe, consistent with the 0080 credit the PR itself keeps in the class
JSDoc three lines below.

N1 (nit, not fixed - for the follow-up issue) - the placeholder residue is 9, not 6

The PR body's proposed item-2 follow-up text says "6 refs inside
.claude/skills/ref-check/SKILL.md (88, 114, 122) and ref-story/SKILL.md
(101, 116, 135)". The same two files also carry 3 path-form placeholders -
ref-check/SKILL.md:24 (path/to/doc.md#anchor) and :96, :115
(docs/vendor/spec.md#tokens) - which are broken for the same reason and would leave
/ref-check non-zero even after the 6 are handled. Issue #457 item 4 counted these
(it says 11 and lists all 9 line numbers). Worth folding into the follow-up issue so it
covers the whole residue.

N2 (nit, not fixed) - PR-body reasoning for the policy.js sites

Recorded under section 2 above: the body's stated ground for keeping #cli on the five
policy.js sites does not match what those glosses say. The conclusion is right; only
the argument needs the Extended-by-note framing. Flagged rather than changed, since
this review does not edit PR bodies.

Fixes pushed

Commit f0f603e, on top of b375535, pushed to fix/issue-457 only.

 src/core/commands/policy.js         | 3 ++-
 src/core/usage-policy/local_only.js | 2 +-

Positively verified against the remote branch content (not just locally):

  • git show origin/fix/issue-457:src/core/usage-policy/local_only.js | sed -n 18p ->
    /** `error_kind` carried by {@link LocalOnlyListUnreadableError} (LLP 0080 #fail-safe). */
  • git show origin/fix/issue-457:src/core/commands/policy.js | sed -n 254,255p -> the
    #reporting + #cli pair above.
  • Re-ran the checker at f0f603e: refs 1368 (+1, the added ref), BROKEN 9 / 6
    LLP-form, WARNING 2, unchanged
    ; policy.js:254 (#reporting) and :255 (#cli)
    both resolve, as do all other LLP 0103#* and LLP 0049#fail-safe sites.

Gates

  • npm test @ b375535: 2846 pass / 8 fail / 1 skipped. The 8 are
    test/core/leave-command.test.js (831, 832, 833, 835, 836, 837, 838, 839). I ran the
    same suite on origin/master in the same worktree: byte-identical 8 failures,
    2846 pass. Baseline confirmed, no regression. Same result after my fixes.
  • npm run typecheck: clean at b375535 and at f0f603e.
  • House style: no em dash (U+2014) on any added line, in the PR's diff or mine
    (grepped the added lines of both diffs for U+2014: 0 hits each). The pre-existing em dashes in
    clients.js (18) and matcher.js (5) are untouched by both diffs - they are the
    [relation] <U+2014> gloss HINTs that issue Pre-existing @ref breakage found by a corrected ref-check: 14 refs to a nonexistent LLP 0103#cli anchor, 2 refs into a Superseded LLP, and 3 duplicate LLP numbers #457 item 4 defers.
  • No runtime behaviour change in either diff: two anchor names in one design doc,
    one rewrap, and @ref/JSDoc prose. Consistent with there being no new unit test.

Held-PR overlap: none, verified

Ran git diff --name-only origin/master...origin/<branch> for every held branch and
grepped for the five files (plus src/core/commands/policy.js, which my fix adds):

branch PR touches any of the six files?
fix/issue-448 #449 no
fix/issue-442 #450 no
fix/issue-452 #456 no
fix/issue-453 #458 no
claude-desktop-consent-and-entrypoint-gate #422 no
integration/bounded-query-execution #221 no
fix/issue-459 (parallel) no

Zero file-level intersection, so no merge-tree reconciliation is needed. #456 does
touch .claude/skills/ref-check/SKILL.md - the document this PR's validation depends
on - but this PR does not edit it, so the dependency is one-way and not a conflict. The
practical consequence is only ordering: until #456 lands, a literal /ref-check run
still uses the heading-slugs-only §3 and will disagree with the numbers above.

Scope statement for #457

Confirmed the body is explicit that merging closes #457 while items 3 and 4 stay
unfixed: the section is headed "Merging this closes #457, and items 3 and 4 are NOT
fixed", followed by "They still need their own issues. Please file:" and three
enumerated follow-ups (duplicate LLP numbers 0098/0099/0111; the skill-doc placeholder
refs; the 459 em-dash HINTs). Clear enough that nothing is lost on close, subject to N1
above (the placeholder count should be 9). I did not touch items 3 or 4.

Verdict

The substance is sound: both judgement calls are right, both are right for defensible
reasons, and the numbers reproduce under an independent implementation of the corrected
algorithm. The two things worth fixing were consistency artefacts of the split itself
rather than errors of judgement, and both are pushed. Ready for a human, still draft -
not merged, not marked ready by this review.


Host state untouched: no hyp subcommand of any kind was run, and no
~/.claude/settings.json, ~/.codex/config.toml or ~/.config/systemd/ file was read
or written. Work was done in a detached throwaway worktree; /work/hypaware was never
switched.

…hor claim LLP 0103 never makes

Two glosses that resolve but that their target's prose does not support.

- `readListEntriesSync` (`matcher.js`) kept its "a corrupt list fails the
  resolve loudly" gloss verbatim when the target moved from LLP 0080
  §fail-safe to LLP 0049 §fail-safe. LLP 0049 §fail-safe prescribes the
  opposite mechanic: an unimplemented class token *resolves* to `ignore` and
  logs a warning, it never throws. The same defect was corrected on
  `local_only.js` (gloss reworded to the pure invariant, throw mechanics
  credited to LLP 0080 in prose) but not here. Gloss now states the
  invariant LLP 0049 actually supplies; the block's own prose credits the
  throw-not-empty mechanic to LLP 0080 §fail-safe, matching `local_only.js`.
- `runPolicyUnset` (`policy.js`) glossed `LLP 0103#cli` with "reuses the
  shared `isEqualOrDescendant` ancestor predicate". LLP 0103 mentions no
  ancestor or descendant predicate anywhere; that is LLP 0071's, already
  cited on `matchList`. Reworded to the symmetric removal `#cli` does mint.

Annotation and JSDoc prose only. `npm test` 2846 pass / 8 fail, all 8 in
`test/core/leave-command.test.js` and all 8 present on `origin/master`;
typecheck clean; corrected ref-check unchanged at BROKEN 9, WARNING 2.
@philcunliffe

Copy link
Copy Markdown
Contributor Author

Neutral review, round 2 of 2 (FINAL) - f0f603e

Round 1 reviewed b375535 and fixed two things in f0f603e
(comment).
This round verified those two fixes, re-derived the ref-check totals
independently, and swept every @ref the PR made resolve for the one failure
mode a resolving-ref checker cannot catch: a gloss its target's prose does not
support. Two such glosses found and fixed in 6a9c6a3.


1. Verdict on round 1's fix A1 (the #reporting/#cli split on policy show)

Correct, and it did not create a new inconsistency. I applied the PR's own
rule ("the anchor has to land on the prose the gloss actually quotes") to all 12
code sites and the 4 doc sites myself.

The two anchors mint these referents:

  • llp/0103:63 - **CLI** {#cli}: - hyp ignore's dotfile meaning unchanged;
    machine-local writes get flags on the same verb (--local-only, --private,
    a sync spelling); hyp unignore grows symmetric removal; plus the
    Extended-by: LLP 0110 note retiring the flag spelling for a class-neutral
    hyp policy verb with "the class taxonomy above is unchanged".
  • llp/0103:88 - **Reporting names the governing source** {#reporting}: -
    "hyp ignore --check and hyp status reporting must name which source
    governs (dotfile vs machine-local entry)".
site anchor what the gloss is about verdict
src/core/commands/clients.js:710 #cli --private/--sync flag parsing OK
src/core/commands/clients.js:755 #cli flag dispatch to the marking verb OK
src/core/commands/clients.js:891 #cli shared marking verb behind the flags OK
src/core/commands/clients.js:938 #cli flag dispatch to the unmarking verb OK (hyp unignore grows symmetric removal)
src/core/commands/clients.js:1008 #cli symmetric class-scoped removal OK
src/core/commands/clients.js:1074 #reporting --check names the governing source OK (the PR's move)
src/core/commands/policy.js:33 #cli store/resolver/lattice unchanged, verb spelling new OK
src/core/commands/policy.js:218 #cli same OK
src/core/commands/policy.js:254 #reporting the report names the governing source and class OK (A1)
src/core/commands/policy.js:255 #cli store/resolver/lattice unchanged OK (A1)
src/core/commands/policy.js:292 #cli "reuses the shared isEqualOrDescendant ancestor predicate" mis-filed, fixed in 6a9c6a3
src/core/commands/policy.js:339 #cli enumerates the version-2 class-per-entry store nit, see below
llp/0111:21, llp/0111:247, llp/0112:42, llp/0112:43 #cli the CLI paragraph / the store-and-classes rationale OK

Two points that settle the A1 question specifically:

  • The five policy.js sites are now genuinely uniform. Each carries exactly
    one #cli "the model is unchanged, only the spelling is new" claim, and show
    carries #reporting on top because it is the only one of the five that
    reports anything. Before f0f603e, show was the odd one out: it made the
    source-naming claim under #cli while its four siblings made the
    model-unchanged claim under the same anchor.
  • The clients.js:1074 vs policy.js:254-255 asymmetry is justified, not a
    new defect. runIgnoreCheck legitimately loses #cli entirely: the #cli
    bullet never mentions --check in any form. runPolicyShow legitimately
    keeps it: the "new verb spelling, same model" claim is policy.js's to make,
    and llp/0111:21 makes that exact claim against that exact anchor
    ("extends its CLI paragraph; store format, resolver, and class lattice
    untouched"), which is independent corpus sanction for the wording round 1
    copied onto :255.

2. Verdict on round 1's fix A2 (the prose credit back to LLP 0080)

Correct, and demonstrably so rather than merely conservative. The two
sections say different things, and the difference is exactly the mechanic:

  • llp/0049-hypignore-usage-policy.spec.md:96-109 {#fail-safe} is about a
    .hypignore naming a class the running version does not implement. It
    resolves to ignore and logs a warning. There is no throw, no
    structured error, no error_kind, and no file naming anywhere in it. What it
    does supply is the invariant: "the safe failure for a privacy control is
    'suppress more,' never 'record-and-export something the user flagged.'"
  • llp/0080-local-only-dir-selection.design.md:79-90 {#fail-safe} is where
    the mechanic lives, verbatim: "the store throws, the resolver propagates, and
    readRowsSince lets the error fail the partition read"
    and "the failure is a
    structured error naming the file - never a silent state."
    llp/0081:20
    T1 then spells the constant out: "unparseable => throw with
    error_kind: 'local_only_list_unreadable' - the LLP 0080 #fail-safe"
    .

So LOCAL_ONLY_LIST_UNREADABLE_ERROR_KIND genuinely belongs to LLP 0080, and
local_only.js:18 is right to say so. The @ref staying on LLP 0049#fail-safe
is also right: [constrained-by] an invariant is what that ref is, and it is the
citation format.js:30 already carries for the same polarity.

But A2 was incomplete. See finding B1.

3. Findings

B1 (medium, FIXED in 6a9c6a3) - the same A2 defect survived in matcher.js

src/core/usage-policy/matcher.js:215. The PR moved this ref's target from
LLP 0080#fail-safe to LLP 0049#fail-safe while keeping the gloss byte-for-byte:

- @ref LLP 0080#fail-safe [implements]: a corrupt list fails the resolve loudly, never silently to "no exclusions"
+ @ref LLP 0049#fail-safe [constrained-by]: a corrupt list fails the resolve loudly, never silently to "no exclusions"

"fails the resolve loudly" is not merely absent from LLP 0049 §fail-safe, it is
the opposite of what that section prescribes: 0049's fail-safe succeeds at
resolving (to ignore) and logs a warning. This is the identical defect round 1
caught on local_only.js - and note the PR itself had already reworded
local_only.js's gloss to the pure invariant ("resolves to 'suppress more',
never to 'no exclusions'"), which is why that one reads correctly. matcher.js
was the one where only the target moved.

The mechanic is credited to LLP 0080 nine lines earlier at matcher.js:170,
but that is matchList's JSDoc, a different construct; CLAUDE.md's attachment
rule means it does not carry to readListEntriesSync. Fixed the same way
local_only.js was: the gloss now states the invariant LLP 0049 supplies, and
the block's own prose credits the throw-not-empty mechanic to LLP 0080 §fail-safe.

B2 (low, FIXED in 6a9c6a3) - #cli glossed with a mechanic LLP 0103 never mentions

src/core/commands/policy.js:292 (runPolicyUnset) glossed LLP 0103#cli as
"reuses the shared isEqualOrDescendant ancestor predicate". grep -i 'ancestor\|descendant' llp/0103-machine-local-policy-classes.decision.md
returns nothing - the predicate is LLP 0071's, already cited correctly at
matcher.js:175 ("segment-aware equal-or-descendant list membership"). This was
invisible before the PR because the ref was BROKEN; making it resolve is what
exposed it. Reworded to the symmetric removal #cli actually mints. The
ancestor-walk fact itself is still stated in the function's prose at
policy.js:278-279, so nothing was lost.

N1 (nit, not fixed) - policy.js:339's gloss is about the store, not the CLI

"enumerates the version-2 class-per-entry store as-is; no format change" quotes
LLP 0103's Decision preamble (the version-2 JSON block, llp/0103:41-49),
not the #cli bullet. By the letter of the PR's rule it wants #decision, which
also resolves. Left alone on purpose: llp/0112:47 T6 explicitly instructs that
"the store-and-classes rationale keeps citing LLP 0103#cli", so changing it
would put the code at odds with an Accepted plan. Same tension applies mildly to
policy.js:33/:218/:255, whose "the store, resolver ... are unchanged" half
is not in the #cli bullet either - but llp/0111:21 makes that exact claim
against that exact anchor, so the corpus is self-consistent there. This is really
a corpus-level question (does #cli want renaming, or does LLP 0112 T6 want
updating) and belongs with #457's citation-form item, not here.

N2 (nit, not fixed) - "and the class" on the two #reporting glosses

clients.js:1074 says "and the entry's class"; policy.js:254 says "and the
class". The #reporting bullet says only "which source governs". Reporting the
class is squarely LLP 0103's point and both enclosing JSDoc blocks document it,
so this is a mild over-reach at worst.

N3 (nit, not fixed) - pre-existing em dash in a block this PR's ref now lives in

src/core/usage-policy/matcher.js:168 (throws — the same fail-safe) is a
CLAUDE.md house-style violation. Present on origin/master unchanged, so out of
scope; my edit is in the adjacent block and adds none. Flagging so it is not
mistaken for a regression.

4. My own ref-check totals, and the 6-vs-9 placeholder question

Ran my own implementation of the corrected §3 (index {#slug} on any line;
inline <a id=>/<a name=>; heading slugs lowercased with one - per
whitespace character and no collapsing, <a id>/<a name> tags stripped off
heading lines only). One calibration note for whoever lands #456: the slugger
must keep _. Strip it and 17 perfectly good refs go BROKEN, e.g.
LLP 0045#settings_file-is-home-relative-and-a-violation-is-loud against
llp/0045:404. The no-collapsing rule is also load-bearing and verifiable:
llp/0045:159 ## Part 2 — The attach handler (...) yields the double
hyphen in part-2--the-attach-handler-srccoreconfigaction_attachjs, which is
what action_attach.js:56 actually cites.

origin/master (f9b9667) f0f603e after 6a9c6a3
refs / files / LLP docs 1367 / 359 / 133 1368 / 359 / 133 1368 / 359 / 133
BROKEN, total 25 9 9
- of which LLP-form 22 6 6
- of which path-form 3 3 3
WARNING 4 2 2
duplicate LLP numbers 3 (0098, 0099, 0111) 3 3

The placeholder residue is 9, not 6. The PR's 22 -> 6 is the LLP-form-only
tally; the true figure is 25 -> 9. The delta is exactly three path-form
placeholder refs in the same skill doc:

.claude/skills/ref-check/SKILL.md:24   @ref path/to/doc.md#anchor
.claude/skills/ref-check/SKILL.md:96   @ref docs/vendor/spec.md#tokens
.claude/skills/ref-check/SKILL.md:115  @ref docs/vendor/spec.md#tokens

And the difference does matter for the follow-up issue, for a reason beyond
arithmetic. Item 2 of #457's follow-up list asks for "an example-block marker or
an exclusion rule" so /ref-check can exit 0. A rule keyed on the LLP-number
form (LLP NNNN#anchor) silences 6 of the 9 and still leaves the tool exiting 1
forever. The rule has to cover the path form too, which is only visible once
you count all 9. Worth writing the number and the form breakdown into the issue.

The other independent confirmations of round 1's numbers: 1367 refs on master,
not 1364; WARNING 4 -> 2 reproduces exactly; and the 2 survivors
(llp/0101:21, llp/0102:18 citing the documents they supersede) are correct as
written.

5. Checks

  • npm run typecheck: clean.
  • npm test: 2855 tests, 2846 pass, 8 fail, all 8 in
    test/core/leave-command.test.js. Exactly the documented origin/master
    baseline; no regression.
  • No em dash added (git diff origin/master...6a9c6a3 | grep '^+' | grep U+2014
    is empty). No semicolons added. § used for section references, matching the
    existing convention in local_only.js and the PR's own prose.
  • Corrected ref-check unchanged by 6a9c6a3 (BROKEN 9, WARNING 2), as expected:
    both fixes are gloss/prose text, neither moves a target.
  • Positively verified 6a9c6a3 against f0f603e: git diff f0f603e 6a9c6a3
    touches exactly src/core/commands/policy.js (1 line) and
    src/core/usage-policy/matcher.js (1 gloss line + 2 prose lines). Nothing else.
  • Anchor form audited: - **Bold** {#slug}: prose is the corpus's established
    list-item convention, used in llp/0049:154, llp/0101, llp/0105,
    llp/0106, llp/0107, llp/0122:107, llp/0138. Both new anchors match it
    exactly, and neither collides with an existing anchor in llp/0103.
  • Spot-checked the llp/0135 unwrap end to end: llp/0011:27
    ## Interactive walkthrough exists, the unwrapped ref resolves, and the
    annotation the doc claims is "currently on runPickerWalkthrough" really is
    there, at src/core/cli/walkthrough.js:314.

6. For triage

Round cap is 2 and this was round 2, so everything below goes to a human rather
than a round 3. Nothing here blocks the PR.

  1. The follow-up issue's placeholder count is 9, not 6, and 3 of the 9 are
    path-form (@ref path/to/doc.md#anchor). The exclusion rule item 2 asks for
    must handle both citation forms or /ref-check still cannot exit 0. Evidence
    in §4.
  2. policy.js:339 (N1): the #cli anchor carries glosses about the store
    format because llp/0112:47 T6 tells it to. Either rename/re-scope the anchor
    or update T6. Belongs with Pre-existing @ref breakage found by a corrected ref-check: 14 refs to a nonexistent LLP 0103#cli anchor, 2 refs into a Superseded LLP, and 3 duplicate LLP numbers #457's citation-form item.
  3. The PR body's stated reason for the five policy.js sites is still wrong
    (round 1's nit, not re-litigated here): the body says all five "gloss the
    --private/--local-only/--sync machine-local marking flags", but four of
    them gloss "store/resolver/lattice unchanged". The choice of #cli holds
    anyway, for a different reason (llp/0111:21 makes the same claim against the
    same anchor; llp/0111:247 and llp/0112:42-43,47 mandate the citation).
    Body deliberately not edited.
  4. N2 and N3 above: cosmetic, no action needed unless a sweep is happening
    anyway. N3 (matcher.js:168) is one of the 459 em-dash HINTs item 3 of the
    follow-up list already covers.

Summary

A1 correct and complete; A2 correct on the fact but incomplete in scope, and the
missing half is now fixed. Two glosses that resolved but that their target's
prose contradicts or omits, both fixed in 6a9c6a3. Corrected ref-check
25 -> 9 BROKEN and 4 -> 2 WARNING at this head, with the settled placeholder
count of 9 (6 LLP-form + 3 path-form) for the follow-up issue. Baseline tests
and typecheck green. Held for human review as usual; not marking ready, not
merging.

@philcunliffe

Copy link
Copy Markdown
Contributor Author

Triage rung (LLP 0017) - review rounds exhausted, PR judged safe to ship

Round cap (2) was reached at head f0f603e with findings still open; this is
neutral's judgement call on whether PR #461 can merge safely as-is.

Re-checked every unresolved finding from both review rounds against the
current head 6a9c6a3 (which already carries round 2's own fixes B1 and B2):

  1. Follow-up issue's placeholder count should be 9, not 6 (3 are path-form,
    .claude/skills/ref-check/SKILL.md:24,96,115) - a text-accuracy point for
    the follow-up issue, not a code defect.
  2. src/core/commands/policy.js:339 - the #cli anchor glosses store-format
    content that arguably belongs to a different anchor/section, per tension
    with llp/0112:47 T6 - a corpus-level citation-form question, not a code
    defect.
  3. The PR body's stated reasoning for the five policy.js #cli sites does
    not match what those glosses say, even though the anchor choice itself is
    correct - inaccurate prose in the PR description, no code impact.
  4. src/core/commands/clients.js:1074 / src/core/commands/policy.js:254 -
    "and the class" / "and the entry's class" is a mild wording over-reach
    beyond the literal #reporting bullet text - cosmetic.
  5. src/core/usage-policy/matcher.js:168 - a pre-existing em dash, already
    present on master, out of this PR's scope.

Classification: all five are documentation/annotation nits (PREFERENCE),
none can cause a production defect.
The whole PR is @ref/JSDoc/design-doc
text only - verified independently: git diff origin/master...6a9c6a3 touches
only annotation lines and two design docs, zero runtime code paths change.
npm test reproduces the exact pre-existing baseline (2846 pass / 8 fail, all
8 in test/core/leave-command.test.js, byte-identical to origin/master in a
fresh install), npm run typecheck is clean, and no em dash was added by this
PR's diff. This repo's privacy-sensitive surfaces (LLM traffic capture,
credentials, prompts) are untouched.

Outcome: PR can merge safely. All residual findings are deferred rather
than blocking.

Follow-up issue

An open follow-up already exists and covers every residual finding above,
including item 2 (the corrected 9-vs-6 placeholder count with the path-form
breakdown), the policy.js:339 anchor tension (its "Not included" section),
and both cosmetics (the matcher.js:168 em dash and the "and the class"
wording) folded into its item 3: #463.

No new issue opened (idempotent - reusing #463 rather than creating a
duplicate).


Triage marker will be appended to the PR body next, at head 6a9c6a3.

@philcunliffe
philcunliffe marked this pull request as ready for review July 29, 2026 23:53
@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 29, 2026
@philcunliffe
philcunliffe merged commit b5fe563 into master Jul 30, 2026
8 checks passed
@philcunliffe
philcunliffe deleted the fix/issue-457 branch July 30, 2026 03:17
philcunliffe added a commit that referenced this pull request Jul 30, 2026
…n em dash (#463 items 2 and 3) (#468)

* LLP corpus: every @ref gloss opens with a colon, never an em dash (#463 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>

* Illustrative @refs are marked, so ref-check can reach exit 0 (#463 item 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>

* The ignore marker only fires inside a comment, and suppression is gated

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>

* A marker is a comment in its file's language and never a code sample

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>

---------

Co-authored-by: neutral-reconciler <neutral@example.com>
Co-authored-by: Claude <noreply@anthropic.com>
bgmcmullen pushed a commit that referenced this pull request Aug 6, 2026
…639) (#651)

* Ref hygiene: the tolerance list expires with the defect it forgives (#639)

Issue #639 reported eleven broken `@ref` anchors. None of them are broken.
Every anchor the issue names resolves on `origin/master`: LLP 0103 grew
`{#cli}` and `{#reporting}` when PR #461 landed, and `0138#one-command`,
`0138#one-materializer`, `0101#deadline`, `0101#no-release` and
`0107#reversal` have been `{#slug}` markers on list items since the
documents were written. A resolver that indexes only heading slugs reports
all six of those as broken, which is the failure `ref-check`'s SKILL.md
already warns is worth 47 references on this corpus.

What did survive is the tolerance list in the hygiene gate. Its five
entries, 16 forgiven occurrences, forgive nothing: PR #461 landed the
repairs and the entries stayed behind, so the only remaining claim that
`policy.js` cites a dead anchor was the list that exists to excuse it.
Issue #639 read that claim back out.

Prune the entries, and make the next one expire on its own: the spend
ledger belongs to the caller now, and a tolerance nobody spends fails the
suite instead of sitting there as evidence of a defect that healed.

With the list empty the gate has no exceptions: 1901 annotations, zero
broken.

Fixes #639

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

* Ref hygiene staleness check: forgive no more than is still broken

The staleness test only checked spent > 0, so a tolerance whose budget
outran the number of genuinely broken refs at that identity passed
silently, leaving unspent forgiveness that could absorb new breakage
at the same file+target. Tighten it to spent < budget, and report both
numbers in the failure message so the offending entry is directly
editable.

---------

Co-authored-by: test <test@example.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: test <test@test.com>
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

1 participant