Commit Graph

8686 Commits

Author SHA1 Message Date
Hunter Bown
84b3dd694f feat(skills): FEAT-022 skills command shapes and retained-host validation (#5829)
Harvested from PR #5825 by @aboimpinto
2026-09-02 17:16:32 +00:00
Hunter Bown
ae1edd1b96 fix(providers): three user-visible DeepSeek-exclusivity gates (#5588) (#5832)
- Remaining-credit lookup was DeepSeek-only (and /balance was a stub
  even there). One fetch_balance seam now covers DeepSeek /user/balance,
  OpenRouter /credits, and SiliconFlow /user/info. /balance runs a live
  fetch; the opt-in footer chip renders the parked value.
- Ghost-text prompt suggestions gated on the DeepSeek enum even though
  the generator speaks ordinary Chat Completions. Gate on wire protocol
  so OpenAI/OpenRouter/custom/Z.ai routes can launch with their own
  credentials; Messages and Responses stay out.
- MCP deepseek/deepseek-reply omitted-model calls hardcoded
  deepseek-v4-pro. They now follow Config::default_model() for the
  active provider.

Co-authored-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: Grok 4.6 <grok@x.ai>
2026-09-02 17:15:37 +00:00
Hunter Bown
329960fcbf feat: Codewhale 0.9.12 shell, brand, fleet, and Operate (mega) (#5826)
* feat(fleet): your fleet is the models you added, and it comes first

Design MODEL-ROUTING-CATALOG-20260901 §10, slice F1. A fleet model is a Pod
member: the selected Pod file's operator route plus every member row that
pins an exact provider + model; the roles a model fills are the member rows
that pin it. No second store.

- crate::fleet::members: fleet_models / add_fleet_model / remove_fleet_model
  / toggle_fleet_model + change_receipt; Config::fleet_members(workspace) is
  the read seam for the operator-awareness slice (F2).
- /pod models | add <provider> <model> [role…] | remove <provider> <model>
  (also via the /fleet alias). A model the configured provider does not
  serve is rejected; the first add creates and selects a user-global Pod
  named 'My fleet'.
- /model picker: ⇧F adds or removes the row's exact route; fleet models
  lead the list labelled 'fleet · <roles>', ahead of ⇧P pins and providers.
- /models prints the fleet before the provider list ('Your fleet is the
  session model only' when empty).
- PickerActionFleet message in all 15 locales; docs/FLEET.md 'Your fleet
  as models'.

Tests: scripts/dev-test.sh tui fleet::members groups::core::fleet
model_picker format_helpers — Summary 37 tests run: 37 passed, 11834
skipped.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* fix(fleet): pass slugify by name (clippy redundant_closure)

cargo clippy -p codewhale-tui --all-targets -- -D warnings -A clippy::too_many_arguments -A clippy::uninlined_format_args -A clippy::unnecessary_map_or: no findings.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* fix(tui): review fixes for fleet toggle and /pod add provider validation\n\n- Reject unconfigured provider ids in "/pod add" before writing, reusing\n  the existing provider_is_configured_for_active predicate and custom\n  provider table checks.\n- Add App.config snapshot so commands can consult the loaded config.\n- Update the stale DEFAULT_FLEET_NAME doc comment to mention ⇧F.\n- Sync crates/tui/CHANGELOG.md.

* style: cargo fmt

* fix(web): align react with react-dom 19.2.8 to unbreak npm ci

Dependabot #5801 bumped react-dom to 19.2.8, whose peer range requires
react 19.2.8; the lockfile still resolved react 19.2.6, so 'npm ci' in
web/ failed ERESOLVE on main and on every branch that merged it
(Lint & Type Check red). Align react to 19.2.8; install verified clean.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* brand: trace supplied whale assets

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* brand: align icon ombre and generated tokens

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* brand: use white icon tile

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* brand: deepen ombre light stop

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* brand: wordmark takes the blue ombre

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* tui: recover from image-input rejections by non-vision routes

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* tui: localize image rejection recovery

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* chore: format 0.9.12 mega branch

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* Redesign Fleet role labels and agent cards

* feat(tui): launch hero as wordmark + small surfacing mark

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* design(tui): retune whale palette to codewhale navy / ombre sky

Field, chrome, panel, plate and raised surfaces move onto the brand navy
(#070C1D → #142352 → #1A2C63); interaction blue becomes the ombre sky
#6AA6DC, light-mode action the ombre cobalt #1535B2; ice/cyan/border/tool
tints follow. web/app/tokens.css regenerated via
scripts/export-design-tokens.py.

Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* test(tui): re-bless ink goldens for navy palette

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* web: Space Mono wordmark, quiet layout refresh, fleet vocabulary in site + docs

Space Mono (OFL) outlined wordmark rebuilt via scripts/build-wordmark.py,
wired as --font-display through next/font/google; body stays IBM Plex Sans,
code stays JetBrains Mono. Nav loses the issue strip, strapline, Discord badge
and second filled CTA; home loses the ticker, seals and tilt figure; docs
shell hero collapses to a one-line band; footer uses the inverted wordmark.

Public noun is fleet (/fleet, codewhale fleet, /docs/fleet canonical; /pod,
codewhale pod, /docs/pod remain compatibility aliases) across docs/, site
dictionaries, vocabulary contract and public-surface facts.

No-Issue: 0.9.12 website lane

* brand: keep the traced wordmark; drop Space Mono outline build

* web: IBM Plex Sans Condensed as display face

* brand: Plex Sans Condensed wordmark; nav mark; drop fabricated home demos; AA meta text

* feat(tui): bottom dock tabs — clickable panel switch + close

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* Fix Fleet role migration verification

* fix(tui): dock keys yield Tab to mode/permission cycles

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>

* web: Impeccable polish — type floors, heading outline, docs measure; add PRODUCT.md/DESIGN.md

* Resolve canonical Fleet roles to legacy members

* web: flat hero — drop cyan glow/gradients/shadow, muted eyebrows

* design: PRODUCT.md/DESIGN.md at repo root — shell direction, bottom dock, anti-slop rules, 0.9.12 tokens

* Auto-enroll used models into the Fleet

* brand: keep the founder's wordmark rasters as the source of truth

The web lane replaced brand/wordmark0901.png and brand/wordmarkinverted.png
with Plex Sans Condensed renders. The founder-supplied PNGs are the brand
source; the SVGs are re-traced from them in a following commit.

* tui(mark): the launch mark has one rung

The hero now paints the small mark over the wordmark, so the medium and
large rungs and the for_area ladder have no consumer and fail the
dead-code lint. Remove them rather than allow them.

* brand: trace the founder's wordmark to SVG

brand/wordmark.svg and wordmark-inverted.svg were an IBM Plex Sans
Condensed text render; the founder's wordmark is the rounded monoline in
brand/wordmark0901.png. scripts/brand/trace-brand.py now traces that PNG
(magick threshold 60% + trim, potrace -s --flat -t 20 -O 0.4 -a 1.2),
folds potrace's transform into one compact path in a tight 1874x264
viewBox, and writes the navy #142352 and white colourways from the same
geometry. The Plex builder scripts/build-wordmark.py is gone with it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* web: derive icons and OG image from the traced mark

app/icon.svg is now the white whale on the #142352 rounded tile as on the
founder's sheet; favicon.ico (48/32/16), apple-icon.png, icon-192.png and
icon-512.png are rasterised from it by scripts/brand/trace-brand.py, and
the manifest colours are the same navy. The social card keeps the navy
ground, white mark and traced wordmark and restores the identity phrase
the page-meta contract expects.

The nav sits on the dark field on every route, so it renders the inline
Whale (white brand ink) and the inverted wordmark instead of a
prefers-color-scheme picture pair; the wordmark box uses object-fit so
the ~7.1:1 trace scales inside the compact nav instead of squashing.

Exploration rasters web/public/brand/codewhale-mark-*.png and their
web/brand/mark tile sources had no consumers and are removed;
codewhale-mark.png stays (public-auth-routes pins its hash).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* web: map stray hard-coded colours to navy tokens

globals.css carried a handful of literal navy-family greys, ice hairlines,
seafoam borders and a cyan glow beside the generated --whale-* tokens.
Each now reads the token it was approximating (whale-bg/chrome/panel,
whale-ice, whale-accent-secondary, whale-action, whale-cyan,
whale-text-dim), and the docs light sheet inks the mark in the brand
navy via --whale-composer (#142352).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* palette: inventory WHALE_* tokens before the one-name-per-colour collapse

Shell design §2.6 (SHELL-DESIGN-20260901) measured "58 WHALE_* symbols;
one colour under five names; 5 dead tokens". Receipt before touching
anything, generated from crates/tui/src/palette/tokens.rs. "uses" is the
whole-word count across crates/ excluding the const's own definition and
`use`/`pub use` lines (wrapper consts inside tokens.rs count).

  name                        value                       alias-of                    dead uses
  WHALE_BG_RGB                (7, 12, 29)                                                    3
  WHALE_CHROME_RGB            (12, 21, 49)                                                   1
  WHALE_PANEL_RGB             (16, 28, 64)                                                   3
  WHALE_COMPOSER_RGB          (20, 35, 82)                                                   3
  WHALE_ELEVATED_RGB          (26, 44, 99)                                                   4
  WHALE_SELECTION_RGB         (30, 60, 143)                                                  3
  WHALE_TEXT_BODY_RGB         (246, 242, 232)                                               10
  WHALE_TEXT_SOFT_RGB         (182, 192, 212)                                                4
  WHALE_TEXT_MUTED_RGB        (147, 160, 184)                                                3
  WHALE_TEXT_HINT_RGB         (138, 153, 179)                                                3
  WHALE_TEXT_DIM_RGB          (105, 119, 145)                                         yes    0
  WHALE_ACTION_RGB            (106, 166, 220)                                                6
  WHALE_COBALT_RGB            (21, 53, 178)                                           yes    0  (web: --whale-cobalt x3)
  WHALE_ICE_RGB               (221, 238, 249)                                         yes    0  (web: --whale-ice, rustRgb("WHALE_ICE"))
  WHALE_CYAN_RGB              (120, 188, 232)                                                2
  WHALE_ACCENT_SECONDARY_RGB  (79, 209, 197)                                                11
  WHALE_BRAND_ORANGE_RGB      (255, 138, 61)                                                 1
  WHALE_BRAND_MAGENTA_RGB     (240, 78, 184)                                                 1
  WHALE_HUMAN_RGB             (246, 196, 83)                                                 5
  WHALE_ACCENT_PRIMARY_RGB    = WHALE_ACTION_RGB          WHALE_ACTION_RGB                   8
  WHALE_WORKING_GREEN_RGB     (155, 214, 111)                                                5
  WHALE_ACCENT_ACTION_RGB     = WHALE_ACTION_RGB          WHALE_ACTION_RGB            yes    0
  WHALE_ERROR_RGB             (255, 134, 178)                                                9
  WHALE_ERROR_HOVER_RGB       (255, 156, 194)                                                3
  WHALE_ERROR_SURFACE_RGB     (43, 21, 34)                                                   6
  WHALE_ERROR_BORDER_RGB      = WHALE_ERROR_RGB           WHALE_ERROR_RGB                    3
  WHALE_ERROR_TEXT_RGB        (255, 219, 232)                                                3
  WHALE_WARNING_RGB           (255, 122, 89)                                                 4
  WHALE_SUCCESS_RGB           = WHALE_WORKING_GREEN_RGB   WHALE_WORKING_GREEN_RGB            9
  WHALE_INFO_RGB              = WHALE_ACTION_RGB          WHALE_ACTION_RGB                  17
  WHALE_BORDER_RGB            (42, 63, 114)                                                  1
  WHALE_REASONING_TEXT_RGB    (224, 153, 72)                                                13
  WHALE_REASONING_SURFACE_RGB (42, 34, 24)                                                   3
  WHALE_REASONING_TINT_RGB    (22, 36, 74)                                                   7
  WHALE_DIFF_ADDED_RGB        (87, 199, 133)                                                 3
  WHALE_DIFF_DELETED_RGB      = WHALE_ERROR_RGB           WHALE_ERROR_RGB             yes    0
  WHALE_DIFF_ADDED_BG_RGB     (18, 42, 34)                                                   3
  WHALE_DIFF_DELETED_BG_RGB   (52, 24, 39)                                                   3
  WHALE_MODE_AGENT_RGB        (126, 180, 232)                                                4  (via MODE_AGENT: 13)
  WHALE_MODE_YOLO_RGB         (255, 112, 160)                                                4  (via MODE_YOLO: 13)
  WHALE_MODE_PLAN_RGB         (185, 220, 236)                                                4  (via MODE_PLAN: 13)
  WHALE_MODE_OPERATE_RGB      (173, 136, 255)                                                4  (via MODE_OPERATE: 14)
  WHALE_TOOL_LIVE_RGB         = WHALE_ACCENT_SECONDARY_RGB                                   3  (via ACCENT_TOOL_LIVE: 5)
  WHALE_TOOL_ISSUE_RGB        = WHALE_ERROR_RGB                                              3  (via ACCENT_TOOL_ISSUE: 5)
  WHALE_TOOL_OUTPUT_RGB       = WHALE_TEXT_SOFT_RGB                                          3  (via TEXT_TOOL_OUTPUT: 13)
  WHALE_TOOL_SURFACE_RGB      (15, 26, 58)                                                   3  (via SURFACE_TOOL: 5)
  WHALE_TOOL_ACTIVE_RGB       (24, 44, 94)                                                   3  (via SURFACE_TOOL_ACTIVE: 9)
  WHALE_ACCENT_PRIMARY        Color(WHALE_ACCENT_PRIMARY_RGB)  -> WHALE_ACTION               9
  WHALE_ACTION                Color(WHALE_ACTION_RGB)                                       85
  WHALE_LIVE                  Color(WHALE_ACCENT_SECONDARY_RGB)                             17
  WHALE_HUMAN                 Color(WHALE_HUMAN_RGB)                                        41
  WHALE_INFO                  Color(WHALE_INFO_RGB)       -> WHALE_ACTION                  105
  WHALE_BG                    Color(WHALE_BG_RGB)                                           96
  WHALE_CHROME                Color(WHALE_CHROME_RGB)                                        5
  WHALE_PANEL                 Color(WHALE_PANEL_RGB)                                        15
  WHALE_COMPOSER              Color(WHALE_COMPOSER_RGB)                                      5
  WHALE_ERROR                 Color(WHALE_ERROR_RGB)                                        35

  57 WHALE_* consts. Pure aliases (9): ACCENT_PRIMARY_RGB, ACCENT_ACTION_RGB,
  ERROR_BORDER_RGB, SUCCESS_RGB, INFO_RGB, DIFF_DELETED_RGB, TOOL_LIVE_RGB,
  TOOL_ISSUE_RGB, TOOL_OUTPUT_RGB. #[expect(dead_code)] (5): TEXT_DIM_RGB,
  COBALT_RGB, ICE_RGB, ACCENT_ACTION_RGB, DIFF_DELETED_RGB.
  Non-WHALE aliases of the same blue in tokens.rs: STATUS_INFO (8 uses),
  ACCENT_PRIMARY (dead, 0). One colour, #6AA6DC, under seven symbols:
  WHALE_ACTION(_RGB), WHALE_INFO(_RGB), WHALE_ACCENT_PRIMARY(_RGB),
  WHALE_ACCENT_ACTION_RGB, STATUS_INFO, ACCENT_PRIMARY — 225 call sites.

Script: python3 over tokens.rs + grep -rnw crates; kept out of scripts/
(one-off receipt, the numbers live here).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* palette: one name per colour — collapse WHALE_INFO / WHALE_ACCENT_PRIMARY into WHALE_ACTION

Shell design §2.6: "`WHALE_INFO`, `WHALE_ACTION`, `WHALE_ACCENT_PRIMARY`
and their `_RGB` twins are one colour. Collapse to `WHALE_ACTION`."

Mechanical rename across crates/ (word-boundary sed, no value changes):
  WHALE_INFO, WHALE_INFO_RGB           -> WHALE_ACTION, WHALE_ACTION_RGB
  WHALE_ACCENT_PRIMARY(_RGB)           -> WHALE_ACTION(_RGB)
  palette::STATUS_INFO                 -> palette::WHALE_ACTION
  WHALE_ACCENT_ACTION_RGB, ACCENT_PRIMARY (dead aliases) -> deleted
The `STATUS_INFO` static in commands/groups/config is an unrelated
CommandInfo and is untouched.

Where two names met in one predicate (adapt.rs light/solarized/community
remaps, grayscale text-soft bucket, SemanticForegroundRole::Action) the
duplicate disjuncts are dropped; `use` lists deduped; the
"primary accent aligns with action" test collapses to its one live
assertion (action blue != human gold). The Blue Stage doc comment moves
onto WHALE_ACTION_RGB. `palette::grammar` untouched: it reads UiTheme
slots, never these consts.

Evidence (CARGO_BUILD_BUILD_DIR=.../mega-tokens):
  cargo check -p codewhale-tui --all-targets      -> Finished, 0 warnings
  cargo clippy -p codewhale-tui --all-targets --all-features --locked
    -- -D warnings (CI allow-list)                -> clean
  cargo test -p codewhale-tui --lib palette::tests:: --locked -- --skip command_palette
    -> test result: ok. 59 passed; 0 failed
  RUST_MIN_STACK=67108864 cargo test -p codewhale-tui --lib startup_ink --locked
    -> test result: ok. 1 passed; 0 failed   (ink goldens unchanged)
  cargo test -p codewhale-tui --lib --locked -- menu_style cursor_accent color_compat
    -> test result: ok. 36 passed; 0 failed
Pre-existing, not from this diff (reproduced on the stashed tree):
tui::command_palette tests, feat012_ac1 and the startup_ink golden
overflow the default test-thread stack in a debug build; they pass with
RUST_MIN_STACK=64MiB.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* palette: delete the dead whale tokens

Shell design §2.6: "delete the five dead tokens". Two of the five went
with the alias collapse (WHALE_ACCENT_ACTION_RGB, ACCENT_PRIMARY); this
removes the rest that have no consumer in crates/ and no web consumer:

  WHALE_TEXT_DIM_RGB     (105,119,145)  0 uses, no --whale-text-dim on the site
  WHALE_DIFF_DELETED_RGB = WHALE_ERROR_RGB  0 uses, no --whale-diff-deleted
  ACCENT_SECONDARY       Color(WHALE_ACCENT_SECONDARY_RGB)  0 uses (TEXT_ACCENT/WHALE_LIVE carry it)
  STATUS_NEUTRAL         = TEXT_MUTED  0 uses

Kept, with a comment saying why the `#[expect(dead_code)]` is honest:
WHALE_COBALT_RGB and WHALE_ICE_RGB have no TUI consumer but the site
reads them through the token export (`--whale-cobalt` x3, `--whale-ice`
and `rustRgb("WHALE_ICE")` in web/lib/blue-stage-contract.test.ts).

Mode and tool-surface `_RGB` tuples stay: each is consumed through its
Color wrapper (MODE_AGENT/YOLO/PLAN/OPERATE 13-14 call sites each,
themes.rs + color_compat.rs; SURFACE_TOOL 5, SURFACE_TOOL_ACTIVE 9,
ACCENT_TOOL_LIVE 5, ACCENT_TOOL_ISSUE 5, TEXT_TOOL_OUTPUT 13). The §1
"12 tokens with zero consumers" counted the tuples, not their wrappers.

Evidence: cargo check -p codewhale-tui --all-targets -> Finished, 0 warnings;
cargo test -p codewhale-tui --lib palette::tests:: --locked -- --skip command_palette
-> test result: ok. 59 passed; 0 failed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* web: regenerate tokens.css after the whale token collapse

scripts/export-design-tokens.py (never hand-edited). Ten lines gone:
--whale-accent-primary(-rgb), --whale-accent-action(-rgb),
--whale-info(-rgb), --whale-text-dim(-rgb), --whale-diff-deleted(-rgb).
No site stylesheet or component consumed any of them
(grep -rn "\-\-whale-" web/app web/components web/lib); the only
reference was the alias-chain example in web/lib/whale-tokens.ts's doc
comment, now `--whale-success` -> `--whale-working-green` -> `#9bd66f`
(the old example also quoted a hex that stopped being true a retune ago).

Evidence:
  cd web && python3 ../scripts/export-design-tokens.py --check   (CI: npm run check:tokens)
    -> design tokens up to date (1 file(s), 42 tokens)
  vitest run lib/blue-stage-contract lib/docs-theme-contract
    -> Test Files 2 passed (2) / Tests 6 passed (6)
  (vitest ran against the main checkout's node_modules via a temporary
  symlink; this worktree has none installed.)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* docs(design): the status-bar grammar names the one blue token

STATUS_BAR_COLOR_GRAMMAR.md never named a collapsed token, so this is the
one sentence it needed: the Identity blue is `WHALE_ACTION`, its former
aliases (`WHALE_INFO`, `WHALE_ACCENT_PRIMARY`, `STATUS_INFO`) are gone, and
the whale theme's `info` / `accent_primary` slots both hold it. No other
document in the repo named them (grep over *.md, *.ts, *.tsx, *.css,
*.py, *.toml, *.yml, *.json outside node_modules); the root DESIGN.md
already speaks in CSS names.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* palette: the field follows the terminal-owned shell; `underwater` aliases deepsea

Shell design §2.0 decision 1 (founder: "We aren't supposed to be using a
blue background anymore"): ground is the terminal's; the navy field is
painted only under the opt-in deepsea column.

What was already true, verified before changing anything:
- Settings::default().theme is "terminal" (settings.rs:103).
- The whale pair (UI_THEME / LIGHT_UI_THEME) ends in
  `.with_terminal_native_shell()`: surface, panel, composer, header and
  footer are `Color::Reset`, pinned by
  `whale_pair_flat_shells_are_terminal_native_without_erasing_semantic_surfaces`.
- The ink goldens' legend reads `a reset on reset`.
- OceanTreatment::Flat is the default; Deepsea repaints Reset cells through
  OceanRamp::for_theme, which matches the whale pair by name + Reset shell.
The reviewer's citations (tokens.rs:6/:250/:465) are the token definitions
deepsea and the semantic surfaces still need, not the theme.

What was not true: ~90 direct `bg(palette::WHALE_BG)` paints in pickers,
overlays and full-screen views (provider_picker 14, views/mod.rs 11,
user_input, live_transcript, help, session/file/model pickers ...) bypass
the theme, and `adapt_bg_for_theme` only remapped them for
`theme_remap_active` presets. On the whale theme they laid navy patches
over the terminal ground. Rung 2 fix, one rule in palette/adapt.rs: the
field (`WHALE_BG` / `BACKGROUND_DARK`) always follows `ui.surface_bg` —
Reset on the whale pair, the user's `background_color` override when set,
the preset surface elsewhere. Panels, selection, elevation, error and
diff surfaces are untouched; no widget file changes.

`underwater` is now an accepted alias of `deepsea` in
settings.rs (normalize + `set`), OceanTreatment::parse and the
config_ui serde enum. Tests extended in place; the color_compat light
test now expects the Reset shell it already had for theme consumers.

DESIGN.md "Field" says the TUI ground is the terminal's own background
and the navy field is deepsea-only.

Contrast, all whale text/accent tokens on #000, #1e1e1e (VS Code),
#282c34 (One Dark), #002b36 (Solarized dark), #300a24 (Ubuntu), #0d1117
(GitHub dark), #282a36 (Dracula): body 12.5-18.8:1, soft 7.7-11.5, muted
5.3-8.0, hint 4.9-7.3 (floor 3:1), action 5.4-8.1, human 8.6-12.9, live
7.5-11.3, error 6.2-9.3, warning 5.5-8.2, green 8.2-12.2, reasoning
5.9-8.8. Only `border` (#2A3F72) is low, 1.4-2.1:1: a non-text hairline.
No token value changed.

Seen, tmux 80x24 PTY, TERM=xterm-256color COLORTERM=truecolor, hermetic
HOME, debug build, counting `48;2;R;G;B` background sequences:
  default (Terminal theme): startup, /theme picker, Help — no painted
    background before or after (picker shows only accent swatches).
  Blue Stage selected via T/Down/Enter, then F1 Help:
    before: 15 x `48;2;7;12;29` (WHALE_BG) + 1 x selection row
    after:  1 x `48;2;30;60;143` (selection row only)
  Startup stage on Blue Stage: none, before and after.

Evidence (CARGO_BUILD_BUILD_DIR=.../mega-tokens, RUST_MIN_STACK=16 MiB as CI):
  cargo check -p codewhale-tui --all-targets -> Finished, 0 warnings
  cargo test -p codewhale-tui --lib --locked -- color_compat palette::tests::
    ocean:: ocean_treatment live_transcript views::tests startup_ink
    --skip command_palette -> test result: ok. 238 passed; 0 failed
  cargo test -p codewhale-tui --lib --locked (full) ->
    test result: FAILED. 11901 passed; 7 failed; 13 ignored
    1 was this change (color_compat light test, updated above); the other 6
    are role-name / slash-list assertions from other lanes on this branch
    (scout<->explore, worker<->general, slash.impeccable) and untouched.
Ink goldens unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* tests(palette_audit): re-pin whale roles to the retuned palette

`whale_roles_are_pinned_and_non_colliding` still pinned the pre-navy
values (WHALE_BG (3,7,13), ACTION (106,174,242), ...) and failed on this
branch before the token slice touched anything. Pins now match tokens.rs;
no colour value changes.

cargo test -p codewhale-tui --test integration --locked palette_audit
  -> test result: ok. 3 passed; 0 failed

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* tui/cli/web: fleet is the public product term; /pod, codewhale pod stay aliases

Founder decision 2026-09-01: "fleet" is the customer-facing name for the
assembled model team and "Pod" is retired from product copy. `/fleet` is
the canonical slash command and `codewhale fleet` the canonical CLI verb;
`/pod`, `codewhale pod`, `loadout`, and `party` remain parser aliases.
Storage keys, the ledger file name, config tables, protocol identifiers,
and MessageId variant names keep their current spelling.

- CommandInfo name/aliases/usage, help text, and the unknown-verb error
  flip to /fleet; `/fleet fleets` (saved/manage) is the saved-fleet picker
  with `/fleet pods` kept as an alias.
- All 15 locale packs: localized values say fleet; the settings goldens
  follow. `KbCompleteCycleModes` names the modes as Plan → Work → Operate
  (Act is only a compatibility alias per docs/MODES.md).
- `scripts/check-tui-product-vocabulary.sh` now rejects `Pod` in en.json
  instead of rejecting `fleet` in every pack.
- Hotbar id `slash.fleet` is canonical; persisted `slash.pod` normalizes.
- Fleet store error prose says fleet.
- Docs: PRODUCT.md lists the current role tokens (general, explore,
  planner, reviewer, implement, test, advisor, custom) and names the old
  spellings as aliases; docs/FLEET.md uses one role vocabulary;
  web/lib/content/vocabulary.ts ADVISORY_ROLE is Advisor with consultant/
  oracle as the legacy spellings (matches fleet/profile.rs migration).

Evidence:
  sh scripts/check-tui-product-vocabulary.sh -> exit 0
  cargo test -p codewhale-config -p codewhale-lane --locked
    -> 638 passed; 0 failed / 62 passed; 0 failed
  cargo test -p codewhale-cli --locked -- fleet pod -> 3 passed; 0 failed
  cargo test -p codewhale-tui --lib --locked -- fleet::store fleet::members
    fleet::identity -> 24 passed; 0 failed
  cargo test -p codewhale-tui --lib --locked -- groups::core::fleet
    localization command_palette hotbar fleet_roster settings widgets
    fleet::control pod_workers -> 605 passed; 1 failed (the failure is
    slash_source_matches_command_palette_command_entries, which reads the
    machine's ~/.claude/skills and finds an `impeccable` skill; it fails
    identically without this change)
  cd web && npm test -- lib/content/vocabulary.test.ts -> 11 passed

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* chore(tui): clear the six clippy 1.98 errors the base lanes left

needless_borrow on provider_identity_for_persistence (five sites) and a
collapsible_if in the work-surface mouse path. No behaviour change.

* tui(composer): restore double-tap Enter as the send-now gesture

While a turn is running, the first bare Enter queues the message (as
before) and opens a 500 ms window (`App::DOUBLE_TAP_WINDOW`, the value
the removed code in c5c42b7d91 used). A second bare Enter inside that
window with an empty composer promotes the just-queued message to a
Steer through `attempt_steer_with_queue_fallback` — the same path
Ctrl+Enter takes, so there is one steering path. A second Enter with
new text is an ordinary queue; Ctrl+Enter still steers immediately;
outside a turn Enter is unchanged. `enter_with_double_tap` is the one
decision point again (`take_queued_for_double_tap_steer` routes through
it), and `submit_disposition_does_not_mutate_the_queue` stays true.

The posture bar advertises the gesture while the window is open
(`PostureHintEnterAgain`, next commit).

Tests (cargo test -p codewhale-tui --lib <filter> --locked):
  double_tap:          test result: ok. 3 passed; 0 failed
  enter_with:          test result: ok. 5 passed; 0 failed
  submit_disposition:  test result: ok. 6 passed; 0 failed

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* tui(shell): one owner per fact — posture bar, metrics line, no dead hints

Design: SHELL-DESIGN-20260901 §2.0 item 3, §2.2, §2.3, §2.3b, §2.11 and
the founder's 2026-09-02 redirect (Claude Code's grammar, less always-on
information). Under the composer there are exactly two chrome rows in
the default state, then the work surface only when it has content:

  ▶▶ ask (Shift+Tab) · work (Tab) · 2 agents · Esc to interrupt   /rc …
  deepseek-v4 · ctx 61% · $0.42 · ttft 400ms · 40 tok/s · ↓ 1.2K  Ctrl+/ help

Fact → owner, before → after (composed 80x24 / 120x32 frames, working
turn with two sub-agents; "strip" = the work-surface bottom view):

  fact              before                                        after
  context %         info line                                     metrics line
  cost              footer (+ info line when priced)              metrics line
  model             info line                                     metrics line
  provider          info line (wide)                              metrics line (wide)
  ttft / tok/s / ↓  behind /cost only                             metrics line
  repo slug         info line (+ idle empty state)                launch header / git view (not chrome)
  branch            info line (+ idle empty state)                launch header / git view (not chrome)
  mode              footer                                        posture bar
  permission        footer                                        posture bar
  phase word        footer ("sub-agents underway")                transcript active row (not chrome)
  elapsed           footer ("1m 15s")                             roster rows (per agent)
  agent count       indicator row + info "pod 2/2" + "whales 2/4" posture bar ("2 agents")
                    + dock tab + strip header                     (+ dock tab, strip header — other slice)
  task/shell count  indicator row above the composer              posture bar
  help hint         footer keys legend (F1) + info line (Ctrl+/)  metrics line (Ctrl+/), from the binding
  other key hints   footer "⌥V:output", compact "? help"          none; cycle keys next to the chip they cycle
  live hint         footer "Esc to interrupt"                     posture bar hint slot
  ≥80 % microcopy   footer right slot                             posture bar hint slot (outranks the hint)
  notice / rc       footer right slot / —                         posture bar right slot

Dead key hints removed: `F1:keys` / `fn+F1:keys` (Help binding's
`footer_chord` is now `Ctrl+/`; `info_help_hint` derives from the
binding), compact `? help`, and the `footer_action_hints` family. The
mode/permission cycle keys print only when the binding table admits
them at the current focus (no `(Tab)` on the launch stage).

Row order: composer → posture bar → metrics line → roster/to-do. The
#5286 background-work chip above the composer is gone (it repeated the
posture bar's counts); `PendingWork` stays as the counts' source.

Goldens re-blessed and read: footer_* (posture bar), infoline_startup_*,
infoline_work_* (metrics line), settings_* (the settings preview's
bottom row); infoline_settings_* deleted with the settings-path segment.

Commands run (CARGO_BUILD_BUILD_DIR=…/mega-frame, RUST_MIN_STACK=16777216):
  cargo check -p codewhale-tui --all-targets            clean
  cargo test -p codewhale-tui --lib infoline --locked   test result: ok. 11 passed; 0 failed
  cargo test -p codewhale-tui --lib tideline_tests      test result: ok. 64 passed; 0 failed
  cargo test -p codewhale-tui --lib one_owner_tests     test result: ok. 4 passed; 0 failed
  cargo test -p codewhale-tui --lib shell_key_routing   test result: ok. 13 passed; 0 failed
  cargo test -p codewhale-tui --lib localization::tests test result: ok. 49 passed; 0 failed
  cargo test -p codewhale-tui --lib --locked            test result: FAILED. 11893 passed; 8 failed
                                                        (config_panel golden re-blessed after; the other 7:
                                                        4 fail on HEAD without this change (fleet rename
                                                        in flight), tmux clipboard passes alone, none in
                                                        files this change touches)
  cargo clippy … -D warnings                            6 pre-existing errors, none in this change's hunks
                                                        (config.rs:2106/2796, apply.rs:759, event_loop.rs:464,
                                                        session_state.rs:1004, work_surface/input.rs:401)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* wip(launch): checkpoint for overnight takeover — braille mark + kitty tier + Claude-Code launch header compile and pass lib tests; PTY cucumber tests still wait on the old heading

Compiles (cargo check --all-targets clean). Lib tests for mark::, underwater::,
mouse_ui::, localization:: pass: test result: ok. 120 passed; 0 failed
(needs RUST_MIN_STACK=16777216 like scripts/dev-test.sh; the rust_i18n static
overflows a 2 MiB test thread with or without this change). Startup goldens
re-blessed and read. Clippy is red only in files outside this slice
(config.rs, apply.rs, session_state.rs, work_surface/input.rs, and a
pre-existing event_loop.rs borrow).

Not done: crates/tui/tests/cucumber/{screen_mode_inline_pty,
active_composer_pointer_pty,plugin_e2e_acceptance}.rs still wait for
"What are we working on?" and press 'w'; they need the new marker
("Codewhale v") and a typed message + Enter to begin the session.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* wip(rail): checkpoint for overnight takeover — dock views compile, 136/137 work_surface tests pass, files/notepad/git views are stubs

Foundation for the one bottom dock (founder redirect 2026-09-02):
RailPanel is now the eight-view cycle (agents, tasks, background, files,
notepad, context, git, price; Pinned folded into tasks), an auto rule opens
agents/tasks/background while they have content, explicit picks stick until
Esc, and Ctrl+Tab / Ctrl+] (fwd) + Ctrl+Shift+Tab (back) cycle. Context and
price views render as rows; files, notepad, git are stubs in views.rs. The
classic sidebar line panels and their dead consumers are deleted.

Known: agent_rows_show_role_assignment_and_open_the_agent_transcript fails
(role_label 'worker' vs 'general'); role derivation is untouched here and
the failure is believed to predate this work — unverified.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* wip(operate): checkpoint for overnight takeover — auto-goal + contract land; one Operate approval test needs a goal-complete mock

Operate now turns a non-trivial prompt into the goal through the same
GoalState::create path as explicit_goal_directive, appends the Operate
contract once as a user-role runtime message (append-only history, not
the prefix), shows the Operate goal receipt, and carries the new mode copy
in all 15 locale packs plus docs/MODES.md.

Compiles. Passing: goal (134), prompts (133, incl.
every_mode_shares_one_prompt_per_host), localization (49),
runtime_handoff (14), session_peek (15), history_cells (2), both new
engine tests. Known failing:
core::engine::tests::operate_model_shell_uses_normal_approval_and_workspace_sandbox
— its mocked model never reports the auto-set goal complete, so the turn
re-prompts to max_steps (wiremock expect(1) sees 199). Six clippy
needless_borrow/collapsible_if hits pre-exist on the branch base.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* wip(fleet): checkpoint for overnight takeover — #5815 review findings 1-9 fixed, compiles, fleet tests green

Findings against the fleet-as-models work (verified against the current
tree, then fixed):

1. `toggle_fleet_model` decides presence by member rows, not the projected
   role list (a role-less row projects to no role, so the old
   `all(== "operator")` was vacuously true). Regression test
   `toggle_removes_a_role_less_member_row` fails on the previous commit
   ("got Unchanged { … operator route … }") and passes now.
2. `selected_or_default` loads an existing personal `My fleet` instead of
   overwriting it and never writes or selects before the add succeeds;
   `FleetModelChange::Added` carries `created_fleet` + `selected_fleet`.
3. `fleet_models` returns `Result<Vec<_>, FleetStoreError>`: a broken
   explicit selection is surfaced in `/fleet models`, `/models`, and the
   picker's ⇧F instead of reading as "session model only".
4. `add_fleet_model` dedupes roles (case-insensitive) and returns
   `Unchanged` without touching the file when every role is present
   (test compares bytes and mtime).
5. `App.config` startup snapshot removed. `/fleet add|remove` now return
   `AppAction::FleetAddModel|FleetRemoveModel`; the UI arm validates the
   provider against the live `Config` (`fleet_provider_rejection`,
   `fleet_catalog_rejection`, re-exported from `commands`).
6. ⇧F applies the same provider gate as `/fleet add`.
7. One roster path: `sync_fleet_roster` (extracted from the
   FleetStoreChanged arm) plus `App::fleet_roster_stale`, flushed once per
   event-loop iteration; `/fleet add|remove`, ⇧F, and every UI-side
   auto-enroll site set it (`auto_enroll_fleet_model` now returns bool).
8. ⇧F receipts go through `push_status_toast` (Success/Info, 6 s) and
   `set_sticky_status` (Error); no new `status_message` writes.
9. All new fleet prose is `tr(locale, MessageId::Fleet…)` (27 keys,
   translated in all 15 packs); `FleetModelError` is typed with a
   localized `message(locale)`.
10. No stale "`a` in /models" doc comment exists in the current tree.

Also re-blessed `config_panel_{80x24,120x32}` goldens (the Config tab
label says Fleet); the diff is that one label.

Evidence (this tree):
  cargo check -p codewhale-tui --all-targets --locked -> Finished
  cargo test -p codewhale-tui --lib --locked -- fleet::members
    groups::core::fleet model_picker format_helpers fleet_roster
    localization golden hotbar command_palette fleet::store
    -> 257 passed; 1 failed (slash_source_matches_command_palette_
    command_entries: reads ~/.claude/skills and finds `impeccable`;
    fails identically on main in this environment)
  cargo clippy … -D warnings (CI flags) -> the only remaining error is
    crates/tui/src/tui/work_surface/input.rs:401 collapsible_if, which
    belongs to the work_surface lane and predates this commit
  cargo fmt --all -- --check -> clean
  sh scripts/check-tui-product-vocabulary.sh -> exit 0

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSVsAXZJnKGZmqkwH1CeKH

* feat(tui): launch card, canonical role vocabulary, DashScope descriptor, test fixes

- Launch is now our own card take (founder, 2026-09-02): thin top line
  ⑂ branch  path; centred bordered card with the whale mark, Codewhale +
  version, one true announcement (no-model warning / MCP news), and the
  menu New worktree / Resume session / Changelog / Quit with real chords
  right-aligned; Enter runs the highlighted entry, Up/Down move it, and
  typing goes straight to the composer. The card dissolves on the first
  keystroke or command (≤240ms, instant under reduced motion); the
  working screen then shows ⑂ branch  path + ⋮ MCP n/m, the
  session_start receipt, and the posture bar + metrics line (hidden
  while the card is up). The composer's bottom rule carries
  model (effort) · permission — the route's one launch reading.
- Role vocabulary: fixtures and the stopship fleet/workflow now use the
  canonical tokens (explore/implement/reviewer/test); the workflow JS
  wire accepts canonical spellings with the pre-rename ones as aliases
  (AgentType serde rename+alias, serialized form is canonical).
- Alibaba Model Studio (DashScope) joins the data-driven descriptor
  table: international compatible-mode endpoint, DASHSCOPE_API_KEY,
  live /v1/models as the Qwen model authority (never a compiled id).
- Tests: role-keyed gate fixtures moved to canonical tokens; the operate
  model-shell test now seals the goal through the deferred update_goal
  tool (deferral retry included) instead of pausing blindly; the
  slash-source hotbar test isolates HOME; ⚠ and ⋮ gained ASCII
  fallbacks; launch goldens re-blessed for the card.

* feat(tui): retire Pod from copy; canonical workflow fixture; gate clean-up

- Pod literal sweep across fleet views, worker runtime, sub-agent tool,
  managed API, and command groups: user-facing copy now says Fleet
  (founder vocabulary decision; /fleet canonical, /pod and
  'codewhale pod' stay as compatibility aliases). Roster tests that
  encoded the retired Pod-public/Fleet-internal split now assert the
  public Fleet vocabulary.
- workflows/stopship + fleets/stopship use canonical role names
  (explore/implement/reviewer/test); the workflow crate's own stopship
  tests and required-roles list follow.
- Operate mode-picker hint shortened to fit 80 columns in every locale.
- Cucumber PTY launch flows: wait for the launch card, type the first
  prompt and press Enter; the live shell is proven by the launch stage's
  top line disappearing and the metrics line's ctx reading (the
  interrupt hint needs a live turn, which an offline route never
  starts). The stopship acceptance feature expects the canonical /fleet
  help copy.
- CHANGELOG receipts synced; DESIGN.md shell direction records the card,
  posture bar + metrics line, and the bottom view cycle.

* test(cucumber): launch-card PTY contract fixes

- The launch-card wait uses the menu's New worktree entry — unique to the
  card; the bare wordmark also matches onboarding copy.
- The live-shell proof is the launch stage's top line disappearing plus
  the metrics line's ctx reading; the interrupt hint needs a live turn,
  which an offline route never starts, and the help hint sheds first at
  the 40-column floor by design (SHELL-DESIGN-20260901 §2.2).
- The pointer-submit queue proof takes the offline onboarding seed into
  account: the receipt toast proves the gesture, and the queue count
  grows by exactly one.

* docs(readme): restore the canonical product screenshot the web contract pins

The brand header redesign dropped the assets/screenshot.webp embed; the
web public-surface contract pins the README and the website to one
canonical optimized screenshot (byte-identical, 1562x1256 lossless
VP8L). Re-embed it.

* test: platform-robust pointer-submit wait and cap-warning diagnostics

- The pointer queue proof accepts either the transient receipt toast or
  the queue-count increment: toast timing differs across runners, and a
  20 s wait missed a toast the queue dump proved had fired.
- The context-cap posture test dumps the drawn rows when the warning
  count misses, instead of a bare 0 != 1, so a platform-specific shed
  (the hint sheds first when the left run exceeds its budget) is
  visible in CI.

* test: fix the linux-only context-cap shed; bounded pointer-click retry

- The context-cap posture test drew at 100 columns, where a backend-less
  platform (linux CI paints 'files: workspace (unenforced)') sheds the
  cap hint first, so the warning count read 0. Draw at 140 columns,
  where the hint survives with the notice present; verified locally.
- The pointer queue proof retries the [↑] click once, re-finding the
  affordance first: under runner load a redraw can shift cells between
  the find and the click, so the first SGR gesture lands nowhere.

* fix(gates): tool-catalog budget covers the fleet rename; readme stamps; pointer baseline

- The Pod->Fleet sweep grew every mode's tool-schema surface by 58 bytes
  (+14 tokens). The receipts are re-measured and the one-way ceilings in
  scripts/runtime-contract-budget.json are raised to them as the
  explicit maintainer decision the gate asks for (the rename is the
  founder's 2026-09-01 vocabulary call).
- The README screenshot embed changed README.md; the 18 translated
  READMEs re-stamp (the embed is language-neutral HTML - no prose
  changed, so no retranslation was needed).
- The pointer queue baseline is captured while the composer is empty:
  the pending preview row hides while a draft sits in the composer, so
  the pre-click depth read None and the growth proof could not fire.

* test: re-click then keep polling until the deadline

The qa_harness Instant wrapper does not implement Div, and the retry's
single read raced the app processing the second gesture: poll to the
full deadline, re-click once at the half-way point.

* test: pointer queue diagnostics (baseline/expected/last-seen) in the failure output

* test: pointer poll keeps per-iteration state only (unused-assignment gate)

* fix: Copilot review findings — planner wire spelling and Advisor copy

- workflow::AgentType::Plan serializes as the canonical 'planner'
  ('plan'/'awaiter' stay accepted aliases), matching the FleetRole
  vocabulary the mega PR declares.
- Web: the vocabulary docs metadata, the vocabulary module header, and
  the docs-map topic description say Advisor (the public advisory term)
  instead of the retired Consultant spelling.
- Polish home dictionary: restore 'Podwodna powłoka terminala' — the
  fleet-vocabulary sweep had merged 'Fleet' into the compound word
  'Podwodna' (underwater), producing the non-word 'fleetwodna'.

* test: pointer proof accepts preview-appears when no baseline count is painted

* test: the tolerant preview-appears proof (the arm the last commit missed)

---------

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 09:32:14 -07:00
Hunter Bown
d7042ab835 Merge pull request #5818 from Hmbown/devin/1788324534-web-react-peer
fix(web): pin react to 19.2.8 to match react-dom peer
2026-09-01 23:38:15 -07:00
Hunter Bown
16a2f0de02 Merge branch 'main' into devin/1788324534-web-react-peer 2026-09-01 23:10:55 -07:00
Hunter Bown
3ca7d3931f Merge pull request #5821 from Hmbown/devin/1788325219-green-main
fix: make main green — macOS OAuth callback read + dead-code budget receipt
2026-09-01 23:10:39 -07:00
Devin AI
60c1fbe568 chore: dead-code budget 425 after #5811
Co-Authored-By: Hunter Bown <hmbown@gmail.com>
2026-09-02 05:32:04 +00:00
Devin AI
658704506a Merge origin/main into green-main 2026-09-02 05:31:45 +00:00
Hunter Bown
b4b8f90584 Merge pull request #5811 from Hmbown/feat/chrome-honest-bar-20260901
feat(tui): the honest info line lives under the composer
2026-09-01 22:27:33 -07:00
Devin AI
2372a8cf8f chore: record dead-code budget at 426 after #5784/#5807/automation slice 1
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>
2026-09-02 05:05:09 +00:00
Devin AI
a902f56f40 fix(tui): ChatGPT OAuth callback stream reads blocking on BSD sockets
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>
2026-09-02 05:05:09 +00:00
Devin AI
0d09988b52 style(tui): rustfmt frame.rs let-chain
Co-Authored-By: Hunter Bown <hmbown@gmail.com>
2026-09-02 05:01:52 +00:00
Devin AI
8a646c5b73 fix(web): pin react to 19.2.8 to match react-dom peer
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>
2026-09-02 04:51:07 +00:00
Hunter Bown
e68dfce14a Merge pull request #5813 from Hmbown/feat/diff-intraline-20260901
feat(tui): diff cards emphasise the changed words within a line
2026-09-01 21:01:11 -07:00
CodeWhale Bot
9384811879 fix(tui): satisfy clippy let-chain and re-bless config panel goldens
- frame.rs: collapse the workspace-guard + branch Option into one let-chain
  (clippy::collapsible_if under -D warnings; house style).
- config_panel_{80x24,120x32}: re-bless after the deliberate context
  sparkline removal — the only diff is the '∿∿∿ 0%' reading leaving the
  preview line, which is this branch's design change, guarded by
  footer_states_no_context_reading.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 20:59:09 -07:00
Hunter Bown
5e1067e6f7 Merge pull request #5814 from Hmbown/feat/screen-mode-inline-20260901
feat(tui): /fullscreen and /inline switch the screen at runtime
2026-09-01 20:58:40 -07:00
Devin AI
13566ea9c0 fix(tui): keep chrome state workspace-aware
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>
2026-09-02 03:39:34 +00:00
Devin AI
b2ccdde16f fix(tui): simplify bounded diff rendering
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>
2026-09-02 03:39:26 +00:00
Hunter Bown
667b0348d8 Merge pull request #5803 from Hmbown/dependabot/npm_and_yarn/web/types/node-26.4.0
chore(deps-dev): bump @types/node from 22.19.17 to 26.4.0 in /web
2026-09-01 20:36:24 -07:00
CodeWhale Bot
e282f397ba Merge origin/main into feat/chrome-honest-bar-20260901
Take main's schema-driven int-setting check (codewhale_config::setting)
over the hand-maintained key list; every listed key, including
fleet.exec.max_spawn_depth, is already in the schema.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 20:16:50 -07:00
Devin AI
fa51f4cd08 Merge remote-tracking branch 'origin/feat/diff-intraline-20260901' into feat/diff-intraline-20260901 2026-09-02 03:16:44 +00:00
Devin AI
532c05630d fix(tui): keep screen mode state synchronized
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>
2026-09-02 03:16:33 +00:00
Devin AI
0a21f0a314 fix(tui): bound intraline diff buffering
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Hunter Bown <hmbown@gmail.com>
2026-09-02 03:16:32 +00:00
CodeWhale Bot
812e2f5738 Merge origin/main into feat/diff-intraline-20260901
Keep the unicode feature on 'similar' (intraline word segmentation) and
take main's new ansi-to-tui dependency.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 20:14:42 -07:00
dependabot[bot]
e5cf5a5aea chore(deps-dev): bump @types/node from 22.19.17 to 26.4.0 in /web
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.19.17 to 26.4.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.4.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 03:12:54 +00:00
Hunter Bown
b76bed0826 Merge pull request #5816 from Hmbown/integration/goal-continuation-5711-20260902
feat(runtime-api): rehydrate persisted goals and run host-managed continuation loop (re-land of #5711)
2026-09-01 20:11:06 -07:00
Hunter Bown
b7b422214e Merge pull request #5812 from Hmbown/feat/ansi-tool-output-20260901
feat(tui): tool output keeps its colours
2026-09-01 20:11:03 -07:00
Hunter Bown
71c613d9e3 Merge pull request #5810 from Hmbown/feat/settings-schema-20260901
feat(settings): one settings schema; /settings is its projection
2026-09-01 20:10:59 -07:00
Hunter Bown
4d8757ae55 Merge pull request #5804 from Hmbown/dependabot/nix/fenix-e0f7f51
chore(deps): bump fenix from `2b240cd` to `e0f7f51`
2026-09-01 20:10:17 -07:00
Hunter Bown
ce0bfb2ece Merge pull request #5802 from Hmbown/dependabot/npm_and_yarn/web/opennextjs/cloudflare-1.20.4
chore(deps-dev): bump @opennextjs/cloudflare from 1.20.2 to 1.20.4 in /web
2026-09-01 20:10:11 -07:00
Hunter Bown
18c21a850d Merge pull request #5801 from Hmbown/dependabot/npm_and_yarn/web/multi-15e0f44842
chore(deps): bump react-dom and @types/react-dom in /web
2026-09-01 20:10:08 -07:00
CodeWhale Bot
1250e8b51c brand: final Codewhale mark and wordmarks (source rasters)
Same sources as codewhale-apps/brand. The web mark, favicon set, OG card,
README, and TUI launch surface are to be re-derived from these.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 19:47:10 -07:00
CodeWhale Bot
4e32291762 fix(tui): settings schema tests describe the shipped surface
- theme/locale hints are their value lists, not prose keys; the schema
  says so and the config test carries the same exemption
- `never` is a managed-only approval value; the editor never offered it,
  so the schema does not declare it as a choice
- projection test names the conditional row sets (permission source,
  shell source, provider endpoint, configured feature flags) and asserts
  no undeclared row exists
- round-trip probe supplies parser-valid samples for validated free text

tui::views::tests: 99 passed; settings_schema: 4 passed.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 19:39:55 -07:00
CodeWhale Bot
7b12d9367c fix(tui): deref Locale in tr_key call 2026-09-01 19:24:09 -07:00
CodeWhale Bot
1dfe17d798 sync(tui): update packaged CHANGELOG slice
Fixes the Version drift gate on this branch by regenerating
crates/tui/CHANGELOG.md from the root CHANGELOG.md.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 19:20:19 -07:00
CodeWhale Bot
f32b577d4a tui(settings): finish schema review fixes for PR #5810 2026-09-01 19:20:00 -07:00
CodeWhale Bot
a2ba3a07b0 Merge remote-tracking branch 'origin/main' into feat/screen-mode-inline-20260901 2026-09-01 19:19:51 -07:00
CodeWhale Bot
67ad27718b fix(tui): recompute mouse capture after /fullscreen or /inline and guard alt-screen tracker on escape failure
Recompute use_mouse_capture from the stored preference on every screen-mode switch, so /fullscreen and /inline do the same as startup. Only update the live alt-screen tracker after the Enter/LeaveAlternateScreen escape succeeds, and roll back the escape on failure. Add regression tests for failed escape rollback, live tracker, and per-screen mouse capture. Sync TUI changelog.

Harvested from PR #5814.
2026-09-01 19:19:47 -07:00
CodeWhale Bot
f4ec2e292f merge origin/main into feat/settings-schema-20260901 2026-09-01 19:19:34 -07:00
CodeWhale Bot
1a5c8dbdab Merge remote-tracking branch 'origin/main' into feat/chrome-honest-bar-20260901 2026-09-01 19:17:40 -07:00
CodeWhale Bot
a708452d6c fix(tui): intraline emphasis maps by word characters and counts words only
Review findings on #5813:

- Emphasis desynced on wrapped, indented lines: `wrap_text` re-inserts
  the indent lead on every continuation chunk and the greedy forward
  match drifted past it. Chunks now map each non-whitespace character
  back to the source by position (the invariant the wrapper keeps);
  whitespace joins an emphasised run only when both neighbours are in
  it. A `debug_assert!` pins the order, and any mismatch falls back to
  whole-line styling — never a wrong-word emphasis.
- The similarity threshold counted whitespace and punctuation tokens,
  so lines agreeing only on syntax passed. The ratio now counts word
  tokens only (tokens bearing an alphanumeric character).
- `\ No newline at end of file` markers are lifted out of a `-`/`+` run
  and painted after it, so files without a trailing newline still pair.
- The buffered run is capped at `INTRALINE_MAX_RUN` (64) replaced pairs
  and the `render_diff_body_bounded` doc states the bound honestly: the
  retained preview, one wrapped source line, and the current run's
  borrowed slices plus word segments for at most 64 pairs.
- Tests: multiple pairs in one run; a rewrite next to an edit in one
  run; syntax-only sharing stays plain; unicode word boundaries; the
  no-newline marker; an indented line whose change lands on the third
  wrapped chunk, asserting the exact text before the emphasis; and the
  wrapping test now asserts emphasis position per chunk.

Evidence: `scripts/dev-test.sh tui diff_render` —
test result: ok. 15 passed; 0 failed; 0 ignored.
`scripts/dev-test.sh tui` — 11860 tests run: 11859 passed, 1 failed
(chatgpt_oauth::tests::callback_is_accepted_on_either_loopback_family,
untouched by this diff; passes alone: 1 passed; 0 failed — loopback
port contention under six concurrent builds). clippy -D warnings with
CI's allow flags: clean.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 18:51:08 -07:00
Hunter Bown
f77ff18255 feat(tui): the work surface sits under the composer by default (#5809)
* feat(tui): the work surface sits under the composer by default

Round 3 of the shell design (codewhale-ops/design/SHELL-DESIGN-20260901.md
§2.0): the bar's information — posture, agent roster, to-do — lives UNDER
the composer, so scrolling up reads as intentional history and nothing
paints above the stage by default.

- `WorkSurfacePlacement::Bottom`: a strip placement that shares Top's
  auto-fit height math (`is_strip()`) and row machinery; only its slot in
  the frame differs (after the composer, before the footer). Its divider
  is the strip's top edge, so a drag on it inverts Top's delta; the body
  starts one row below the divider so row hitboxes stay honest.
- `bottom` is the default (`work_surface_placement`, `WorkSurfaceState`,
  `/rail on`, the rail hotbar toggle and Ctrl+Alt+0 all restore it) and
  the fallback for unknown values. Narrow terminals keep Bottom; only the
  side rails still fall back to Top below the side-rail width.
- `/config work_surface_placement bottom` is accepted; the error text
  lists the five placements.

Upgrade note: the default for `work_surface_placement` changes from
`left` to `bottom`. Users who set it explicitly are untouched.

Legacy ui/sidebar/mouse test fixtures pin `Top` because they assert the
old strip-above-transcript geometry; the Bottom default has its own
coverage (`bottom_placement_draws_the_work_strip_under_the_composer`,
`rail_panels_render_in_all_placements` now includes Bottom).

No goldens changed: the startup goldens carry no work rows, so the strip
is zero-height in both placements.

Evidence:
  scripts/dev-test.sh tui (cargo test -p codewhale-tui --lib):
    test result: FAILED. 11848 passed; 3 failed; 13 ignored
    (the 3 — runtime_api::omitted_runtime_models_use_the_active_provider_default,
    runtime_threads::approval_required_awaits_external_decision_allow,
    sandbox::seatbelt::file_provider_synthetic_operations_are_independent_under_seatbelt —
    are unrelated to this change and pass in isolation:
    test result: ok. 3 passed; 0 failed)
  focused rerun after the last edits:
    test result: ok. 11 passed; 0 failed; 0 ignored
  cargo clippy --workspace --all-targets -D warnings (CI allows): clean
Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* fix(tui): the bottom strip keeps every other frame slot in place

Buildkite 1535 on #5809: two cucumber PTY tests against the real binary
failed under the new Bottom default — at 40x12 a queued message had no
`Queued #1:` preview, and the plugin acceptance stage stayed blank.

Cause: the Bottom layout dropped the strip's leading slot and re-ordered
the constraint list, but the stage, workflow panel, pending-input preview
and background chip are addressed by fixed slot index (`body_chunks[1..4]`),
so under Bottom they drew into the wrong, zero-height slots.

Fix: one nine-slot layout in both placements. The strip owns a slot at
each end (above the stage for `top`, under the composer for `bottom`) and
only one has height, so every other slot keeps its index. The dual
constraint list is gone.

Test: `bottom_placement_keeps_the_stage_and_queued_preview_at_twelve_rows`
renders a 40x12 frame under Bottom with a transcript row and a queued
message and asserts both paint.

Evidence:
  cargo test -p codewhale-tui --all-features --locked --test cucumber -- \
    active_composer_pointer_submit_queues_without_provider \
    plugin_toml_binary_lifecycle_skill_and_stdio_mcp_acceptance
    test result: ok. 2 passed; 0 failed; 0 ignored
    (before the fix, the pointer test failed exactly as on Buildkite:
     "40x12: queued preview ... not visible after pointer submit")
  cargo test -p codewhale-tui --lib -- work_surface tui::ui::tests
    test result: ok. 808 passed; 0 failed; 0 ignored
  cargo test -p codewhale-tui --lib -- bottom_placement
    test result: ok. 3 passed; 0 failed; 0 ignored
  cargo clippy -p codewhale-tui --all-targets -D warnings (CI allows): clean
Signed-off-by: CodeWhale Bot <bot@codewhale.net>

---------

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 18:47:14 -07:00
CodeWhale Bot
52ac02300f fix(tui): enforce the info line's floor, clear launch hitboxes, retarget the PTY sentinels
The previous commit's floor fix, placement-test rewrite and blessed
goldens were lost between the working tree and the commit (a re-merge of
main reset them), which is why Buildkite 1537 and the Windows Test job saw
three unit failures. They are back, plus the review findings on #5811:

- Below the floor (a row narrower than route identity + join + `context
  NN%`) the join is skipped and the reading pins to the right edge whole;
  the route's tail is what gets covered. `infoline_sheds_bar_then_help…`
  now asserts that contract at 24 columns instead of only "contains".
- The launch branch of `frame.rs` clears `last_infoline_hitboxes` when the
  row has no height, as the session shell already did, so stale model /
  context targets cannot route a click at tiny heights.
- `composer_rows_stay_pinned_across_turn_state_transitions` reads the last
  row as the info line and the row above as the posture row; the work-strip
  probe reads the first body row. Stale "topbar" prose in the files this
  branch touches now says "info line" (test names included).
- Goldens `infoline_{startup,work,settings}_{80x24,100x30,120x32,160x40}`
  deleted and re-blessed (CODEWHALE_BLESS_GOLDENS=1): wordmark, `model`
  label, `│` joins and the right-pinned reading left the row by design.

PTY tests (feature `long-running-tests`) keyed on the old top bar, which on
main still paints the `CODEWHALE` wordmark and put the composer one row
lower:
- `active_composer_pointer_pty`: the startup and live-shell contracts prove
  the chrome painted via `context` (the info line's floor, never shed) and
  any advertised help route (`? help` / `Ctrl+/ help` / `F1:keys`) instead
  of the wordmark.
- `plugin_e2e_acceptance::wait_for_composer_ready`: the cursor sits above
  the composer's bottom rule, the posture row and the info line — four rows
  from the end, not three.

Evidence:
- `RUST_MIN_STACK=16777216 scripts/dev-test.sh tui`:
  test result: FAILED. 11854 passed; 1 failed; 13 ignored; 0 measured
  — `remote_control::tests::separate_predispatch_crashes_on_one_run_get_distinct_recovery_turn_ids`,
  untouched here, passes 3/3 in isolation (test result: ok. 1 passed).
- `cargo test -p codewhale-tui --lib -- infoline tui::ui::tests::composer_rows_stay_pinned tui::ui::tests::frame phase_strip`:
  test result: ok. 40 passed; 0 failed.
- `cargo nextest run -p codewhale-tui --all-features --profile ci -E 'test(active_composer_pointer_submit_queues_without_provider) | test(plugin_toml_binary_lifecycle_skill_and_stdio_mcp_acceptance)'`:
  Summary 2 tests run: 2 passed (on origin/main 2be2cb456 both also pass;
  on this branch before this commit both failed).
- clippy with CI flags clean; cargo fmt clean.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 18:45:01 -07:00
CodeWhale Bot
661c877021 fix(tui): a coloured intact path stays on one line
Codewhale review on #5812: intact (path/URL-like) rows with SGR were
routed through the wrapping styled renderer, so a long coloured path
could wrap and move its click region while the plain one stayed on one
line. Intact rows now take render_card_detail_line_single_styled — one
line, same rail+label prefix — so hitboxes are identical with or without
colour. Also from the review: split_segments borrows the segments and the
wrapped renderer reuses row.text instead of rebuilding the string each
frame.

Test: coloured_intact_path_stays_on_one_line_with_the_plain_hitbox.
  ansi_colour: Summary 7 tests run: 7 passed
  history:     Summary 199 tests run: 199 passed
  scripts/dev-test.sh tui: Summary 11859 tests run: 11859 passed, 13 skipped
  clippy (CI flags) exit 0; fmt clean.
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 18:43:20 -07:00
CodeWhale Bot
7a892238ab merge main: config_integer_key stays schema-driven
#5808 dropped one row from the hard-coded integer-key list; this branch
replaces the list with the schema's is_int(), which already excludes that
row. Kept the schema call.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 18:37:11 -07:00
CodeWhale Bot
719cb4807d test(runtime-api): the host goal loop's rehydration and settlement contracts
Review notes on #5711 asked for coverage of two pieces the diff had left
untested:

- GoalState::from_persisted keeps tokens_used / time_used_seconds /
  continuation_count (sync_from_host_status resets them on a changed
  objective; the same objective through the host path keeps them), starts
  evidence empty, and stamps finished_at only for a terminal status.
- settle_thread_goal_after_turn mirrors a complete / blocked engine
  snapshot into the durable record, accrues the turn's usage and the
  engine's continuation counter, and arms no further pass — a restarted
  host must not resume a goal the verifier already closed.

The cap and failed-pass cases were already covered by the contributor's
two host_goal_loop tests.

Evidence:
  scripts/dev-test.sh tui runtime_threads::tests::host_goal tools::goal::tests
  Summary [ 1.701s] 28 tests run: 28 passed, 11840 skipped
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 18:35:04 -07:00
CodeWhale Bot
8db1e4d57b ci: retrigger pull_request checks (the opened event never started ci.yml)
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 18:34:32 -07:00
Ben Gao
e1510884c6 fix(tui): make the host goal loop cap- and catalog-safe
Address review on the host-managed goal continuation loop:

- The engine snapshot is now the authority for the durable continuation
  counter (it counts every intra-turn pass via record_continuation),
  replacing the flat per-turn +1 that diverged from the engine's own
  ContinuationLimit gate and could re-arm passes the engine refuses.
- A snapshot at or beyond goal_max_continuations mirrors the engine's
  stop into the record as Paused and does not re-arm.
- A finished turn whose tool catalog lacked update_goal (allowed_tools
  restrictions, isolated_chat) no longer re-arms: the model has no
  terminal path, so the loop would spend one provider call per pass.
- Kickoff item summaries read "Goal kickoff (host-driven)" instead of
  "continuation pass #0".
- Reword the two comments that claimed the turn loop was the only
  continuation dispatcher; they now name the intra-turn hook and the
  host's cross-turn re-arm. Document the spawn_goal_continuation
  cancellation semantics and the mail-wake race at the settle site.
- Add runtime_threads tests pinning kickoff/continuation arming, the
  engine-cap park, the no-update_goal and failed-pass non-arming paths.

(cherry picked from commit 8ec0834a54)
2026-09-01 18:30:47 -07:00
Ben Gao
26a5a1e855 feat(runtime-api): rehydrate persisted goals and run host-managed continuation loop
Restore a thread's durable goal into the engine on load so the goal loop,
prompt surface, and update_goal tool operate on the persisted record from
the first turn, preserving accumulated usage and continuation counters.
Add GoalState::from_persisted and the shared-state constructor that
rehydrates without resetting counters (unlike sync_from_host_status).

Host-managed runtime engines never self-continue, so the runtime host now
owns the durable loop: each completed turn settles the goal (accrues spend,
mirrors the model's terminal complete/blocked/paused decision) and arms the
next pass after the configured quiet period while still Active. Goal upsert
also injects the objective into a cached engine and dispatches the kickoff
turn when idle; delete/complete/block sync the terminal status back in.

(cherry picked from commit efa39b5a77)
2026-09-01 18:30:46 -07:00
CodeWhale Bot
9105ccc74e fix(tui): the inline viewport follows the terminal size
Stock ratatui keeps `Viewport::Inline(rows)` at the rows it was built with,
so after the window grew a "full-height" inline session stopped at the old
height and left the new bottom rows blank. On `Event::Resize` an inline
session now rebuilds its terminal at the event-reported size over the same
negotiated backend facts.

The cursor is parked on row 0 first: a full-height viewport is anchored
there, and from row 0 the full height is exactly the room ratatui asks for,
so it appends no lines and the host scrollback gains nothing. Fullscreen
keeps the existing `terminal.resize` path.

The PTY proof grows the terminal 24→32 rows and shrinks it back, asserting
the bottom rows are painted and the alternate screen is never taken. Shown
failing without the refit:
  grow to 32 rows: nothing painted in the bottom rows after resize
  test result: FAILED. 0 passed; 1 failed
and passing with it:
  test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 30 filtered out

  cargo test -p codewhale-tui --lib -- screen_mode tui::ui::terminal
    test result: ok. 5 passed; 0 failed
  cargo clippy --workspace --all-targets (CI allow set): clean

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 18:19:11 -07:00