cast(phase 6): land Phase 1 launcher + verification#99
Merged
Conversation
PR #93 already shipped the Cast event follower, transcript renderer, exit summary, `cast.summary` event writer, and stdin forwarding for *newly launched* sessions. The remaining Phase 2 gap was `/attach` and `/summon`: those still routed through the legacy `attach_session` loop in main.rs, which uses a HashSet<String> for dedup and reads from the SQLite store directly — bypassing the daemon's `/events` endpoint and `afterSeq` cursor. This commit closes the gap. New `cast/attach.rs`: - `CastAttachSummary` + `find_cast_summary` decode the most recent `cast.summary` event from a session's history so the attach outcome can describe what the prior run did. - `summary_already_recorded` lets the launch-side writer skip a duplicate summary when attach replays existing events through the same follower. - `format_summary_note` renders the decoded summary as a one-line outcome card note (status, exit code, harness, request — truncated to 60 chars). `shell.rs` wiring: - New `attach_via_cast` dispatcher: live sessions stream through `follow_until_exit` with the same `afterSeq` cursor as launches and accept stdin forwarding; completed sessions replay the full event log through the same `TranscriptObserver` so the user sees the transcript shape they'd see at original launch, then surface the prior `cast.summary` in the outcome. - Falls back to the legacy `attach_session` loop when the daemon isn't running, with an outcome note explaining the fallback. - `write_cast_summary_event` is now idempotent — guards against double-logging when an attach replay reaches the exit event a second time. - New `AttachOrigin` enum distinguishes `/attach` from `/summon` in the outcome card's `launched` label. `main.rs`: - Extract `summon_only_command` from `summon_session_command` so Cast can un-archive without dragging in the legacy attach loop. The top-level `coven summon` CLI behaviour is unchanged. Tests: 14 new (11 in `cast::attach`, 3 in `shell::attach_tests` covering AttachOrigin labels and `replay_completed_session` over a stub client). Total: 275 unit + 4 smoke tests pass. cargo fmt clean, clippy clean, diff --check clean. Non-interactive `coven` smoke renders the Cast frame and exits 0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/OpenCoven/coven/sessions/2b765f92-b723-47d6-b4ab-4172234a81d1 Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Defines the sleek-minimalist Cast Codes target so Phase 2 has rules to implement against: surfaces in scope, the 14-char field column, hierarchy order, color roles re-anchored on PRIMARY_STRONG (#9A8ECD), the chip system for risk states, copy tone, and twelve explicit anti-patterns lifted from the current launcher (workspace map graph, fake task inbox, +---+ ASCII chrome, repeated brand voice, Store footer, etc.). Ends with the file seam Phase 2 will edit and a done-when checklist. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Defines the sleek-minimalist Cast Codes target so Phase 2 has rules to implement against: surfaces in scope, the 14-char field column, hierarchy order, color roles re-anchored on PRIMARY_STRONG (#9A8ECD), the chip system for risk states, copy tone, and twelve explicit anti-patterns lifted from the current launcher (workspace map graph, fake task inbox, +---+ ASCII chrome, repeated brand voice, Store footer, etc.). Ends with the file seam Phase 2 will edit and a done-when checklist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rework the launcher and theming across the TUI: replace the old "CovenCLI" identity with "Cast" and update tests to match the new wording and layout. Add several new theme tokens and helpers (SURFACE_0, SURFACE_3, BACKDROP, TEXT, TEXT_DIM, BORDER_DIM, SCROLL_TRACK), a Status semantic with status_token/status_style, a Palette helper, and a reusable fit_chars utility with tests. Remove duplicated fit_chars implementations and import theme::fit_chars where needed. Refactor cast renderer to use a LauncherSnapshot model and a two-column command+snapshot layout, introduce new rendering helpers (push_line, push_snapshot_row, render_paired_line, launcher_command_window, RowCell), use the U+203A selection marker, and simplify prompt/footer/preview rendering. Update chat renderers to use the new theme tokens (text, dim, border, scroll track, status_style) and replace ad-hoc color choices with semantic tokens. Adjust sessions and shell code to consume the shared utilities.
Plan/outcome cards now follow the Phase 1 TUI visual contract: a 14-char label column, fixed-width ALL-CAPS risk chips ([ SAFE ] / [CONFIRM ] / [ REJECT ]) colored by severity, noun-first risk reasons as continuation rows (no `!`/`X` glyphs), numbered step list capped at four, and a risk-aware footer hint. Sacrifice plans now ask for the typed `sacrifice` word in the footer; rejected plans steer the user to reframe. Execution semantics are unchanged: planner output, safety classification, and gate behavior are untouched — only the renderer (and the theme tokens the renderer leans on) moved. Also adds `BORDER_SUBTLE` / `BORDER_STRONG` semantic tokens so future single-rule separators have a brand-aligned color, mirroring the `--oc-border-subtle` / `--oc-border-strong` CSS variables. Tests assert key labels, every risk chip, harness-source copy (`Cast default` / `user-chosen`), the typed-confirm footer for sacrifice, the intent-row fallback for system actions, step/note caps, and that plain output never leaks an ANSI escape across any risk state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds cast::quest, a pure module that decomposes a high-level user goal into an ordered Quest of phases (design → implement → verify by default). Each phase carries a concrete sub_prompt the harness will receive; advance() attaches a structured QuestHandoff from the prior phase and recomposes the next sub_prompt deterministically, while preserving any user-authored override. render_quest_handoff() turns the handoff into a visible card — source phase, prior status, carried context, target harness, and the verbatim sub-prompt — so every delegation is inspectable before it lands. No LLM planner is introduced; sub-prompts are assembled from structured templates plus the recorded prior outcome. The cast shell wiring for a `/quest <goal>` intent is left as the next-phase seam and documented in docs/design/cast-quest-flow.md. Covered by 15 new unit tests (11 in cast::quest, 4 in cast::render) for the composer, advancer, edits, skip, failure framing, long sub-prompt clipping, and quest exhaustion. Full cast suite: 105/105 green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`cargo fmt --check` flagged unformatted regions in three Cast modules left over from earlier phases. No behavior change; pure formatting cleanup so the next render-touching commit produces a minimal diff. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 3 added 10 launcher tests for the Phase 1 design contract but never landed the implementation -- the renderer in `tui/shell.rs::render_magical_tui_frame_with_mode_and_width` was still the pre-contract code (CovenCLI header, ASCII workspace map, task inbox, "Selected command", "Store: ~/.coven", `+--+` chrome) and the launcher tests were all failing. Rewrite the launcher against [`docs/design/cast-tui-contract.md`](docs/design/cast-tui-contract.md): - `Cast` identity row in `PRIMARY_STRONG`. - Single thin `─` rule above and below the prompt -- no bezels, no `Ask anything` label. - Two-lane body: `Commands` rail (windowed, 6 visible, `›` selection marker, `N of 14` scroll hint) + `Snapshot` lane (project / harness / daemon, resolved best-effort). - `spell` / `detail` field rows preview the selected slash command. - One `DIM` footer hint: `enter run · ↑↓ select · esc quit · ctrl+u clear`. Delete the workspace-map, status, task-inbox, and input-box helpers. Replace `>` with the U+203A `›` marker. All 10 previously-failing `magical_tui_frame_*` tests now pass; full `cargo test -p coven-cli` green (313 unit + 4 smoke). Reconcile `docs/start/coven-tui.md` with the implementation: - Slash command table now lists the 14 real commands (was claiming `/clear`, `/export`, `/agent` which do not exist). - Keyboard shortcuts match `shell.rs::run` (drop `h`, `Tab`, `Ctrl+L`; add `Backspace`, `Ctrl+U`). - Arrow-key prose describes the Commands rail, not session-browser rituals. - Intro names Cast and the spell-parser flow. Add `docs/design/cast-phase6-inspection.md` with the captured NoColor launcher frames at width=76 and 96 for selection=0 (empty + typed) and selection=12 (`/sacrifice`), the live non-interactive Cast frame, gate results, and explicit "what was not tested" / "risks open before merge" sections. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR implements the Phase 1 “Cast” launcher contract in the CLI TUI (previously only covered by tests), updates Cast rendering primitives and attach/summon flows to better match the new transcript/outcome shape, and reconciles user + design documentation with the new UI.
Changes:
- Rewrites the launcher frame renderer in
tui/shell.rsto the Cast contract layout (identity row, thin-rule prompt, windowed Commands rail + Snapshot lane, compact action preview, single footer hint). - Introduces Cast attach helpers + quest flow rendering/logic, and improves attach/summon handling via daemon follower + summary notes.
- Updates docs to reflect the new launcher behavior/command set and adds Phase 6 verification artifacts.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/start/coven-tui.md | Updates end-user TUI docs to describe Cast launcher, commands, and shortcuts. |
| docs/design/cast-tui-contract.md | Adds the Phase 1 visual/design contract for Cast TUI surfaces. |
| docs/design/cast-quest-flow.md | Adds the Phase 5 quest-flow design contract and wiring notes. |
| docs/design/cast-phase6-inspection.md | Adds Phase 6 verification notes with captured frames and manual-check guidance. |
| crates/coven-cli/src/tui/shell.rs | Implements Cast launcher layout; adds daemon-based attach/replay path and summary idempotence. |
| crates/coven-cli/src/tui/sessions.rs | Deduplicates truncation helper by using theme::fit_chars. |
| crates/coven-cli/src/tui/chat/render.rs | Aligns Ratatui chat rendering colors with semantic theme tokens. |
| crates/coven-cli/src/tui/cast/render.rs | Refactors Cast plan/outcome rendering to contract-style rows/chips; adds quest handoff card renderer. |
| crates/coven-cli/src/tui/cast/quest.rs | Adds deterministic quest model + composer + advancer with unit tests. |
| crates/coven-cli/src/tui/cast/mod.rs | Exposes new Cast modules/helpers (attach + quest) to the shell. |
| crates/coven-cli/src/tui/cast/attach.rs | Adds helpers to decode/format cast.summary for attach outcome notes. |
| crates/coven-cli/src/theme.rs | Adds semantic tokens (backdrop, scroll track, border colors, text roles) + palette/status helpers + shared fit_chars. |
| crates/coven-cli/src/store.rs | Adds event_kind_exists query helper with unit test. |
| crates/coven-cli/src/main.rs | Refactors summon into summon_only_command for Cast-driven summon+attach flow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+856
to
+869
| // For completed/replayed sessions, surface the original Cast summary so | ||
| // the user can see what the prior run was about. Live attaches just wrote | ||
| // the summary themselves, so showing it again would only echo the exit | ||
| // line we already printed. | ||
| if !is_live { | ||
| let history = client.list_events(ChatEventQuery { | ||
| session_id: &session.id, | ||
| after_seq: None, | ||
| limit: None, | ||
| })?; | ||
| if let Some(note) = cast::find_cast_summary(&history).and_then(|s| format_summary_note(&s)) | ||
| { | ||
| notes.push(note); | ||
| } |
Comment on lines
+391
to
+400
| /// `label value` row with a fixed 14-char label column. Two-space gap | ||
| /// before the value so the eye locks onto a single value column across the | ||
| /// whole frame. | ||
| fn push_label_row(frame: &mut String, p: &Palette, label: &str, value: &str) { | ||
| let label_block = format!("{:<width$}", label, width = LABEL_COLUMN_WIDTH); | ||
| frame.push_str(&format!( | ||
| "{}{}{} {}{}{}\n", | ||
| p.field_label, label_block, p.reset, p.text, value, p.reset | ||
| )); | ||
| } |
| Cast, your Coven familiar, is ready. Type a spell, or use a slash command. | ||
|
|
||
| Context | ||
| Project /Users/buns/Documents/GitHub/OpenCoven/coven |
The Phase 1 contract added `theme::BORDER_SUBTLE` and `theme::BORDER_STRONG` but no renderer plumbed them in, leaving four `dead_code` warnings on every build. Apply them per the docstrings in theme.rs: - Top rule above the prompt → `BORDER_SUBTLE` (quiet panel separator) - Bottom rule below the prompt → `BORDER_STRONG` (focused underline; the launcher prompt is always the interactive element) `cargo build -p coven-cli` no longer emits any warnings. Gates remain green (fmt, clippy --tests -D warnings, 313 unit + 4 smoke). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Cast redesign (Phases 1–6 on `cast/*` branches) is the largest in-flight Coven runtime change and was not yet on the public roadmap. Add it under `Coven > Now` with links to the visual contract and quest-flow design docs and a pointer to PR #99 as the current review slice. Refresh the "Last updated" stamp. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Cast non-interactive frame (printed when `coven` runs without a TTY) shipped with three contract violations called out in `cast-tui-contract.md` §2.6 and §3: - "Cast — your Coven familiar" headline used an em-dash decoration and lampshaded the familiar's name. - "Cast, your Coven familiar, is ready. Type a spell, or use a slash command." used second-person greeting + "is ready" copy that §2.6 forbids. - "Project" and "Default harness" field labels were Title Case; §2.3 requires lowercase labels in a fixed 14-char column. - Two consecutive `Tip:` lines (one from `render_cast_frame_with_mode`, one appended by `print_cast_non_interactive_frame`) repeated the same instruction. Tighten to: - Identity row: `Cast` alone (PRIMARY_STRONG), no decoration. - One subtitle line in `FIELD_LABEL`: `Coven familiar · type a spell or pick a slash` -- keeps the brand positioning without second-person greeting or lampshading. - `project` / `harness` field rows now use the shared `push_label_row` helper so they live in the canonical 14-char label column alongside the plan and outcome cards. - Single dim footer hint: `run \`coven\` in a terminal to open the launcher · empty input runs a slash`. Drops the duplicate `Tip:` println from the shell-level printer. All `cast_non_interactive_frame_*` and `non_interactive_frame_*` tests still pass -- the "Coven familiar" substring lives in the subtitle, and the project / harness assertions match the new lowercase labels by substring. Full `cargo test -p coven-cli` green (313 unit + 4 smoke); fmt and clippy --tests -D warnings clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 20, 2026
BunsDev
added a commit
that referenced
this pull request
May 20, 2026
Re-applies the two improvements the Copilot SWE bot wrote on PR #98's branch (commit 438690b) but never landed on main, adapted to the current quest.rs surface (phases 7-10 evolved it further). The original PR #98 was overtaken by the squash-merges of #99 and #100; this commit brings the bot's fix forward on its own so reviewers can read it independently. The two changes: 1. **Structured failure detection.** Today's `handoff_reason` keyed off string patterns (`failed`, `error`, `exit 1`, `interrupted`) which misclassified non-zero exit codes other than 1 (e.g. exit 2, 137, 130) as success framing. New `phase_failed(summary)` helper checks `exit_code != 0` first, then falls back to the status-string match for cases where exit_code is `None` (interrupted runs). 2. **Advance over Skipped phases.** `advance` (and `skip_phase` when nudging the cursor) now uses a new `next_pending_index` helper so the cursor never lands on a Skipped or Complete row. Previously the shell loop carried a defensive `if matches!(..., Skipped { .. })` guard to walk past those rows; that guard still exists as a safety-net for a hypothetical async UX that mutates `cursor` directly, but the common path is now correct at the data layer. Two new tests pin the behaviour: - `advance_skips_over_skipped_phases_and_preserves_skip_reason` - `non_zero_exit_codes_use_failure_handoff_reason` `skip_phase_advances_cursor_and_marks_status` updated to assert quest exhaustion (instead of "cursor lands on the skipped row") since the new cursor advances past Skipped. Gates: cargo fmt clean, cargo clippy --tests -D warnings clean, 346 unit + 4 smoke tests pass (2 new). Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
tui/shell.rs::render_magical_tui_frame_with_mode_and_width— Phase 3 had added 10 tests for the contract but the implementation was never written. The launcher tests were red on this branch line; they're green now.+--+corner art) with the contract layout:Castidentity, thin─rule prompt, two-laneCommandsrail +Snapshotbody with›selection marker +N of 14scroll hint,spell/detailaction preview, singleDIMfooter hint.docs/start/coven-tui.mdwith reality (slash table now lists the 14 real commands; keyboard shortcuts matchshell.rs::run).docs/design/cast-phase6-inspection.mdwith captured launcher frames at widths 76 + 96 and an explicit "what was NOT tested / risks open" section so reviewers can pick up the gaps.Gate results
cargo fmt --all -- --check— cleancargo clippy -p coven-cli --tests --no-deps -- -D warnings— cleancargo test -p coven-cli— 313 unit + 4 smoke, 0 failuresRisks open before merge
theme::BORDER_SUBTLE/theme::BORDER_STRONGare still dead code (4cargo buildwarnings). The Phase 1 contract added the tokens; no renderer plumbed them in. Doesn't fail any gate but signals an incomplete handoff.docs/ROADMAP.mdanddocs/PRODUCT-SPEC.mdunchanged. "Visible work" thesis intact; roadmap not updated since 2026-05-09 and does not yet mention the Cast redesign.Test plan
covenin a real terminal at width 80 and width ≥ 96. ConfirmCastidentity row is the onlyPRIMARY_STRONGaccent at the top, the prompt sits between two─rules, and the Commands rail shows 6 items with›next to the selection./sacrifice(item 13). Confirm the rail scrolls and13 of 14renders under the rail.DIMtoTEXTas the cursor advances;Ctrl+Uclears it.coven </dev/null. Confirm the non-interactive Cast frame still renders (regression check).🤖 Generated with Claude Code