Skip to content

tri: an empty match is not a number - #3068

Merged
gHashTag merged 1 commit into
masterfrom
w57-empty-match-is-not-a-number
Sep 3, 2026
Merged

tri: an empty match is not a number#3068
gHashTag merged 1 commit into
masterfrom
w57-empty-match-is-not-a-number

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Closes #3067

Reproduced

$ ./scripts/tri wave
  lesson    last  -> next 1
  theorem   last T727 -> next T728

The document is .claude/skills/t27-wave-loop.md242 KB, present — and its own worked examples are headed ## Worked example — Wave Loop 898.

$ grep -cE '^\*\*[0-9]+\.' .claude/skills/t27-wave-loop.md
0

Why the guard could not fire

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 past the matcher is the case that actually happened.

The write path takes the same route

tri lesson carries the guard written for exactly this. Traced with bash -x on a fixture with no **N. lines:

++ true
+ no=
+ next=1
+ anchor=

no= and next=1, with the die skipped. Nothing was written only because an unrelated downstream step then failed on the same empty anchor — the guard did not stop it, luck did.

After

input result
document with no **N. line lesson UNREADABLE -- no line matched in .claude/skills/t27-wave-loop.md
tri lesson on it refuses, no numbered lessons found in …, writes nothing
document carrying **41. lesson last 41 -> next 42, and tri lesson writes **42.

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.

mutant result
the old reader (empty is a value) 3 defect assertions FAILED
a reader that always fails all three controls FAILED
restored 8 ok

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.

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
gHashTag enabled auto-merge (squash) September 3, 2026 22:15
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-03 22:20:11 UTC

Summary

Status Count
Total Open PRs 13
PRs with Failing Checks 10
PRs with All Checks Green 3
READY 0
FAILING 10
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=9b8875f1c9d4 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@gHashTag
gHashTag merged commit 069198c into master Sep 3, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tri wave prints 'lesson last -> next 1' against a document whose last wave loop is 898

1 participant