tri: an empty match is not a number - #3068
Merged
Merged
Conversation
Closes #3067 `tri wave` printed lesson last -> next 1 against `.claude/skills/t27-wave-loop.md`, which is 242 KB, present, and whose own worked examples are headed `## Worked example -- Wave Loop 898`. `grep -cE '^\*\*[0-9]+\.'` on it is 0: the document's format moved past the matcher. `last_lesson_no` tested that the file EXISTS and then returned whatever the matcher produced. Empty output with exit 0 is not a failure, so `$(last_lesson_no || echo '-')` never substituted the '-', and `$(( "" + 1 ))` is 1. The `-f` test catches an absent file; a present file whose format has moved is the case that actually happened. The write path took the same route. `tri lesson` carries the guard written for exactly this -- `no="$(last_lesson_no)" || die "no numbered lessons found"` -- and `bash -x` shows `no=` then `next=1` with the die SKIPPED. Nothing was written only because an unrelated downstream step failed on the same empty anchor: the guard did not stop it, luck did. Both readers now return 1 when the matcher produced no number, so the '-' and the die work again, and `tri wave` says UNREADABLE and names the file instead of inventing a number. `theorem` is fixed the same way although it reads correctly today -- the two halves of one display should not differ in whether they can lie. Deliberately not done: widening the matcher to the `## ... Wave Loop N` form. Which heading is canonical belongs to whoever owns that document, and "I cannot read this" has to land first either way. Two mutants. The old reader kills the three defect assertions. A reader that always fails kills all three CONTROLS -- a document that does carry lessons must still be read, and a new lesson must still be written as 42 rather than 1. Found by a read-only fan-out over gates (41 candidates, 15 confirmed); this was one of the twelve left after the two merge-blocking ones.
gHashTag
enabled auto-merge (squash)
September 3, 2026 22:15
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3067
Reproduced
The document is
.claude/skills/t27-wave-loop.md— 242 KB, present — and its own worked examples are headed## Worked example — Wave Loop 898.Why the guard could not fire
last_lesson_notested that the file exists and then returned whatever the matcher produced. Empty output with exit 0 is not a failure, so$(last_lesson_no || echo '-')never substituted the-, and$(( "" + 1 ))is 1.The
-ftest catches an absent file. A present file whose format has moved past the matcher is the case that actually happened.The write path takes the same route
tri lessoncarries the guard written for exactly this. Traced withbash -xon a fixture with no**N.lines:no=andnext=1, with thedieskipped. Nothing was written only because an unrelated downstream step then failed on the same emptyanchor— the guard did not stop it, luck did.After
**N.linelesson UNREADABLE -- no line matched in .claude/skills/t27-wave-loop.mdtri lessonon itno numbered lessons found in …, writes nothing**41.lesson last 41 -> next 42, andtri lessonwrites**42.theoremis fixed the same way although it reads correctly today: the two halves of one display should not differ in whether they can lie.Deliberately not done
Widening the matcher to
## … Wave Loop N. Which heading form is canonical belongs to whoever owns that document — and "I cannot read this" has to land first either way, because until it does, every answer that command gives about lessons is unfalsifiable.Found by the read-only fan-out behind #3061 — 41 candidates, 15 confirmed — as one of the twelve left after the two merge-blocking ones.