f6-out-of-sample-RESULT.md left three failures unexplained beyond "n=2 wasn't enough". Reviewing what's already decoded: two of the three (the ptcopyright/ parent ramp ratio, the sweep-leads-plate lead) fail in the SAME direction (0.75x, 0.71x of predicted), and f6-unit10 already established the sweep and plate families are gated by separately-triggered parents -- a ratio across two independently-triggered elements has no structural guarantee of being a constant, unlike a ratio internal to one element family (which is exactly what the passing three checks are). The gross mislabeling that caused the ORIGINAL 1.7x conflict was fixed two days before the prereg was written, so that's ruled out as the cause here; whether the cross-group phase genuinely varies boot to boot vs. an artifact in the frame-based ratio math is still open, and needs more captures to tell apart -- filed as a follow-on, not run here. check_labels.py conflated two different claims under one "N LABEL(S) DRIFTED" verdict: identity checks (which element -- clock-free, still 3-for-3 out of sample) and timing checks (a cross-element ratio and a self-consistency curve fit -- 0-for-2 out of sample). Split into two reported groups; only identity gates the exit code now. Tolerances untouched -- widening them to pass f6c would be tuning the check on the case that failed it, the same error class already named twice in this corpus. Verified with synthetic data shaped like the real f6c residue (3/3 identity, 0/2 timing, exit 0) and confirmed the selftest's injected mislabel still fails an identity check (exit 1) -- no capture exists in this fresh container to run it against real logs. The withdrawn sweep->plate lead (0.138-0.141) stays withdrawn. Nothing here reinstates a number.
6.6 KiB
Shaping the F6 out-of-sample residue (issue #9)
f6-out-of-sample-RESULT.md already did the honest thing: three predictions
held, three failed, and the failing three were withdrawn rather than patched.
This page answers what issue #9 asked for — what remains open in my own words,
what instrument would close it, and whether check_labels.py is worth
repairing or withdrawing.
The residue is not three independent failures — it's one, seen three ways
| # | quantity | what it actually measures |
|---|---|---|
| 4 | pulse amplitude vs ptbtn00f.rat's declared curve |
a single element's own curve, content-lag-aligned to itself |
| 5 | ptcopyright ramp / parent (sweep-gate) ramp |
a ratio across two separately-triggered elements |
| 6 | sweep leads ptbtn00f, in sweep-loops |
a cross-element phase offset |
Items 5 and 6 both fail in the same direction (0.75× and 0.71× of predicted) — that is not two coincidental misses, it is one relationship measured two ways. Item 4 is a different animal: it never compares two elements, so it needs its own explanation, below.
Why 5 and 6 plausibly share a cause, and it isn't mislabeling
f6-plate-identity-and-clock-conflict-resolved.md
(2026-09-02) fixed the gross mislabeling — ptbtn00f is the pulsing plate,
x=-0.54 is ptcopyright — two days before the prereg
(8927d2f, 2026-09-04) was written. check_labels.py already encodes the
corrected identities. So "the label is wrong" in the sense that cost us the
original 1.7× conflict is ruled out as the explanation for this residue —
that bug was fixed and stayed fixed in-sample.
What is not ruled out, and is already sitting in the corpus:
f6-unit10-parent-alpha-gates-the-sweep.md
states plainly that the sweep is switched on by its own parent's declared
alpha ramp, "not by anything to do with the plate." The sweep-family clock and
the plate-family clock are declared as separately gated. A ratio between
two independently-triggered elements' timings is only a constant if both
triggers fire in a fixed relative order and interval to each other — which is
an empirical claim about the boot sequence, not something either element's own
.rat declaration can guarantee. Items 5 and 6 are exactly that kind of
cross-group ratio; items 1–3 are not (each stays inside one sweep-family
element or one loop-ratio internal to it), which is the cleanest read I have
for why 1–3 are 3-for-3 and 5–6 are 0-for-2.
I am not asserting this is confirmed — it is the hypothesis the existing
corpus already points at, not a new measurement. The alternative (an
instrument artifact in how check_labels.py derives parent_ramp and
ptcopyright_ramp from raw frame numbers in a differently-shaped capture) is
not excluded either. Both are live.
Item 4 needs its own account
The amplitude-curve fit doesn't compare two elements; it compares
ptbtn00f's own drawn alpha, within its own cycle, against its own disc-read
curve, after a content-based lag search. Its upf (title-units-per-frame)
comes from this same run's pulse_period — so if f6c's automatic
slot-finder measured a noisier pulse_period (fewer clean cycles, a
frame-drop landing differently), the curve fit inherits that error even if the
underlying decoded curve is right. check_labels.py already prints the best
lag and the mean error; it does not yet print the per-run pulse_period
sample count or variance, which is the number that would tell them apart.
What instrument would close it
Not more reasoning from what's already written down — that's what this page
is, and it is the ceiling of what a static review can do. More independent
no-input boots, ⟨capture⟩, with check_labels.py's per-run diagnostics
(lag, sample count, per-cycle period variance) saved alongside the pass/fail
line rather than discarded. Two outcomes distinguish the two live hypotheses:
- items 5–6 cluster into two or more distinct values across runs (e.g. a bimodal split rather than a spread) → points at a real, possibly discrete, boot-order dependency in when each group's trigger fires;
- items 5–6 scatter without structure, while item 4's error tracks
pulse_period's own sample-to-sample noise → points at instrument sensitivity in the frame-based measurement, not a game fact.
Three more runs (total n = 6) is the smallest batch that could show clustering
vs scatter; two would not be enough to tell either from noise, which is the
same lesson f6-out-of-sample-RESULT.md already drew from n = 2. Filed as a
follow-on item rather than run here, since it is new exploratory dynamic RE
and the mission asks for state/approved before starting it, not a
continuation of #9's own scope.
check_labels.py — repair, don't patch the tolerance
Not worth silently loosening. Widening tolerance 4/5 until f6c passes
would be tuning a check on the case that failed it — the exact error class
this whole session has been naming. The tool staying red is more honest than
a quietly-widened green.
Worth repairing its claim, though, because right now a failure reads as "N LABEL(S) DRIFTED" for checks 4–5 exactly as it would for checks 1–3, and those are different claims: 1–3 test which element you're looking at (clock-free, robust, still 3-for-3 including out of sample); 4–5 test whether a cross-element or self-consistency timing number holds, which the analysis above says may not even be a constant. Splitting them lets a future reader — human or agent — tell "the identification is wrong" from "the identification is right and the timing varies" without re-deriving this page.
Applied below: check_labels.py now prints identity checks and timing
checks as two labelled groups, and only identity checks affect the exit code.
Timing-check failures still print in full, still say FAIL, and still return a
nonzero count in the summary line — they are demoted from gating, not
hidden.
Answering issue #9's pass criterion
Concrete follow-on:
- New item (state/proposed, this iteration): "F6 — cluster or scatter?
Three more independent no-input boots, diagnostics saved, to tell a real
cross-group phase dependency from an instrument artifact in the
frame-based ratios." Instrument: ⟨capture⟩ × 3,
check_labels.py(repaired) with diagnostics logged per run. check_labels.pyrepaired in place (this commit) to stop conflating the two claims — no emulator needed for that part, verified by--selfteststill detecting the seeded mislabel.- The sweep→plate separation constant stays withdrawn, as it already was. Nothing here reinstates it.