Repository navigation
Fix cargo fmt formatting issues - #1
Conversation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary of ChangesHello @Hmbown, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses and resolves automated code formatting issues identified by Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request correctly applies cargo fmt to resolve formatting issues. The changes look good. I've added one suggestion in src/commands/note.rs to improve the consistency of the note formatting, which is a minor logic improvement related to the changed code.
I am having trouble creating individual review comments. Click here to see my feedback.
src/commands/note.rs (48-53)
The current logic for writing notes can lead to inconsistent formatting. Specifically, it always prepends \n---\n, which results in an unnecessary leading newline and separator for the very first note in an empty file. A better approach is to check if the file is empty. If so, write the note content directly. Otherwise, prepend the separator. This ensures notes are always formatted consistently without leading whitespace in new files.
// Write separator and note content
let is_empty = match file.metadata() {
Ok(md) => md.len() == 0,
Err(e) => return CommandResult::error(format!("Failed to read file metadata: {e}")),
};
let format_string = if is_empty { "{}" } else { "\n---\n{}" };
if let Err(e) = writeln!(file, format_string, note_content) {
return CommandResult::error(format!("Failed to write note: {e}"));
}
Sub-area #1 of the v0.6.6 UI redesign (issue #121). User cells now lead with `▎` (solid bar, no animation — input is finished). Assistant cells lead with `●`. While streaming, the bullet pulses on a 2-second cycle between 30%..100% brightness via `palette::pulse_brightness`; once a turn completes the bullet sits at full DEEPSEEK_SKY so finished history reads as solid. Honors `low_motion`: the pulse is suppressed and the glyph holds full brightness regardless of streaming state. Pager / clipboard exports (`transcript_lines`) also skip the pulse so screenshots are stable. Existing pager titles in `ui.rs` (`history_cell_to_text`) keep the literal "You" / "Assistant" wording — those drive the modal title bar and read better as words than as glyphs. Tests: glyphs replace the literal labels in both User and Assistant cells; streaming pulse demonstrably dips below source brightness; idle and low_motion both pin to source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sub-area codewhale-hq#1 of the v0.6.6 UI redesign (issue codewhale-hq#121). User cells now lead with `▎` (solid bar, no animation — input is finished). Assistant cells lead with `●`. While streaming, the bullet pulses on a 2-second cycle between 30%..100% brightness via `palette::pulse_brightness`; once a turn completes the bullet sits at full DEEPSEEK_SKY so finished history reads as solid. Honors `low_motion`: the pulse is suppressed and the glyph holds full brightness regardless of streaming state. Pager / clipboard exports (`transcript_lines`) also skip the pulse so screenshots are stable. Existing pager titles in `ui.rs` (`history_cell_to_text`) keep the literal "You" / "Assistant" wording — those drive the modal title bar and read better as words than as glyphs. Tests: glyphs replace the literal labels in both User and Assistant cells; streaming pulse demonstrably dips below source brightness; idle and low_motion both pin to source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…whale-hq#4 🔴 Fix codewhale-hq#1 (Embedder blocks async runtime): - Pre-warm embedder in init_vector_db() via warmup_embedder() - Model downloaded during startup, not first retrieval - Eliminates 5-10s UI freeze on first embed call 🔴 Fix codewhale-hq#2 (Compaction summary pollution): - store_compaction_summary_to_vector_db() now extracts only the summary section from the SystemPrompt, stripping boilerplate headers and workflow context - Uses regex extraction between ## Summary and ## Workflow markers 🔴 Fix codewhale-hq#3 (No vector index → full table scan): - New ensure_vector_index() in LanceDbBackend - Checks list_indices() for existing IVF-PQ index on embedding column - Creates missing index via table.create_index(["embedding"], Index::Auto) - Called from ensure_tables() for all three tables 🔴 Fix codewhale-hq#4 (Compaction threshold regression for non-vector users): - Added MINIMUM_AUTO_COMPACTION_TOKENS_WITHOUT_VECTOR = 500K - should_compact() now accepts vector_db_enabled: bool - Non-vector users keep 500K floor; vector users use 50K floor - auto_floor_tokens=0 (explicit disable by tests) bypasses both floors - token_threshold default restored to 800K (fallback only; real values come from compaction_threshold_for_model_and_effort) 🟡 Fix codewhale-hq#7 (No score threshold → noise injection): - search_memories() and search_summaries() now filter results where score < 0.4 before returning - Prevents low-quality retrieval from polluting system prompt Tests: 62/62 compaction ✓, 17/17 vector_db ✓
🔴 codewhale-hq#1 Embedder blocks async runtime: - Pre-warm embedder in init_vector_db() via warmup_embedder() - Model downloaded at startup, eliminating 5-10s UI freeze 🔴 codewhale-hq#2 Compaction summary boilerplate pollution: - store_compaction_summary_to_vector_db() now extracts only the actual summary section using regex, stripping headers and workflow context from embeddings 🔴 codewhale-hq#3 No vector index → full table scan: - New ensure_vector_index() checks list_indices() for IVF-PQ - Creates missing index via create_index(["embedding"], Auto) - Called from ensure_tables() for all 3 tables 🔴 codewhale-hq#4 Compaction threshold regression: - Added MINIMUM_AUTO_COMPACTION_TOKENS_WITHOUT_VECTOR = 500K - should_compact() accepts vector_db_enabled: bool - Non-vector users keep 500K floor; vector users get 50K - auto_floor_tokens=0 bypasses both (test compatibility) - token_threshold default restored to 800K 🟡 codewhale-hq#7 Score threshold filtering added: - search_memories() and search_summaries() filter score < 0.4 Tests: 62/62 compaction ✓, 17/17 vector_db ✓, 99/100 engine ✓
… precision - resolve_path: normalise .. components; reject workspace escapes (codewhale-hq#1,codewhale-hq#5) - tool_read_file: clamp start_line >= 1 to prevent underflow panic (codewhale-hq#4) - tool_edit_file: use replacen(.., 1) to avoid mass-replacement bugs (codewhale-hq#6,codewhale-hq#8)
) When an OpenAI-compatible backend (vLLM, Ollama, LM Studio, Together AI, self-hosted vLLM/SGLang, etc.) streams an assistant message containing multiple tool_calls in a single round, only the **last** tool's `Event::ToolCallStarted` was firing. The preceding N-1 tool calls executed and produced tool_result events, but never announced their start to consumers (TUI / runtime API / embedder bridges), leaving them with N orphan tool_result blocks and no matching tool_use blocks in the assistant history. ## Reproduction ```text backend dispatches: 7 × write_file + 1 × exec_shell log shows: 7 × ApprovalRequired events ✓ listeners receive: 1 × chat:tool_start, 7 × chat:tool_end session history: 1 tool_use + 7 tool_result (6 orphans) ``` Tested against vLLM 0.7 + Qwen3.6-35B-A3B with a "scaffold 7-file Tauri template" prompt. Any model+backend combo that emits batch tool_calls trips this — typical when a single LLM round asks for multiple parallel file writes or edits. ## Root cause `run_turn` tracked the currently-streaming tool block with a single `current_tool_index: Option<usize>`. The Anthropic-style adapter (non-streaming response → events at `chat.rs::L1807`) emits Start/Stop pairs in lockstep so the slot never overlaps. But the OpenAI streaming parser (`chat.rs::L1954-2064`) emits every `ContentBlockStart::ToolUse` as soon as a tool_call delta lands, then batches every `ContentBlockStop` at `finish_reason`: ```text Start { index: 0 } // tool #1 Delta { index: 0, .. } Start { index: 1 } // tool #2 — overwrites current_tool_index Delta { index: 1, .. } … Start { index: 6 } // current_tool_index = Some(6) Delta { index: 6, .. } Stop { index: 0 } // take() returns Some(6) ← wrong tool! Stop { index: 1 } // take() returns None Stop { index: 2 } // take() returns None … ``` The first `Stop` consumes the last index and emits `ToolCallStarted` for the wrong `tool_uses` entry; every subsequent `Stop` finds the slot already `None` and skips the entire `if let Some(index) = …` branch, dropping the announcement. ## Fix Replace the single slot with `HashMap<u32 block_index, usize tool_uses_idx>`: - `ContentBlockStart::ToolUse` and `::ServerToolUse` insert the `(event.index → tool_uses.len())` mapping. - `InputJsonDelta` looks up by the `ContentBlockDelta` outer index. - `ContentBlockStop` removes by the stop's index, so each Stop routes to its own `tool_uses` entry regardless of arrival order. Routing no longer depends on `current_block_kind` (which has the same single-slot overwrite problem); `current_tool_indices.remove(&index)` returning `Some(_)` already proves the Stop belongs to a tool block. ## Tests Added `batch_tool_calls_preserve_all_tool_use_indices` in `core/engine/turn_loop.rs::tests` — feeds 7 Starts and 7 Stops through the same `HashMap` API used by `run_turn`, asserts every index round-trips. Manual end-to-end verification: vLLM + Qwen3.6-35B + 7-file Tauri template prompt → frontend `messages` history now contains all 7 `write_file` tool_use blocks paired with their tool_result blocks. Co-authored-by: hexin <he.xin@h3c.com>
…he_control, verify fast-path, naming - combined_hash now hashes full tool JSON (name + description + schema), not just tool names, so schema/description changes are detected (codewhale-hq#1). - build_messages preserves cache_control from SystemPrompt::Blocks so DeepSeek context-cache breakpoints are not lost (codewhale-hq#2). - FrozenPrefix::verify() compares raw text before hashing to avoid redundant SHA-256 computation (codewhale-hq#3). - build_messages uses self.message_count() for vector capacity (codewhale-hq#4). - Strict-mode error message no longer references nonexistent /prefix-unfreeze command — suggests restart or config toggle (codewhale-hq#5).
Buildkite 1535 on #5809: two cucumber PTY tests against the real binary failed under the new Bottom default — at 40x12 a queued message had no `Queued #1:` preview, and the plugin acceptance stage stayed blank. Cause: the Bottom layout dropped the strip's leading slot and re-ordered the constraint list, but the stage, workflow panel, pending-input preview and background chip are addressed by fixed slot index (`body_chunks[1..4]`), so under Bottom they drew into the wrong, zero-height slots. Fix: one nine-slot layout in both placements. The strip owns a slot at each end (above the stage for `top`, under the composer for `bottom`) and only one has height, so every other slot keeps its index. The dual constraint list is gone. Test: `bottom_placement_keeps_the_stage_and_queued_preview_at_twelve_rows` renders a 40x12 frame under Bottom with a transcript row and a queued message and asserts both paint. Evidence: cargo test -p codewhale-tui --all-features --locked --test cucumber -- \ active_composer_pointer_submit_queues_without_provider \ plugin_toml_binary_lifecycle_skill_and_stdio_mcp_acceptance test result: ok. 2 passed; 0 failed; 0 ignored (before the fix, the pointer test failed exactly as on Buildkite: "40x12: queued preview ... not visible after pointer submit") cargo test -p codewhale-tui --lib -- work_surface tui::ui::tests test result: ok. 808 passed; 0 failed; 0 ignored cargo test -p codewhale-tui --lib -- bottom_placement test result: ok. 3 passed; 0 failed; 0 ignored cargo clippy -p codewhale-tui --all-targets -D warnings (CI allows): clean Signed-off-by: CodeWhale Bot <bot@codewhale.net>
* feat(tui): the work surface sits under the composer by default
Round 3 of the shell design (codewhale-ops/design/SHELL-DESIGN-20260901.md
§2.0): the bar's information — posture, agent roster, to-do — lives UNDER
the composer, so scrolling up reads as intentional history and nothing
paints above the stage by default.
- `WorkSurfacePlacement::Bottom`: a strip placement that shares Top's
auto-fit height math (`is_strip()`) and row machinery; only its slot in
the frame differs (after the composer, before the footer). Its divider
is the strip's top edge, so a drag on it inverts Top's delta; the body
starts one row below the divider so row hitboxes stay honest.
- `bottom` is the default (`work_surface_placement`, `WorkSurfaceState`,
`/rail on`, the rail hotbar toggle and Ctrl+Alt+0 all restore it) and
the fallback for unknown values. Narrow terminals keep Bottom; only the
side rails still fall back to Top below the side-rail width.
- `/config work_surface_placement bottom` is accepted; the error text
lists the five placements.
Upgrade note: the default for `work_surface_placement` changes from
`left` to `bottom`. Users who set it explicitly are untouched.
Legacy ui/sidebar/mouse test fixtures pin `Top` because they assert the
old strip-above-transcript geometry; the Bottom default has its own
coverage (`bottom_placement_draws_the_work_strip_under_the_composer`,
`rail_panels_render_in_all_placements` now includes Bottom).
No goldens changed: the startup goldens carry no work rows, so the strip
is zero-height in both placements.
Evidence:
scripts/dev-test.sh tui (cargo test -p codewhale-tui --lib):
test result: FAILED. 11848 passed; 3 failed; 13 ignored
(the 3 — runtime_api::omitted_runtime_models_use_the_active_provider_default,
runtime_threads::approval_required_awaits_external_decision_allow,
sandbox::seatbelt::file_provider_synthetic_operations_are_independent_under_seatbelt —
are unrelated to this change and pass in isolation:
test result: ok. 3 passed; 0 failed)
focused rerun after the last edits:
test result: ok. 11 passed; 0 failed; 0 ignored
cargo clippy --workspace --all-targets -D warnings (CI allows): clean
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
* fix(tui): the bottom strip keeps every other frame slot in place
Buildkite 1535 on #5809: two cucumber PTY tests against the real binary
failed under the new Bottom default — at 40x12 a queued message had no
`Queued #1:` preview, and the plugin acceptance stage stayed blank.
Cause: the Bottom layout dropped the strip's leading slot and re-ordered
the constraint list, but the stage, workflow panel, pending-input preview
and background chip are addressed by fixed slot index (`body_chunks[1..4]`),
so under Bottom they drew into the wrong, zero-height slots.
Fix: one nine-slot layout in both placements. The strip owns a slot at
each end (above the stage for `top`, under the composer for `bottom`) and
only one has height, so every other slot keeps its index. The dual
constraint list is gone.
Test: `bottom_placement_keeps_the_stage_and_queued_preview_at_twelve_rows`
renders a 40x12 frame under Bottom with a transcript row and a queued
message and asserts both paint.
Evidence:
cargo test -p codewhale-tui --all-features --locked --test cucumber -- \
active_composer_pointer_submit_queues_without_provider \
plugin_toml_binary_lifecycle_skill_and_stdio_mcp_acceptance
test result: ok. 2 passed; 0 failed; 0 ignored
(before the fix, the pointer test failed exactly as on Buildkite:
"40x12: queued preview ... not visible after pointer submit")
cargo test -p codewhale-tui --lib -- work_surface tui::ui::tests
test result: ok. 808 passed; 0 failed; 0 ignored
cargo test -p codewhale-tui --lib -- bottom_placement
test result: ok. 3 passed; 0 failed; 0 ignored
cargo clippy -p codewhale-tui --all-targets -D warnings (CI allows): clean
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
---------
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: CodeWhale Bot <bot@codewhale.net>
…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 (codewhale-hq#5533, codewhale-hq#5831, codewhale-hq#5825, codewhale-hq#5829, codewhale-hq#5801, codewhale-hq#5815, codewhale-hq#5826, codewhale-hq#5286, codewhale-hq#5809, codewhale-hq#5778, codewhale-hq#5725, codewhale-hq#5701, codewhale-hq#5712, codewhale-hq#5747, codewhale-hq#5782, codewhale-hq#4394) in CHANGELOG.md; filter commit-body scrape noise (#000, codewhale-hq#1, codewhale-hq#24, codewhale-hq#26, codewhale-hq#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 (codewhale-hq#1), and the ocean ramp / ambient life were never wired into the stage (codewhale-hq#2, codewhale-hq#3). The stage paints the same ramp and life the transcript does; the life field ends at the composer dock. - Recent rows resume (codewhale-hq#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 (codewhale-hq#5): ModeCycle is AnyShell. The card's rule now reads `model (effort) · mode · permission` so the cycle is legible and matches the posture bar (codewhale-hq#7). Work bar - One-time migration of a persisted `work_surface_placement = "top"` to `bottom` (codewhale-hq#9), recorded in `work_surface_bottom_migrated`. - Idle scheduled automations no longer count as work that auto-opens the dock (codewhale-hq#10); live shells, tasks, or a running automation still do. - ORDER / AUTO_ORDER lead with TODO, then AGENTS (codewhale-hq#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 (codewhale-hq#17–codewhale-hq#19, codewhale-hq#25 — the `tools: 0` rows were the passive config snapshot). - The markdown inline parser honours backslash escapes, so `computer\-use` renders as `computer-use` (codewhale-hq#21). - Extensions keyboard follows grokbuild: Tab / Shift+Tab move across tabs, including mid-search with the query kept (codewhale-hq#22, codewhale-hq#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 (codewhale-hq#14, codewhale-hq#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 (codewhale-hq#26). Picker ⇧F enrolls under `general`. - Roster detail is role-first and compact (codewhale-hq#27, codewhale-hq#28); footer is ↑↓ · Enter · Tab workers · f · Esc (codewhale-hq#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>
…or say what is actually wrong `scripts/release/check-versions.sh` runs on **every** release path — release.yml:107, auto-tag.yml:102, release-candidate.yml:73 — and outside the advisory set, so these two failures blocked any 0.9.14 tag. **crates/memory pinned its own version and MSRV.** Check #1 forbids a literal `version = "..."` in any `crates/*/Cargo.toml` and check #2 requires `rust-version.workspace = true`; the vendored crate did both wrong. It now inherits. The MSRV change is a semantic no-op — the literal was `1.88` and the workspace MSRV is `1.88`. The version moves 0.2.0 -> the workspace version, which has no external meaning for a `publish = false` crate and is the convention every other crate here follows. `check-versions.sh` now passes everything except the changelog sync. **The publish-order validator demanded an unpublishable crate.** `release_names` collected every `codewhale-*` workspace package with no `publish = false` filter, so it required `codewhale-memory` to appear in `crates.sh` — a contradiction, since `crates.sh` drives `cargo publish`, which refuses a crate marked unpublishable. `cargo metadata` renders `publish = false` as an empty registry list, so the filter is exact. That fix does not make the problem disappear; it makes the validator report the real one. `validate-crate-publish-order.test.sh` now fails with "codewhale-tui depends on workspace crate codewhale-memory [normal], which is not in the codewhale-* release inventory" instead of the misleading "mixed versions". That message is correct and the condition is genuine: `cargo publish -p codewhale-tui` cannot succeed while a **normal** dependency is absent from crates.io. Resolving it is a decision, not a cleanup — publish `codewhale-memory`, feature-gate the dependency, or stop publishing `codewhale-tui` — so it is filed rather than guessed at. Tests: `bash scripts/release/validate-crate-publish-order.test.sh` — its hermetic fixtures pass; its final live-workspace block now reports the real dependency problem rather than the version one. `bash scripts/release/check-versions.sh` — the only remaining error is `crates/tui/CHANGELOG.md is out of date`, which is a separate commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: CodeWhale Bot <bot@codewhale.net>
`crates/memory` moved from a literal `version = "0.2.0"` to `version.workspace = true` in the previous commit so it would satisfy `check-versions.sh` check #1. Cargo.lock still recorded 0.2.0, which made every `--locked` invocation refuse to proceed: error: cannot update the lock file ... because --locked was passed That took out `scripts/release/check-ohos-deps.sh` (its `cargo tree` retried three times and failed) and would have taken out CI's "Lockfile drift guard" step, which is `git diff --exit-code -- Cargo.lock`. Regenerated offline. One line changed: `codewhale-memory` 0.2.0 -> 0.9.13. No dependency was added, removed or bumped. `bash scripts/release/check-ohos-deps.sh` passes again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: CodeWhale Bot <bot@codewhale.net>
…assification (determinism audit batch 2, #1) The auto_reasoning keyword classifier (debug/error -> Max, search/lookup -> Low, plus CJK/JP lists) made cost and answer quality depend on vocabulary. auto now resolves the declared High default everywhere: turn loop, route planner, CLI/exec paths, runtime threads, and subagent assignment. The prompt plumbing through the spawn-route functions is deleted with it (-375/+106); select() is the seam a model-declared escalation hint lands in. Tests: auto_reasoning/resolve_auto_effort/cli_auto/model_routing filters 47 passed, 0 failed; tools::subagent 672 passed, 1 failed where the single failure (issue_5305 untethered fail-closed) also fails on clean HEAD and is unrelated to this change. clippy --all-targets clean, fmt clean.
引擎早就会收图(POST /v1/threads/{id}/turns 的 images:[{mime,dataBase64}]、三种 wire 格式
都投影好了),官方 web 一个字没做 —— 属铁律 codewhale-hq#1 的「搬表」。
做法:只猴补 window.fetch,认 POST /turns 时把 images 拼进 body,官方发送逻辑一个字没改
(流式与乐观 UI 全照旧)。取图三处:按钮 / 粘贴 / 拖入。校验照引擎口径(PNG/JPEG/GIF/WebP、
单图 4MiB、总 5MiB、最多 10 张)。能力判定照引擎口径:只认明确 supported,auto 当不支持,
拿不到会话时是 unknown(不猜、不假装支持)。
只发图不打字会被引擎拒(prompt 不能为空),而输入框空时官方发送按钮本来就是灰的
—— 所以改成选完图就提示,不拦按钮点击(disabled 按钮根本不发 click 事件,实测)。
The negated-keyword check matched only `#N`, so "Refs codewhale-hq#1. Does not close https://github.com/<owner>/<repo>/issues/N" (or owner/repo#N) passed via the Refs line while GitHub still closed the issue on merge. Match all three reference forms GitHub closes on: #N, owner/repo#N, full issue URL. Refs codewhale-hq#6184 Checks run: - actionlint on pr-issue-link.yml: pass (0 findings); on all 6 lane workflows before the fix: pass (0 findings) - pr-issue-link run script vs 19 PR bodies with stub gh: 19/19 expected under BSD grep (macOS) and GNU grep 3.11 (ubuntu:24.04); before the fix 2 of 17 mismatched (negated owner/repo#N with Refs passed; Closes <URL> failed) - github-script bodies: node --check 4/4 ok - grep createComment .github/workflows: 0 hits Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
In a shared process the events golden drifted in parallel_tool_calls: its operation_activity span ids read #11/#12 against the recorded #2/#3 (whole codewhale-tui lib, three of three parallel runs; passes alone). Span ids end in a process-wide sequence so a consumer can deduplicate spans, and the goldens carry the numbers of one pass over the cases in an idle process (one_tool_call #1, parallel_tool_calls #2 and #3). Every span another test started in between shifted them. On the last hosted shared-process run this was hidden behind the earlier registry-count drift. The events replay now numbers spans from a thread-scoped sequence starting at 1 (test builds, unix only); production keeps the process-wide counter. No golden changes. Evidence: conformance:: 13 passed; 0 failed alone; whole codewhale-tui lib in one process: test result: ok. 14479 passed; 0 failed; 22 ignored. Drift captured in batch11/parallel-golden-drift.diff. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: CodeWhale Bot <bot@codewhale.net>
… the user sends them Refs #6978, CW-7427. @SergiuSSoluS reported that the same three old messages were answered again at every launch. The cause, traced in the thread: a session's parked queue file (sessions/checkpoints/<id>.offline_queue.json) was restored at launch, and its messages went out one by one after the user's next message. On their Windows machine the file's clear never reached disk, so every launch replayed them. The launch packet (codewhale-ops item 22) recommends not auto-sending restored messages. This implements that conservative default. It is reversible, and nothing ships until the next release; 0.10.2 is frozen. What - App.held_restored_queue counts the restored messages at the front of queued_messages. - restore_matching_offline_queue_state sets it to the restored count. That covers launch, --continue, /resume and every other session transition that installs a parked queue. - pop_queued_message, the only auto-dispatch (after TurnComplete), now takes the first message after the held ones. Messages queued in this session still send in order, ahead of held ones. - remove_queued_message (/queue send <n>, edit <n>, drop <n>) decrements the count when it removes a held message. - restore_queued_message re-inserting inside the held range increments it. - ↑ (pop_last_queued_into_draft) clamps it. - The double-tap steer drain, /queue clear, the session transition clear and /new reset it to 0. - The restore notice now says the messages are not sent automatically and names /queue send <n> and /queue clear. It is also written to the transcript: a resume's "Resumed session" status replaced the toast, which is why the reporter never saw it. All 15 locale packs are updated. - docs/ARCHITECTURE.md and the zh_hans twin (offline queue, item 4), and a CHANGELOG [Unreleased] bullet with the known limit: ↑ then Esc on a held message puts it back as an ordinary queued one. Verification - Real TUI (isolated tmux socket and HOME, local mock gateway). One turn, /quit. A parked queue file for that session with "queued alpha" and "queued beta". Then `codewhale --continue`, "fresh message", /queue, /queue send 1, /queue. - Before (installed 0.10.2, 91dbd4f): the mock received "fresh message", then "queued alpha", then "queued beta", all in the same second, with no user action. The transcript shows all three. - After (this tree): transcript note "Restored 2 queued message(s) from an earlier session. They are not sent automatically: /queue send <n> sends one, /queue clear discards them." Only "fresh message" was sent. /queue listed both. /queue send 1 sent "queued alpha". Beta stayed queued after alpha's turn ("Queued #1: queued beta"). - scripts/dev-test.sh tui queue offline_queue session_state dispatch tui::app tui::ui commands::tests commands::groups::core: 2061 tests run: 2061 passed (after the transcript-note edit; the earlier run of the same filters also passed). Localization: 54 passed. - CI-form clippy (workspace, all targets, all features, -D warnings with the CI allows): exit 0, no warnings. cargo fmt --check: clean. - npm test: root 101/101, runtime-sdk 19/19, extension-host 420 (413 pass, 7 skipped), web 7/7, vitest 772/772. npm run check:web: exit 0. npm --prefix web run prebuild regenerated web/data/agent-docs.generated.json for the ARCHITECTURE line. - sync-changelog --check: up to date. check-versions --require-dated-release: OK. Not verified: Windows, and the reporter's 99 MB session. The probe wrote the parked queue file itself, in the format the app writes; the reporter's underlying "clear never reaches disk" write failure is not fixed here, only made harmless. Seen while probing and not chased here: `codewhale --continue` after a killed TUI printed "Recovered interrupted session" and then refused with "session ... is open in an interactive Codewhale session"; the process-local live registry treats its own recovered session as live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NBKbhx19BRbZtvWK8rVKjz
Review exists to stop bad changes from merging at the lowest cost in human attention, and nothing measured either. #1 asks for a corpus built from our own history and a replay that asks, of each pull request that introduced a bug we later fixed, whether the reviewer would have caught it before merge. scripts/review-eval.mjs, one command, no model spend in the corpus half: - corpus: walks non-merge fix commits, blames each changed old line to the commit that introduced it, resolves that commit to its pull request, and keeps the case only when the replay is actually possible. Four checks do the filtering, and each drop is counted rather than skipped silently: the pull request head must be fetched from refs/pull/N/head; the introducing commit must be an ancestor of that head (a "(#1234)" subject can name another repository's pull request); the blamed line must exist at that head; and the line must be one the pull request itself added, compared against base..head, so a fix that touches pre-existing code is not scored as a missed defect. Oversized pull requests and non-code paths are excluded, both counted. - replay: runs a named reviewer over the corpus. `hosted-floor` calls the control plane's deterministic reviewer in-process (free, no model); `cli` runs the released binary's `codewhale review --json` with an optional money cap that stops the run and says so; `skill` runs the `/review` instructions through `exec`. Each case is written as its own JSON record so a rerun resumes past finished work and a partial run is auditable. - grade: recall at a line window and at same-file, claims and words per case (the attention-cost inputs), wall time, estimated cost from usage and an optional per-million rate, and a failure count. `report` diffs two grades so a later issue can state its delta on the same corpus. Checks: node --check passes; the corpus was built over 3363 fix commits and 15774 hunks from 2026-07-01 and the free deterministic-floor baseline was graded over it. The model-backed baselines for `cli` and `skill` are NOT run: #1 requires a founder-approved route and budget, and there is no approved eval budget, so this stops at the free baseline and asks rather than spending. Refs CW-7503, codewhale-agent #1
…s whether it applied #7: a docs typo and a billing migration were reviewed with the same fixed effort. `tools/review_triage.rs` now sets the budget from the diff alone, before any model call: - triage is a path and size read: sensitive paths (auth, billing, payment, migrations, crypto, secrets, permission/policy, sandbox, locking, deletion, schema) or a change of 400+ lines / 20+ files is high risk; API, route, server, config, database, storage and contract paths or 80+ lines / 6+ files is medium; everything else is low. Every reason is kept, so a receipt can explain its own tier instead of asserting one. - budget: low risk gets one step and no repository tools (the diff still gets reviewed), medium gets 4 steps and 6 tool calls, high gets 6 steps and 12 calls, with an output-token ceiling per tier. - the money cap is `--money-cap-usd` with `--input-usd-per-mtok` and `--output-usd-per-mtok`. A cap without a price cannot be enforced, so `money_limit_supported` stays false and the receipt reports the cap as unsupported rather than implying it ran out. The core checks the cap before each model step and again at the end. - a stop is named: money_cap, step_budget, tool_budget, output_limit or model_failure, and the summary carries a partial-review note naming the limit that stopped it and saying plainly that unreached files were not reviewed and are not thereby correct. It is never a silent truncation. Triage, budget, stop and spend travel on the tool metadata and the CLI JSON, so the same review can be audited after the fact. Checks: scripts/dev-cargo.sh check -p codewhale-tui --lib clean; scripts/dev-cargo.sh test -p codewhale-tui --lib tools::review = 80 passed, 0 failed (6 new triage tests: docs-low vs billing-migration-high, size and sensitive paths raising the tier independently, a cap without a price reported unsupported, cost over every billable counter, and the partial note naming its limit). Not claimed: cost per corpus pull request is not yet shown to fall with no recall loss, because the model-backed baseline is blocked on an approved eval route (see #1). The free deterministic-floor baseline is unaffected by this slice, since it never calls the core. Refs CW-7503, codewhale-agent #7
…tracts and the summary shape Four issues that share one schema change, because they are four questions about the same claim: what did it mean to do, what did it break, how do we know, and should you merge. #5 — intent is checked, not obeyed. `ReviewIntent` (title, description, commit messages, linked issue) travels as a named field of one untrusted JSON document, never as prose. The old `commit_intent_preamble` that spliced commit messages into the prompt body is deleted, and the PR prompt no longer embeds the description when the core carries it, so the author's claims exist once and in a field labelled as claims. The prompt asks for `kind: intent` when what the code does differs from what it says, in either direction: named and missing, or done and unannounced. #4 — changed contracts. `kind: contract` claims carry `became` (what the change made newly true or false) and `dependents_checked` (every dependent the reviewer looked at, including the ones that turned out fine). The dependents use the read-only tools the core already had; no index was built, because the corpus has not shown search to be the recall bottleneck. #6 — evidence tiers. Every claim carries `reproduced`, `traced` or `suspected`. A `reproduced` or `traced` claim without a detail is demoted, not trusted: the tier is a claim about evidence and an empty one is no evidence. Suspected claims leave the posted findings entirely and become the summary's "worth a human look" list, capped at three. A second model call with the same read-only tools then tries to refute each remaining claim; it can grade a claim `stands`, `refuted` or `unproven`, and it can add nothing, so the pass can only shrink a review. It runs on high-risk changes, where the budget already bought repository tools. #9 — the summary shape, one renderer for every forge: `render_review_summary` leads with merge risk and its reason, states what was not reviewed before the findings, prints each finding with its evidence tier and its changed contract, then what was verified, then the assessment. It does not restate the diff. `ForgeMarkdown` carries the one capability that changes the rendering: GitHub gets committable `suggestion` fences, a forge without them gets a fenced diff, and the two bodies are byte-identical otherwise. `render_review_markdown` now delegates to it, so the Engine has one renderer rather than two. Schema compatibility: every new field is optional with a default, so receipts and model output written before this change still parse; `kind` reads as `defect` and a missing tier reads as `suspected`. Checks: scripts/dev-cargo.sh check -p codewhale-tui --lib clean; cargo fmt -p codewhale-tui --check clean; scripts/dev-cargo.sh test -p codewhale-tui --lib tools::review = 82 passed, 0 failed. Four new tests: the core moves a suspected claim out of the posted findings into the summary; the summary's section order and evidence rendering; GitHub vs no-suggestion-forge bodies identical except the suggestion block; the severity scale. Updated the core tool-loop fixture to carry a traced and a suspected claim rather than one bare one. Not claimed, and why: #6's "calibration at least 90% for reproduced and traced claims" and #4's "recall improves on the corpus" and #5's "precision does not fall" are all measured quantities, and the model-backed corpus run is blocked on an approved eval route and budget (#1). `refute = true` is the high-risk path; whether it raises precision is the next number to measure, not something this commit asserts. Refs CW-7503, codewhale-agent #4 #5 #6 #9
…author, and the tally is machine readable
Two mechanisms from the competitive research note
(docs/research/competitive-code-review-20261022.md), plus the negative result
that makes a review's silence meaningful.
**Authorship is decided mechanically, never by the model.** `introduced_lines`
computes the lines this change actually added from the diff, and
`apply_claim_limits` sets `authorship` on every claim from it. A model that
labels its own accusation "pre-existing" is overruled. Three cases:
- the anchor is an added line → `introduced`;
- the anchor is inside a hunk but not added (unchanged context) → `preexisting`,
because the code was already there;
- no usable anchor → `preexisting`, because we cannot prove this change wrote
it and an accusation we cannot support is worse than a label we can.
**A pre-existing defect cannot hold the merge risk up on its own.** If every
claim is pre-existing, the finding signal is zero and the level is set to low,
keeping the model's reason and prefixing why. Enforced inside
`apply_claim_limits`, the one place claims are adjudicated, so every caller
inherits it. Structural risk from triage is separate and untouched.
**Pre-existing claims are never inline comments.** `inline_issue_comments`
skips them, so the author is not handed an inline accusation for code they did
not write; the summary renderer gives them their own section, after the
findings this change introduced.
**`ruled_out`: the negative result.** What the reviewer checked and found
sound, one line each, capped at five, rendered under "Checked, nothing wrong".
Distinct from `verified` (what established a claim) and from `not_reviewed`
(what was not looked at). It never carries a claim and never affects merge risk.
**A machine-readable tally.** Every summary ends with
`<!-- codewhale-review: {"blocker":N,"concern":N,"nit":N,"introduced":N,"preexisting":N} -->`
— integers and closed enums only, asserted by a test that feeds paths and prose
through it and checks neither appears.
Also: `inline_anchorable` is recorded per claim, so a claim whose line is not in
the diff is marked rather than assumed — the publisher turns that into the body
fallback.
Schema compatibility: every new field is optional with a default; `ruled_out`
and `authorship` are absent from older receipts and read as empty and
`introduced`.
Checks: `scripts/dev-cargo.sh check -p codewhale-tui --lib` clean; rustfmt
clean on the changed files; `scripts/dev-cargo.sh test -p codewhale-tui --lib
tools::review` = 86 passed, 0 failed, including four new tests: authorship
decided from the diff while the model's own label is overruled; an unanchorable
claim kept and marked instead of dropped; pre-existing findings alone forcing
merge risk to low while the model's reason survives; and the summary's section
order plus a tally that parses as JSON and contains no path.
Not claimed: the corpus delta. #18's done-when includes behaviour on real pull
requests, and the model-backed corpus run is still blocked on an approved eval
route and budget (#1).
Refs CW-7503, codewhale-agent #18 #19
…ar the release gates The 0.10.2 CHANGELOG stopped at 8bb5cc0; eighteen commits landed after it with no receipt, including a security fix and a breaking settings change. - Latest fixes: tool-call argument redaction, same-host provider redirects and the wider IPv6 network guard, bounded MCP template cache and fail-closed prefix allow rules (d1fd6f1, 38d7a09); the non-Latin auto-review crash (26155e9, #7048, credited to @8sero on all three credit surfaces); Computer Use consent in Full Access (ad697b3); the 16 MiB plugin state limit (1d44435). - Added: the shared review core, risk-scaled budgets, money cap, evidence tiers, refutation pass and pre-existing labelling (CW-7503: 42b7d19, 0ab0d4a, 1f40f4b, 5573bab). - Changed: transcript_spacing is compact | spacious, spacious default (39621ff). - The release date moves from 2026-10-19 to 2026-10-22; 0.10.2 is untagged. Gates: - check-feature-release-notes.sh: the CW-7503 commits cite issues in the private codewhale-agent repository as bare #4 #5 #6 #7 #9 #16 #18 #19 and quote "(#1234)" as an example. Codewhale's own #4-#19 are March/April roadmap items and #1234 an input-box request, none of them this work, so they join the existing false-positive skip list (which already holds that repo's #1). - check-contributor-credit.py: "Codewhale Agent <agent@codewhale.dev>" is the project's automation identity (CW-7495), so it joins "codewhale bot" in the bot skip set. Checks: scripts/release/prepare-release.sh 0.10.2 exit 0 (receipts OK, 17 linked references; Version state OK workspace=npm=npm-binary=0.10.2, lockfile in sync; OHOS linker, graph and bindgen OK); scripts/check-contributor-credit.py exit 0; scripts/sync-changelog.sh regenerated crates/tui/CHANGELOG.md. No compile: docs and scripts only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Test plan
cargo fmt --all -- --checkpasses🤖 Generated with Claude Code