# FRZ-2 — FRZ-1 said B4 and B5 were "the only" unenforced append-only pre-registrations; they are four. The C2 null-calibration memo and the C4 purge-leak memo declare the same freeze and have none — and both are cited as BINDING by CLAUDE.md and by the current production `purge_days`

status: open · raised 2026-10-21 (implementer, building FRZ-1 — found by the generalisation test that build added, not by reading; FILED not fixed, because cutting a freeze hash is the owner's input) · class: C0, registration hygiene (G5) — a declared freeze with no mechanism behind it · judgement: **YES** · effort ~0.1 pd (the apparatus now exists; this is two hashes and one allowlist entry removed) · horizon: decidable immediately

---

**Plain-language summary for an owner reading one paragraph.** FRZ-1 fixed two
documents that claimed a checksum lock they did not have. Its body states that
those two "are the only self-declared-append-only pre-registrations in
`research/` with no mechanical enforcement." That is false, and the FRZ-1 build
proved it mechanically rather than by argument: the generalisation test it
added — *any* file under `research/` that declares itself append-only must be
named by a hash test — failed on first run and named **two more**, the C2 null
calibration memo and the C4 purge-leak memo. Both open with the words
"**Status: BINDING PRE-REGISTRATION, append-only after today.**" Neither is
hashed. Both are load-bearing right now: `CLAUDE.md` makes C2 the binding
convention for how every PBO and IS-OOS-correlation figure in the shop must be
quoted, and C4 is the memo behind the current production `cpcv.purge_days:
504`. The fix is the one FRZ-1 just built, applied twice more.

## Verified at HEAD `fad1d78` (FRZ-1's base commit)

- `research/2026-08-01_c2_null_calibration_memo.md:3` —
  "**Status: BINDING PRE-REGISTRATION, append-only after today.** Written and
  committed BEFORE any calibration run exists (panel item C2 …). The runner
  (`scripts/run_null_calibration.sh`) refuses to execute unless this file is
  present, and every result JSON records this …" — 213 lines, no hash test.
- `research/2026-08-01_c4_purge_leak_memo.md:3` — the same sentence, same
  structure, for the C4 grid (`scripts/run_c4_purge_grid.sh`) — 181 lines, no
  hash test.
- The hash-frozen set in `tests/test_docs_consistency.py` before FRZ-1 was
  exactly five (December memo, micro-cap battery, A4 DTC memo, VRP v2 spec,
  NORTHSTAR §1–§5); FRZ-1 makes it seven. Neither C2 nor C4 is in it.
- Both are currently carried as RECORDED, not silently passing:
  `test_every_self_declared_append_only_registration_has_a_hash_test` holds
  them in a `RECORDED_UNENFORCED` dict that cites this row by name. Delete the
  two entries and the test reds until the hashes are cut — so this row cannot
  be forgotten, and closing it is mechanically checked.

**Why the stakes are not lower than FRZ-1's.** C2 fixes what "no-skill" looks
like on this harness (its no-skill band, median |corr| 0.443, is the reason a
negative IS-OOS correlation is not standalone evidence of overfitting) and
every PR in the shop is required to quote null percentiles "per
`research/2026-08-01_c2_null_calibration_memo.md`". C4 is the pre-registration
behind a *production config value that was actually changed* — `purge_days`
252 → 504. A quiet edit above either line would rewrite the standard by which
results are read, after the results exist. That is the exact act the freeze
doctrine exists to prevent.

## The fix

Exactly FRZ-1's, applied twice:

1. Two sentinel-hash tests in `tests/test_docs_consistency.py` on the
   December-memo pattern. **Neither memo carries a sentinel phrase today** —
   unlike B4/B5, whose §8 blocks name one — so the owner picks the freeze
   boundary, or a sentinel line is appended to each as a dated append (which
   is itself legitimate under their own declaration) and the hash cut above it.
2. Add both to `DENY_PATTERNS` in `scripts/automerge_guard.py`, beside the two
   FRZ-1 added.
3. Delete the two `RECORDED_UNENFORCED` entries, which reds until (1) lands.

## Test design + negative control

Identical to FRZ-1's, which the owner has already accepted: an in-place edit
above each sentinel must red the gate suite as CI invokes it
(`pytest tests/ -m "not observability and not research"`), and a dated append
below it must still pass. Plus: deleting a `RECORDED_UNENFORCED` entry while
the hash test is absent must red — the allowlist must not be able to go stale
in the silent direction.

## Pre-registered kill criterion

This row dies if the owner rules that C2 and C4 are **not** append-only, in
which case the declaration at `:3` of each is removed by a dated append and
they become ordinary living records. It does **not** die on "git history is
the record" — the December memo's own test docstring rejects that argument,
and FRZ-1 was approved over it.

## Note on provenance and on the claim it corrects

Found while building **FRZ-1** (PR for 2026-10-21). This row is the reason the
FRZ-1 build kept its generalisation test rather than the two hard-coded tests
its approval literally scoped: the hard-coded pair would have left this
undiscovered and the false "only two" claim standing. FRZ-1's body is **not**
edited to fix that claim — an in-place rewrite of an open registration is the
same act FRZ-1 is about. It is corrected here, in a dated row, which is the
discipline.
