# relay-baton v0.5.0 Release Notes Date: 2026-05-28 > Shipped. This note describes what landed in v0.5; the design sections below > doubled as the work spec and remain accurate to the implementation. ## Summary v0.5 has two independent headline features and a small set of carry-overs from v0.4. **1. Plan-execute mode** — a second workflow shape next to fallback mode. One agent (planner, default Claude) writes a structured `plan.md`; another (executor, default Codex) implements from it. Quality-gated. **2. Context compression mode** — proactive, mid-session compression of `state.md` / `commands.log` so an agent can run longer before fallback. Deterministic only (no LLM). Both an explicit `relay-baton compress-context` command and an auto-watcher inside `run`. The rest of this document covers each in turn. ### What shipped - `relay-baton plan ""` + `relay-baton execute` (+ `--then-execute` chaining). - `.ai-session/plan.md` with `PlanQualityGate`; `PromptBuilder.planner` / `.executor`. - `SessionMeta.workflowMode` + `planning` / `plan_ready` / `executing` / `compressing` statuses. - `relay-baton compress-context` (`--dry-run` / `--threshold` / `--force`) + an auto-pass inside `run`. - `ContextCompressor` with an inline gate (must shrink, state still parses, last fallback hit survives, rollback-on-failure); `LogCompactor.compressLog`. - `planExecute` and `contextCompression` config blocks (with defaults). - Test totals at release: **124 tests** (core 111, cli 13), green in CI. Deferred from the original plan to v0.6: TUI panes for the new modes, and project-level fallback overrides (the testing/observability spine took priority). ### Plan-execute mode v0.5 introduces a **second workflow mode**. Up to v0.4, relay-baton has exactly one workflow: **fallback mode** — primary agent runs until it hits a quota / context wall, then secondary agent resumes from a compact handoff. The orchestration is *reactive*. v0.5 adds **plan-execute mode** — a *proactive* sequence where one agent (typically Claude Code, used as a planner) writes a structured plan document, and another agent (typically Codex, used as an executor) implements it. The two roles are explicit, the plan is a first-class artifact, and the existing fallback machinery still applies inside the execute phase. This answers the recurring question from real usage: *"Codex is great at writing code from a clear spec, Claude is great at reading a repo and deciding what to do — can I just have Claude plan and Codex execute, instead of waiting for one to fail before switching?"* Yes. That's the mode. Scope is intentionally bounded: - New commands: `relay-baton plan` and `relay-baton execute`. - New artifact: `.ai-session/plan.md` with a PlanQualityGate. - `SessionMeta.workflowMode: "fallback" | "plan-execute"` for status tracking. - Token-diet integration: plan goes through the same profile budgets as handoff. - Backward compatible: existing `run` / `handoff` keep working unchanged. Carried over from v0.4's "Recommended Next Work": - OpenCode / Gemini / Aider adapter scaffolds (only the first one that has a real user). - macOS / Windows CI matrix. - Project-level fallback pattern overrides. Out of scope (deferred, with reasons): - TUI fuzzy switcher / command palette — wait until plan-execute is in the TUI dashboard, then design the input surface once. - Per-model tokenizer / semantic plan diff / autopilot — violates `CLAUDE.md` MVP policy. - Multi-turn planner-executor loop with automated review — v0.6 candidate (see "After v0.5"). ## Design — plan-execute mode ### Why "mode", not "another flag on run" The user-facing question we got was: *"is this just another flag on `run`?"* The honest answer is no. The flow shape is different: | | Fallback mode (today) | Plan-execute mode (v0.5) | |---|---|---| | Trigger | Reactive — primary agent fails | Proactive — explicit user invocation | | Artifact | `handoff.md` ("here's how to continue") | `plan.md` ("here's what to build") | | Sequence | one agent → other on failure | planner → executor, always | | Status transitions | `running → fallback_detected → running_fallback → completed` | `initialized → planning → plan_ready → executing → completed` | | Quality gate | HandoffQualityGate | PlanQualityGate (new) + existing gates on execute | Conflating them into `run --plan` would muddle two distinct intents. Better: surface the mode by giving it its own command pair, just like `handoff` has its own command. ### Commands ```bash # 1) Planner phase. Claude reads the repo, writes .ai-session/plan.md. relay-baton plan "" [--with claude] [--diet balanced] [--no-run] # 2) Executor phase. Codex (or another agent) implements from plan.md. relay-baton execute [--with codex] [--from .ai-session/plan.md] [--diet balanced] # 3) Combined, when you trust the planner enough to chain them. relay-baton plan "" --then-execute [--planner claude] [--executor codex] ``` Defaults: `--with claude` for plan, `--with codex` for execute. Both overridable. `--no-run` on `plan` writes the file without launching the executor — useful for human review of the plan before commit. ### `.ai-session/plan.md` Required sections (enforced by `PlanQualityGate`): ``` # relay-baton plan ## Goal … ## Scope (in) … ## Out of scope … ## Approach … ## Steps 1. … 2. … ## Risks … ## Verification how the executor will know it's done ## Next step exactly one bullet for the executor to start with ``` Why these sections: each one answers a question the executor would otherwise have to ask. The plan should be a contract, not a chat transcript. ### Token diet for plans - `plan.md` goes through the same profile system as `handoff.md`. - New profile field: `maxPlanChars` (default mirrors `maxHandoffChars`). - Plans never inline large diffs / logs. The planner is told via prompt to reference files by path, same rules as handoff. - `PlanQualityGate` rejects plan documents that inline `AGENTS.md`, `CLAUDE.md`, or large diff blocks — same shape as `TokenDietQualityGate`. ### Status transitions and observability `SessionMeta` (additive): ```ts interface SessionMeta { // existing fields... workflowMode?: "fallback" | "plan-execute"; planAuthor?: AgentId | null; executor?: AgentId | null; planFinalizedAt?: string; executeStartedAt?: string; // v0.4 fields (startedAt / endedAt / durationMs / handoffCount) still apply } ``` Status values gain two new entries: - `planning` — planner is running, plan.md is not finalized. - `plan_ready` — plan.md exists, executor hasn't started. - `executing` — executor is running. The existing `fallback_detected` / `running_fallback` / `completed` / `failed` states keep their meaning; an `execute` phase can fall back to a different agent the same way `run` already does. So plan-execute mode and fallback mode are *composable*, not exclusive. ### Quality gates New: `PlanQualityGate` (mirror of HandoffQualityGate) - Required: plan.md present and non-empty. - Required sections: Goal, Scope (in), Out of scope, Approach, Steps, Risks, Verification, Next step. - Non-empty Steps list. - Non-empty Next step. - `plan.md` ≤ active profile's `maxPlanChars`. - No inline of large `commands.log` / `AGENTS.md` / `CLAUDE.md`. Fails the `execute` launch the same way HandoffQualityGate fails fallback launches. `--force` overrides. ### Prompt construction - **Planner prompt** (`PromptBuilder.planner(task)`): - reads `AGENTS.md` / `CLAUDE.md` / `task.md` / `repo-map.md` first (by reference, not inlined), - explicitly told NOT to write code, only the plan document, - structured output requirement — fail if sections are missing, - encouraged to flag uncertainty in Risks rather than guess in Steps. - **Executor prompt** (`PromptBuilder.executor()`): - reads `plan.md` first, - works step by step, updating `.ai-session/state.md` after each step, - encouraged to STOP and escalate (write to `errors.md`) when reality diverges from the plan, rather than silently improvise. ### Agent role matrix The roles are configurable, not hardcoded: | Planner | Executor | Use case | |---|---|---| | Claude Code | Codex CLI | **default** — Claude reads the repo well, Codex executes from spec | | Codex CLI | Claude Code | when Codex has more context (already in a long Codex session) | | Claude Code | Claude Code | "two-pass" — separate planning context from execution context for token budget reasons | | Codex CLI | Codex CLI | same, on the Codex side | `relay-baton.config.json` gains an optional `planExecute` block: ```json { "planExecute": { "defaultPlanner": "claude", "defaultExecutor": "codex", "maxPlanChars": 30000 } } ``` ## Context compression mode This is a **second headline feature** for v0.5, alongside plan-execute mode. The two are independent. ### Problem Today's token diet is **deterministic pre-compaction**: at handoff time we trim diff / log / state to a character budget and write a fresh document. That's good for the boundary between two agents, but does nothing for the *interior* of an agent run: - The agent's own context window fills up turn by turn. - `state.md`, `commands.log`, and the cumulative tool-call history grow unboundedly during a long run. - By the time we'd detect a `context length exceeded` fallback, the agent has already wasted thousands of tokens re-reading its own thinking. The fix is **mid-session compression** — periodically or proactively summarize the running context into something smaller, without breaking continuity. Inspired by `/compact` style commands in Claude Code itself, but applied to relay-baton's own artifacts so it works the same regardless of which agent is active. ### What v0.5 will add **New command** ```bash relay-baton compress-context [--profile caveman] [--threshold 0.8] [--dry-run] ``` - Inspects the current `.ai-session/` for "weight" — total chars of `state.md`, `commands.log`, `decisions.md`, `errors.md` against the active profile budgets. - If weight crosses `--threshold` of the budget (default 80%), rewrites `state.md` and `commands.log` to compressed forms in place. - `--dry-run` reports what would change without writing. **Deterministic only.** Same MVP rule as today: no LLM call inside relay-baton. Compression here means: - `state.md` → re-emit through `StateCompactor` plus dedup of repeated `Done:` / `In Progress:` bullets across history. - `commands.log` → keep last N lines + lines surrounding fallback-pattern hits + lines surrounding exit codes; everything else collapsed into `[N lines elided]` markers. Original kept at `commands.log.full.` for one rotation. - `decisions.md` / `errors.md` → drop entries older than the last successful handoff (recoverable via git history if needed). **Auto-trigger from `run`** `run` already streams the agent's stdout. Add a watcher that estimates running context (chars in `commands.log` + active `state.md`) every N lines. When it crosses the threshold: - Print a one-line notice `[relay-baton] context approaching limit; compressing…`. - Run the same logic as `relay-baton compress-context`. - The next agent turn sees a smaller `state.md` and a tighter `commands.log` tail, which is the data the continuation prompt points at anyway. This is the **proactive** counterpart to the existing **reactive** fallback machinery. Two layers of defense against context exhaustion: 1. Compress mid-run (this feature) — extends how far one agent can go. 2. Fall back to another agent (existing) — when compression alone isn't enough. ### Status transitions Add one optional status, `compressing`, between `running` and the next user-visible status. Brief — typically sub-second. Surfaces in `relay-baton status` and the TUI dashboard so the user understands the pause. ### Configurability ```json { "contextCompression": { "enabled": true, "auto": true, "threshold": 0.8, "rotateRawArtifacts": true } } ``` - `enabled: false` is the v0.4 behavior — pure pre-compaction at handoff time only. - `auto: false` disables the watcher in `run` but keeps the explicit `compress-context` command available. ### Quality gate New: `ContextCompressionGate` — runs after a compression cycle to verify: - `state.md` still parses into the canonical sections. - `commands.log` still contains at least the last fallback-pattern hit (if any) so detection isn't disarmed. - Rotated raw file is on disk and non-empty. - Compressed sizes are actually smaller than pre-compression sizes (otherwise the cycle was wasted). If the gate fails, the compression is rolled back from the rotated raw file. Compression must never make the session **worse**. ### Out of scope (still) - Semantic / LLM-based summarization. Defer until we have a strong story for cost + determinism. - Cross-session compression (compressing artifacts from past sessions into a history archive). v0.6 candidate. - Adaptive thresholds based on per-model context limits. v0.6 candidate. ### Composition with plan-execute mode - During `relay-baton execute` (v0.5 plan-execute mode), the same auto-compression watcher applies to the executor agent. - Plan documents themselves are NOT auto-compressed mid-execute — the plan is the contract, rewriting it would change goalposts. Compression only touches running state / log. ## Other v0.5 items ### OpenCode / Gemini / Aider adapter scaffolds Trigger this only when an external user shows up with a concrete use case for one of them. Otherwise it's premature surface area. Concrete deliverable when triggered: - `packages/core/src/agents/Adapter.ts` implementing the existing `AgentAdapter` interface. - One test mirroring `CodexAdapter.test.ts`. - Documentation row in `README.md` requirements table with the relevant subscription / CLI link. ### macOS / Windows CI matrix Add a `matrix.os` to `.github/workflows/ci.yml`. Likely fast cost increase. Decide at v0.5 implementation time whether we accept that. If yes: - `matrix: { os: [ubuntu-latest, macos-latest, windows-latest] }`. - Watch for path-separator and line-ending tests; expect at least one Windows-only fix. ### Project-level fallback pattern overrides `BatonProject` gains an optional `fallbackPatterns?: string[]` that overlays the global patterns for `run` / `handoff` calls scoped to that project. Useful when one repo's CI consistently prints something a sibling repo doesn't. ## Compatibility - `plan` and `execute` are new subcommands; nothing renamed. - `SessionMeta` additions are optional fields; existing sessions keep working. - A session created in fallback mode (existing behavior) never transitions through `planning` / `plan_ready`. The new status values are only emitted by `plan` / `execute`. - `relay-baton.config.json.planExecute` is optional; defaults apply if absent. - No breaking changes planned. If v0.4 work surfaces a bug, fix it in v0.4.x rather than mutating v0.5 scope. ## Order of Work (suggested) This order keeps each step shippable. **A-block** is plan-execute mode; **B-block** is context compression mode. The two blocks are independent — either can ship without the other. A. Plan-execute mode 1. **`SessionMeta.workflowMode` + new status values** (~30 min) — types-only change, lays the foundation. 2. **`plan.md` schema + PlanQualityGate** (~1h) — pure functions, easy to test. 3. **`PromptBuilder.planner` / `PromptBuilder.executor`** (~30 min) — string templates, snapshot tests. 4. **`relay-baton plan` command** (~1h) — wraps existing AgentRunner, writes plan.md, runs PlanQualityGate. 5. **`relay-baton execute` command** (~1h) — reads plan.md, runs executor agent, reuses HandoffQualityGate for any in-execute fallback. 6. **`--then-execute` chaining flag** (~30 min) — orchestrates plan → execute in one call. B. Context compression mode 7. **`StateCompactor` dedup pass + `LogCompactor` rotation helper** (~45 min) — pure functions on existing modules. 8. **`relay-baton compress-context` command + ContextCompressionGate** (~1h) — explicit invocation path first. 9. **Auto-watcher inside `run`** (~1h) — threshold check, brief `compressing` status, rollback-on-gate-failure. 10. **`contextCompression` config block** (~20 min) — config plumbing + defaults. C. Wrap-up 11. **TUI: plan-execute status pane + compression status indicator** (~45 min). 12. **README + i18n updates** for both new modes (~45 min) — translations follow the English shape. 13. **Optional**: project-level fallback pattern overrides if time allows. 14. Version bump to 0.5.0, finalize this note, tag. Items 1–5 and 7–9 are the v0.5 minimum. Everything else is a layered increment that doesn't block the release. ## After v0.5 (early v0.6 candidates) Plan-execute lineage: - **Multi-turn planner-executor loop** — planner re-engages when executor reports the plan diverged from reality. Needs careful budget design to avoid runaway token use. - **Plan diffing** — when re-planning, show what changed vs the previous plan instead of writing a fresh document. - **Plan execution receipts** — `plan.md` gets annotated with `[done]` / `[skipped]` markers as steps complete, producing a verifiable trail. - **Cross-session plan reuse** — pointing a new session at an existing `plan.md` from another repo for "same change, different repo" workflows. Context-compression lineage: - **Adaptive per-model thresholds** — auto-compression threshold tuned to the active agent's known context size (e.g. compress earlier when the agent advertises a smaller window). - **Cross-session compression** — fold artifacts from past completed sessions into a single rolled-up archive, instead of leaving each session's raw files indefinitely. - **Semantic / LLM-based summarization** — only once we have a cost-bounded, deterministic-fallback design that respects the no-API-call MVP rule for the relay-baton process itself. These are explicitly *not* v0.5 work. Listed so the v0.5 scope stays small while the longer vision is documented.