Ruling eureka-body:ruling:cli-over-mcp-agent-surface. Exact command forms in campaign-state.md; MCP kept as the fallback for shell-less clients. persona_get and persona_put have no CLI verb: recorded as a known gap. tools/mind.py and tools/recall.py (MCP-bound scripts) are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
36 KiB
Brief templates
These are starting points. Fill in every <> placeholder. Each brief should let
its agent act without asking what the invariants are. Campaign state is typed:
a brief gives ids and recipe names, and never pastes or paraphrases a spec, a
ruling or a finding. The agent reads those itself. Recipes are in
campaign-state.md.
Every brief that reads or admits campaign state carries:
- the campaign slug
<c>, and the session label<session>for its admissions; - the Rehydrate block below, as the agent's first step, with
<recipes>set to the recipes that brief names.
Every dispatch names its subagent_type and its model. The type is the
faculty's lowercase name (imagination, hands, soul, eyes, modeling,
life) and loads that faculty's charter from ~/.claude/agents/; a
general-purpose dispatch loads no charter, so the brief would carry the whole
faculty alone. The charter owns identity, method and boundaries; the brief owns
the task. Model: Sonnet for Hands, probes, search and Life; Opus for
Imagination and for Soul over a cut that puts the foundation at risk. Never
leave it as "default", which inherits the root's model.
Every agent is capped at about 300k context or 150 calls. At the cap it commits what is verified, admits its report with what is undone, and stops; Self dispatches a fresh continuation agent with the report id. Do not stop because the scope merely looks large.
This block is the one definition of rehydration; the other files point here.
Contents
- Rehydrate (every faculty that touches campaign state)
- Self: open a campaign, rule, triage
- Imagination: settle identity, lifecycle and authority
- Imagination: map or refresh a cut
- Hands: execute a cut
- Soul: falsify executed work
- Life: phase boundary
- Eyes: gather evidence
Rehydrate
Campaign state lives in the instance's mind, through the huginn CLI
(huginn whoami, admit, view, query; `huginn --help`). Recipes are in references/campaign-state.md.
0. If `huginn` does not run in your shell, stop and report that. Never
work from prose or memory instead.
1. Run `huginn whoami`. On Unavailable, retry once; if it still does not answer, stop
and report the error. Self will continue you once the mind answers.
2. Run the campaigns recipe. If there is no campaign, or <c> is not among
them, stop and report which.
3. Run <recipes>, with root = <c>, and view every id this brief names.
Admit as faculty <Faculty>, agent <your name>, session <session>. A refusal
names the rule that refused: fix the batch and admit again. InvalidInput and
TooLarge are the same: fix the call (or narrow it) and retry. Retrying an admit
is safe (AlreadyAdmitted). Unavailable after one retry, any other isError, or
the mind going away mid-pass: stop where you are and report the step; never
send your record as prose.
A denial headed "This is a deliberate hold" is the near-neighbour check, not a
refusal: read the listed records; if none is the same decision send the identical
call again, otherwise revise the batch.
Self: open a campaign, rule, triage
Self's own checklist; Self admits as SelfFaculty.
Open: check `huginn` runs, then `huginn whoami` (the mind's
instance document, <instance>:instance:self, already exists). Write the target
document and commit it on the working branch. Then admit the campaign
(slug, title, every repo a cut may touch, working_branch, target_doc = a
DocRef to that commit) and target revision 1 (labelled invariants, not in
scope, canonical implementations, doc = the same DocRef). Run the
substrate-gaps query in campaign-state.md, so the campaign starts knowing
which workarounds are in force.
Rule: for each operator answer, a ruling (answers, choice, authority, and
operator_quote when the words matter). A direction that answers no question is
a ruling with no answers, authority Operator, and the words verbatim in
operator_quote. A ruling's operator_quote and text carry only what the operator
said; Self's reading goes in a question, or is clearly marked as Self's (three
supersedes on 2026-10-07 came from text she never said). A changed ruling carries a resolution superseding the old one,
in the same batch.
Status, not only words: a ruling's text says whether her words decide,
illustrate or sketch. Examples she gives are examples, never a closed list, and
a remark made while thinking aloud is advice input, not a ruling at all. The
same holds for a campaign's title and a target: they name the problem, not her
first examples of it. (aetheria-fauna, 2026-10-13: her "krill, minnows, sharks,
whales" became the campaign title, the target and every brief, and the lore pass
wrote exactly those four. Operator: "I mentioned krill, minnows, whales and
sharks and that ended up being the whole ecosystem somehow".)
A creative or content pass (lore, design text, catalog content, art direction)
is briefed to develop and challenge: its inputs, her sketches and the existing
canon alike, are drafts. Its report ends with what it added beyond its inputs
and where it would change them, her ideas and existing content included.
Triage each open finding: a Hands brief, then a Fixed resolution admitted by
Self only after a Soul pass on the fix's report holds; a question raised in the
finding; a follow_up plus a Deferred resolution; or a Recorded or Withdrawn
resolution. A missing tool or surface a faculty worked around is a follow_up
owned by the missing thing's owner.
Before a Hands brief, check that the spec is not blocked by an open question
raised in it (campaign-state.md, specs with no report).
hand_off is parked (Huginn Cut 12): one mind, no transfer.
Imagination: settle identity, lifecycle and authority
Dispatch: subagent_type: imagination, model Opus.
Run this once, before any cut is mapped. It answers the questions that cause re-cuts, and it needs almost no source.
Migration: <migration>. Produce the model page in <prose map
path>. Do not map cuts, do not write code, do not commit; the root agent
commits.
Rehydrate (recipes: target in force, rulings in force, open questions and
follow-ups). Read enough of the Body to enumerate the persistent kinds. Orient
and probe through scripts, one call per question, output at most 150 lines.
Wait only through `tools/wait-on.sh` (never `true`, `echo` or `sleep`), write only
under `<scratchpad>/<agent>/`, and use `py -3` for Python.
Give one table, a row per persistent kind:
- Identity: what names it. Is the namespace stated? Is the name injective? Can
two different things collide? Is any part of it derived, and from what?
- Lifecycle: what happens to it over time. Created, revised, superseded,
withdrawn, reinstated, transferred, deleted, replayed. For each, what the
record looks like afterwards and what is now in force.
- Authority: who decides. One owner per decision. Name the forbidden writers.
Then admit, in one batch, a question for everything only the operator can
answer: options, the recommended label, and what depends on the answer.
An empty cell is the finding. Say so plainly rather than inventing a plausible
value. Report the cells you could not fill, the source you checked, and the
question ids you admitted.
Imagination: map or refresh a cut
Dispatch: subagent_type: imagination, model Opus.
Migration: <migration>. Admit a cut_spec for <the next cut, and at most a
sibling Self will run beside it> so Hands can go straight to the cut with
little reading. Map nothing further ahead (SKILL.md step 1, "Map only what is
about to run"): later cuts are one line each in the prose map's cut sequence.
Admit only cut_specs, their superseding resolutions, and questions. In the
prose map at <path>, edit body facts and rationale. Also edit the model page
whenever a probe changes a row's identity, lifecycle or authority. Imagination
wrote that page in 0b, and a model a cut contradicts is stale steering text.
Do not commit; the root agent commits. Do not change code in any repo. Scratch probes under
<scratchpad>/<agent>/ are fine. Orient and probe through scripts: one call per question,
output at most 150 lines. Wait only through `tools/wait-on.sh` (never `true`,
`echo` or `sleep`), write only under `<scratchpad>/<agent>/`, and use `py -3` for
Python. Budget 40-80 calls and about 250k context.
Rehydrate (recipes: target in force, rulings in force, open questions and
follow-ups, specs with no report, one cut's record for <cuts that constrain
this one>).
Body facts to verify (not trust):
- <repo@SHA>
- <tags>
- <API or behaviour claims>
Every mechanism claim must come from a probe or a source read, not a name.
Record each probe and its result in the prose map's body facts, and cite them
from the spec by DocRef rather than restating them.
The cut_spec's fields are the spec standard. Beyond filling them:
- file_changes against base <sha>, for code that exists
- file_changes are Hands' context pack. Each range is exact at base and is the
window Hands needs for that change, at most 100 lines (split a longer site). A
new file goes in adds, never as a line-1 anchor. Write no prose about lines
moving. After admitting, run `py -3 tools/context-pack.py <spec id> <worktree
at base>`: every anchor must read SAME and each window must show what its
change names, or admit a revision.
- cite in `rulings` every ruling in force the cut rests on
- the deploy owner: for any cut that changes what a service needs (transport,
env, dependency, state), the recipe, binding and runbook that must admit it,
changed in the same pass (Ghostlight, 2026-09-23)
- every seam with a foreign owner: the owner's code or published schema the
fixture is built from, not our own struct
- refreshing a spec: admit revision N+1 with a resolution superseding
revision N by it, in one batch; never a second spec for the same cut
If a cut would not fit one Hands pass under <Self's context budget, SKILL.md
step 3>, admit its first part as the spec, with its own verification, and add the
rest to the prose map's cut sequence.
Where only the operator can decide, admit a question with a recommended
option, raised in the spec it blocks.
Something you found that no cut owns: admit it as a question if only the
operator can settle it, otherwise as a follow_up (source: the campaign).
Report: the ids you admitted and the HEAD you pinned to.
Hands: execute a cut
Dispatch: subagent_type: hands, model Sonnet.
Cut: <cut>. The spec is <cut_spec id>. Rehydrate (no recipes:
view the spec and each ruling it cites, operator directions included). Follow
the spec; do not redesign it. If the Body contradicts it, fix the smallest
thing that keeps its intent true and record the discrepancy in the report's
deviations. If you hit a real operator fork, admit it as a question raised in
the spec, stop, and report. If that happens before your first commit, the pass
ends there: report the question id and its receipt, and admit no cut_report
(a report needs a commit).
Repo/branch: <repo> <branch> at HEAD <sha>. Check that git status is clean
first. Pinned siblings: <repo@sha>. Do not change them.
Before any grep or Read, expand the pack: `py -3 tools/context-pack.py <cut_spec
id> <worktree>`. An anchor that reads NEW for a path the spec does not create,
OUT_OF_RANGE, UNRESOLVED, or GONE outside deletes is a spec discrepancy: record
it in deviations.
When the brief names a worktree, every tool call's path goes under it: Read,
Edit and Write as well as the shell's working directory. An absolute path into
the main checkout silently edits the wrong tree.
Standing rules (the skill's own; the campaign's rulings are the ones the spec
cites, read by id):
- Gaps are filled in their owner, never with local helpers.
- Delete before adding. No shims.
- One rule, one path. A simpler case of the rule (one lane, one cell, an
empty set) runs through the general path, not a fast path beside it. If a
second path looks necessary, stop and report it; do not build it.
- An error, log line or refusal never echoes an input value, and that
includes a path, a URL or a line of the offending config. Name the field and
the error code. Test it with a canary value: no byte of the canary may appear.
Soul found this class twice on 2026-09-30: Heimdall's secret reader printed a
URL bound as a path, and TOML parse errors quote the whole `KEY = "value"` line.
Every rule the spec or the operator names gets a behavioural test. Measure the
suite with <the ecosystem's mutation tool>, scoped to this cut's diff
(`--since:<base>` or the tool's equivalent), against the final spelling of the
code. The report's mutations (at most 64) holds every survivor and the kills
Soul should rerun; put the totals (generated, caught, unviable, missed) in
verification evidence. Triage every survivor
by name and line: killed by a new test, killed by fixing a degenerate fixture,
or equivalent with a one-line reason. The mutation record has no field for that
reason, so each survivor also gets one deviations entry (what: its label; why:
the triage). A survivor that weakens a rule and cannot be killed says so there,
not hidden. Code no tool reaches gets behavioural tests at the layer where the
rule is decided, never a committed mutation suite (operator, 2026-09-22).
A fix for a proxy-for-truth defect is not done until every other site that
draws a conclusion from the same class of evidence has been enumerated and
ruled on in writing, including sites outside the repo. Name the inference, not
the line. And a change to how a test reports — an ignore, a filter, a skip, a
guard — is followed by running every command in the owning runbook verbatim and
reading what it prints: that surface is the one a test suite cannot check.
A test's inputs come from the production path the rule is about. A helper
that re-spells what production prints, parses or derives makes the test agree
with a copy, and the rule then breaks in production with the suite green.
A green run is not evidence until it shows a non-zero count for the tests you
meant to run. Paste the count for each named file. On StreamPixels, all three
of these produced a green that had run nothing, or the wrong thing:
- A `node --test` glob that matches no files exits 0.
- A package script's `--dir .` made vitest discover zero tests (`cc71ce2`).
- `pnpm --filter X test -- <files>` ran the whole suite instead of the named
files (`4624b53`).
Set environment variables only on the step that needs them. Exporting
`DATABASE_URL` for the whole job silently moves `app.test.ts` onto the
Postgres driver (`e019de0`).
"Pre-existing" is not a diagnosis. A failure that is not yours still gets a
one-line cause: what it depends on, and whether it has ever passed on a clean
checkout. Several agents waved a StreamPixels test through as pre-existing. It
read a git-ignored, licensed asset pack and had never passed on a clean
checkout (`27053f9`). Reproducing a failure at the base commit proves nothing
when both runs share the cause, for example a Windows file lock on the same
machine.
A hand mutation hits the rule at the layer it protects: the production call
site, observed where the consequence lands (disk, the kernel, the request
sent). Mutating a helper or an in-memory mirror proves the helper, not the
rule; a later write can hide the deleted one.
When a claim is "behaviour unchanged", a value captured from the new code is not
evidence. Pin it with a value computed at the base commit.
Detached scripts: confirm the log starts within 60 s; a script that dies on a
parse error is silent otherwise. Wait on one with `tools/wait-on.sh`.
Shared build caches: record a full path list before building, not only counts,
and delete exactly the new paths afterwards. Counts cannot attribute hardlinked
or rewritten outputs.
When a cut deletes, list every rule that had a test before and has none after.
A rule that still exists in code with its only test deleted is the failure mode
of subtraction; either the rule goes too, or it gets a test in the same cut.
Context is a budget, and a worker that fills it gets careless before it gets
stuck. Aetheria's stats and shield cuts ran Hands past 400k tokens and the late
work in those runs is where the sloppy claims appeared: a mutation reported
against code that had moved, a failure called environmental without a check, a
table printing numbers the old code never produced. Self sized this cut to fit
one head (SKILL.md, step 3); keep it fitting:
- **Verify once, at the end.** Not after every edit. A full mutation sweep and
a batchmode compile per edit is most of a long run's spend and proves nothing
the final sweep will not. One job carries the build, the scoped tests and the
mutation run, so the queue and the compile are paid once.
- **Orient in one script.** One Bash call prints git status, the base and the
pack (`tools/context-pack.py`). Independent
reads, views and greps go in one parallel message.
- **Bound every observation.** No command prints more than about 150 lines.
Logs go to a file; read `tail -n 60` or `grep -n -m`, and read windows of a
file, never the whole of anything over about 300 lines. After editing,
`py -3 tools/context-pack.py <cut_spec id> <worktree> --since <the HEAD you
started from>` reprints only the changed windows; re-read only files you
edited.
- **Read the spec by id, not the map.** The cut_spec is bounded and holds only
this cut; a worker that reads 900 lines of map to find 60 has spent its
budget before it starts.
- **Keep expensive scaffolding alive across cuts** (a pinned dependency
worktree, a warm build) rather than creating and removing it per pass.
- **Hand back rather than push through.** A worker that finds itself far past
the work it was briefed for commits what is verified, reports the remainder
honestly, and stops. A partial with clean evidence is worth more than a
complete report written from a full head.
"Far past" means the worker's context really is filling. It does not mean the
remaining scope looks large. Two Sonnet passes on the selection cut stopped at
about 150k tokens with no fork, one of them after fourteen tool calls. Each
said the remaining work "did not fit the budget". Both had been pointed at a
1,500-line map instead of given only their cut. So state the scope as one
deliverable, and say the budget is sufficient for it. The hand-back clause is
for a head that is actually full, not for a job that looks big.
- **Run every consuming test project, and report each count.** Before
reporting, list every test project that references an assembly, package or
crate the cut changed, found from the project files rather than from memory.
Run each one at the head, and put one evidence line per project in the
report: its name and its pass, fail and skip counts. "Did not build" is a
result and must appear as one. A project you could not run is listed in
`undone`. Scars from 2026-10-01 and 2026-10-02:
- An earlier Soul hold on C2a covered a range in which a Caching test was
already red.
- The R1 merge into C2a was reported green while GameCult.Mesh.Tests did not
compile at any commit of the range. That broken build hid a High finding:
Mesh's own zero-byte reader.
Long jobs: wait for them and finish, and never end your turn before your
report. Each turn end reaches Self as a notification and costs a read of its
whole context. Start the job as one background shell (run_in_background) with
`SUMMARY=<scratchpad>/<agent>/<job>.log` in front of `tools/stopgap/ygg-verify.sh`, a
fresh log path per job. Then wait in the foreground, one call at a time:
`tools/stopgap/ygg-verify.sh wait <scratchpad>/<agent>/<job>.log` blocks for at most
270 s (run it with a Bash tool `timeout` of at least 280000 ms: the default 120 s backgrounds the call and costs a wake), so your cache stays warm, and prints the bounded summary when the job is
done. If it prints `RUNNING <n>s` (exit 99), call it again. Exit 98 means the
launcher is gone with no verdict: rerun the job, do not guess its outcome. A call
longer than 5 minutes rewrites your whole context. Never run a second watcher
on the same job, never use a timer shorter than 60 s in a loop of your own, and
send no interim message.
Every other wait goes through `tools/wait-on.sh`, never a loop of your own.
Prefer `tools/wait-on.sh log <log> <done-regex>` with `WAIT_PID=<pid>` whenever
the job writes a done line (a build, a Unity batchmode run, a script that echoes
`DONE`): it is the only form that can tell success from a crash, because a dead
pid with no match is exit 98. `tools/wait-on.sh pid <pid> [<log>]` is only for a
process with no completion evidence; it prints `GONE (no completion evidence)`
and its exit 0 means "gone", not "succeeded". `tools/wait-on.sh file <path>` is
for an output file written last. The regex is an ERE; an invalid one is exit 3.
Exit 3 means the regex is invalid: fix it, never retry it unchanged.
Every job gets a fresh log path (put the job's own unique name or a timestamp in
it; for Unity, a new `-logFile` path per launch), and a waiter is never pointed
at a log a previous job wrote: a reused log's old done line reads as success at
once, and an offset cannot fix that because truncating writers (`>`, Unity
`-logFile`) rewrite below it. Same contract as the ygg-verify wait: pass a Bash tool `timeout`
of at least 280000 ms, it blocks at most 270 s, exit 0 is done with a bounded
log tail, `RUNNING <n>s` (exit 99) means call it again, exit 98 means the thing
is gone with no completion evidence. Bare `true`, `echo waiting` and `sleep`
calls are forbidden as polling: on 2026-10-07 and before, Hands and Soul runs
made 974 idle `true`/`echo` calls and 324 `sleep` calls (49 refused by the
harness), each one a model turn that waited on nothing.
Write only under your own scratchpad subdirectory, `<scratchpad>/<agent>/`:
a shared `job.sh` or probe file gets overwritten (one Soul clobbered `job.sh`
and `SoulProbe.cs`, 2026-10-07). `<agent>` is the name Self gives this dispatch,
unique among agents in flight, never the bare faculty name: two Hands both
writing under `hands/` overwrote each other's job scripts on 2026-10-09.
Python: use `py -3`, which works from Git Bash and PowerShell alike. Bare
`python` hit the Windows Store stub in 65 calls (2026-10-07).
Self pastes this wait protocol into a brief verbatim, never as a shortened
"wait mode": abbreviated, agents ended their turns mid-job three times on
2026-10-07.
Mutation tools run on schemata or copies and never edit the tree. A Soul
probe that mutates by hand works on a scratch copy: the stopgap's container
or a throwaway clone, never the working tree.
Warnings: measure from a forced rebuild and compare distinct messages; cargo
replays warnings only when it actually rebuilds.
Semantic properties ("exactly one call site", "this step actually runs"):
prefer a semantic tool (a Clippy lint, the type system) over a text scanner,
or state the scanner's limits in the test itself.
If you work around a missing tool, service or typed surface, say in the
report's deviations what was missing and what it would have prevented. The
workaround is not the finding; the gap is.
Commits:
- Small and pushed.
- Stage explicit paths only; never `git add -A`.
- Never amend a commit or force-push. Self and other agents commit on the
same branch while you work. Fix a commit with a new commit. On a rejected
push, `git pull --rebase`, then push.
- In Git Bash, one call: `git add <paths> && git commit -F- <<'MSG' ... MSG &&
git push`. Never commit from PowerShell (it writes a BOM and breaks
here-strings).
- End each message with the attribution trailer.
- Before every push, build and run the test project the commit touches. A
commit that does not build is never pushed; if a local commit could not
build, say so in its message and make the pushed head build.
Verification: <exact builds, tests, captures, negative greps>.
- No test run of any size on Starfire: a "quick" local suite counts. Only
Windows-only checks the brief names run there (py-msgpack-default h1 ran two
Python suites locally on 2026-10-12, red for Windows-only reasons).
- Verify through `tools/stopgap/ygg-verify.sh` with `SUMMARY=<log>` (above):
read the summary, and open the full log only at a named failing test, with
`grep -n -A 40`. Hand mutants go in one list to `tools/mutation-table.sh`,
one job for all of them.
- Stryker `--mutate` ranges are character offsets, not lines, into the file as
Stryker reads it: the bytes of the checkout the job runs in. Line endings there
follow `.gitattributes`, not the platform: CultLib's `packages/cultmath/src/**`
is `eol=crlf`, so even the Linux container checks it out with CRLF. Compute the
offsets inside the job from that checkout's file, never from `git show` (index
LF) or a different clone. Then check that the run reports mutants inside the
method you meant to measure. A run whose ranges hit nothing tests nothing, and
a green with zero mutants tested is not evidence (line-scoped runs tested
nothing, 2026-10-06; LF offsets on a CRLF file missed the code under test,
cut-bls-input-field.s3, 2026-10-11).
On Aetheria, Stryker.NET 5.0.0 has no test filter and runs the whole suite
per pass (about 86 minutes, modifier-subtypes 2026-10-09): budget one pass,
never a second to read a truncated tail (write its report to a file), and
give Soul hand mutants through `tools/mutation-table.sh` instead.
- Aetheria verify jobs set `DOCKER_ARGS='-v /home/gamecultadmin/eureka-verify/pins:/pins -v /home/gamecultadmin/eureka-verify/lfs-aetheria:/lfs:ro'` (jobs that read sibling repos add `-v /home/gamecultadmin/eureka-verify/repos:/repos:ro`). `/lfs` is content-addressed: for each LFS file the tests read (`GameData/Aetheria.cc`, `Assets/Resources/Settings.asset`), read the oid from the checked-out pointer and copy `/lfs/<oid>` over it; copying by path leaves the pointer and fails 200+ tests. Pass both pins to the build: `-p:CultLibRoot=` and `-p:CultMathRoot=` a `/pins` directory holding the sha Aetheria pins for each. From the CultMath 0.3 pin cut on (branch `eureka/aetheria-release-cultmath-0-3-pin` and everything after its merge) the revision guard runs on Linux and reads git, so both must be git clones, not exports: CultLib `/pins/cultlib-git-45c2f40`, CultMath `/pins/cultlib-614fd44`; an export pin fails at `VerifyCultLibRevision` with git exit 128 before compiling. Older branches use CultLib `/pins/cultlib-45c2f40` and CultMath `/pins/cultlib-6d5e209` (a CultMath pin lacking `erf` fails to compile). `ls /pins` in the job when unsure. The lot-product and emoji merges each lost three runs to this (2026-10-08); follow-up `aetheria-release:follow_up:aetheria-verify-job-script` replaces this recipe with a project-owned script.
- Revert incidental asset churn.
Budget: if the cut can't land coherently, stop at a pushed, building commit
boundary with no half-deleted authority, and put what remains in undone.
Don't edit the prose map.
Admit one cut_report for <cut_spec id>, attempt <n>, and no resolution of any
finding: Self closes a finding Fixed only after Soul holds the fix
(campaign-state.md). A fix batch is the next
attempt on the finding's in-force spec, on that spec's branch; if the fix
cannot land there, stop and report, because Self or Imagination must admit a
revised spec first. Its fields:
- commits (builds: false for any that don't build), and the range; the head
must be one of the commits
- verification: one evidence entry per build, test run and grep, with the
command as locator and the counts as result
- mutations: every survivor and the kills Soul should rerun (at most 64),
each with its exact edit (before and after) and whether it failed as
expected; totals go in verification, each survivor's triage in deviations
- deviations: spec discrepancies you fixed, each with why
- forks: the question ids you admitted
- in a fix batch, each finding id you addressed with the commit you believe
fixes it, as a labelled promise for Soul to measure
- structural_delta: lines, dependencies, formats and targets removed or added
- landed_names, undone, and promises: each thing you claim the cut now
guarantees, labelled, for Soul to measure
Report to Self, and nothing else: the cut_report id and receipt, and, for
output too long for an evidence line, the scratch path that holds it. No
narrative of the pass, no restating the brief or the report.
Soul: falsify executed work
Dispatch: subagent_type: soul, model Opus when the cut puts the foundation at risk, else Sonnet.
Cut: <cut>. Falsify the promises of the executed commits in the scope
below. Admit one verdict and its findings, and nothing else.
- Use a temporary detached worktree for other checkouts, and remove it
afterwards.
- Do not run <expensive runtime> unless told.
- Check the report's per-project test counts against the project files
yourself. A consuming test project missing from the report is a finding,
and you run it.
- Long jobs and waits: Self pastes the Hands brief's `Long jobs` block here
verbatim (the canonical wait protocol, including the scratch subdirectory and
`py -3` rules); do not reword or shorten it.
- Bound every observation: no command prints more than about 150 lines, logs go
to files, and you read windows of them.
Scope: <cut_report id>. Rehydrate (recipes: target in force, rulings in force,
one cut's record for <cut>), then view the report (its range and its promises), the cut_spec it
cites, and every ruling the spec cites. `py -3 tools/context-pack.py <cut_spec id>
<worktree at the report's head>` prints the cut's sites at that head. Admitting your verdict is a write to
the mind, not to a repo.
Hands' promises are one input, not the boundary of the pass. Falsify every
promise, every ruling the spec cites, every operator direction in force (even
one admitted after the spec; in the operator's own words), and every target
invariant the cut touches, and
specifically:
- <the load-bearing claim, and what would make it false>
- <where split authority could hide. Two paths that decide the same rule are
a defect whether or not they agree today, so report the split itself as
CONFIRMED and do not spend the pass proving the paths equivalent. Look for
a survey or check that picks between an "ordinary" and a "special" case of
one rule, and name the single path that replaces both>
- <which tests might pin spelling instead of behaviour; rerun N mutations,
including ones that are not plain reverts>
- <the layer where the invariant really fails: wire bytes, another runtime's
decoder, the editor, the compiler, a legacy file decoded independently>
- <for a sealed or private boundary: attack constructibility from an external
scratch crate or consumer, not path privacy. Try every public constructor the
language gives away: Deserialize and other derives, Default/From impls,
public fields, functions returning mutable references. A compile_fail test
pins a name; on stable rustc it does not even pin the error code>
- <for rejection tests: forgeries must be shaped like real values (same length,
prefix, case and alphabet), or a weakened comparison passes them all>
- <for any text scanner, grep assertion or line pin: try evasions (aliases,
function values, formatting, control flow around the pinned text such as
`if false` or `true ||`). A scanner is a tripwire with stated limits, not
proof of a semantic property>
- <leftover greps>
- <for every seam with another owner: build the fixture from that owner's code
or schema and prove both directions; a fixture that restates our struct can
only agree with its author>
- rerun the builds, tests and captures yourself, but pay for each one once.
Every verify job queues for a shared slot, so the number of jobs, not the
compile, is what makes a pass slow. While probing, run only the test
binaries or files the cut touches and the ones that name its rules. Run the
full suite exactly once, at the end. Put that run in the same job as your
mutation run where you can, since that job pays for the build anyway. Batch
probes into one job script rather than one job each. Run the mutation tool
in parallel (`--jobs 3`), and skip by name any test that a follow-up records
as flaky, so that its flakes cannot count a surviving mutant as killed.
Dump the range once (`git diff <base>..<head> > <scratchpad>/<agent>/range.diff` plus
`--stat`) and grep that file instead of re-running `git show` per file. Hand
mutants go in one list to `tools/mutation-table.sh`, never one job each.
Before recommending a mechanism as the fix direction, check that it was not
already built and deleted. Run `git log -S '<distinctive identifier>'` and
`git log --grep` on the owner's files, run the precedent recipe, and read the
scars in SKILL.md. If it
was tried, cite the commit that removed it and say why the new situation
differs, or recommend something else. (2026-09-30: a Soul pass recommended
writer-thread ordered delivery in CultCache. That scheduler was built in
`bcae483` and deleted in `4562340`, 34 minutes later, for deadlocking, and
Imagination re-proved the deadlock.)
Rerun the mutation tool on the range yourself; do not trust Hands' survivor
triage. Challenge each "equivalent" call that is not a float boundary flip:
measure it, as a mutant that changes how a thing is built can leave what it
does untouched, and one that looks cosmetic can move damage to the wrong side
of a ship.
Admit one verdict (pass <n>) and its findings in one batch:
- one claim per promise in the report, Holds, Falsified or Unproven, each with
its evidence (the numbers: test counts, entries killed, the survivors you
retriaged) and the report's mutation labels it reran
- one claim per ruling the spec cites and per operator direction in force,
its text starting with the ruling id (a claim has no ruling field), whether
or not Hands promised anything about it
- claims of your own for what you attacked beyond those
- each finding Confirmed or Plausible, with its locations (file:line), failure
scenario, severity, origin, and the target invariant labels it breaks. A
Falsified claim names a Confirmed finding.
Report to Self, and nothing else: the verdict id and receipt, and one sentence
on whether the cut closes. No narrative of the pass, no reasoning about mutants
that died.
<For a second or later pass on the same cut:> scope is the fix batch's
cut_report and its diff, plus one rerun of the suite. Do not re-derive the whole
cut unless an invariant moved.
Life: phase boundary
Dispatch: subagent_type: life, model Sonnet.
Phase boundary: <what landed, as cut_report ids>. The authoritative record is
the campaign's mind and the prose map and target doc; don't edit those, and
admit nothing. Rehydrate (recipes: rulings in force, open questions and
follow-ups) to check memory against the mind.
Surfaces: <memory dirs and files>. Budget at most 25 calls: inventory the
surfaces in one script, then make the edits.
Candidates for durable memory: <ruling ids, scars>. Check each against its
owner first, the mind included, and don't duplicate what the owner records. A
memory that restates a ruling or spec is a second copy: point at the id or
retire it. Retire or supersede stale
claims instead of adding corrections next to them.
Falsify at least one persisted claim against the Body.
Proving something is gone is the claim most likely to be wrong, because the
check that looks for it is usually a grep and prose reflows. A phrase split
across a line break, rewrapped, re-cased or re-spelled is still live steering
text and an exact search will not see it. Search for the shortest distinctive
fragment, search the concept and not the phrasing, and read the surrounding
text before reporting a removal.
Report: surfaces inspected, the claim you checked, mutations made, and proposals
for operator-owned surfaces. If you worked around a missing tool or surface, say
what was missing and what it would have prevented.
Eyes: gather evidence
Dispatch: subagent_type: eyes, model Sonnet.
Write findings to <scratchpad>/<agent>/<name>.md and return a
short summary. For a prior-art or literature pass, Self commits that file to
<repo>/docs/research/<topic>.md before the fork goes to the operator (SKILL.md
step 2, "Research is always kept"); write it to be read there. Budget at most 40 calls: transcripts by script, never whole
reads, waits only through `tools/wait-on.sh`, writes only under
`<scratchpad>/<agent>/`, Python as `py -3`, and fetches batched in parallel. You read the campaign's mind with query and view only, through
the Rehydrate block (recipes: <as needed>); you never admit.
Sources:
- <repos and ranges>
- <transcripts> (grep with targeted patterns; never read whole)
- <docs>
Produce facts with evidence pointers (SHA, file:line, transcript line). Mark
anything inferred as "(inferred)".