Wiki · Evidence & Verdicts
Phase 4 — the green run, two canaries firing, and one limit that stays open
How to read this page
Three ways to read this page. Precise is the document itself, exactly as it is written in the repository. Plain and Clear were written for this website to help you meet that document — they are about it. They are not it, and they are not evidence.
Eighty-seven dated pages: receipts, pre-registrations, handoffs, validation records and review verdicts. A receipt is written at the moment a piece of work was checked. It names what was claimed, the commit and the seed, what was actually run, and the outcome in one of a small set of controlled words. Then it names what the work did not achieve. That last part is what makes it a receipt rather than an announcement. A pre-registration is the same discipline run in advance: the conditions that would count as a pass and the conditions that would falsify the claim are written down before the run, so neither can be adjusted once the numbers arrive.
That is why so many small dated stubs are an audit trail rather than noise. No one of them is meant to be a good read. The value is in the sequence and in the dates, because you can watch a prediction be registered, then the run happen, then the verdict land — sometimes against the prediction. Pages here record a falsified result, a rejected fix, a retracted overclaim, and a green receipt that turned out not to be reproducible from the commit that carried it. A record that carried only successes would be worth a good deal less than this one.
A gentle way in is to read a pre-registration first, so the shape becomes familiar, then a result page, then one of the corrections. This section sits off the main navigation on purpose: it is the record you check the rest of the site against, not the place to begin.
What it is not: documentation, and not a summary. Nothing here has been tidied in hindsight. Every entry reads as of its date, a later entry may overturn an earlier one, and the presence of a page is not a claim that its result stood.
Your browser cannot switch reading levels, so the document itself is shown.
Precise — the source document
This is the document. Rendered from the repository at the commit above, with nothing rewritten for the web. A gate re-renders it on every deploy and fails the build if a single byte differs.
Date: 2026-07-26 · Repo: UNI.Minecraft, gen2-runtime · Red: f9c5167
Plan: docs/control-plane/phases/PHASE-4.md (UNI-FLAGELLUM)
Raw: phase4_red_2026-07-26.txt · phase4_green_2026-07-26.txt
$ mix test test/sp/control_plane
211 tests, 82 failures # red, f9c5167
211 tests, 0 failures # green
Test count identical across red and green. Full suite 765 tests, 0 failures (was 681).
1. The gap that mattered most is closed: the Control Plane can record its own writes
SP.ControlPlane.Store — durable, append-only, two plain files a human can open:
<dir>/ledger.ndjson one canonical entry per line
<dir>/anchor.json the head and length, held outside the chain
Append-only is enforced before the write, not detected after. persist/2
refuses unless the bytes already on disk are an exact prefix of the ledger being
persisted, and a refused write writes nothing at all. A shorter ledger, a
divergent history, or a file that grew behind its back all refuse — and the
refusal names the seq where the histories part company.
The premise this phase rested on was checked before anything was built on it,
as the plan required: File, :crypto and stdlib JSON carry durable
persistence in the zero-dep app. git diff mix.exs is empty. No
STOP_PROTOCOL_CHANGE_REQUIRED.
2. BOTH CANARIES FIRED, exactly as they were written to
Phase 3 left two tests whose job was to fail the moment persistence landed. They did. Neither was deleted.
| canary | written in | what happened |
|---|---|---|
anchor_detects_truncation_test — "STATED LIMIT: nothing persists an anchor yet" |
Phase 3, item 3.6 | FIRED. Replaced by the assertion it pointed at: the limit is lifted, Store.attest/1 exists, and the in-practice behaviour is proven in store_anchor_in_practice_test.exs. The residual stays asserted separately. |
read_never_actuates_test — "no Phase 2 module performs disk IO" |
Phase 2, F11 | FIRED. Narrowed from a blanket to an allowlist — see §3. |
Deleting a canary that fires is how a limit quietly stops being tracked. Both were replaced with what they were guarding, and both replacements were mutation-tested.
3. A guard was deliberately WEAKENED, and here is the exact trade
read_never_actuates_test asserted that no module in the namespace performs
disk IO. That was always a proxy for the real rule — a read never actuates
— and it was only true while nothing persisted anything.
- Weaker: one module may now touch disk.
- Stronger: it is an allowlist of exactly one. A second writer, or a writer appearing inside a module that reads, now fails — which the blanket form could never distinguish.
- Unchanged: the purity of every read is asserted directly, function by function, and never depended on this scan.
Mutation-tested: appending File.write to pair.ex fails both this test and the
store's own allowlist. Reverted; diff empty.
4. ADVERSE — two of my own tests contradicted each other
run_status_refusals_test asserted the status vocabulary was five words.
run_failure_refusals_test asserted :FAILED_RUN is in the vocabulary, "so
it cannot be a surprise value nothing renders".
Both were mine, written an hour apart, and they cannot both hold. The failure test is right — a status the vocabulary does not admit is exactly the kind of value that gets rendered as blank or dropped. The status test was corrected to six, on the merits, not by loosening whichever assertion was easier to move.
5. ADVERSE — two real defects in Store, caught by my own new tests
store.ex's moduledoc namedevidence/gates.ndjson. The Phase 4 test "no Control Plane module names the canonical gate ledger" failed on the very module it was written to watch. A module that names the canonical evidence file is one edit away from writing to it. The reference is now indirect, and the moduledoc says why.- An empty store did not create its ledger file, so
load/1reportednot_a_storefor a store that had been correctly initialised. An initialised store with no entries is a real state, not an absent one.
6. A FOURTH pre-registered phrase was imprecise, corrected before it was built on
Item 4.3 said "the same run twice produces byte-identical canonical bytes". Taken literally that is false and must stay false: a run record carries wall-clock start and end, and two executions happen at different moments. A record that hid that would be lying.
Split into two things that are now tested separately:
- identity — code, env, inputs, params, seeds,
planned_n,stopping_rule, hashed intorun_id. Two runs of the same thing share it. - record — times, exit code, outputs,
actual_n. These differ, and must.
planned_n and stopping_rule are inside the identity on purpose.
CLAUDE.md: "never increase replicates after seeing a width." If either were a
free field, a short run could be relabelled COMPLETE, or a stopping rule
declared once the numbers were in. Because both are hashed, doing either
changes what run this is — the laundering leaves a mark. That is asserted.
7. Item 3.6: upgraded, with the residual asserted rather than footnoted
What is now true: the anchor persists beside the ledger, so a reload that has lost its tail fails to attest. Truncation is caught in practice, across restarts, against loss, corruption and accident. Losing a single entry is caught. A stale anchor no longer attests a grown chain. An absent anchor is a refusal, never a pass.
What is still not true: it does not stop a tamperer with write access to
the store directory, who truncates ledger.ndjson and rewrites anchor.json to
match. Nothing local can. It needs an anchor the ledger's writer cannot reach.
store_anchor_in_practice_test.exs performs that attack and asserts it
succeeds, so the limit cannot quietly stop being true. It is PHASE-5.md's
first item.
8. Item 4.7: the rollback lesson is now mechanical
The mixed-EOL trap that cost a rolled-back write on 2026-07-25 is a test:
appender_takes_last_line_terminator_test.exs. It asserts the terminator comes
from the last line in both directions, that appending n rows adds exactly
n lines, that no blank line appears, and that the file ends with exactly one
newline. A final test reads the canonical ledger live and asserts it really
is mixed — 58 CRLF among 206 lines, ending on the minority terminator — so the
premise is checked, not remembered.
9. What was built
| module | file | what |
|---|---|---|
SP.ControlPlane.Store |
store.ex |
durable append-only persistence; the only module that touches disk |
SP.ControlPlane.Run |
run.ex |
immutable run identity, six statuses, three refusals from real defects |
SP.ControlPlane.Pair |
pair.ex |
exactly one differing variable, or VOID |
SP.ControlPlane.Ledger |
amended | from_entries/1 — reconstruction, named as the trust boundary it is |
Failure modes covered: F12–F18, on top of F1–F11 and F23.
Run's three refusals come from defects that are still live in the flagellum:
fit.py storing res.success and never reading it; score.py's bare zip
truncating silently; and a crash being recordable as a scientific negative.
aggregate/2 additionally refuses a repeated unit id, because frames are not
independent replicates.
10. Verification
| command | result |
|---|---|
mix test |
PASS — 765 tests, 4 doctests, 0 failures (was 681) |
mix test test/sp/control_plane |
PASS — 211 tests, 0 failures (82 red at f9c5167) |
mix compile --warnings-as-errors --force |
PASS — 127 files |
mix format --check-formatted — Control Plane files |
PASS |
mix format --check-formatted — repo-wide |
FAIL, standing known-fail (lib/sp/brain/language.ex, PHASE-3-RESULTS §3) |
git diff mix.exs |
empty — no hex dependency |
evidence/gates.ndjson sha256 |
964ea25c…1d8a4c44 unchanged — this phase wrote no row |
node viewer/gaia/verify_gaia.cjs |
PASS — 12 checks, 0 FAIL |
node viewer/gaia/gaia_lint.cjs |
PASS — 0 violations |
test/sp/brain/mc_test.exs |
untouched |
11. What this phase did NOT do
No row written to the canonical ledger. No rooms, airlocks or keys. No Gaia seat.
No lab view. No Phoenix. No ui/ change. No P-level moved —
P8 = FULL_PARITY = false, first unsatisfied rung P4.
nursery-fenced-red-stocked remains FAIL, falsified 2026-07-19.
No verdict has been authored about any real scientific claim.
Rollback: delete lib/sp/control_plane/{store,run,pair}.ex and the eight new
test files, and revert Ledger.from_entries/1. The two canary transitions should
not be rolled back — they record that a limit moved.
sha256 e563687ec6a5ae82 — of the original file, so what was ingested stays checkable.
Plain — written for this website, not the source document
A green run whose most interesting parts are the ones that fired, broke, or stayed open. Two tests written earlier specifically to fail once this work landed did fail, and neither was deleted; each was replaced by the thing it had been guarding. One guard was deliberately weakened, and the exact trade is written out rather than glossed over. Two of the author's own tests contradicted each other, and one was corrected on the merits rather than by moving whichever assertion would be easier to move. And one limit stays open, with a test that performs the attack and asserts it succeeds. It closes on a flat statement: no verdict has been authored about a real scientific claim.
Plain · written 2026-08-01 by claude-opus-5 · not yet checked by a person · about the document whose sha256 is e563687ec6a5ae82
Clear — written for this website, not the source document
A green receipt — the file recording what was run — with the counts before and after, and a note that the test count is identical across red and green, which is what makes the comparison mean anything.
The gap that mattered most is closed: the system can now record its own writes durably, in two plain files a person can open. The key property is that append-only is enforced before the write rather than detected after it, so a refused write writes nothing at all, and the refusal names the exact point where the two histories part company. The premise that this was possible without adding a dependency was checked before anything was built on it.
Two canaries fired exactly as they were written to. One had said in its own text that nothing yet persisted an anchor. The other had asserted that no module in the namespace touched disk. Both were replaced with what they were guarding rather than deleted, and the reason is given in one line: deleting a canary that fires is how a limit quietly stops being tracked.
One guard was deliberately weakened, and the trade is written out in three parts. What is weaker: one module may now touch disk. What is stronger: it is an allowlist of exactly one, so a second writer, or a writer appearing inside a reader, now fails, which the blanket form could never distinguish. What is unchanged: the purity of every read is asserted directly, function by function, and never rested on that scan. It is then mutation-tested and reverted.
Two adverse findings follow. Two of the author's own tests, written an hour apart, contradicted each other about the size of a vocabulary, and the page says which one was right on the merits rather than moving whichever assertion was easier to move. And the new tests caught two real defects in the new module, one of them a moduledoc naming the evidence file that counts, which the page treats as being one edit away from writing to it.
A fourth phrase, written down in advance, is corrected before anything is built on it. Taken literally, a demand that the same run twice produce identical bytes is false and must stay false, because a record carries wall-clock times. So it is split into an identity, which two runs of the same thing share, and a record, which differs and must. The subtle part is which fields sit inside the identity. The planned count and the stopping rule are hashed in. So quietly increasing replicates after seeing an answer, or declaring a stopping rule once the numbers are in, changes what run it is and leaves a mark.
One item is upgraded with its residual asserted rather than footnoted. Truncation is now caught in practice across restarts, and an absent anchor is a refusal rather than a pass. What is still not true is that it stops someone with write access to both files, and nothing local can. A test performs that attack and asserts it succeeds, so the limit cannot quietly stop being true.
An earlier rollback becomes mechanical as a test, including one that reads the master list live and asserts it really does have the awkward property, so the premise is checked rather than remembered. A build table, a verification table carrying one standing known failure, and a list of what the phase did not do close the page, ending on the line that no verdict has been authored about any real scientific claim.
Clear · written 2026-08-01 by claude-opus-5 · not yet checked by a person · about the document whose sha256 is e563687ec6a5ae82