R-291: assert that a tag one reader accepts stays visible to the other

c0 asked whether R-290's widening of tagsIn reaches past outcome-coverage's loose
counter, which would zero its unparsed count for the wrong reason and retire a
canary. It does not -- 22 beats from each reader on CLEAN_GROUND.md, nothing that
tagsIn accepts invisible to the other guard -- but "does not today" decays quietly,
so it is now a test importing both real functions rather than a copy of either
pattern. Mutation-checked: widening tagsIn to accept [CUS Spot] turns it red.

Measuring it found something the question did not ask about, recorded in the log:
for [cus: ...], [CUS : ...] and [ CUS: ...] the two guards disagree -- tagsIn reads
the tag, beatLikeIn calls it unparseable -- because beatsIn's strict half kept the
case-sensitive literal. The build stops, which is the safe direction, but names the
wrong thing. outcome-coverage.mjs is c0's file and the call is theirs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
slaguru666
2026-09-13 15:50:05 +01:00
co-authored by Claude Opus 5
parent ae8179bd9b
commit ed851b76e3
2 changed files with 58 additions and 0 deletions
+26
View File
@@ -6840,3 +6840,29 @@ starts from a clean corpus rather than from an exception list.
pattern**: widen the reader for the spellings you thought of, and add a looser counter that
names the one you did not. A marker nothing reads is worse than no marker, because whoever
wrote it believes the thing is declared.
## R-291 — the canary c0 asked about, asserted rather than remembered
R-290 widened `tagsIn`. c0 asked the right question about it: `outcome-coverage`'s
`beatLikeIn` names the tags its own reader cannot parse, and if the widened `tagsIn` reaches
past that loose pattern, the unparsed count drops to zero for the wrong reason and stops
being a canary.
**It does not.** Measured on both real readers rather than by copying either pattern:
CLEAN_GROUND.md gives 22 beats from `tagsIn`, 22 from `beatsIn`, 0 unparsed. Across spellings,
everything `tagsIn` accepts is seen by the other guard — parsed, or named. `[CUS Spot]` and
`[CUS= Spot]` are refused by both strict readers and still named by the counter.
"Does not today" is exactly the kind of claim that decays without anyone noticing, so it is a
test: **a tag one reader accepts is never invisible to the other.** It imports both real
functions, so neither pattern is duplicated and the test cannot agree with a stale copy.
Mutation-checked — widening `tagsIn` to accept `[CUS Spot]`, past the counter, turns it red.
**What the measurement found that the question did not ask about.** For `[cus: …]`,
`[CUS : …]` and `[ CUS: …]` the two guards now *disagree*: `tagsIn` reads the tag and checks
it for reachability, while `beatLikeIn` reports it unparseable and fails the build, because
`beatsIn`'s own strict half kept the case-sensitive literal R-290 removed from `tagsIn`. Not
silence — the build stops, which is the safe direction — but the message names the wrong
thing, and the fix is the one check-rollable already made for the cast: read through the other
guard's function rather than a second regex. That file is c0's and the call is theirs; it is
recorded here so it is a decision rather than something nobody wrote down.