Skip to content

Release 0.8.67 — Fleet/Workflow usability, goal-timer fix, whaleflow→workflow rename#4047

Merged
Hmbown merged 91 commits into
mainfrom
work/v0.9.0-cutover
Jul 6, 2026
Merged

Release 0.8.67 — Fleet/Workflow usability, goal-timer fix, whaleflow→workflow rename#4047
Hmbown merged 91 commits into
mainfrom
work/v0.9.0-cutover

Conversation

@Hmbown

@Hmbown Hmbown commented Jul 6, 2026

Copy link
Copy Markdown
Owner

CodeWhale 0.8.67 — Fleet/Workflow usability release

Merges the 0.8.67 release candidate to main. Branch name (work/v0.9.0-cutover) is legacy; the content targets 0.8.67 (the 0.9.0 Operation/Operate-mode architecture remains deferred).

Fast-forward clean against main (candidate is 78 ahead, 0 behind at open time).

Highlights

  • Fleet setup that's usable — constitution-first /setup wizard, /fleet roster, provider-agnostic rosters/loadouts, operator built-in roster member.
  • Workflow substrate — typed IR + QuickJS runtime + WorkflowTool; the product-ready /workflow run command + TUI run view stay tracked for 0.8.68 (v0.8.68 Workflow: wire the model-facing workflow tool and run driver #2974, v0.8.68 Workflow: product-readiness tracker #4038).
  • Goal elapsed-timer fix — completed/blocked goals freeze the sidebar "completed in {elapsed}" readout instead of ticking forever; /goal resume clears the freeze.
  • whaleflowworkflow rename across the workspace (crates, identifiers, JS bridge symbols, env vars, docs). Historical changelog/retro ledgers keep the old name as record.
  • Session resume restores model provider; secret-safe provider/setup behavior; typed workflow-run / status receipts.
  • clippy::large_enum_variant fix in fleet/ledger.rs (pre-existing lint that blocked the CI clippy gate; Box<T> is serde-transparent so the on-disk ledger format is unchanged).

Verification (local, this candidate)

  • cargo build --release -p codewhale-tui — OK
  • cargo test across fleet / workflow / setup / goal suites — green
  • scripts/v0867-setup-qa.sh — 33 passed, 0 failed (confirms plan_limit_probed == false)
  • CI clippy gate (--all-features with the repo's -A set) — passes
  • cargo fmt --all --check, check-versions.sh (workspace = npm = 0.8.67), verify-workspace-version.sh 0.8.67 — OK

Explicitly NOT in this PR (still gated)

  • No git tag v0.8.67, no crates.io/npm publish, no GitHub Release — those remain separate manual steps after this merge.
  • The fleet model-selection rework (operator + concrete per-slot models, retiring model classes) is a separate branch (work/fleet-model-selection, 0.8.68 scope) and is not included here.

Review follow-up (pushed 2026-07-06)

  • CI unblocked: restored docs/rfcs/1364-hooks-lifecycle.md (deleted by the wip checkpoint; web check-docs requires it) and fixed the two clippy errors (collapsible_match in compaction, unnecessary_sort_by in the fleet roster).
  • All four Claude-review findings resolved: removed panic = "abort" from the release profile (restores catch_unwind panic supervision); added codewhale-build-support and codewhale-workflow-js to the crate publish order; finish_scheduled_run now persists the run record even when its automation was deleted mid-enqueue; paused goals keep their sidebar timer frozen across usage snapshots (regression tests added for both).
  • /model selection is now the operator: fleet workers with no task/profile model pin inherit the active session route instead of a hardcoded auto (route receipts record model_source = "run.model"); headless callers with no session keep the legacy default.
  • New /workflow command (aliases /workflows, /wf): context-dependent orchestration opt-in — bare /workflow orchestrates the current work (the model derives the objective from the conversation), /workflow <objective> narrows it, status/cancel relay typed run receipts.
  • Bare /goal now declares a goal from the conversation context via create_goal instead of printing usage.
  • Changelog updated for all of the above.

Note: the four workflow-tool integration tests can flake under full-suite parallelism (child failed vs completed); verified pre-existing at 4a92b44dd before this follow-up — filtered runs pass deterministically. Tracked for a deflake pass.

🤖 Generated with Claude Code

Hmbown and others added 30 commits July 2, 2026 15:08
…ning' into codex/0867-finish-local-candidate
…ements' into codex/0867-finish-local-candidate
Safety checkpoint of multi-agent cutover work (fleet roster core,
whaleflow-js runtime, /fleet roster view, audit fix lanes). Tree may
not compile: the agent-tool profile edit in tools/subagent/mod.rs was
interrupted mid-edit (credit exhaustion). Checkpoint precedes repair.
Revert two interrupted files (shell.rs, todo.rs) to last-good; workspace
compiles, whaleflow-js 36 tests pass, fleet roster 7 pass. Half-finished
shell ring-buffer and todo blocked_on features deferred for clean redo.
The per-worker token-budget tests (e.g.
`per_worker_token_budget_does_not_double_count_scope_accounting`)
intermittently timed out at their 5s `tokio::time::timeout` when the
full `subagent::` suite ran in parallel.

Root cause: `rate_limit_pause_blocks_subagent_spawn` opens a 30s
process-wide rate-limit window (`retry_status::note_rate_limit`) and
clears it milliseconds later. But `client::send_with_retry` read the
remaining window once and committed to `tokio::time::sleep` for the
FULL duration without re-checking. A budget worker in a concurrent test
whose model request entered that loop during the open window was stranded
for up to 30s — long past the test's 5s deadline. The pause is global
state, so one test's window leaked into unrelated tests.

Fix:
- Sleep the rate-limit pause in bounded 250ms slices so an already-cleared
  (or shortened) window releases waiting requests promptly, instead of
  honoring a stale full-window deadline.
- Add `ClearRateLimitOnDrop` RAII guard to `rate_limit_pause_blocks_subagent_spawn`
  so a panicking assertion can't leak the 30s window into the rest of the suite.
- Add `worker_is_not_stranded_by_transient_global_rate_limit_window`
  regression test reproducing the strand-and-release sequence.

Verified with 13 consecutive clean runs of the full parallel
`subagent::` suite (192 tests) plus the `retry_status` and `client::`
suites.
config_override_requires_explicit_opt_in drains the process-global
PROMPT_OVERRIDE_NOTICES queue but didn't hold test_support::lock_test_env(),
while its sibling (tui::ui::tests::prompt_override_notice_surfaces_in_transcript_and_toast)
does. With only one side locking, the two could interleave their
take_prompt_override_notices() calls under the multi-threaded test binary
and intermittently see an empty vec. Take the lock so both serialize.

Signed-off-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
(cherry picked from commit d3c2d933dca4593d5808341a328f89e4c48d7aab)
discover_from_directories already warns when two roots yield the same
normalized command name, but discover_recursive pushed every SKILL.md
unconditionally. With aggressive slugging, two sibling dirs under one root
(e.g. `My Skill/` and `my_skill/` → `my-skill`) could both land in the
registry with identical names; get() only ever resolves the first, leaving
the other silently unreachable. Mirror the cross-root behavior: keep the
first and emit a "shadowed by" warning. Adds a same-root regression test.

Signed-off-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
(cherry picked from commit baf0ec414a2ba9ff2982f2e7d48014cb0cdb0806)
Trusting a workspace is a security boundary. The Trust step bound Enter —
the "advance" key on every other onboarding screen — to complete trust,
so a reflexive Enter granted execution trust, and the behavior was
inconsistent with the footer (which lists only 1/Y to trust, 2/N to exit).
Enter now surfaces a guidance hint pointing at the explicit keys instead of
trusting; it is neither an accidental-trust path nor a silent dead key. The
explicit Y/1 and N/2 handlers are unchanged.

Signed-off-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
(cherry picked from commit b12a7374b9778b19aa3d6dc0043840a25bb4ee97)
The field doc claimed None "in tests / when no home directory is available,"
but discovery always sets the store and default_user_plugins_dir falls back
to /tmp. Reword to the real invariant: set by discovery; None only when a
registry is built without a store (e.g. a direct PluginRegistry::new()).

Signed-off-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
(cherry picked from commit a8e5a01813cea677b58279963b95c6183447e197)
Adds LongCat (Meituan's LongCat API platform) as a built-in OpenAI-compatible
Chat Completions provider, mirroring the existing Sakana provider wiring:

- ProviderKind::LongCat / ApiProvider::LongCat with aliases
  (long-cat, meituan-longcat, meituan) and LONGCAT_API_KEY / LONGCAT_BASE_URL /
  LONGCAT_MODEL env overrides.
- Defaults: model LongCat-2.0, base https://api.longcat.chat/openai/v1,
  credential page https://longcat.chat/platform.
- Registered in the provider registry + ModelRegistry (LongCat-2.0), and wired
  through every provider match (config accessors, defaults, reasoning-effort,
  base-url persistence, env overrides, merge, header badge).
- API-key based like Sakana — deliberately NOT routed through the OpenAI Codex
  OAuth "missing credentials" message.
- Docs (PROVIDERS.md) and config.example.toml updated.

Verified: cargo check --workspace --all-features, the CI clippy gate
(-D warnings), codewhale-config tests (347), and tui provider tests (366) all
pass.

Signed-off-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
(cherry picked from commit 242ef6d)
Adding the LongCat ApiProvider variant tripped the web `check:facts` gate
("unmapped ApiProvider variant(s): LongCat"). Add LongCat to
PROVIDER_LABEL_MAP (web/scripts/facts-lib.mjs) and labelMap
(web/lib/facts-drift.ts), mirroring the Sakana entry, and regenerate
web/lib/facts.generated.ts. `node scripts/check-facts.mjs` now passes
(providers=30, version=0.8.67).

Signed-off-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
(cherry picked from commit a54a8dec2e74e2293f95d8904efba3464fade119)
Resolve the private 0.9.0 cutover base against origin/main and repair stale merged expectations.

Receipts: cargo build -p codewhale-tui --locked; cargo test -p codewhale-tui --locked.
Hmbown added 9 commits July 5, 2026 19:44
Two call sites used let config instead of let mut config, causing
borrow errors after apply_loaded_session was changed to require &mut Config.
The operator slot was added to the built-in roster. Update three tests
that assert exact member order to include the new entry.
The operator slot shifted member indices: scout moved from [1] to [2],
synthesizer from [5] to [6]. Update the slot/loadout assertions in
built_in_party_is_complete_with_floor_permissions to match.
Goal mode text in the To-do sidebar is a feature, not a leak. Keep the
full objective visible but prefix it with 'Goal:' so the compact row is
clearly identified as a goal-mode objective rather than raw prompt text.
Also update hover text for consistency. Remove the 50-char truncation and
SIDEBAR_GOAL_MAX_CHARS constant.
Completed and blocked goals now freeze the sidebar elapsed timer instead
of ticking forever. Added finished_at to HuntState, propagated through
apply_goal_snapshot_to_app / close_hunt / resume_hunt / clear and both
sidebar render paths; the engine snapshot() clamps elapsed at finished_at.
Runaway/continuation guards preserved (they short-circuit on !is_active()).
Adds regression tests for freeze-on-complete, freeze-on-close, and
resume-clears-frozen-timer.
Completes the whaleflow -> workflow rename workspace-wide:
- crates/whaleflow{,-js} -> crates/workflow{,-js}; codewhale-whaleflow -> codewhale-workflow
- WhaleflowVm -> WorkflowVm, WhaleflowJsError -> WorkflowJsError,
  WHALEFLOW_LIFETIME_CAP -> WORKFLOW_LIFETIME_CAP, codewhale_whaleflow(_js) paths
- __whaleflow_* -> __workflow_* JS bridge symbols; CODEWHALE_WHALEFLOW_JS_* ->
  CODEWHALE_WORKFLOW_JS_* env vars; whaleflow-js-vm thread; fleet_whaleflow_ledger
  -> fleet_workflow_ledger durable owner tag
- CI: output key, bash var, path filter crates/workflow/*, job id
  workflow-rlm-cache, cargo -p codewhale-workflow
- docs renamed WHALEFLOW_AUTHORING -> WORKFLOW_AUTHORING and
  rfcs/WHALEFLOW_EXTERNAL_MEMORY -> WORKFLOW_EXTERNAL_MEMORY; inbound links,
  active doc prose, and web/lib/facts.generated.ts updated
- README zh-CN/ja-JP/vi 'current lane' section reframed to 0.8.67
- Cargo.lock updated. CHANGELOG and retro ledgers left as historical record.
Adds the 0.8.67 changelog entries for the goal elapsed-timer freeze fix
(Fixed) and the whaleflow -> workflow workspace rename (Changed). Fixes a
stale 'v0.9.0 Track' cross-reference in README.vi.md to match the reframed
'Track tiep theo' heading (other locales already point to the generic
next-track heading).
…variant)

The RunCreated(FleetRun) and ReceiptRecorded(FleetReceipt) variants dwarfed
the rest (688/256 bytes), failing the CI clippy gate. Box both payloads;
Box<T> is serde-transparent so the on-disk JSONL ledger format is unchanged.
This was a pre-existing lint that blocked CI, unrelated to model selection.
Copilot AI review requested due to automatic review settings July 6, 2026 15:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@claude

claude Bot commented Jul 6, 2026

Copy link
Copy Markdown

Claude finished @Hmbown's task in 11m 48s —— View job


Review complete

Todo list

  • Gather context (PR description, diff stat, CLAUDE.md/AGENTS.md guidance)
  • Review correctness bugs (panics, unwrap/expect, error handling, exhaustive matches)
  • Review provider/model/route safety invariant (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608)
  • Review reuse/simplification opportunities
  • Review test coverage for new behavior
  • Review security (secrets, exec policy, input validation)
  • Post final review summary

This is a large PR (~187 files, +19,935/-5,636 across the diff vs origin/main), so I split the review across core-engine/goal, Fleet/roster, config/provider/secrets, build-system, and the remaining TUI/rename surfaces, then verified the highest-severity claims directly against the code.

Correctness — confirmed issues

  1. Cargo.toml:64-69 — new [profile.release] sets panic = "abort", which breaks the codebase's panic-supervision design. This block (lto, strip, panic = "abort", codegen-units = 1) is entirely new vs main. The codebase relies on std::panic::catch_unwind/spawn_supervised in 13+ files (utils.rs, engine.rs, runtime_threads.rs, mcp/sse.rs, tools/file.rs, tools/web_run.rs, automation_manager.rs, …) specifically so a panic in one tool call/task doesn't take down the whole TUI session. With panic = "abort", unwinding is disabled process-wide, so catch_unwind can no longer catch anything in release builds — the exact build users run. A panic in, say, PDF/HTML extraction now aborts the entire session instead of failing that one tool call gracefully. This looks like an accidental side effect of bundling perf/size tuning (alongside the new mimalloc dependency) rather than an intentional design change — worth confirming with Hunter before this ships. Fix this →

  2. scripts/release/crates.sh — the new codewhale-build-support crate is missing from the publish order. crates/cli/Cargo.toml:43 and crates/tui/Cargo.toml:88 both add codewhale-build-support = { path = "../build-support", version = "0.8.67" } as a [build-dependencies] entry, but release_crates in scripts/release/crates.sh doesn't include it anywhere in the list. cargo publish requires path deps with a version to already exist on the registry, so publishing codewhale-tui/codewhale-cli from this script will fail at release time with a missing-dependency error. Fix this →

  3. crates/tui/src/automation_manager.rs:696-864 (finish_scheduled_run / scheduler_tick_shared) — a scheduled run's task can become an untracked orphan if its automation is deleted mid-enqueue. The tick does: (a) enqueue the task via enqueue_run_task without holding the manager lock, which already calls task_manager.add_task(...), creating a real running task; then (b) reacquire the lock and call finish_scheduled_run, which does self.save_run(run) only if get_automation(&run.automation_id) still succeeds (line 697-700). If the automation was deleted during step (a)'s await, finish_scheduled_run returns Ok(()) early and the run record is never persisted — so collect_pending_runs (which walks list_automations()) can never find or reconcile it. The task runs to completion fully untracked. Compare with run_now_with, which unconditionally calls manager.save_run(&run)? before checking whether the automation still exists — that's the correct pattern here too. This looks like a real regression introduced by the finer-grained locking refactor (previously the lock was held across the whole tick, so this race couldn't occur). Fix this →

  4. Goal elapsed-timer freeze/unfreeze conflict between commands/groups/project/goal.rs:close_hunt and tui/ui.rs:apply_goal_snapshot_to_app for paused goals. close_hunt (goal.rs:144-151) freezes app.hunt.finished_at for all verdicts including Wounded/Paused, with a comment explicitly noting paused goals should record a frozen pause instant. But apply_goal_snapshot_to_app (ui.rs:7023-7033) defines "terminal" as Hunted | Escaped only (i.e., Complete/Blocked) and explicitly clears finished_at back to None for any other status, including Paused. This path is reachable: close_hunt's AppAction::SetGoalStatushandle_set_goal_status (engine.rs:2225-2260) calls emit_goal_updated() at the end, and separately every turn completion calls record_goal_usage_for_turn (which unconditionally records usage regardless of pause status) followed by another emit_goal_updated() (engine.rs:2670-2674). So if the user pauses a goal (freezing the timer) and then sends any further manual message, the next GoalUpdated snapshot has an updated time_used_seconds/tokens_used, changed becomes true in apply_goal_snapshot_to_app, and since Wounded isn't "terminal" there, finished_at gets reset to None — silently un-freezing the sidebar timer for a goal the user just paused. This reintroduces a variant of the exact bug this PR sets out to fix. Worth reconciling the two "terminal" definitions (or having apply_goal_snapshot_to_app also treat Wounded as freeze-but-not-terminal instead of clearing finished_at). Fix this →

Provider/model/route safety invariant (EPIC #2608)

Checked closely across the Fleet roster/loadout feature (fleet/roster.rs, fleet/manager.rs, fleet/worker_runtime.rs, protocol/fleet.rs), the config/provider/session-resume paths (config/provider.rs, config_persistence.rs, runtime_api/sessions.rs, session_manager.rs), and the client/session code (client.rs, client/chat.rs). No violations found. Model/provider selection is driven by explicit stored fields throughout (e.g. worker_runtime.rs has an explicit comment: "Provider authority belongs to route resolution, so we do not infer a provider here"), and "session resume restores model provider" is implemented by threading an explicit stored model_provider string through save/load rather than parsing/prefix-matching a model id. Good discipline given how easy this class of bug is to reintroduce.

Reuse / simplification (lower severity)

  • crates/tui/src/fleet/manager.rsrecord_task_outcome calls self.ledger.rebuild_state() once, then resolve_task_effective_permissions/resolve_task_route each independently call rebuild_state()/agent_roster() again for data already computed a few lines earlier. Not a bug, but for large ledgers this makes task-completion latency scale worse than necessary — worth threading the already-computed state/roster through instead of recomputing.
  • crates/tui/src/core/engine/streaming.rs and core/tool_parser.rs maintain independent marker-list constants for the same fake-tool-call-wrapper detection (TOOL_CALL_START_MARKERS/_PAIRS vs FAKE_TOOL_CALL_MARKERS + a hand-written regex). Adding a new marker to one but not the other would make the live-stream filter and the post-hoc parser silently disagree.
  • crates/build-support/src/lib.rs is a clean, genuine dedup of the old crates/cli/build.rs/crates/tui/build.rs (verified byte-for-byte equivalent logic, just parameterized) — no leftover duplication or dead code in either trimmed build.rs.
  • crates/tui/src/config/subagent_limits.rs bumps DEFAULT_MAX_SUBAGENTS 20→64, MAX_SUBAGENTS 20→128, MAX_SUBAGENT_ADMISSION 200→1024. This is commented as an intentional "temporary high-throughput default," but it's a real cost/rate-limit-affecting behavior change not mentioned in the PR's highlights — worth a one-line release-note callout so users on metered plans aren't surprised.

Tests

  • New roster.rs, authority.rs, and config_persistence.rs/config/tests.rs tests are substantive (the config persistence golden-file tests in particular encode real prior bugs as regression fixtures, not superficial coverage).
  • Gaps: no test asserts the built-in operator roster role maps to full-capability routing (worker_runtime.rs fallback currently gives it the right answer by accident, not by an explicit match arm); runtime_api/tests.rs only updates struct literals for the new model_provider field without a test that actually round-trips it through save/load; fleet/ledger.rs's Box<T> change has no literal legacy-JSON fixture test (unlike protocol/fleet.rs, which does this correctly for FleetResolvedRoute).

Security

No issues found. Secret handling looks improved (save_api_key_for now rejects persisting OAuth tokens as plaintext API keys), atomic config writes use 0600 perms, and .cargo/audit.toml/Cargo.lock changes are clean upgrades with no downgrades or new suppressions.


Given the scale of this PR, I'd treat findings #1#4 above as worth resolving or explicitly acknowledging before merge#1 and #2 affect the release build/publish process directly, and #3/#4 are real regressions in behavior this PR itself is trying to fix or ship.

CodeWhale Agent added 10 commits July 6, 2026 10:14
docs/rfcs/1364-hooks-lifecycle.md exists on main but was deleted by the
0.8.67 wip checkpoint (000c041). web/lib/docs-map.ts still points its
hooks topic at the file, so the Web Frontend check-docs gate fails on the
release PR. Restore the file verbatim from origin/main.
The CI clippy gate (-D warnings) fails on two new sites: the retained
thinking-block truncation collapses into a match-arm guard, and the fleet
roster extras sort becomes sort_by_key. No behavior change.
The release profile added panic=abort alongside lto/strip/codegen-units,
but the TUI's panic supervision (catch_unwind/spawn_supervised across
utils, engine, runtime_threads, tools, automation_manager, ...) depends on
unwinding: with abort, one panicking tool call takes down the whole
session in exactly the builds users run. Keep the size/perf flags, drop
the abort.
codewhale-tui and codewhale-cli depend on codewhale-build-support as a
path+version build-dependency, and codewhale-tui depends on
codewhale-workflow-js the same way. Neither crate was in release_crates,
so cargo publish would fail on the dependents at release time. Add both
in dependency order.
…queue

scheduler_tick_shared enqueues the run's task without holding the manager
lock, then calls finish_scheduled_run to persist. If the automation was
deleted during the enqueue await, finish_scheduled_run returned Ok(())
without saving the run record, leaving the already-created task running
untracked (collect_pending_runs walks automations and could never find
it). Save the run unconditionally, matching run_now_with; only the
schedule advance is skipped when the automation is gone.
apply_goal_snapshot_to_app cleared finished_at for every non-terminal
verdict, including Wounded (paused). Usage keeps accruing while paused
(record_goal_usage_for_turn runs each turn), so the next GoalUpdated
snapshot silently re-armed a timer the user just paused — conflicting
with close_hunt, which records the pause instant. Freeze on
Hunted/Escaped/Wounded; only an explicit resume to Hunting re-arms.
The /fleet roster UI already pins the operator row to the live session
route, but dispatch passed a hardcoded "auto" run model, so workers
without a task/profile model pin never actually inherited the session
selection. Adopt the active route as the run-level model:

- FleetManager::with_session_model() stores the session route (empty and
  "auto" ignored); run_model() feeds worker-spec resolution at both
  dispatch call sites instead of "auto"
- resolve_fleet_route() takes the session model so route receipts agree
  with dispatch and record model_source="run.model" when it applies
- the runtime API and headless fleet CLI adopt config.default_model()
- task.model and agent_profile.model pins still win; headless callers
  with no session keep the legacy auto sentinel

The model picked in /model is now the model that runs the fleet, which
is the seam Operate mode builds on.
The workflow engine and model-facing tool are wired, but a user had no
way to invoke them: no /workflow command existed and discoverability was
zero. Add /workflow (aliases /workflows, /wf) as the user-side opt-in:

- bare /workflow authorizes orchestrating the CURRENT work — the model
  synthesizes the objective from the conversation context instead of
  demanding a restatement (the same context-dependence /goal mode uses)
- /workflow <objective> narrows the run to an explicit objective
- /workflow status [run_id] and /workflow cancel <run_id> relay typed
  run receipts through the workflow tool without starting new runs
- agent-mode prompt documents the opt-in contract: the invocation is
  the authorization, fan-out scales to the ask, pipeline over barriers,
  responseSchema for structured child output, receipts close the loop

CmdWorkflowDescription is localized across en/es-419/ja/pt-BR/vi/zh-Hans
(zh-Hant falls back, matching the other command descriptions).
/goal with no argument and no active goal used to print usage. Now it is
context-dependent, like bare /workflow: the model synthesizes the
objective from the conversation and sets it with the create_goal tool,
which the end-of-turn GoalUpdated snapshot syncs into the sidebar. With
an active goal, bare /goal remains the status readout; explicit
/goal <objective> is unchanged.
…re /goal

Adds 0.8.67 entries for the session-model-as-operator fleet inheritance,
the /workflow orchestration opt-in command, and context-dependent bare
/goal, plus Fixed entries for the paused-goal timer freeze, the
scheduled-automation orphan race, and the release-profile panic=abort
removal. Regenerates the embedded crates/tui CHANGELOG slice.
@claude

claude Bot commented Jul 6, 2026

Copy link
Copy Markdown

Claude finished @Hmbown's task in 3m 7s —— View job


Re-review after new commits (synchronize event)

Todo list

Prior findings — all four resolved

  1. panic = "abort" (Cargo.toml) — removed. Cargo.toml:66-70 now explicitly omits it with a comment explaining why (catch_unwind/spawn_supervised panic supervision needs unwinding). Confirmed.
  2. codewhale-build-support missing from scripts/release/crates.sh — added; it's now first in release_crates (line 5), ahead of codewhale-tui/codewhale-cli. Confirmed.
  3. Orphaned scheduled-run task in automation_manager.rs — fixed. finish_scheduled_run (line 698-707) now calls self.save_run(run)? unconditionally before the get_automation early-return, so a run whose automation was deleted mid-enqueue still gets persisted instead of leaving its task untracked. A dedicated regression test (finish_scheduled_run_persists_run_when_automation_deleted_mid_enqueue) covers exactly this race. Confirmed.
  4. Goal timer freeze/unfreeze race for paused goals — fixed in 9c5bc32. apply_goal_snapshot_to_app (crates/tui/src/tui/ui.rs) now matches on Hunted | Escaped | Wounded to freeze finished_at, and only an explicit resume to Hunting clears it — reconciling with close_hunt's freeze-on-pause behavior. New test apply_goal_snapshot_keeps_paused_timer_frozen_across_usage_updates directly exercises the pause → usage-update → still-frozen → resume sequence described in the original finding. Confirmed.

New commits since last review

Three feature commits landed (e8453bb, df56c4d, 9487a69), plus a changelog update (027cc7f):

  • e8453bb — fleet session-model-as-operator inheritance. Reviewed closely against the EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608 invariant since it touches model routing directly. FleetManager::with_session_model() stores an explicit route string (ignoring empty/"auto"), fed from config.default_model() at both construction sites (main.rs's headless run_fleet_command, runtime_api.rs's open_fleet_manager, which backs all interactive /fleet dispatch). resolve_fleet_route/fleet_route_model_selector_with_source treat this purely as an explicit fallback value — no prefix parsing or inference — and task/profile model pins still take priority over it (verified via resolve_fleet_route_uses_session_model_as_run_fallback, which asserts agent_profile.model beats the session route). No violation found; this is exactly the "explicit stored field" pattern the codebase already uses elsewhere.
  • df56c4d/workflow opt-in command. Straightforward, mirrors the existing /goal context-dependent pattern. status/cancel parsing and tests look correct; no new provider/model surface touched.
  • 9487a69 — bare /goal context-dependent declaration. Small, well-tested change; goal_usage() remains referenced elsewhere (not dead code) since the invalid-status-arg path still uses it (goal.rs:50).
  • 027cc7f — changelog-only, no code risk.

No new correctness, safety, or security issues found in this round. The previously-noted lower-severity items (ledger rebuild_state() recomputation in fleet/manager.rs, duplicate fake-tool-call marker lists in streaming.rs/tool_parser.rs, the subagent_limits.rs default bump needing a release-note callout) are unchanged — still non-blocking, not re-flagging in detail.

Note: I was unable to run cargo check/cargo test in this sandbox (network/compute-heavy commands required approval that wasn't available in this run); the assessment above is from direct code and test inspection. The PR description's own verification log (cargo build --release, cargo test, clippy gate, cargo fmt --check) covers this.

Summary

All four blocking findings from the previous review are resolved with targeted fixes and regression tests. The three new feature commits are clean, respect the provider/model routing invariant, and are adequately tested. I have no further blocking concerns for this PR.

The 20->64 max_subagents default (admission 200->1024) is a real
cost/rate-limit-affecting change for metered plans; record it under
Changed with the config override, per review feedback.
@claude

claude Bot commented Jul 6, 2026

Copy link
Copy Markdown

Claude finished @Hmbown's task in 25s —— View job


Re-review after new commit (synchronize event)

Todo list

  • Gather context (diff since last review at 027cc7f)
  • Check what changed in the new commit (e106874)
  • Confirm no code/behavior changes requiring re-review of correctness, provider/route safety, or tests
  • Post final review summary

What changed

Only one new commit since the last review (027cc7fe106874), and it's changelog-only:

CHANGELOG.md            | 4 ++++
crates/tui/CHANGELOG.md | 4 ++++
2 files changed, 8 insertions(+)

It adds the release-note callout for the max_subagents default bump (20→64, ceiling 128, admission cap 200→1024) that I flagged as a non-blocking gap in the first review round ("worth a one-line release-note callout so users on metered plans aren't surprised"). No source files changed.

Summary

No new code to review — this commit only adds documentation. All four previously-identified blocking findings (panic = "abort", missing codewhale-build-support in the release publish order, the orphaned scheduled-run task race, and the goal-timer freeze/unfreeze race) remain resolved as confirmed in the prior review pass, and the lower-severity subagent-limits callout is now also addressed. I have no further concerns for this PR.

…dead vocabulary

Role model cleanup per maintainer direction:

- Operator vs manager hierarchy made explicit: the operator (the
  session's /model selection) runs the operation and assigns managers to
  workflows; a manager is the middle manager of exactly one workflow.
  Built-in descriptions rewritten and both roles now carry real role
  instructions that flow into worker prompts on the Fleet task-spec path
  and the agent/workflow profile spawn path (the wiring existed; built-ins
  never used it). fleet_role_to_agent_type maps 'operator' to General by
  an explicit arm instead of the unknown-role fall-through, with a test.
- Reviewer is adversarial: the built-in member assumes the change is
  broken and tries to refute it (instructions shipped), and
  REVIEW_AGENT_INTRO adopts the same framing while staying read-only and
  severity-scored.
- Removed vocabulary that never routed differently: FleetSlot::ToolHeavy
  and the strong/balanced/deep-reasoning/code/review/tool-heavy loadout
  tiers. inherit (operator's route) and fast (provider's faster class)
  remain. Retired names in existing configs parse as Custom with the
  identical auto routing, covered by tests. The /fleet setup model-class
  step now offers only the two real choices.
- Tutorial example and changelog updated.
@claude

claude Bot commented Jul 6, 2026

Copy link
Copy Markdown

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


Re-review after new commit (synchronize event)

Todo list

  • Gather context (diff since last review at e106874)
  • Check what changed in the new commit (c15e46e)
  • Confirm no correctness/provider-safety regressions in the new commit
  • Post final review summary

What changed

One new commit since the last review (e106874c15e46e), a fleet role/vocabulary refactor (not docs-only, unlike the prior two rounds):

CHANGELOG.md                            | 15 +++++++++++
crates/config/src/lib.rs                | 32 ++++++++---------------
crates/config/src/tests.rs              | 24 +++++++++++++++---
crates/tui/CHANGELOG.md                 | 15 +++++++++++
crates/tui/src/fleet/executor.rs        |  2 +-
crates/tui/src/fleet/profile.rs         | 10 ++++++--
crates/tui/src/fleet/roster.rs          | 45 +++++++++++++++++++++++++--------
crates/tui/src/fleet/worker_runtime.rs  | 41 +++++++++++++++++++-----------
crates/tui/src/tools/subagent/mod.rs    |  6 ++---
crates/tui/src/tools/subagent/tests.rs  |  2 +-
crates/tui/src/tui/views/fleet_setup.rs | 37 ++++++---------------------
docs/FLEET_WORKFLOW_TUTORIAL.md         |  2 +-

Summary of the change: makes the operator/manager coordination hierarchy explicit (both now carry real role.instructions that flow into worker prompts on the Fleet task-spec and agent/workflow profile spawn paths), makes the built-in reviewer explicitly adversarial, and removes FleetSlot::ToolHeavy plus the strong/balanced/deep-reasoning/code/review/tool-heavy FleetLoadout tiers that never routed differently from Auto.

Verified:

  • No compile breaks from the removed variants. Grepped the full tree for ToolHeavy / FleetLoadout::Strong|Balanced|DeepReasoning|Code|Review — no remaining references. The one non-exhaustive-looking match at crates/tui/src/tools/subagent/mod.rs:5975-5981 has a wildcard _ => ModelRoute::Inherit arm, so removing variants doesn't break it.
  • Backward compatibility for existing configs is real, not just claimed. FleetLoadout::from_name/FleetSlot::from_str route retired names ("strong", "tool-heavy", etc.) into Custom(_), which fleet_model_route_for_loadout still maps to ModelRoute::Auto — identical behavior to before removal. Covered by updated tests in crates/config/src/tests.rs and crates/tui/src/fleet/profile.rs.
  • fleet_role_to_agent_type now maps "operator" to SubAgentType::General via an explicit match arm (worker_runtime.rs:466) instead of falling through the unknown-role default, with a dedicated test (fleet_role_operator_maps_to_general_explicitly). Synthesizer/summarizer still correctly stays on the read-only Plan surface — no regression there.
  • Provider/model routing invariant (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608) unaffected. This commit only touches loadout class routing (Auto/Faster/Inherit), not provider/model string inference — no new prefix-parsing or freeform-text-based provider switching introduced.
  • fleet_setup.rs's MODEL_CLASSES shrink from 6 → 2 entries is safe. All indexing into the array (self.model_idx.min(MODEL_CLASSES.len() - 1), Step::Model => MODEL_CLASSES.len()) is done via .len(), not hardcoded constants, so the wizard adapts correctly to the smaller list.
  • Changelog entries (CHANGELOG.md, crates/tui/CHANGELOG.md) and the tutorial doc (docs/FLEET_WORKFLOW_TUTORIAL.md, "model_class": "balanced""inherit") accurately reflect the code change.

No correctness, provider-safety, or test-coverage issues found in this commit. All four blocking findings from the original review round (panic = "abort", missing codewhale-build-support in the release publish order, the orphaned scheduled-run task race, and the goal-timer freeze/unfreeze race) remain resolved as confirmed previously. I have no further concerns for this PR.

@Hmbown Hmbown merged commit c15e46e into main Jul 6, 2026
19 of 21 checks passed
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.

2 participants