Files
BFM-decomp/cookbook/C0116.md
T

1.9 KiB

§105 — A gate's revert must survive an EXCEPTION, not just a failure (Phase 29 T53, jtbl_family_bank)

jtbl_family_bank.bank() was carefully revert-on-fail: every return path restored the overlay, and the stage loop even caught a stage that raises while producing a candidate. Nothing covered the stages never being reached. A wrong exemplar made remap_hseq raise after the carve had rewritten config/splat.<ov>.yaml + overlays.mk and jr_isolate had created a region file — the exception propagated out, the revert never ran, and the tree was left with:

  • a rewritten carve config (recoverable by git checkout), and
  • an untracked region file, which git checkout -- src/ does not remove.

In a 132-member sweep that residue rides silently into the next member's build. Found by being bitten by it while testing something else.

The fix is a wrapper, not a bigger try inside: snapshot the region set, call the real body, and on ANY exception revert and return a per-member exception status. Loud (it appears in the tally), non-fatal (the sweep continues), and the tree is provably clean afterwards.

def bank(...):
    keep = region_files(to_ov)
    try:
        return _bank(...)
    except Exception as e:
        revert(to_ov, keep_regions=keep)
        return "exception", repr(e)[:140]

Negative-control proven: the same crashing invocation now reports {'exception': 2} and leaves git status -- config/ src/ at 0.

The law: "revert on failure" is not the same property as "revert on every exit". Enumerate the exits — success, gate-fail, refusal, and the throw — and put the restore where all four pass through it. Same family as §97 (the gate's own tree hygiene), and the reason it matters more in a sweep than in a one-off: a one-off's residue is visible in the next git status; a sweep's residue is consumed by the next iteration first.