# Console v2 Manual Walkthrough — 2026-05-14

**Tester:** Claude (browser automation, 1440×900 viewport)
**Build at HEAD:** 23a0ea87 (post-lineage-manifest)
**Dev URL:** http://127.0.0.1:5173/ → vite proxy → engine on :8431
**Session:** ~7 min, all 5 projects + 5 top-tabs traversed

---

## Lineage-manifest gate — RESULT

**Blocked at the data layer: no post-Phase-1 take exists yet.**

Direct adapter query against `/api/beats/{beat}/lineage?take_id=…`:
- All 21 takes in `tartarus/EP001_SH01`, `EP001_SH09/T11472`, and `EP001_SH27` return the **legacy** DAG payload (`prompt → step → output → sibling/eval`, `manifest: None`).
- 4 takes in `EP001_SH27` carry a `parent_take_id` (a partial fixture) but no `inputs_snapshot`, so `_build_manifest_lineage` (`recoil/api/adapters/lineage.py:202`) never branches into manifest mode. The adapter falls through to the beat-rooted graph.
- No take in the project tree has `builder_name` populated.

Implication:
- Code path is correct (pytest 233/233, vitest 10/10) and the legacy fallback renders fine — Lineage view for T001 shows `¶ Prompt` + `¶ Model config` cards as expected.
- The new manifest UI (manifest header, ref thumbnails, prompt expand/copy, params rows) is **untested on real data**. **Recommend kicking off one tartarus regen so `inputs_snapshot` lands, then re-validate.**

---

## Findings — by severity

### 🔴 CRITICAL

**C1. Episode click resolves to the wrong project when episode IDs collide across projects.**
- Repro: Hierarchy → click `tartarus` (focus = tartarus). Expand it, click the `EP001` node nested under tartarus. → breadcrumb flips to `afterimage · EP001 · EP001`, right rail says `project afterimage / focus EP001`, terminal spawns `launching ttyd for afterimage`.
- Mechanism: both `afterimage` and `tartarus` have an `EP001` (and `EP999`). The episode→project resolver looks up by episode ID alone and lands on the first project containing that ID.
- Blast radius: silent cross-project contamination — wrong terminal, wrong context for any subsequent operation. Worst on tartarus (the active project) because afterimage is older/quieter.
- Likely files: `lib/use_focus.ts` or `lib/focus_walker.ts`; the episode tree node click handler in `Hierarchy.tsx`; ttyd launcher routing.
- Note: clicking a **shot** under the bad episode (e.g. `tartarus/EP001/EP001_SH01`) recovers the correct project. So the bug is isolated to the episode-level click handler.

**C2. Selection state leaks across project switches.**
- After selecting `tartarus/EP001_SH01/T001`, clicking `afterimage` (or any other project) in the Hierarchy keeps `selection EP001_SH01_T001` in the right rail and `· T001` in the breadcrumb — but that take belongs to tartarus. Breadcrumb becomes `afterimage · T001` / `ronin-drm · T001` / `driver-beware · T001`, all nonsensical.
- Should clear selection on project change, or at minimum drop the selection if it doesn't belong to the new project's tree.

---

### 🟠 HIGH

**H1. `episode_id_derived_from_filename_prefix` fires constantly (167 FALLBACK events in this session).**
- Events tab, source `api/adapters/beats`. Every project switch fires several of these per second. The fallback path is doing real work — meaning the canonical episode_id is missing from a lot of take/shot records and the adapter regenerates it from filename prefix every read.
- Performance + correctness risk: filename-prefix derivation is brittle (`shot_001_take7_6940.png` → ?). If the underlying data is missing `episode_id` it should be backfilled in storage, not at every read.

**H2. `session id capture timeout` (chat/sessions WARNING) on terminal restart.**
- Two WARNING events under `chat/sessions` while the terminal swapped between projects. ttyd session ID isn't captured before the timeout — affects ability to programmatically interact with the embedded terminal.
- Symptom: status-bar toast `session id capture timeout`. Repeatable on every project switch.

**H3. `/api/config` returns 404 on every page load.**
- Console: `[recoil] /api/config fetch failed, using fallback Error: config HTTP 404` × 3 on cold load, again on every HMR reconnect.
- Source: `src/lib/use_runtime_config.ts:13`. Endpoint not registered on the engine (`http://localhost:8431/api/config` → 404). Either wire the endpoint or silence the warning.
- The 3× fire on a single mount also suggests `useRuntimeConfig` is being called from 3 independent consumers without deduping — even after the endpoint is fixed, the dedupe should land.

**H4. ttyd processes are spawned per project switch and not torn down.**
- Console logs show new ttyd websocket connections on ports 7681/7682/7683 fired repeatedly — by end of a 7-min session there were 16+ `[ttyd] websocket connection opened` events, with ~3 distinct ports always active.
- Risk: zombie ttyd processes accumulate over a long session. Also drives the "session id capture timeout" cascade (H2) when new ones spawn before old ones settle.

**H5. Events FAILURE filter chip is broken (or unintuitive).**
- Default view: WARNING+FALLBACK selected → `261/401` events. Click `FAILURE 85` → header drops to `176/401`, FAILURE chip lights up, but the visible rows don't change (still WARNING+FALLBACK). The chip appears to behave as a toggle that's also flipping siblings, or the count math doesn't match the rendered set. Either way, "click FAILURE to see failures" doesn't work.

---

### 🟡 MEDIUM

**M1. Empty Takes pane has no empty state.**
- Focus a project (e.g. tartarus) without selecting a shot → center pane is fully blank, only stats bar reads `takes 0`. No "pick a shot" hint. The Lineage view DOES have a "no lineage for this focus — pick a beat that has a primary take" empty state and it reads well; Takes view should match.

**M2. Take card "EVAL/FAILED" badge overlaps the model-name string.**
- Repro: Takes view, any take with eval/failed state. The bottom-row badge (`• EVAL` green / `x F` red rated) sits flush against `gemini-3.1-flash-image-preview` → letters visually collide (`x F` overlaps `g` of gemini, `EVAL` overlaps `gem`).
- The model name is fully readable but the cards look unfinished. Likely a missing left-margin / gap on the badge or a row-wrapping issue under 280px card width.

**M3. Driver-beware shot names truncate aggressively (250px hierarchy width).**
- `DEER_SMOKE_OR_FIRE_SEEDA…`, `DEER_STOP_VARIATION_SEED…`, `DEER_STOP_WAVE_HEAD_COMF…`, `DEER_STOP_WAVE_LIVE_SHEE…` — all cut at `…` so you can't disambiguate them by reading. Tooltip on hover would solve this; or shrink font; or wrap.

**M4. Driver-beware shots show no take counter, all other projects do.**
- `EP001_SH01 21t`, `EP001_SH02 9t` … vs `CRASH_JUMP_01`, `CRASH_JUMP_02` (no counter). Either inconsistency in the data shape (flat-vs-episode projects) or the counter renderer doesn't fire on flat-list shots.

**M5. Memory view: 5 LEARNING rows render with empty NOTE column.**
- Memory tab → first 5 rows show `LEARNING / script / [blank] / 0.60 / 1`. ANTI-PATTERN rows render with full body text. The LEARNING entries may legitimately have no note, but the table cell rendering "blank" with no placeholder gives the impression of a render bug.

**M6. Memory view: ANTI-PATTERN notes use `|` as paragraph separator.**
- The note column inlines `… | Breaks the cliffhanger contract … | Instead: open episode N+1 with a physical action …`. Each `|` is a paragraph boundary in the source but the renderer doesn't replace with `<br>` / paragraph spacing. Hard to read at scale.

**M7. `App.tsx: focusedProjectAspect fallback to '9_16' fired (no focusPath)` warning on every cold load.**
- Dev-only debug log, but it fires on every reload — points at a missing gate. Spec from log says "Consumers must gate on `focused`" — would be worth honoring that and dropping the log.

---

### 🟢 MINOR / COSMETIC

**X1. Terminal banner truncation in right rail (340px column).**
- `Opus 4.7 (1M context) with…` — cut mid-word. `~/Dropbox/CLAUDE_PROJECTS` → `~/Dropbox/CLAUDE…`. Acceptable for a sidebar but `(1M context)` duplicates the model-line wordmark — drop the parenthetical or wrap path.

**X2. `BINDS CLICKS ↔ CHAT` label reads like clickable tabs but isn't.**
- Adjacent to `SELECTED CONTEXT ·` in the right rail header. Sentence-case it (or use a clear separator) so users don't try to click each segment as I did.

**X3. Events timestamp column is cramped.**
- Format: `2026-05-14T17:08:54.4334742` (ISO-with-microseconds), wraps mid-string into the source-column. Drop the microseconds and probably the date prefix when "today" — show `17:08:54.4` and a relative "5s ago" instead.

**X4. Project hub naming is ambiguous.**
- Hierarchy shows `afterimage` and `afterimage` `afterimage-anime` (the latter has the slug rendered in a dim secondary weight after the display name). At a glance you have to scan twice to find "the anime one." Suggest: bold the slug when display names collide, or always show the slug.

**X5. Stray test fixture in production tree.**
- `tartarus/EP001/TEST_I2V_CHAIN_SH34 0t` visible in the Hierarchy alongside real shots. Should be filtered out of UI, or moved into a `_test/` folder in the data.

**X6. Stats bar all-zero shouldn't render dashes for primary.**
- `takes 0 / succeeded 0 / … / primary — / spend $0.00 / eval $0.000` — mixing `0` with `—` in adjacent cells reads as data-loaded vs not-loaded states. Pick one convention.

---

## Surfaces / good notes

- **Queue view** is excellent. PromptRewriteProposal / BeatInsertionProposal / ParameterChangeProposal cards are clean. Diff rendering (red strike-through removals, green additions) reads at-a-glance. `defer / reject / approve ⌘↵` action row is well-placed.
- **Events view** structure is right: source / kind / message / expand. Just needs the filter math fixed (H5) and tighter timestamps (X3).
- **Memory view** structure is right too — togglable rows with confidence + hit counts. Notes column needs typography (M5/M6).
- **Lineage legacy fallback** renders cleanly when manifest data is absent.
- **Top status bar** density is good — LIVE / RENDERING / EVALUATING / QUEUED indicators, fails/warns pills, cmd-\ shortcut all visible.

---

## Suggested fix order (impact × effort)

1. **C1** episode→project resolution (fix the resolver to include parent project in lookup) — small fix, blocks correct use of the console for any user with cross-project ID collisions.
2. **C2** clear selection on project change — small fix, removes confusing breadcrumb state.
3. **H1** backfill `episode_id` in the take/shot data so the fallback stops firing — or accept it but tighten the log volume.
4. **H4** + **H2** terminal lifecycle: tear down ttyd on project switch, then session-id capture has a stable target.
5. **H5** Events filter logic — sanity-check the chip toggle math.
6. **H3** wire `/api/config` or downgrade the log.
7. **M1, M2** — empty-state copy + badge gap in the take grid.
8. **M3–M7, X1–X6** — typography / data-cleanup polish, batch in one cleanup PR.

---

## What was not exercised

- Fresh-generation manifest take (gated on real data).
- Filter button / sort button / retry-selected ⌘R button (top-right bar).
- ⌘\ shortcut on episodes_synthesized_from_shot_index status pill.
- `open queue →` link in the bottom right of the top status bar.
- Pasteboard upload (Choose files / drag).
- Right-rail terminal interaction (would need to send input).
- BINDS/CLICKS modes (label only — could not find UI surface).
