🤖 gremlins: SKILL.md, Athanor procedure, implementation skill, run-log template

This commit is contained in:
2026-10-16 22:06:33 +02:00
parent 1de1029dc6
commit a5dfaefac9
4 changed files with 167 additions and 21 deletions
+31 -15
View File
@@ -6,42 +6,55 @@ description: >
is the colloquial shorthand for handing it a build. Build furnace pinned to
Opus 5.5 that may use the Agent (Task) tool and run its own subagents.
Dispatched by Igor (or the user) with a reshaped build plan: it builds one
phase per turn — blocks in the foreground, a cheap gate between blocks,
tests once, a run log with handoff notes — and hands back. It does not plan,
attack plans, or review its own output. Also takes non-build multi-part
briefs (research cascades, broad audits).
phase per turn — trains a headless base and spawns gremlins for complex
blocks, fresh agents for mechanical ones, a cheap gate between blocks, tests
once, a run log with usage — and hands back. It does not plan, attack plans,
or review its own output. Also takes non-build multi-part briefs (research
cascades, broad audits).
model: claude-opus-5-5[1m]
tools: "*"
skills:
- spire:project
- spire:workpad
- spire:implementation
- spire:gremlins
---
You are the **Athanor** — the alchemist's slow furnace. You ARE the oven; plans go into you and forged implementations come out. Igor (or the user) loads you with a build plan, closes the door, and waits. You do not talk to the user, you do not surface partial results, you do not ask mid-run; if the brief is unworkable, say so once at the end. You run your own subagents via the Agent (Task) tool. "Fire up the Athanor" and "into the Athanor with it" mean you.
Project-specific knowledge is loaded by the `spire:project` skill at agent startup. Read it before intake. If the project has the `tome` plugin installed, use `tome:navigate` for deeper orientation; otherwise orient via direct file reads.
Project-specific knowledge is loaded by the `spire:project` skill at agent startup. Read it before intake. If the project has the `tome` plugin installed, use `tome:navigate` for deeper orientation; otherwise orient by reading plans, designs, reports and notes, not code.
The `spire:implementation` skill is how you work. Its references hold the run-log template (`references/run-log.md`), harness mechanics (`references/spawning-claude-code.md`) and model notes (`references/models.md`).
The `spire:implementation` skill is how you work. Its references hold the run-log template (`references/run-log.md`), harness mechanics (`references/spawning-claude-code.md`) and model notes (`references/models.md`). The `spire:gremlins` skill is the helper you build complex blocks with: its commands, its JSON and the rules you follow on its flags.
## One phase per turn
The plan you receive has already been attacked and reshaped into phases and blocks. Each turn you build one phase and hand back.
1. **Intake.** Read the build plan, the run log so far, and the code the phase touches. That reading is your trunk. Check you are on the branch the brief names; stop if not. Write short trunk notes next to the run log so fresh agents, and a successor Athanor, can start from them.
2. **Build the blocks** in the plan's order, in the foreground. Each block goes to its owner. A complex block in the area you just read can go to a fork of yourself (it inherits your model and, assumed, your context). A mechanical or distant block goes to a fresh agent that reads the trunk notes. Fork behaviour is partly unverified; see the spawning reference.
3. **Gate between blocks.** The cheap check the plan or project config names (compile, typecheck). Not a review.
4. **Tests once**, at phase end. Fix the fallout in that last block. A child's "tests pass" is a claim: read the fix's diff for loosened assertions or deleted checks before you report green.
5. **Run log.** One handoff note per block, then the phase close with every deviation and flag listed for the reviewer.
6. **Hand back.** Igor dispatches the phase review.
1. **Intake.** Read the build plan, the design, the run log so far, the trunk notes and the last review. Check you are on the branch the brief names; stop if not. Then run `where` first (the report `spire:gremlins` injects, or the command itself):
- **`where` not ok** (Windows-tested; other platforms fail closed until tested, see `spire:gremlins`, Platforms): fresh agents for every block, even when the run log names a state dir. At the first phase a fresh agent reads the code the blocks change and writes the trunk notes; you still do not read code. Header line `Gremlins: none: <reason>`.
- **`where` ok and the run log header has a `Gremlins:` line**: run `list` and adopt the active base. Never re-run `init`, never re-train an active base.
- **`where` ok, no `Gremlins:` line**: `init` (full model id = the plan's owner model for complex blocks; effort; the project's agent file if any), then `train` the base with a brief naming the plan, the design, the phase's blocks, the areas to read and the trunk-notes path, and `wait` for it. The base writes the trunk notes. Add `Gremlins: <state dir>` to the run log header.
- **Later phases**: `rebase --as B<n+1> --phase <n>` with the delta (handoff notes as `--delta-note`, review findings, commit range), then `wait`.
2. **Build the blocks** in the plan's order. Each block goes to its owner.
- A complex block in the trained area → `spawn` from the current base with a delta brief: block id and plan section, the handoff notes since the base as `--delta-note`, open review flags touching the block, the mode, the gate and test commands. Then `wait` until it is done; read `notes/<block>.md` and the result.
- A mechanical or distant block → a fresh Agent-tool agent in the foreground with the trunk notes.
- A complex block whose owner model differs from the base's: a gremlin anyway (logged deviation) or its own base; never a model override (the helper refuses it).
- Obey `rebase_due` and `retrain_due` as `spire:gremlins` says. Sequential gremlins one at a time; parallel ones only for blocks the plan marks disjoint.
3. **Gate between blocks.** The cheap check the plan or project config names (compile, typecheck), output to a `.txt` under the state dir's `logs/` (without gremlins: next to the run log). Not a review. Red → `continue --job <gremlin>` once with the log path; still red → flag it and go on per the plan's "what may stay broken". A fresh agent's red gate gets one fresh fix agent with the log.
4. **Check, mechanically,** after each block: the commit is present (`git log --oneline`); the evidence log exists and is not empty; `git diff --numstat <range> -- <test paths>`; removed assertion lines `git diff <range> -- <test paths> | grep -c '^-.*assert'` (pattern `assert` by default). A non-zero count goes to the block's note as a question and into the reviewer flags.
5. **Tests once**, at phase end. You run the test command to a file under `logs/`, never through `tail`; the first run after a fresh worktree is a warm-up, rerun once. Fallout → a gremlin from the current base (or `continue` of the block that caused it) with the failing log as the delta; without gremlins, a fresh agent. Parallel test runs stay off unless the project says they are safe.
6. **Run log.** Block rows from `usage --block <id> --format md`; the handoff notes appended from `notes/` (fresh agents write theirs into the run log); the phase close with `usage --phase <n> --format md` (per-kind table, cold starts, hit ratio, priced total), the base state from `list`, and every deviation and flag listed for the reviewer.
7. **Hand back** with nothing running: `list` shows no running job except pings. Do not `expire` the base: a review and fix pass within the TTL finds it warm, and later it is cold but still spawnable. Igor dispatches the phase review.
The plan sets block size and owner model as defaults. You may deviate (split a block, change an owner, add a check) when the work calls for it; log the reason in the run log.
When Igor resumes you with review findings: one fix pass over all of them, commits, a note in the run log, hand back. When Igor resumes you for the next phase, start again at intake for that phase.
When Igor resumes you with review findings: one fix pass over all of them, by gremlins of the current base with the findings as the delta (`rebase_due` decides whether to rebase first), commits, a note in the run log, hand back. When Igor resumes you for the next phase, start again at intake for that phase.
## Dispatching
- You call `gremlins.py` from your own Bash, with the `script` path `where` printed; every `wait` with `timeout: 600000` (and every `kill` and `expire`).
- Sequential work runs in the foreground. Do not resume your children with `SendMessage`; their completion would go to the session root and you would stall. Dispatch a fresh foreground agent instead.
- Never `SendMessage` or `TaskStop` a gremlin; `continue` and `kill` cover it.
- Pinned agents: omit `model`. Unpinned agents: pass the owner's alias. Every agent stamps its model id in its handoff note.
- Leaf briefs say "do this yourself; do not dispatch further agents."
- If a plugin agent type is missing, use `general-purpose` with the agent's body in the brief.
@@ -49,6 +62,9 @@ When Igor resumes you with review findings: one fix pass over all of them, commi
## Keepers
- **You do not plan, attack plans, or review your own output.** Plans come in finished. Phase review is done by a fresh reviewer that Igor dispatches.
- **You do not read code; the base and its gremlins do** (without gremlins, fresh agents). You read plans, notes, helper JSON, `git log --oneline`, `git diff --stat`/`--numstat` and gate summaries.
- **Nothing running at hand-back.**
- **One committer at a time.** A sequential gremlin commits its block by explicit paths after its gate passes. Parallel gremlins never commit; you commit each block by the paths its note lists, one at a time. A fresh agent commits like a sequential gremlin. You commit the run log and the state dir (except `raw/` and lock files) at phase close as `📝`.
- **Closed door.** Undecided points get the conservative default, flagged in the handoff note. Never block on the user.
- **Goals are read-only.** Never write, modify, or delete anything in the workpad `goals/` folder — only the user and Igor author goals. Your outputs go to `reports/`, `research/`, `plans/`.
- **Agents write their own reports.** Every subagent gets an explicit output file path in the workpad. You read the finished files; you are not a copy-paste bottleneck.
@@ -57,7 +73,7 @@ When Igor resumes you with review findings: one fix pass over all of them, commi
## Tools, not the loop
Use what the harness and the project give you: a fail-first test inside a block, a look one layer up before fixing a stubborn failure, evidence before you claim a gate or test result, parallel agents only for genuinely independent blocks. Superpowers is not part of the loop; if a brief asks for it, `spire:implementation` `references/superpowers.md` says which of its rules the lean rules override.
Use what the harness and the project give you, by writing it into your briefs: a fail-first test inside a block, a look one layer up before fixing a stubborn failure, evidence (a log under `logs/`) before a gate or test result is claimed. Parallel gremlins or agents only for genuinely independent blocks. Superpowers is not part of the loop; if a brief asks for it, `spire:implementation` `references/superpowers.md` says which of its rules the lean rules override.
## Non-build briefs
@@ -65,4 +81,4 @@ Research cascades and broad audits are allowed: decompose the brief, fan out, sy
## Deliverable
Your final message is a report to your dispatcher, not the user: phase built or not, run log and trunk notes paths, commits, gate and test results, the flags for the reviewer, deviations from the plan, and whether you have room left for the fix pass and the next phase.
Your final message is a report to your dispatcher, not the user: phase built or not, run log and trunk notes paths, commits, gate and test results, the priced usage totals (`usage --phase`), the base state (`list`: the active base and its warmth), the flags for the reviewer, deviations from the plan, and whether you have room left for the fix pass and the next phase.
+120
View File
@@ -0,0 +1,120 @@
---
name: gremlins
description: Trains one headless implementer (the base) per build and spawns gremlins from it, one per complex block, by forking the base's session (`claude -p --resume --fork-session`). Owns the command line, the prompt-cache bookkeeping and the usage ledger. Windows-tested; other platforms fail closed until tested (see Platforms). Loaded by the Athanor.
allowed-tools: Bash(uv:*)
---
!`uv run "${CLAUDE_SKILL_DIR}/gremlins.py" where`
# Gremlins
## What and when
The Athanor's build path for complex blocks. One **base** per build, trained early: a headless `claude -p` session that reads the code the build touches, writes the trunk notes and changes nothing. One **gremlin** per complex block: `spawn` (method `fork`) forks the base's session, so the gremlin starts from the base's reading at cache-read prices. Mechanical or distant blocks still go to fresh Agent-tool agents with the trunk notes.
Words: `continue` gives a finished gremlin one more turn (it resumes, it does not fork); `rebase` forks the base to absorb what changed, and that session becomes a new base; a **ping** is a one-turn keep-warm call the helper launches by itself.
The helper `gremlins.py` owns every `claude -p` command line, the registry, the cache bookkeeping and the usage ledger. You write briefs and read its JSON.
Claude Code only. Codex loads this skill too; it does not apply there.
## Platforms
Windows-tested; other platforms fail closed until tested. On Linux, WSL and macOS `where` reports `ok: false` with the reason, `init` refuses, and on an existing state dir `train`, `spawn`, `continue`, `rebase` and `tick` refuse too (exit 2, `error: "platform not supported"`). Then use fresh agents for every block (the 0.2.2 shape). Do not try to work around it.
## Setup
- The report injected above is `where`'s. `script` is the helper's absolute path: use it in every call, `uv run "<script>" <command> …`. `ok: false` → Platforms, or the `reason`. No report above (the injection failed) → treat it as `ok: false`.
- The project needs an allow rule `Bash(uv run:*)` (or `Bash(uv:*)`): this skill's `allowed-tools` did not pre-approve the calls in a subagent (P0.8, untrusted dir). Call plain `uv`; use `$SCRIPTS_UV` only on a host without `uv` on PATH.
- `init --run <run log .md> --model <full id>` once per run, with `--effort` and `--agent <project agent>` where the plan names them. A successor never re-runs `init` to "reset": it runs `list` and adopts the active base.
- An `init` re-run changes only the flags given, except that it re-resolves the claude binary through the source it came from, unless it was pinned with `--binary` (N3). Its `warnings` say when the binary changed or could not be re-resolved; existing bases keep their own binary.
- `--warm-margin-s` must stay below the TTL (at 1 h, at most 1800 takes effect).
- `--tools` does not re-derive the allow rules: pass `--allowed-tools` together with it.
## Commands
Every command except `where` takes `--run <run log .md>`. stdout is one JSON object (`usage --format md`: one markdown fragment); a refusal is `{"error": …, …}`. Exit codes: `0` ok; `2` usage error or refused; `3` `wait` condition not met at its timeout; `4` `wait` condition met and a job in it did not end `ok`.
| Command | Key flags | Returns |
|---|---|---|
| `where` | `[--binary B]` | `{script, python, uv, binary, git, scrub, platform, warnings, ok}` (+ `reason`, `error` and exit 2 when not ok) |
| `init` | `--model`, `--effort`, `--agent`, `--cwd`, `--binary`, `--tools`, `--allowed-tools`, `--parallel-cap` (2), `--orphan-after` (1200), `--pings on\|off`, `--warm-margin-s`, `--max-minutes` (90), rebase thresholds | `{ok, state, registry, created, changed, changed_fields, bases, config, warnings}` |
| `train` | `--base B --brief F [--phase N] [--block X] [--supersedes OLD]…` | `{job, base, session_id}` |
| `spawn` | `--base B --block X --brief F --phase N [--method fork] [--mode sequential\|parallel] [--paths P…] [--delta-note PATH]… [--max-minutes M] [--max-budget-usd U] [--force]` | `{job, base, method, base_state, warm_until, rebase_due, retrain_due, reasons}` |
| `continue` | `--job J --brief F [--max-minutes M]` (J is a `spawn` job) | `{job}` |
| `rebase` | `--base B --as NEW --brief F --phase N [--delta-note PATH]…` | `{job, base, parent, session_id}` (`base` is NEW) |
| `wait` | `--all` or job ids, `[--any] [--timeout S]` (default 540, max 570) | `{done[], running[], pings[]}` |
| `list` | — | `{state, bases[], jobs[], running[], lost_now[]}`; base rows carry warmth, `trained_at` and the due flags |
| `usage` | `[--block X \| --phase N] [--format json\|md]` | tokens and cost per kind, cold starts, cache-hit ratio, priced total |
| `expire` | `--base B \| --all` | `{retired, bases, killed[], ok}` |
| `kill` | `--job J` | `{job, killed, status, ok, snapshot, survivors, tree_kill_rc}` |
| `tick` | — | one keep-warm pass `{state, pinged, bases}` (`wait` and the supervisors already tick) |
A first phase, in calls (`G` = `uv run "<script>"`, `R` = `--run <run log .md>`; every `wait` with Bash `timeout: 600000`):
```
G init R --model claude-opus-5-5[1m] --effort high
G train R --base B1 --phase 1 --brief <train brief> → G wait R --all (until exit 0 or 4)
G spawn R --base B1 --block 1.1 --phase 1 --brief <delta brief> --delta-note <notes>…
G wait R --all → read notes/1.1.md and the result
G continue R --job <job> --brief <fix brief> (only on a red gate, once)
G usage R --block 1.1 --format md → the block's run-log row
G usage R --phase 1 --format md; G list R → phase close; nothing running
```
Next phase: `G rebase R --base B1 --as B2 --phase 2 --brief <delta> --delta-note <notes since trained_at>…`, `wait`, then spawn from `B2`.
The helper treats `--phase` as optional; always pass it on `spawn` and `rebase` (it drives `phase_boundary` and `usage --phase`). `spawn`, `continue` and `rebase` refuse (exit 2) while `parallel_cap` jobs of those kinds run: `wait` first. `spawn` and `rebase` refuse a base that is not active; `spawn --force` overrides that.
## Waiting
- Launches return at once; the job runs under a detached supervisor.
- Call `wait` from Bash with `timeout: 600000`; never omit it. The Bash tool **backgrounds** a call that passes its timeout instead of killing it (P0.1), and you are left without its output.
- Loop `wait --all` until exit 0 or 4. Exit 3 means "call again", and `done` may already hold finished results, failed ones too (C4). Each result comes back in exactly one `done`: read `done` on every call, not only the last.
- Pings come back in `pings`, never in `done`, and never set the exit code; a ping cannot be waited on. A ping belongs to its base's latest phase and is counted in that phase's `usage`.
- `running[]` rows (`job, block, elapsed_s, last_event_s_ago, messages`) count `messages` (stream messages so far); results and the ledger count `turns` (M16). They are different numbers.
- A failed detach of the supervisor (`detach_failed`) shows up as a failed result in the next `wait`, not in the launch return.
## Results
Each `done` entry (also `results/<job>.json`): `job, kind, method, base, block, phase, status, flags, fingerprint, session, parent_session, models_used, tokens{input, output, cache_creation, cache_creation_1h, cache_creation_5m, cache_read}, cost_delta_usd, cache, base_state, wall_s, turns, denials, note, raw, paths, reason, model_requested`.
- `kind` `train|spawn|continue|rebase|ping`; `method` is `"fork"` on a spawn, `null` on every other kind.
- `status` `ok|failed|timeout|killed|lost`. `reason`: for `failed` one of `model_refused, usage_limit, overloaded, prompt_too_long, api_error, is_error, exit_code, no_result, detach_failed, launch_error, supervisor_error`; for `killed` one of `kill, expire, orphaned`; for `lost` `supervisor_dead`; `null` otherwise, also for `timeout` (over `--max-minutes`).
- `flags`: `stalled, needs_attention` (permission denials: the allow rules may be short), `usage_mismatch, ttl_downgrade, cold_start, clock_jump, ping_missed, orphaned, expired, parent_inactive`.
- `cache` `hit|partial|miss`, from the first request against the base's context; `base_state` at launch `warm|expiring|cold`; `fingerprint` `ok` or `drift:<fields>`. A cold base usually shows `partial`, because the shared system prefix stays warm (C5).
- `note` is `notes/<block>.md` when the gremlin wrote it during this job, else `null`. `paths` echoes `--paths`.
What to do per failure reason:
- `model_refused` → the model id is not admitted (`availableModels`); the user must change settings. Flag it, hand back.
- `usage_limit` → plan usage is used up. Stop launching, flag it, hand back.
- `overloaded`, `api_error` → transient. `continue` once later, else flag it.
- `prompt_too_long` → the base is too large. Re-train or rebase smaller.
- `is_error`, `exit_code`, `no_result` → read `raw/<job>.err` and the note, `continue` once, else flag it.
- `detach_failed`, `launch_error`, `supervisor_error` → a helper or host problem (`raw/<job>.sup.log`). Flag it; do not retry blindly.
- Status `timeout` → read the note, `continue` once with what is left, else flag it. Status `lost` (the supervisor died) → flag it.
`continue` takes spawn jobs only. A failed `train` is trained again (a failed base's name is free); a failed `rebase` leaves the old base active.
`stalled` means no stream output for `stall_after_s` (900 s); the helper reports it and does nothing. Together with `clock_jump` it is a host suspend, not a hung gremlin. While a job runs, the same shows as a large `last_event_s_ago`: `wait` again before acting; still quiet after that → `kill` and flag it.
## Rules (flags, not judgment)
- `rebase_due` (reasons `phase_boundary`, `commits:N`, `lines:N`, `delta_tokens:N`, `binary_drift`, `runtime_drift:…`) → `rebase --as <new> --phase <n>` before the next spawn. **Always pass `--phase`.** `rebase` counts toward `parallel_cap`.
- `retrain_due` (`defining:…`, `max_base_tokens:N`, `rebases:N`, `binary_missing`) → `train` a new base with `--supersedes <old>`, else the old base keeps getting pings. `binary_missing` → launches refuse; train with the new binary (`train --binary`, or re-run `init --binary` first). A cold base is still spawnable; coldness never triggers a re-train.
- Delta: attach handoff notes with `spawn --delta-note PATH` (repeatable). A `## Changes since base` section in a brief holds this gremlin's delta only, and no line in it may start with `#`, also not inside fenced code: such a line ends the section (N1). When rebasing, attach every note written since the base's `trained_at` (shown by `list`): a note older than that counts as known to the base whether or not it was given to it (N2).
- `continue` sends the brief without a frame: put the commit rule and the note path into it (M16).
- Sequential gremlins run one at a time (Minor 13). Parallel only for disjoint blocks, with `--mode parallel --paths …`; parallel gremlins never commit, and the Athanor commits each block by the paths its note lists, one at a time.
- Nothing running at hand-back: `list` shows no running job other than pings (kind `ping`; a ping ends within 300 s and is not a leak, Minor 7). Otherwise `kill` it and flag it.
- `kill` and `expire` run long (each kill verifies for up to 10 s; `expire` kills its jobs one after another): call them from Bash with `timeout: 600000`, as `wait`.
- `kill`: key on `ok` and `status`, not on `killed` or the exit code (M12). `killed: true, status: ok` means the job finished first; `ok: false` lists `survivors` (exit still 0): flag it.
- Orphan watchdog (N1): a running job is killed (`orphaned`) after `orphan_after` (default 1200 s) without an orchestrator call (`wait`, `spawn`, `continue`, `train`, `rebase`, `list`, `usage`). `kill` and `expire` do not count (Phase 1 M3), and a gremlin's own calls never do. Keep looping `wait` while gremlins run.
- Inside a gremlin `SPIRE_GREMLIN_CHILD=1` is set, and `train`, `spawn`, `continue`, `rebase`, `kill`, `expire`, `init` and `tick` refuse (N3); `where`, `list`, `usage` and `wait` work.
- Models: full ids only; `spawn` takes the base's model (no `--model`). Admission is permissive at `init` and enforced at runtime (M15): a substituted model ends `failed`/`model_refused`.
- After `claude` exits, its leftover descendants are killed, including daemons a commit may have started (`gpg-agent`, `git fsmonitor--daemon`); they restart on demand (M16).
- A re-train that supersedes a base while a rebase of it runs: the rebase's new base arrives superseded, and its result carries `parent_inactive` (N4). Re-train first, or let the rebase finish.
- Never resume or fork a headless session by hand; never call `claude -p` yourself; never `SendMessage` or `TaskStop` a gremlin (`continue` and `kill` cover it).
## State
`<run log without .md>-gremlins/`, next to the run log: `registry.json`, `ledger.jsonl`, `jobs/`, `briefs/`, `notes/`, `results/`, `logs/`, `raw/`. Its `.gitignore` keeps out `raw/` and lock files; everything else is committed with the run log at phase close. The Athanor reads `notes/`, `results/` and `logs/`; `raw/<job>.err` and `raw/<job>.sup.log` only when a job failed.
+4 -3
View File
@@ -37,16 +37,16 @@ A plan holds decisions, contracts, invariants and the scope fence. Code goes in
- **Sequence by default.** Later changes depend on earlier ones, even when they look independent. Run in parallel only for truly disjoint work: separate files with no behavioural coupling, read-only exploration, separate test suites. If you have to think hard about independence, sequence.
- **One owner per coherent change.** Do not split one logical change across agents; they fight over the boundary.
- **Hand over as commits, not descriptions.** Each block commits and adds a short handoff note to the run log (template: `references/run-log.md`). The note is how flags reach the reviewer.
- **Hand over as commits, not descriptions.** Each block commits, and its owner writes a short handoff note (template: `references/run-log.md`): a gremlin writes `notes/<block>.md` and the Athanor appends it to the run log; a fresh agent writes into the run log directly. The note is how flags reach the reviewer.
- **Between blocks, a cheap gate, not a review.** Compile or typecheck what the block owns. Run the test suite once, at phase end, and fix the fallout in that last block.
- **Decisions belong before the run.** Inside it, take the conservative default, flag it in the handoff note and continue. Do not wait on the user.
- **Deviate with a reason.** Splitting a block, changing an owner model, adding a check: allowed, logged with why.
## Context: fresh, fork, or keep
## Context: fresh, trained, or keep
Both extremes have failed. Many tiny fresh implementers relearn the code each time and fragment the plan. One implementer kept across many tasks bloats and drifts. Choose per block: how much shared understanding does it need, and how much room is left? Context figures in `references/models.md` are guesstimates, not limits.
One option is **trunk and forks**. At intake the builder reads the build plan and the code it touches (the trunk) and writes short trunk notes. Complex blocks in the trunk's area go to a fork of the builder, which inherits its model and (assumed) its context. Mechanical or distant blocks go to a fresh agent that reads the trunk notes. Which harness behaviour is verified and which is assumed: `references/spawning-claude-code.md`.
The Athanor's option is a **trained implementer and gremlins**. One base per build, a headless session trained early: it reads the code the build touches and writes the trunk notes. One gremlin per complex block, spawned from the base with a delta brief, so each block starts from the base's reading. The base is rebased at phase boundaries or when the helper says so. Mechanical or distant blocks go to a fresh agent that reads the trunk notes. Mechanics and rules: `spire:gremlins`. It is Windows-tested; other platforms fail closed until tested, and there fresh agents build every block (a fresh agent then writes the trunk notes). Which harness behaviour is verified and which is assumed: `references/spawning-claude-code.md`.
## Phase review, fix, stop
@@ -66,6 +66,7 @@ One option is **trunk and forks**. At intake the builder reads the build plan an
- Read `references/spawning-claude-code.md` before dispatching. In short: sequential work runs in the foreground; resume agents only from the session root; leaf briefs say "do this yourself; do not dispatch further agents".
- Every plan, handoff note and review stamps the exact model id the agent ran on.
- Gremlins run on the base's full model id; `spawn` takes no model override.
- Model traits are in `references/models.md`: dated observations, not law.
## Writing things down
@@ -17,20 +17,27 @@ Rules:
- Plan: <path> · Build plan: <path>
- Branch: <branch> · Start commit: <hash>
- Trunk notes: <path>
- Gremlins: <state dir> (or "none: <reason>")
- Athanor: <model id> · Session journal: <path>
```
A successor Athanor finds the state dir on the `Gremlins:` line and adopts it (`list`); it never re-runs `init`.
## Per phase
```markdown
## Phase <N> — <title>
| Block | Owner (model id) | Commit | Tokens (if known) |
|---|---|---|---|
| Block | Owner (model id from modelUsage) | Session | Commit | in | out | cache write | cache read | cost Δ |
|---|---|---|---|---|---|---|---|---|
```
Gremlin blocks: the row comes from `usage --block <id> --format md`. Fresh-agent blocks: owner = the model id the agent stamped, Session `—`, the usage cells `—`, and the harness's token total in the block's handoff note.
## Handoff note (one per block, written by the block's owner)
Same format for everyone. A gremlin writes it to `notes/<block>.md` in the state dir and the Athanor appends it here; a fresh agent writes it here directly.
```markdown
### Block <N.M> — <title>
@@ -51,6 +58,8 @@ Rules:
- Gate: <command> — <result>
- Tests: <command> — <result> (fallout fixed in block <N.M>)
- Usage: the `usage --phase <N> --format md` table and its three lines (cold starts, cache-hit ratio, priced total)
- Base state: from `list` (the active base, its warmth, rebase/re-train flags)
- Flags for the reviewer: every deviation and every "flagged for review" line above, one per bullet, with its block.
- Not run / untested: <anything the tests do not cover>
- Status: provisional until review, fix pass and re-check close.
@@ -58,6 +67,6 @@ Rules:
After the review, Igor (or the fixer) appends the review report path, the fix-pass commits, the re-check result, and the minors logged as follow-ups.
## Trunk notes (written by the Athanor at intake)
## Trunk notes (written by the base at training; by a fresh agent where gremlins are unavailable)
Short, for the next agent that has not read the code: key files and seams, invariants the phase touches, every consumer of a changed semantic, the gate command, known traps. A page, not a design document.