Change: the localhost eval viewer (uv run lab web) becomes an
agent-structure mirror. Four founder-review asks from 2026-07-26 land together: real persona
avatars, an agent card per brain, a Models lens answering "which models, which route, what
cost", and Input/Output Schema lenses scoped to the selected surface. A CI conformance gate
makes the standard agent layout enforceable: an agent that breaks it shows a red
STRUCTURE GAP banner in the viewer and fails
tests/test_agent_structure_conformance.py.
kimi-coding:k3 to a production-eligible route (founder call 2026-07-26) and fills
the model-pricing.yaml gaps the Models lens exposed. The lens is where the
founder reads that decision back; the two changes review together.uv run lab web # prints: lab web: http://127.0.0.1:<port>/?token=<per-launch token>
Every check below is a GET on that URL. The token is per-launch; nothing here writes anything.
| Click | Expect |
|---|---|
| Top nav | Circular portrait before each persona name — Atlas, Blake, Vega, Indigo, Mira. Blake and Indigo portraits are new (agents/registrar/assets/blake.png, agents/indigo/assets/indigo.png). context-judge stays text-only (no persona, by design). |
| Any agent → 01 Overview | Agent card: portrait, role line, and a 1–2 paragraph description of what the agent does, sourced from that agent's identity.py (description field). |
Portraits serve from each agent's own assets/ dir via a token-gated
/avatars/<agent> route; an agent without a portrait file falls back to a
monogram tile. The viewer is identity-driven: it loads agents/<key>/identity.py
directly — no duplicate name/portrait maps inside evalkit/lab.py.
| Click | Expect |
|---|---|
| Mira → conversational → 05 Models | Only conversational seats: converse leg 1 kimi-coding:k3 (direct · kimi-coding (plan), $0.05 / $0.5 per 1M), leg 2 gemini-flash-lite (OpenRouter gateway), leg 3 gpt-5.6-luna (direct · openai). Judges listed separately — eval infrastructure, shown on every surface. |
| Mira → command → 05 Models | Command seats only (command_mapper, proposer, critic, synthesizer). No conversational seats mixed in. |
| Vega / Indigo / Blake → 05 Models | Same table, parsed generically from each agent's llm.yaml — scalar seats, fallback chains, and Blake's nested primary/fallbacks all resolve. No per-agent code in the viewer. |
Columns: seat, model (alias-resolved via model-registry.yaml), provider path,
$in/$out/$cache-read per 1M tokens (from model-pricing.yaml), INT (INR per 1M),
cache policy, price note. Env-placeholder seats (Atlas) render as
deploy-env resolved instead of a fake price.
| Click | Expect |
|---|---|
| Mira → conversational → 06 Input Schema | MiraConverseInput only: matter_id, current_frame, counsel_message required; types, defaults, constraints per field. No command contracts. |
| Mira → command → 06 Input Schema | MiraCommandInput, MiraMapCommandInput, PanelInput. No conversational contract. |
| Mira → command → 07 Output Schema | FrameOutput (the Brief), full field table. |
| Mira → conversational → 07 Output Schema | Honest note: converse streams plain markdown — no typed output contract. Shared supporting shapes listed separately. |
Contracts are read from the agent's own models.py / panel_models.py
(pramaan-docs ADR-117 — the type IS the contract), deduped by class name.
The surface rail shows only surfaces that have suites — Mira shows COMMAND and CONVERSATIONAL; AMBIENT is gone (previously visible-but-disabled under Story-2973 D35; reversed on founder review). Every lens — Datasets, Runs, Prompts, Models, both Schema pages — is scoped to the selected surface. Selecting conversational never shows command content, and vice versa.
uv run pytest tests/test_agent_structure_conformance.py -q 3 passed
The gate asserts, for every agent dir: identity.py complete (5 fields,
key == directory), avatar file present (context_judge exempt — no persona),
<key>.yaml, llm.yaml, prompt source. Atlas was brought to full
structure as part of this change (new agents/atlas/llm.yaml;
config.py fails closed when a catalog placeholder is undeclared). Break any one
and every page for that agent renders a red STRUCTURE GAP banner naming the
missing piece — the viewer never papers over agent drift.
uv run pytest tests/test_provider_routes.py -q # guard coverage kept uv run pytest agents/mira/tests -q # 257 passed uv run pytest agents/atlas/tests -q # 516 passed
agents/_shared/provider_routes.py: kimi-coding is now
production_allowed — the Models lens shows it as
direct · kimi-coding (plan) with no non-prod marker. Fallback chains behind
kimi-coding:k3 are retained as availability insurance;
production_lookup still fails closed for any future non-production route.
Pricing rows verified against the live OpenRouter catalog 2026-07-26.
uv run pytest tests/ -q # 423 passed (governance + lab web + conformance) uv run ruff check agents/ evalkit/ tests/ scripts/ # all checks passed
| Item | Why deferred |
|---|---|
| Case-table visual tightening (drop uniform columns, clamp descriptions, attack-class badges) | Proposal on the table; founder has not picked an option. |
| Runs lens: Logfire trace/span deep-links | Needs logfire.url_from_eval design. |
| Prompts lens: honest-label vs effective-stack | Open question from Story-2973 handoff. |
Registrar provider.py: kimi-coding 90s wire bound | No Blake seat uses that route today; flagged in Chore-2993. |
| Indigo judge-fallback docstrings (moonshot vs anthropic-sonnet) | Comment drift only; flagged in Chore-2993. |