agent-bios 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,7 +24,7 @@ Two layers:
24
24
  - **Every span we write into a file we do not own has a remover** — the Codex config block, the settings hook registrations, the `AGENTS.md` central region and the zsh hook line each sit in a span identifiable as ours (a marker pair, a tagged line, or a manifest name), and `uninstall` removes exactly those and nothing around them. The entry `CLAUDE.md`/`AGENTS.md` are seeded once and then yours: the corpus is assembled under `central/` and the entry file only imports it, so your own additions are never mixed with ours and never removed with them. `uninstall` is a security operation — everything of ours goes, and what it took leaves as one archive you can hand off or delete.
25
25
  - **Metadata selects, observation authorizes** — a record of what was promoted can propose an irreversible act but never authorize one; it has never seen the machine it will run on. Clearing a user's personal copy of a promoted learning asks the deployed corpus whether the replacement is really there, excludes the copy being deleted from that evidence, and keeps the copy on any uncertainty: a kept duplicate is redundant, a wrong removal is data loss.
26
26
  - **The deployment outlives the act that made it** — a command's success says nothing about which version landed; a cached package can serve the previous one at exit 0. So the deployed state carries its own version marker and `status` reads that marker rather than the source it was launched from. With no marker the answer is `unknown`, never a guess.
27
- - **`Evidence Base` (per guide) is the single owner of numbers.** Measure with `session-cost.py`.
27
+ - **`Evidence Base` (per guide) is the single owner of numbers.** Measure with `session-cost.py` (`agent-bios cost` on an installed package).
28
28
  - **English is canonical and installed; Korean (`ko/`) is reference only.** The harness loads only fixed-name English files; the installer deploys English only.
29
29
 
30
30
  ## Layout
@@ -42,7 +42,7 @@ Two layers:
42
42
  | `wrappers/` | internal Codex wrappers, deployed to `$CODEX_HOME/bin/` |
43
43
  | `gates/` | author-side verification (mirror generation, parity, lexicon, payload, assembler scenarios) — reachable only from a repo checkout, and `check-package.sh` fails if any of it enters the npm payload |
44
44
  | `ontology/` | what a change obliges elsewhere — entities, obligation edges, and the service's routes, held against real source by `check-ontology.py`. `instances/graph.json` is canonical; `LEXICON.md`, the RDF views, the HTML map, and the competency/extension docs are generated from it |
45
- | `install.sh`, `session-cost.py` | the CLI and the cost meter — the two things you run directly |
45
+ | `install.sh`, `session-cost.py` | the CLI and the cost meter — the two things you run directly (`agent-bios` and `agent-bios cost` once installed) |
46
46
  | `decisions/` | the decision record for developing this repo — what was decided and which alternative it closed; author-side, never shipped |
47
47
  | `packages/` | authored corpus packages, organized by package identity rather than by concept home |
48
48
  | `.githooks/` | the pre-commit hook that runs the gates against the index, enabled per clone with `core.hooksPath` |
@@ -95,11 +95,11 @@ agent-bios uninstall # remove deployed files and the zsh hook
95
95
 
96
96
  From a git clone, run `./install.sh install` directly (the same CLI). `install` respects `CLAUDE_CONFIG_DIR`, `CODEX_HOME`, `AGENT_LAUNCH_VENV`, and `ZDOTDIR`; `--dry-run` prints actions without changing anything. Deploy to **every active environment in one sitting** — a partial deploy leaves a shared global pointing at a guide some environment lacks; globals are English only. Replaced files are backed up under `~/.local/share/agent-bios/backups/<timestamp>/`, and the installed set is recorded in a manifest that `uninstall` consumes. Those copies are **a manual escape hatch, not a restore mechanism**: nothing reads them back, so recovering from one means copying files yourself. The supported paths are the ones `agent-bios help` prints — a clone rolls corpus content back to a registered version, an npm install rolls the whole package back by version — and `uninstall` emits one archive of everything it removed. The backups exist for the case those three do not cover: the exact bytes that were on disk before a particular install. The published npm package ships only the deploy set (never `settings.json`, `config.toml`, `ko/`, or `benchmarks/`).
97
97
 
98
- In a TTY, zero-argument `codex` or `claude` opens the launch preflight. Every arrow-key TUI selection screen keeps the complete current setup in a fixed top panel, followed by the highlighted option's description and then the option list. Move with Up/Down, select with Enter, use Esc to return to the previous menu, and use `q` to cancel; Esc at the mode root also cancels. Each tier's model is chosen from the host's configured catalog, or via **Other** to type any model id the backend accepts; that text input preserves values that start with `q`, so Esc or Ctrl-C cancels immediately there, while submitting `q` cancels after Enter. The Textual preflight reflows to the terminal size, so there is no fixed minimum geometry. The root menu picks a mode — **Software Engineer** (repo-scoped work that defers to the project's own AGENTS.md/CLAUDE.md: **Vanilla**, the bare CLI with no launch contract, tier bindings, or applied permissions, plus **Custom**; SE-specific review defaults arrive later), **Builder** (the tier presets: Balanced, Deep review, Fast batch, Solo with delegation off, plus Custom), **Session distill** — then a preset within it. Select **Custom** (in Software Engineer or Builder) to open a persistent settings hub for the main tier, review setup, host policy, and each tier binding. Every edit returns to that hub; **Start with these settings** is the final launch confirmation, **Save these settings globally and start** additionally persists the setup as a named preset (with host-scoped tier overrides) in your user config for reuse elsewhere, and **Exit without launching** cancels the launch. The rich preflight runs from a managed virtualenv (`launch/provision-venv.sh`, at `~/.local/share/agent-launch/venv`) that the launcher re-execs into on the interactive path; when that venv is unavailable, or the call is non-interactive, or `TERM` is `dumb`/unset, the launcher falls back to numbered prompts, where `b` is the back command. Backend command names are resolved from the calling environment's `PATH`; shell functions are not re-entered. Codex child bindings are materialized as session-selected agent configs under the user cache, while Claude receives model and effort in `--agents` JSON when delegation is enabled. Review runs cross-family by default (`review_family`, default `cross`; `same` restores today's same-family projection): because the main's tiers are one model family, every dispatchable review route — native and the deep route — runs on the opposite family. The exception is `slash-review`, the host's own built-in review command (`/code-review` on Claude, with `ultra` for its deep multi-agent pass; `/review` on Codex): it needs no dependency and always resolves, but being the main's own command it cannot be dispatched cross-family, so under `cross` it runs as the same-family floor and its verdicts are labeled PROPOSED. A Claude main dispatches gpt/codex review (native via the `codex-run` reviewer wrapper resolved under `$CODEX_HOME/bin`, deep via plain `codex exec -m <frontier model> -c model_reasoning_effort="ultra"` with a self-contained packet on stdin — `-c service_tier="fast"` is the explicit faster, shallower opt-in); a Codex main dispatches Anthropic/Claude review (native via `claude -p --permission-mode plan`, deep via the `claude` CLI headless with the keyword `ultracode` in the prompt, which is what opens Claude Code's dynamic workflow for that turn). The concrete reviewer command, resolved absolute path, and opposite-family tier bindings are named in the injected session-start contract; cross-family reviewers are dispatched as read-only subprocesses, not CLI-native subagents, since neither CLI hosts the other family as a native subagent. When a cross-family route is unavailable at launch or unauthenticated at use time it degrades to same-family native subagent review labeled PROPOSED (family collapse) rather than blocking; a requested non-none review with no cross-family route and no same-family fallback (delegation off) stays fail-closed. Review setup means configured/requested; this launcher does not claim that review completed, and unavailable runtimes such as Ultrawork are not offered until integrated.
98
+ In a TTY, zero-argument `codex` or `claude` opens the launch preflight. Every arrow-key TUI selection screen keeps the complete current setup in a fixed top panel, followed by the highlighted option's description and then the option list. Move with Up/Down, select with Enter, use Esc to return to the previous menu, and use `q` to cancel; Esc at the mode root also cancels. Each tier's model is chosen from the host's configured catalog, or via **Other** to type any model id the backend accepts; that text input preserves values that start with `q`, so Esc or Ctrl-C cancels immediately there, while submitting `q` cancels after Enter. The Textual preflight reflows to the terminal size, so there is no fixed minimum geometry. The root menu picks a mode — **Software Engineer** (repo-scoped work that defers to the project's own AGENTS.md/CLAUDE.md: **Vanilla**, the bare CLI with no launch contract, tier bindings, or applied permissions, plus **Custom**; SE-specific review defaults arrive later), **Builder** (the tier presets: Balanced, Deep review, Fast batch, Solo with delegation off, plus Custom), **Session distill** — then a preset within it. Two entries below the modes act on the installation rather than the launch: **Corpus packages** opens a checklist of the optional domain packages (core + infra always install) and, on Apply, hands the exact checked set to `agent-bios onboard --domains` — the installer alone deploys, runs the activation canary, and records the outcome, which the status panel then reports, loudly when an apply failed. **Language** switches the interface text between English, 한국어, and 日本語 and persists the choice in your own `launcher.local.toml`; it changes the launcher's own screens only, since the corpus the models consume is English-unified. Select **Custom** (in Software Engineer or Builder) to open a persistent settings hub for the main tier, review setup, host policy, and each tier binding. Every edit returns to that hub; **Start with these settings** is the final launch confirmation, **Save these settings globally and start** additionally persists the setup as a named preset (with host-scoped tier overrides) in your user config for reuse elsewhere, and **Exit without launching** cancels the launch. The rich preflight runs from a managed virtualenv (`launch/provision-venv.sh`, at `~/.local/share/agent-launch/venv`) that the launcher re-execs into on the interactive path; when that venv is unavailable, or the call is non-interactive, or `TERM` is `dumb`/unset, the launcher falls back to numbered prompts, where `b` is the back command. Backend command names are resolved from the calling environment's `PATH`; shell functions are not re-entered. Codex child bindings are materialized as session-selected agent configs under the user cache, while Claude receives model and effort in `--agents` JSON when delegation is enabled. Review runs cross-family by default (`review_family`, default `cross`; `same` restores today's same-family projection): because the main's tiers are one model family, every dispatchable review route — native and the deep route — runs on the opposite family. The exception is `slash-review`, the host's own built-in review command (`/code-review` on Claude, with `ultra` for its deep multi-agent pass; `/review` on Codex): it needs no dependency and always resolves, but being the main's own command it cannot be dispatched cross-family, so under `cross` it runs as the same-family floor and its verdicts are labeled PROPOSED. A Claude main dispatches gpt/codex review (native via the `codex-run` reviewer wrapper resolved under `$CODEX_HOME/bin`, deep via plain `codex exec -m <frontier model> -c model_reasoning_effort="ultra"` with a self-contained packet on stdin — `-c service_tier="fast"` is the explicit faster, shallower opt-in); a Codex main dispatches Anthropic/Claude review (native via `claude -p --permission-mode plan`, deep via the `claude` CLI headless with the keyword `ultracode` in the prompt, which is what opens Claude Code's dynamic workflow for that turn). The concrete reviewer command, resolved absolute path, and opposite-family tier bindings are named in the injected session-start contract; cross-family reviewers are dispatched as read-only subprocesses, not CLI-native subagents, since neither CLI hosts the other family as a native subagent. When a cross-family route is unavailable at launch or unauthenticated at use time it degrades to same-family native subagent review labeled PROPOSED (family collapse) rather than blocking; a requested non-none review with no cross-family route and no same-family fallback (delegation off) stays fail-closed. A reviewer this launcher has never seen is yours to add: **Register another reviewer…** in the review editor asks for the descriptor a method needs, proves the candidate by running it through the real config reader before a byte is written, and appends it to `review-methods.local.toml` beside your config — a file the installer never deploys, verifies, or overwrites, whose entries face exactly the validation a shipped one does and whose name may not shadow a shipped method. A refusal shows the reader's own message and leaves that file byte-identical. Review setup means configured/requested; this launcher does not claim that review completed, and unavailable runtimes such as Ultrawork are not offered until integrated.
99
99
 
100
100
  At the shell-wrapper boundary, every argument-bearing command (`codex exec ...`, `claude -p ...`) and every non-TTY invocation skips launch-profile projection and preserves caller arguments. The Claude direct path intentionally retains its wrapper default, `--dangerously-skip-permissions`. `codex --no-tui ...` / `claude --no-tui ...` explicitly take that direct path, and `AGENT_LAUNCH_TUI=0` disables zero-argument TUI interception for a process tree.
101
101
 
102
- Direct `agent-launch` calls still require a valid profile to resolve the backend command and its default arguments. `--preset`, `--custom`, or `--dry-run` select the configured-launch path even when non-TTY or combined with `--no-tui`; a non-TTY bare `--dry-run` deterministically uses Balanced, and a custom profile without that preset must pass `--preset NAME`. Forwarded backend arguments are expert overrides appended after projected defaults and may supersede them; the summary discloses this when arguments are present. For scripted configured launches, call `$HOME/.local/bin/agent-launch --preset NAME --yes HOST -- ...` or add `$HOME/.local/bin` to `PATH`. The summary goes to stderr so backend stdout stays machine-consumable.
102
+ Direct `agent-launch` calls still require a valid profile to resolve the backend command and its default arguments. `--preset`, `--custom`, or `--dry-run` select the configured-launch path even when non-TTY or combined with `--no-tui`; a non-TTY bare `--dry-run` deterministically uses Balanced, and a custom profile without that preset must pass `--preset NAME`. Forwarded backend arguments are appended verbatim after the projected defaults; one that would override a projected option (the seat, the contract, delegation, policy) is refused at launch so the contract keeps describing the run, and the summary discloses forwarded arguments when present. For scripted configured launches, call `$HOME/.local/bin/agent-launch --preset NAME --yes HOST -- ...` or add `$HOME/.local/bin` to `PATH`. The summary goes to stderr so backend stdout stays machine-consumable.
103
103
 
104
104
  Add `$CODEX_DIR/bin` to `PATH` or invoke the wrappers by absolute path. `codex-helm` follows the local CLI default and launches the HELM main with `--dangerously-bypass-approvals-and-sandbox`; an explicit `--sandbox MODE` disables bypass for that run regardless of flag order. `AGENTS.md` gives root/main local Codex sessions standing ordinary-subagent authorization when the delegation gates fire. A non-Ultra HELM main sets native multi-agent off by default and instructs HELM to send tiered dispatch through the internal `codex-run` adapter, where the selected model, effort, and sandbox are pinned; native multi-agent defaults on only when the HELM main itself is explicitly Ultra. FRONTIER is instructed to run as a separate `gpt-5.6-sol` root that is always read-only, at max by default, Ultra for genuinely divisible complex work, or a lower supported effort when cost or latency dominates. Because the HELM main has bypass authority and arbitrary expert `-c` by design, this dispatch route is an instruction-backed, live-E2E-verified default rather than a security boundary. Keep `codex-run` as the low-level internal adapter, not as a user-facing policy boundary. Both wrappers accept `-c key=value` as an expert override, and that override may intentionally change wrapper defaults for a single run.
105
105
 
@@ -154,6 +154,50 @@ How much independence a review actually bought, as an ordinal grade per reviewer
154
154
  - After resume/clear/relocation, verify pwd, branch, and HEAD against the pinned handoff before acting.
155
155
  - Attribute a parallel session's action (commit, branch, resource) by execution evidence in that session's own transcript, never by token mentions — shared handoff/memory files inject the same tokens into every session's context.
156
156
 
157
+ ## Context Budget And Reset
158
+
159
+ Context growth is a property of the work, not of the host: over 1,075 sessions of 50+
160
+ requests across both CLIs it runs ~2,400 tokens per request (IQR 1,850-2,950), the two
161
+ hosts within 7% of each other (2,280 Claude, 2,450 Codex); the longest sessions (400+
162
+ requests) run lower, ~1,800. One budget therefore serves both, and what differs per host
163
+ is the price of ignoring it. The figure is a prior; the live session is measured below.
164
+
165
+ - **Automatic compaction fires only when the window is nearly full** — Claude at 84-87%
166
+ (windows cluster at 200K and 1M), Codex at ~95% of the window its transcript records as
167
+ `model_context_window` (258,400 on the sessions measured; a later Codex/model pair
168
+ records 353,400 — read the value, never assume it). Cache read is charged per
169
+ request against the whole loaded context, so leaving the reset to the host pays the
170
+ maximum on every request before it. Measured: input is 92-94% of session cost and
171
+ output 6-8%, at a 95-97% cache hit rate — the context is the bill, and uncached input
172
+ is 0.0% of it.
173
+ - Reset deliberately instead. Cost per request falls ~4x from an 867K auto-compact point
174
+ to 200K. The cost-theoretic optimum is ~65K, but it buys a compaction every ~32
175
+ requests at 2-3 minutes each, so 150-250K is the working range and the tail below it is
176
+ not worth chasing.
177
+ - **What bounds the budget is what survives the reset, not the token count.** A compaction
178
+ keeps a ~14K summary plus 3-4 recent messages and discards the rest — unguided when it
179
+ fires on its own. Anything already written to a file survives every reset, so the
180
+ earliest safe threshold is the one where durable state is already on disk. That is what
181
+ decouples cost from loss: without it, resetting more often loses proportionally more.
182
+ - Choose the mechanism by what is known, not by how large the context grew:
183
+
184
+ | Situation | Mechanism |
185
+ |---|---|
186
+ | Stage finished, what to keep is known | clear + dated handoff file — cheapest, and the loss is not a loss |
187
+ | Mid-stage, what to keep is known | write the handoff first, then compact with explicit instructions |
188
+ | Mid-stage, the needed detail is not yet identifiable | compact with instructions; a summary spans what a file cannot yet name |
189
+ | Original detail likely wanted later | clear, and record the transcript path in the handoff — transcripts persist on disk |
190
+ | The next question is unknown | new session plus messaging; only this keeps a round trip available |
191
+ | Growth is tool output | offload to subagents instead — measured ~20x cheaper (tier ~5x, context isolation ~4x) |
192
+ | The judgement trail itself is load-bearing | keep the session and pay the 2-5x resume |
193
+
194
+ - Measure rather than estimate: `agent-bios cost --context <transcript>` (the installed
195
+ entry to `session-cost.py`) reads either
196
+ host's transcript and reports current context, growth rate, compactions, and requests
197
+ remaining against a budget. Codex records `model_context_window` directly; Claude does
198
+ not, so the tool reports its observed auto-compaction point instead of assuming a
199
+ window.
200
+
157
201
  ## Handoff Contract
158
202
 
159
203
  Write for the next agent and re-verification, not narrative. Required content:
@@ -223,3 +267,5 @@ Single owner of numeric defaults; one production environment, 2026-07. Recalibra
223
267
  | two live delegation sessions | tiering saved ~3.3×; discarded prefixes made fresh respawn cheaper; unpinned reviewers inherited FRONTIER |
224
268
  | Codex reach contrast | inherit ~16.5K vs hermetic ~8.7K tokens; schema and stdout/stderr contract verified |
225
269
  | Codex native-spawn probe + HELM E2E | requested max/Ultra native children recorded xhigh/role null; separate read-only roots recorded max and Ultra successfully |
270
+ | 1,075 sessions of 50+ requests, both hosts (2026-08-16; the earlier 62-session top-by-size sample gave ~1,800-2,000) | context grows ~2,400 tok/request (IQR 1,850-2,950), hosts within 7%, ~1,800 in 400+-request sessions; auto-compaction fires at 84-95% of window, never earlier |
271
+ | 2 sessions decomposed by cost component (2026-08) | input 92-94% of cost, output 6-8%, cache hit 95-97%, uncached input 0.0%; an 867K→200K budget cuts cost per request ~4x |
@@ -172,9 +172,19 @@ def main() -> int:
172
172
  payload = json.load(sys.stdin)
173
173
  except Exception:
174
174
  return 0 # never block on malformed input
175
- if payload.get("tool_name") != "Bash":
175
+ # Malformed is a SHAPE, not just a parse failure. The guard above only covered bytes
176
+ # that are not JSON; a well-formed payload of the wrong shape — a top-level array, a
177
+ # list-valued tool_input, a non-string command — walked straight into `.get` or into
178
+ # `re.search` and left this hook exiting 1 with a traceback. That is the one thing
179
+ # the charter says it never does, and it would do it before EVERY Bash call in every
180
+ # deployed session, on a payload shape decided by a host this repo does not own.
181
+ # Advisory means silent on anything it cannot read, so each check returns 0.
182
+ if not isinstance(payload, dict) or payload.get("tool_name") != "Bash":
183
+ return 0
184
+ tool_input = payload.get("tool_input")
185
+ command = tool_input.get("command") if isinstance(tool_input, dict) else None
186
+ if not isinstance(command, str) or not command:
176
187
  return 0
177
- command = (payload.get("tool_input") or {}).get("command") or ""
178
188
  hits = matches(command)
179
189
  if not hits:
180
190
  return 0
@@ -219,6 +229,31 @@ def self_test() -> int:
219
229
  if r2.stdout.strip():
220
230
  problems.append(f"hook injected on a command that matches nothing: {r2.stdout.strip()[:80]!r}")
221
231
 
232
+ # Never-blocks is a claim about EVERY payload, so the shapes that are valid JSON and
233
+ # not the expected object are the ones worth naming. Each of these exited 1 with a
234
+ # traceback before the shape guard existed, and each stands for a different way the
235
+ # host's payload could move: a different top-level type, a different tool_input type,
236
+ # a command that is not a string. The two controls above are the other half — a guard
237
+ # that returns 0 on everything would satisfy this block and nothing else.
238
+ for label, raw in (
239
+ ("top-level array", "[1, 2, 3]"),
240
+ ("top-level null", "null"),
241
+ ("top-level string", '"hello"'),
242
+ ("tool_input as a list", '{"tool_name": "Bash", "tool_input": [1, 2]}'),
243
+ ("tool_input as null", '{"tool_name": "Bash", "tool_input": null}'),
244
+ ("command as a number", '{"tool_name": "Bash", "tool_input": {"command": 123}}'),
245
+ ("command as a list", '{"tool_name": "Bash", "tool_input": {"command": ["git", "pull"]}}'),
246
+ ("command absent", '{"tool_name": "Bash", "tool_input": {}}'),
247
+ ("empty stdin", ""),
248
+ ):
249
+ r3 = subprocess.run([sys.executable, str(here)], input=raw, capture_output=True, text=True)
250
+ if r3.returncode != 0:
251
+ problems.append(
252
+ f"hook exited {r3.returncode} on {label} instead of staying silent: "
253
+ f"{r3.stderr.strip()[-120:]}")
254
+ elif r3.stdout.strip():
255
+ problems.append(f"hook injected context for {label}: {r3.stdout.strip()[:80]!r}")
256
+
222
257
  # Every rule needs a command that must reach it. A nonempty pattern is not evidence: a
223
258
  # trigger of `(?!)` can never match, and a rule suppressed by the special-case logic in
224
259
  # matches() never reaches the caller either — both leave the rule inert while this file
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: repo-charter
3
+ description: Write or overhaul a repository's AGENTS.md (with CLAUDE.md as a one-line shim) for the agents that will work in it. Use when a repo has no agent instructions, when its AGENTS.md is a preamble that names nothing, or when the user asks to set a repo up for coding agents. Reads invariants and traps out of the code rather than pasting a template.
4
+ ---
5
+
6
+ # Repo charter
7
+
8
+ A repository's AGENTS.md is the repo's own layer of agent instruction — what a global
9
+ corpus cannot know because it is true only here. This skill produces that layer for a real
10
+ repository, and it produces it *from the repository*: the substantive half is reading the
11
+ invariants, the gates and the traps out of the code, and no template can do that part.
12
+
13
+ The output belongs to the repository — its team, its contributors, its git history. Nothing
14
+ this skill writes carries a marker, a version stamp, or copyable boilerplate, because nothing
15
+ outside that repo will ever update it.
16
+
17
+ ## 0. First decide whether the file is warranted
18
+
19
+ Many repositories correctly have no instruction file. Content and documentation repos need
20
+ at most a few lines of orientation; a personal repo rarely needs governance; and a one-line
21
+ fix in someone else's project is not the moment to charter it — the file is theirs to create.
22
+ Write one when agents will do repeated, non-trivial work here and the repo carries rules a
23
+ newcomer would otherwise learn by breaking something. If the answer is no, say so and stop;
24
+ an empty file with a heading is worse than none.
25
+
26
+ Read enough to answer honestly before deciding: the top-level tree, the package or build
27
+ manifest, hook and CI configuration, and the last fifty commit subjects. Steps 0 and 1 are
28
+ provisional; step 3 may revise them. **Draft from the code first** — if the repo already has
29
+ an instruction file, open it only at step 8, because reading it earlier anchors your
30
+ structure to it and the comparison stops being evidence.
31
+
32
+ ## 1. Purpose type, then category mix
33
+
34
+ Decide what the repo is *for* before writing a line. The type fixes which categories must be
35
+ thick; everything else stays thin or absent.
36
+
37
+ | Purpose type | Categories that must be thick |
38
+ | --- | --- |
39
+ | Library / framework that accepts outside contribution | contribution protocol, completion gates, hard boundaries |
40
+ | Application / product | project invariants, pitfall warnings, co-change duties |
41
+ | CLI / single-author tool | repo orientation, project invariants, task procedure |
42
+ | Monorepo / platform | context routing, task procedure |
43
+ | Corpus / payload the repo publishes elsewhere (the code is delivery, the content is the product) | hard boundaries, co-change duties, completion gates |
44
+ | Content / docs | repo orientation only, minimal |
45
+
46
+ A repo may take two rows; take the union and note which row explains each thick category.
47
+
48
+ The category vocabulary, in one line each: **repo orientation** (what lives where, so nobody
49
+ has to search), **command recipes** (the exact commands), **house form** (the shape of
50
+ outputs), **project invariants** (semantic contracts the code must keep), **context routing**
51
+ (when to open which document, skill or tool), **task procedure** (the order for a kind of
52
+ work), **pitfall warnings** (traps and environment quirks that cause misdiagnosis),
53
+ **contribution protocol** (how a change gets accepted), **completion gates** (what must pass
54
+ before "done"), **co-change duties** (change X, then also Y), **hard boundaries** (never),
55
+ **human authority** (where to stop and hand over), **agent conduct** (how the agent itself
56
+ operates). Across thousands of real files, human authority is the rarest of these — people
57
+ write prohibitions but not escalation paths — so it is asked for explicitly in step 5.
58
+
59
+ ## 2. AGENTS.md is the body; CLAUDE.md is a shim
60
+
61
+ Write one file: `AGENTS.md`, which Codex reads natively. Where Claude Code is used, add a
62
+ `CLAUDE.md` whose only content is an import of `AGENTS.md` (`@AGENTS.md`), plus at most a
63
+ sentence saying why the shim exists — and if that sentence claims something about a tool's
64
+ behaviour, date it or drop it, because such claims rot into lies. Two bodies drift; one body
65
+ and one pointer cannot. A file's name does not tell an agent which tool it is for — content
66
+ that opens "guidance for tool X" is copied between tools verbatim, so leave the tool's name
67
+ out of the body; the shim sentence is the one exception.
68
+
69
+ ## 3. Read the invariants and traps out of the code
70
+
71
+ This is the work, and it is done by reading, not by asking the repo's owner to dictate.
72
+ For an existing repo, collect at least these before drafting:
73
+
74
+ - **What runs before a commit and what it enforces.** Hooks, CI configuration, gate scripts,
75
+ lint and test entrypoints. For each rule you state, name the file that enforces it, and
76
+ mark the rules nothing enforces as convention — a reader must be able to tell a gate from
77
+ a wish.
78
+ - **What a green check does and does not prove.** A test suite that skips a leg, a check that
79
+ runs against a fixture rather than the real path, a scan whose subject set can be empty.
80
+ These belong in the file because they are exactly what a newcomer misreads.
81
+ - **Generated paths.** Anything produced from another file must be named as such, with the
82
+ generator and the command that regenerates it; hand-editing a projection is the most
83
+ common silent defect.
84
+ - **Where authority lives.** For each value that appears in more than one place, which copy
85
+ is the source and how the others follow it.
86
+ - **What the history paid for.** Read the recent log and any postmortems or dated design
87
+ notes for mistakes that cost a real attempt — the deploy that took the wrong tree, the
88
+ command whose exit status lied, the flag that was rejected rather than implemented. Each
89
+ becomes a present-tense warning that names the symptom.
90
+ - **Runtime shape that a reader cannot infer.** Which entrypoint is real, which directory
91
+ is payload versus tooling, which environment the tests assume.
92
+
93
+ State each finding as a rule about the present, in one or two sentences, and cite the code
94
+ by **file and identifier** — function, constant, leg name — never by line number: a symbol
95
+ survives an edit and is greppable, a line number is wrong within a day. A rule that cannot
96
+ cite its enforcer or its evidence is a candidate for deletion. Stop collecting when a new
97
+ source stops producing rules you would keep — usually after the hooks, the gate entrypoints,
98
+ the packaging manifest, the installer, and the recent commit subjects.
99
+
100
+ ## 4. Choose the sections from the findings, not from a template
101
+
102
+ Group what step 3 produced under headings that match the repo's thick categories from
103
+ step 1. Order by what an agent hits first: orientation and the pre-commit contract before
104
+ style; traps near the commands that trigger them. The file is re-sent to every session, so it
105
+ is a token budget: if the draft exceeds roughly 2,000 words, the excess is a guide — move the
106
+ longest section behind a pointer and keep the one rule that says when to read it.
107
+
108
+ ## 5. Ask for human authority explicitly
109
+
110
+ Ask the user, in outcome terms, which decisions an agent must bring to a person rather than
111
+ settle: releases and publishes, schema or data migrations, anything touching credentials or
112
+ production, deleting or rewriting history, spending money, changing a public contract.
113
+ Write the answer as a short list of *stop points* with what to bring (evidence, options),
114
+ not as a prohibition. If the user has no answer yet, record the question as open in the
115
+ file rather than inventing a policy; if the run is unattended, draft the stop points as a
116
+ proposal marked as requiring confirmation, and never infer one the code does not evidence
117
+ as irreversible or outward-facing.
118
+
119
+ ## 6. Refuse the zero-content preamble
120
+
121
+ Delete any sentence that could open any repository's file: "this file provides guidance to
122
+ agents working in this repository", "follow best practices", "write clean code", restated
123
+ general engineering rules the agent already carries. Read the draft once more asking of each
124
+ line, "what would an agent do differently after reading this?" — a line with no answer goes.
125
+
126
+ ## 7. Directory-scoped files only for the monorepo or platform type
127
+
128
+ A `CLAUDE.md` or `AGENTS.md` placed inside a subdirectory is loaded when a file beside it is
129
+ touched, which makes it survive the loss of early session context. That is valuable when
130
+ subtrees have genuinely different rules — the monorepo case — and noise everywhere else,
131
+ where it splits one file's authority in two. Default to one root file.
132
+
133
+ ## 8. Before handing it over
134
+
135
+ Self-review against three constraints, and fix rather than annotate:
136
+
137
+ - Nothing in the output is boilerplate meant to be pasted elsewhere, and nothing points back
138
+ to this skill, to a version, or to any tool that would "update it later" — no such
139
+ mechanism exists, and a stamp that promises one is a lie the repo will carry.
140
+ - Every rule names the code that enforces it, or is marked as convention.
141
+ - The file states current behaviour only. Change narratives, rejected alternatives and
142
+ handoff logs are pointed to where they live (design notes, the log), not repeated here. A
143
+ live process that is followed but not enforced is current, not history — it belongs in the
144
+ file or behind a pointer, and length decides which.
145
+
146
+ Now read the repo's existing instruction file, if any, and verify each of its claims against
147
+ the code — a stale sentence beside current code reads as fact. Show the user your skeleton
148
+ beside it and name the divergences: a large gap means either the draft or the existing file
149
+ is wrong, and finding out which is the point of the exercise.
@@ -154,6 +154,50 @@ How much independence a review actually bought, as an ordinal grade per reviewer
154
154
  - After resume/clear/relocation, verify pwd, branch, and HEAD against the pinned handoff before acting.
155
155
  - Attribute a parallel session's action (commit, branch, resource) by execution evidence in that session's own transcript, never by token mentions — shared handoff/memory files inject the same tokens into every session's context.
156
156
 
157
+ ## Context Budget And Reset
158
+
159
+ Context growth is a property of the work, not of the host: over 1,075 sessions of 50+
160
+ requests across both CLIs it runs ~2,400 tokens per request (IQR 1,850-2,950), the two
161
+ hosts within 7% of each other (2,280 Claude, 2,450 Codex); the longest sessions (400+
162
+ requests) run lower, ~1,800. One budget therefore serves both, and what differs per host
163
+ is the price of ignoring it. The figure is a prior; the live session is measured below.
164
+
165
+ - **Automatic compaction fires only when the window is nearly full** — Claude at 84-87%
166
+ (windows cluster at 200K and 1M), Codex at ~95% of the window its transcript records as
167
+ `model_context_window` (258,400 on the sessions measured; a later Codex/model pair
168
+ records 353,400 — read the value, never assume it). Cache read is charged per
169
+ request against the whole loaded context, so leaving the reset to the host pays the
170
+ maximum on every request before it. Measured: input is 92-94% of session cost and
171
+ output 6-8%, at a 95-97% cache hit rate — the context is the bill, and uncached input
172
+ is 0.0% of it.
173
+ - Reset deliberately instead. Cost per request falls ~4x from an 867K auto-compact point
174
+ to 200K. The cost-theoretic optimum is ~65K, but it buys a compaction every ~32
175
+ requests at 2-3 minutes each, so 150-250K is the working range and the tail below it is
176
+ not worth chasing.
177
+ - **What bounds the budget is what survives the reset, not the token count.** A compaction
178
+ keeps a ~14K summary plus 3-4 recent messages and discards the rest — unguided when it
179
+ fires on its own. Anything already written to a file survives every reset, so the
180
+ earliest safe threshold is the one where durable state is already on disk. That is what
181
+ decouples cost from loss: without it, resetting more often loses proportionally more.
182
+ - Choose the mechanism by what is known, not by how large the context grew:
183
+
184
+ | Situation | Mechanism |
185
+ |---|---|
186
+ | Stage finished, what to keep is known | clear + dated handoff file — cheapest, and the loss is not a loss |
187
+ | Mid-stage, what to keep is known | write the handoff first, then compact with explicit instructions |
188
+ | Mid-stage, the needed detail is not yet identifiable | compact with instructions; a summary spans what a file cannot yet name |
189
+ | Original detail likely wanted later | clear, and record the transcript path in the handoff — transcripts persist on disk |
190
+ | The next question is unknown | new session plus messaging; only this keeps a round trip available |
191
+ | Growth is tool output | offload to subagents instead — measured ~20x cheaper (tier ~5x, context isolation ~4x) |
192
+ | The judgement trail itself is load-bearing | keep the session and pay the 2-5x resume |
193
+
194
+ - Measure rather than estimate: `agent-bios cost --context <transcript>` (the installed
195
+ entry to `session-cost.py`) reads either
196
+ host's transcript and reports current context, growth rate, compactions, and requests
197
+ remaining against a budget. Codex records `model_context_window` directly; Claude does
198
+ not, so the tool reports its observed auto-compaction point instead of assuming a
199
+ window.
200
+
157
201
  ## Handoff Contract
158
202
 
159
203
  Write for the next agent and re-verification, not narrative. Required content:
@@ -223,3 +267,5 @@ Single owner of numeric defaults; one production environment, 2026-07. Recalibra
223
267
  | two live delegation sessions | tiering saved ~3.3×; discarded prefixes made fresh respawn cheaper; unpinned reviewers inherited FRONTIER |
224
268
  | Codex reach contrast | inherit ~16.5K vs hermetic ~8.7K tokens; schema and stdout/stderr contract verified |
225
269
  | Codex native-spawn probe + HELM E2E | requested max/Ultra native children recorded xhigh/role null; separate read-only roots recorded max and Ultra successfully |
270
+ | 1,075 sessions of 50+ requests, both hosts (2026-08-16; the earlier 62-session top-by-size sample gave ~1,800-2,000) | context grows ~2,400 tok/request (IQR 1,850-2,950), hosts within 7%, ~1,800 in 400+-request sessions; auto-compaction fires at 84-95% of window, never earlier |
271
+ | 2 sessions decomposed by cost component (2026-08) | input 92-94% of cost, output 6-8%, cache hit 95-97%, uncached input 0.0%; an 867K→200K budget cuts cost per request ~4x |