返回 CodeWhale
TIDELINE_RATATUI_TRANSLATION.md
根目录 / docs / design / TIDELINE_RATATUI_TRANSLATION.md
1 # Tideline → Ratatui translation spec
2
3 **Status:** spec + scaffolding. Not merged, no PR opened.
4 **Branch:** `design/tideline-ratatui-translation-20260828` (worktree
5 `cw-main-0912-integrator-20260828`), based on `origin/main` `dde9cdb044cf7625b78eeb31e53a479ae07b16f2`
6 (rebase-past #5699/#5700 verified). Branch point ancestry: `e5a6046cb` → #5699 → #5700.
7 **Implemented & green:** topbar (`crates/tui/src/tui/topbar.rs`) + golden-buffer
8 harness, 6/6 via `./scripts/dev-test.sh tui topbar` (11,329 skipped).
9
10 Authority order used: the three approved screenshots > this document's
11 prose > the recovered motion sketch (motion language) > `tideline-redesign.html`
12 (interaction-registry idea only, superseded on layout).
13
14 ---
15
16 ## 1. What the three approved screens actually contain
17
18 Cell-inventory read of the references (startup, work+fleet, settings/appearance):
19
20 - **Topbar (all three).** One row: `CODEWHALE` wordmark; contextual
21 segments (`run …`, `fleet …`, `3/4 whales`, `model …`, `theme …`,
22 `Settings / Appearance`, `folder …`); pinned right = `context NN% ▰▰▱▱▱` +
23 full clock. Segment set varies per screen; brand/meter/clock never move.
24 - **Startup.** Centered hero: "What are we working on?", one dim
25 subtitle; `QUICK ACTIONS` band with 3 rows (icon · label · description ·
26 command + `›`); a 4-column option strip (New worktree / Chat only / Theme /
27 Help); whale-outline composer; footer with route · cost · keys.
28 - **Work + Fleet.** Left rail (RUNS / WHALES / FLEET / WORK / CONTEXT, then
29 help/settings, `«` collapse); receipt stream (user + assistant turns, a
30 `├──/└──` pod-formation tree, state-marked receipt rows with timestamps and
31 receipt counts, an indented conclusion block, a legend row); `FLEET LEDGER`
32 table (WHALE/ASSIGNMENT/STATE/ELAPSED/RECEIPTS/LAST UPDATE, selected row
33 marker `▶`); composer; footer with cost and keys.
34 - **Settings.** 3 panes: category rail (Appearance → Advanced + help/file/
35 feedback); theme list (13 rows: 4 mode rows + 9 presets, selected row boxed
36 + `✓`) and MOTION (OPTIONAL) toggles; live Deepsea preview pane replaying a
37 transcript + mini ledger + composer, with translucent whale silhouettes in
38 the background; footer with 6 key bindings.
39
40 ## 2. HTML lies — resolutions (non-negotiable)
41
42 | In the reference | Why it cannot ship | Decision |
43 |---|---|---|
44 | Composer drawn as a stroked whale outline | No bezier strokes; only box-drawing glyphs | **Rounded border.** `╭─╮│╰╯` border (dim at rest, Info on focus); the old fluke cap is retired and no brand glyph is hand-drawn into the composer. The send `↑` becomes a 3-cell hitbox `[↑]` right-aligned inside the border. The terminal never approximates the canonical raster mark. |
45 | Translucent whale silhouettes behind text (Deepsea preview) | No alpha; painting over text destroys it | **Empty-cells-only compositing**, the `ambient_life.rs` rule verbatim: write only cells that are open water (`is_open_water` + `TEXT_CLEARANCE_ROWS = 1` clearance from `occupied_text_bounds`). Eviction order when water is scarce: bubbles first, then fish school, then jellyfish; the whale cameo is evicted last (highest identity value). Caustic-style tinting stays bg-only on `cell.symbol() == " "`. Deepsea ambient runs only under `MotionMode::Full`. |
46 | SVG icons (plug, clock, folder, palette, chat) | Not renderable | One glyph per action, added to `glyphs.rs` with declared ASCII fallbacks via `ascii_fallback`: plug `⌁`→`+`, resume `↺`→`<`, folder `▤`→`=`, palette `◐`→`*`, chat `◌`→`o`, worktree `⑂`→`y`, help `?`→`?` (identity). Each is 1 cell, no wide glyphs. |
47 | Ledger cells wrapping to two lines | Table columns are exact integer cells | Fixed column widths + per-column truncation: WHALE 10 (never truncates — names are short by contract), ASSIGNMENT = remainder (truncate with `…`, never wrap), STATE 12 (glyph + word), ELAPSED 8, RECEIPTS 8, LAST UPDATE 8 (`HH:MM:SS`). **At 80 columns** the rail is hidden and ledger sheds to `WHALE │ ASSIGNMENT │ STATE` — ELAPSED, RECEIPTS, LAST UPDATE drop in that order before ASSIGNMENT loses cells. |
48 | Meter bars `▰▱`, tree `├── └──`, state marks `●○✓!` | Fine — they are glyphs | Keep. Declared ASCII fallbacks already in `glyphs::ascii_fallback` (`▰→#`, `▱→-`, `├→+`, `└→+`, `●→.`, `○→.`, `✓→Y`, `!→!`). Verified single-width in the ascii-safe golden test. |
49
50 **Rule restated:** if it cannot be expressed as cells, it is not in the design.
51 Nothing else in the three screens requires non-cell expression.
52
53 ## 3. Lineage — what each component replaces (frame.rs slot map)
54
55 The live shell renders ten slots (`tui/ui/frame.rs` header split ~:818, body
56 constraints ~:928). The references collapse the bottom into one footer:
57
58 | Slot today | Disposition |
59 |---|---|
60 | header (`underwater::render_header`) | **Replaced** by `topbar::Topbar` (implemented here). Facts survive: mode/permission chips move to the footer activity segment; route/model stays a topbar segment. |
61 | 0 work strip (`work_surface::render`) | **Extends** — becomes the rail's WORK group (owned by #5699; do not fight their files). |
62 | 1 chat (+rail via `split_chat`) | **Extends** — receipt stream + fleet ledger attach to the transcript column; rail reuses `work_surface::split_chat`. |
63 | 2 workflow panel | **Kept unchanged** (drill-in above composer). |
64 | 3 pending input preview | **Merged into the composer** as a one-row crumb above the input line — the reference shows queued messages as composer content, not a band. |
65 | 4 background-work chip | **Deleted as a band**; the fact moves to the topbar `fleet n/m` segment and the rail WORK group (one surface owns each fact). |
66 | 5 session boot receipt | **Deleted as a band**; boot lines become ordinary transcript receipts. |
67 | 6 activity band | **Merged into the footer** (left half: phase chip + echolocation + cost). |
68 | 7 composer | **Extends** — rounded border + `[↑]` hitbox; composer authority logic untouched. |
69 | 8 identity band | **Merged into the footer** (right half: depth line + key legend). `phase_strip::render_identity` is the merge target; `render_footer` delegates today already. |
70
71 Orphaned facts, each with exactly one home: cost/token ledger → footer;
72 boot receipts → transcript; background-work → topbar fleet segment + WORK rail;
73 permission/mode chips → footer activity segment; session metrics detail →
74 `/cost` (the sketch's rule: the ledger row moves behind `/cost`).
75
76 ## 4. Founder directive resolutions
77
78 1. **Blue stage vs `Color::Reset`.** Smallest honest resolution: Dark and
79 Light keep `Color::Reset` (the host-terminal inheritance rule and sketch
80 principle 1 are load-bearing). The blue stage ships as **Deepsea, promoted
81 to a first-class one-keystroke toggle** (topbar Theme segment opens the
82 picker with Deepsea preselected; the theme strip on startup), plus a
83 blue-biased ink ramp for Dark so the stage reads cool even on Reset.
84 **Tradeoff named, not papered over:** a user on Dark who wants the painted
85 field without Deepsea's underwater life cannot have it — with no alpha,
86 "inherit host background" and "painted blue field" are mutually exclusive;
87 anything else is a third theme pretending not to be one.
88 2. **Continuous activity vs "no ambient loops".** Resolution: *state is always
89 shown; decoration is opt-in.* The footer's phase segment (echolocation chip
90 + phase word + live detail) is persistent in every phase, matching
91 `phase_strip::activity_height()`'s existing always-reserved row. Ambient
92 life (fish/jelly/bubbles/cameo) stays `MotionMode::Full`-gated. Nothing
93 new loops for decoration.
94 3. **Mature and original.** One surface owns each fact (§3 map). The braille
95 spinner is **retired** in favor of echolocation (`<·>` family) at the same
96 420 ms cadence; `spinner.rs` constants remain for non-Tideline call sites
97 until the migration slice deletes them.
98 4. **Motion gating + still frames.** Every motion below declares a still frame
99 that carries the same information and rides `MotionMode`
100 (`MotionPolicy::from_settings(low_motion, fancy_animations, …)`), wall-clock
101 keyed, never frame-count keyed.
102
103 ## 5a. Component inventory
104
105 Data sources are real `App` fields/methods (verified at HEAD). "Hitbox" =
106 where the `Rect` is stored for `mouse_ui` (existing pattern:
107 `viewport.last_*_area` / `work_surface.hitboxes` / `launch.row_areas`).
108
109 | Component | What it does | States | Data source | Replaces | Owning file | Keys | Mouse hitbox | Golden name |
110 |---|---|---|---|---|---|---|---|---|
111 | Topbar | One-row status surface | per-screen segment set; hover; shed | `effective_route_identity_display()`, run/fleet summaries, `context_budget` pct, injected clock | `underwater::render_header` | `tui/topbar.rs` ✅ | Tab⇄, Enter activate | brand/menu + per-segment rects → `viewport.last_topbar_hitboxes` | `topbar_{startup,work,settings}_{w}x{h}` ✅ |
112 | Hero (startup) | Centered prompt + subtitle | first-run vs returning | `LaunchState`, `workspace_session_count` | `render_launch_screen` | `tui/underwater.rs` | — | none | `startup_{w}x{h}` |
113 | Quick actions | 3 command rows | selected/hover/disabled (no model) | `LaunchAction`, provider state | launch menu rows | `tui/underwater.rs` + `mouse_ui.rs:441` | ↑/↓, Enter, Esc | row rects (exists) | `startup_*` |
114 | Option strip | 4 columns (worktree/chat/theme/help) | hover/selected | `LaunchState` | launch options row | same | Tab, Enter | 4 col rects | `startup_*` |
115 | Rail | Left column, 5 groups + collapse | expanded/collapsed/focused | `WorkSurfaceState`, `subagent_cache`, run list, git status | work strip + `sidebar` remnants | `tui/work_surface/` (#5699 territory) | Tab, ↑/↓, Enter, `«` | `WorkHitbox{WorkRowId,row_y}` (exists) | `work_{w}x{h}` |
116 | Receipt stream | Turn + receipt rows, pod tree | streaming/settled; selected | `history` cells, pod formation receipt | transcript rail | `tui/history.rs`, `work_surface/render` | ↑/↓, Enter inspect | row rects (transcript click path) | `work_*` |
117 | Fleet ledger | Whale table | row selected; state per whale | `subagent_cache` + worker runtime states | workflow-panel duplicate | `tui/work_surface/panels.rs` | ↑/↓, Enter/click inspect | row rects → inspector | `ledger_{w}x{h}` |
118 | Theme list | 13 themes + motion toggles | selected/preview/applying | `ThemeId`, `ocean_treatment`, `low_motion`, `fancy_animations` | `theme_picker.rs` | `tui/theme_picker.rs`, `views/` | ↑/↓, Enter preview/apply | row rects | `settings_{w}x{h}` |
119 | Live preview | Projection of a real screen in chosen theme | mirrors screen state; never a second store | same render fns, `TestBackend`-style projection into the pane | settings preview | `tui/views/` settings | — | none (passive) | `settings_*` |
120 | Settings rail | 8 categories + meta rows | selected | `ConfigView` | `ConfigView` nav | `tui/views/mod.rs` | ↑/↓, Tab | category rects | `settings_*` |
121 | Composer | Input with rounded border + send hitbox | focus, pending crumb, approval-replaced | `ComposerState`, pending preview | composer_ui/chrome (extends) | existing composer files | Enter, ⇧Enter, Esc | `[↑]` submit rect; border focus click | `composer_{w}x{h}` |
122 | Footer | One band: phase·cost (left), depth line·keys (right) | per-phase ink; 80% warn | `SessionState` cost, phase, `context_budget` | slots 6+8 merged | `tui/phase_strip.rs` | — | depth segment → context inspector | `footer_{w}x{h}` |
123 | Notifications inbox | Attention rows (gold ◆) | unread/read; per-kind | `status_toasts`/`sticky_status` → typed records | toast soup | `tui/notifications.rs` | Enter, `r`, Esc | row rects | `notifications_{w}x{h}` |
124
125 No component lacks lineage; the two deletions (slots 4, 5) and two merges
126 (3, 6+8) are stated above.
127
128 ## 5b. Layout contract (copy-pasteable)
129
130 Screen shell — all three screens:
131
132 ```rust
133 let [topbar, stage, footer] = Layout::vertical([
134 Constraint::Length(1), // topbar (implemented)
135 Constraint::Min(1), // stage: the screen
136 Constraint::Length(1), // footer: phase·cost · depth·keys (merged 6+8)
137 ]).areas(area);
138 ```
139
140 Startup stage:
141
142 ```rust
143 let [hero, rule_a, quick, rule_b, strip, spacer] = Layout::vertical([
144 Constraint::Percentage(38), // hero: heading + subtitle
145 Constraint::Length(1), // wave rule `⋯ ∼∼∼ ⋯` (dim, static)
146 Constraint::Length(3 + 2), // QUICK ACTIONS: label row + 3 rows + margins
147 Constraint::Length(1), // wave rule
148 Constraint::Length(3), // option strip: 4 columns × 2 rows
149 Constraint::Min(1),
150 ]).areas(stage);
151 // options strip:
152 let [wt, chat, theme, help] = Layout::horizontal([
153 Constraint::Ratio(1, 4); 4]).areas(strip);
154 // composer+footer below stage per screen shell (composer Length(4) incl. border)
155 ```
156
157 Work + Pod stage:
158
159 ```rust
160 let rail_w = if width >= 120 { 22 } else if width >= 100 { 16 } else { 0 };
161 let [rail, main] = Layout::horizontal([Constraint::Length(rail_w), Constraint::Min(1)])
162 .areas(stage);
163 let [stream, ledger, composer] = Layout::vertical([
164 Constraint::Min(6), // receipt stream
165 Constraint::Length(1 + 1 + rows.min(4)), // POD LEDGER: header + rows
166 Constraint::Length(4), // composer
167 ]).areas(main);
168 // ledger columns at main width w (rows are 1 line, never wrap):
169 let cols = if w >= 130 {
170 [Length(10), Min(8), Length(12), Length(8), Length(8), Length(8)] // +LAST UPDATE
171 } else if w >= 110 {
172 [Length(10), Min(8), Length(12), Length(8), Length(8)] // +RECEIPTS
173 } else {
174 [Length(10), Min(8), Length(12)] // core three
175 };
176 ```
177
178 Settings stage (3-pane):
179
180 ```rust
181 if width >= 100 {
182 let [nav, form, preview] = Layout::horizontal([
183 Constraint::Length(18), Constraint::Min(30), Constraint::Percentage(38),
184 ]).areas(stage);
185 } else {
186 let [nav, form] = Layout::horizontal([Constraint::Length(16), Constraint::Min(20)])
187 .areas(stage); // preview sheds
188 }
189 ```
190
191 **Shed order (global, what collapses first):** ① topbar Theme segment →
192 ② topbar Workspace → ③ topbar Whales → ④ Pod → ⑤ Run/breadcrumb →
193 ⑥ settings preview pane → ⑦ rail (100 → 0) → ⑧ ledger time columns
194 (LAST UPDATE → RECEIPTS → ELAPSED) → ⑨ clock sheds date prefix →
195 ⑩ option strip 4→2 columns. Transcript and composer never shed below their
196 existing floors (`MIN_CHAT_HEIGHT = 3`, `MIN_COMPOSER_HEIGHT = 2` + border).
197 Topbar ①–⑤ and ⑨ are implemented and tested.
198
199 ## 5c. Golden buffers
200
201 12 files, cell-exact, under `crates/tui/src/tui/goldens/`:
202 `topbar_{startup,work,settings}_{80x24,100x30,120x32,160x40}.txt` ✅
203 (`BLOCKER_SIZES` from `views/status_picker.rs:423`). The remaining 8 names in
204 §5a are reserved; each lands with its component. Re-bless:
205
206 ```sh
207 CODEWHALE_BLESS_GOLDENS=1 ./scripts/dev-test.sh tui topbar
208 ```
209
210 The harness (`topbar/tests.rs`) renders through `TestBackend`, dumps
211 `Buffer` cell symbols, and asserts byte-exact equality; a missing golden
212 fails the test unless blessing. Goldens are the design contract — a visual
213 change that cannot show as a golden diff did not happen.
214
215 ## 5d. Color contract
216
217 Semantic roles only — `ChromeInk` (17 variants, `palette/grammar.rs:49`) via
218 `chrome_style`; **no hex in widgets**, presets stay in `palette/`. Grammar
219 families obey `STATUS_BAR_COLOR_GRAMMAR.md`:
220
221 | Element | ChromeInk | Family |
222 |---|---|---|
223 | Wordmark | `Attention` (gold) | Cognition — restrained text identity |
224 | Segment labels / separators / clock | `Metadata` / `MetadataDim` / `MetadataHint` | Metadata |
225 | Route · model · run | `Identity` | Identity |
226 | Pod (live) / `3/4` / context meter / theme name | `Active` / `Info` | Active / Identity |
227 | `not connected` / ≥80% meter | `Waiting` / `Attention` | Cognition |
228 | Receipt working / done / caution / failed | `Active` / `Outcome` / `Attention` / `Failure` | Active/Outcome/Cognition/Failure |
229 | Ledger `failed` (preview error row) | `Failure` | Failure — red stays failure-only |
230
231 Resolution paths: truecolor presets → ANSI-256 → ANSI-16 injective role
232 matrix (`palette/adapt.rs:598`, `role.ansi16()`), ANSI-16 backgrounds drop to
233 `Color::Reset` (`adapt_bg`). Nonempty `NO_COLOR` selects a distinct monochrome
234 depth: foreground, background, and underline colors become `Color::Reset`,
235 while bold, underline, reverse, and selection symbols remain. ASCII rendering
236 is independent. Dark/Light/Deepsea all resolve through the same output adapter.
237
238 ## 5e. Motion table (wall-clock keyed; `MotionMode`-gated; still frames carry the facts)
239
240 | Motion | Trigger state | Cadence | Cell footprint | Still frame | Gate | Mechanism |
241 |---|---|---|---|---|---|---|
242 | Echolocation | turn active (any phase) | 420 ms × 6 frames (`widgets/header.rs:13`) | 7 cells, band; 1 cell chip; never shifts adjacent text | static `<·>` (chip: `·`) | Reduced freezes frame 0; Still static | pure fn of `(turn_started_at, Instant::now)` beside `header_status_indicator_frame`; replaces braille `spinner.rs` frames |
243 | Spout | turn success, once (suppressed if next turn <400 ms) | one-shot 900 ms, ease-out, droplets +60–110 ms | 1 col × 3 rows above activity band's right edge; ≤4 cells | skipped — `surfaced ✓ …` summary line carries it | `Full` only | `surfaced_at: Instant` + `FrameRequester::request_at` for the 900 ms window, direct `Buffer` writes (`paint_marks` technique); failure surfaces flat `✗`, no motion |
244 | Bioluminescent tail | tokens streaming | decay 1.5 s (existing), 12 graphemes | ≤12 cells restyled, no new draws | frozen settled brightness (current behavior) | Reduced/Still keep freeze semantics | extend `hot_tail.rs`: keep `HOT_TAIL_GRAPHEMES=12`, `breath_luminance` period; add accent-tint step truecolor, bold-only ANSI-16 |
245 | Depth line | token count changes only (turn boundaries + stream ticks) | no private clock | ≤16 cells in footer right | identical (static per count) | always visible, all modes | hand-rolled span builder (not `Gauge`): `▁▂▄▆∿▄▄▄▄ 61%`; ≥80% cap `▲` Attention + `surface soon — /compact` |
246 | Idle breath | idle, nothing else moving | 4.2 s cosine luminance, 1 cell | header bit `·` dim→bright→dim | dim `·` | `Full` only | same cell, luminance-only function of `Instant`; wave rules `⋯ ∼∼∼ ⋯` are static `Span`s |
247
248 Cadence law: everything is a function of `Instant`/wall clock (echolocation
249 reuses the 420 ms heartbeat), never a frame counter; `FrameRequester` stays
250 the only additional-frame source and the poll loop the only draw emitter.
251
252 ## 6. Clickability contract (the grokbuild bar)
253
254 Every selectable target follows the registry discipline from
255 `tideline-redesign.html` ("Every navigates"): visible focus, recorded hitbox,
256 keyboard + mouse parity, an inspect action, no decorative glyph asks to be
257 clicked. Mechanically this repo already has the pattern — `WorkHitbox`
258 storage, `viewport.last_workflow_panel_area`/`last_workflow_cancel_area`,
259 `launch.row_areas`, `sidebar_hover.sections` row matching
260 (`mouse_ui.rs:270,441,695`). This spec adds **one** new registry:
261 `viewport.last_topbar_hitboxes: Vec<(TopbarSegmentId, Rect)>` filled by
262 `topbar::topbar_hitboxes` at render (same shapes as painted cells — asserted
263 by test) and consumed in `mouse_ui` exactly like the workflow-panel cancel
264 zone. Hover = value ink brightens + underline (tested); press = the segment's
265 action (`Model→` provider inspector, `Pod→` ledger, `Context→` context
266 inspector, `Theme→` theme picker, `Run→` run dashboard, `Brand→` menu).
267 Keyboard parity: Tab cycles topbar → rail → stream → ledger → composer;
268 Enter activates the focused target; Esc backs out. Ledger rows, quick
269 actions, theme rows, and option-strip tiles reuse the row-rect pattern above —
270 no second mouse system is introduced.
271
272 ## 7. Moment map (craft lens; surprise budget ≤3 per screen)
273
274 Ladder per screen — enter → orient → hover → press → action begins → waiting
275 → completion → failure → recovery → exit — with the moments that must feel
276 authored, everything else staying calm:
277
278 - **Startup.** *Enter:* hero breathes once (idle breath) — the quietest
279 "I'm here". *Waiting (connect):* echolocation on the model segment; the
280 word `not connected` itself stays still. *Completion (first connect):*
281 **spout** — the one payoff moment; suppressed if work resumes instantly.
282 *Failure:* flat `✗` + the model segment says what to do next (microcopy:
283 "no route yet — /connect"), never "Error". *Exit into work:* composer
284 keeps focus — no modal re-orientation. Surprise budget: spout and hero
285 breath; composer focus changes only its border and send affordance.
286 - **Work + Pod.** *Orient:* the pod-formation tree draws its `├──└──` rows in
287 one ≤600 ms top-down reveal, then is forever still (continuity: the tree is
288 the same object in the ledger below). *Waiting:* receipt rows carry typed
289 state marks (`●○✓!`) + echolocation only on the live row — no duplicated
290 status anywhere (footer owns phase, ledger owns whales, stream owns events).
291 *Press:* ledger row `▶` + inspect opens beside the evidence, never
292 replaces it. *Failure:* the caution/failed receipt is Attention/Failure ink
293 **plus a word** — color never invents state. Surprise budget: formation
294 reveal, the legend row that teaches the marks in place.
295 - **Settings.** *Orient:* the live preview is a real projection of the real
296 renderers — what you see is what ships. *Press (apply theme):* one depth-
297 line ripple across the preview's context meter, one-shot, ≤16 cells —
298 change you can feel without a transition festival. *Hover:* theme rows
299 preview on hover-delay (300 ms) only in `Full` motion. *Recovery:* Esc
300 restores the pre-preview theme with no flicker (projection, not mutation).
301 Surprise budget: the apply ripple, Deepsea's silent ambient life (empty-
302 cells-only, opt-in, settles under low motion).
303
304 Accessibility is non-negotiable throughout: every motion above has a still
305 frame; NO_COLOR retains meaning in glyph/intensity, and ANSI-16 adds its role hues; every
306 target is keyboard-reachable with visible focus; red stays failure-only.
307
308 ## 8. Acceptance status
309
310 - ✅ Topbar implemented end-to-end: deterministic widget, shed order, hitboxes,
311 ascii-safe, meter warn ink, hover — **6/6 tests green** via
312 `./scripts/dev-test.sh tui topbar` (6 run, 11,329 skipped; nextest).
313 - ✅ 12/12 topbar goldens written and asserted at the four blocker sizes.
314 - ✅ Spec covers every region of all three screens with lineage (§5a), real
315 constraint lists (§5b), semantic color only (§5d), gated wall-clock motion
316 with still frames (§5e), clickability registry (§6), moment map (§7).
317 - **Partial (named):** the remaining components (hero, quick actions, option
318 strip, rail, receipt stream, ledger, theme list, live preview, settings
319 rail, composer restyle, footer merge, notifications) are specified but not
320 implemented; their 8 reserved golden names are empty until each lands.
321 Wiring `topbar` into `frame.rs` is deliberately **not** done here — it
322 collides with live writers #5698/#5699 and belongs to the first landing
323 slice after their bases settle.
324
325 Not authorized / not done: no merge, no PR, no deploy, no `BASE_PROMPT` or
326 `motion/ethos.rs` edits, no billing, no writes into other agents' worktrees.
327
327 lines MARKDOWN