/reports/how-proved.md · raw

How we proved our kills never halt

All 18 kills are proved the same way at heart. Each one is a Rocq file, checked against the unmodified busycoq framework, that ends in the statement "this machine, started on a blank tape, never halts".

This page gives the common recipe, a table of the 18 kills, what is special about each kind of proof, and a few sentences you can use to describe a proof in chat.

1. The common recipe

Two words first.

The recipe has five steps.

  1. Find the repeating structure. Simulate, and look at the tape each time the head comes back to the same spot. In every kill it is a counter of some kind: a binary counter whose digits are blocks, a bouncer that piles up blocks, or a stack of blocks that call each other.

  2. Write one round as a macro rule. Usually a handful of rules, one per kind of round (an ordinary count, an overflow, a merge).

  3. Find an invariant that excludes halting. The halting transition can only fire from a few tape shapes that no rule covers. The invariant is chosen so that, while it holds, some rule always applies, and every rule keeps it.

  4. Show the run starts inside it. Simulate the opening exactly (from a few steps to about 92,000) until the tape has the round shape, and check the invariant there.

  5. Let Rocq check every step. busycoq's multistep_nonhalt handles the opening: if the start reaches tape c in exact steps and c never halts, the start never halts. Its progress_nonhalt_cond handles the rest: if every tape with the property moves, by at least one real step, to another tape with the property, and the first tape has it, the machine never halts.

Every macro rule is an exact run of the real transition table, so no halting step can hide inside a round.

Two tricks come back again and again.

2. The 18 kills

Status: accepted means our busycoq pull request is merged; submitted means it is in an open pull request; claimed means the Rocq proof passes all our checks but is not submitted yet. Currently that is 1 accepted, 4 submitted and 13 claimed.

Each label links to the machine's card. #11b, #25, #24 and #12b have no card of their own, so they link to their kill page. Rows are grouped by family, and within a family by transition table; the first row of each table is its parent proof.

Kill Machine Family Approach Status
#4 1RB0LA_0RC1RD_0LD1RE_1LA0RB_0RD1RF_1RC--- Sync bouncer counter, #4's table a safe region for the block counts that both even and odd cycles keep claimed
#4b 1RB---_0LC1RF_1LE0RD_0RB1RC_1RD0LE_0RC1RA Sync bouncer counter, #4's table same proof as #4, with renamed states and a new start claimed
#8 1RB0LF_0RC---_1RD0RA_1LE0RC_0LA0LE_1LE1LB Sync bouncer counter a window kept at every overflow outside a pause, for a spread-out binary counter claimed
#11 1RB0LE_1RC0RC_1LD0RB_1LA0LE_1LD1LF_1LD--- Skelet 1-like Class 2, #11's table an invariant showing an increment never reaches a 0 digit with no fuel left claimed
#33 1RB0RE_1LC0RE_1LD0LD_1RA0LC_1RA1RF_1RA--- Skelet 1-like Class 2, #11's table #11's proof mirrored, new start (#11's state D) claimed
#33b 1RB0LD_1RC0RE_1LD0RE_1LA0LA_1RB1RF_1RB--- Skelet 1-like Class 2, #11's table same proof as #33 (#11 mirrored), new start claimed
#11b 1RB0RB_1LC0RA_1LE0LD_1LC1LF_1RA0LD_1LC--- Skelet 1-like Class 2, #11's table same proof as #11, new start (#11's state B) claimed
#3 1RB0LD_1RC0RA_1RD1RF_1LA0LE_1RE1LD_0RB--- Skelet 1-like Class 2, #3's table an invariant on a binary down-counter that regroups its blocks submitted (PR #15 open)
#25 1RB1RF_1LC0LE_1RD0LB_1RA0RC_1RE1LB_0RD--- Skelet 1-like Class 2, #3's table same proof as #3, new start (one block 2 longer) submitted (PR #15 open)
#24 1RB0RE_1LC0RA_1LD0LB_1LA1LF_1LE1RA_0LC--- Skelet 1-like Class 2, #3's table #3's proof from a new start, carried over by a mirror and a renaming submitted (PR #15 open)
#3b 1RB0RD_1RC1RF_1LD0LE_1RA0LC_1RE1LC_0RA--- Skelet 1-like Class 2, #3's table same proof as #3, new start on its own path submitted (PR #15 open)
#12 1RB1LC_1RC1LB_1LD0RB_0LE1RD_---0LF_0LA1LF Counter Class 4 exact cost-counting for a self-similar call stack claimed
#12b 1RB1LA_1LC0RA_0LD1RC_---0LE_0LF1LE_1RF1LB Counter Class 4 same proof as #12, one changed transition and a 2-call edge budget claimed
#2 1RB0RF_1LC0RE_0LC0LD_1RD1LB_0RF1RA_0RB--- Bell eats counter a proved neighbour's sweep recipe plus a +1 step, shown never to get stuck accepted (PR #14 merged)
#30 1RB0RF_1RC1RF_1LD0RE_---0LE_1LA0LE_0RA0LC Bell eats counter, #30's table an invariant showing each round's recursive cleanup fits in its budget claimed
#7 1RB0RA_1LC0LD_1LE1LD_0LB0RE_1RF0LA_---0RA Bell eats counter, #30's table same proof as #30, mirrored, new start (#30's state E) claimed
#31 (W4-1) 1RB1RF_1LC0RD_---0LD_1LE0LD_1RA0RF_0RE0LB Bell eats counter, #30's table same proof as #30, renamed states, new start (#30's state B) claimed
W4-2 1RB1LF_0RC0RB_0LD1LA_1LC0LE_0LA---_1LA1LF Bell eats counter a size invariant tying the counter to the length of the last round claimed

Only 8 of the 18 needed a new idea (#2, #4, #8, #11, #3, #12, #30, W4-2). The other 10 reuse a parent proof.

3. What is special about each kind of proof

Counters whose digits carry fuel (#11's and #3's tables)

The tape is a binary counter whose digits are pairs of blocks, such as (10)^u (01)^v. Each count step changes the block lengths of every digit it touches.

In #11 (counting up), each increment burns one 01 of "fuel" from every digit it touches. In #3 (counting down), each decrement lengthens one block of a pair and shortens the other.

At an overflow (for #3, every second time the count reaches zero) the blocks re-pair: each block joins a neighbour to form a new digit, so the roles of the two kinds of block swap, and the counter gains a digit.

The danger is a block running dry at the wrong moment. The invariant combines parity (a block that runs dry has the right parity, so it just merges with its neighbour, which a rule covers) with lower bounds (the other blocks never get too short). It never needs to know when the merges happen.

Bell eats counter (#2, #30's table, W4-2)

One side of the tape is a counter. The other side is a budget, or a stretch of filler blocks. When the budget runs out, the machine eats the low end of the counter and refills the budget. Halting needs an eat to meet a tape in a bad shape.

Sync bouncer counters: a safe region (#4, #8)

A binary counter sits at one end, a row of blocks grows at the other, and the head bounces between them adding 1 per bounce. The numbers that matter are the block counts at each overflow.

A call stack with an exact cost (#12)

The tape is a stack of blocks of 1s, and each block acts like a function call that costs its caller one 1. Each round starts at the left edge with 3 calls to spend; halting needs a 4th.

A round replays the machine's own earlier history one level down, with some extra lone 1s added. An induction says exactly which inner rounds cost 3 instead of 2. The real top level has no extra lone 1s, so every top round costs exactly 2. This is exact counting, not an estimate.

Siblings: same table, new start

Nine kills share a transition table with a parent kill. They differ only in the state letters (for #33, #33b, #24 and #7 also by a mirror) and in which state the run starts from.

The same table means the same macro rules and the same invariant. The only new piece is the opening: simulate from the new start until the tape has the round shape, and check the parent's invariant there. For example, #3b reaches a tape with #3's property after 38,732 steps.

Some siblings shadow the parent's run (#25's far block is always 2 longer than #3's). Others take their own path through the same rules (#3b, #4b, #33b, #7, #31). Both are fine, because the invariant is about the shape of the tape, not about one particular run. Most sibling files are the parent's proof with the state names swapped, plus a new opening.

Mirrored siblings

A mirrored machine swaps L and R in every transition, so its run is the parent's run seen in a mirror.

busycoq already proves that mirroring a machine (flip_halts) or renaming its states (perm_halts) does not change whether it halts. So for #24 and #7, the file proves the parent's table never halts from the matching start state. It then checks that the mirrored, renamed table is exactly the parent's, and these two lemmas carry the result over.

33 is done the other way. Its tape rules are rewritten in mirrored form directly, and #11's invariant is used word for word, since it only talks about digit sizes, never about direction. #33b then reuses #33's file with its states renamed.

Neighbours: one transition changed (#12b)

After renaming, #12b differs from #12 in one transition. That transition only fires when a call starts at the left edge, where it takes a 6-step detour to the same tape #12 reaches in 2 steps.

So #12's whole picture carries over. But #12b's edge budget is 2 calls instead of 3, so it needs #12's exact result that every top round costs 2, with zero slack.

Adapted templates

busycoq holds proofs of hundreds of machines. For each new parent proof we started from the closest one and kept its architecture: how a tape shape is written, how one round is proved, and the final non-halting call. The rules, the invariant and the arithmetic are new.

Kill Template in busycoq How close
#2 SOMDCv6, TM7 one transition away
#4 SBCv7, TM4 one transition away
#11, #3 SBCv6, TM21 same kind of counter, different table
#8 CounterClass3v2, TM1 same architecture, unrelated table
#12 CounterClass2v1 overall shape only
#30 FractalBEC, tm10 mirrored, one transition away; only its bounce rule is kept
W4-2 BECv5, TM1 and TM2 one transition away

"Adapted" means few lines in common. Counting the non-blank lines of our file that also appear, word for word, somewhere in the template's file, only 10% to 34% match. One changed transition can change the behaviour a lot (#4 is one transition from its template, yet only 18% of its lines match). By the same count, 81% to 95% of a sibling's lines also appear in its parent's file.

4. How to say it in one breath

Examples you can paste or adapt.