Skip to content

release: Codewhale v0.9.12 candidate - #5884

Merged
Hmbown merged 131 commits into
mainfrom
release/v0.9.12
Sep 5, 2026
Merged

release: Codewhale v0.9.12 candidate#5884
Hmbown merged 131 commits into
mainfrom
release/v0.9.12

Conversation

@Hmbown

@Hmbown Hmbown commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Codewhale v0.9.12 release candidate

This branch is the frozen 0.9.12 source: the local integration line that produced
the dogfood builds since 2026-09-03, rebased-by-merge onto public main
(c042ea2, including #5882 and #5873), plus the two CI candidates
(hermetic Buildkite home + real npm-wrapper execution; bounded CLI telemetry
persistence outcomes).

What ships

Local proof on this exact line (macOS, offline cargo)

  • fmt + CI Clippy clean; nextest --workspace --all-features --profile ci:
    14,287 passed, 0 failed, 14 skipped (at 20d9326; the only later commit is
    changelog text and its generated derivatives).
  • telemetry-ingest: 171 passed; website vitest 407 passed; website production
    build clean; browser QA EN/ZH at 390/1280.
  • Stamped build 171acee6 installed and verified from a fresh shell; installed
    automation-editor and work-bar PTY acceptance rerun on it.
  • check-versions.sh OK (workspace/npm/binary 0.9.12, lock in sync);
    check-ohos-deps.sh OK; npm wrapper smoke passed against local assets;
    publish-crates.sh dry-run package contents verified;
    check-feature-release-notes.sh v0.9.11 HEAD OK (73 references).

Not claimed here

Hosted CI on this exact head (this PR provides it), the release-candidate
artifact matrix (dispatched separately), notarized desktop bundles, live
PostHog delivery, or any deployment.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Bs5x11yXAg3sJ4giZf4krF

No-Issue: release candidate; the issue references live in CHANGELOG.md.

Paulo Aboim Pinto and others added 30 commits September 3, 2026 10:01
…es to command contract

- CommandPluginContext: object-safe synchronous facet covering registry reads/mutations, async-bridged install/update/uninstall with sync receipts (D11), export, legacy scan, kimi managed import, and marketplace
- Portable DTOs: PluginSummary/Detail/Diagnostic/McpServerDetail, mutation outcome+receipt, export receipt, legacy tool+scan, managed candidate+scan, marketplace catalog/candidate/add/state, suggestion
- PLUGIN = 1 << 10 capability bit and one plugin envelope slot with with_plugin builder
- Contract tests: object safety, field/variant closure, sync receipt outcomes, exact-hash mismatch, managed/marketplace portability, envelope transport, duplicate-slot rejection, bit stability
- Contract boundary gate green; workspace compiles; fmt clean; 23/23 contract tests pass

Generated with Claude Code

Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
…misleading receipts

Code-review finding: trust/enable/disable/revoke_trust returned a PluginMutationReceipt
with outcome always NoChange, which is semantically wrong (NoChange means 'already up
to date' in the install/update path). The host registry returns Result<(), String>;
the handler renders the action word from its own dispatch arm and re-reads detail
for post-mutation state. Return Result<(), String> — the exact-minimum typed surface.

Generated with Claude Code

Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
…tricted exposure

- PluginAdapter implements CommandPluginContext against App: registry reads (summaries/detail/diagnostics/validation/suggest), registry mutations (trust/enable/disable/revoke with skill-cache + active-skill side effects), async-bridged install/update/uninstall with synchronous receipts (D11), export, legacy scan, kimi managed scan/install, marketplace state/add/remove/install (incl. builtin official catalog)
- CommandContextBundle grows to eleven slots with plugin; contexts() exposes plugin only for PLUGIN capability
- Portable conversion helpers: summary/detail/mcp server/diagnostic/marketplace diagnostic/mutation receipt/export receipt/legacy tool/marketplace candidate/catalog
- kimi_import: scan_managed_plugins_portable wrapper; group modules made pub(crate); plugin_network_policy/run_async exposed
- Adapter tests: host-data projection, registry mutation + suggest behavior, restricted exposure (3 tests)
- Full TUI lib suite 11395/0; boundary gate green; fmt clean

Generated with Claude Code

Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
…acet parity

- mod.rs: portable plugins() dispatch consuming workspace/presentation/plugin facets; legacy shell builds bundle and delegates (Phase 6 replaces with from_contract)
- render.rs: render_bundle_detail/escape helpers consume portable PluginDetail + presentation facet
- legacy.rs: consumes PluginLegacyScan; kimi_import.rs: consumes PluginManagedScan; marketplace.rs: consumes PluginMarketplaceState with localized plan text
- Presentation facet: key_to_plugin_message_id maps all 52 plugin keys; source_path carried for marketplace provenance
- Contract: PluginSuggestion.state_label, PluginDetail.inventory_summary, PluginMarketplaceCatalog.source_path, reload() facet method
- Tests: 18 plugin tests converted to the portable shell path; full parity preserved
- Full TUI lib 11394/0; contract 23/23; boundary gates green

Generated with Claude Code

Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
…nk both frontiers

- PluginsCmd implements contract RegisterCommand<CommandResult> with exact WORKSPACE | PRESENTATION | PLUGIN; PluginsCommands group registers via ContextualCommand::from_contract
- plugins_contextual destructures facets with safe missing-facet errors; transitional App shell now test-only
- Public dispatch tests: exact capability set, undeclared facets absent, public seam dispatch, no-panic matrix (3 tests)
- Remove plugins from PENDING_GROUPS and scripts/command-migration-topology.json frontier (same commit)
- Migration fixture updated for six-group frontier; feat015 legacy-assertion test adds plugin to MIGRATED
- All gates green: contract 23/23, TUI lib 11397/0, migration/boundary/CI fixtures + live gates, fmt, diff hygiene

Generated with Claude Code

Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
- Fix clippy findings in FEAT-020 plugin files: identical if blocks (contract.rs), useless as_ref/map (marketplace.rs), useless format + redundant closure (render.rs), manual unwrap_or_default (mod.rs), collapsible if (contract tests)
- Boy Scout: repair pre-existing lints outside FEAT-020 scope (computer-use linux.rs &PathBuf->&Path, config catalog tests contains()/type_complexity)
- cargo clippy --workspace --all-targets --locked -- -D warnings exits 0 with zero warnings

Generated with Claude Code

Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
rollback_hash_mismatch called crate::plugins::install::uninstall directly
from the portable handler, a TUI-owned executable dependency that violates
the D1 boundary and would break the FEAT-040 physical move. Add
CommandPluginContext::uninstall_path(name, plugins_dir) - a file-level
rollback removal with no registry resolution or skill side effects - and
route the content-hash-mismatch rollback through it. The host adapter owns
the crate::plugins call.

Verified: contract 23/23, plugins group 18/18, plugin-scoped TUI suite
205/0, clippy -D warnings clean.

Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
Append PLUGIN after the capability identities already published by FEAT-021/022 and preserve current-main behavior for marketplace-backed suggestions, one-shot reload nudges, canonical review tokens, and shared marketplace document validation. Do not resurrect the retired computer-use builtin catalog.

Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
Scope symlink refusal tests to Unix, where symlinks are actually created, so Windows all-feature test compilation remains warning-free. Scan all visible trust-command candidates in the binary acceptance harness so a retained partial command cannot hide the complete canonical review token.

Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
Replace the stale 0.0.0.0 warning expectation with an explicit non-loopback rejection check and verify that the error explains the enforced loopback-only boundary.

Signed-off-by: Paulo Aboim Pinto <paulo.aboim.pinto@gmail.com>
…d rail_panel preference

The persisted rail_panel preference (files, context, git, price...) always
has rows to paint, so letting it auto-open made the dock a permanent band
under the composer. Without an explicit pick (cycle, tab click,
/workbar <view>) the strip now opens only for work — live agents, the
to-do list, background jobs (RailPanel::AUTO_ORDER).

Gates: cargo fmt --check clean; cargo clippy --workspace --all-targets clean;
tui lane 11668 passed / 0 failed / 13 skipped (work_surface filter 138/138
before fmt-only reflow).
…plus receipt (harvested)

Harvest the sound core of the interrupted wt-p2a-store slice (detached at
4380592; applied cleanly onto main, fmt reflow only). What it does:

- Delete the TuiPrefs/KeybindPrefs second store (theme, font_size,
  keybinds). tui.toml is no longer read as a source of truth.
- On Settings::load, fold an existing tui.toml once: settings.toml is
  silent on theme -> adopt it; settings.toml names theme explicitly ->
  it wins and the disagreement is recorded; any other key (font_size,
  [keybinds], ...) is quarantined BY NAME, never dropped.
- Original bytes are renamed to tui.toml.migrated-<date> (never clobbering
  an earlier backup), never deleted. Folded values are persisted into
  settings.toml so the next launch cannot lose them.
- Startup surfaces one localized status line (folded / kept / quarantined
  message keys added to all 15 locale packs) instead of the old
  malformed-tui.toml warning.

Why not repaired: two stores disagreeing about theme with no stated
winner is the exact disease Phase 2 names; a sync layer between them
would preserve it.

Still open in 2A: SettingDef coverage for every persisted field,
config_ui.rs/schemaui deletion, /config opening the canonical ConfigView.

Gates: cargo fmt --check clean; cargo clippy -p codewhale-tui
--all-targets clean; tui lane filter 'settings' 145 passed / 0 failed.
Ten settings.toml keys had a field but no declaration: tool_collapse_mode
and max_input_history (canonical names behind the tool_collapse /
max_history rows), default_provider, sandbox_mode, provider_models,
enabled_models, pinned_models (route-picker memory), and three one-way
internal flags (feature_intro_shown, yolo_deprecation_shown,
behavioral_tip_impressions). They get hidden defs (ui: None) grouped at
the end of SETTINGS_SCHEMA — declaration order stays render order, and
the row round-trip contract only iterates rows.

New test every_persisted_settings_field_is_declared_in_the_schema forces
every Option/collection present, serializes, and asserts each key
resolves via setting(); it pins the ten hidden names so a future
skip_serializing silently dropping a key fails loudly instead of
passing vacuously.

Gates: cargo fmt --check clean; schema filter 124 passed / 0 failed.
… open the canonical ConfigView

Delete crates/tui/src/config_ui.rs (2192 lines: a second settings editor
with its own TUI suspend-resume path, browser web editor, local HTTP
session, draft/commit event pump, and a parallel value-enum system) plus
the schemaui dependency and the tui/web/json/toml feature shims that only
existed for it. Cargo.lock drops the schemaui graph; the workspace
jsonschema pin stays for workflow-js.

Why not repaired: the canonical ConfigView already edits, validates, and
persists every row with provenance facts; a second editor doubles every
future settings change and its web session owned a poll tick and an event
loop branch. Route memory (provider_models/enabled/pinned) was already
ConfigView-adjacent, never schemaui-owned.

Migration of the last consumers, all in this slice:
- OpenConfigEditor action and ConfigUiMode are gone; bare /config and
  /config tui|web|native all yield OpenConfigView (mode words accepted
  for muscle memory, unknown words get Usage).
- apply.rs loses the suspend-terminal TUI arm and the web-launch arm;
  apply_command_result and execute_command_input lose the session param.
- event_loop loses the session slot, the per-tick drain, and the 16ms
  poll-floor it forced; handlers lose the threaded params.
- provider_setup keeps presets/rollback/key verification; its
  drain_web_config_events goes with the session.
- ui/tests.rs web draft/commit tests become a ConfigView-only-surface test.

Gates: cargo fmt --check clean; cargo clippy -p codewhale-tui
--all-targets clean; show_config 4/4, config_view 51/51,
provider_setup 7/7.
…(5e, muse-built, reviewed)

Muse agent's 5e slice (wt-p5e-hints@9ce48aa88), cherry-picked onto main
cleanly and reviewed hunk by hunk. Each footer hint carries a stable key
(permission_cycle, mode_cycle, esc_interrupt, enter_again, agent_arrows)
and shows until its binding fires USES_TO_RETIRE=2 times, then renders
the bare state — chip without the chord, or no hint. Counts persist in
Settings.footer_hint_uses (BTreeMap beside behavioral_tip_impressions,
skipped when empty) and hydrate App at startup; recording is best-effort
via transact_opt and abandons the write once retired, so each key costs
at most 2 writes per install and never blocks input. Deliberate
non-recordings kept: control-socket interrupt verb, SendQueuedNow Enter
path.

Landing fix on review: footer_hint_uses had no SettingDef, which the new
2A-ii coverage test caught — added the hidden def plus probe coverage.

Gates: cargo fmt --check clean; cargo clippy -p codewhale-tui
--all-targets clean; full tui lane 11650 passed / 0 failed / 13 skipped.
…sumer (harvested)

Harvest the wt-p2c-theme slice (module already git rm'd there; applied
onto main, fmt-clean). The legacy flat-token module goes with all of it:
file_tree pane chrome now reads UiTheme::for_mode plus the WHALE_BG /
LIGHT_PANEL / panel_bg slots (horizontal-only padding: uniform(1) ate
two rows of a compact pane); history tool-card ink (title, status,
label, value, rail, glyph) is explicit palette constants with the OMP
border rule and the tideline-01 mockup reading documented at each site;
the underwater-retro comment stops naming the dead module.

Why not repaired: a parallel token system beside palette guarantees the
two disagree about the same pixel; the survivor (palette) already had
full theme-registry coverage including Underwater Retro, which stays
selectable and untouched.

Inventory: dropped the two stale FILE lines for config_ui.rs (2A-iii)
and deepseek_theme.rs from the auto-generated tui instructions.

Gates: cargo fmt --check clean; cargo clippy -p codewhale-tui
--all-targets clean; history 194/194, file_tree 9/9, theme 79/79.
…+ Settings ledger)

New canonical rank in codewhale-config (crates/config/src/resolve.rs):
ManagedPolicy > CliFlag > SessionOverride > ProjectConfig > UserConfig >
Default. resolve_scalar picks the highest-ranked layer for a key
(scalars replace, never merge); merge_maps/merge_provenance deep-merge
map layers with highest-rank-wins per leaf; SecretRef carries a secret's
layer plus its reference (env:/keychain:) and never its value. Seven
unit tests pin the rank, the replace rule, the merge, and stable names.
Layers with no producer today (policy files, project settings) abstain
by construction — the rank is the contract, not scaffolding.

Settings carries the ledger: a runtime-only BTreeMap<canonical key,
Layer>. Load marks keys the document named as UserConfig (rest read as
Default); set() marks the canonical key SessionOverride on success only
(a rejected value leaves the prior layer in force), via a canonical_key
map over every accepted spelling — merged the duplicate ui_theme arm so
the map stays truthful — pinned per-spelling by
set_marks_session_provenance_for_every_spelling. provenance() resolves
aliases to the row's key. /settings prints a footer naming the owned
keys (from settings.toml: … / session override: …), covered by
display_names_user_configured_keys.

Boundary, stated plainly: ConfigView rows already show their source
layer from hand-built facts; this slice does not replumb App's scattered
live session state into a unified overlay, so no row changes authority
yet. CLI flags (2D) arrive with a real producer and take the CliFlag
layer through this same ledger and reader.

Gates: cargo fmt --check clean; cargo clippy -p codewhale-tui -p
codewhale-config --all-targets clean; tui settings:: 92/92, config lane
633/633, resolve:: 7/7.
Parity for the config command family (get/set/unset/list/path/import/
export already existed):

- config edit opens the file in VISUAL/EDITOR (else vi), printing the
  path first; a failing editor fails the command.
- config doctor is read-only: unknown keys warn (the loader preserves
  them, never applies them — quarantine thinking at the CLI), empty
  secrets and non-http(s) endpoints fail with names but never values.
- config dump prints the effective config as TOML via the new
  ConfigToml::redacted_toml_value (name-based redaction, nested tables
  inherit sensitivity; honest doc comment: unrecognized key names pass
  through, so pasting deserves a glance).
- Global repeatable --set KEY=VALUE applies after load, in memory, for
  every subcommand, and is never saved. config set/unset/import refuse
  it outright rather than persisting a merged document; dump banners
  active overrides.

Owner decision: the program asked for -c, but short -c is --continue.
Renaming an established resume flag for a new override flag would break
muscle memory worse than a long-only --set, so --set it is — stated in
the flag help.

Gates: cargo fmt --check clean; cargo clippy --workspace --all-targets
clean; cli lane 339/339 (incl. 6 new: parse matrix additions, doctor
clean/warn/fail, overlay memory-only + malformed spec, mutating-command
refusal); config lane incl. redacted_toml_value test.
Tabs are the dock's primary navigation: AGENTS TODO BACKGROUND FILES
NOTEPAD CONTEXT GIT PRICE. TODO replaces TASKS as the plan-step label —
the Background panel already holds running tasks, so TASKS collided
with it. Setting values stay lowercase (tasks), and /rail accepts todo
and todos alongside; title() is display-only, as_setting() is data.

Golden dock_80x24 updated by hand for the approved change (drift
verified case-and-label only, spacing rebalanced by the renderer);
narrow-dock asserts track the new labels.

Gates: cargo fmt --check clean; cargo clippy -p codewhale-tui
--all-targets clean; work_surface 137/137, rail 85/85.
Providers become data, not files: AccessMethod (OwnedOAuth /
ExternalImport / ApiKey / reserved AcpBridge), OAuthProvider, and a
per-provider params table (issuer, client, scopes, env overrides,
device/token paths, discovery flag) drive one shared device-code core
built on the parameterized device_code.rs transport (20s requests,
64 KiB bodies, 256 B whitespace-collapsed error detail, content-type
named but body never echoed).

xai_oauth.rs sheds its device-code types and login/poll loop (~700
lines) for pending_from_unified, a transitional bridge into the legacy
pending shape the not-yet-unified activation path still consumes;
lib.rs run_xai_device_auth and the TUI event-loop login call the
unified device_code_login(Xai) through it. ChatGPT PKCE, activation
unification, and the per-provider module deletions follow in 3b-ii/iii
— nothing deleted that still has a consumer.

Gates: fmt clean, workspace clippy clean, tui lane 11654/0.
The PKCE browser flow moves out of chatgpt_oauth.rs into the oauth.rs
core, parameterized by the provider table like the device-code flow
before it: authorize path, authorize extras, originator, pinned revoke
path, callback path/ports, and relogin guidance are now rows, not
module constants. One `login(provider)` dispatcher picks device-code or
browser PKCE from the row; the CLI and TUI login call sites (xAI and
ChatGPT) all go through it. chatgpt_oauth.rs keeps only the storage
half (entry format, activation, refresh-on-read, revoke) plus a
pending_from_unified bridge, mirroring the xai_oauth.rs arrangement —
both bridges die with their modules when activation unifies.

Also restored side-effect-trap parity the 3b-i migration dropped: the
unified device transport (discovery, device grant, poll) and the new
form-post client record oauth network/refresh in test builds again, so
"zero external I/O" proofs keep covering the unified path. The §D
security tests ported with the flow: honest originator, malformed
issuer fails loudly, state match, no-echo token errors, dual loopback
family, read-to-header-terminator callback.

Gates: fmt clean, workspace clippy clean, tui lane 11656/0,
cli 339/339, config 634/634, lane 67/67.
…chatgpt_oauth.rs

Deleted modules (3,511 lines total):
- crates/tui/src/xai_oauth.rs (2,546 lines)
- crates/tui/src/chatgpt_oauth.rs (965 lines)

Why:
Per-provider OAuth modules duplicated storage, activation, refresh, and revocation logic.
These now live as provider data rows and unified flows in crates/tui/src/oauth.rs,
parameterized across OAuthProvider {Xai, Chatgpt}.

What replaces it:
- Unified owned storage and activation (activate_login) in oauth.rs.
- Unified refresh and revoke dispatch (refresh_for_provider, revoke_owned_login_locked_with).
- Consolidated provider-first API signatures across config, tui, and UI consumers.
- Unified apply_codewhale_owned_login helper in tui/ui/apply.rs.

Gate results:
- cargo fmt --all -- --check: passed clean
- cargo clippy --workspace --all-targets --locked -- -D warnings: passed clean
- scripts/dev-test.sh tui: 11,656 passed, 0 failed, 13 skipped
- scripts/dev-test.sh tui-integration: 280 passed, 0 failed, 0 skipped
- scripts/dev-test.sh tui-cucumber: 16 passed, 0 failed, 0 skipped
- scripts/dev-test.sh cli: 339 passed, 0 failed, 0 skipped
- scripts/dev-test.sh config: 634 passed, 0 failed, 1 skipped
- scripts/dev-test.sh lane: 67 passed, 0 failed, 0 skipped
- cargo test --workspace --locked: passed clean (all workspace tests passed)
…s saved

Founder decision 2026-09-03 (model B): R1's 'fleet names are out' gets one
visible carve-out — the fleet is the live collective (who is working right
now); saved configurations are teams and roles. There is no /team command.

User-visible changes (one locale-consistent landing, all 15 packs):
- Live collective keeps the fleet word: /fleet command + help, fleet
  workers, fleet runs, fleet setup, 'N in the fleet' status lines.
- Saved configuration becomes team: 'Your team `x`', 'Added to the team',
  'new personal team', 'Team `Default`', 'No team selected', 'saved teams',
  route-save and profile-save receipts.
- New 'teams' verb on /fleet (fleets/saved/manage keep working) switches
  between named saved teams.

Deletions (not worth keeping):
- /loadout and /party command aliases and roster verbs: joke spellings,
  unconventional per R1; nothing references them.

Deliberately kept (flagged wire, D/3a owns the deep rename):
- fleets/<name>.toml, .codewhale/fleet.jsonl, [fleet] tables,
  fleet.exec.max_spawn_depth, 'codewhale fleet' CLI, lane fleet.* ids,
  fleet: receipt labels, {fleet} placeholders, store 'fleet file not
  found' errors. Renaming any of these touches persisted user data or
  scripts, so they stay until the staged deep rename.

Gates: fmt clean; clippy workspace -D warnings clean;
config 634 passed 1 skipped; lane 67/67; tui 11656 passed 13 skipped;
cli 339/339; tui-integration 280/280; tui-cucumber 16/16.
Founder ruling R3: codewhale-cu is the canonical computer-use plugin, and it
ships inside the product rather than as a sibling repository. This merges
/Volumes/VIXinSSD/CW/codewhale-cu into plugins/computer-use in the engine's
real bundle format (Agent Plugins v1 plugin.json + sibling mcp.json,
net.codewhale extension) — the format the incoming tree already targeted.

Deleted, with reasons:

- plugins/computer-use/server/server.py + test_server.py (697 lines): a
  stdlib-only macOS/Linux MCP server offering screenshot, click, type_text,
  press_key, screen_size — and no scroll on macOS. The Node implementation
  supersedes it on every axis (38 tools, four platforms, accessibility-first
  observation with pixel fallback, recording, ssh/hdc remote computers), so
  keeping both would ship two computer-use servers behind one plugin name.
- The invented codewhale-cu.json packaging format and install.mjs never come
  across: the engine discovers, reviews, and installs the bundle itself.
- Smoke receipts stay out of the tree (gitignored) — they are per-run local
  evidence, not source.

The bundle's own suites now run in CI beside the chat bridges, which already
run dependency-free `node --test` bundles; they cover the manifest contract,
the registry, the exec/ssh transport, the four backends, and the MCP stdio
protocol. The in-repo Rust guard test moves with the bundle: node/server.mjs
instead of python3/server.py, both skills present, and every mcp.json arg
proven to resolve inside the plugin root.

The codewhale-apps binding (packages/desktop-use, PR #181, 40033659) is
repointed deliberately in the same slice, per R3: it imports
<plugin>/src/transport.mjs, which the merge preserves, so only the default
checkout path changes (CODEWHALE_CU_PLUGIN_PATH overrides it). That repo's
suite is green at 7/7 against the new location.

Storage and protocol identifiers are untouched on purpose: the ~/.codewhale-cu
state directory, the remote agent path, CODEWHALE_CU_STATE_DIR, and the MCP
serverInfo name all keep their spelling — those change only through an
explicit migration.

Gates: cargo fmt --all -- --check clean; cargo clippy --workspace
--all-targets --locked -D warnings clean; actionlint on ci.yml reports the
same 12 pre-existing shellcheck notes as HEAD (all in codes CI ignores) and
none in the new step. Lanes: tui 11656 passed/13 skipped, tui-integration
280/0, tui-cucumber 16/0, cli 339/0, config 634 passed/1 skipped, lane 67/0.
Plugin suite: 31 tests run, 31 passed, 0 failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013r9fgwt1HYymrB7iZGSkCm
… root

`DiscoveryConfig::builtin_plugin_dirs` and `PluginScope::Builtin` have existed
since plugin discovery landed and had no producer: every construction site
passed an empty list, so `plugins/computer-use` reached nobody who had not
cloned the repository. This is that producer, not a second install path —
installed bundles still arrive through plugins::install, and discovery, trust,
and enablement are untouched.

The bundle is embedded with include_str! (the pattern locale packs and the
mobile client already use) and written under $CODEWHALE_HOME/builtin-plugins,
so one binary carries it to npm, tarball, cargo install, and brew without any
of those channels learning what a plugin file is. Sixteen runtime files; the
tests, smoke script, package.json, and README stay out because nothing at
runtime reads them.

Three properties the implementation defends, each with a test:

- **Materializing is not enabling.** A written bundle is disabled and
  NeverReviewed like any other, because the registry enables only what
  state.json says. Computer use can drive the desktop; it waits to be
  reviewed. `/plugin list` shows it as `builtin · not-reviewed`.
- **Read-only diagnostics stay read-only.** `doctor` and `setup status` run
  this on every startup and must not bring a home into existence
  (tests/integration/diagnostic_read_only.rs). Materializing only into an
  existing home keeps that promise; the first version of this change broke
  all three sealed-home tests, which were right.
- **A partial tree is never discoverable.** The bundle stages in a sibling
  directory and is swapped in, and the stamp that marks it current is written
  last. A stamp mismatch — what an upgraded binary sees — forces a rewrite; an
  in-place edit alone does not, so debugging the bundle is not fought.

Not memoized on purpose: the root is derived from $CODEWHALE_HOME, and caching
it process-wide would pin whichever caller ran first. Steady state is one stamp
read. hex_digest is reused from plugins::manifest rather than rewritten.

Two existing tests moved with the behavior: `/plugin list` now legitimately
shows two bundles and asserts the built-in row, and the ambient-roots test now
asserts that no *ambient* plugin loaded rather than that the registry is empty.

Evidence: the new discovery test fails without the context.rs wiring
("the built-in computer-use bundle must be discovered") and passes with it.
Against the real debug binary: an absent home stays absent after `doctor`, an
existing home gets the bundle, and the materialized copy answers a real MCP
stdio handshake with 38 tools.

Gates: fmt clean; clippy --workspace --all-targets --locked -D warnings clean;
tui 11659 passed/13 skipped, tui-integration 280/0, tui-cucumber 16/0,
cli 339/0, config 634/1 skipped, lane 67/0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013r9fgwt1HYymrB7iZGSkCm
Founder asked for a 0.9.12 build to test. This bumps the version everywhere
`scripts/release/check-versions.sh` audits, so the tree is internally
consistent rather than half-bumped: workspace `Cargo.toml`, all 66 internal
`codewhale-*` path-dependency pins across 14 crate manifests, `Cargo.lock`,
the npm packages and both lockfiles, the VS Code extension, the remote-smoke
`RELEASE_TAG` default, and the generated `web/lib/facts.generated.ts`.

The changelog's `[Unreleased]` section already described 0.9.12 work, so it is
promoted to `## [0.9.12]` with a fresh empty `[Unreleased]` above it and the
compare links added — the mechanical release-notes move, not a judgement about
scope. One new entry records that computer use now ships with the binary.

`check-versions.sh` reports `Version state OK: workspace=0.9.12, npm=0.9.12,
npm-binary=0.9.12, lockfile in sync.` Its remaining output is the pre-existing
advisory about already-merged commits in the v0.9.11..HEAD range that never got
release-note receipts; that predates this commit and still blocks the release
paths until someone writes them.

This is a version bump, not a release: no tag, no push, no prepare-release.sh.

Gates on the bumped tree: fmt clean; clippy --workspace --all-targets --locked
-D warnings clean; tui 11659 passed/13 skipped, tui-integration 280/0,
tui-cucumber 16/0, cli 339/0, config 634/1 skipped, lane 67/0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013r9fgwt1HYymrB7iZGSkCm
…umber hang, mobile smoke, release receipts)

- Windows warnings (§5.1): restructure link and error bindings in
  document.rs and runtime_api/tests.rs so variables exist only on targets
  that use them. Note: Windows CI cannot be run locally on this host.
- macOS PTY acceptance (§5.2): in plugin_e2e_acceptance, iterate over all
  marker match indices in review_confirmation_in_text rather than only the
  first, preventing false stalls on the composer's typed command echo.
  Full 31/31 cucumber tests passed.
- Mobile smoke (§5.3): update scripts/mobile-smoke.sh Test Group 3 to
  assert loopback security warning, matching the loopback-only hardening
  from e80be8c. 9/9 passed.
- Release-note receipts (§5.4): cite genuine issues (#5533, #5831, #5825,
  #5829, #5801, #5815, #5826, #5286, #5809, #5778, #5725, #5701, #5712,
  #5747, #5782, #4394) in CHANGELOG.md; filter commit-body scrape noise
  (#000, #1, #24, #26, #1834, #142352) in check-feature-release-notes.sh.
  check-versions passes in strict release mode (71 receipts checked).

Evidence:
- cargo nextest run --workspace --all-features --locked:
  14229 passed; 0 failed; 15 skipped
- cargo clippy --workspace --all-targets --all-features --locked -- -D warnings:
  clean (0 warnings)
- cargo test --workspace --all-features --doc --locked:
  3 passed; 0 failed; 8 ignored
- cargo test -p codewhale-tui --test cucumber --features long-running-tests --locked:
  31 passed; 0 failed; 0 ignored
- scripts/mobile-smoke.sh:
  9 passed; 0 failed
- scripts/release/check-versions.sh --require-dated-release:
  Feature release-note receipts OK (71 linked issues); Version state OK
- Python policy gates:
  check-provider-registry, check-command-crate-boundaries,
  check-dead-code-budget, check-runtime-contract-budget,
  check-readme-translations, check-tui-locale-parity all PASS
…room, fleet roles

Launch / splash
- The launch stage now samples the ambient clock and asks for frames while
  the mark surfaces, the card dissolves, or the water is alive. Before, the
  only clock driver was the transcript widget, which the launch screen never
  builds: surface_progress stayed at 0 and the braille whale was painted in
  the field colour forever (#1), and the ocean ramp / ambient life were never
  wired into the stage (#2, #3). The stage paints the same ramp and life the
  transcript does; the life field ends at the composer dock.
- Recent rows resume (#4): AppAction::LoadSession never dismissed the launch
  stage, so a resumed session loaded under a still-visible card.
- Tab cycles the mode on the launch screen (#5): ModeCycle is AnyShell. The
  card's rule now reads `model (effort) · mode · permission` so the cycle is
  legible and matches the posture bar (#7).

Work bar
- One-time migration of a persisted `work_surface_placement = "top"` to
  `bottom` (#9), recorded in `work_surface_bottom_migrated`.
- Idle scheduled automations no longer count as work that auto-opens the
  dock (#10); live shells, tasks, or a running automation still do.
- ORDER / AUTO_ORDER lead with TODO, then AGENTS (#11). The dismissed dock
  re-opens on new work in any auto view, and `← for agents` works from the
  TODO view. Golden dock_80x24 re-blessed for the order.
- The posture bar's live counts are click targets that open the dock view
  they count; with nothing live a dim `todo` chip is the bottom affordance.

/mcp, /plugin, Extensions
- /mcp and every MCP action open Extensions → MCP rebuilt from the live
  pool snapshot; the text pager and its formatter are deleted (#17#19,
  #25 — the `tools: 0` rows were the passive config snapshot).
- The markdown inline parser honours backslash escapes, so `computer\-use`
  renders as `computer-use` (#21).
- Extensions keyboard follows grokbuild: Tab / Shift+Tab move across tabs,
  including mid-search with the query kept (#22, #23).

Automations
- New AutomationsView (`/automation`, `/automation show <id>`, the dock's
  background row): list + detail, p pause/resume, r run, x cancel the live
  run (via /task cancel), d delete (two-step confirm), Tab list↔detail.
  Every action is the typed command, so receipts match (#14, #16).
  `/automation list` and `print <id>` keep the text forms.

Fleet
- Roles are members. `add_fleet_model` requires a role (NeedsRole); the
  auto-enroll that turned every selected model into a role-less member is
  deleted with its 7 call sites; bare model pins are dropped on read so
  27 → 12 members (#26). Picker ⇧F enrolls under `general`.
- Roster detail is role-first and compact (#27, #28); footer is
  ↑↓ · Enter · Tab workers · f · Esc (#29). `m`, `s` and the model-picker
  shortcut event are removed.

Gates (run):
- cargo fmt --all -- --check: clean
- cargo clippy -p codewhale-tui -p codewhale-config --all-targets: clean
- scripts/dev-test.sh tui: 11658 tests run: 11658 passed, 13 skipped
- scripts/dev-test.sh tui-integration: 280 passed
- cargo test -p codewhale-config --lib: 634 passed; 0 failed; 1 ignored
- budget checks: dead-code 418/425, runtime-contract, provider-registry,
  command boundaries, migration manifest, locale parity (15 packs
  complete), product vocabulary: all PASS
- release build + tmux dogfood at 120x34: whale surfaces, ramp + fish on
  launch, Tab cycles mode, Enter on recent row resumes, dock closed at
  start and bottom/TODO-first when opened, /mcp opens Extensions with live
  tool counts, /automation opens the room, fleet shows 12 role members.
Not run: cargo nextest --workspace, tui-cucumber, web rung (no web change).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
`release: 0.9.12 in the tree` bumped the version but left every derived and
hand-written public surface on 0.9.11, so five web parity tests were red
against a release that had already landed.

- regenerate `web/lib/changelog.generated.ts` from CHANGELOG.md (6 releases)
- bump the source-candidate version in `docs/INSTALL.md` and
  `docs/zh_hans/INSTALL.md`; the "v0.9.10 or earlier" upgrade note is a
  historical fact and stays
- `docs/public-surface-facts.json` sourceCandidate 0.9.11 -> 0.9.12;
  latestPublishedRelease stays v0.9.11 because 0.9.12 is not published
- add the 0.9.12 changelog intro paragraph the dated-release contract
  requires, written from that release's own Added/Changed entries

Gates: cargo fmt --check PASS · cargo nextest run --workspace --all-features
14228 passed / 0 failed / 15 skipped · cargo test --doc PASS (8 ignored) ·
web vitest 385 passed / 1 failed / 386 total · check:facts, check:docs,
check:tokens, check:locales, check:latest-release all PASS.

The one remaining web failure is `keeps current-release website credits in
exact changelog parity`: CHANGELOG.md has no `### Contributors` block for
0.9.12, and `docs/CONTRIBUTORS.md` has no v0.9.12 band. Crediting named
people for a release is a human attribution call, not a mechanical one, so
it is left for the founder rather than guessed from git authorship.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QCLLenseqfQdVaZUBqn9yG
…tuation

`TEXT_DIM` is aliased to `TEXT_HINT` in every theme but Solarized, so a
`MetadataDim` affordance paints the exact grey as the separators around it.
In an idle footer that made the one clickable word indistinguishable from
the dots beside it. Live counts already carry `Active`; the idle case now
carries `MetadataValue` (#B6C0D4, 9.83:1 on chrome vs 6.24:1) so it earns
the same "you can click this" without touching a brand token.

Separators and hints keep `MetadataDim`/`MetadataHint` unchanged.

Gates: cargo fmt --check PASS · clippy -p codewhale-tui PASS · nextest
-p codewhale-tui 11983 passed / 1 failed / 13 skipped, the one failure
being exec_persistent_service::failed_exec_kills_pending_service_and_exits_nonzero
timing out under parallel load; it passes 3/3 in isolation and cannot be
reached by a chrome ink change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QCLLenseqfQdVaZUBqn9yG
CodeWhale Bot and others added 12 commits September 4, 2026 20:26
The required Ubuntu wrapper job previously reported success after only a CNB
location echo; Dependabot and fork PR branches need not be mirrored to CNB.
Run its checkout, setup, Linux dependencies, locked build and install smoke
on heavy pull_request events as well as exact-head workflow_dispatch.
Retain CNB delegation for non-PR/non-dispatch pushes and existing light skips.
Trust classification, dispatch SHA validation, permissions, hosted runner
selection, context names, cache guards and build/smoke commands are unchanged.

Validation:
- Existing release workflow contract: 1 passed / 0 failed, including 10 event
  cases across all 12 wrapper step guards (120 guard assertions).
- Existing CI migration-wiring policies: 11 passed / 0 failed.
- Old-workflow injected control: 0 passed / 1 failed, expected at the heavy PR
  checkout guard; live candidate workflow stayed byte-identical.
- actionlint 1.7.12 with existing CI ignore flags and git diff --check passed.
- Structural YAML comparison preserves every other job/top-level field and
  wrapper metadata; only 11 step guards and delegation prose change.
- npm test && npm run check:web exits 1: root has no test script, zero tests;
  check:web was not reached. No hosted wrapper execution is claimed.

The preceding four-file test-home isolation commit remains untouched.

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

Add closed schema3/notice_version5 ingest and generated CWC contract. Preserve original v1 first-party-only and v2/consent4 semantics, reject mixed policy fields before storage or forwarding, and retain IP/host/operator/content guards. Existing Analytics Engine column positions remain fixed; schema_version distinguishes old consent from new notice metadata. Document the founder's amended default-on/opt-out policy and preserve prior declines. Rust/app preference changes are integrated separately.

Validation: ingest 171 passed, 0 failed across 6 files; TypeScript, facts, 23 documentation topics and git diff checks passed. Original validator rejects the new v3 fixture; current validator accepts v2 and v3. Proposed Rust v3 fixture accepted by ingest; sender proof pending Rust integration. Root npm test && npm run check:web exits1 because no test script exists (0 tests, check:web not reached). No hosted deployment, processor activation or runtime-installed policy change claimed.
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Preserve a566606 authorship and evidence-bounded EN/ZH claims plus all18 numeric-provider copy corrections. Resolve only duplicated earlier opt-in documentation against the founder's later default-on/notice5 policy; retain the incoming Plan/provider-count definitions and the new ingest contract. Prior source gates are frontend407/0 and ingest171/0; this merge does not claim combined website or new Rust policy acceptance. Final integration follows the pending frontend/Rust default-on changes. Root npm scripts are unavailable (0 tests), as recorded in the preceding slice.
407 tests passed across 47 files; production build 785 pages. Lint 0 errors, 2 existing logo warnings; locale/catalog checks passed. EN and ZH inspected at 390x844 and 1280x900; no narrow page overflow and Copy remains in bounds with visible keyboard focus. Local preview only; no hosted CI or deployment claimed.
Usage counting no longer waits for an accepted processor notice. A missing
preference resolves on; a persisted `telemetry = false`, a decline recorded
under the earlier opt-in notice, unreadable privacy state, and the
environment/run kill switches all stay off. Schema 3 carries
`notice_version` 5 in place of `consent_version`; the v1/v2 goldens are
untouched and a new v3 golden pins the envelope.

Disclosure is presentation, never acceptance: headless surfaces print one
stderr line per policy revision and record only that it was shown; the TUI
draws its localized toast (15 packs) and records the same display marker
from the event loop instead of a stray stderr line. Display bookkeeping and
Settings/CLI preference writes share a non-blocking fd lock so a stale
disclosure write cannot erase a concurrent decline; corrupt state is never
replaced with defaults. `config set telemetry false` durably disables and
wipes; `config set telemetry true` deliberately re-enables through the
same two-register Settings transition. `doctor` reports recorded opt-outs
and unreadable privacy state instead of "consent_required".

Gates on this exact tree (macOS, offline cargo):
- cargo fmt --check: pass
- workspace clippy --all-targets --all-features -D warnings: pass
- nextest --workspace --all-features --profile ci: 14284 passed, 0 failed,
  14 skipped (1 non-failing leak observation, hooks executor EOF test)
- focused: config+telemetry libs 687/0/1; CLI/TUI telemetry+localization
  +doctor selection 229/0; real CLI process kill-switch dispatch 5/0;
  TUI integration telemetry_contract 15/0; credential handoff dispatch 3/0
- `npm test && npm run check:web` do not exist in this repository and were
  not run.

No live ingest, PostHog delivery, hosted CI, install, or release is claimed.

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

The website no longer opens a consent sheet. Anonymous counting of page,
docs, install-copy and download totals is on by default; the privacy page
hosts the status and the Turn off / Turn on control, and the footer "Usage
data" link points there. The historical storage key is kept so an opt-out
recorded under the old opt-in policy stays off, unreadable stored state
fails closed, and nothing records an acceptance on the visitor's behalf.
The envelope moves to schema 3 / notice 5 to match the runtime and ingest.

Trust page, FAQ, roadmap, and docs/public-surface-facts.json describe the
default-on 0.9.12 behavior and name the published 0.9.11 release as the one
that asked first. The privacy policy gains an "Anonymous usage counting"
section (effective date September 4, 2026). Screenshot provenance records
the founder capture's visible build, v0.9.12 (15fe698), and the README
image becomes a lossless 1136x615 downscale of that same capture.

Gates on this tree: vitest 407 passed / 0 failed (47 files); tsc clean;
eslint 0 errors (2 pre-existing img warnings); check:locales, check:facts,
check:docs pass; clean `next build --webpack` succeeded. Browser check of
the production build in EN/ZH at 390x844 and 1280x900 passed with no
console errors and no horizontal overflow. Receipt:
docs/operating/website-usage-default-on-receipt-20260904.md. The Rust root
has no `npm test` / `check:web`; they were not run. No deployment claimed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bs5x11yXAg3sJ4giZf4krF
…kend-takeover-20260904

# Conflicts:
#	docs/public-surface-facts.json
vitest in web: 407 passed / 0 failed after regeneration; telemetry-ingest
171 passed / 0 failed; both typecheck clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bs5x11yXAg3sJ4giZf4krF
…ackend-takeover-20260904

# Conflicts:
#	crates/tui/src/tui/widgets/mod.rs
…kend-takeover-20260904

# Conflicts:
#	crates/tui/tests/integration/telemetry_contract.rs
…shot fixes

Credits @yiheng-kkk (#5871, #5873) and @Lfanxing; sets the 0.9.12 heading
to the release date; regenerates crates/tui/CHANGELOG.md via
scripts/sync-changelog.sh and web/lib/changelog.generated.ts.
check-feature-release-notes v0.9.11..HEAD: 73 linked issue references OK;
web changelog parity test 6/6.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bs5x11yXAg3sJ4giZf4krF
Copilot AI lite review requested due to automatic review settings September 5, 2026 04:40
@cursor

cursor Bot commented Sep 5, 2026

Copy link
Copy Markdown

This PR is too large for Bugbot to review. It changes 53,659 lines and 3,818,070 characters. Split the change into smaller pull requests to get a review.

@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

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


Review in progress

This is a large release-candidate PR (474 files, ~64k lines changed, 128 commits). I've split the review across the highest-risk areas called out by the repo's review guidelines:

  • Gather context (PR description, changed files, diff stat vs origin/main)
  • Understand request (code review of release PR)
  • Review provider/model/route safety (config crate, route resolver, provider_lake) — running
  • Review telemetry default-on opt-out changes — running
  • Review CLI update.rs / command-contract facets.rs changes — running
  • Review acp_server / lifecycle_outbox / turn-loop invariant — running
  • Synthesize and post final review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The fleet legacy-cleanup predicate can incorrectly delete valid members, and the telemetry ingest path likely shouldn’t await optional PostHog forwarding on the request critical path.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Release-candidate branch for Codewhale v0.9.12, updating the repo-wide version, website + docs copy, telemetry ingest/persistence behavior, plugin packaging, and a broad set of runtime/TUI fixes and contract updates aligned with the 0.9.12 dogfood line.

Changes:

  • Bump workspace/package versions and refresh generated facts/credits/readmes for the 0.9.12 release candidate.
  • Expand telemetry: default-on disclosure + website counters, ingest forwarding (optional PostHog), schema/goldens, and related contracts.
  • TUI/runtime improvements: shell/help affordances, fleet/team semantics, route/provider tombstones (Antigravity), tool schema additions, and multiple acceptance/golden updates.
File summaries
File Description
web/scripts/facts-lib.mjs Exclude legacy providers in facts
web/lib/whale-tokens.ts Support --light-* tokens
web/lib/release-credits.ts Update release contributor credits
web/lib/public-installer-contract.test.ts Installer contract behavior update
web/lib/public-billing-legal-routes.test.ts Legal updated date assertion
web/lib/page-meta.ts New identity phrase
web/lib/page-meta.test.ts Update identity phrase test
web/lib/llms-txt.ts Add /product to index
web/lib/legal-copy.ts Update privacy/telemetry copy
web/lib/i18n/links.ts Primary/secondary nav split
web/lib/i18n/dictionaries/zh/docs-web.ts Add /rc remote-control docs (ZH)
web/lib/i18n/dictionaries/zh/docs-trust.ts Telemetry wording update (ZH)
web/lib/i18n/dictionaries/zh/docs-fleet.ts Fleet copy adjustments (ZH)
web/lib/i18n/dictionaries/zh/chrome.ts Nav labels + tagline update (ZH)
web/lib/i18n/dictionaries/vi/chrome.ts Nav labels + tagline update (VI)
web/lib/i18n/dictionaries/uk/chrome.ts Nav labels + tagline update (UK)
web/lib/i18n/dictionaries/tr/chrome.ts Nav labels + tagline update (TR)
web/lib/i18n/dictionaries/ru/chrome.ts Nav labels + tagline update (RU)
web/lib/i18n/dictionaries/pt-BR/chrome.ts Nav labels + tagline update (PT-BR)
web/lib/i18n/dictionaries/pl/chrome.ts Nav labels + tagline update (PL)
web/lib/i18n/dictionaries/ko/chrome.ts Nav labels + tagline update (KO)
web/lib/i18n/dictionaries/ja/chrome.ts Nav labels + tagline update (JA)
web/lib/i18n/dictionaries/it/chrome.ts Nav labels + tagline update (IT)
web/lib/i18n/dictionaries/id/chrome.ts Nav labels + tagline update (ID)
web/lib/i18n/dictionaries/hi/chrome.ts Nav labels + tagline update (HI)
web/lib/i18n/dictionaries/fr/chrome.ts Nav labels + tagline update (FR)
web/lib/i18n/dictionaries/es/chrome.ts Nav labels + tagline update (ES)
web/lib/i18n/dictionaries/en/docs-web.ts Add /rc remote-control docs (EN)
web/lib/i18n/dictionaries/en/docs-trust.ts Telemetry wording update (EN)
web/lib/i18n/dictionaries/en/docs-fleet.ts Fleet copy adjustments (EN)
web/lib/i18n/dictionaries/en/chrome.ts Nav labels + tagline update (EN)
web/lib/i18n/dictionaries/de/chrome.ts Nav labels + tagline update (DE)
web/lib/i18n/dictionaries/ca/chrome.ts Nav labels + tagline update (CA)
web/lib/i18n/dictionaries/ar/chrome.ts Nav labels + tagline update (AR)
web/lib/facts.generated.ts Facts regen for v0.9.12
web/lib/facts-drift.ts Drift provider filtering parity
web/lib/docs-theme-contract.test.ts Contrast contract update for themes
web/lib/docs-tasks.ts Install wording update
web/lib/docs-map.ts Add public-surface facts source
web/lib/content/vocabulary.test.ts Install recommendation sourced from facts
web/lib/content/account-entry.ts New sign-in/sign-up page copy
web/lib/bounded-form.ts Delegate bounded reads to helper
web/lib/bounded-form.test.ts Multibyte boundary test
web/lib/bounded-body.ts New bounded body reader
web/components/install-code-block.tsx Record install copy usage
web/components/getting-started-steps.tsx Remove reveal-group attribute
web/components/footer.tsx Add waterline + privacy usage link
web/app/sitemap.ts Add /product route
web/app/[locale]/pricing/page.tsx Add data-usage markers
web/app/[locale]/legal/privacy/page.tsx Embed usage preference control
web/app/[locale]/error.tsx Count error page shown
web/app/[locale]/docs/web/page.tsx Add remote-control section
telemetry-ingest/test/schema-doc.test.ts Schema/doc parity for new fields
telemetry-ingest/test/golden/browser-v3.json New v3 website golden batch
telemetry-ingest/test/golden/browser-v2.json New v2 website golden batch
telemetry-ingest/src/index.ts Ingest forwards to PostHog (optional)
telemetry-ingest/src/datapoint.ts Add schema/privacy + aggregate blobs
telemetry-ingest/scripts/export-product-schema.mjs Export product schema artifact
scripts/test_check_command_migration_manifest.py Update topology expectations
scripts/remote-smoke/setup-vm.sh Default tag v0.9.12
scripts/release/install-dogfood.test.sh macOS identity/alias verification
scripts/release/install-dogfood.sh Ensure byte-identical signed aliases
scripts/release/generate-release-body.test.sh Release-body contract updates
scripts/release/generate-release-body.sh Recommend GH release installer
scripts/release/check-feature-release-notes.sh Skip known issue IDs
scripts/export-design-tokens.py Export LIGHT_* tokens to CSS
scripts/dead-code-budget.json Dead-code budget update
scripts/command-migration-topology.json Remove plugins group
README.md New install guidance for GH releases
README.zh-CN.md Sync install guidance (ZH-CN)
README.zh-TW.md Sync install guidance (ZH-TW)
README.vi.md Sync install guidance (VI)
README.uk.md Sync install guidance (UK)
README.tr.md Sync install guidance (TR)
README.ru.md Sync install guidance (RU)
README.pt-BR.md Sync install guidance (PT-BR)
README.pl.md Sync install guidance (PL)
README.ko-KR.md Sync install guidance (KO)
README.ja-JP.md Sync install guidance (JA)
README.it.md Sync install guidance (IT)
README.id.md Sync install guidance (ID)
README.hi.md Sync install guidance (HI)
README.fr.md Sync install guidance (FR)
README.es-419.md Sync install guidance (ES-419)
README.de.md Sync install guidance (DE)
README.ca.md Sync install guidance (CA)
README.ar.md Sync install guidance (AR)
PRODUCT.md Update product install/media truths
plugins/computer-use/src/remote-runtime.mjs Exec facade for remote runtime
plugins/computer-use/skills/recording/SKILL.md New recording skill docs
plugins/computer-use/plugin.json Expanded plugin metadata/when
plugins/computer-use/package.json Add Node test/smoke scripts
plugins/computer-use/mcp.json Switch MCP server to Node
plugins/computer-use/commands/computer.md Expand /computer command help
package-lock.json Bump npm packages to 0.9.12
npm/runtime-sdk/package.json Bump runtime-sdk to 0.9.12
npm/codewhale/package.json Bump wrapper + binary version
integrations/feishu-bridge/package.json Update qs override floor
extensions/vscode/package.json Bump extension to 0.9.12
docs/AGENT_RUNTIME.md Telemetry default-on harness note
docs/zh_hans/AGENT_RUNTIME.md Telemetry default-on harness note (ZH)
docs/WORKROOM_SECURITY.md Telemetry wording update
docs/TERMUX.md Android/Termux install guidance
docs/HarmonyOS.md HarmonyOS install guidance
docs/GUIDE.md New install-first-launch path
docs/operating/website-terminal-scale-receipt-20260904.md Add visual QA receipt
crates/tui/tests/cucumber/main.rs Add PTY long-running modules
crates/tui/tests/cucumber/active_composer_pointer_pty.rs Update help-hint acceptance
crates/tui/src/tui/work_surface/render/mod.rs Compact header height logic
crates/tui/src/tui/work_surface/interaction.rs Dock dismiss row accounting
crates/tui/src/tui/work_surface/input.rs Agents strip liveness detection
crates/tui/src/tui/views/help.rs Hide Shift+Enter chord when unsupported
crates/tui/src/tui/ui/session_state.rs Stop auto-enrolling model pins
crates/tui/src/tui/ui/motion.rs Work-bar priority over ambient mark
crates/tui/src/tui/tideline.rs New interaction targets/actions
crates/tui/src/tui/subagent_routing.rs Include provider fields in tests
crates/tui/src/tui/shell_job_routing.rs Include origin IDs in tests
crates/tui/src/tui/setup/operate.rs Rename fleet → team in setup copy
crates/tui/src/tui/setup/mod.rs Rename fleet → team in setup copy
crates/tui/src/tui/mod.rs Export new TUI modules
crates/tui/src/tui/markdown_render.rs Implement backslash escapes
crates/tui/src/tui/infoline/tests.rs Switch help hint to /help
crates/tui/src/tui/goldens/dock_80x24.txt Update dock golden output
crates/tui/src/tui/goldens/infoline_work_80x24.txt Update infoline golden output
crates/tui/src/tui/goldens/infoline_work_100x30.txt Update infoline golden output
crates/tui/src/tui/goldens/infoline_work_120x32.txt Update infoline golden output
crates/tui/src/tui/goldens/infoline_work_160x40.txt Update infoline golden output
crates/tui/src/tui/goldens/infoline_startup_80x24.txt Update startup infoline golden
crates/tui/src/tui/goldens/infoline_startup_100x30.txt Update startup infoline golden
crates/tui/src/tui/goldens/infoline_startup_120x32.txt Update startup infoline golden
crates/tui/src/tui/goldens/infoline_startup_160x40.txt Update startup infoline golden
crates/tui/src/tui/format_helpers.rs Rename fleet → team in output
crates/tui/src/tui/file_tree.rs Pane chrome theming adjustments
crates/tui/src/tui/composer_ui.rs Terminal Shift+Enter capability probe
crates/tui/src/tui/composer_chrome.rs Remove unused geometry/hitboxes
crates/tui/src/tui/command_palette.rs Hide unlisted commands
crates/tui/src/tui/automation_panel.rs Add provider fields in tests
crates/tui/src/tui/app/types.rs Codex effort tiers + new actions
crates/tui/src/tools/tui_help.rs Hide unlisted commands in help
crates/tui/src/tools/tasks.rs Add model provider pin fields
crates/tui/src/tools/spec.rs Add origin IDs to tool context
crates/tui/src/runtime_handoff.rs Include origin IDs in messages
crates/tui/src/runtime_api.rs Default model only when no provider pin
crates/tui/src/route_runtime.rs Hard-fail Antigravity routes
crates/tui/src/route_billing.rs Move XAI OAuth checks into oauth module
crates/tui/src/plugins/mod.rs Add builtin plugin module
crates/tui/src/plugins/discovery.rs Ensure only builtin plugins load
crates/tui/src/plugins/context.rs Materialize builtin plugin dirs
crates/tui/src/palette/themes.rs Update theme doc wording
crates/tui/src/operate.rs Add provider fields for keepalive
crates/tui/src/models_dev_live.rs Persist cache write failures
crates/tui/src/model_inventory.rs Use oauth helper for XAI auth mode
crates/tui/src/mcp/oauth.rs Thread explicit scopes through login
crates/tui/src/main/tests.rs Doctor excludes Antigravity
crates/tui/src/hooks.rs Re-export hooks template/allowance
crates/tui/src/fleet/store.rs Compat cleanup for legacy model pins
crates/tui/src/fleet/exact.rs Update Codex tier mapping test
crates/tui/src/external_credentials.rs Remove unused external open helper
crates/tui/src/exec_agent.rs Add reasoning-only config + flush outbox
crates/tui/src/credentials/tests.rs Update oauth module references
crates/tui/src/core/engine.rs Add reasoning-only recovery config
crates/tui/src/config/models.rs Antigravity defaults to non-runnable
crates/tui/src/commands/groups/core/setup.rs Update /setup help copy
crates/tui/src/commands/groups/core/provider.rs Antigravity tombstone gating
crates/tui/src/commands/groups/core/hooks.rs Add /hooks edit action
crates/tui/src/commands/groups/core/core.rs Stop auto-enrolling model pins
crates/tui/src/commands/groups/config/mod.rs Import-claude usage docs
crates/tui/src/client/responses.rs Use shared SSE helpers
crates/tui/src/client/chat/tests/stream_decoder.rs Use shared SSE decoder types
crates/tui/src/client/anthropic.rs Use shared SSE helpers + config key fix
crates/tools/Cargo.toml Bump protocol dep version
crates/telemetry/src/lib.rs Telemetry naming + disclosure hook
crates/telemetry/src/actor.rs Add notice_version + persistence test
crates/telemetry/Cargo.toml Bump internal crate versions
crates/state/Cargo.toml Bump internal crate versions
crates/secrets/Cargo.toml Bump internal crate versions
crates/lane/Cargo.toml Bump internal crate versions
crates/hooks/Cargo.toml Bump internal crate versions
crates/execpolicy/Cargo.toml Bump internal crate versions
crates/core/Cargo.toml Bump internal crate versions
crates/config/src/route/tests.rs Reject Antigravity route resolution
crates/config/src/route/resolver.rs Hard-block Antigravity provider kind
crates/config/src/route/providers-export.golden.json Remove Antigravity export
crates/config/src/route/golden_route_ids.txt Remove Antigravity route id
crates/config/src/route/descriptor.rs Remove auth methods for tombstone
crates/config/src/provider_defaults.rs Antigravity .invalid tombstone defaults
crates/config/Cargo.toml Bump internal crate versions
crates/command-contract/Cargo.toml Bump core dep version
crates/cli/tests/root_resume_dispatch.rs Expand root flag parsing contract
crates/cli/tests/credential_handoff_dispatch.rs Allow one-time disclosure on stderr
crates/app-server/Cargo.toml Bump internal crate versions
crates/agent/Cargo.toml Bump config dep version
config.example.toml Add reasoning-only + google provider docs
Cargo.toml Bump workspace version to 0.9.12
.gitignore Ignore computer-use receipts dir
.github/AUTHOR_MAP Add contributor email mappings
.buildkite/steps/test.sh Hermetic test-home enforcement
Review details
  • Files reviewed: 110/474 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +173 to +182
/// A row that was a model pin promoted to a member: no role, and an id
/// that is just the model's slug. Such rows are not members.
#[must_use]
pub fn is_bare_model_pin(&self) -> bool {
self.role.trim().is_empty()
&& self
.model
.as_deref()
.is_some_and(|model| self.id.trim().starts_with(slugify(model).as_str()))
}
Comment on lines +186 to +188
// The optional processor sees only the same validated batch, never the
// incoming request. Its bounded, best-effort failure cannot reject AE data.
await deliverPostHog(result.batch, env);
…dless agents

Five hosted failures on release/v0.9.12, each fixed at its cause:

- Website credit parity: the two new changelog credits (@yiheng-kkk,
  @Lfanxing) are added to web/lib/release-credits.ts and the v0.9.12 band of
  docs/CONTRIBUTORS.md; lib/public-copy.test.ts passes 7/7.
- Archive installer contract: .github/scripts/release-workflows.test.js
  expected the pre-safety `install_binary` overwrite of `codewhale-tui`; it
  now asserts the preserve-existing design (validate the retired path with
  check_destination, never overwrite; `codewhale update` migrates it).
- computer-use ssh round-trip test: a headless Linux runner has no wmctrl or
  Wayland tooling, so the remote backend's named fail-closed error is
  accepted as proof of the round trip on linux only; 31/31 locally.
- Hermetic test home: materialized builtin plugins leave a read-only runtime
  tree that defeated `rm -rf` and masked the exit status; the EXIT trap now
  chmods the tree and never alters the command's status (3/3 script tests).
- Plugin adapter tests read the shared macOS agent's real ~/.codewhale
  plugins through capture_pre_dotenv(); they now discover only the temp
  bundle (47 command-contract tests pass).

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea1f09f29c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +357 to +358
} catch {
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Fail closed when browser storage reads throw

When Storage.getItem throws, this converts the failure to null, which readUsagePreference interprets as an absent preference and therefore enables counting. In browsers where the storage object exists but is unreadable, a previously recorded opt-out can consequently be ignored and events can be sent; propagate an unreadable-state sentinel or make the recorder unavailable instead.

AGENTS.md reference: AGENTS.md:L28-L29

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
# The bundled plugin is dependency-free too; its suites cover the
# manifest contract, the registry, the exec/ssh transport, the four
# platform backends, and the MCP stdio protocol. No GUI input runs.
run: (cd plugins/computer-use && npm test)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep the computer-use suite headless in CI

In the inspected integrations job, this runs the entire plugin suite on a headless ubuntu-latest runner without installing or starting X11/Wayland tools. server-protocol.test.mjs:136-147 dispatches a real remote list_apps call and requires a nonempty result, while the Linux backend rejects it without wmctrl, swaymsg, or hyprctl; running this exact command produced 30 passes and that test failure, so the required job remains red unless the test mocks this backend interaction or CI provisions a GUI session.

AGENTS.md reference: AGENTS.md:L104-L107

Useful? React with 👍 / 👎.

install_binary "$SCRIPT_DIR/codewhale" "$legacy_tui"
echo " $legacy_tui (refreshed legacy compatibility command)"
if [[ -e "$legacy_tui" || -L "$legacy_tui" ]]; then
check_destination "$SCRIPT_DIR/codewhale" "$legacy_tui"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reconcile the archive installer with its release gate

The new fresh-install behavior only preflights an existing codewhale-tui path and no longer invokes install_binary for it, but .github/scripts/release-workflows.test.js:559-563 still requires archive upgrades to refresh that path. The CI command at ci.yml:220 now fails with archive upgrades must refresh the retired TUI path from consolidated bytes; either retain the compatibility refresh or update the gate to assert the intentionally delegated updater contract before landing this release candidate.

AGENTS.md reference: AGENTS.md:L104-L107

Useful? React with 👍 / 👎.

Comment thread crates/cli/src/lib.rs
// Per-run `--set KEY=VALUE` overlays: validated and applied in memory,
// never saved. Mutating config subcommands refuse them below rather than
// letting a per-run value leak into the file.
apply_per_run_overrides(&mut store, &cli.overrides)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Prevent global overlays from leaking through other mutators

Applying --set directly to the shared mutable ConfigStore means non-config commands that later call store.save() persist supposedly per-run values. For example, codewhale --set verbosity=concise model set foo reaches ModelCommand::Set and saves the overlaid verbosity, and auth set has the same problem; the rejection in run_config_command does not cover these dispatch branches, so overlays need isolation from every persistent writer.

Useful? React with 👍 / 👎.

Comment on lines +1594 to +1595
config: self.config.clone(),
model: self.model.clone(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore the saved route and mode when loading ACP sessions

session/load restores the transcript and workspace but replaces the durable session's metadata.model, model_provider, model_provider_id, and mode with the ACP server's current defaults. Loading a session created on another provider therefore sends its next turn to the wrong route, and loading a saved Plan session under an Agent-mode server can expose mutating tools that the saved session did not have; resolve the saved provider/model and apply its mode before building the session tool registry.

AGENTS.md reference: AGENTS.md:L69-L69

Useful? React with 👍 / 👎.

Comment thread crates/cli/src/lib.rs
&& !url.starts_with("http://")
&& !url.starts_with("https://")
{
errors.push(format!("`{name}` is not an http(s) URL: {url}"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Redact malformed endpoint values from config doctor output

When a malformed base_url contains credentials, such as a mistyped htps://user:token@host, config doctor prints the entire value both as an error line and again through the returned error chain. This contradicts the command's promise that diagnostics never print credentials and can leak tokens into support logs; report only the field name and validation class, or render the URL with userinfo and sensitive query values redacted.

AGENTS.md reference: AGENTS.md:L28-L29

Useful? React with 👍 / 👎.

Comment thread crates/cli/src/lib.rs
Comment on lines +4627 to +4631
let editor = std::env::var("VISUAL")
.or_else(|_| std::env::var("EDITOR"))
.unwrap_or_else(|_| "vi".to_string());
let status = Command::new(&editor)
.arg(&path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Parse editor arguments before launching config edit

Command::new(&editor) treats the complete environment value as the executable name, so common settings such as EDITOR="code --wait" fail with ENOENT, and an empty VISUAL prevents fallback to EDITOR. The existing implementation in crates/tui/src/tui/external_editor.rs:40-54 already filters empty values and splits Unix editor commands; reuse that resolution behavior instead of introducing a second incompatible path.

AGENTS.md reference: AGENTS.md:L16-L18

Useful? React with 👍 / 👎.

Comment on lines +985 to +989
if migrate_legacy_file && !receipt.folded.is_empty() {
// The fold is only real once settings.toml owns the value;
// otherwise the next launch would read the moved-aside file's
// theme back out of nothing and quietly lose it.
if let Err(error) = settings.save_to_path(&write_path) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Persist migrated settings before moving the source aside

fold_tui_prefs renames tui.toml to its backup before this save is attempted, and a save failure is only logged. If the rename succeeds but settings.toml cannot be replaced—for example because of an I/O or space failure—the current process uses the migrated theme, but the next launch finds neither that value in settings.toml nor the original tui.toml and silently falls back to a default; save the destination first or roll the rename back on failure.

AGENTS.md reference: AGENTS.md:L28-L29

Useful? React with 👍 / 👎.

Comment on lines +135 to +138
if codewhale_config::persistence::atomic_write(
&destination,
&bytes,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Make the Claude instructions import atomically no-clobber

If another process creates instructions.md after the destination.exists() check, this call uses atomic_write, whose final persist replaces an existing destination, and overwrites the newly created user file despite the command's explicit no-clobber contract. Use a create_new/persist_noclobber publication step so concurrent imports or edits cannot lose the operator's instructions.

AGENTS.md reference: AGENTS.md:L28-L29

Useful? React with 👍 / 👎.


fn footer_hints(&self) -> Vec<ActionHint> {
let locale = self.locale;
let mut hints = vec![ActionHint::new("↑↓", tr(locale, MessageId::LaunchHintMove))];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Source automation footer chords from the binding table

The new automation view embeds its navigation/action chords directly in footer_hints (↑↓, Tab, Enter, p, r, x, d, and Esc) rather than deriving them from shell_key_routing. Any remapping or terminal-specific decoding can therefore leave the footer advertising keys different from those the router accepts; register these actions in the shared binding table and render its chord labels.

AGENTS.md reference: crates/tui/AGENTS.md:L8-L13

Useful? React with 👍 / 👎.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T04:55:43.628203Z ea1f09f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

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


Review in progress

  • Gather context (PR description, changed files, diff stat vs origin/main)
  • Understand request (code review of release PR, per repo custom instructions: correctness, provider/model/route safety, reuse/simplification, tests, security)
  • Review provider/model/route safety (config crate, route resolver, Antigravity tombstone) — running
  • Review telemetry default-on / opt-out changes (crates/telemetry, telemetry-ingest) — running
  • Review CLI update.rs / command-contract facets.rs changes — running
  • Review acp_server.rs / lifecycle_outbox.rs / fleet/store.rs — running
  • Synthesize and post final review

Comment thread crates/cli/src/lib.rs
);
println!("Details: codewhale config telemetry");
} else {
println!("{value}");
Comment thread crates/cli/src/lib.rs
}
} else {
store.save()?;
println!("set {key}");
.get("api_key")
.and_then(toml::Value::as_str)
.expect("api_key keeps its slot");
assert!(!api_key.contains("secret"), "{api_key}");
Adds the "Reports and reproductions" credits the release checklist asks
for: @slowly247 (#5820 Ollama input budget), @ronohara (#5769 recoverable
stream errors), @Lujc0523 (#5863 ACP session configuration), and @senka9h
(#5864 ACP session/list and session/load), in CHANGELOG.md, the v0.9.12
band of docs/CONTRIBUTORS.md, and the website ledger (RELEASE_HELPERS).
Every merged-PR author since v0.9.11 was cross-checked against the ledger;
the two remaining names (@bistack, @xyzs996) were already credited in 0.9.11.
Regenerated crates/tui/CHANGELOG.md and web/lib/changelog.generated.ts;
public-copy + changelog parity tests 13/13; check-versions OK.

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

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

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


Review in progress

  • Gather context (PR description, changed files, existing bot review comments)
  • Review provider/model/route safety (config/route resolver, Antigravity tombstone, provider_defaults) — running
  • Review CLI changes (crates/cli/src/lib.rs, update.rs, --set overlay, config doctor/edit) — running
  • Review telemetry default-on/opt-out (crates/telemetry, crates/config/setup_state.rs) — running
  • Review ACP server session load/save and lifecycle_outbox — running
  • Review command-contract facets.rs and fleet/store.rs — running
  • Synthesize findings and post final review

Note: extensive automated reviews from Copilot, Codex, and CodeQL are already posted on this PR — cross-checking those and focusing on anything not already flagged.

…patch smoke stalled on

The Windows test job for the 0.9.12 candidate ran 90 minutes and was
killed by the runner with 13,924 passed and two tests still running:
commands::tests::every_registered_command_dispatches_to_a_handler and
every_command_alias_dispatches_to_a_handler, which execute every slash
command against an isolated app. Both pass on Linux and macOS (every
command returns within 31 ms locally), so the stall is inside one handler
on Windows and the killed job left no output to say which.

The ci nextest profile now terminates a test after ten slow-timeout
periods (10 minutes) and reports it as failed with its captured output,
and the two smoke tests print a breadcrumb before and after each command,
so the next Windows run names the handler instead of timing out the job.
Shipped binary bytes are unchanged: both edits are test-only.

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

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 2m 46s —— View job


Review

  • Gather context (PR description, changed files, existing bot review comments)
  • Review provider/model/route safety (config/route resolver, Antigravity tombstone, provider_defaults)
  • Review CLI changes (crates/cli/src/lib.rs, update.rs, --set overlay, config doctor/edit)
  • Review telemetry default-on/opt-out (crates/telemetry, crates/config/setup_state.rs)
  • Review command-contract facets.rs, lifecycle_outbox.rs, release/install.rs
  • Synthesize findings and post final review

This PR already has thorough automated coverage from Copilot, Codex, and CodeQL. I cross-checked those and focused on what they hadn't caught.

New finding

crates/config/src/resolve.rs is a mostly-unadopted abstraction (posted inline). The new resolve_scalar, merge_maps, merge_provenance, and SecretRef have zero callers anywhere in the workspace outside their own tests — only the Layer enum got adopted (by crates/tui/src/settings.rs). This is the exact pattern AGENTS.md's ponytail method warns against: "An abstraction must delete caller code... it gets built, adopted once, and abandoned." Here it wasn't even adopted once. Either wire the merge/resolve functions into the settings-precedence logic they were clearly written for, or drop them and keep just the Layer enum.

Provider/model/route safety — looks solid

The Antigravity retirement is defense-in-depth and consistent everywhere I checked:

  • crates/config/src/route/resolver.rs and crates/tui/src/route_runtime.rs both hard-block the Antigravity kind before any route is built, with the same LEGACY_ANTIGRAVITY_TOMBSTONE_MESSAGE.
  • ProviderKind::ALL (42, was 43) drops Antigravity from the selectable catalog; all_providers()/parse_config_identity still resolve it (and its agy alias) so old config can be read and explicitly cleared, but ProviderKind::parse (catalog parse) can no longer mint it.
  • set_value/set_provider_config_value reject writes to provider = "antigravity" or [providers.antigravity] with the same tombstone message.
  • No code infers a provider switch from a model-string prefix; the existing "provider is NEVER inferred from a model prefix" invariant in resolver.rs is untouched.

Already flagged by other reviewers that I concur are real and should block merge

  • crates/tui/src/fleet/store.rs:182 (Copilot) — is_bare_model_pin's prefix match can misclassify a legitimate role-as-id member (e.g. gpt-4o-reviewer vs model gpt-4o) as a legacy pin to delete. Confirmed the predicate is a plain starts_with, not an exact/-<n> match.
  • crates/cli/src/lib.rs:1949 (Copilot) — confirmed by reading run(): apply_per_run_overrides(&mut store, &cli.overrides) mutates the shared ConfigStore before dispatch to every subcommand, and only run_config_command refuses to combine --set with a save. codewhale --set verbosity=concise model set foo (and auth set) reach their own store.save() untouched, so a per-run overlay can persist.
  • web/lib/telemetry/product-usage.ts:358 (Codex, P1) — fail-open on unreadable Storage.getItem is a real bug in a PR whose headline feature is "durable opt-out": readUsagePreference(null-from-throw) reads as "default" (counting on) instead of failing closed.
  • crates/tui/src/acp_server.rs:1595 (Codex, P1) — session/load rebuilding AcpSession with self.config/self.model instead of the saved session's route/mode is a real cross-provider/cross-mode leak on resume.
  • CodeQL findings on crates/cli/src/lib.rs:4555/4578 — these look like standard println! redaction-pattern false positives (the surrounding code prints resolved status, not raw secrets), consistent with the config doctor design, but worth a maintainer's explicit dismissal rather than silent ignore since CodeQL is a required check.

Not re-flagged (already accurately described by others, nothing to add)

Telemetry-ingest awaiting deliverPostHog on the critical path, the headless computer-use CI suite, the install.sh/release-gate mismatch, the editor-arg parsing duplication, the ACP insertion-order dedup, the settings.rs migration ordering (save-before-rename), and the Claude-import atomic no-clobber race — all confirmed by reading the surrounding code; I have nothing to add beyond what's already posted.

Good things worth noting

  • crates/hooks/src/lifecycle_outbox.rs's new flush() barrier is correctly ordered (FIFO through the same mpsc queue, bounded by timeout, well covered by a stalled-webhook test) and doesn't retry on timeout.
  • crates/telemetry/src/setup_state.rs's notice-version bump handling correctly keeps prior explicit declines durable across the v3→v5 notice change, uses an fd-lock for the update-in-place path, and never manufactures a decision from a "shown" event.
  • crates/release/src/install.rs's InstallMethod::detect now checks real package ownership before honoring CODEWHALE_INSTALL_METHOD, so the env override can no longer be used to bypass protection on an actually-managed binary — a real hardening over previous behavior.

assert_eq!(Layer::UserConfig.name(), "user");
assert_eq!(Layer::Default.name(), "default");
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unadopted abstraction: resolve_scalar / merge_maps / merge_provenance / SecretRef have zero callers

This new module's Layer enum is adopted in crates/tui/src/settings.rs (provenance tagging), but the module's actual logic — resolve_scalar, merge_maps, merge_provenance, and SecretRef — has no caller anywhere in the workspace outside its own #[cfg(test)] block:

$ grep -rn "resolve_scalar\|merge_maps\|merge_provenance\|SecretRef" crates/ --include=*.rs | grep -v resolve.rs
(no matches)

This is exactly the pattern this repo's own AGENTS.md calls out under the ponytail method: "An abstraction must delete caller code. If adopting it is pure obligation — required methods, no default bodies that do work — it gets built, adopted once, and abandoned." Here it wasn't even adopted once — only the tag enum was, the merge/resolve functions are dead weight shipped in a release candidate.

Either wire resolve_scalar/merge_maps into the actual settings-resolution call site they're described for (replacing whatever ad hoc precedence logic exists in settings.rs today), or drop them from this PR and land just the Layer enum where it's used. Shipping unused public API in a release candidate adds a maintenance surface (and a doc/test burden) with no caller to justify it.

Fix this →

assert_eq!(Layer::UserConfig.name(), "user");
assert_eq!(Layer::Default.name(), "default");
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unadopted abstraction: resolve_scalar / merge_maps / merge_provenance / SecretRef have zero callers

This new module's Layer enum is adopted in crates/tui/src/settings.rs (provenance tagging), but the module's actual logic — resolve_scalar, merge_maps, merge_provenance, and SecretRef — has no caller anywhere in the workspace outside its own #[cfg(test)] block:

$ grep -rn "resolve_scalar\|merge_maps\|merge_provenance\|SecretRef" crates/ --include=*.rs | grep -v resolve.rs
(no matches)

This is exactly the pattern this repo's own AGENTS.md calls out under the ponytail method: "An abstraction must delete caller code. If adopting it is pure obligation — required methods, no default bodies that do work — it gets built, adopted once, and abandoned." Here it wasn't even adopted once — only the tag enum was; the merge/resolve functions are dead weight shipped in a release candidate.

Either wire resolve_scalar/merge_maps into the actual settings-resolution call site they're described for (replacing whatever ad hoc precedence logic exists in settings.rs today), or drop them from this PR and land just the Layer enum where it's used.

Fix this →

@Hmbown
Hmbown merged commit dcd4c20 into main Sep 5, 2026
33 of 35 checks passed
@Hmbown
Hmbown deleted the release/v0.9.12 branch September 5, 2026 08:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants