# Eve Small-Model Excellence (SMX) — Design (2026-08-16)

Companion execution ledger: `EVE_SMALL_MODEL_EXCELLENCE_TODOS_2026-08-16.md`
(root). Status: DESIGN — nothing below is shipped unless a TODOS checkbox says
so.

## Thesis

Run the assistant brain (member-facing Lilith + builder-facing Eve, one runner)
on the smallest model that passes a regression-locked evaluation deck — today's
champion is `deepseek/deepseek-v4-flash-0731` at ~$0.10/$0.18 per 1M tokens,
~100x under frontier — and make that sustainable by moving intelligence out of
the model and into the harness: a strong model (Claude, at build time, in
session) does the capability analysis, writes the skills, designs the checkers,
and distills the exemplars; the small model executes inside a harness that
routes, scopes, validates, verifies, and escalates. The deck is the contract
between the two: no skill line, prompt edit, checker, or model swap lands
without a measured delta.

This is not a new philosophy for this repo — it is the Eve polish initiative's
own discovery, systematized. The polish work already proved (with 20-run arms
and Fisher exact tests) that a prompt sentence is a request and a server-side
check is a mechanism (`routes/assistant.ts:1143` block, EVE-VIS-128/277, the
announce-before-act wire hold). SMX finishes the thought: **compile-time
intelligence, run-time economy.**

## Scope

**In scope:** the assistant brain end to end — provider bindings
(`apps/oshun/bff/src/assistant/agent-provider-config.ts`), the turn runner
(`agent-turn-runner.ts`), prompt assembly and toolset assembly
(`routes/assistant.ts` streaming turn route), all tool surfaces (member domain
adapters, docs, tours, audit, admin, workbench read/write), the eval harness
(`evals/`), turn metrics, and the OpenRouter routing configuration in
`@oshun/ai`.

**Out of scope:** the codex coding-agent lane (`tools/eve-codex-agent.mjs`,
subscription-billed, its own loop); the deterministic engine's message route
(already model-free); voice STT/TTS vendors; member-web panel rendering (polish
initiative owns it). The pi-ai subscription transport
(`CodexSubscriptionProvider`) stays as an explicit-only provider and benefits
from every harness change for free, since all of SMX lands above the
`LLMProviderInterface` seam.

**Adjacent, not forked:** the repo has a second brain — `@oshun/assistant`, the
Hermes-parity autonomous agent (channels, scheduling, its own Voyager-style
skill store; `docs/audits/HERMES_PARITY_TODOS.md`). SMX builds for the BFF
brain, but the deck harness pattern, skill disciplines, and checkers are
designed so that stack can consume them too — two brains, one measurement
doctrine.

## Doctrine

Eight principles. Each is anchored to evidence already in this repo; none is
aspiration.

1. **Mechanism over request.** A rule the model must obey becomes a server-side
   check, not (only) a prompt sentence. Anchor: EVE-VIS-128 (audit-skip
   provenance enforced by the store), EVE-VIS-277 (mutating tools refuse without
   the confirm bridge), the announce-before-act stream hold, and the measured
   verdict "this sentence is NOT a fix" (20%→6%, p=0.34) in
   `routes/assistant.ts:1143`.
2. **Evals decide, not vibes.** Every prompt, skill, checker, and model change
   is judged by the deck, with pinned routing and stated statistics. Anchor: the
   repo's Fisher-exact discipline and the existing pure, unit-tested grader ("a
   vacuous grader cannot hide", `evals/eval-harness.ts`).
3. **Compile-time intelligence, run-time economy.** The strong model is a
   compiler: it analyzes failures, writes skills, authors exemplars, proposes
   checkers — at build time, in session, costing nothing at runtime. The small
   model is the runtime. The strong model NEVER sits in the serving path
   (CLAUDE.md cost rule).
4. **Smallest model per LEG, not per product.** Turn execution, routing,
   judging, and embedding are separate legs with separate bindings; each leg
   gets the cheapest model that passes its own gate. A product-wide "we use
   model X" is a category error.
5. **Context is a budget, and small models pay it first.** Long undifferentiated
   prompts are where flash-class models drown: today every tool-capable turn
   carries the full conduct block, the anchor registry, and the curated tour
   catalog regardless of task, and an admin turn can offer ~60 tools. Anchor:
   the measured `tara_favorites` skip (4/20) under the full surface.
6. **Every failure becomes a case; every fix becomes a mechanism, a skill line,
   or an exemplar — traceable both ways.** Anchor: the defect ledger (279 rows,
   every closed S1/S2 with a runnable lock,
   `tools/eve-polish/ ledger-lock-audit.mjs`). SMX extends the same discipline
   to model behavior.
7. **Price the route, not the model.** An OpenRouter slug is an auction
   (eighteen providers behind `deepseek-v4-flash`, $0.068 to $0.200 input).
   `sort: price` + pinned quantization for measurement; live-priced tables
   before any spend decision. Anchor: `docs/agents/model-cost-openrouter.md`,
   including its two recorded corrections.
8. **Honesty invariants are harness-owned.** Fabrication is caught by checkers
   and refused by the server, never merely discouraged in prose. The quality bar
   ("never ship a fabricated result") applies to the MODEL'S output the same way
   it applies to our code.

## Current state (audited 2026-08-16)

### Assets — already built, load-bearing for SMX

| id  | asset                                                                                                                                                       | where                                                            |
| --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| A1  | Bounded provider-agnostic turn loop; sentence-atomic streaming; scaffolding-tail drop; envelope unwrap; tools-off final iteration; honest `refused` outcome | `agent-turn-runner.ts`                                           |
| A2  | Five provider bindings behind one interface (anthropic, openai, openrouter, codex-cli, codex-subscription); fail-closed resolution                          | `agent-provider-config.ts`, `@oshun/ai`                          |
| A3  | ~38 member tools + admin/audit + ~17 workbench tools, server-injected auth, typed definitions                                                               | `agent-tools.ts`, `admin-agent-tools.ts`, `audit-agent-tools.ts` |
| A4  | Confirm bridge; `mutating` tools refuse without it; actor attribution per conversation+turn                                                                 | `action-confirm.ts`, workbench bindings                          |
| A5  | Server-side behavior checkers on the wire: safety supersede (stream + completion), announce-before-act hold, audit-skip provenance, completion checker      | `routes/assistant.ts`                                            |
| A6  | Golden eval harness at the REAL route surface with a pure unit-tested grader; live runs credential-gated (`pnpm --filter @oshun/bff eval:assistant`)        | `evals/eval-harness.ts`, `evals/assistant-golden.eval.ts`        |
| A7  | Ten-conversation live battery, transcript capture                                                                                                           | `tools/eve-conversation-battery.mjs`                             |
| A8  | Defect ledger: 279 rows, zero open, machine-audited locks                                                                                                   | `docs/audits/EVE_POLISH_DEFECT_LEDGER_2026-08.md`                |
| A9  | Durable turn metrics + per-member/tenant token budgets with honest refusal                                                                                  | `turn-metrics.ts`                                                |
| A10 | Cost doctrine: price-the-route method, `OPENROUTER_PROVIDER_SORT=price`, quantization pinning, the `-0731` reproducibility decision                         | `docs/agents/model-cost-openrouter.md`                           |
| A11 | Deterministic engine fallback (agent unconfigured → honest `agent_not_configured`)                                                                          | engine + routes                                                  |
| A12 | Feature catalog (typed graph, CI integrity gates) + durable audit store                                                                                     | `@oshun/shell-assistant/feature-catalog`, `audit-store.ts`       |
| A13 | Docs search over the curated corpus (member) / full estate (admin)                                                                                          | `docs-search.ts`                                                 |

### Gap register — what "smallest model, excellent behavior" still needs

Each gap gets closed by a numbered phase; the TODOS file carries the tasks.

- **SMX-G1 — Deck too small to decide anything.** 7 golden cases, member-side
  only, single-shot, no reliability statistics, no committed scorecard, no
  ratchet. You cannot downshift a model or land a skill against 7 cases.
- **SMX-G2 — Monolithic prompt.** One conduct block (~20 rules covering member
  data, audits, tours, highlighting, ids) plus persona, memory, anchors
  registry, page context, tour catalog, workbench block — assembled for every
  turn regardless of task (`routes/assistant.ts:2117`). No task families, no
  just-in-time loading.
- **SMX-G3 — Full tool surface every turn.** Up to ~60 tools offered at once in
  admin scope. Measured harm on the champion model: `tara_favorites` skipped
  4/20 (Phase 4.1 record). No per-task scoping, no forced tool choice.
- **SMX-G4 — No uniform argument boundary.** Tool args go from the provider
  straight to per-tool `execute`; no shared schema validation, no instructive
  error contract, no single-repair retry. Small models misfill arguments far
  more often than frontier models; today each tool improvises its response.
- **SMX-G5 — Designed-but-unbuilt grounding checks.** EVE-VIS-080 (refuse
  member-owned-data claims when no member-data tool ran THIS turn) is written
  down as "the real mechanism" in a comment and tracked, not built. No
  id/title/count citation check exists (EVE-VIS-211 full-id rule is prompt-only
  plus one store-side resolve).
- **SMX-G6 — No escalation ladder.** A wrong or checker-refused cheap turn has
  no verified-failure retry and no bounded model bump; quality problems become
  member-visible instead of costing one extra iteration.
- **SMX-G7 — One binding for every job.** The same model serves smalltalk,
  six-tool audit protocol turns, and workbench mutations; router/judge legs
  don't exist as bindings. No per-leg registry, no downshift protocol.
- **SMX-G8 — Telemetry can't answer SMX questions.** `AssistantTurnMetricRecord`
  lacks cost, cached-token counts, task family, skill version, checker verdicts,
  escalation tier, per-tool error names. No failure taxonomy, no cost report.
- **SMX-G9 — Zero exemplars anywhere.** The prompt is all rules, no worked
  examples. Few-shot exemplars are the single highest-leverage prompt content
  for small models and the easiest thing for a strong model to author and
  verify.
- **SMX-G10 — Cache-hostile prompt order.** The session-dynamic engine prompt
  (persona/daypart) is the FIRST prompt part; every stable byte after it is
  un-cacheable on providers with prefix caching. Order is a pure cost lever at
  zero behavior change — if the routed provider caches; measured, not assumed.
- **SMX-G11 — No model-facing adversarial coverage.** Nothing probes prompt
  injection via tool results (docs snippets, thread bodies, page context
  carrying instructions), fabrication traps against empty stores, or
  small-model-specific failure shapes. Small models are more injectable and more
  suggestible; the deck must measure it.
- **SMX-G12 — Battery judging is manual.** Ten conversations are captured "for
  judging" with no automated rubric judge, so multi-turn quality has no
  regression signal.
- **SMX-G13 — No abort propagation (verified 2026-08-16, pi review).**
  `runAssistantAgentTurn` carries no `AbortSignal`: a member closing the panel
  mid-turn does not cancel the provider stream — tokens keep billing and tools
  keep executing to turn end. pi-ai treats combined abort signals as
  first-class; Eve should too.
- **SMX-G14 — No wire-level turn inspector.** Nothing records exactly what the
  model READ (assembled prompt parts, tools offered, per-iteration payloads), so
  distillery work starts blind. pi's debug ethos — the exact request is always
  inspectable — is the standard to meet.

## Target architecture

Twelve components. Wiring points named so the TODOS tasks are checkable.
Everything lands above `LLMProviderInterface`; nothing binds to a vendor.

### C1 — Task-family router (deterministic first)

A pure function `resolveTaskFamily(turnFacts) → { family, why }` consulted by
the turns route before prompt/toolset assembly. Families v1:

`member-data` · `docs` · `navigate` · `tour` · `audit` · `workbench-read` ·
`workbench-write` · `capability-smalltalk` · `general` (fallback = today's full
surface, so routing FAILS OPEN to current behavior).

Priority of signals, highest first — **server state beats text**:

1. Active audit run in the audit store → `audit` (state, not classification).
2. Pending confirm-card reply / mutation verbs + admin scope → workbench
   families.
3. Page context path + client capabilities (tour_player, action_confirm).
4. Keyword/pattern heuristics reusing the deterministic engine's vocabulary and
   the feature catalog's titles.
5. Nothing matched → `general`.

A micro-model classifier leg is DEFERRED until misroute telemetry proves the
deterministic router insufficient (decision recorded either way). Router is
pure, unit-tested, and every routed turn logs `{family, why}`.

### C2 — Skill registry (typed, budgeted, eval-linked)

`apps/oshun/bff/src/assistant/skills/` — one TS module per family exporting a
typed `AssistantSkill`:

```ts
interface AssistantSkill {
  id: string; // 'skill.audit.v3'
  family: TaskFamily;
  body: string; // the just-in-time playbook block
  exemplars: readonly SkillExemplar[]; // verified worked examples, ≤2
  toolAllowlist: readonly string[]; // must ⊆ real tool names (spec-enforced)
  checkerIds: readonly string[];
  maxIterations?: number; // audit needs 6; smalltalk needs 2
  evalCaseIds: readonly string[]; // every skill traces to deck cases
}
```

Registry disciplines, all spec-enforced (not asked politely):

- **Token budget:** core conduct ≤ ~350 tokens; skill body + exemplars ≤ ~700
  (chars/4 heuristic with stated margin — an approximation, documented as one).
  A budget-busting skill fails the registry spec.
- **Traceability lint:** a skill with zero `evalCaseIds`, or naming a deck case
  that doesn't exist, fails the spec. No unmeasured sentence — the polish
  initiative's rule, made structural.
- **Allowlist integrity:** `toolAllowlist ⊆` the union of actually-registered
  tool names, checked against the live builders, so tool renames can't silently
  orphan a skill.
- **Privilege clamp (from the Hermes-parity skill store):** at runtime a skill's
  allowlist INTERSECTS the tools the turn is actually authorized to carry
  (scope, capabilities) — a skill can only narrow a surface, never widen it
  (`clampAllowedTools` semantics, `libs/oshun/assistant/src/skills/`).
- Versioned ids + a changelog line per edit, because P8's maintenance loop needs
  to correlate skill versions with telemetry.

The current `AGENT_CONDUCT_INSTRUCTIONS` block is dismantled INTO this registry:
universal honesty/grounding rules stay in the small core; everything
family-specific (audit protocol, tour conduct, workbench register, highlight
rules) moves into its skill and stops taxing unrelated turns.

### C3 — Toolset scoper + forced tool choice

Per-family filter over the existing `buildAssistantAgentToolset` output —
allowlist projection, nothing rebuilt. Tool-mandatory families (`member-data`,
`docs`) run with `toolChoice: 'required'` on the first iteration where the
provider supports it (OpenAI-compatible wire does; capability-checked per
provider, honest fallback to `auto`). Target surface per routed turn: ≤ 12 tools
instead of ~60. `general` keeps the full surface — and for it (and any family
whose tail of rarely-used tools resists a static allowlist) adopt pi-ai's
deferred-tools pattern: tail tools offered as names only plus a `load_tools`
meta-tool that pulls full definitions on demand.

### C4 — Constrained argument boundary (validate → instruct → repair once)

One shared validation layer at the `toolset.execute` seam: every tool's JSON
schema (already declared in its `ToolDefinition`) is enforced BEFORE the tool
body runs. On violation the tool result is a structured instructive error —
exact field, expected shape, one inline example — and the runner grants ONE
repair iteration for argument errors (bounded; a second violation returns the
honest failure to the member). For typed non-tool outputs (router/judge legs,
never member prose) use OpenRouter structured outputs
(`response_format: json_schema`) with `provider.require_parameters: true` so
only endpoints that honor the contract serve the call — verified with a positive
control the way the quantization flag was (`int2` → 400 proves arrival). Where
the routed endpoint supports grammar-constrained sampling (pi-ai ships
constrained-sampling grammar tool inputs), constrain argument GENERATION itself
rather than only validating after — availability through OpenRouter is measured
per provider before it is relied on.

### C5 — Grounding checkers (the EVE-VIS-080 family, built)

Pure, unit-tested functions over the turn's observable facts (tool calls +
results + reply text), run server-side before `turn.complete`:

- **Member-data grounding (VIS-080):** reply claims member-owned data
  (possessive + domain-noun heuristic, tuned on the deck and honest about being
  a heuristic) while zero member-data tools ran this turn → hold the reply,
  inject the verdict, allow one corrective iteration; if it still claims
  ungrounded data, replace with an honest "let me check" refusal. The 20%→6%
  prompt sentence stays; the checker is the fix.
- **Citation grounding:** every full-id-shaped token (work items, threads,
  tours, decisions) and every catalog title named in the reply must appear in
  THIS turn's tool results; violations follow the same hold → correct-once →
  honest-refusal path. Extends EVE-VIS-211 from a request to a mechanism.
- **Count sanity:** numeric claims about list sizes must match the length the
  tool returned (exact-extraction cases only; no NLP heroics — stated limit).

Checkers follow the wire-hold pattern already proven for announce-before-act:
sentence-atomic streaming is what makes clean interception possible.

### C6 — Escalation ladder (verified failure, bounded, logged)

Per-family policy, default:

1. **Tier 1:** champion small model (registry-bound).
2. **Tier 2:** same model, one retry carrying the checker/validator verdict.
3. **Tier 3:** escalation model — a mid-tier slug chosen in the P7 tournament,
   cheap-first, NEVER a frontier model by default (CLAUDE.md binding); builder
   surface may enable a stronger tier-3 by explicit env opt-in.

Every escalation emits `{family, tier, reason, caseShape}` to telemetry — an
escalation is a signed confession that a skill or checker needs work, and P8's
maintenance loop consumes exactly that queue. Member-facing latency stays
bounded: at most one extra iteration per mechanism, p95 budget per family
tracked. The existing per-member token budget stays authoritative above all
tiers.

### C7 — Per-leg model registry

`apps/oshun/bff/src/assistant/model-registry.ts` — the single authority mapping
legs → bindings:

| leg        | job                                | default binding                         |
| ---------- | ---------------------------------- | --------------------------------------- |
| turn       | member/builder turns (per family)  | `deepseek/deepseek-v4-flash-0731` (pin) |
| escalation | tier-3 retries                     | chosen in P7, mid-tier                  |
| judge      | deck rubric judging (build/eval)   | chosen in P7, must ≠ turn leg model     |
| router     | residual classification (deferred) | none until misroute data demands it     |
| embedding  | docs search (exists)               | unchanged                               |

Each row: pinned slug, provider preferences (`sort: price`; fp8 pin for
MEASUREMENT arms per A10), price snapshot + date + the command that produced it.
Env overrides keep working; the registry is the default, not a cage. Production
routing keeps today's semantics unless a row says otherwise.

### C8 — Eval deck v2 (the instrument everything else is measured with)

Grow 7 → 150+ cases, kept in the existing harness (route surface, pure grader).
Composition, mapped to sources:

- **Ledger-born locks (~50):** closed EVE-VIS rows whose failure was MODEL
  behavior (tool skips, id elision, operator register, envelope leaks,
  announce-before-act ordering, audit-skip provenance, internal-id leakage)
  re-expressed as deck cases. **Ledger-born cases are non-negotiable:** a
  candidate model or skill that fails one at k=10 is disqualified regardless of
  aggregate wins — the same "regression lock" semantics the ledger already
  enforces for code.
- **Family coverage (~60):** per family × (happy path, empty-store honesty,
  tool-error honesty, refusal correctness), including the admin/workbench and
  audit-mode behaviors the current 7 cases don't touch.
- **Adversarial (~30):** prompt-injection probes through every untrusted channel
  a tool result can carry (docs snippets, thread bodies, page context, work-item
  titles containing instructions); fabrication traps (empty favorites,
  nonexistent docs); id-precision traps; member-vs-operator register traps.
- **Multi-turn (~10):** the battery's ten conversations, promoted from "captured
  for judging" into graded multi-turn deck cases (SSE per turn, grader over the
  full transcript).
- **Provider-free self-checks:** crisis supersede, budget refusal, confirm
  bridge refusal — free to run, keep the harness honest.

Grader statistics (new, in-harness): per-case k runs (default k=3; k=10 for
promotion decisions); report **pass@1** (mean) and **pass^k** (all k passed —
the reliability number that matters for agents); Wilson 95% intervals per
family; flake policy per the existing comment — a case flaky on a frontier model
poisons regression signal and gets rewritten or dropped, recorded.

Artifacts, committed:

- `docs/audits/EVE_SMX_SCORECARD.md` — human-readable scorecard per run:
  per-family pass@1/pass^k + CI, cost per run and median per turn (from
  `usage: {include: true}`), latency, model + provider + quantization served,
  prompt-bytes hash.
- `docs/audits/eve-smx-ratchet.json` — machine ratchet: per-family floors + the
  prompt-bytes hash the scorecard was measured against. CI (spending nothing)
  recomputes the hash of conduct core + skills + registry; a mismatch with no
  fresh scorecard commit fails the build. **You cannot change what the model
  reads without re-measuring what the model does.** Floors only ratchet upward;
  lowering one requires a human sign-off row in the scorecard, same as the
  conventions ratchet.

Spend gating unchanged and explicit: live deck runs are operator-invoked only,
never CI-automatic; CI runs provider-free cases + grader/scripted-binding specs.
Estimated full-deck cost at champion prices: ~600–800 turns ≈ $0.15– 0.40 per
k=3 run (estimate, to be replaced by the measured number in the P0 scorecard).

### C9 — Rubric judge (validated before trusted)

For qualities deterministic expectations can't grade (tone, brevity, register,
multi-turn coherence): a rubric judge on the judge leg's cheap model. The judge
is VALIDATED before it grades anything that gates: ≥40 human-labeled transcripts
(drawn from battery + polish evidence), agreement threshold stated in the
scorecard; below threshold the judge stays advisory and the case class stays
human-judged. Judge model ≠ turn model (self-preference bias), judge prompts
live in the skills directory under the same budget/traceability lints.

### C10 — The distillery (the strong-model loop, standing)

The pattern the initiative exists for, made a pipeline rather than a heroic
one-off. Claude (or any frontier model, IN SESSION, build-time only):

1. Reads the escalation queue + failure taxonomy + newest deck failures.
2. Writes/edits skills, exemplars, checker rules, tool-description examples.
3. Authors exemplar transcripts by specifying (not roleplaying) ideal tool
   sequences; exemplars are verified by running the deck, not by looking
   plausible.
4. Lands everything as ordinary commits measured by deck delta; the ratchet hash
   forces the re-measure.

Escalation-tier transcripts that PASSED after a tier-2/3 retry are distillation
gold: the delta between the failed cheap attempt and the passing retry is
exactly what the exemplar should teach. The distillery consumes its own exhaust.

### C11 — Telemetry v2 + cost ledger

Extend `AssistantTurnMetricRecord` (snapshot-versioned, per its own v1→v2
precedent): `costUsd`, `cacheReadTokens`, `cacheWriteTokens` (pi-ai's `Usage`
splits both cache directions with per-bucket cost — adopt that shape), `family`,
`skillVersion`, `checkerVerdicts`, `escalationTier`, `escalationReason`,
`toolErrors` (by tool name), `iterations`. New `tools/eve-smx-cost-report.mjs`:
per-family cost, cache-hit rate, escalation rate, checker-fire rate, top failure
shapes — the weekly input to the maintenance loop. Nothing silently rationed:
budgets keep their honest-refusal semantics.

### C12 — Cache-ordered prompt assembly + context budgets

Reorder `promptParts`: stable bytes first (core conduct, then the routed skill),
session/turn-dynamic bytes last (persona, daypart, memory recall, page context,
anchors — and anchors/tour-catalog blocks only for families whose skills use
them). Measure `cachedInputTokens` before/after on the routed provider; if the
auction's cheap endpoint doesn't cache, the reorder still costs nothing and
every cache-capable route inherits it. Per-family history window and a
server-side digest for long tool results (with an explicit "call again with
offset" affordance) complete the context budget. Behavior guard: the deck must
be flat within CI across the reorder — this phase buys cost, not behavior.

Two further levers from the pi review: (1) **compaction over truncation** —
today's history is `slice(-AGENT_HISTORY_MAX_TURNS)`, which amputates early
context; a rolling summary carried in the dynamic suffix (deterministic or
cheap-leg authored, behind a flag, deck flat) preserves it at bounded cost; (2)
**session cache affinity** — pass whatever session/user affinity the routed
provider honors (the codex transport already takes `sessionId`; the champion
path passes nothing) and record the finding even when the answer is "none
honored".

## Model strategy

- **Champion (turn leg):** `deepseek/deepseek-v4-flash-0731`, `sort: price`, fp8
  pinned for measurement arms — the reproducibility trade recorded in A10
  stands; do not re-litigate it without re-reading that file.
- **Challenger slates** are named by CLASS here and priced by ROUTE at decision
  time (any $ written today would be stale by P7): sub-flash tier for
  router/judge legs (qwen-flash-class, gemini-flash-lite-class, tiny llama
  class, `:free` variants for dev where rate limits allow); flash-class
  alternates for the turn leg; one mid-tier for the escalation leg.
- **Promotion protocol (any leg):** price the route live → paired deck runs,
  same pinned quantization, k=10 → promote only if (a) no ledger-born case newly
  fails, (b) every family's Wilson lower bound ≥ ratchet floor, (c) cost and p95
  latency deltas are stated in the scorecard. Demotion is symmetric and
  automatic when telemetry floors break (P8).
- **Frontier models:** never in the serving path, never in tests/harnesses
  (CLAUDE.md binding); allowed exactly two jobs — the distillery (build-time, in
  session) and one-off flake audits of deck cases ("would a frontier model pass
  this deterministically?" — the existing golden-catalog admission rule).

## Phase plan (summary — tasks live in the TODOS)

| phase | name                               | closes                  | exit gate                                                                                                                                                                                                |
| ----- | ---------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| P0    | Instrument & baseline              | G1, G13, G14, G8 (part) | ≥120 graded cases across all families incl. adversarial; telemetry v2 fields land; abort propagation + turn-trace capture landed; baseline scorecard + ratchet committed; ZERO prompt/tool changes in P0 |
| P1    | Cache order & context budgets      | G10                     | deck flat within CI; cached-token + cost delta measured and recorded; compaction flag + affinity findings recorded                                                                                       |
| P2    | Router, toolset scoping, skills v1 | G2, G3, G9 (part)       | tool-selection families strictly better; no family below floor; prompt tokens/turn and tools/turn reduced, measured; misroute rate < stated threshold                                                    |
| P3    | Constrained argument boundary      | G4                      | tool-arg error rate down, measured; repair loop bounded; no floor broken                                                                                                                                 |
| P4    | Grounding checkers                 | G5                      | adversarial fabrication family improves; VIS-080 shape ~0 at k=10; wire holds verified at the SSE level                                                                                                  |
| P5    | Escalation ladder + model registry | G6, G7                  | effective pass^k with ladder vs cost curve published; escalation rate < threshold; zero unlogged escalations                                                                                             |
| P6    | Distillery pass 1 (exemplars)      | G9                      | measured family deltas from exemplars alone; skills changelog + traceability complete                                                                                                                    |
| P7    | Downshift tournament               | G7 (finish)             | every leg on the cheapest model passing protocol; decision table with live-priced routes committed                                                                                                       |
| P8    | Sustainment loop                   | G12 + drift             | maintenance runbook live; held-out case set reporting separately; monthly route-price note; demotion triggers wired                                                                                      |

Phases are strictly ordered — P0 before anything touches a prompt, because an
uninstrumented improvement is indistinguishable from a regression with good
posture.

## Lessons intake — pi / Hermes review (2026-08-16)

Provenance: read against the installed `@earendil-works/pi-ai` 0.83.0 source and
`docs/audits/HERMES_PARITY_TODOS.md` after the user asked what agents like pi
and Hermes can teach this harness.

**Dependency decision — keep pi-ai, pin it exactly.** The codex-subscription
capability is already ours (our provider, our runner, our auth bridge); pi-ai is
only the wire underneath, isolated behind the injectable
`CodexSubscriptionTransport` seam and lazy-loaded. Reimplementing it means
owning ~2,500 lines of churn-tracking protocol (codex Responses wire 1,272 +
shared Responses conversion/streaming 651 + OAuth refresh 442 + utils),
including zstd request bodies, a WebSocket transport variant, session resume,
and a rate-limit/billing error taxonomy — against an unofficial backend that
changes without notice. Churn absorption is the product the dependency delivers.
Hardening instead: the catalog range `^0.83.0` becomes an exact `0.83.0` pin
with reviewed bumps (token-touching dep), and the exit ramp is recorded here: if
pi-ai ever stalls, implement the seam ourselves — our narrower needs (the CLI
owns login; refresh + SSE POST only) size to roughly 600–800 lines plus the
churn ownership. Capability noted, not planned: pi-ai also ships
Anthropic/Copilot/Kimi subscription OAuth, so a `claude-subscription` binding
would be one thin provider away — explicit-only, operator's call, same honesty
caveat as codex.

Adopted lessons and where each lands:

| lesson                                                                                                                 | source                                               | lands           |
| ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | --------------- |
| Abort propagation route→runner→provider                                                                                | pi-ai `combineAbortSignals` (verified absent in Eve) | G13 → P0        |
| Wire-level turn-trace capture (what the model READ)                                                                    | pi's inspect-the-exact-request ethos                 | G14 → P0        |
| Cache read/write token + cost split in telemetry                                                                       | pi-ai `Usage` shape                                  | C11 → P0 (0.14) |
| History compaction over truncation                                                                                     | pi session management                                | C12 → P1        |
| Session cache affinity on the champion path                                                                            | pi-ai prompt-cache keys; our codex `sessionId`       | C12 → P1        |
| Deferred tools (names only + `load_tools` meta-tool)                                                                   | pi-ai `splitDeferredTools`                           | C3 → P2         |
| Privilege clamp: skill allowlist ∩ turn scope, never widen                                                             | `@oshun/assistant` `clampAllowedTools` (Hermes P3)   | C2 → P2         |
| Grammar-constrained argument sampling where endpoints support it                                                       | pi-ai constrained-sampling                           | C4 → P3         |
| Retrieval-ranked skill hints for `general`; model-authored skills feed the distillery queue, never the prompt directly | Hermes Voyager-style skill store                     | C2/C10 → P2/P6  |

Deliberately not copied: model-writable, runtime-growing prompt skills on the
member surface — curated, budgeted, eval-locked skills are the point; and a
north star borrowed from pi in one line: **nothing in the turn is invisible to
the operator, nothing in the prompt is unmeasured.**

## Risk register

| risk                                                                                 | mitigation                                                                                                                                                                                               |
| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Goodhart / deck overfitting** — skills tuned to cases, not behavior                | Held-out set authored AFTER skills exist, never tuned against, reported separately (P8); adversarial cases refreshed per phase; ledger-born locks immune to aggregate gaming                             |
| **Router misroutes** — wrong family = wrong tools = confident nonsense               | Fail-open to `general` (today's behavior); `{family, why}` logged every turn; misroute taxonomy reviewed at P2 exit before scoping tightens further                                                      |
| **Engine-prompt split is the riskiest refactor** (session prompt is dynamic-first)   | Behind a flag; byte-diff spec proves old order reproducible; deck flat within CI required to keep the flag on                                                                                            |
| **Provider auction drift** — the cheap endpoint changes under the pin                | Monthly price-the-route note in the scorecard (A10 command); `sort: price` stability finding already recorded; quantization pinned for every measurement arm                                             |
| **Latency from repair/escalation loops**                                             | Hard bound: one extra iteration per mechanism; per-family p95 in telemetry; smalltalk family capped at 2 iterations                                                                                      |
| **Skill/token budget creep** — the prompt regrows one "important" sentence at a time | Budget is a failing spec, not a guideline; traceability lint makes every sentence carry a case id                                                                                                        |
| **Judge circularity / self-preference**                                              | Judge validated on human labels before gating; judge model ≠ turn model; below-threshold judges stay advisory                                                                                            |
| **Small-model injectability** — tool results carrying instructions                   | Measured by the adversarial family; capability scoping is the real defense (mutating tools already refuse without the bridge; scoped toolsets shrink the blast radius); checkers catch ungrounded claims |
| **Eval spend surprises**                                                             | Live runs operator-invoked only; cost printed per run in the scorecard; k=10 reserved for promotions                                                                                                     |
| **This Mac** (16 GB, three freezes on record)                                        | Deck runs are network-bound: BFF + Postgres only, no Next dev server needed; memory checkpoint before every run; PIDs tracked and killed per CLAUDE.md                                                   |

## Success criteria (the initiative's own honesty bar)

1. **Quality:** the full deck passes on the pinned champion at floors ≥ the P0
   frontier-baseline arm (P0 runs one calibration arm on a stronger model to set
   "what good looks like" — a measurement, not a serving-path binding), and
   every ledger-born lock holds at k=10.
2. **Cost:** median member turn cost and cache-hit rate published in the
   scorecard, trending down across P1/P2 with the deltas attributed to named
   mechanisms, not vibes.
3. **Downshift:** every leg bound to the cheapest model that passes the
   promotion protocol, with the decision table and live-priced routes committed.
4. **Sustainment:** the maintenance loop runs on telemetry, not enthusiasm —
   escalation queue → distillery → deck delta — and the ratchet makes silent
   decay structurally impossible.

## Appendix A — deck case sources map (P0 mining guide)

| source                                               | case class                                                                            |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------- |
| EVE-VIS-080 record (4/20 tool skip)                  | member-data grounding traps, k=10                                                     |
| EVE-VIS-211 (elided ids)                             | full-id citation cases (workbench)                                                    |
| EVE-VIS-212 (member experiences offered to operator) | register traps (admin scope)                                                          |
| EVE-VIS-017 (JSON envelope leak)                     | envelope/prose cases                                                                  |
| Announce-before-act hold (spine run 12)              | SSE-ORDER case: no completed-mark delta before `audit_mark` result ok                 |
| EVE-VIS-128 (skip provenance)                        | audit-skip refusal + reason-elicitation cases                                         |
| Internal-id leakage rule                             | member-surface id-hygiene cases                                                       |
| Battery conversations (10)                           | multi-turn graded cases                                                               |
| Docs honest-absence (existing)                       | expanded per corpus tier (member vs admin)                                            |
| Crisis/budget/bridge (provider-free)                 | harness self-checks, run always                                                       |
| NEW: injection probes                                | instructions embedded in docs snippets, thread bodies, page context, work-item titles |
| NEW: empty-store honesty                             | zero favorites / zero threads / zero open work — honest empties, no invention         |

## Appendix B — glossary

- **pass@1** — mean pass rate over k independent runs of a case.
- **pass^k** — fraction of cases passing ALL k runs; the reliability number.
- **Ledger-born lock** — a deck case derived from a closed EVE-VIS row; failing
  one disqualifies a change outright.
- **Leg** — an independently model-bound job (turn, escalation, judge, router,
  embedding).
- **Distillery** — the build-time strong-model loop that turns failures into
  skills, exemplars, and checkers.
- **Ratchet** — committed per-family floors + prompt-bytes hash; CI fails on
  prompt drift without a fresh scorecard.
