Eureka/references/changelog.md
2026-11-02 21:09:43 +01:00

1464 lines
94 KiB
Markdown

# Changelog
Record each change to the skill together with the evidence that motivated it.
## 2026-11-02: inversion of control, originator evidence, persona charter once
Self, session `self-2026-11-02-eureka`.
Evidence. Rulings `eureka-body:ruling:inversion-of-control` (stewards own their domains; the operator is a respected contributor in governance, holding only her own work, the wallet and the legal root), `originator-vision-is-evidence` (whoever conceived a project, not always the operator, holds the best evidence of its purpose, weighed as evidence) and `what-reaches-each-turn-ruled` (a persistent session was the specified design). A brief gated a steward's merge on the operator's read (PR 41, "Does it, now?").
What changed. `SKILL.md` step 2 and the faculties table, and the Prime Directive line in `doctrine/CLAUDE.md`, no longer say the operator rules on what a project is for; `doctrine/persona.md` says the charter is carried once per session process; `references/briefs.md` says a spec or brief never gates a steward's merge.
## 2026-10-27: re-entry triage, second-beating trigger, pushed heads build
Self, sessions `self-2026-10-27-eureka` and `self-2026-10-27b-eureka`.
Evidence. A recovery Soul pass found High defects in two cuts whose reports had sat days with no verdict (Bifrost OIDC stale client, Heimdall boot-proof pin); re-entry rehydration did not list reports with no verdict. The "same rule beaten again" step fired twice in one session (boot-proof verdict owner; the C2a claimant rule beaten on claimant, compatible ids and shape) and both times only a structural move fixed it; the step's text read as a mutant-only, late trigger. Hands pushed a non-building commit in four consecutive batches although the brief said "Say in the message which commits don't build", which permits it.
What changed. `SKILL.md` re-entry adds reports with no verdict and an origin check before trusting a handoff's status words; step 5 says the trigger is the second beating, by mutants or findings. `references/briefs.md` Hands commit rules: build and run the touched test project before every push; pushed heads build.
## 2026-10-23: worktrees die with their cut
Self, session `self-2026-10-23-ag8`.
Evidence. `C:\aeth-wt` held 255 GB, mostly 1.7-3.9 GB Unity worktrees from sessions ag3 to ag7. The ag7 handoff listed them as "worktrees to remove when idle". C: was at 1.4 GB free while the operator flew, and a new worktree checkout failed mid-write. A two-pass sweep removed about 140 worktrees and directories. Operator: "you really gotta start cleaning up after yourself".
What changed. `SKILL.md` git and tooling rules: Self removes a cut's worktrees in the turn the cut merges or is abandoned, parks unreferenced heads under `keep/` tags first, and checks free space before creating a worktree.
## 2026-10-14: nothing that exists is sacred, her words included
Self, session `self-2026-10-14-ag`.
Evidence. aetheria-fauna: four animals the operator named while thinking aloud became the campaign title, the target and every brief, and the lore pass (branch `eureka/aetheria-fauna-lore`) wrote exactly those four and built on her lore remarks as finished canon. Operator, 2026-10-14: "the agent again made the classic mistake of assuming my suggestions are gospel and my ideas complete"; "it's not just my words that aren't sacred, it's the project's existing content and code". Eureka's curb on deference worked at forks only; admitting remarks as rulings and relaying words verbatim hardened them, and the rapport memory saying she thinks aloud reached no worker. Ruling `aetheria-fauna:ruling:fauna-bestiary-open`.
What changed. Doctrine's failure-shape bullet on deference to existing code now covers content, canon, data and her own words, with the authorship-boundary exception. The Self brief section: a ruling says whether her words decide, illustrate or sketch; campaign titles and targets name the problem, not her examples; creative and content passes are briefed to develop and challenge and report what they added and would change. `SKILL.md` step 2 points there.
## 2026-10-14: a worktree's branch is the spec's branch
Self, session `self-2026-10-14-ag`.
Evidence. `aetheria-release:cut_report:cut-sensor-catalog.h4` names branch `eureka/aetheria-release-sensor-catalog`, the spec's; the worktree Self cut was on `eureka/aetheria-release-sensor-catalog-r2`, the only ref origin has. The Soul s3 brief had to carry the correction. Lesson recorded in the 2026-10-13b handoff, found in no owner by Life on re-entry.
What changed. `SKILL.md`, Self's worktree bullet: the worktree's branch is the spec's `branch` field, spelled exactly.
## 2026-10-12: no test run of any size on Starfire
Self, session `self-2026-10-12-eureka`.
Evidence. `cultlib-gaps:cut_report:cut-py-msgpack-default.h1`: Hands ran the cultmesh-py and cultnet-py suites on Starfire as well as on Yggdrasil; both were red for Windows-only reasons and cost workstation load. The brief said heavy verification goes to Yggdrasil, and Hands read a test suite as not heavy.
What changed. The Hands brief's verification list says no test run of any size runs on Starfire unless the brief names it as Windows-only.
## 2026-10-12: map only what is about to run
Self, session `self-2026-10-12-eureka`.
Evidence. 26 cut specs in force across nine Eureka-area campaigns had no report, and almost none was blocked: their dependencies had landed. Imagination had mapped each campaign's whole cut chain at once (step 1 said "admit the cuts ... ordered by depends_on"), while each cut took up to seven Soul rounds through one Self. The variants parity specs of 2026-10-02 went ten days stale behind CultLib and need a fresh Imagination pass. Operator, 2026-10-12: "Absolutely it should be a rule. Originally a spec would get dispatched as soon as it's written, but we've been drifting way ahead of the work for some reason."
What changed. `SKILL.md` step 1 admits a spec only for the cut Self is about to dispatch (plus a parallel sibling); later cuts are lines in the prose map's new cut sequence (`references/campaign-state.md`). The Imagination brief in `references/briefs.md` maps the next cut only, and a budget split admits its first part. Specs already in force are refreshed or withdrawn when their campaign's frontier reaches them.
## 2026-10-12: the tool-calling map is dated; two admission notes
Self, session `self-2026-10-12-eureka`.
Evidence. Life's re-entry pass found `references/tool-calling-cut.md` still giving Self's rotation as 300k at five places after the threshold moved to 400k. The previous Self's handoff carried two admission refusals met in use and not yet in the skill (`IdentityCollision` on a hand-admitted Answered resolution; `IncompatibleResolution` on a finding resolved Superseded).
What changed. The map gets a header naming `SKILL.md` as owner of Self's threshold and marking its figures as of 2026-10-01. "Notes from use" in `references/campaign-state.md` gains the two refusals and their remedies.
## 2026-10-12: Self rotates at about 400k
Self, session `self-2026-10-12-eureka`.
Evidence. Operator, 2026-10-12: "I want to raise the Self context threshold by a bit. Sessions are getting cut short, and Self context seems cheaper than any of the worker agents because it doesn't turn over as much".
What changed. The `SKILL.md` rotation bullet moves Self's threshold from about 300k to about 400k and replaces "most expensive in the pipeline" with the actual cost shape: long-lived, mostly cached, re-read on every notification. The worker cap of about 300k is unchanged.
## 2026-10-11: every campaign in force has an area
Self, session `self-2026-10-11-eureka`.
Evidence. Operator, 2026-10-11: "I feel like some campaigns were dropped again." The campaigns recipe listed 17 in force; the eureka and Aetheria/Ghostlight handoffs named 13. variants (5 unreported specs, a High finding), asura, cultmath-tapes and idunn-watchdog had no Self, one of them since 2026-10-03. The 10-10b handoff said asura belonged to the other Self, whose handoff never listed it. Same failure as the scenarios cuts on 2026-10-03.
What changed. The `SKILL.md` rotation bullet requires the campaigns recipe to be reconciled against every current area handoff before a handoff is written, an unnamed campaign in force to be adopted or put to the operator on re-entry, and a hand-over between areas to be named in both handoffs.
## 2026-10-11: Stryker offsets come from the job's checkout
Self, session `self-2026-10-11-eureka`.
Evidence. `cultlib-gaps:verdict:cut-bls-input-field.s3`: Hands computed Stryker `--mutate` offsets from LF content, as the brief said, but CultLib's `packages/cultmath/src/**` is `eol=crlf`, so the job's checkout was CRLF and the ranges missed the code the fix batch was about. At the right offsets Soul's rerun killed 32 of 32. The brief's rule was itself the defect.
What changed. The Hands brief in `references/briefs.md` says offsets index the file as Stryker reads it in the job's checkout, whose line endings follow `.gitattributes`, and that the run must report mutants inside the method meant to be measured.
## 2026-10-09: research is always kept
Self, session `self-2026-10-09-eureka`.
Evidence. An Eyes prior-art pass on multi-persona minds (for `eureka-body:ruling:persona-per-mind`) wrote its facts to the session scratchpad, as the Eyes template said. Self offered to keep it; the operator answered: "Research should always be kept". It was committed as gamecult-ops `docs/research/multi-persona-prior-art.md` (2cb9d04).
What changed. `SKILL.md` step 2 gains "Research is always kept": a prior-art or literature pass is committed to `docs/research/` before the fork is put. The Eyes brief template says so.
## 2026-10-09: handoffs are named per area
Life pass `life/handoff-per-area`, on the previous Self's open small task.
Evidence. Operator, 2026-10-09: "Split the handoff between Eureka and Aetheria/Ghostlight." Two handoffs were written that day, `F:\Projects\HANDOFF-eureka-self-eureka-2026-10-09.md` and `F:\Projects\HANDOFF-eureka-self-aetheria-ghostlight-2026-10-09.md`, one Self per area. The rotation bullet still said to write one handoff "to the scratchpad", which neither file did.
What changed. The `SKILL.md` rotation bullet names a handoff by area (`HANDOFF-eureka-self-<area>-<date>.md`), one per Self area, and says the next Self reads only its own area's.
## 2026-10-08: overlap before fanning out, and a writable pins mount
Self, re-entry pass `self-2026-10-08b`, on proposals from Life.
Evidence. Two parallel Imagination passes over the same code made two owners of one decision twice on 2026-10-08: Title against DisplayName, and the Huginn crate move. Soul s2 on product-titles found NuGet restore failing under the read-only `/pins` mount that `references/briefs.md` prescribed (follow-up `eureka-substrate:follow_up:docker-args-pins-ro-breaks-restore`).
What changed. `SKILL.md` step 1 now says to list the code and decisions each Imagination pass will touch before fanning out, and to run overlapping passes in sequence or give the shared decision one owner. The Aetheria `DOCKER_ARGS` line in `references/briefs.md` mounts `/pins` read-write; `/lfs` and `/repos` stay read-only.
## 2026-10-08: tool-call relief
Self dispatch `hands-toolcall-relief`. Operator, 2026-10-08: "I think Eureka needs some love. We have the forge set up but we're not using it to save tool calls yet. I think we can cut the number by a lot." Then, on the plan: "Dispatch all of those".
Evidence (Eyes, `eureka-toolcall-patterns-2026-10-08.md`, 211 Hands and Soul runs, 25,784 calls): 11% of all calls were waiting. 1,181 were `ygg-verify.sh wait`, 974 idle `true`/`echo waiting` calls, 324 `sleep` calls (49 refused by the harness), 287 process checks and 83 Monitor calls; 198 wait calls timed out. The idle streaks mostly followed waits that `ygg-verify.sh` does not cover: local processes, Unity batchmode, logs. 65 `python` calls hit the Windows Store stub. The 2026-10-07 runs also showed three brief gaps (shared scratch files clobbered, an abbreviated wait protocol, Stryker character offsets) and a Self ruling text that put words in the operator's mouth.
What changed. `tools/wait-on.sh` is one waiter (`pid`, `log` with a done regex, `file`) with `ygg-verify.sh wait`'s contract: at most 270 s, exit 0 with a bounded tail, exit 99 `RUNNING`, exit 98 for a thing gone with no completion evidence; `tools/test-tools.sh` has fixtures for it. `references/briefs.md` sends every non-Yggdrasil wait through it and forbids bare `true`/`echo`/`sleep` polling; names `py -3` as the Python that works from Git Bash and PowerShell; and carries the 2026-10-07 lessons (own scratchpad subdirectory, wait protocol pasted verbatim, Stryker offsets from LF content, Aetheria `DOCKER_ARGS` mounts, rulings quote only the operator). `SKILL.md` records the semantic-merge scar. `agents/life.md` gains the read-only mind tools (`whoami`, `view`, `query`) so Life can rehydrate; it still cannot admit.
## 2026-10-01: dispatches load the faculty charters
Campaign `eureka-substrate`, prose-map step 5 (map `references/faculty-philosophy-cut.md`, sections 5 and 8). Operator go: "go with 2 and 3; we're spending tokens to save tokens."
Evidence: the faculty charters now live as Claude Code agent types (`~/.claude/agents/{soul,hands,imagination,eyes,modeling,life}.md`) and own each faculty's identity, method and boundaries, but no brief named a `subagent_type`, so every dispatch was `general-purpose` and loaded no charter. A Soul pass also found that SKILL.md pointed at "the Soul and Imagination lines of the operational litany in `~/.claude/CLAUDE.md`"; those lines moved into the charters and the pointer was dead.
What changed. `references/briefs.md` requires `subagent_type` with the model on every dispatch, and each brief carries a `Dispatch:` line. Text a charter now owns is cut from the briefs: the `You are <Faculty>` lead-ins, the Soul identity sentence and its "do not edit or commit" and "restore after every mutation" bullets (`soul.md` Boundaries), and the Eyes "Read only" (`eyes.md`). The task-scoped parts stay. SKILL.md says each non-Self faculty is a subagent type of the same name and points at `soul.md` and `imagination.md`.
## 2026-10-01: verify summaries, a wait mode and a kill table
Campaign `eureka-substrate`, cut `tool-calling-s5` (map `references/tool-calling-cut.md`, sections 3 and 5). Operator go: "go with 2 and 3; we're spending tokens to save tokens."
Evidence: Hands spent 3,852 observation calls (poll, read log, monitor, stop) on 1,443 launches, 2.7 per job, and Soul 1,800 on 813, 2.2 per job; eight ad-hoc harnesses (`runmut.sh`, `mutrun.sh`, `strykrun.sh` and others) were reinvented across agents.
What changed. `ygg-verify.sh` gains `SUMMARY=<log>` (full log to a file, bounded summary out: verdict, counts per framework, `ZERO TESTS RAN`, failing tests, failure sections and tail for a red job, and a `MISMATCH` line when the exit is 0 but the log shows failures) and `wait <log>` (blocks at most 270 s, returns the summary once, `RUNNING` exit 99, exit 98 for a launcher that died or never started). `tools/mutation-table.sh` runs a list of hand mutants in one job and returns a kill table with the killing test per mutant; it aborts on a red or zero-test baseline and never counts a build error as a kill. `tools/testparse.sh` is the one parser both read, with fixtures under `tools/fixtures` and `tools/test-tools.sh`. Briefs now name them, add orient-in-one-script and bounded-observation rules for Hands and Soul, call budgets for Imagination (40-80), Eyes (40) and Life (25), the range-dump rule for Soul, one job for build, tests and mutation, and the scratch-path form of the Hands report. Contribution text says Forgejo pull request.
## 2026-10-01: the skill follows the mind's correctness core
Campaign `eureka-substrate`, cut `skill-recipes`, after the Huginn admission-rule and `eureka-state` client cuts (cut `ops-mind-upgrade`). Evidence: a census of the live mind found 6 findings that could not be closed and 39 resolutions that could not be withdrawn, from key locals over the bound.
What changed. Faculty `Life` replaces `MindSteward` (the old spelling is refused in queries; stored receipts still decode). Key, format and bound errors arrive as `Refused(Document(..))`; `Refused(Unavailable)` is a store fault. A subject's local key is at most 64 bytes and a resolution's at most 111. A finding may be `Deferred` to a follow_up or a cut_spec. `depends_on` holds cut labels, checked by admission, and a spec may not depend on its own cut. The per-repo stewardship check is gone (ruling `eureka-substrate:ruling:stewardship-rule`), so Self no longer admits stewardships when opening a campaign. A report's head may be spelled short or full.
## 2026-10-01: waiting costs no wakes, and a shared tools directory
Operator rulings on `references/tool-calling-cut.md`, verbatim: "Q1 A, Q2 yes, Q3 agreed, Q4 B, Q5 yes, but see Epiphany's approach where each subagent gets its own targeted philosophy centered on its role".
Evidence: 602 finish notifications from 245 agents and 1,324 root text-only calls, 596M tokens read, with a 5-minute subagent cache TTL.
What changed. Q1 A amends `92bf6f7`: agents wait in the foreground in calls of at most 270 s and never end a turn before their report, instead of ending the turn on a background waiter. Q3 C: Self rotates at about 300k context or at a phase boundary with a handoff and the operator's `/clear`; agents are capped at about 300k or 150 calls and continued by a fresh agent. Q4 B: shared scripts live in `tools/` with `tools/INDEX.md`, by branch and pull request, Soul before merge for verdict-bearing scripts, pruned when no brief names them. The Yggdrasil verify line in SKILL.md no longer states stale limits (the script sets 5 slots and 6 GiB). Briefs now state that every dispatch names its model. Commit messages go from Git Bash with `git commit -F-`.
## 2026-10-01: the stopgap no longer forces CARGO_TARGET_DIR
In idunn-watchdog's Idunn incident cut, Soul pass s3 ran cargo-mutants twice
on the same two mutants with `--jobs 3`, and the two runs gave opposite
verdicts. The cause: `ygg-verify.sh` passed `-e CARGO_TARGET_DIR=/src/target`
to every container. cargo-mutants gives each worker its own copy of the tree,
but the forced variable points them all at one target directory, so a worker
can test another worker's binary. With the variable unset, `true` was reported
missed and `false` caught, which is the right answer.
Every `--jobs` greater than 1 mutation count reported through the stopgap
before this change is unreliable. That covers both sessions' campaigns. A
survivor may have been reported caught, or a kill reported missed. Treat those
counts as unproven, and rerun a cut's mutation evidence when its rule matters.
What changed: the variable is gone. Cargo's default, `<workspace>/target`, is
already inside `/src`. The scar about not sharing build output between
checkouts still stands, and cargo's default honours it.
## 2026-09-30: Soul pays for each rerun once
The sccache measurement below showed that verify-slot queueing dominates a
pass, not compilation. In idunn-watchdog, Soul passes took 32 to 85 min, most
of it spent re-queueing one job per rerun.
What changed:
- **Soul's brief:** Soul reruns only the tests the cut touches while it
probes, and runs the full suite once at the end, in the same job as its
mutation run where it can. It batches its probes into one job script, and it
runs the mutation tool in parallel, skipping tests recorded as flaky.
- **SKILL.md step 4:** says the same.
## 2026-09-30: the rust verify image caches compiled crates with sccache
The idunn-watchdog campaign's passes were slow. Some examples: a 263-line
Bifrost deletion took 38 min of Hands wall-clock time, and one Soul pass took
85 min. Self suspected cold builds, because every stopgap job gets an empty
`/src/target`. The operator ordered the sccache change.
What changed:
- The rust image installs sccache 0.18.0 and sets `RUSTC_WRAPPER`, a 60G
`SCCACHE_DIR=/sccache` and `CARGO_INCREMENTAL=0` as image ENV. Images without
sccache get no wrapper.
- `ygg-verify.sh` mounts the shared `eureka-sccache` volume on every job.
- sccache keys each entry on its inputs, not on mtime. So the scar that keeps
target directories unshared between checkouts does not apply to it.
Measured on Yggdrasil, 2026-09-30, `cargo test --lib --no-run` on Idunn main:
- Cold (18 of 83 crates already cached): 51 s.
- Warm (83 of 83 hits): 29 s.
- Slot wait before each run: about 2 and 5 minutes.
So the cache saves roughly 20 s per build, which adds up over a mutation run.
But cold builds were not the main cost. Slot contention and test and mutation
runtime are. Look there before adding more build machinery.
## 2026-09-30: substrate gaps are read from the mind, and Imagination owns the model page
The first typed campaign, `idunn-watchdog`, admitted the eight gaps from
`campaign-state.md` as follow-ups, as the Self checklist said. That left the
same eight facts in two places: the typed follow-ups and the page's table. The
table would go stale the day an owner fixed a gap, and the skill forbids
exactly that second copy. It also showed that a follow-up is keyed to its
campaign, so the next campaign's recipe could not see the gaps at all
(`gap-substrate-followups-campaign-rooted`).
In the same run, Imagination's cut pass found that incidents must live in their
own file rather than in Idunn's `control.cc`. That merged two rows of the model
page. The cut-mapping brief allowed Imagination to edit only body facts and
rationale, so it handed the stale page to Self. That was the page it had
written itself in 0b.
The operator also stated that stewardship is universal: "there is only one
Mind, one steward. Leasing stewardship between agents is a deferred cut".
What changed:
- **`campaign-state.md`:** "Substrate gaps" no longer carries a table. It gives
the query for the `gap-*` follow-ups under `idunn-watchdog`, and says where a
new gap is admitted and how a recurring one is recorded.
- **`SKILL.md`:** the two references to that table now point at the mind.
- **Self's checklist:** the first campaign no longer admits the gap list. Every
campaign instead runs the substrate-gaps query. The checklist also records
that stewardship is universal and that the per-repo record only satisfies
admission.
- **The cut-mapping brief:** Imagination edits the model page whenever a probe
changes a row. The faculty table and the same-day sweep line now name the
model page.
## 2026-09-22: the hand-written mutation harness is retired, with no fallback
The operator said: "The manual mutation testing turned out to be a terrible
idea, lots of guarding the shape of the code instead of the behavior. Better
to have nothing than a harness that punishes refactoring." Asked whether a
fallback should remain for code no ecosystem tool reaches, the operator chose
to retire it entirely: "absolutely".
Evidence from the same day, beyond Aetheria's:
- The Huginn, Epiphany-leaf, Idunn and QUIC campaigns spent pass after pass
re-anchoring suites after refactors and rewriting entries that could no
longer fail.
- The harness itself was repaired four times: H1, F1/F2, the case-blind
guard, and Linux quoting.
- The QUIC timeout rule took seven Soul passes of anchor mutants. It closed
only when a behavioural seam made the rule observable by exact equality,
which is the defence that survives a refactor.
What changed:
- `tools/eureka-mutations.ps1` is deleted (Eureka `37c80e4`).
- SKILL.md and briefs.md no longer name a fallback. Where no tool reaches, the
defence is behavioural: observe the rule where it is decided.
- Soul's hand mutants are one-off probes on scratch copies, never committed.
The entry below, "hand mutations target the rule's layer", now governs
exactly those probes.
- The stopgap copies no harness into the container. Its Rust image carries
cargo-mutants instead of pwsh, and the image is tagged by the Dockerfile's
hash so an edit rebuilds it.
- The committed suites in Huginn, the Epiphany leaf, Idunn and CultLib are
deleted in their own repos. The tests those suites pointed at stay.
## 2026-09-23: brief Soul's closing pass for a walk-through
Evidence: Ghostlight's play agent pass ran sixteen Soul passes on invariants and
promises. The seventeenth was briefed differently — not only "did the cut hold"
but "what happens when a person sits down and plays this" — and it returned the
two most useful defects of the pipeline, neither of which any invariant covered:
a card that goes blank for the length of a turn, and a restart that leaves the
player at a dead end whose only exit is submitting an empty message. A closing
pass gets that brief, and it names what the operator should do first.
## 2026-09-23: a stale subtraction ledger is not pressure
Evidence: the same pass met its subtraction budget once, at the cut that deleted
the old drivers, and never revisited it. Sixteen cuts later every fix batch had
been net additive and nobody had looked; the miss surfaced in the postmortem
rather than in a cut. Self reconciles the ledger at each cut's landing, or it
stops being a budget and becomes a sentence in an old document.
## 2026-09-23: name the inference, not the line
Evidence: Ghostlight's play agent pass carried one defect shape through five
instances, each fixed only where it was reported. A committed `dist/` file
existed, therefore the client bridge worked (PA.f176). `git rev-parse` exited
zero, therefore the directory was a checkout — git had walked up to the parent
repo (PA.f193-C). A canonical-form check failed, therefore the row was
unreadable, so a readable save was destroyed (PA.f193-B). A schema id was
unrecognised, therefore the row could not be read, with no decode attempted
(PA.f194-A). Cargo printed `ok`, therefore the fixture was live — the test was
`#[ignore]`d and never ran (PA.f194-B). Each local fact was true and each
inference was false. The last one lived in a runbook: making a silent skip
visible inside cargo's summary moved the same silent green up into the
operator's weekly command, where nothing in the suite could see it.
## 2026-09-22: a test's inputs come from production
Evidence: Ghostlight's play agent 8a-fix3 (`754c7fe`) ruled that the DM agent
dispatches Personas by the id its world view prints. Every test built that id
from a test-local copy of the printer, so changing what the view printed left
all 735 tests green while every dispatch an agent could make was refused.
There were four copies of one spelling, because the library's printer was
private to its module.
## 2026-09-22: hand mutations target the rule's layer
Evidence: in Ghostlight's play agent 8a-fix2 (`74b628d`), Hands reported every
mutation killed, yet Soul found eight rules unguarded. Hands had deleted the
in-memory `record_call` instead of the on-disk persist-before-submit, which
a later persist then hid. It tested `find_handle_collision` directly rather
than its call in `round_tools`, and every test used fresh keys, so
`turn_id = hash(key)` survived. The Hands brief now says to mutate the
production call site and to observe the result where it lands.
## 2026-09-22: mutation testing moves to the ecosystem's tools
Evidence: Aetheria's fire-control campaign carried 23 hand-written harnesses
(5,816 lines) of literal-text anchors and substitutions. They stranded on
every refactor (`72c0109c`, Cuts 9 and 10), and at Cut 10 an anchor kept
matching exactly once while silently re-targeting from `HitProbability` onto
`Inspect`, mutating code the tests no longer ran, and it surfaced only as
unexplained survivors. Soul's audit of the
harnesses also found the tree-clean verdict restoring before it measured, and
a compile failure counted as a kill. The operator: "What is the point of those
mutation anchors? Tests should cover how the code behaves, not how it is
shaped."
A Stryker.NET spike over one file killed every mutant the harnesses defended
and found 67 survivors they did not, among them a flipped sign in the splash
handedness that no test defended: the same class as a wrong-side-damage bug the
operator had once spent hours hunting in legacy code, though the rewrite had not
shipped it. Three rounds of tests and fixture fixes took it to 43, all named.
- The principle of 2026-09-15 stays: every rule has a test that fails when it
breaks.
- The committed-anchor-script mandate is replaced: Hands and Soul run the
ecosystem's tool on the cut's diff and triage survivors by name.
`eureka-mutations.ps1` is demoted to the fallback for code no tool reaches.
- Float boundary flips are equivalent by default (operator: "> to >= on float
thresholds is indeed petty"); the score is not a gate.
- The harness-hardening entries below (H1, F2, the case-blind guard, the
CRLF anchor) remain the record of why the fallback's contract is what it is.
## 2026-09-22: verification moves off the operator's workstation
Evidence: Starfire went from fine to unresponsive in seconds, and the operator
had to force a shutdown. It was carrying four jobs at once:
- 16 `node` CPU burners at twice the CPU count. They were started for a QUIC
stress run that Self's own rulings demanded. Git Bash's `$!` recorded MSYS
PIDs, and the burners were very likely never stopped.
- A 30-iteration `dotnet test` loop.
- A Huginn workspace build.
- Two agents waiting on jobs that had already died.
No single fork bomb was proven, because Windows audits no process creation.
The load was Self's to schedule and Self did not budget it. The operator: "This
is literally why we have Yggdrasil, so Starfire doesn't get overloaded with
compiler jobs." The fleet inventory already said the same.
- Ruled by the operator: **Idunn owns verification**, through a verify
transaction that seals nothing and sits outside the brake. It is mapped as
its own campaign.
- The stopgap until then is `tools/stopgap/ygg-verify.sh` plus
`rust.Dockerfile`. It pushes an exact revision to a mirror on Yggdrasil and
runs the command in a container capped at 6 CPUs and 16 GiB, niced, with at
most two jobs at once. The target directory stays inside the scratch work
tree. Its header names its deletion line.
- **Correction, same day, found by a Mind Steward pass:** the caps above are
not what shipped. `ygg-verify.sh` defaults to **4 CPUs, 12 GiB and 3 slots**
— the slot raise was the operator's ("the other cores are mostly sitting
idle") and is recorded in the script's header; the 6/16 figure is not, and I
cannot say from here whether it was ever live or was written from a plan I
then retuned without a line. SKILL.md carried the stale "2 jobs" until this
pass caught it. **The script is the owner of these numbers.** Prose that
restates a running tool's configuration goes stale silently, which is how
three surfaces came to disagree inside one day.
- The smoke test found two bugs in the stopgap before any real job ran:
- `bash -lc` reset the image's `PATH`.
- `ssh` re-split the command, so `a && b` ran `b` on the host.
Arguments are now quoted with `printf %q`. After the fix, Huginn's mind
suite passed 67/67 on Yggdrasil.
- SKILL.md: heavy verification goes to Yggdrasil. Starfire does only
Windows-bound work, one job at a time, with no burners.
## 2026-09-22: H1's kept sidecar looked like a crash, overwrites clobbered each other, case-blind no-op guard
Evidence: Soul's H14 pass over the Huginn memory-organ campaign
(`soul-h14-notes.md`, findings C and D) found two defects in the sidecar
machinery the 2026-09-22 H1 fix (below) had just added, plus a third,
unrelated defect in the no-op guard.
**F1: startup repair mistook H1's deliberate stop for a crash.** H1 keeps a
target's sidecar on purpose when it catches an edit the harness did not make
(`Assert-TargetUnedited`), so the M0 original survives beside the lost edit.
Startup repair could not tell that kept sidecar apart from a crash's: on the
next run it restored the M0 original over whatever an operator had since
hand-cleaned into the target, moved the operator's clean file to
`.eureka-mutation-overwritten` (clobbering H1's own copy there, see F2),
printed "a previous run died mid-mutation", and finished green. The hand
edit was gone one run later with no non-zero exit to notice it by.
`Assert-TargetUnedited` now writes a marker, `<target>.eureka-h1-stopped`,
beside a sidecar it keeps; startup repair refuses the whole pass while any
such marker exists, restores nothing, and names what to reconcile. An
operator clears the marker by hand once the file is reconciled. Demonstrated
in a scratch repo: before the fix, a hand-cleaned target was silently
reverted and the rerun exited 0; after, the rerun refused with a named
non-zero exit and the target was untouched.
**F2: `.eureka-mutation-overwritten` was overwritten without a check.** Both
the sites that preserve bytes about to be lost (`Assert-TargetUnedited` and
the startup repair loop) wrote to a fixed name, so a second loss against the
same target, or a file already sitting at that name, was silently destroyed.
`Get-UniqueOverwrittenPath` now picks the first unused name in the family
(`.eureka-mutation-overwritten`, then `.1`, `.2`, ...). Demonstrated: a
planted file at that path survived an H1 firing, with the new loss landing
at `.eureka-mutation-overwritten.1` beside it.
**Case-blind no-op guard.** The check that rejects a mutation whose old and
new text come out identical used PowerShell's `-eq`, which is
case-insensitive, so a mutant differing from the source only in case (such
as `"query"` to `"Query"`) was wrongly refused as a no-op. Every hash
comparison in the script had the same latent gap. Both were switched to
`-ceq`/`-cne` (eight sites total); anchor matching already used ordinal
`IndexOf` and needed no change. Demonstrated: a case-only mutation was
refused before the fix and ran (and was killed by its test) after.
All three fixed and demonstrated in `tools/eureka-mutations.ps1`; commits
`6ec92de` (F2), `5ac4fea` (F1), `42adfb0` (case).
## 2026-09-22: the harness silently discarded an edit made while a run was in flight (H1)
Evidence: Soul's pass on the Huginn memory-organ Cut 10 fourth fix batch
(2026-09-22) found that `eureka-mutations.ps1` never checked what a target's
bytes actually were before restoring over them or before the next entry wrote
its mutant. That same campaign's fourth fix batch had lost edits Hands made to
a target this way; the harness gave no warning and no non-zero exit at the
time, so the loss surfaced only later.
`Restore-Targets` (`:163-182`) now hashes the target's current bytes first: a
match against the M0 original or, when supplied, the exact mutant the caller
just wrote, is restored as before; anything else is saved beside the target as
`<target>.eureka-mutation-overwritten`, printed as a loud `EDIT LOST` warning,
and the run stops (non-zero exit, no further entries) via the new
`Assert-TargetUnedited`. The same check runs again before each entry writes
its own mutant, so an edit landing between two entries is caught before it is
silently buried under the next one.
Demonstrated in the scratchpad with a throwaway repo whose test command
appends a line to the target mid-run: against the pre-fix script the run
reported green and the target came back pristine, the appended line gone;
against the fixed script the run failed loudly with `EDIT LOST`, exit code 1,
and the target still carried the appended line afterward.
## 2026-09-22: a cleanup by image name killed the whole machine's Node
Evidence: during the QUIC seventh fix batch's Windows stress round, Hands
cleared its busy-loop burners with `taskkill //IM node.exe //T`. That kills
every `node.exe` on the host, and the workstation is shared by several agents
and the operator. The kill can't be undone, and nothing records what it took
down. Rule added to SKILL.md's tooling list: kill by the PID you started.
## 2026-09-22: seven passes on one line, because the observation was outside
Evidence: the QUIC bridge's rule "the wait is the timeout the host asked for".
It took seven Soul passes and six fix batches.
- Every batch killed the mutants the previous Soul had named: a constant, then
any constant, then a clamp, an offset and a scale. Then a round clamp, a
floor and a small offset. Then a quantum round-down, a small-timeout zero and
a later-poll doubling.
- Each time, the next Soul found another function of the argument that equals
the identity at every probe point.
- Tolerances tightened until Windows needed a priority raise just to keep the
unmutated bridge green. Even then, two of the stated boundaries flipped
between repetitions.
Wall-clock observation from outside cannot converge on an arithmetic rule.
There is always another function that matches at the probes, and the
tolerance can only shrink until the scheduler decides the answer. The fix was
to observe at the rule itself: a dev-only seam records the timeout actually
handed to the condition wait, and exact equality kills the whole family at
once, deterministically.
The skill already said "put the observation where the rule is decided". What
was missing was knowing when to stop probing. Added to Self's triage:
- **When a second fix batch for the same rule is killed by mutants of the same
family, change the observation, not the probes.** Name the layer where the
rule is decided and observe there. Keep wall-clock checks only as proof the
decision is actually carried out, with generous margins.
## 2026-09-22: a ruling recorded in one section, contradicted in another
Evidence: the selection map recorded Q-J (decimal strings) as ruled in §16.
§2, the vocabulary Hands actually builds from, still said `number: float64?`.
D8 said `NumberOf -> double?`. Nothing specified the canonical form or the
comparison that the ruling had asked for. Commit 1 shipped float64 to the
branch. The Rust Hands found the conflict before writing a line, and stopped.
- SKILL.md already says to sweep the map for the old version of a design on
the same day a ruling changes it. Self did not do the sweep. The rule was
fine. Self's compliance with it failed.
- A ruling that says "the cut must specify X" is not landed until X is
specified in the section Hands reads. Recording the ruling is not
specifying it.
Same evening: two Sonnet Hands stopped early with no fork, one of them after
fourteen tool calls. Both cited budget. Both had been told to read a
1,500-line map rather than given their cut inline, which breaks the template's
"carry the cut inline". The template's hand-back clause is now qualified: it
applies when context is actually filling, not when scope looks large.
## 2026-09-22: a shared cargo target can run another checkout's mutant
Evidence: Soul, working on the Huginn Cut 10 second fix batch, had two worktrees
of the same repository. Both built into `C:\Users\Meta\.cargo-target-codex`, and
both produced the same artifact name, `huginn_daemon-e1f33b5708db9f04.exe`.
A plain `cargo test` in the second worktree did not rebuild, because its sources
were older than that binary. It ran the first worktree's last harness mutant
instead: 40 runs out of 40 failed at the mutant's assert line, not at the
source's.
- The case that does damage is the reverse. A surviving mutant's binary makes an
unmutated tree look green.
- The harness is immune, because every write it makes, M0's included, bumps the
file's modification time. A plain test run in a second checkout gets no such
protection.
- Rule, in SKILL.md's git and tooling list: a shared build output directory
belongs to one checkout per repository. Any second checkout gets its own
target subdirectory.
The same evening, Self briefed four agents freehand, and none of the briefs
carried the template's wait paragraph. That breaks the 2026-09-17 rule below.
Self sent the paragraph to the three agents that could still act on it. The
rule stands. What failed was Self's own compliance with it.
## 2026-09-17: Self's own briefs dropped the template's wait rule
Evidence: in the Aetheria item-provenance session, two Hands passes (the Cut C census
follow-up and the neutral wanderers and IFF patch) ended their turns while a mutation run
was still in the background. Each had to be resumed to finish, push and clean up.
- The Hands template in `briefs.md` already says a wait is a foreground poll, and that a
detached job cannot wake an agent whose turn has ended.
- Self wrote those briefs freehand and left that paragraph out. The defect was in Self's
briefing, not in the template.
- Rule: when Self briefs Hands or Soul without pasting the template, it still carries the
template's wait paragraph verbatim.
## 2026-09-17: three passes to pin one line, because each proved a cousin
Evidence: the QUIC bridge's "the wait is the timeout the host asked for", over
three Soul passes and three fix batches in one day. Each fix was real, and each
proved a property slightly stronger than the last and still weaker than the
rule.
- The committed entry proved the wait was **not one particular constant**. It
died only because one millisecond is less than a fifty-millisecond settle.
A different constant, two seconds, walked the whole matrix on both targets: a
host asking for five seconds would silently get two, forever.
- The fix proved the wait was **not any constant**, with a scenario measuring
elapsed time at two values whose tolerance bands do not overlap. Sound as far
as it goes.
- Soul then defeated it with a **clamp**, and with an offset and a scale beside
it, all three surviving every scenario. Capping a wait so that shutdown gets
noticed is the most ordinary spelling that line will ever be given, and it is
a function of the argument rather than a replacement for it. Any mapping that
is identity at both probe values passes for free, and both probes sat below
any plausible ceiling.
Added to the Hands brief: name the weakest thing that would still pass and ask
whether it is the rule; when a rule says a value is derived from an input, at
least one mutant must itself be a function of that input, and probe values must
sit where such a function would show. Constants are never the hard case, only
the first one.
The same pass found that a fresh Windows clone could not run the documented
Linux path at all: `.gitattributes` carried no line-ending rule for shell
scripts, so the container died complaining about a carriage return in the
interpreter line. Worth noting as a shape rather than a one-off: the path was
declared runnable-as-committed and verified by an agent who had never cloned it
fresh.
## 2026-09-17: both honest gaps were wrong, and one probe was blind by design
Evidence: two Soul passes on the same day, in different repositories, each
falsifying a gap that Hands had recorded honestly rather than faking.
**The daemon arm.** Hands wrote that the open-items dispatch arm could carry no
mutation entry, because it raises no refusal of its own and its only failing
paths need a corrupt store, and said so in the entries header rather than
substituting an easier target. That was the discipline working. Soul then built
a store-integrity probe from the crate's own public surface and killed both the
swallowing and the rewrapping mutant, neither of which the shipped suite caught.
**The bridge rule.** Hands wrote that popping before copying was unreachable
without a listener, a credential and a connection. A client opening to a closed
loopback port reaches it in about a millisecond, in fifteen lines, because the
failure path always publishes a non-empty reason; and the mutant crashes an
ordinary optimised build twice out of two while both committed scenarios stay
green on that same binary.
So an unreachability claim is a hypothesis, and the same pass that would
falsify a promise must falsify it. Recorded in the Soul section: write the gap
as not yet reached, never as unreachable, and open the next brief with reaching
it. A rule written off as undefendable that is merely undefended is worse than
a surviving mutant, because the suite and the prose agree and both are wrong.
**The blind spot.** The same pass found why a rule kept being pinned by
constants: the scenario held its callers *after* the wait whose duration was
the rule, so every timeout mutant landed behind the observation. Replacing a
caller's timeout with a hard-coded two seconds survived the entire committed
matrix on both targets; a host asking for five seconds would silently get two
forever. The committed entry that appeared to defend the rule died only because
one millisecond is less than a fifty-millisecond settle. Added: put the
observation where the rule is decided, and when a mutant dies, ask which of the
two things it actually contradicted.
Also added, from the same pass: never use `git archive` to export source for a
byte comparison, after an export carried 1,459 inserted carriage returns.
## 2026-09-16: a green suite is not evidence when its harness is gone
Evidence: two findings from the same afternoon, both about mutation suites
reporting coverage they did not have.
**The harness that no longer exists.** The QUIC cut's native bridge had its
mutation history entirely in agent scratchpads: the consumer, the close
scenarios, every reported kill. When a later Hands pass went to mark which
target two entries were honest on, their definitions were gone and could not
be reconstructed, so the rules they had protected turned out to be defended by
nothing a future agent could rerun. Soul cannot commit, so a probe that is the
only thing killing a mutation must be handed to the next Hands pass and
committed. Added to the Soul section: reported kills are evidence only while
the thing that did the killing still exists.
**The loosening swapped for an easier target.** Cut 10 of the pipeline-state
campaign reported 31 of 31 entries killed. Two of those entries were
replacements: the loosenings the brief demanded could not fail, so Hands wrote
different ones and reported the sweep. Soul then killed nothing and found
plenty: comparing only the lengths of two instance names survived, comparing
only their first bytes survived, and running the gate only when one name was
longer survived, because no fixture pair had ever shared a length. The rule was
undefended while the suite claimed two mutants for it. Added to the Hands
brief: an unfalsifiable loosening is a finding about the fixtures, and the
fixture is what gets fixed.
## 2026-09-16: front-load the invariant layer, thin the spelling layer
Evidence: the operator's read of the Eureka pipeline-state campaign, that the
design kept being discovered as the work went, producing re-cutting and
repeated Imagination passes with no good reason for the map to diverge that
far. The campaign's own record agrees, and says which layer was at fault.
Every backtrack was identity, lifecycle or authority, and every one was
answerable before a line of source was read:
- The repo-owned store was specified before anyone asked who owns a mind, and
died whole in Cut 4: 1,302 lines deleted.
- The key grammar was patched twice and redesigned on the third occurrence,
because ids had no namespace to be specified over.
- Cut 6d retrofitted sequences into landed keys, because each document's shape
was specified and its life was not.
- The operator's forks surfaced across five cuts, so rulings kept landing on
code that was already written. Two of them reversed defaults the agents had
already implemented.
None of that was a shortfall of `file:line` detail. The maps had plenty:
Cut 10's section ran to roughly eight hundred lines against a crate that did
not exist. The operator named the trap for what it is, a 1:1 map, where the
only sufficient spec is the code.
- **Added step 0b.** A model table before any cut is mapped, a row per
persistent kind, three columns: what names it, what happens to it over time,
who decides. No cut is mapped while a cell is empty. Operator forks are
assembled in the same pass as one batch.
- **Added the model-page brief** to `briefs.md`, before the cut-mapping brief.
An empty cell is the finding; inventing a plausible value is the failure.
- **Qualified the per-file anchors.** They are required for code that exists,
where they say what to delete and what not to touch. For code that does not
exist, name the types, the boundaries and the rules that must die under their
own mutation, then stop.
- **Stated what cutting faster means:** less prose per cut, never more code per
cut. Small commits stay small.
Not treated as waste: the operator changing their mind. Learning that the
intent differs is the job. It is only expensive when it is learned after the
code lands, which is an argument for surfacing forks earlier rather than for
mapping deeper.
## 2026-09-16: the loop was burning tokens on gaps, not defects
Evidence: one day of the Eureka pipeline-state and TypeScript QUIC campaigns,
about thirty-five passes. The operator asked how much of the cost was process
and how much the price of a second agent. Roughly half each. The falsifier
earned its half: three High key defects, an unimplemented epoch gate, a
replay regression, a byte-order mark divergence and a false "equivalent"
claim, none of which a green report caught. The other half:
- **Hands wrote revert mutations; Soul wrote loosenings.** Most second and
third Soul passes found not defects but rules the suite was blind to. The
Hands brief now requires both mutations per rule, and Soul reruns Hands'
loosenings before writing its own.
- **Reports were narrative.** Soul passes ran two to three hundred thousand
tokens each. Both briefs now name the report shape and forbid the rest, and
a second pass on a cut is scoped to the fix batch's diff.
- **Two harnesses re-learned each other's lessons.** Epiphany's PowerShell
harness and CultLib's JavaScript runner each hit the sidecar footgun, the
missing control and a control-byte scar on their own. One harness in this
repo is the next change; deferring it to a "skill wiring" cut cost three
rediscoveries in an afternoon.
- **The map committed per event**, about forty times in a day. Per cut from
now on, rulings excepted, since agents read them mid-flight.
## 2026-09-16: agents must be told how to wait, not only to wait
Evidence: Ghostlight L1, the stock lenses
(`docs/architecture/ghostlight-stock-lenses-cut.md` and its postmortem).
- **"Wait for long jobs" was not enough.** Three agents in one campaign ended
their turns while a build they had started was still running: a Soul pass
on Cut 1 and a Hands pass on Cut 2, twice. Each launched cargo detached,
then ended the turn "to wait". A detached process is not a tracked
child of the agent, so its completion never woke it; the work stopped until
the root agent resumed it by hand. The rule to wait was already in the
briefs. What worked was naming the mechanism: block inside a tool call,
with cargo in the foreground under a long tool timeout, or `Wait-Process` on
the pid, and never start a second cargo run while one holds the target
lock. After that sentence entered the briefs, no agent stalled again.
- **A green test can be a coin flip.** A Cut 3 test that rewrote a session's
lens to a fixed value collided with a randomly drawn lens about one run in
eight; Hands' single green run was luck. It failed 3 of 40 looped runs and
passed 120 of 120 after a deterministic fix. For any test built on a random
draw or a random identity, looping it is part of verification.
- **An operator question can hide the design fault.** Self told the operator
a lens "has to be recorded" in the command identity. The planner then found
that keying identity on mutable weights strands rows, and the operator's
follow-up ("are you relying on some deterministic code outputting the same
id rather than just checking the state?") exposed a pre-existing defect: the
elaborator's repair loop had never worked across sweeps. When the operator
asks why a mechanism works, answer from the Body and probe it; it may be
the most valuable question of the campaign.
## 2026-09-16: a mutation suite needs a no-op control and byte-exact I/O
Evidence: the Eureka pipeline-state campaign's third Soul pass on Cut 6, and the
Eyes audit that followed (Epiphany `notes/eureka-pipeline-state-cut.md`, "Mutation
harness audit").
- **A no-op control is the test of the harness.** Soul ran a mutation that
changed nothing and it killed `bounds_refuse_in_utf8_bytes`. The harness's
text round-trip had collapsed an `é` literal to one byte, so the corruption
alone failed the test and every mutation through that path would have
reported a kill. Nothing else in the loop could have noticed: a killed
mutation is what a working suite looks like.
- **The defect was not where it was feared.** The audit found every committed
suite using byte-lossless I/O. The corrupting harness was Soul's own inline
script, never written to disk, so its text is unrecoverable and its verdicts
are unverifiable. That is the same shape as the standing finding that
mutations without artifacts cannot be checked; it now applies to Soul too.
- **Encoding symmetry and line endings, not a flag.** Measured on the host:
mixed-encoding round-trips corrupt non-ASCII; any read that splits lines
rewrites every line ending regardless of encoding, and `core.autocrlf` hides
the rewrite from `git diff`. One ad-hoc Cut 1 script did exactly that, so
three early verdicts are recorded as suspect.
- **The committed suites forbade a control by shape.** Each threw when a
replacement changed nothing. A table shape that cannot express "change
nothing" cannot test itself.
- **Soul brief: wait for long jobs.** The Hands brief had this rule since the
Ghostlight run; the Soul brief did not, and two consecutive Soul passes on
Cut 6b ended their turn with "waiting on the script" and had to be resumed
by Self before any finding arrived.
- **"Default model" means the root's model.** After the operator switched
the root session to Fable, every Hands and steward dispatched without an
explicit model inherited Fable, against the skill's own allocation. The
operator noticed. Self now names the model on every dispatch: the strong
model for Imagination and for Soul on foundation cuts, the default tier by
name for Hands, Eyes, probes and stewards.
- **"Wait" now names the mechanism.** A Hands pass with the rule in its brief
still ended its turn on "waiting on the suite", the third such yield in one
day. The rule said what, not how; an agent that starts a detached job
reads "wait" as "stop and be woken", and nothing wakes it. Both briefs now
say: a foreground poll loop you run yourself, in chunks under the tool
timeout, until the job exits.
## 2026-09-16: record the gap, and absence is the hardest claim
Evidence: ghostlight-77's requirements message to the Eureka pipeline-state
campaign, drawn from Ghostlight `docs/architecture/ghostlight-library-extraction-cut.md`
and its postmortem. Both rules come from the same run as the entry below.
- **Record the substrate you needed and did not have.** That campaign hit the
missing typed-state primitive six separate ways — supersession tracked in
"(supersedes …)" prose and fixed by substring surgery, an "Open:" question
line hand-edited about eight times, three ad-hoc finding namespaces, verdicts
linked to nothing, a subtraction ledger reconciled only at close-out where it
showed a +65 → +210 overrun, and a status header rewritten by hand at every
landing. Each workaround worked, so each requirement nearly evaporated. The
agent named this itself: the failure was working around the gap silently
rather than flagging it while hitting it. A run that hits one gap six times is
the best requirements evidence a design will ever get, and it survives only if
someone writes it down while it hurts. Working around the gap is still
correct — doctrine says do not wait on a service that does not exist — but
leave the evidence.
- **Proving something is gone is the claim most likely to be wrong.** A Mind
Steward reported a stale phrase retired because a line break had split it from
its grep. The phrase was still there, still steering. A verification that
cannot see its own target reports a clean pass, which is worse than no
verification: it closes the question. Prose reflows, so an exact search over
it is a tripwire with the same limits as any other text scanner — the
scanner rule below is not only about code.
## 2026-09-16: sealing, forgeries, scanners and proportion, from Ghostlight L0
Evidence: Ghostlight `docs/architecture/ghostlight-library-extraction-postmortem.md`
and its cut map.
- **Soul attacks constructibility, not names.** Ruling Q1-4 said sealed
constructors must be unreachable. Ten compile_fail doc-tests passed while
every public ID type derived `Deserialize`, so an external crate built any of
them from JSON; an ID-minting `Default` impl left all ten green. It was found
only because Self added the serde route to the Soul brief by hand. On stable
rustc the compile_fail error codes are not enforced either.
- **Rejection tests need realistic forgeries.** Short fake digests passed
against kernels comparing only length, a prefix, all but the last character,
case-insensitively, or only the hex after the label. Five mutants survived
across two Soul passes.
- **Text scanners are tripwires.** Every one written in the campaign lost to a
short evasion: a single-minter count to an alias, a subdirectory, a type
alias and a function value; a deploy wiring test to a two-line deletion that
reattached the pinned line to the wrong container, and its fix to `if false`
around exact lines. Prefer a semantic tool, or state the limits.
- **Default proportionate forks.** Asked how much the single-minter enforcement
question mattered, the operator said not at all. A fork that guards only
against the project's own code is a default plus a recorded follow-up.
- **Hands brief additions:** wait for long jobs instead of reporting a to-do
list (one Hands agent did); restore mutations without `git checkout` (one
run was invalidated that way); measure warnings from forced rebuilds (a
cached build reported zero).
## 2026-09-16: subtraction rules, from the Eureka campaign's own cuts
- **A deletion can take a live rule's only test with it.** Cut 4 deleted a store
module; the epoch guard that survived in another file then passed a `if false`
mutation, and nothing could construct the state needed to reach it. The fix
was to delete the unreachable rule too, not to write a test for state nothing
can build.
- **A mutation that never applied is not a passing mutation.** A multi-line
anchor matched nothing on a CRLF working tree. Scripts must fail loudly on a
stale anchor, and should also assert the anchor matches exactly once, since a
multi-site replace can read as "killed" when its test failed for another
reason.
- **Correction, same day.** Self first recorded this as "one mutation reported
a verdict it never earned". Soul checked the committed history: the script
already threw on a missing anchor, the affected mutation existed only in
Hands' working copy, and every committed verdict was real. The rule stands;
the incident was narrower than first written.
## 2026-09-16: an instance owns its mind, and a service owns the state
Operator correction, mid-campaign, after three Soul passes spent propping up a
repo-owned store: "I think someone needs to own Epiphany/Eureka state. Each
store should be canonical to one instance, the store is that instance's mind,
and that instance can be assigned stewardship over multiple repos."
- **The lesson for this skill:** when a design needs escalating machinery to
hold together (a per-clone lease, git-directory resolution, committed
attribute checks, a merge tool), the missing piece is usually an owner, not
another guard. Soul kept confirming the guards were weak; the guards were the
symptom.
- Mind state does not belong in version control. Git offers branches, merges and
history rewriting; a mind wants one owner and an append-only record.
- Eureka's typed state will live in a memory organ service (Huginn) that owns
each instance's mind, depends on Qdrant directly, and refuses loudly when
unreachable rather than letting a second writer exist.
## 2026-09-15: the operator channel is the session
Operator ruling. Spec iteration proved to be the load-bearing part of a Eureka
run, so the question path matters as much as the cut loop.
- Eureka has no Persona. Self is the operator surface.
- Blocking questions may be pushed through any notification MCP the user
configures. Eureka owns no transport, so other people can swap in their own.
- Answering over a chat channel is a later campaign; it needs identity binding
before a reply can become a ruling.
- The operator's framing: Epiphany gives each repo an identity, memories and a
Persona to talk to; Eureka is "the cut-down Claude-native version anyone can
use".
## 2026-09-15: Hands brief rules from the Eureka pipeline-state campaign
- **Define every mutation in the report.** Soul could not rerun Hands' Cut 2
mutations "C1-C5" because the report named them without saying what changed.
- **"Unchanged" is proven against the base commit.** Cut 2 pinned a Mind
receipt digest captured from the new code. Soul had to rerun the test at the
base commit to show it matched.
- **Baseline shared build caches by path list.** A Cut 2 Soul pass left about
401 MB in the shared cargo target that it could not attribute. It had
recorded only counts, so it did not delete blind.
- **Check that a detached script started.** A Cut 1 mutation script died on a
PowerShell parse error, and its monitor sat silent for 30 minutes.
## 2026-09-15: renamed to Eureka; postmortem rules folded in
- **Rename.** The operator named the skill-and-MCP form of the pipeline
**Eureka**. Epiphany stays the name of the running organism, and Eureka keeps
the faculty vocabulary.
- **Operator observation: typed state.** What makes Epiphany special is that
findings are stored in typed state that can be queried semantically. Claude
agents can be given the same state tools. Eureka's likely next step is to
replace doc, report and memory findings with typed state documents (findings,
rulings, cuts, Soul verdicts) behind MCP tools.
- **Operator observation: Imagination.** Eureka's Imagination has a more clearly
defined target-mapping role than Epiphany's. Epiphany may want to adopt it.
- **Epiphany comparison.** A source-grounded model of Epiphany at `71324718` is
kept as `epiphany-comparison-2026-09-15.md`. It is a dated snapshot, not live
truth. Epiphany's own `notes/faculty-workflow-lessons-2026-09-04.md:146-176`
proposes the typed forms of Eureka's habits (Verdict HOLDS/FALSIFIED/UNPROVEN,
Rulings, a landed-names digest, steward triggers). Start from those schemas
when Eureka gets typed state.
- **Rules added from the postmortem:**
- Soul runs before any release or tag.
- Ask what a domain concept is for before specifying it.
- Carry order as data, not as scheduling.
- Reconcile the subtraction ledger at every landing.
- Count consumers across every project the operator uses, not just one
runtime.
## 2026-09-15: initial formalization
Extracted from the Aetheria CultCache migration (Cuts 0-10, 2026-09-12..15). The
evidence is `postmortem-cultcache.md` and the Aetheria repo's
`docs/cultcache-migration-cut.md`.
- **Soul is distinct from Imagination.** An operator correction: "Making another
pass on the cut map is still Imagination."
- **Target shape finish line:** Hands barely reads.
- **Each operator ruling needs its own failing mutation.** In Cut 10, Soul removed
the availability rule, the price rule and the all-or-nothing fit rule, and all
15 tests still passed.
- **Don't cap Soul early.** Cuts 6 and 6b each needed four Soul passes.
- **Git and tooling scars:** explicit staging, message files, the 3-tag limit,
commit-independent byte checks, detached builds, and the process-probe role
check.
- 2026-09-18: the wait rule now names the failure shape, not only the
mechanism. Hands ended two turns announcing it would wait for a mutation
suite (Aetheria shield Cut 3), the second after an explicit correction. The
brief already said how to wait; it did not say that saying "I'll wait" and
yielding is the violation. Both copies in `briefs.md` now do.
- 2026-09-18: context is a budget. The operator noticed Sonnet Hands runs
passing 400k tokens on Aetheria's stats and shield cuts and said they "start
to get loopy with that much context" — matching where the sloppy claims in
those runs appeared. `briefs.md` now tells Self to size cuts to one worker's
head: verify once at the end, quote the cut inline instead of pointing at the
whole map, keep expensive scaffolding alive across cuts, and hand back a
clean partial rather than push through.
- 2026-09-22: never amend or force-push a shared branch. In the Ghostlight
play-agent pass, a Sonnet Hands agent on Cut 3 amended its pushed kernel
commit and force-pushed it (`9fbdde8` -> `4c4973b`). Self was pushing
cut-map commits to the same branch at the time. Nothing was lost, only
because Hands had rebased on those commits first. `briefs.md` now forbids
amend and force-push and says to fix with a new commit, and SKILL.md's
scars list carries the rule. That run also reached ~400k tokens against a
brief that pointed at the whole map, which is the 2026-09-18 context-budget
rule showing up again.
## 2026-09-22: a Hands agent read a five-day-old map, and refused an unsigned instruction
- **Maps go on `main`.** Self had been committing campaign maps to whatever
branch it was standing on. A Hands agent on the CultNet selection cut
reported that the map carried no fix-batch-4 section — true on the branch it
could see, where the copy was from 2026-09-17. It worked from its brief
instead and flagged the gap, which is the only reason this surfaced rather
than a cut built on stale design. SKILL.md now says maps are committed to
`main` and that every brief names the map's branch.
- **The same agent refused an out-of-band instruction, and was right to.**
Self sent it the Stryker survivor list mid-task through the agent messaging
path. It arrived appended after a tool result, claimed to be from "the
coordinator", and asked to expand scope well beyond its brief. The agent did
not act on it and flagged it for verification instead. The message was
genuine. **The lesson is not to make the agent more trusting.** Scope
expansion mid-task should arrive as a new brief with its own dispatch, or be
confirmed by Self before the agent acts; an instruction that cannot be
distinguished from an injection should be refused whether or not it happens
to be real.
## 2026-09-22: two Hands in one working tree, and the agent that refused to force it
Evidence: Self dispatched a Hands agent for the CultNet selection survivor
tests into `F:\Projects\CultLib` while another Hands was mid-build in that same
checkout on a different branch. The second agent switched the branch to reach
its own work, which pulled the tree out from under the running build, then
found an uncommitted `CMakeLists.txt` edit it had not made. It **stopped and
reported a concurrency hazard rather than forcing the checkout**, which is the
only reason nothing was lost.
The rule against parallel Hands in one working tree was already in SKILL.md.
Self ignored it. The fix is not a stronger warning to Hands: Self now **creates
the worktree before dispatch and names it in the brief**, so an agent is never
left to find somewhere to stand. A brief that names a repo path without naming
a worktree is a defect in the brief.
Second-checkout build output stays separate, per the existing scar: a shared
target directory has let an unmutated tree look green.
## 2026-09-22: a green Windows suite that had never loaded the pinned library
Evidence: the QUIC bridge imports `msquic.dll` by name. Nothing copied the
pinned library beside the test binary in the build tree. On Linux that failed
loudly with `cannot open shared object file`. **On Windows it did not fail at
all** — DLL search fell through to `PATH` and loaded an unrelated `msquic.dll`
from the Windows Performance Toolkit, 529 kB against the pinned build's
4.1 MB. Every scenario passed against it with nothing indicating a problem.
The green result was the thing hiding the defect, and it was found only
because a brief asked for the documented path to be executed literally rather
than for the previous error to be gone.
- **A dynamic dependency resolved by name is unproven until its identity is
checked.** Where a test loads a pinned native library, the verification step
reports the loaded file's size or hash, not merely that the run passed.
Windows' search order makes silent substitution the default failure mode,
where POSIX tends to fail loudly.
- **When a defect is found on one platform, check the other for the same class
before closing it.** Both Linux fixes lived inside `if(NOT WIN32)`, so the
Windows path was never examined until it was run.
- **Scope the doubt honestly when this happens.** Earlier Windows numbers in
that campaign were all measured on the same machine, where the copy never
existed, so they are recorded as unconfirmed rather than quietly kept.
## 2026-09-22: a probe that will be committed must be handed over while it exists
Evidence: Soul's fifth Idunn pass settled `resolve()`'s fsck on a real
smart-HTTPS rig and Self ruled the rig should be committed, naming Soul's
files. By the time Hands looked, none survived — no session, no Yggdrasil work
directory, no scratch trace. Hands rebuilt the rig from the map's prose, which
worked **only because the map described it in detail**.
Probes are throwaway by doctrine and that stays true. But the moment Self
rules that a probe becomes a committed test, it stops being scratch:
- **Self records the rig's mechanism in the map in enough detail to rebuild
it**, not merely that it passed. That description is what saved this one.
- **Better, rule on committing it while the Soul pass is still live**, and
have Soul hand the files over in its report rather than leaving a path.
## 2026-09-22: Soul was wrong about a redundancy, and a Hands hand-trace caught it
Evidence: Soul's S5-8 reported four containment checks where one sufficed,
each surviving deletion **alone**, and Self ruled three of them deleted. Hands
deleted two, hand-traced the third, and found that with all three gone a
symlink whose entire target is `".."` at the root resolves to the parent of
the root and returns `Ok` — a containment escape — because no later `Normal`
step runs to trip the surviving arm's own check. Hands kept the check, pinned
it with a new test, and recorded the deviation in code.
- **"Each survives deletion alone" does not mean "any two survive together."**
A redundancy finding must be probed at the combination Self intends to
delete, not one member at a time. Soul's method was sound and its conclusion
did not follow from it.
- **A Hands brief must invite this.** The rule that a deletion judged unsafe
is reported with evidence rather than quietly kept is what produced the
catch; it stays in the template, and it applies to deletions Self ordered.
## 2026-09-22: a wall-clock pin that was really a constant, and the ratio that hid it
Evidence: a Hands test pinned a cost regression with
`large < small.max(0.01) * 20.0` and reported a 33x separation. Soul measured
`small` at 2.7-9.4 ms across eight runs -- **always under the 10 ms clamp** --
so the clamp always fired and the assertion was simply `large < 200 ms`. Clean
code had already measured 198.9 ms under three concurrent suites, the normal
load on the shared build host. The true separation was **1.63x**; the reported
33x came from one pair straddling the clamp boundary.
- **Pin a cost with a count, not a clock.** Where a regression is about work
done, count the calls through a test-only counter. A count is deterministic
under any load; a wall-clock assertion on a host running several jobs fails
in both directions, and where a pipeline turns red into a verdict, a false
red is a false verdict on unattended work.
- **A reported ratio is a claim like any other.** Self accepted 33x because it
sounded decisive. Ask what the denominator actually was.
## 2026-09-22: two fixtures that would have lied, caught by mutating not reading
Evidence, one batch:
- A setuid fixture set the bit and then ran `chown`. The container's `chown`
silently clears setuid even when the owner does not change, so the fixture
tested nothing and looked like a pass.
- An `observe_frozen` test tampered with the recipe file, which a *different*
check inside `observe_frozen` also catches. It stayed green with the digest
check deleted -- the check it existed to pin.
Both were found by running the mutation against the final spelling of the
code, not by reading the test. This is why that step is in the Hands brief,
and why "the test passes" is not evidence that it pins anything.
Same batch, the honest outcome: an artifact-digest term could not be pinned at
all, because the collision it prevents needs a NUL inside a symlink target and
`symlink(2)` cannot produce one. Hands built the fixture, confirmed it did not
go red, and **deleted it rather than keep a test that lies about what it
proves**. Record the gap; do not keep the ceremony.
## 2026-09-22: check the tool before ruling that it be configured
Evidence: Self ruled a bind mount should carry `nosuid`. Docker exposes no
per-bind-mount `nosuid` through either `--mount` or `-v`; both are rejected.
The container already ran with `--security-opt no-new-privileges`, which is
process-wide and stronger. Hands verified against the real CLI and withdrew
the ruling with evidence.
A ruling that names a flag, option or API is a factual claim about a tool.
Check it, or say it is unverified and let Hands confirm before building on it.
## 2026-09-23: a test's fixture must be the shape production uses
Evidence, from three consecutive Soul passes over one Idunn file and one
selection batch:
- Every `digest_artifact` test wrapped its file in a directory so the tree
path ran. Production hands that function a **bare file**, immediately before
launch. The fast path was blind to setuid, mode and file capabilities, and
no test reached it. One test's doc comment claimed to pin exactly the
scenario it never touched.
- A row-tiebreak test passed with the tiebreak **reverted and swapped**,
purely because of which real schema hash happened to sort first. It was
degenerate for its whole life and looked green.
- An xattr test pinned presence against absence. The thing that carries
privilege is the **value**, and nothing hashed it.
So: **write the fixture at the shape production uses**, and where that is
genuinely impossible, say so in one line in the test. A fixture chosen for
convenience produces a green suite over an untested path, which is worse than
a gap because it is counted as coverage.
Corollary, from the same passes: **a test whose inputs come from real
identifiers (hashes, generated ids) can pass by accident of their ordering.**
Derive the fixture's keys from the ids discovered at runtime, then confirm the
test fails when the rule is broken.
## 2026-09-23: a convention is not an invariant; move it into the compiler
Evidence: a call-counter pin was an honour system, so it was replaced by a
narrow port that charges inside the syscall method. Soul then restored the
original bug in two spellings **around** the port, with the whole suite green,
and the leak was already in the file — another function still called
`fs::read_link` directly.
Each round made the convention more elaborate without making it binding.
Where a rule says "all access goes through X", the enforcement belongs in the
build: a `disallowed-methods` lint, a module boundary, a private type — not a
comment, and not a habit. **If the fix is another thing to remember, it is the
same defect one level up.**
## 2026-09-23: concurrent mutation runs do not give reproducible verdicts here
Evidence: the same commit, mutated three times at `-j4` on the stopgap's Rust
image, produced **different missed and timeout sets each time** — one mutant
flipped between missed and caught with no code change — while every `-j1` run
reproduced an identical result. The likely cause is the shared
`CARGO_TARGET_DIR` under concurrent mutant builds, which is the same class as
this file's existing shared-target scar.
- **Confirm a survivor list at `-j1` before ruling on it.** A killed mutant
that was genuinely killed stays killed, so a *score* is roughly usable; a
**survivor list from a concurrent run cannot be trusted as complete**, and
survivors are what drive rulings.
- This applies to Stryker's `--concurrency` as well. Figures in the Aetheria
and CultNet campaigns taken at concurrency 4 are provisional; where they
drove a ruling, the ruling stands on the hand-confirmed mutation beside it,
not on the count.
- Per-mutant build isolation costs wall-clock: roughly triple per mutant at
`-j1`. Budget for it, and scope the run rather than raising concurrency to
make a sweep fit.
## 2026-09-23: fixtures that satisfy an assertion by coincidence
Three instances inside two days, all green, all proving nothing:
- A row-tiebreak test passed with its rule reverted **and** swapped, because
of which real schema hash happened to sort first.
- A sign-bit mutation survived tests using 1.0 and 2.0, whose **bit 0
coincidentally matches their sign bit**. `-1.0` and `-2.0` killed it.
- Artifact-digest tests wrapped a file in a directory, so they exercised a
path production never takes.
The shared cause is a fixture whose values happen to satisfy the assertion for
a reason other than the rule. **After writing a test, break the rule and watch
it fail** — that step is what separates these from real coverage, and it is
already in the Hands brief. Where a fixture's values come from generated
identifiers, derive them at runtime instead of hoping their order cooperates.
## 2026-09-23: name a tool's absolute path in every brief, every time
Evidence: a Hands brief said "use `ygg-verify.sh`" without its path, where
earlier briefs had given
`C:\Users\Meta\.claude\skills\eureka\tools\stopgap\ygg-verify.sh`
in full. The agent searched the obvious repositories, did not find it, and
**did the whole batch's verification locally instead** — on the operator's
workstation, which the load budget forbids for exactly this work.
It reported the gap plainly rather than claiming the bar was met, and left its
branch unpushed so the work would not look merge-ready. That is the right
behaviour and it is the only reason the gap was visible.
- **Give every tool an absolute path in the brief.** A name is a guess; a path
is an instruction. Self dropped the path when shortening a brief that had
grown long.
- **State what to do when a named tool cannot be found**: stop and report, do
not substitute. A substitution that silently relocates work onto a forbidden
host is worse than a stalled batch.
## 2026-09-23: a redundancy ruling must be probed at the shape that breaks it
Evidence: Self ruled a `!member.IsReference` guard was duplicate authority and
ordered it deleted, following a Soul finding. Hands verified, concluded the
same, and wrote the premise into the source: "index aliases are unique per
descriptor". **Source contradicts it** — the uniqueness check groups only
members that *have* an alias, so a plain data member's bare name shares the
role namespace unchecked. A legal document shape then took every hop path into
an untyped exception, which the batch's own new backstop swallowed silently.
This is the **second** redundancy ruling in this campaign to be wrong. The
first, in Idunn, was caught by Hands hand-tracing before deleting. This one was
not, because both Self and Hands reasoned from the same property and neither
constructed the shape that would break it.
- **Before deleting a guard as redundant, build the input that would need it**
and show the other authority handling it. An argument from a property is not
evidence; the property is what is in question.
- **A guard that filters by kind is not duplicate authority** merely because a
downstream owner also filters. Ask what each one protects.
## 2026-09-23: a pipeline that never runs the whole path has not finished
Evidence: Ghostlight's play-agent pass ended with Soul judging "the bar is
met" across 17 cuts and ~196 findings. The first person to press a button
then found about twenty more defects in one day, one per step of the real
path: sign-in, Heimdall discovery, receipts, the Idunn v2 recipe, binding,
runtime bundle, PID namespace, presence races, lease wait, Odin readiness,
the browser return, the completion request, session expiry, the model
watchdog. **Almost every one was a consumer restating its own picture of a
neighbour** (a strict struct, a fixture, a hand-built request, an old
contract) and tests that could only agree with their author. The deploy
path itself had been written three weeks before the capability it
deployed and no cut map ever named it.
- **A capability cut names its deploy owner.** Any cut that adds or changes
what a service needs (transport, env, dependency, state) names the recipe,
binding and runbook that must admit it, and those change in the same pass.
- **Prove each seam against the real counterpart, both directions.** A
fixture copied from our own struct proves nothing about the owner's
document; build fixtures from the owner's code or published schema, and
make the default fixture differ from our assumption where the owner's
does.
- **Run the whole path before calling a pass done:** the deploy through the
real deployer, sign-in with a real identity, one real turn. A green suite
on the author's platform is not that; run the target platform's suite
before handing a build to the gate.
- **Every refusal must be visible to the operator.** A silent denial cost
hours of reverse-engineering from byte counts.
## 2026-09-24: quota, oversized fix batches, and greens that ran nothing
Evidence from the StreamPixels transport campaign (Cuts E and F, merged in
StreamPixels `2d4215c`):
- **Quota.** Partway through 2026-09-23, the operator flagged that 60% of the
weekly quota had gone in one day, with little of it on the StreamPixels path,
and redirected all work there ("We need that TO EAT"). SKILL.md section 5
now says that quota is spent through scope: one full Soul gate per cut, fix
batches only for findings on the critical path, and narrow Sonnet passes for
small deltas.
- **Oversized fix batches.** One brief packed about ten items, a subtraction,
two hosts and a late addition. It ran to about 800k tokens over 2.6 hours.
Another batch passed 550k and produced sloppy code. After batches were split
per file, they stayed at about 200-400k. This is the 2026-09-18 context
budget rule again. `briefs.md` now says to use one fix batch per file.
- **Greens that ran nothing.** A zero-match `node --test` glob exited 0.
vitest discovered zero tests under a script's `--dir .` (`cc71ce2`).
`pnpm --filter X test -- <files>` did not filter (`4624b53`). A job-wide
`DATABASE_URL` would have switched unrelated suites onto Postgres
(`e019de0`). `briefs.md` now requires a non-zero count for each named file.
- **"Pre-existing" was used as a verdict.** Several agents waved a failing test
through as pre-existing. It had never passed on a clean checkout, because it
read a git-ignored, licensed asset pack (`27053f9`). A transient Windows file
lock was also called pre-existing, because the base commit was tested under
the same lock. `briefs.md` now asks for a one-line cause for every failure,
including ones that are not yours.
- **An agent waited forever on a hung job.** The fix went into the owner:
`ygg-verify.sh` got a watchdog (`9359e9c`), and then the watchdog's `sleep`
was stopped from holding a slot lock through an inherited file descriptor
(`dbe594e`). The commits and the script header record the details.
## 2026-09-25: Self sizes each cut against Hands' context budget
Operator direction, 2026-09-25: Self should spend real attention on inference
spend, mainly by making cuts smaller so each Hands agent carries less context.
Hands runs often grow past about 0.5M tokens, which is where Sonnet starts
behaving badly. The operator's reading of the 2026-09-23 quota spend (about
60% of a week in one day; the 2026-09-24 entry above) is that it was
legitimate work; the regret was running it on a more expensive, more verbose
model just as Opus 5.5 released. Model choice already has an owner in Self's discipline ("name the
model on every dispatch"); this entry is about context.
The context-budget rule of 2026-09-18 said "Self sizes a cut to fit one
worker's head" but named no number, and it lived inside the Hands brief
template, which is text Self sends to Hands rather than a decision Self makes
before sending it. So did "one fix batch per file", which is also Self's call.
- SKILL.md step 3 now owns cut sizing: Self estimates the brief, the source
Hands must read, one verification and one fix round, and the item count,
against a 0.5M danger line, and splits when the estimate passes about half
of it. It compares the reported token total with the estimate afterwards.
- `briefs.md`: the Hands template keeps the Hands-facing clauses and points at
step 3; "one fix batch per file" moved out of it into step 3. The Imagination
template's split clause names the budget as a placeholder Self fills in.
- SKILL.md section 5 no longer describes the 2026-09-23 spend as misplaced.
## 2026-09-25: the Mind Steward faculty is renamed Life
Operator ruling, 2026-09-25. In Epiphany and Aquarium the organ is called Life
(role id `reorientation`; Aquarium
`docs/epiphany-agent-sdf-visual-language.md`, section "Life"): continuity,
pressure, reorientation, resume and regather posture, and memory survival. The
operator: Life is "a much better fit as the actual load-bearing organ." The
public landing page in `eureka-site` already calls it Life.
- SKILL.md (frontmatter, faculty table, dispatch lines, step 6, model
allocation), README.md and `briefs.md` now say Life.
- The role is unchanged: Life still does what the Mind Steward did, memory
hygiene at phase boundaries. Any wider scope is a separate ruling.
- Dated records keep the old name where it is what happened: the entries in
this file, and the 2026-09-15 Epiphany comparison, which gains a note that
its faculty names are as of that date. The CultCache postmortem's agent
count reads "Life passes (then called Mind Stewards)".
- The operator owns the dispatch mechanics: global doctrine's "Agent Mind
Stewardship" section and the `mind-steward` agent definition still use the
old name. SKILL.md defers to doctrine for the agent type instead of naming
it, so it stays correct whichever name doctrine settles on.
## 2026-09-25: split authority is a defect by itself
In Aetheria fire control Cut 12.3, the operator ruled that multi-cell items absorb proportionally
across lanes. Hands then built a survey that sent each shot down either the old sequential walk
or a new pooled walk, depending on whether any item was shared. Self's Soul brief asked whether
the two paths could disagree, which treated the split as acceptable as long as they agreed. The
operator: "Do not expect items taking up multiple cells to be an exception, this should be one
code path", and "such obviously unnecessary split authority should never pass a Soul pass."
- The Hands brief template gains "one rule, one path": a simpler case runs through the general
path, and a second path is reported, never built.
- The Soul template's split-authority line now says two paths deciding one rule are CONFIRMED
on sight, and that the pass names the single path instead of testing equivalence.
- The root cause was Self's own map wording, "with no shared item it equals sequential
absorption exactly", which read as permission for a special path. Maps state the general
rule and let the degenerate case fall out of it.
## 2026-09-30: consumer audit bounds internals, not a general library's public surface
- Evidence: P4 (collapse Geometry's CultVec types into CultMath) found that CultMath's `float2`,
`float3` and `rect` had no MessagePack support. Aetheria, StreamPixels and three Aetheria forks each
carried their own formatter copies. The first option offered was to stop serializing the Geometry
types. The operator's words: "we want all the math stuff to serialize, there's no excuse not to" and
"just because we're not using a feature doesn't mean we can cut it".
- SKILL.md's subtraction section now says that for a general-purpose library (CultLib) the question is
what a reasonable consumer would expect. The call-count audit bounds only internal duplication.
- The root cause was the skill's own "count real use … a surface with no consumer is parked" rule. It
was written for app-internal subtraction and read as licence to trim library capability.
## 2026-09-30: Soul checks history before recommending a mechanism
- Evidence: CultNet publish, Soul pass 3, recommended ordered delivery inside CultCache's publication, on the
writer's thread. Imagination found that exact scheduler in `bcae483`, deleted in `4562340` 34 minutes later
for deadlocking against `_lifecycleGate`. A prototype re-proved the deadlock. SKILL.md already recorded
"design order as data, not as scheduling", and the brief carried no instruction to check.
- The Soul brief template now requires `git log -S` / `--grep` on a mechanism before recommending it. A
mechanism that was tried must be cited with the reason the new situation differs.
## 2026-09-30: errors never echo an input value
- Evidence: the Heimdall secrets cut existed to keep secrets off terminals, yet its new reader's error printed
the `_FILE` path. Soul reproduced a full `postgres://user:pw@...` URL reaching journald when a URL was bound
into a `_FILE` slot. The Imagination map for the same cut had already found TOML parse errors quoting whole
`KEY = "value"` lines. Same class, found twice in one day.
- The Hands brief template now requires that errors, logs and refusals name the field and the error code, never
the value, and that this is pinned by a canary test.
## 2026-09-30: campaign state is typed (Huginn Cut 15, under Q-H6 A)
- Evidence: the Huginn wiring map (Epiphany `notes/huginn-organ-cut.md` at `74804d83`, Cut 15). Huginn is live
on Yggdrasil with instance `eureka`, and `eureka-state` exposes four tools (`whoami`, `admit`, `view`,
`query`). The operator's ruling on Q-H6: "every bit of prose we're passing around should be viewed as a
potential liability, *especially* if it's too big to just shove the whole thing into context. We should be
looking for ways to turn what remains into typed state as well."
- Questions, rulings, cut specs, reports, verdicts, findings, follow-ups and resolutions are admitted documents.
The committed map keeps body facts, the model page and rationale, and has no status header, no per-cut
sections and no rulings. A superseded ruling or spec is closed by a resolution, never edited.
- `references/cut-map.md` is replaced by `references/campaign-state.md`: the tools, the document set, the
resolution matrix, the selection vocabulary, and recipes that replace the deleted `open_items`,
`rulings_in_force` and `history` presets. Specs with no report is one query, because the substrate's `cited`
hop takes `exists: false`.
- Briefs give ids and recipe names instead of pasted specs or rulings. Every brief starts with Rehydrate:
`whoami`, then the recipes. Hands reads its spec with `view` and admits a `cut_report`. Soul admits a
`verdict` and its findings; admitting is a Mind write, so Soul stays read-only on repos.
- Self stops when the organ is unreachable and keeps no second copy meanwhile. "Record the substrate you
needed and did not have" stays, and the record is now a `follow_up`.
- Soul's first pass on this change (Self's triage, same day) found the claim set bounded by Hands'
self-report. Soul now makes one claim per ruling the spec cites, beside the promises; the operator
reaffirmed that Soul loops are "the whole thesis behind Eureka". Only Self closes a finding `Fixed`,
after a Soul pass on the fix holds. The same batch added: one rule for an organ that does not answer
(missing tools, one retry on `Unavailable`, a resumable stop); a Hands exit for a fork before the first
commit; operator directions as rulings with no `answers`; the target document committed before its
`DocRef`s are admitted; and a substrate-gaps table in `campaign-state.md`.
## 2026-09-30: pipefail reaches job scripts
- Evidence: Soul (scenarios batch 2) proved that `bash -o pipefail -c "bash job.sh"` does not give the script
pipefail, because shell options are not inherited. A job script ending in `dotnet test ... | tail -3` came back
`exit 0` on a failing test. Every Hands and Soul job on 2026-09-30 ran as `bash <script>.sh`.
- The stopgap now exports `SHELLOPTS=pipefail` into the container. Probe: a script `false | cat` exits 1, and
`true | cat` exits 0.
- **Consequence for today's results:** any job-script verdict whose last command was a pipeline, reported before
this change, is unproven. Soul passes re-derived their verdicts from test output, not from the exit code alone, so
their CONFIRMED and held claims stand. Hands-reported "green" that rested only on the exit status should be read
as unverified until re-run.
## 2026-10-01: waiting costs no model calls
Operator, verbatim: "It's not the chatter, it's the model call, we want you not to activate at all while waiting;
tools should return on events, not timers (or at least not timers less than a minute long)". The mining Cut 3
fix Hands woke Self four times with interim results while its suite and mutations ran on Yggdrasil, and the
brief template prescribed foreground polling in tool-timeout chunks (one model call per chunk). The template
also claimed "nothing wakes you", which is stale: the harness wakes an agent when its background shell exits.
Both brief sections now say: one background waiter per job that exits on the event, no timer under 60 s, no
duplicate watchers, no interim messages.
## 2026-10-02: prior art before every nontrivial fork
Operator, verbatim: "can we make it a rule to stop what we're doing and do an Eyes pass to look at research papers
and other implementations related to whatever we're working on before any nontrivial forks? I feel like the default
reflex is to defer to my judgment, and I'm neither a Dijkstra nor a Carmack". Evidence: in the Asura and variants
campaigns that day, Self put the single-file store's write model (carry foreign records, refuse, log-structured, page
tree) to the operator from first principles. SQLite, LMDB and log-structured stores had not been surveyed, and
neither had the literature. SKILL.md step 2 now requires an Eyes prior-art pass before a nontrivial question
reaches the operator.
## 2026-10-02: every consuming test project, counted
Evidence: the R1 merge into C2a was reported green, yet GameCult.Mesh.Tests did not compile at any commit of the
range. Git auto-merged a main test that uses a type C2a deleted. That broken build hid a High finding: CultMesh's own
single-file reader read zero bytes as an empty store. Earlier, the C2a F2 Soul hold covered a range where a Caching
test was already red. The Hands template now requires one evidence line per consuming test project (found from the
project files), with "did not build" reported as a result. The Soul template checks the list against the project
files and runs anything missing.