release: Codewhale v0.9.12 candidate - #5884
Conversation
…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
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
|
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 finished @Hmbown's task in 1m 36s —— View job Review in progress
|
There was a problem hiding this comment.
🟡 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.
| /// 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())) | ||
| } |
| // 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
There was a problem hiding this comment.
💡 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".
| } catch { | ||
| return null; |
There was a problem hiding this comment.
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 👍 / 👎.
| # 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) |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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 👍 / 👎.
| // 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)?; |
There was a problem hiding this comment.
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 👍 / 👎.
| config: self.config.clone(), | ||
| model: self.model.clone(), |
There was a problem hiding this comment.
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 👍 / 👎.
| && !url.starts_with("http://") | ||
| && !url.starts_with("https://") | ||
| { | ||
| errors.push(format!("`{name}` is not an http(s) URL: {url}")); |
There was a problem hiding this comment.
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 👍 / 👎.
| 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) |
There was a problem hiding this comment.
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 👍 / 👎.
| 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) { |
There was a problem hiding this comment.
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 👍 / 👎.
| if codewhale_config::persistence::atomic_write( | ||
| &destination, | ||
| &bytes, | ||
| ) |
There was a problem hiding this comment.
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))]; |
There was a problem hiding this comment.
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 👍 / 👎.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Claude finished @Hmbown's task in 1m 10s —— View job Review in progress
|
| ); | ||
| println!("Details: codewhale config telemetry"); | ||
| } else { | ||
| println!("{value}"); |
| } | ||
| } 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 finished @Hmbown's task in 1m 41s —— View job Review in progress
|
…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 finished @Hmbown's task in 2m 46s —— View job 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
Provider/model/route safety — looks solidThe Antigravity retirement is defense-in-depth and consistent everywhere I checked:
Already flagged by other reviewers that I concur are real and should block merge
Not re-flagged (already accurately described by others, nothing to add)Telemetry-ingest awaiting Good things worth noting
|
| assert_eq!(Layer::UserConfig.name(), "user"); | ||
| assert_eq!(Layer::Default.name(), "default"); | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| assert_eq!(Layer::UserConfig.name(), "user"); | ||
| assert_eq!(Layer::Default.name(), "default"); | ||
| } | ||
| } |
There was a problem hiding this comment.
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.

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
dispatch, per-session control socket, and the reworked shell (launch card,
work bar,
fleet) — seeCHANGELOG.md## [0.9.12].codewhale models --update/--refreshwith account-scoped Codex modeldiscovery and cache isolation.
fixes, and byte-identical macOS aliases.
Ctrl+] work-bar hotkeys; stream-approval recovery (Network errors sometimes cause the engine to stop #5769); provider output
budget and Muse reasoning fixes (Ollama provider: input budget collapses to 1024 tokens on 32K local models (default output reservation 64K clamps the window) #5820, muse-spark-1.3 reasoning effort + Codex XHigh/Ultra picker rows #5853); ACP session config (ACP Function Enhancement #5863);
--freshforwarding; todo transcript fix (To-do list history clutters the transcript with no way to clear it without losing context #5871/fix(tui): replace stale todo transcript snapshots #5873).(schema 3 / notice 5). Every recorded opt-out,
telemetry = false,CODEWHALE_TELEMETRY=0, and unreadable privacy state stay off; showing thedisclosure never records acceptance.
codewhale config set telemetry falseturns it off and wipes queued counts.
trust/FAQ/roadmap copy; real 0.9.12 terminal screenshot with recorded
provenance (
v0.9.12 (15fe6983bfa5)).Local proof on this exact line (macOS, offline cargo)
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).
build clean; browser QA EN/ZH at 390/1280.
171acee6installed and verified from a fresh shell; installedautomation-editor and work-bar PTY acceptance rerun on it.
check-versions.shOK (workspace/npm/binary 0.9.12, lock in sync);check-ohos-deps.shOK; npm wrapper smoke passed against local assets;publish-crates.sh dry-runpackage contents verified;check-feature-release-notes.sh v0.9.11 HEADOK (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.