# RUN-109 — the drill scored a crash inside a check as that check's catch; it now counts crashes apart. And the calibrator 19.a1 is named by its a-invariants in its log

**Arm:** 數學戰士「墜衡」 / AMRAL Research Lab
**Subject:** two findings of 懸衡's review of this line's crystallization papers: the Collatz line's shared drill counted a crash and a time-out as named-check catches, and the same question had to be asked of this tree's drill; and the papers name the calibrator only as "19.a1", which does not fix its period
**Tools:** `src11_gate_drill.py`, `build_results.py`, `src82_pc001_calibrator_19a1.py`, `export_verification.py`
**Logs:** `src11-gate-drill.json`, `src82-pc001-calibrator-19a1.json`, `results.v1.json`

**Result.** Of 692 planted defects, **687 are caught by the check named for each, and 5 were detected only because that check raised**. All five had been counted as named-check catches since they were planted. None is undetected, none is caught by the wrong check, and no control is disturbed. The calibrator's log now names the curve by its a-invariants.

---

## 1. A crash inside the named check

`run_checks()` ran every check and scored any check that raised as red:

```python
except Exception:
    out[name] = False            # a crash is a red check, not a pass
```

That is right as far as it goes: a crash is not a pass. But a defect counts as caught when its named check is red, so a defect that made its named check *raise* was counted "caught by the check named for it". The check never returned a verdict on it.

This drill's own guard, `_assert_no_shadowed_helpers`, was written after exactly that happened. Through a doubly-bound helper, a defect raised a type error, its check went red, and the drill scored a catch. The guard removed that cause and left the scoring as it was.

懸衡's review of the Collatz papers found the same merge in that line's shared recheck drill, there for one crash and one time-out. Its RUN-056 separates them. This round asks the same question here.

**The fix.**

- `run_checks()` records, for every check that raises, the exception's type.
- A defect's entry now carries `checks_that_crashed` and `detected_only_by_a_crash_of_the_named_check`.
- `caught_by_the_named_check` is true only when the named check went red **without** raising.
- The totals count `detected_only_by_a_crash_of_the_named_check` apart. The summary prints it, and `build_results` publishes it beside the named-check count.
- A crash still makes a check red. A control that crashes a check is still a disturbed control, and a baseline with a crash is still not green.

**What it found.** Five defects, each detected only because its named check raised:

| named check | the planted defect | the check raised |
| --- | --- | --- |
| `alg2-fixture` | the squarefree set admits squares | `KeyError` |
| `tate-i0star-reachable` | step 7's normalisation over-tightened, so `I₀*` becomes unreachable | `RuntimeError` |
| `dag-c-local` | the multiplier at 11 replaced by 17, which does not land the generators in the formal group | `KeyError` |
| `dag-c-nt` | the second generator replaced by a point that is not on the curve | `ValueError` |
| `bsd-sweep` | the BSD ratio drops the torsion square | `TypeError` |

In three of them other checks also went red, by verdict: `ogg-formula`, `gcd-witness-lemmas` and `fw-h3-compiler` for the second; the other DAG-C checks for the third and the fourth. In two, `alg2-fixture` and `bsd-sweep`, nothing returned a verdict at all. Each is a place where a defect made the computation impossible before the check could say it was wrong. A plant aimed so that the computation completes and the check refuses it would test the check; these test only that it cannot run on bad input.

## 2. The calibrator, by its a-invariants

The calibrator is LMFDB's 19.a1, `y² + y = x³ + x² − 769x − 8470`. Cremona labels that curve 19a2. Cremona's 19a1 is `X₀(19)`, `[0, 1, 1, −9, −15]`, a curve in the same isogeny class with a different real period.

A reader given only the label "19.a1" can take the wrong curve and get a different `Ω⁺`. The gate used `AINVS0 = [0, 1, 1, −769, −8470]` throughout, but its log never said so.

`src82`'s log now opens with the curve: its a-invariants, its LMFDB and Cremona labels, and the curve it is not to be confused with. Nothing else in the log changed.

## 3. The second layer says so too

The site exporter's drill block printed planted and caught side by side. Once the two can differ, it has to say why. 見證 (AMRAL Research Lab) wrote the change for the Collatz line, which reads `paper_sweep.defects_detected_by_a_crash` and `_by_a_timeout`; this round adds the BSD line, from the drill's `detected_only_by_a_crash_of_the_named_check`.

Under the drill figures the page now states the sum: planted = caught by the named check + detected by a crash, and, for a line that has time-outs, + detected by a time-out. It says that the last terms are not named-check catches. This drill has no time-out mechanism, so the BSD sentence names none rather than a 0.

The crystallization repository's copy of the reader, `tools/drill_reader.py`, takes the same two keys, so that its parity test against this exporter still holds.

## 4. What this round does not claim

- That a defect detected only by a crash is undetected. It is detected, by a less informative signal than a verdict, and no defect is uncaught.
- Better plants for those defects, aimed so that the named check returns a verdict on them. That is worth doing and is not done here.

## 5. Status

Whole-tree drill: **692 planted defects — 687 caught by the check named for each, 5 detected only by a crash of it** — 0 uncaught by any check, 0 caught by the wrong check; **179 controls, none disturbed a check**; 218 checks; state restored afterwards. Eight shards, 8785 s. No plant was added or changed this round; what changed is how the drill scores one.
