Epiphany's stake in the campaign closed with the leaf owning the schemas and nothing else. The record keeps the scars: a store deleted whole after the operator rejected it, a grammar redesigned on its third patch, a fix that introduced a replay regression, a harness that ate the work it was testing, and the five corrections the operator had to make to the agents' model. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
225 lines
11 KiB
Markdown
225 lines
11 KiB
Markdown
# Eureka pipeline state, schema-ownership phase: postmortem
|
||
|
||
Date: 2026-09-16. Written at the phase boundary, not at the end of the
|
||
campaign. Cuts 1-6d closed Epiphany's stake in the work; Cuts 7-16 build
|
||
Huginn and the `eureka-state` client and are still running. Evidence: the cut
|
||
map `notes/eureka-pipeline-state-cut.md`, the branch
|
||
`codex/eureka-pipeline-state`, and the Soul reports recorded in the map's
|
||
Landed section.
|
||
|
||
## Summary
|
||
|
||
Epiphany now owns the schemas for Eureka's pipeline state, in one leaf
|
||
library that a separate organ can depend on without dragging the runtime in
|
||
with it. `epiphany-pipeline` is 2,444 lines with its tests, 34 of them, and
|
||
publishes 13 derived schemas. It has no dependency on `epiphany-core`; the
|
||
dependency runs the other way. The public surface is five doors:
|
||
registration, prepare, decode, write-envelope validation, and reference
|
||
validation.
|
||
|
||
The phase landed across 113 commits in three days. At writing, the leaf is
|
||
pinned by Huginn at `5cda0886` and nothing in Epiphany blocks the organ.
|
||
|
||
## Scope and invariants
|
||
|
||
The target was schema ownership, and only that. The organ, the index, the
|
||
CultNet surface and the MCP client were all excluded from this repo by
|
||
design, because the previous shape put a store, a lease and git preconditions
|
||
inside Epiphany and the operator rejected it mid-campaign.
|
||
|
||
The invariants that had to survive: one owner per decision, a key grammar in
|
||
which no reader can excuse a document it does not like, schemas that derive
|
||
byte for byte from the types rather than being maintained beside them, and a
|
||
leaf that a second repo can consume without inheriting the runtime.
|
||
|
||
The earlier attempt failed by bundling. The repo-owned store design is kept
|
||
in the map under History, clearly marked dead, rather than deleted, because
|
||
the reason it died is the reason Cut 4 exists.
|
||
|
||
## Timeline
|
||
|
||
| Cut | What it owned | Outcome |
|
||
|---|---|---|
|
||
| 1-2 | Re-pin CultLib, collapse to one commit owner | Landed, Soul-closed |
|
||
| 3a | Ten document kinds and schemas, plus store, lease, git gates | Landed, then half of it deleted by Cut 4 |
|
||
| 4 | Delete the repo-owned store whole | 1,302 lines removed, 549 added |
|
||
| 5 | Collapse the store profile back out | Landed |
|
||
| 6 | Extract `epiphany-pipeline` as a leaf | Landed after two fix batches |
|
||
| 6b | The key grammar | Three fix batches; the grammar was redesigned rather than patched a fourth time |
|
||
| 6c | The Ghostlight shapes | Landed, one fix batch plus two harness repairs |
|
||
| 6d | Resolution history and stewardship by sequence | Landed both sides; the Huginn follow-up needed its own fix batch |
|
||
| follow-up | The public reference validator | Landed both sides, `5cda0886` and `5ba7b0c6` |
|
||
|
||
## Structural delta
|
||
|
||
Cut 4 is the honest headline: it deleted 1,302 lines and added 549, and
|
||
retired one store format, one lease and two git preconditions. Cut 6 moved
|
||
the types out of `epiphany-core` rather than copying them, so the extraction
|
||
was a move and not a fork.
|
||
|
||
The estimates were wrong in a consistent direction. Cut 4's test arithmetic
|
||
was off because the deleted module held 20 tests on Windows rather than 15,
|
||
and Cut 6's `epiphany-core` estimate of −623 should have read about −1,060
|
||
once the test module travelled with the code. Both corrections are recorded
|
||
in the map against the sections they falsify. Cut 6d's test delta was +181,
|
||
not the +173 claimed.
|
||
|
||
Nothing was parked in this phase.
|
||
|
||
## What Soul caught
|
||
|
||
Green tests passed all of the following.
|
||
|
||
**Split and mislocated authority.** The epoch guard had no test that could
|
||
even construct an envelope to reach it, so an `if false` mutation survived.
|
||
Self ruled deletion over testing what nothing can construct, and Cut 8 wrote
|
||
it in the organ where admission can reach it. Separately, the write path was
|
||
believed live and was in fact test-only; `decode` and the CultCache imports
|
||
looked live only because dead code mentioned them.
|
||
|
||
**A rule open one level up, three times.** The key grammar was patched twice
|
||
and the same invariant came back both times. The third occurrence was the
|
||
signal to redesign rather than patch. Soul then found no collision in 212,450
|
||
adversarial keys against the new grammar.
|
||
|
||
**Readers excusing what writers refuse.** The reader accepted a trailing dot
|
||
on the root and the local while the writer refused it, so a reference with
|
||
trailing junk passed the reader against its own documentation. A mutant
|
||
excusing the `Instance` root from the kind check survived: exactly the shape
|
||
Cut 6b existed to kill.
|
||
|
||
**Tests pinning spelling instead of rules.** A depth test restated the
|
||
literal 64 rather than referencing the bound, so moving both to 60 passed it.
|
||
Fixtures that used the sequence 1 at every nesting level left a nested
|
||
resolution's own sequence unpinned.
|
||
|
||
**A regression introduced by an earlier fix.** An exact replay of any batch
|
||
carrying a derived write was refused rather than recognised as already
|
||
admitted, because the derivation recomputed a sequence over an image that
|
||
already held the first admission's record. The replay test had used only
|
||
batches without derivations.
|
||
|
||
**Ordering by key rather than by meaning.** The hand-off replay picked its
|
||
subject by key order, so from the eleventh transfer on, where `n11` sorts
|
||
before `n2`, it derived a withdrawal of the wrong record. Ten round trips
|
||
alone did not expose it.
|
||
|
||
**Dead code with a test that could not reach it.** The resolution-base arm of
|
||
the reinstatement rule was unreachable because the depth cap fired first.
|
||
Deleting it was the point: a rule defended by a test that cannot run is the
|
||
failure mode of subtraction, not a defence against it.
|
||
|
||
## Operator corrections
|
||
|
||
**The repo-owned store.** The operator rejected it mid-campaign, and Cut 4
|
||
deleted it whole rather than demoting it. This is the single largest
|
||
correction of the phase and the reason the leaf is clean.
|
||
|
||
**A hand-off is a transfer, not a lease.** Self had modelled every stewardship
|
||
hand-off as a loan that returns. The operator's example was Odin holding a
|
||
swarm of infrastructure tools and spinning off a new steward when the
|
||
workload justifies it, with no return. The missing context was that Self had
|
||
only ever seen the cross-cutting case, where borrowing is the natural reading.
|
||
|
||
**Withdrawn resolutions are kept.** Self's default threw the record away.
|
||
The operator's model was a subject as a question with one canonical answer
|
||
and an affordance for withdrawal, where the withdrawn answer is context to
|
||
learn from. The operator also named the assumption the ruling rests on: that
|
||
the context actually reaches the agents. Self had landed the opposite default
|
||
and reverted it.
|
||
|
||
**Single-writer semantics are CultCache's.** Self attributed them to redb.
|
||
redb is the storage engine underneath; the owned store lock and the
|
||
compare-and-swap batch are CultCache's, and were built in this same session.
|
||
|
||
**Stop spending the scarce model on Hands.** The default model inherits the
|
||
root's, so every dispatch had been running on Fable without anyone choosing
|
||
it. The rule now is to name the model on every dispatch.
|
||
|
||
## Incidents
|
||
|
||
**Cargo through the Bash tool.** `CARGO_TARGET_DIR=C:\...` has its
|
||
backslashes collapsed, and cargo starts a from-scratch build into a path that
|
||
does not exist. Nothing landed in the shared directory. Cargo on this host
|
||
runs through PowerShell only.
|
||
|
||
**A scoped `cargo clean` against the brief.** Hands freed and partly retook
|
||
about 400 MB of the shared target directory. Soul could not establish that
|
||
another crate's artifacts were evicted. The next brief forbids it by name.
|
||
|
||
**Agents ending their turn mid-job, three times.** The briefs now name the
|
||
mechanism rather than the intent: a wait is a foreground poll loop inside one
|
||
tool call. One agent left detached timers that re-fired notifications for an
|
||
hour, so the briefs also forbid background monitors that outlive the run.
|
||
|
||
**The harness ate fixes.** An early mutation runner restored from a sidecar
|
||
that predated the fix it was testing, reverting real work. A later version
|
||
committed a NUL byte and a literal control character into a target. The
|
||
current harness reads the original as bytes, writes the sidecar before any
|
||
write, restores hash-first, attempts every target, and prints the original
|
||
digest before rethrowing. A tool-enforced timeout was the exact path that
|
||
left a tree mutated, so the harness now owns its own timeout and kills the
|
||
process tree inside its own cleanup.
|
||
|
||
**Map splices landing at the wrong heading depth.** A specification written
|
||
in a scratchpad began one level deeper than the map's section, and the fold
|
||
put every heading under it a level too deep.
|
||
|
||
**My own errors in the map**, each corrected in place: a follow-up number
|
||
used twice, an ungreppable regex, seventeen entries described as nineteen, a
|
||
zip digest labelled as the DLL's, a claim that CI was green throughout, an
|
||
overstated scar, and one commit attributed to its neighbour.
|
||
|
||
## What worked
|
||
|
||
**Mutation suites as the unit of proof, not test counts.** Every operator
|
||
ruling and every new rule gets a test that fails under its own mutation, and
|
||
the suite is committed with a no-op control that proves the harness restores
|
||
bytes exactly. The controls caught the harness eating fixes; test counts
|
||
never would have.
|
||
|
||
**A revert mutation and a loosening mutation per rule.** A plain revert is
|
||
too easy a target. The loosening is what found the reader excusing a root
|
||
kind and the unpinned nested sequence. When a loosening turns out to be
|
||
unfalsifiable by construction, it is demoted to a recorded note with the
|
||
reason rather than left in the suite implying coverage it does not have.
|
||
|
||
**Redesigning on the third occurrence.** The key grammar came back twice
|
||
under patches. The rule that emerged is to treat the third appearance of an
|
||
invariant as evidence about the design, not about the instance.
|
||
|
||
**Keeping dead designs marked rather than deleted.** The repo-owned store
|
||
sections carry banners naming their false sentences. A reader who finds the
|
||
old model finds it labelled.
|
||
|
||
**Not capping the Soul loop on a foundation cut.** Cuts 6 and 6b each took
|
||
four passes and the later passes still found real defects.
|
||
|
||
## What to change in the pipeline
|
||
|
||
All four of these were approved by the operator on 2026-09-16 after a cost
|
||
review that put roughly half the overhead on process waste and roughly half
|
||
on the inescapable cost of a second agent.
|
||
|
||
1. **A loosening mutation per rule, alongside the revert.** Landed in the
|
||
Hands brief.
|
||
2. **One harness in the Eureka repository.** Owed. The PowerShell harness
|
||
still lives in `F:\Projects\Epiphany\tools\` and Huginn reaches across for
|
||
it; CultLib's JavaScript runner needs the same contract by name.
|
||
3. **Reports are findings, promises and numbers only, and a second Soul pass
|
||
is scoped to the fix batch diff.** Landed in both briefs.
|
||
4. **A map commit per cut, rulings excepted.** In effect.
|
||
|
||
## Open follow-ups
|
||
|
||
- **Cut 9's spec text calls the `V10L` loosening failable.** It was demoted
|
||
when the reference validator made it unfalsifiable. Stale text in the map's
|
||
Cut 9 section, harmless to the code.
|
||
- **FU-9: CultLib's lock failure is text-matched.** `cultcache-rs` needs a
|
||
typed error. Owner is CultLib, and it can wait because the only consumer
|
||
matching the text is a test.
|
||
- **FU-1 through FU-8** stand as recorded in the map, each with its file and
|
||
its reason.
|
||
- **Cut 11 must index withdrawn resolutions with their reasons.** This is the
|
||
second obligation the operator's Q17 ruling created; the first, a history
|
||
view, landed in Cut 9.
|