Eureka/references/tool-calling-cut.md
2026-10-12 03:16:36 +02:00

454 lines
29 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Tool-calling cut map (Imagination, 2026-10-01)
A dated map. Self's rotation threshold is owned by the `SKILL.md` rotation
bullet (about 400k since 2026-10-12); the Self figures below are as of
2026-10-01. The Hands and Soul caps below still stand.
Target: fewer model calls, and less context re-read by each call, with the
Soul loop untouched. Pinned to skill HEAD `92bf6f7`. Eyes' facts are in the
session scratchpad (`eyes-tool-calling-facts.md`). The classifier scripts are
`tc_index.py`, `tc_classify.py` and `tc_comp.py` in the same scratchpad, and
every number below can be rerun from them.
## 0. The cost model (measured)
Every model call re-reads the whole context. A subagent starts at **~70k**
tokens: about 34k shared system prompt and tools, plus about 36k written per
agent (CLAUDE.md files, brief and listings). It then grows by a nearly constant
**~1.7-1.9k tokens per call**. In the two largest Hands there was no single
jump over 15k: affba1c4 went 69k→742k over 386 calls, and a0a3154a went
70k→781k over 332. So for an agent of N calls:
reads ≈ N·70k + 0.9k·N²
For a 390-call Hands the N² term is ~85% of its 163M. **Call count is the
lever, and it acts quadratically.** Halving a long agent's calls cuts its
reads by about 3x. Splitting it in two at the midpoint cuts them by about 40%.
The **5-minute subagent cache TTL is real.** Measured over all subagent calls
since 09-29:
| gap since previous call | calls | cache write | avg write |
|---|---|---|---|
| < 60 s | 25,232 | 56M | 2k |
| 60-300 s | 2,336 | 5M | 2k |
| **300-3600 s** | **1,241** | **292M** | **235k** |
| > 1 h | 10 | 2.5M | 249k |
79% of all subagent cache writes come from the 4% of calls that follow an idle
gap of more than 5 minutes. Each of those rewrites the agent's whole context at
1.25x the base price, where a cache read costs 0.05-0.1x. The quota weighting
of reads against writes on a subscription is unpublished, and Eyes flagged it
as open. In API-price terms, these rewrites are roughly a third of subagent
input cost.
## 1. Waste taxonomy (measured, 09-29 → 10-01)
### Self (root), 09-29/30, deduplicated by message id
- 2,135 model calls, 984M context read, median context about 500k, peak 924k.
- **1,703 of the 2,135 calls (80%) emitted no tool call.** 1,035 of them had
no text at all: Self woke up, read about 500k, and ended the turn.
- 1,324 text-only calls followed a task notification. They cost **596M, 61% of
root reads.**
- Root received 602 "Agent … finished" notifications from **245** agents.
**68 agents finished more than once**, giving 357 interim notifications: one
Hands 29 times, two Souls 21 times each. Docs: a background subagent's
results reach Claude "as a completion notification", and a subagent's own
background command notifies the subagent when it ends. Measured: every
interim turn end of a subagent reached the root as a finished notification.
**The pattern in `92bf6f7` ("end the turn with the waiter running and say
nothing") is exactly this pattern.** It moves the wait's cost from the
subagent (~200k context) to Self (~500-900k).
- Agent results pasted into root: median 203 chars, p90 7.7k chars, about 404k
tokens over two days. The p90 is the "raw verification output, pasted"
clause in the Hands report.
### Hands: 167 agents, 16,863 calls, 3.79B read
| class | calls | share of reads | what it is |
|---|---|---|---|
| search (Bash grep/find/rg) | 21.2% | 19.0% | Mostly legitimate orientation. Avg result 700 tok. |
| **wait.poll** (foreground `until/for … sleep`) | **11.5%** | **14.9%** | e.g. `for i in 1..18; do grep …; sleep 20; done; tail -60`, repeated per job |
| **verify.read_log** (tail/cat/grep a job log) | **9.0%** | **10.2%** | reading raw logs after or between polls |
| read.file | 12.3% | 9.9% | avg 1.9k tok per result, the largest growth source |
| edit (Edit/Write) | 9.4% | 10.2% | necessary |
| verify.launch | 8.6% | 8.6% | 8.6 jobs per agent, often one per probe or mutant |
| git.read | 9.3% | 7.9% | status, log, diff, show |
| **edit.script** (python heredoc editors) | **6.5%** | **7.3%** | each agent re-invents a CRLF/BOM-preserving `def edit(p,f)`; its inputs were the largest context item in 3 of 5 heavy Hands (39-56k tok) |
| git.write | 2.4% | 2.5% | add/commit/push, plus Write for the message file and a `git log -1` check |
| wait.monitor + wait.stop + ssh.inspect | 3.8% | 4.1% | Monitor arms, TaskStop, ssh docker/ls checks |
- Waiting and observing (poll, read_log, monitor, stop) took **3,852 calls
against 1,443 launches: 2.7 observation calls per job.**
- Exact duplicate calls: 1,564 (9.3%), mostly re-polls.
- **Ad-hoc harnesses were reinvented per agent:** `runmut.sh`, `runprobe.sh`,
`harness.sh`, `fulljob.sh`, `mutjob.sh`, `mutrun.sh`, `strykrun.sh` and
`scriptrun.sh` appear across the sampled Hands and Souls. They are the same
three jobs (verify, mutate, probe), written fresh each time.
### Soul: 146 agents, 8,752 calls, 1.40B read
- search 23.4%, **git.read 21.2%** (avg result 1.1k: the range diff re-read
piecemeal), read.file 12.4%.
- Waiting and observing: wait.poll 9.3%, read_log 9.3%, monitor and stop 1.9%.
That is **1,800 observation calls against 813 launches (2.2 per job).**
- Sampled Soul aba0348c: 163 calls, 6 `mutrun.sh` and 5 `strykrun.sh`
launches, each followed by its own poll and read cycle.
### Imagination: 25 agents, 2,130 calls (85 each), 431M read
- search 28.5%, git.read 24.5%, read.file 18.6%. Orientation by single shell
calls, where probe scripts would do the same work in fewer calls.
### Baseline prefix
- The global `~/.claude/CLAUDE.md` is 56k chars and `F:\Projects\CLAUDE.md` is
14k chars, about 17.5k tokens together. Both load into every subagent.
- Over about 28.8k subagent calls since 09-29 that is about 500M re-read, about
9% of subagent cache reads.
## 2. Target call shape per faculty
These are the target numbers per faculty. "Script per phase" means one Bash
call that runs a written script and prints a bounded result. It replaces
chained single commands.
| faculty | calls | peak ctx | phases (one script or one parallel batch each) | lifetime |
|---|---|---|---|---|
| **Self** | ≤ 3 per dispatched agent (dispatch, read the final notification, triage) | **≤ 300k**, rotate past it | rehydrate is one recipe batch; triage is parallel `view`s | rotates at phase boundaries (§4) |
| **Imagination** | 40-80 | ≤ 250k | (1) an orient script that dumps `git log`, file:line excerpts and grep hits to a scratch file and prints ≤ 150 lines; (2) probe scripts, one per question, each run in one verify job; (3) one admit batch | one map pass per agent; a refresh is a new agent |
| **Hands** | **60-150** | **≤ 300k** | (1) an orient script: status, base, spec `view`, and `sed -n` excerpts of the spec's anchors in one call; (2) edits through Edit, with independent edits in one parallel message; (3) **one verify job** (build, scoped tests, mutation) through `ygg-verify` summary mode, waited in the foreground (§3a); (4) **one commit call** per commit; (5) the report admit | **hand off at ~300k** or ~150 calls: commit what is verified, admit the `cut_report` with `undone`, stop. Self dispatches a continuation |
| **Soul** | 50-120 | ≤ 300k | (1) dump the range once: `git diff base..head > $S/range.diff` plus `--stat`, then grep that file instead of re-running `git show` per file; (2) **one probe job** carrying all probes, the hand mutants as a list (§3b) and the scoped reruns; (3) **one final job**: full suite plus the mutation tool; (4) the verdict admit | a second pass covers only the fix delta (already ruled) |
| **Eyes** | ≤ 40 | ≤ 200k | parallel WebFetch and search batches; transcript work as scripts, never whole reads | one question set per agent |
| **Life** | ≤ 25 | ≤ 150k | one inventory script over the surfaces, then the edits | per phase boundary |
Rules common to every faculty:
- **Observation bounds.** No command prints more than ~150 lines. Logs go to a
file. You read `tail -n 60`, or `grep -n` hits with `-m`. You never `cat` a
whole log or source file over ~300 lines; read the anchor's range instead.
Reading in 100-line windows matches the SWE-agent ablation (18.0% against
12.7% for full-file views). Reading a file twice is a smell. Write what you
need to keep to a scratch notes file instead.
- **Parallel tool calls.** Independent reads, views and greps go in one
message. This saves wall time and turns, not reads.
- **No turn ends before the report.** A subagent's turn end is a paid wake of
Self (§1).
## 3. Tooling ledger, and the shared script body
Code is a liability, so deletions come first:
- Delete the `git log -1 --format=%B` check (briefs.md 305).
- Delete "poll the log" (briefs.md 309).
- Delete "Run long builds detached … and poll" for agents (SKILL.md 577-579).
- Delete the raw-output paste in the Hands report (briefs.md 334-336).
- Delete the per-agent harness reinvention, by replacing it with the shared
scripts below.
| tool | owner | consumer | calls saved (from the taxonomy) | carrying cost |
|---|---|---|---|---|
| **(a) `ygg-verify` summary mode + foreground waiter.** `SUMMARY=1` keeps the full log in a file and prints a bounded summary: exit verdict, pass/fail/skip counts per test binary (and a `ZERO TESTS RAN` line when the count is 0), failing test names, and the last 30 lines of each failure, capped at ~80 lines. The companion `wait` mode blocks ≤ 270 s per call on a done-file and prints `RUNNING <elapsed>` or the summary. | the stopgap's owner (the skill repo). It dies with the stopgap when Idunn's typed verify verdict lands, and must not outlive the deletion line in `ygg-verify.sh` | Hands, Soul, Imagination probes | Observation calls fall from 2.7 to ~1-2 per job: about **−2,400 Hands and −1,000 Soul calls per two days, ~13% of calls.** Because of N², that is ~15-20% of reads | ~60 lines of bash plus a parser per ecosystem (cargo test, dotnet test, vitest, pytest). **Verdict-bearing**: a summarizer that drops a failure forges a green. It gets a Soul pass before merge, plus a fixture test per parser: a failing run, a zero-test run, a killed run and a no-verdict run |
| **(b) mutation runner.** It takes the ecosystem tool's diff scope, *or* a list of hand mutants (`file`, `before`, `after`, `tests`), and returns a kill table (label, outcome, killing test) in one job. Hand mutants are applied in the container clone, so the tree is never touched | same | Soul primarily, Hands for the scoped run | Replaces the reinvented `mutrun`/`strykrun`/`runmut` scripts. One job instead of one per mutant: in aba0348c, 11 launches with their wait/read pairs become ~2. **Estimate (inferred): −20-40 calls per Soul pass** | ~100 lines. **Verdict-bearing**: same Soul and fixture rule. It must report `unviable` and `timeout` separately from `caught` (the brief's totals already require this) |
| **(c) commit helper.** **Not built.** In Git Bash, `git add <paths> && git commit -F- <<'MSG' … MSG && git push` is one call. The BOM and here-string scar is PowerShell-only (SKILL.md 568-571) | — | Hands, Self | ~1.5 calls per commit, ~1.5% of Hands calls, at **zero code** | none. A brief and SKILL text change only |
| **(d) CRLF/BOM-preserving edit.** **Probe first**: does Edit preserve CRLF and BOM on these repos? If yes, the brief says "use Edit; no python editors" and nothing is built. If no, one shared `edit.py` replaces 1,097 reinvented editors | probe: Self, now | Hands | 6.5% of Hands calls, and the largest tool-input context item | zero if the probe passes, ~30 lines if not |
| (e) an orient script per faculty | — | — | not a shared tool: one line in each brief ("orient in one script") | none |
### 3x. The shared script body (operator: "Is that too ambitious?")
**Direct answer: no, provided it stays a directory in a git repo with ordinary
review.** It is small and it already exists: `tools/stopgap/` in the skill
repo. What the data shows is the opposite problem. Every Hands and Soul writes
its own `runmut.sh` or python editor and throws it away, and that costs calls
and context in every pass. The ambitious part would be a bespoke admission
process, and per the operator's follow-up none is needed: collaborating on code
is a solved problem.
**Facts (gamecult-ops):**
- Forgejo 16.0.3 runs on Yggdrasil at `127.0.0.1:3300` behind nginx as
`git.erycina.org`.
- Its purpose is "public, anonymously readable source hosting for Erycina".
The inventory says Erycina is "not a GameCult subdomain by intent … meant to
read as creator-owned and separately stewarded".
- Actions are **deliberately not enabled**: "CI runners on the same host as the
state store … Revisit when there is something to run".
- Registration is disabled, and accounts are created by an admin through the
forgejo CLI.
- Git runs over SSH through the host `sshd` as the `git` user, with keys
registered in Forgejo. Agents would authenticate with an SSH key registered
to a dedicated bot account (for push) and a Forgejo API token held outside
briefs (for PRs). No secret appears in any brief or in this map.
- No mirroring is documented. Forgejo supports push mirrors natively.
- As of 09-05 TLS was staged pending DNS. Whether it is issued now is unverified.
- The skill repo's `origin` today is `github.com/GameCult/Eureka`.
**Shape (recommended, pending Q4):**
- **Where it lives:** in the skill repo itself, in `tools/`, with the stopgap
staying under `tools/stopgap/`. It does not go in a separate repo. Scripts
and briefs change together, since a brief names a script's flags, so one
revision pins both. Agents already have the checkout at
`~/.claude/skills/eureka`, so there is no clone path to manage and nothing
separate to pin. Self records the skill SHA in the campaign's first brief
line, which pins it for the run.
- **Contribution:** an agent that writes a reusable script pushes a branch
`tools/<name>` and opens a PR. It never pushes to `main`. The PR carries the
script, its fixture test, and one line of evidence: which calls it replaces
(a transcript count from `tc_classify.py` or a named pass) and its live
consumer, the brief line that will call it. **A PR without a consumer line is
not merged.**
- **Admission = merge.** Self merges convenience scripts. Scripts whose output
a verdict depends on (the summarizer, the mutation runner, the waiter) are
cuts: a Soul pass on the PR comes before the merge, by the same rule as "a
release is a cut". Without Actions, the fixture tests run through
`ygg-verify` on the PR head, and the reviewer pastes the summary line into
the PR. Forgejo PR review is the record.
- **Discovery:** a `tools/INDEX.md` with one line per script (name, purpose,
consuming brief section). Briefs name the exact script they want. Agents do
not browse.
- **Pruning:** an `INDEX` line whose consuming brief no longer names the script
marks it dead, and the next Self pass deletes it. Stopgap scripts keep their
DELETION LINE header. Life does not touch scripts (they are Body); at a phase
boundary it may report an INDEX entry with no consumer.
- **What stays out:** anything that knows a project's layout (CultLib crate
paths, Aetheria test names, a cut's mutant list). That goes in the project,
or in the job's argument.
## 4. Self's lifecycle
What Claude Code supports, per the docs fetched 10-01:
- `/clear` "costs nothing" and starts fresh. It is an operator command. Self
cannot run it on itself.
- Auto-compaction triggers near the auto-compact window.
`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` sets the threshold and "applies to
subagents as well". Compaction "is itself a large request".
- The main-conversation cache lifetime is 1 h on a subscription and drops to
5 min once usage credits are drawn. The 5-minute subagent TTL is measured
above, not documented on that page.
- The env var `CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL` comes only from a
third-party repo (Eyes). **It is unverified.**
- **API-only:** the context-editing and memory tools, programmatic tool
calling, and the tool-search tool. Claude Code applies its own tool-result
clearing and compaction. Self cannot call these.
Shape:
1. **No interim wakes.** Subagents never end a turn before their report (§2,
§3a). This removes the 357 interim notifications, worth about 600 root calls
at ~500k, which is **~30% of root reads**.
2. **Rotate the root at about 300k, or at each phase boundary, whichever comes
first.** All campaign state is in Huginn, so the handoff is short: the
campaign slug, the session label and the next action, written to the
scratchpad. A clear ends the session's background agents (observed
2026-10-03), so rotate when none is in flight; see SKILL.md. Self then asks the operator to `/clear` (Q3). A
fresh root rehydrates with the campaign recipes, at about 100k. At a median
root context of ~200k instead of ~500k, **every remaining root call costs
about 60% less**.
3. **Reports stay id-only.** Hands and Soul return an id, a receipt and one
sentence. Long raw output goes to a scratch file, and the report gives its
path.
4. **Self delegates reading.** It never reads a log or a diff itself. It reads
the verdict and the report by `view`.
5. Backstop: if the operator prefers it, a lower auto-compact threshold (Q3)
stops a forgotten rotation from riding to 900k.
## 5. Text changes (anchors at skill HEAD `92bf6f7`)
### `references/briefs.md`
- **275-283 (Hands "Long jobs" paragraph): replace with:**
> 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 whose command
> exits only when the job does. Then wait in the **foreground** with
> `ygg-verify.sh wait <done-file>` (or `until [ -e <done> ]; do sleep 60;
> done` with a 270 s cap), one call at a time, each ≤ 270 s so that your
> cache stays warm. A call longer than 5 minutes rewrites your whole context.
> Never run a second watcher on the same job, no timer shorter than 60 s, and
> no interim message.
(Pending Q1. If the operator picks B, keep the current text and add only
the 270 s cap.)
- **309: delete** "Long builds run detached, with a log in the scratchpad;
poll the log." Replace it with: "Verify through `ygg-verify.sh` with
`SUMMARY=1`; read the summary, and open the full log only at a named failing
test, with `grep -n -A 40`."
- **255-257 ("Verify once, at the end"): append** "One job carries build,
scoped tests and the mutation run (briefs Soul, 390-399)."
- **248-273 (context budget block): add three bullets:**
- **Orient in one script.** One Bash call prints git status, base and the
spec's anchor ranges (`sed -n`) for every file the spec names. Independent
reads go in one parallel message.
- **Bound every observation:** ≤ 150 lines per command; logs go to files;
read windows, not whole files.
- **Hand off at ~300k context or ~150 calls.** Commit what is verified,
admit the `cut_report` with `undone`, and stop. Self dispatches a
continuation with the report id. This replaces the "far past" judgment at
263-273 with a measurable line. Keep the clause's warning against stopping
because the scope looks large.
- **296-306 (Commits): replace** the Write-tool and `git log -1` bullets with:
"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)."
- **334-336 (Hands report): replace** "then any raw verification output too
long for an evidence line, pasted, not summarised" with "and, for output too
long for an evidence line, the scratch path that holds it".
- **350-353 (Soul long jobs): same replacement as 275-283.**
- **390-399 (Soul reruns): append** "Dump the range once (`git diff
<base>..<head> > <scratch>/range.diff` plus `--stat`) and grep that file.
Hand mutants go in one list to the mutation runner, never one job each."
- **428-430:** unchanged. It is already id-only.
- **117-163 (Imagination map) and 91-112 (0b): add** "Orient and probe through
scripts: one call per question, output ≤ 150 lines."
- **469-480 (Eyes): add** "Budget ≤ 40 calls; transcripts by script; batch
fetches in parallel."
- **439-465 (Life): add** "Budget ≤ 25 calls; inventory the surfaces in one
script."
### `SKILL.md`
- **215-236 (sizing): add** after 219: "Size by calls too. Each call re-reads
the context, so an agent's reads grow with the square of its calls (measured
≈ 70k·N + 0.9k·N²). A cut that needs more than ~150 Hands calls is two cuts,
or one cut and a continuation."
- **451-513 (Self's discipline): add two bullets:**
- **Self's own context is the most expensive in the pipeline.** Every
notification re-reads all of it. Rotate at ~300k or at a phase boundary:
write the handoff (slug, session label, next action; no agent in flight)
and ask the operator to `/clear` (ruling: Q3). Never read logs or diffs in
the root; `view` reports and verdicts.
- **A subagent that notifies before its report is a brief defect.** Count
finished notifications per agent. More than one means a turn ended
mid-wait, and the brief template is what gets fixed.
- **537-552 (Yggdrasil):** the line says "at most 3 jobs at a time" and "12
GiB". The script says `SLOTS=5` and `MEM=6g`. **This is stale again.** Drop
the numbers and point at the script, as the line itself demands.
- **568-571 (commit messages): replace** with "Commit from Git Bash with
`git commit -F- <<'MSG'`. PowerShell 5 here-strings break `-m`, and its
`Out-File`/`Set-Content` write a BOM."
- **577-579 ("Run long builds detached … and poll"): split.** Operator-side
builds (Unity batchmode) stay detached, with a log and a PID. Agents wait in
the foreground in ≤ 270 s calls (briefs Hands).
- **New short section after 513, "Shared tools":** the `tools/` body, `INDEX.md`,
the PR-as-admission rule, Soul before merge for verdict-bearing scripts, and
pruning by consumer (§3x).
### `references/changelog.md`
- New top entry: "Waiting costs no wakes". Evidence: 602 finish notifications
from 245 agents, 1,324 root text-only calls, 596M, and the 5-minute TTL table.
It amends `92bf6f7`.
## 6. Operator questions
**Q1. How does a subagent wait?** (It amends `92bf6f7`, landed today.)
- A. Foreground calls of ≤ 270 s on a done-file, about one call per 4.5
minutes. The cache stays warm and Self is never woken. **Recommended.**
Per 10 minutes of waiting it costs about 2.2 cache reads, ≈ 0.2 context
units, against 1.25 for one rewrite. Timers inside the call are ≥ 60 s, per
the operator's floor.
- B. Keep the background waiter and the turn end (current). Measured: 357
interim root wakes in two days at ~500k each, plus a 235k cache rewrite per
resume after 5 minutes.
- C. Foreground calls of up to 10 minutes. Fewest calls, but each one is a
full rewrite. Pick C only if Q2 lands the 1 h TTL.
Depends on it: the briefs' long-jobs text and the waiter mode of
`ygg-verify.sh`.
**Q2. Probe the subagent 1 h cache TTL?** `CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL=1h`
is unverified, from a third party. It doubles each write's price, and in
exchange idle gaps up to 1 h stop forcing rewrites (292M of rewrites in two
days).
- A. Self runs one bounded probe: set the variable, run a subagent with a
7-minute gap, and check `cache_creation` on the next call. Then decide.
**Recommended.** Settings are yours, so this needs a go.
- B. Leave it alone.
**Q3. How does Self's context get reset?**
- A. Self writes a handoff and asks you to `/clear` at about 300k or at a
phase boundary.
- B. Set `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` so that auto-compaction fires around
300k.
- C. Both: A by default, B as the backstop. **Recommended.**
Depends on it: Self's discipline text. The root stayed at a median of ~500k
for two days.
**Q4. Where does the shared script body live?**
- A. In the skill repo's `tools/`, on GitHub `GameCult/Eureka`, with PRs there.
This costs zero setup.
- B. The skill repo moves to Forgejo, primary, with a push mirror to GitHub.
Review happens on Forgejo, beside where the scripts run. This means a GameCult
org on a forge documented as Erycina's, "separately stewarded" and with a
landing page fixed to Erycina, plus a bot account and key.
- C. A separate scripts repo on Forgejo, pinned by the skill. That adds a
second checkout and a pin, for nothing the skill repo cannot carry.
**Recommended: B if you are content to put GameCult tooling on the Erycina
forge, otherwise A.** The workflow is identical either way: branch, PR, merge
as admission. Forgejo Actions stays off (the runbook's choice), and tests run
through `ygg-verify`.
**Q5. Slim the always-loaded doctrine?** The two CLAUDE.md files are about
17.5k tokens in every subagent, about 9% of subagent cache reads. Claude Code's
docs advise keeping CLAUDE.md "under 200 lines" and moving workflow-specific
text into skills.
- A. Move the long-form Cult, Praxis and Infrastructure sections into a
referenced file. The Prime Directive, Code Is A Liability, the rebuild
contract and the faculty lines stay inline. **Recommended.**
- B. Leave it.
It is your text; this is a question, not a cut.
## 7. Measured versus inferred
**Measured** (scripts in the scratchpad, rerunnable):
- the per-faculty class distributions;
- the 2.7 and 2.2 observation calls per job;
- the duplicate counts;
- the reinvented-harness names;
- the root call profile, its text-only share and the notification counts per
agent;
- the context growth per call;
- the 70k baseline;
- the cache-write-by-gap table;
- the CLAUDE.md sizes;
- the Forgejo facts (gamecult-ops);
- the stale slot and memory numbers in SKILL.md.
**From docs (fetched 10-01):**
- `/clear`, compaction and `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` reaching
subagents;
- the main-cache TTL;
- the background-command notification;
- the PreToolUse filter;
- the CLAUDE.md advice.
**Inferred:**
- That each interim subagent turn end produces a root notification. This is
consistent with the counts and the docs, but no single event was traced.
- The per-tool savings estimates, and the calls saved by the mutation runner.
- The 300k and 150-call handoff lines. They are chosen from the N² model and
the 400k "loopy" scar, not tuned.
- The quota weight of cache reads against writes. It is unpublished, so the
savings are stated in reads and writes, not in quota.
**Classifier limits:** Bash commands are classified by regex on mixed command
lines, so a compound call counts once, under its first matching class. Before
reordering, the "verify.launch" class absorbed poll loops that grep for
"ygg-verify:" in logs. The tables above are from the corrected ordering.
Parallelism was not measured.
## 8. Rulings (operator, 2026-10-01)
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".
- Q1 = A: subagents wait in the foreground, in calls of at most 270 s, and never end a turn before their report. This supersedes `92bf6f7`.
- Q2 = A: Self runs one bounded probe of `CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL=1h`.
- Q3 = C: rotate at about 300k or at a phase boundary, with a handoff and the operator's `/clear`. A lower `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` is the backstop.
- Q4 = B: the shared scripts stay in the skill repo on GitHub (`tools/`, `tools/INDEX.md`), contributed by branch and pull request.
- Q5 = yes: slim the global CLAUDE.md along Epiphany's per-role philosophy. A separate Imagination pass maps it.
## 9. Probe results (2026-10-01, Haiku 4.5, `claude -p`, a 7-minute idle gap)
- **`CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL=1h`: CONFIRMED, though undocumented.**
- With it, the post-gap call read 112,158 cached tokens and created 401, all `ephemeral_1h`.
- Without it, the call read 29,331 and created 83,366, all `ephemeral_5m`; the context had expired and was rewritten.
- The official docs list only `CLAUDE_CODE_PROMPT_CACHE_TTL`, for the main conversation.
- **`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`** is documented as a percentage of the auto-compact window. It can only lower the threshold and it applies to subagents too. A value sized for the root would compact subagents early, so the Q3 backstop is not set; rotation carries Q3.
## 10. Q4 superseded (operator, 2026-10-01)
Q4 is now **A**: the Eureka repo, prose and `tools/`, moves to Yggdrasil's Forgejo and is shipped open source with contributions welcome. See `faculty-philosophy-cut.md` "Rulings" Q3 for the verbatim reason. Contributions use Forgejo PRs. Whether the GitHub copy is archived, mirrored or deleted is still open; ask the operator.
## 11. Follow-ups from the tools' Soul passes (2026-10-01)
The `tools/` work merged after four Soul passes (last at `838b859`). Recorded, not blocking:
- **Jest `infail` scope.** `infail` resets only on `PASS` or `Test Suites:`, so a crash after an earlier failing file is named as a failing test. The verdict stays right; the name is wrong. Reset at a blank line or the next non-indented non-`●` line.
- **No automated guard for the RUN_ON_HOST watchdog (exit 124).** It is proven only by the probe recipe in `tools/fixtures/mutation-probe/README.txt`; `tools/test-tools.sh` never starts the stopgap, so reverting the fix stays green.
- **Shared volumes are outside the isolation.** The cargo registry, nuget and `eureka-sccache` volumes carry state across the control and mutant containers. A test that counts into one could pass the control and read CAUGHT later. The runner header names the limit; no probe was built.