cast(phase 7): wire /quest + cast.quest.* event ledger#100
Merged
Conversation
Phase 5 landed the standalone `cast::quest` module (data model, composer,
advance, render_quest_handoff) but the shell had no way to invoke it. The
explicit Phase 6 deferral in `docs/design/cast-quest-flow.md` §6/§7 (shell
wiring + cast.quest.* event ledger) is the entire scope of this phase.
Per the four scope answers locked at the start of Phase 7:
- **Auto-advance per phase** — Cast prints the handoff card, gates the
sub-prompt, dispatches, waits for exit, advances. No edit / skip prompt.
- **First-session anchor** — every `cast.quest.*` event is written to
phase 0's session log; each phase still gets its own `cast.summary`.
- **Daemon path only** — quest event writes are best-effort; in the
local-PTY fallback (no session id) they are silently skipped.
- **Minimal re-attach** — `/attach <anchor>` surfaces a one-line note
(`Quest anchor — `<title>` (phase N/M, in progress)`) alongside the
existing cast.summary note. No full state rebuild.
Changes:
- `tui/cast/intent.rs` — new `CastIntent::Quest { goal }` variant.
`/quest <goal>` slash branch and four natural-language triggers
(`start a quest to …`, `begin a quest to …`, `quest to …`, `quest: …`).
Empty goal errors clearly. 4 new tests.
- `tui/cast/plan.rs` — `quest_plan` builds a plan card listing the
default design → implement → verify rhythm plus the resolved harness;
risk is `Safe` because each phase reruns the gate against its own
sub-prompt. 2 new tests.
- `tui/cast/quest.rs` — drops the blanket `#![allow(dead_code)]` (the
module is no longer dead); narrows the allow to the deferred-UX surface
(`set_phase_sub_prompt`, `skip_phase`, `compose_sub_prompt`,
`QuestPhaseStatus::Running`/`Skipped`, convenience accessors). Doc
comments name the Phase 7 scope decision so a future phase knows why
they're still in the crate surface.
- `tui/cast/attach.rs` — new `find_cast_quest_info` /
`format_quest_attach_note` helpers decode the anchor session's
`cast.quest.{started, phase_completed, completed}` events into a
`CastQuestAttachInfo` and a one-line outcome-card note. 6 new tests.
- `tui/cast/mod.rs` — re-exports the new helpers; trims the broad
`#[allow(unused_imports)]` on quest types now that most are wired.
- `tui/shell.rs` — `dispatch_cast_quest` loop (build quest, render
handoff, gate per-phase sub-prompt, dispatch via dispatch_cast_launch,
derive QuestPhaseSummary from the cast.summary event, write the
cast.quest.* event, advance). `attach_via_daemon` surfaces the quest
attach note for non-live replays.
- `docs/design/cast-quest-flow.md` — ticks the §7 Phase 6 done-when boxes
(now landed in Phase 7) and adds a §8 "Deferred to a later phase"
section listing what auto-advance does NOT cover (edit/skip UX, full
re-attach rebuild, local-PTY fallback ledger, async Running state).
Gates: cargo fmt clean, cargo clippy --tests -D warnings clean,
325 unit tests + 4 smoke tests pass (12 new tests in this phase).
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 wires Cast “quests” end-to-end in the CLI TUI: parsing /quest <goal> (plus natural-language triggers), rendering/dispatching multi-phase handoffs, and recording cast.quest.* ledger events on an anchor session so coven attach <anchor> can surface minimal quest progress.
Changes:
- Add
CastIntent::Quest, plan-card support, and shell dispatch loop that gates + launches each phase and advances viacast.summary-derivedQuestPhaseSummary. - Write
cast.quest.{started,phase_started,phase_completed,advanced,completed}events to the phase-0 “anchor” session and detect/format a one-line quest note on attach. - Update design docs and add unit tests around intent parsing, planning, and attach quest detection.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/design/cast-quest-flow.md | Marks Phase 7 scope as landed; documents deferred items for future phases. |
| crates/coven-cli/src/tui/shell.rs | Implements /quest dispatch loop and best-effort quest event ledger writes; surfaces quest note on attach. |
| crates/coven-cli/src/tui/cast/quest.rs | Updates quest module docs and narrows dead_code allowances to deferred APIs. |
| crates/coven-cli/src/tui/cast/plan.rs | Adds quest plan card + tests for default/no-harness behavior. |
| crates/coven-cli/src/tui/cast/mod.rs | Re-exports new attach helpers and quest APIs for shell/tests. |
| crates/coven-cli/src/tui/cast/intent.rs | Adds /quest parsing and natural-language quest triggers + tests. |
| crates/coven-cli/src/tui/cast/attach.rs | Adds quest-anchor detection/formatting helpers + tests. |
Comments suppressed due to low confidence (1)
crates/coven-cli/src/tui/shell.rs:658
- Same ownership issue as above: this payload moves
quest.titleinto the JSON value. Even after fixing the earliercast.quest.startedpayload, this will still need a clone/borrow to avoid moving out ofquest.
serde_json::json!({
"title": quest.title,
"phase_count": quest.phases.len(),
}),
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+595
to
+596
| "title": quest.title, | ||
| "goal": quest.goal, |
| if let Some(anchor) = anchor_session_id.as_deref() { | ||
| write_quest_event( | ||
| anchor, | ||
| CAST_QUEST_PHASE_STARTED, |
Comment on lines
+505
to
+510
| /// Event kind prefix Cast writes to the anchor session's event log so a | ||
| /// future `coven attach <anchor>` can detect that the session belongs to a | ||
| /// quest. Per the Phase 7 scope decision (first-session anchor), every | ||
| /// `cast.quest.*` event lives on phase-0's session; later phases still | ||
| /// emit their own `cast.summary` events on their own sessions. | ||
| const CAST_QUEST_STARTED: &str = "cast.quest.started"; |
| }); | ||
| let harness_note = match harness { | ||
| Some(plan_harness) => format!( | ||
| "Each phase delegates to {} unless you override the sub-prompt", |
| /// happy path). | ||
| pub(crate) fn format_quest_attach_note(info: &CastQuestAttachInfo) -> Option<String> { | ||
| let title = info.title.as_deref().unwrap_or("(untitled quest)"); | ||
| let progress = match info.total_phases { |
Comment on lines
+149
to
+150
| - **Edit / skip UX per phase.** Phase 7 auto-advances every phase. `set_phase_sub_prompt`, `skip_phase`, `compose_sub_prompt`, `QuestHandoff`, and `QuestPhaseStatus::Skipped` are still in the crate surface but reachable only through tests; their `#[allow(dead_code)]` notes call this out. | ||
| - **Full re-attach state rebuild.** `/attach <anchor>` currently prints a one-line note ("phase N/M, in progress / complete"). Replaying `cast.quest.*` events to reconstruct a `Quest` and re-render the next handoff card is the long pole and a separate phase. |
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
/quest <goal>(and four natural-language triggers) end to end: parser → plan card → shell loop → per-phase gate → dispatch → advance.cast.quest.{started, phase_started, phase_completed, advanced, completed}events to the anchor (phase-0) session so a future re-attach can reconstruct quest state from the event log.coven attach <anchor>surfaces a one-lineQuest anchor — \<title>` (phase N/M, in progress / complete)` note alongside the existing cast.summary note.docs/design/cast-quest-flow.md§7 (boxes now ticked); adds a new §8 listing what is deliberately deferred.Scope decisions (locked at start of phase)
cast.quest.*storageGate results
cargo fmt --all -- --check— cleancargo clippy -p coven-cli --tests --no-deps -- -D warnings— cleancargo test -p coven-cli— 325 unit + 4 smoke, 0 failures (12 new tests this phase: 4 intent, 2 plan, 6 attach)Deferred to a later phase
Documented in
docs/design/cast-quest-flow.md§8:set_phase_sub_prompt,skip_phase,compose_sub_prompt,QuestPhaseStatus::Skippedremain in the crate surface but reachable only via tests./attach <anchor>prints a one-liner; replaying events to reconstruct a Quest and re-render the next handoff card is the long pole.Test plan
🤖 Generated with Claude Code