7910 Commits

Author SHA1 Message Date
Hunter Bown 369868d23e fix(tui): start first run on Welcome, not the provider list
Skipping Welcome to land on the local-runtime picker was an over-correction.
New users never saw a start screen or the calm "pick where your model runs"
explanation, so the first API-key setup had no way in except Esc-from-a-list
they did not ask for.

First run now always opens Welcome. Enter routes to language, the provider
explanation, trust, or ready depending on what this run actually needs; a
second Enter on the provider screen opens the picker. Returning missing-key
recovery still opens the picker on launch so a persisted route cannot be
silently replaced.

Evidence: cargo nextest -p codewhale-tui --lib -E 'test(/first_run_|launch_onboarding_|welcome_routes|provider_back_walks|tui::onboarding::/)' 31/31 pass.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
v0.9.10
2026-08-20 00:51:29 -07:00
Hunter Bown 22b5b3f3d9 Merge pull request #5520 from Lstarsky0/feat/docs-sandbox-web
feat(web): move docs/sandbox and docs/web onto the dictionary spine (#5337)

Preserves Lstarsky0's original commits. Same merge shape as #5517.
2026-08-20 00:35:14 -07:00
Hunter Bown 5baa345e80 Merge branch 'main' into feat/docs-sandbox-web 2026-08-19 23:58:17 -07:00
Hunter Bown e4ea0341ab ci: lint test code, and stop allowing the lints that reached the gate
CI's clippy step ran without `--all-targets`, so it never linted a single
test target. That is why the v0.9.10 release gate opened with four clippy
failures sitting on a green main: every one of them was in test code, and
two of the four were additionally allowed outright by
`-A clippy::collapsible_if -A clippy::assertions_on_constants`. The gate
crates/tui/AGENTS.md points contributors at is the all-targets one, so CI
was enforcing a weaker rule than the repo documents.

This is not a hypothetical gap. While fixing the timeout bug in the parent
commit I wrote a fresh `assertions_on_constants` violation in a new test;
the local all-targets gate caught it immediately, and CI as configured
would have merged it.

Widen the step to `--all-targets` and drop those two allowances. The three
that remain — uninlined_format_args, too_many_arguments, unnecessary_map_or
— are deliberate project style rather than oversights, so they stay. The
Lint job's budget goes to 45 minutes because the ubuntu Test job is skipped
on push, which means this runner has no warm cache for test targets.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 23:56:48 -07:00
Hunter Bown 8cd4457f71 fix(tui): bound a foreground bash run that names no timeout
A community report sat on one tool call for 9,522 seconds. The model had
already run the CLI once, seen it print "Not authenticated", and then
invoked it again; that second call blocked, and the turn stayed pinned with
the tool row simply counting seconds.

The hang is ours, not the CLI's. `BashTool::contract_delegate` sets
`optional_timeout`, so an omitted `timeout_ms` stayed `None` and fell
through `timeout_ms.unwrap_or(BASH_MAX_TIMEOUT_MS)` to `i32::MAX`
milliseconds — about 24.8 days. The tool's own input schema already
promises "action=run 120000", and its description already says foreground
mode is for bounded commands, so the implementation was contradicting the
contract it advertises to the model. We also already had the right
recovery: `FOREGROUND_TIMEOUT_RECOVERY_HINT` kills the process and tells
the model to rerun with `background=true` and poll with `action="wait"`.
It could never fire, because it is gated on a timeout that was never set.

Resolve the lifetime in one place, `contract_bash_timeout_ms`, and only for
foreground runs. Background and interactive runs are meant to outlive the
call, so bounding them here would kill long-lived jobs the model
deliberately detached — they keep `None`. An explicit `timeout_ms` still
wins at any value, including well above the default, so genuinely long
foreground work stays possible by asking for it.

The shared schema text now scopes the 600000 cap to the standalone Bash
tool, which is the only variant that clamps to it, and says what an omitted
foreground timeout does.

Evidence: `contract_bash_foreground_without_a_timeout_is_bounded_not_endless`
covers the omitted, explicit-long, explicit-short, background, interactive,
and non-contract cases; the shell and timeout suites pass 85/85; the exact
all-target all-feature clippy gate exits 0.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 23:56:45 -07:00
Hunter Bown 8aac1e8e3d test(tui): stop onboarding fixtures inheriting the developer's machine
Three onboarding tests build their app with `App::new`, which derives the
first-run decision from the ambient box — whether a locale can be inferred,
whether `settings.toml` exists, whether a provider key is in the
environment. Each test then overrode only the flags it named and inherited
the rest, so the same commit asserted different things on different
machines. That is why main is red in two directions at once:

- Windows CI (clean box, no locale, no key) fails
  `welcome_routes_to_the_first_decision_this_run_needs` and
  `progress_counts_only_required_decisions`: `App::new` had already set
  `onboarding_had_language_step`, so welcome routed to Language instead of
  Provider and the counter saw two required steps instead of one.
- A configured developer box fails
  `provider_back_walks_to_the_last_decision_this_run_asked`: `App::new` set
  `onboarding_missing_key_recovery`, whose branch exits to the offline
  composer instead of walking back.

Neither is a product defect — `advance_onboarding_from_welcome`,
`required_progress`, and `back_from_provider_onboarding` are all correct.
The fixtures were under-specified. Reset the onboarding decision fields to
"nothing asked yet" in `test_app_with_locale` and name the recovery flag in
the walk-back test, so each test opts in to the steps it is actually about.
No assertion is weakened or removed; the missing-key-recovery branch keeps
its own case.

Evidence: `cargo nextest run -p codewhale-tui -E 'test(tui::onboarding::)'`
25/25 pass, and the walk-back test passes under both nextest and libtest.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 23:39:49 -07:00
Hunter Bown 59e1825bc4 docs(changelog): file the #5517 receipt under 0.9.10, not Unreleased
Lstarsky0's receipt landed in `[Unreleased]`, but v0.9.10 is not tagged
yet, so everything on main ships in it. generate-release-body.sh extracts
only the `[<version>]` section, which means an `[Unreleased]` entry would
be dropped from the 0.9.10 release body — the exact disappearance that
check-feature-release-notes.sh was written to catch.

Move the entry beside its sibling phase in 0.9.10 `### Added` and adopt
that section's established shape, so #5504 and #5517 now read alike. The
wording, the epic framing, and the contributor's own commit are kept; only
the placement changes. main already carries the matching Contributors
credit line.

crates/tui/CHANGELOG.md regenerated with ./scripts/sync-changelog.sh.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 23:27:33 -07:00
Hunter Bown 725d81317e Merge branch 'main' into feat/docs-sandbox-web 2026-08-19 23:26:03 -07:00
Hunter Bown 98d3001b3b docs: add the missing release-note receipt for #5517
PR #5517 shipped docs/constitution and docs/runtime-api onto the typed
dictionary spine and merged into this release, but the 0.9.10 Contributors
section never named it. The new feature-release-note gate catches this on
any branch whose commits reference #5517 — including Lstarsky0's follow-up
PR #5520, where the honest fix is to record the receipt rather than delete
the contributor's reference to their own earlier work.

The entry states what actually landed: 28 inline `isZh` branches replaced
by typed English and Chinese dictionaries held to key and token parity,
with the other sixteen locales keeping the English fallback.

crates/tui/CHANGELOG.md regenerated with ./scripts/sync-changelog.sh.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 23:24:21 -07:00
Hunter Bown 9ff4116ed1 test(web): assert first-launch meaning instead of a frozen sentence
The getting-started contract corroborated the site's keyless-launch claim
by requiring docs/GUIDE.md to contain one exact sentence, "On first launch,
Codewhale opens with a recommended working agreement". The honest first-run
work replaced that flow, so the sentence is gone and the assertion pinned
obsolete copy rather than product truth. The web suite fails identically on
main and on any branch built from it.

Assert what the docs actually owe this step: inside the First Launch
section, that the first launch asks only for the decisions the install
still needs, and that the provider step keeps an explicit offline route.
Together those are what make "starts without any API key" true, and they
survive rewording. The section slice matches heading-level hashes only, so
the `# npm` comments inside the fenced install snippets cannot end it.

Evidence: `npm test` 311/311 pass (was 310/311), `npm run lint` clean,
`npx tsc --noEmit` clean, `npm run check:facts` and `check:docs` pass.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 23:24:20 -07:00
Hunter Bown f8b1f0b0f9 fix(tui): clear the all-target clippy gate
`cargo clippy --workspace --all-targets --all-features --locked -D warnings`
failed on four mechanical lints, which kept the release gate red while the
code itself was correct. Each fix is narrow and behavior-preserving:

- `qa_harness::watchdog::progress` collapses its nested `if` into the
  edition-2024 let-chain clippy suggested; the label is still only taken
  when the lock is free and only rewritten when it actually changed.
- The raw-stream ceiling invariants move into `const { assert!(..) }`
  blocks. These compare two constants, so the check now fails the build
  rather than a test run — strictly stronger evidence, same guarantee.
- The 80-column language-picker test drops a redundant closure.
- The copy-answer projection fixture is only iterated, so it is an array
  rather than a `Vec`.

Evidence on this tree: `cargo fmt --all`; the exact all-target clippy
command exits 0; the four owning tests pass
(`raw_stream_ceiling_clears_every_downstream_bound`,
`bounded_output_keeps_last_two_thousand_complete_lines`,
`every_option_is_visible_at_80_columns`,
`completed_assistant_answer_projection_excludes_reasoning_tools_and_status`).

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 23:24:03 -07:00
Lstarsky0 692b8a122d docs(changelog): record the #5517 dictionary-spine phase
#5517 merged after v0.9.10 was cut and left no changelog entry, so
check-feature-release-notes.sh fails on any feature commit whose message
mentions it, including this branch's. crates/tui/CHANGELOG.md is the
sync-changelog.sh slice of the same entry.

Signed-off-by: Lstarsky0 <59827030+Lstarsky0@users.noreply.github.com>
2026-08-20 14:21:31 +08:00
Hunter Bown fbccf6c167 Merge branch 'main' into feat/docs-sandbox-web 2026-08-19 23:08:21 -07:00
Hunter Bown d53617f1e6 test(tui): wait for composer readiness
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 23:07:04 -07:00
Hunter Bown 22bf21dbca docs: align 0.9.10 public product truth
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 23:05:31 -07:00
Hunter Bown 3b0a58a4b1 test(tui): align Windows release contracts
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 22:55:25 -07:00
Hunter Bown f184ef4ecb fix(tui): make first-run setup honest and lightweight
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 22:48:26 -07:00
Hunter Bown ab55875359 fix(release): require feature note receipts
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 22:45:27 -07:00
Lstarsky0 7f1fb21bbe feat(web): move docs/sandbox and docs/web onto the dictionary spine (#5337)
Both pages carried 14 and 15 isZh branches; both are now zero. Same
shape as #5504 and #5517: an en and a zh dictionary per page, wired
through types.ts and index.ts, and both files added to
check-locales.mjs's OPTIONAL_FILES so zh is held to key and token
parity while the other sixteen locales fall back to English.

The config values the policies paragraph typesets as inline code, and
the commands and flags on the browser-client page, stay code-owned in
the page and reach the dictionaries as {token}s, per docs/VOICE.md.

Rendered output is unchanged across all 36 pages (18 locales x 2).
2026-08-20 13:25:05 +08:00
Hunter Bown 1d140927e5 fix(web): settle canceled runtime items
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 22:15:29 -07:00
Hunter Bown f35546859a fix(tui): show truthful workflow lifecycle
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 22:11:31 -07:00
CodeWhale Bot f7896c87f1 fix(ci): satisfy topology checkpoint lint
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-08-19 22:10:00 -07:00
Hunter Bown de823c6726 fix(compaction): make cancellation authoritative
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 22:08:42 -07:00
Hunter Bown 3b221c7a6f fix(tui): keep work visible in compact rail
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 21:58:46 -07:00
Hunter Bown 4d99f30da5 test(tui): remove orphan discovery fixture
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 21:55:16 -07:00
Hunter Bown 5689f0dde8 fix(ci): format slash discovery
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 21:54:00 -07:00
Hunter Bown a6df64af8b feat(tui): make setup a progressive repair guide
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 21:45:10 -07:00
Hunter Bown 3a5f587e00 fix(tui): review saved workflows before running
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 21:39:56 -07:00
Hunter Bown 83299d6b44 fix(tui): make slash discovery approachable
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 21:39:17 -07:00
Hunter Bown 91e62b9c69 fix(tui): persist goal controls before delivery
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 21:33:09 -07:00
Hunter Bown d29a8a9506 test(web): stop freezing README copy
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 21:28:52 -07:00
Hunter Bown 06c5a00ea5 feat(tui): start with only required setup
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 21:15:56 -07:00
Hunter Bown 78d0a2e392 fix(tui): attach remote control during active turns
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 20:56:29 -07:00
Hunter Bown 5444ab10bb feat(plugins): activate trusted declarative adapters
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 20:55:44 -07:00
Hunter Bown de062ebd16 fix(tui): restore Agent truth after compaction
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 20:22:56 -07:00
Hunter Bown f63cd610d4 fix(tui): make agent footer controls actionable
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 20:20:22 -07:00
Hunter Bown 99a8f790c3 fix(tui): review workflows before execution
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 20:15:05 -07:00
Hunter Bown 07fc195675 docs: refresh translated READMEs
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 20:08:27 -07:00
Hunter Bown fbaf63b519 docs: rewrite the project introduction
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 20:06:33 -07:00
Hunter Bown fcb716c3d0 perf(tui): decouple ambient and interactive cadence
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 20:06:15 -07:00
Hunter Bown 0426dbae9b refactor(tui): simplify the first screen
Move route identity to the phase rail, remove obsolete orchestration advertising, and replace the launch lecture with one localized invitation.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 19:59:24 -07:00
Hunter Bown 19ba7de2b8 docs: invite contributors to Whale Brothers
Make the WeChat community path explicit alongside Discord.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 19:10:27 -07:00
Hunter Bown 352f350c95 docs: add a direct community contact path
Invite contributors to discuss ideas on Discord or WeChat before opening an issue or pull request.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 19:09:37 -07:00
Hunter Bown 57d9272225 docs: retire harness as Codewhale product language
Describe Codewhale as a coding agent throughout the public landing copy while retaining DeepSeek Harness only as the external integration proper name.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 19:06:24 -07:00
Hunter Bown 65daa60a42 fix(tui): keep provider onboarding calm before catalog
Show the typed provider decision and offline action before opening the detailed provider picker on fresh installs.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 19:01:25 -07:00
Hunter Bown 9607e06e81 fix(ci): remove stale acid-bath test references
Stop invoking deleted brittle harnesses, drop the retired PTY nextest target, and preserve Agent Mail error and redaction behavior without duplicate branches.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 18:52:05 -07:00
Hunter Bown 90449f6a80 feat(tui): replace first-run lecture with calm onboarding
Ask only required decisions, preserve real CLI prompts, keep offline state visible, seed the first task, and route optional web continuation through /rc.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 18:46:58 -07:00
Hunter Bown 198dff31c7 feat(tui): add a visible "Copy answer" action for the final assistant reply
The pager already carries per-surface copy payloads (`c`/`y` body, `e`
turn handoff), but no path copied just the final assistant answer: Ctrl+Y
copies whatever cell is focused and `c` in a details pager copies the
rendered body with glyph/label scaffolding.

Add an answer-only payload to the existing mechanism:

- `HistoryCell::is_completed_assistant_answer` is the typed "final answer
  only" projection in the history module: only a completed (non-streaming)
  assistant cell qualifies, so reasoning/thinking blocks, tool calls and
  results, sub-agent transcripts, runtime status, and archived-context
  seams are excluded by construction.
- Serialization reuses `history_cell_to_clipboard_text`, the canonical
  clean-copy path that returns the authored assistant Markdown.
- `PagerView`/`PagerPage` gain `with_copy_answer`; `a` copies it with a
  visible `a copy answer` footer hint (inert where no answer is attached).
- Attached on the Turn Inspector pages (each turn's latest completed
  answer, so the latest page carries the latest completed answer), on the
  raw tool-details pager when the focused cell is a completed assistant
  answer, and on the last-message pager.

Clipboard writes stay on the existing ViewEvent::CopyToClipboard ->
app.clipboard path; no new clipboard mechanism.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 18:29:10 -07:00
Hunter Bown d42a8fdc90 merge: converge v0.9.10 release train onto main
Preserve merged contributor work from PRs #5517, #5514, and #5509 while integrating the verified v0.9.10 runtime, Agent Mail, context recovery, onboarding, documentation, and release-hardening train. Remove one obsolete page-source copy assertion exposed by the completed dictionary migration.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 18:27:34 -07:00
Hunter Bown fd43e5eee8 Merge pull request #5509 from SparkofSpike/codex/v099-restore-independent-title
fix(tui): restore /title as an independent terminal window title (#5430)
2026-08-19 18:15:25 -07:00