Skip to content

Compose Tideline startup into the shared composer shell - #5770

Merged
20 commits merged into
mainfrom
feat/tideline-shell-composition-20260830
Aug 31, 2026
Merged

Compose Tideline startup into the shared composer shell#5770
20 commits merged into
mainfrom
feat/tideline-shell-composition-20260830

Conversation

@Hmbown

@Hmbown Hmbown commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Closes #5768 (source composition only; no merge or release action).

Scope

  • Cherry-picks the current-mark, startup, rounded-composer, quiet-boot, route-control, and responsive-rail source proposals into one reviewable branch.
  • Wires Startup to the existing four-row rounded Tideline composer shell instead of the flat startup-only strip.
  • Reuses one [↑] paint/hitbox geometry source for the Startup shell and preserves the normal launch keyboard/mouse submit dispatcher.
  • Keeps compact terminals on the truthful one-line fallback.

Local evidence

  • RUST_MIN_STACK=33554432 cargo test -p codewhale-tui launch_composer_tests:: --locked — 13 passed.
  • RUST_MIN_STACK=33554432 cargo test -p codewhale-tui composer_chrome::tideline_tests:: --locked — 9 passed.
  • RUST_MIN_STACK=33554432 cargo test -p codewhale-tui mouse_ui::tests::composer_click_focuses_and_send_click_matches_the_keyboard_submit --locked — 1 passed.
  • cargo fmt --all -- --check and git diff --check passed.
  • Real PTY frames: Startup at 80x24, 100x32, and 120x32 now paints rounded ╭…╮│…│╰…╯ chrome and a visible [↑]; a pointer click and Enter reached the same resulting frame aside from the live clock.

Still explicitly open

This does not prove active-work composer parity, rail/receipt/ledger runtime behavior, clean onboarding completion, provider safety/selection, CI, release artifacts, deployment, auth, or launch readiness. The current generated diving/current whale mark remains the only brand mark; no retired fluke asset is reintroduced.

CodeWhale Bot added 17 commits August 30, 2026 17:32
(cherry picked from commit ca18b92)
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T01:18:22.723035Z 84a988d PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 56s —— View job


Reviewing PR #5770

This is a large PR (17 commits, 58 files, ~2.4k/1k lines). Digging into the core TUI logic now (session_boot.rs, composer_chrome.rs, underwater.rs, work_surface/tideline.rs, mouse_ui.rs, frame.rs) and running focused checks in parallel.

Hosted Lint on #5770 failed clippy::collapsible_if in the compact
startup-composer fallback. Same cells; no behavior change.

Refs #5768
Signed-off-by: Hunter Bown <hmbown@gmail.com>
@claude

claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 22s —— View job


Reviewing PR #5770

Digging into the core TUI logic now (session_boot.rs, composer_chrome.rs, underwater.rs, work_surface/tideline.rs, mouse_ui.rs, frame.rs).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 84a988dc0e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// provider/config notice, or a previous session record: only an
// intentional resume or explicit initial input enters the live session
// path directly.
let launch_visible = resume_session_id.is_none() && initial_input.is_none();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Run deferred setup gates after leaving Startup

Every clean interactive launch now sets launch.visible to true, but the event loop's only call to open_setup_checkpoint_if_due is guarded by !app.launch.visible; later, begin_launch_session merely clears the flag without invoking that gate. Consequently, a returning user with a due versioned Constitution checkpoint can choose New Session, Chat, or submit from the Startup composer and work for the entire process without seeing the checkpoint; --skip-onboarding also no longer records its deferral. Run the post-launch setup gate when Startup transitions into a session, or otherwise preserve the gate before exposing Startup.

Useful? React with 👍 / 👎.

Comment on lines +353 to +354
&[work_line.as_str()],
crate::tui::phase_strip::context_percent_from_app(app),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep context and work out of the active rail

At widths of 100 columns or more, this newly wired active-session rail renders both the work summary and context_percent, even though the same frame already renders the context meter in the topbar and the footer, creating multiple owners for a fact that the TUI contract assigns to the header; the contract likewise assigns work to the top strip. This makes the passive rail compete with established surfaces and allows their projections to diverge, so the live rail should omit these facts or replace their existing owners rather than duplicating them.

AGENTS.md reference: crates/tui/AGENTS.md:L8-L10

Useful? React with 👍 / 👎.

Comment on lines +203 to +204
after = AFTER
print("ARCHIVED: former Codewhale empty-state portrait — main vs Signal Cut\n")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore sys before using the default preview mode

When the documented default invocation python3 scripts/preview-whale-art.py runs, evaluating sys.stdout.isatty() raises NameError because this rewrite removed import sys; --plain only avoids the error through short-circuiting, and the missing-Pillow path also still uses sys.stderr. Restore the import so the normal preview and PNG fallback execute.

Useful? React with 👍 / 👎.

Comment on lines 1013 to 1015
if let Some(input_row) = hitboxes
.composer
.input
.map(|area| area.y.saturating_sub(stage_area.y))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep completion rows above the rounded composer border

With the full four-row Startup composer, the new input rect begins at dock.y + 1, but render_launch_completion_popup paints its first result at input_y - 1; typing / or @ therefore writes the selected completion directly over the rounded shell's top border instead of above it. Pass an anchor that accounts for the enclosure's border, or reserve the border row before stacking popup entries.

Useful? React with 👍 / 👎.

Comment on lines +174 to +177
route: hitboxes
.iter()
.find(|hitbox| hitbox.id == TopbarSegmentId::Model)
.map(|hitbox| hitbox.area),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Derive the route hitbox from the shed topbar layout

When an active run or pod adds segments before Model and a narrow terminal causes those segments to shed, the renderer moves Model next to the brand, but topbar_hitboxes still positions it after every original segment. Registering that stale rectangle as the new route target means clicking the visibly painted route does nothing, while an unrelated or off-screen rectangle owns the action; compute the target from the same kept-segment layout used for painting.

AGENTS.md reference: crates/tui/AGENTS.md:L19-L21

Useful? React with 👍 / 👎.

The clippy collapsible_if repair was still two lines; rustfmt --check
wants the let-chain on one line.

Refs #5768
Signed-off-by: Hunter Bown <hmbown@gmail.com>
@claude

claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 58s —— View job


Reviewing PR #5770

The latest push (5cda759, 5aa6052) is just a clippy/rustfmt cleanup of the status-line if let — no behavior change, and it doesn't touch any of the issues below. I re-verified the open Codex findings against current HEAD (5aa6052) by reading the actual code paths, not just the diff hunks. All five are still live.

Confirmed correctness issues

  1. crates/tui/src/tui/app/init.rs:121 — the versioned-Constitution setup gate is silently skipped for almost every launch now. launch_visible is now unconditional except for resume/initial-input, so open_setup_checkpoint_if_due (only called from crates/tui/src/tui/ui/event_loop.rs:362 under !app.launch.visible) never runs, and begin_launch_session (crates/tui/src/tui/ui/session_state.rs:624-646) just flips launch.visible = false without calling it either. A returning user with a due checkpoint, or --skip-onboarding's deferral bookkeeping, never sees/records it for the life of the process. This needs the gate invoked at the Startup→session transition (or preserved before Startup is shown).

  2. crates/tui/src/tui/ui/frame.rs:1013-1015 + underwater.rs:1125 — with the enclosed 4-row composer, input_y = dock.y - stage.y + 1 (below the top border), but render_launch_completion_popup paints its first row at input_y - 1, which lands exactly on the rounded shell's top border row (dock.y - stage.y). Typing / or @ in Startup will paint the completion list over ╭───╮ instead of above it.

  3. crates/tui/src/tui/ui/frame.rs:172-177interaction_hitboxes.route is derived from topbar_hitboxes(&topbar, area), which (in topbar.rs:452) still iterates the full topbar.segments list, not the shed-pass kept list the renderer actually draws (topbar.rs:326-332, shed_pass). topbar.rs itself wasn't touched by this PR, but this PR is what newly wires the Model/route segment as a click target (OpenProviderPicker). Once a run/pod segment causes Model to shed leftward, the registered click rect no longer matches the painted position — clicking the visible route segment misses, and an unrelated rect becomes clickable instead.

  4. scripts/preview-whale-art.py:201sys.stdout.isatty() is called at line 201 but the file no longer has import sys (only argparse is imported at the top, and sys.stderr is still used at line 143 in the Pillow-missing path). The default invocation (python3 scripts/preview-whale-art.py, no --plain) raises NameError before any preview prints.

Contract issue

  1. crates/tui/src/tui/work_surface/tideline.rs:324-354 (active_session_tideline_rail_groups) — the new live-session rail summary includes both a work line and context_percent_from_app(app). Per crates/tui/AGENTS.md:8-10, context belongs to the header and work belongs to the top strip; both are already rendered there in the same frame. This gives the fact two owners that can diverge (e.g. rail vs. topbar context meter disagreeing during a fast-moving turn) instead of one. Worth dropping those two facts from the rail's projection or having it read the header/top-strip's already-computed value instead of re-deriving it.

Other notes

  • No provider-prefix-inference issues found — scanned the diff for deepseek/, anthropic/, openai/, qwen/ and prefix-based starts_with branching; the only hits are an env var name and pre-existing shell-entry heuristics unrelated to provider/model selection (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608 invariant intact).
  • Reuse is good: the PR explicitly reuses one [↑] hitbox/paint source between Startup and the live composer rather than forking a second implementation, and the active-rail groups are a read-only projection over existing App state (no parallel store).
  • Could not execute cargo test/cargo fmt in this sandboxed review session (command approval required); relied on direct code reading to confirm each finding against the call sites rather than trusting the diff hunks alone. The PR's own reported local runs (launch_composer_tests, tideline_tests, the composer mouse/keyboard-parity test) are unchanged by the two follow-up commits, so they should still hold, but I did not re-run them.

None of these were introduced or fixed by the two most recent commits (5cda759, 5aa6052) — they trace back to 84a988d, the commit Codex already reviewed. Items 1, 2, and 3 are the ones I'd block on before merge: they affect a checkpoint/compliance gate, a visible chrome bug on every Startup completion popup, and a dead click target respectively. Item 5 is a contract violation worth fixing but is more of a "two sources of truth" risk than an outright bug. Item 4 is script tooling, not shipped product code, but is a one-line fix.

@codewhale-agent codewhale-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codewhale review

The PR composes the Tideline startup surface into the shared rounded composer shell, removes the retired launch_screen setting, centralizes provider-picker entry, adds a passive active-session Tideline rail, and migrates MCP/plugin boot output into the compact footer. The diff includes extensive tests and golden updates. Main concerns are a narrow-terminal startup composer edge case, the removal of a user-facing launch preference, and a few integration gaps around the centralized provider-picker path and Top-placement rail.

Findings

  • [WARNING] Enclosed startup composer can render an empty shell at narrow dock widths (crates/tui/src/tui/underwater.rs)
    The enclosed decision in render_tideline_startup uses layout.dock.width >= 6, but tideline_composer_geometry positions the [↑] submit target before the content rect until roughly width 9. For dock widths 6–8, render_launch_composer hits content_width == 0 and returns after painting only the rounded shell, leaving no placeholder or input. Compact terminals should fall back to the one-line projection instead of painting an empty shell.
  • [WARNING] Unified provider-picker path may lose the duplicate-picker guard (crates/tui/src/tui/ui/overlays.rs)
    The previous AppAction::OpenProviderPicker code guarded the non-onboarding case with app.view_stack.top_kind() != Some(ModalKind::ProviderPicker) before pushing a new picker. The new open_provider_picker helper delegates to open_launch_provider_picker, and it is not clear from the diff whether that helper preserves the guard. If it does not, repeated F3 or topbar clicks while the picker is already open could stack duplicate picker views. A regression test for the already-open case is missing.
  • [WARNING] Removing launch_screen is a breaking startup behavior change without an opt-out (crates/tui/src/tui/app/init.rs)
    Settings::launch_screen is deleted and App::new now always sets launch_visible for fresh interactive launches, except resume or initial input. Returning users who previously set launch_screen = false will now land on the Tideline startup hero. The new test only verifies that the legacy key is dropped when saving settings; there is no migration warning or runtime opt-out described. This should be confirmed as intentional and accepted by product behavior.
  • [INFO] Missing test coverage for the explicit compact composer_border=false startup path (crates/tui/src/tui/underwater/tideline_tests.rs)
    LaunchComposerDisplay.enclosed and tideline_startup_hitboxes_with_composer(stage, false) are the explicit compact fallback, but the new tests mostly cover the default composer_border = true rounded shell. A test pinning the one-line fallback rendering and hitboxes, plus the fallback when a full shell cannot fit, would lock in the truthful compact-terminal behavior.
  • [INFO] No integration test for F3 opening the actual provider picker (crates/tui/src/tui/ui/event_loop.rs)
    The keyboard-shortcut unit test and the mouse-event unit test only prove that TopbarRoutePickerRequested is emitted. There is no event-loop or handler test showing that F3 on an empty view stack ultimately opens the picker exactly once and respects onboarding. This is a new shell-wide route binding, so an integration test would prevent regressions in the centralized open path.
  • [INFO] Active-session rail has no test for Top placement (crates/tui/src/tui/work_surface/tideline.rs)
    active_session_tideline_rail_width claims Top receives the compact summary, but the new tests cover only default Left, occupied Left, Right, and Off. Top placement with an empty legacy work surface is untested and could regress the rail width ladder or its interaction with the top work strip.

Assessment

The PR is substantial and generally well-tested, with clear separation between the shared composer geometry and caller-owned content. The main correctness risk is the narrow-terminal startup composer shell, and there are a few integration gaps around the centralized provider-picker entry and rail placements. The launch_screen removal also needs explicit product confirmation because it changes existing users' startup behavior. I would not merge until the narrow-shell edge case and provider-picker guard are verified.


Advisory review by Codewhale (codewhale review --pr 5770 --post, head 5aa6052e6d2ac04d7efe4b93d9f8b5324cb0f248). Line-specific findings are also posted as inline review comments; mechanical fixes arrive as committable suggestions you can apply from the Files tab. CODEOWNERS approval still governs merge.

The blocker-size startup goldens still pinned the retired flat
composer strip, so startup_matches_goldens_at_blocker_sizes failed on
ubuntu and windows CI. The 40x10 floor golden is unchanged: the
composer sheds at that size by design.

Refs #5768
Signed-off-by: Hunter Bown <hmbown@gmail.com>
@claude

claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 2m 15s —— View job


Reviewing PR #5770 (synchronize)

  • Gather context (diff, changed files, AGENTS.md/CLAUDE.md rules)
  • Understand the request (re-review after new commit 57db457)
  • Check whether 57db457 addresses the previously confirmed findings
  • Review correctness/panics/race conditions
  • Review provider/model/route safety invariant (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608)
  • Review reuse/simplification/dead code
  • Review test coverage
  • Review security
  • Post final review summary

The new commit since the last review, 57db457 ("re-bless startup goldens for the rounded composer shell"), only touches four golden fixture files (startup_80x24.txt, startup_100x30.txt, startup_120x32.txt, startup_160x40.txt) — no source changes. I diffed it directly: it replaces the retired flat composer strip in the goldens with the rounded ╭─╮│ │╰─╯ shell the earlier commits (84a988d) already implemented, which is what startup_matches_goldens_at_blocker_sizes needed to stop failing on CI. This is a legitimate fixture fix, not a masked regression — the 40x10 floor golden is correctly left untouched since the composer sheds there by design.

Because this commit changes no logic, I re-verified each of the five previously-confirmed findings directly against current HEAD (57db457) rather than re-deriving them — all five are still live:

Confirmed correctness/contract issues (unchanged by this push)

  1. crates/tui/src/tui/app/init.rs:121launch_visible is unconditional except for resume/initial-input, but the only caller of open_setup_checkpoint_if_due (crates/tui/src/tui/ui/event_loop.rs:362) is still gated on !app.launch.visible, and begin_launch_session (crates/tui/src/tui/ui/session_state.rs:624-646) just clears the flag without invoking the gate. A returning user with a due versioned-Constitution checkpoint can go through Startup and never see/record it for the life of the process.

  2. crates/tui/src/tui/underwater.rs:977-982 + :1125, wired at crates/tui/src/tui/ui/frame.rs:1013-1017 — with the enclosed 4-row composer, input_y = dock.y - stage.y + 1 (the interior row below the top border), but render_launch_completion_popup's first row still paints at input_y - 1, which is exactly the rounded shell's top border row. Typing / or @ in Startup paints the completion list over ╭───╮ instead of above it.

  3. crates/tui/src/tui/topbar.rs:452, consumed at crates/tui/src/tui/ui/frame.rs:171-177topbar_hitboxes still iterates the full topbar.segments list rather than the shed-pass kept list (topbar.rs:227-247) the renderer actually paints. This PR is what newly wires the Model/route segment as a click target (OpenProviderPicker); once a run/pod segment causes Model to shed leftward on a narrower terminal, the registered hitbox no longer matches the painted position, so clicking the visible route segment misses.

  4. scripts/preview-whale-art.py:201sys.stdout.isatty() is called but import sys was dropped from the top of the file (only argparse remains); sys.stderr is also still used at line 143. The documented default invocation, python3 scripts/preview-whale-art.py (no --plain), raises NameError before printing anything.

  5. crates/tui/src/tui/work_surface/tideline.rs:346,354 (active_session_tideline_rail_groups) — the new live-session rail still folds in both a work line and context_percent_from_app(app). Per crates/tui/AGENTS.md:8-10, context belongs to the header and work to the top strip, both already rendered in the same frame — this gives each fact two independent owners that can visibly disagree during a fast-moving turn.

None of these are touched by 57db457; items 1–3 were also confirmed unchanged by the two commits before it (5cda759, 5aa6052) in the prior review pass. Item 5 is a contract violation (two sources of truth) rather than a crash; item 4 is script tooling, not shipped product code, but is a one-line fix.

Other notes (unchanged from prior pass)

  • No provider-prefix-inference issues (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608 invariant intact) — no code in this branch infers a provider/model switch from a wire-id prefix or freeform text.
  • Reuse is good: one [↑] hitbox/paint source is shared between Startup and the live composer, and the active-rail groups are a read-only projection over existing App state.
  • I still can't execute cargo test/cargo fmt in this sandboxed session; findings are confirmed by direct code reading against the call sites at current HEAD, not by re-running the PR's reported local test commands.

Recommendation: items 1–3 (checkpoint/compliance gate skipped, completion popup painting over the border, dead route-click hitbox) are the ones I'd block merge on. Item 5 is worth fixing before merge as a contract violation. Item 4 is a trivial one-liner.

@codewhale-agent codewhale-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codewhale review

PR #5770 composes the Tideline startup shell into the shared rounded composer geometry, removes the retired launch_screen setting, adds a live session Tideline rail, and refactors boot diagnostics into a compact footer notice. The patch is broad but generally coherent, with several new tests covering the main paths. The highest review risk is the provider-picker apply refactor possibly losing a duplicate-view guard, and the compact composer fallback lacking direct regression coverage.

Findings

  • [WARNING] Provider-picker duplicate guard may be lost in apply refactor (crates/tui/src/tui/ui/apply.rs)
    The previous AppAction::OpenProviderPicker branch only pushed ProviderPickerView when view_stack.top_kind() != Some(ModalKind::ProviderPicker). The refactor delegates to open_provider_picker, which calls open_launch_provider_picker in the non-onboarding path. If open_launch_provider_picker does not itself preserve that top_kind guard, repeated /provider commands or topbar route clicks can push duplicate picker views. Verify the guard exists in open_launch_provider_picker; otherwise restore it in open_provider_picker.
  • [WARNING] Compact composer fallback has no regression coverage (crates/tui/src/tui/underwater/tideline_tests.rs)
    The launch composer was split into a rounded-shell path and a compact one-line fallback, but the new tests and updated goldens exercise only the default enclosed shell (composer_border=true). The composer_border=false path, including hitbox behavior and the removed divider rule, has no direct unit or golden coverage. A silent regression there would not be caught by the listed test commands.
  • [INFO] launch_screen setting removal is a visible behavior change (crates/tui/src/tui/app/init.rs)
    Removing Settings::launch_screen and making launch_visible depend only on resume_session_id and initial_input means existing users who explicitly set launch_screen = false will now see the startup hero on fresh launches. The legacy setting is accepted and dropped on save, but the PR should ensure release notes and user-facing docs explicitly call out that this opt-out no longer exists.

Assessment

The change is well-structured and test-conscious, with focused tests for startup visibility, provider route events, boot diagnostics, and the live Tideline rail. Before merge, the provider-picker guard should be confirmed, and explicit coverage for the compact composer_border=false startup fallback is recommended. The core shell geometry sharing looks sound.


Advisory review by Codewhale (codewhale review --pr 5770 --post, head 57db4575c73558b2db59b93f448a66bcb92546f6). Line-specific findings are also posted as inline review comments; mechanical fixes arrive as committable suggestions you can apply from the Files tab. CODEOWNERS approval still governs merge.

@Hmbown Hmbown closed this pull request by merging all changes into main in 3d87d2f Aug 31, 2026
@Hmbown
Hmbown deleted the feat/tideline-shell-composition-20260830 branch August 31, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compose and verify the Tideline shell as one coherent running TUI

1 participant