Huginn/docs/eureka-substrate-postmortem.md

100 lines
5.4 KiB
Markdown
Raw Normal View History

# eureka-substrate postmortem (cuts 2026-10-01)
The campaign fixed the mind's correctness core that the first typed Eureka
campaign (idunn-watchdog) tripped over: keys too long to resolve, key errors
reported as outages, report heads compared by spelling, findings that could
not defer to a cut, an unchecked `depends_on`, a stewardship ritual nobody
needed, and a faculty still named `MindSteward`. Target:
`docs/eureka-substrate-target.md`; map: `docs/eureka-substrate-map.md`. The
campaign stays open as the standing home of substrate gaps (ruling `gap-home`).
## What landed
| Cut | Repo | Merge / commit | Soul passes |
|---|---|---|---|
| merge-epiphany-main (0a) | Epiphany | `2bf39eef` | 1, holds |
| merge-huginn-main (0b) | Huginn | `922e6194` | 1, holds |
| leaf-key-bound | Epiphany | `ef956685` | 1, 2 Lows deferred |
| huginn-pin-and-refusals | Huginn | `b85cd5d7` | 2 |
| huginn-admission-rules | Huginn | `107552dc` | 2 |
| ops-mind-upgrade | gamecult-ops | `1f4cb12e`, `0dd1a1a3` | 1 |
| skill-recipes | Eureka | `e7567333`, `243182cc` | 2 |
Live: Huginn `107552dc` serves `eureka` on Yggdrasil since 12:00:59 UTC
(14:00:59 CEST). Before and after the flip, 346 documents and 108 receipts.
The read-back test on a scratch copy of the cold copy validated and re-keyed
all 346 documents and withdrew 45 long-keyed resolutions. The first admission
with a local key over 64 bytes went in at 12:15:10 UTC, so rollback is now
restoring `/var/backups/huginn-upgrade/107552dc…/mind.redb`, not a flip.
Closed with it: six substrate gaps (`gap-unresolvable-finding-key`,
`gap-sha-identity-literal`, `gap-defer-finding-to-cut`,
`gap-depends-on-pins-revision`, `gap-stewardship-ceremony`,
`gap-faculty-enum-name`), and the seven idunn-watchdog findings that the key
bound had left stuck in force (three Fixed, four Deferred to the follow-ups
already carrying them).
## Soul's ledger
10 verdicts, 14 findings: 8 Fixed, 3 Deferred (to `leaf-bound-hygiene` and
`leaf-typed-prepare`), 3 Recorded. No Blocker or High. Every finding was in
the tests or the prose, none in shipped behaviour, which says the cuts were
small enough for Hands to get right and the suites were not yet strict enough
to prove it:
- The read-back test first proved one withdrawal and skipped refusals
(`readback-tolerates-refusal`). Rebuilt, it censused the whole mind and
became the deploy's gate. That gate is the most valuable artefact of the
campaign.
- Exact-match rules were pinned with fixtures that cannot tell exact from
prefix (`dep-near-miss`) or case (`dep-case`): labels `1, 2, 3, 9`. A
fixture for an equality rule needs near misses; digits alone are degenerate.
- `cargo mutants` scored 8/8 on admission-rules but mutates `check()` and
`matrix()` whole, so the score defended no individual rule. Soul's hand
mutants that were functions of the input (prefix in one direction, length
compare, case fold) found what the tool could not.
- The docs pass left a recipe steering Self to the stewardship step the
operator had dropped (`stale-steward-recipe`). Deleting a rule from code does
not delete the prose that teaches it; the docs cut needs its own grep for
every consumer of the dropped step, not only its name.
## What the campaign itself proved about the substrate
- **The bug bit its own fix.** Self's first attempt to close two findings of
the pin cut was refused `InvalidFormat` wrapped as `Unavailable`, the very
defect the cut was fixing. Admitting a resolution had to wait for the deploy.
Any campaign that fixes the mind's admission runs on the unfixed mind until
the upgrade; plan resolutions after it.
- **A finding cannot be `Answered`.** Closing the self-dependency fork by its
ruling was refused `IncompatibleResolution`; it was closed `Fixed` by the
commit that implemented the ruling.
- **Old clients survived the rename.** The pre-upgrade `eureka-state` decoded
every response used afterwards, including receipts carrying `Life`, because
the receipt digest excludes provenance. Not every kind was exercised through
the old client.
- **The cold-copy-then-read-back pattern worked as designed**: build, record
counts, stop, copy, prove on a scratch copy, flip, recount, view every
document. Total downtime was about three minutes.
## What went wrong in the running of it
- A merge was run with `git merge -F -` (stdin), which git rejected; the
following push was a no-op and the worktree was removed anyway. Nothing was
lost because the branch was pushed, but the chain should have stopped on the
failed merge. Write merge messages to a file.
- The admission-rules spec's line anchors were taken at `133324a7`; Hands
branched from `b85cd5d7` and re-anchored by content. `base` cannot say
"main after X merges" (`gap-depends-on-pins-revision`, second half, still
open as prose in `first`).
- Self decided the self-dependency fork as a Defaulted ruling on proportion
rather than asking. The operator may overturn `eureka-substrate:ruling:self-dep`.
## Open
- Fresh-session check: the Rehydrate block and recipes in a new session, which
also loads the swapped client and its `Life` faculty.
- 15 substrate gaps in force under `eureka-substrate` (verify stopgap,
landing records, faculty attribution, mutation triage field, and others).
- Leftovers to clean: `~/huginn-readback-107552dc…` and
`~/huginn-build/107552dc…` on Yggdrasil; the client clone in the session
scratchpad. Keep the cold copy until the operator retires it.