Enhance tool evidence display and improve light mode contrast#35
Enhance tool evidence display and improve light mode contrast#35div0-space wants to merge 464 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces several major enhancements to CodeScribe, including an onboarding lane chooser (Basic vs. Agentic), an Agentic-lane readiness probe, grouped tool activity in the voice chat timeline, Light Mode semantic contrast fixes, and a post-LLM lexicon pass to prevent protected term corruption. The review comments correctly identify opportunities to improve Rust stable compatibility by avoiding unstable let-chains, optimize string truncation performance to prevent O(N) traversal on large inputs, and fix a bug where missing image files are incorrectly reported as valid vision inputs.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if let Some(idx) = state.active_tool_activity_index | ||
| && state | ||
| .messages | ||
| .get(idx) | ||
| .map(|msg| msg.role == ChatRole::ToolActivity) | ||
| .unwrap_or(false) | ||
| { | ||
| return idx; | ||
| } |
There was a problem hiding this comment.
Using let-chains (&& with let) is currently an unstable nightly feature in Rust. To ensure compatibility with stable Rust compilers, it is safer to use nested if let statements.
| if let Some(idx) = state.active_tool_activity_index | |
| && state | |
| .messages | |
| .get(idx) | |
| .map(|msg| msg.role == ChatRole::ToolActivity) | |
| .unwrap_or(false) | |
| { | |
| return idx; | |
| } | |
| if let Some(idx) = state.active_tool_activity_index { | |
| if state | |
| .messages | |
| .get(idx) | |
| .map(|msg| msg.role == ChatRole::ToolActivity) | |
| .unwrap_or(false) | |
| { | |
| return idx; | |
| } | |
| } |
| let capped = if text.chars().count() > MAX_BUBBLE_DISPLAY_CHARS { | ||
| let mut t: String = text.chars().take(MAX_BUBBLE_DISPLAY_CHARS).collect(); | ||
| t.push_str("\n… (truncated for display; full text preserved in the thread)"); | ||
| t | ||
| } else { | ||
| text.to_string() | ||
| }; |
There was a problem hiding this comment.
Calling text.chars().count() traverses the entire string to count characters, which is highly inefficient ($O(N)$) for pathological or extremely large inputs (e.g., pasted base64 blobs or giant log files). Since we only care if the string exceeds MAX_BUBBLE_DISPLAY_CHARS, we can use char_indices().nth(MAX_BUBBLE_DISPLAY_CHARS) to find the truncation boundary in
| let capped = if text.chars().count() > MAX_BUBBLE_DISPLAY_CHARS { | |
| let mut t: String = text.chars().take(MAX_BUBBLE_DISPLAY_CHARS).collect(); | |
| t.push_str("\n… (truncated for display; full text preserved in the thread)"); | |
| t | |
| } else { | |
| text.to_string() | |
| }; | |
| let capped = if let Some((idx, _)) = text.char_indices().nth(MAX_BUBBLE_DISPLAY_CHARS) { | |
| let mut t = text[..idx].to_string(); | |
| t.push_str("\n… (truncated for display; full text preserved in the thread)"); | |
| t | |
| } else { | |
| text.to_string() | |
| }; |
| if codescribe_core::attachment::image_media_type(path).is_some() { | ||
| let size = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0); | ||
| if size > codescribe_core::attachment::MAX_VISION_IMAGE_BYTES { | ||
| out.push_str(&format!( | ||
| "(image too large for vision input: {} bytes > {} max; not sent)\n", | ||
| size, | ||
| codescribe_core::attachment::MAX_VISION_IMAGE_BYTES | ||
| )); | ||
| } else { | ||
| out.push_str("(image detected; will be sent as vision input)\n"); | ||
| image_paths.push(display.to_string()); | ||
| } | ||
| } else { | ||
| out.push_str("(skipped: not UTF-8 text)\n"); | ||
| out.push_str("(skipped: unsupported image format or not UTF-8 text)\n"); | ||
| } |
There was a problem hiding this comment.
If the file does not exist or is unreadable, std::fs::metadata(path) will fail, and unwrap_or(0) will return 0. This causes the code to incorrectly report (image detected; will be sent as vision input) for a non-existent file. Handling the Result of metadata properly allows us to correctly report missing or unreadable files as skipped.
if codescribe_core::attachment::image_media_type(path).is_some() {
match std::fs::metadata(path) {
Ok(meta) => {
let size = meta.len();
if size > codescribe_core::attachment::MAX_VISION_IMAGE_BYTES {
out.push_str(&format!(
"(image too large for vision input: {} bytes > {} max; not sent)\n",
size,
codescribe_core::attachment::MAX_VISION_IMAGE_BYTES
));
} else {
out.push_str("(image detected; will be sent as vision input)\n");
image_paths.push(display.to_string());
}
}
Err(_) => {
out.push_str("(skipped: image file not found or unreadable)\n");
}
}
} else {
out.push_str("(skipped: unsupported image format or not UTF-8 text)\n");
}There was a problem hiding this comment.
Pull request overview
This PR improves the assistive voice-chat UX by (1) grouping and “de-wiring” tool evidence in the conversation timeline, (2) making image attachments reliably flow as real vision input with clearer user-visible error handling, and (3) improving Light Mode readability via deterministic contrast tokens; it also refines hotkey gating and adds an onboarding “Basic vs Agentic” lane with readiness probing.
Changes:
- Added grouped Tool Activity blocks (per assistant turn) and friendly tool labels/status messaging; tool failures now carry an explicit
is_errorbit to the UI. - Centralized image-attachment marker parsing + vision loading in
core::attachment, wired through both agent and legacy send paths, and added protection against empty image blocks. - Introduced deterministic Light/Dark contrast palettes (WCAG-checked) and added onboarding lane persistence + agentic readiness probe/step flow.
Reviewed changes
Copilot reviewed 40 out of 40 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| pico.save | Removed an editor scratch file from the repo. |
| docs/architecture/attachment-vision-pipeline.md | Added architecture doc for the attachment→vision pipeline and its shared marker contract. |
| core/tests/env_validation.rs | Tightened env-loader test isolation for llm_endpoint. |
| core/quality/qube_report.rs | Added protected-term loss detection to quality reporting. |
| core/pipeline/stream_postprocess.rs | Added operator vocabulary + protected terms lexicons; added lexicon re-apply and loss detection helpers; expanded tests. |
| core/llm/ai_formatting.rs | Reused shared attachment parsing/loading; increased image cap; applied lexicon post-LLM. |
| core/config/settings.rs | Added persisted onboarding_mode to settings (incl. env promotion + tests). |
| core/attachment.rs | Introduced shared image marker parsing + vision image loading + tests. |
| core/agent/session.rs | Tool-result summaries now surface first error message; propagates is_error to UI event. |
| core/agent/event.rs | Added is_error to AgentUiEvent::ToolResult for UI rendering. |
| assets/protected_terms.jsonl | Added curated protected-term lexicon entries (proper nouns/brands/tools). |
| assets/programming.jsonl | Updated Loctree canonical term/casing and expanded mispronunciations. |
| assets/operator_vocabulary.jsonl | Added Polish UI-command/operator normalization seed lexicon. |
| app/ui/voice_chat/tool_activity.rs | New pure module for per-turn grouped tool activity + evidence summaries + tests. |
| app/ui/voice_chat/state.rs | Added tool-activity group storage and “last sent attachments” state. |
| app/ui/voice_chat/mod.rs | Exported tool-activity APIs and module. |
| app/ui/voice_chat/handlers/menus.rs | Added “Re-attach previous” menu item; updated attachment menu logic. |
| app/ui/voice_chat/handlers/classes.rs | Registered new onAttachReattach: handler. |
| app/ui/voice_chat/handlers/attachments.rs | Implemented re-attach behavior for last-sent attachments. |
| app/ui/voice_chat/api/tests.rs | Added state-level tests for grouped tool activity rendering and turn boundaries. |
| app/ui/voice_chat/api/send.rs | Cleared attachments after send but preserved for re-attach; improved image honesty in attachment block. |
| app/ui/voice_chat/api/mod.rs | Imported tool-activity types used by the API layer. |
| app/ui/voice_chat/api/messages.rs | Added tool-activity record APIs; added bubble text capping/breaking to prevent CoreText hangs; added click toggling behavior. |
| app/ui/voice_chat/api/lifecycle.rs | Ensured tool-activity turn state resets on overlay clear. |
| app/ui/voice_chat/api/export.rs | Included Tool Activity role in markdown export labels. |
| app/ui/shared/helpers/mod.rs | Added deterministic Light/Dark contrast palette tokens + WCAG tests + appearance resolution. |
| app/ui/settings/engine_tab.rs | Added agentic readiness rows (gated by persisted onboarding mode); refactored tone→color mapping. |
| app/ui/onboarding/window.rs | Added onboarding lane chooser UI and readiness table UI scaffolding. |
| app/ui/onboarding/widgets.rs | Added mode radio syncing and a system orange color helper. |
| app/ui/onboarding/tests.rs | Added tests for onboarding mode persistence and lane-dependent step navigation. |
| app/ui/onboarding/steps.rs | Inserted Mode step; added AgenticReadiness step; kept stable index flow array. |
| app/ui/onboarding/state.rs | Added OnboardingModeChoice and initial mode selection from settings. |
| app/ui/onboarding/render.rs | Rendered Mode/AgenticReadiness steps; lane-aware sidebar markers; readiness table rendering. |
| app/ui/onboarding/handlers.rs | Added onModeSelected: handler wiring. |
| app/ui/onboarding/actions.rs | Added lane-aware step navigation; persisted onboarding mode; removed hardcoded full-disk step index. |
| app/controller/tests.rs | Updated hotkey gating tests; added predicate-level tests for assistive “Talk Anytime” vs raw start blocking. |
| app/controller/mod.rs | Allowed assistive start events during agent in-flight sends; kept Busy-state hotkey blocking. |
| app/controller/helpers.rs | Added friendly tool-name/status mapping; implemented image-attachment loading for agent send path; updated tool UI event handling; added tests. |
| app/agent/tools/mcp.rs | Added agentic readiness probe and tests; classified prerequisites including PRView detection heuristic. |
| app/agent/openai_provider.rs | Skipped empty image blocks when building provider input to avoid request rejection. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if codescribe_core::attachment::image_media_type(path).is_some() { | ||
| let size = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0); | ||
| if size > codescribe_core::attachment::MAX_VISION_IMAGE_BYTES { | ||
| out.push_str(&format!( | ||
| "(image too large for vision input: {} bytes > {} max; not sent)\n", | ||
| size, | ||
| codescribe_core::attachment::MAX_VISION_IMAGE_BYTES | ||
| )); | ||
| } else { | ||
| out.push_str("(image detected; will be sent as vision input)\n"); | ||
| image_paths.push(display.to_string()); | ||
| } | ||
| } else { |
| /// Load an image file as `(bytes, media_type)` for vision input. | ||
| /// | ||
| /// Returns `None` (with a warning) when the extension is not a vision-supported | ||
| /// image, the file is unreadable, or it exceeds `max_bytes`. |
| /// Loaded rules-only via `load_seed_jsonl` (seed format gives whole-word + | ||
| /// case control), so these common words never enter `protected_canonicals` and | ||
| /// never trip the downstream loss-detection gate. Canonicals were confirmed | ||
| /// real and high-frequency via `loct occurrences` before being chosen. |
| let mut chars = word.chars(); | ||
| if let Some(first) = chars.next() { | ||
| out.extend(first.to_uppercase()); | ||
| out.push_str(chars.as_str()); | ||
| } |
| assert_eq!( | ||
| friendly_tool_name("mcp__github__create_issue"), | ||
| "Create Issue · Github" | ||
| ); |
| assert_eq!( | ||
| tool_running_status("Create Issue · Github"), | ||
| "Running Create Issue · Github…" | ||
| ); |
| fn prettify_source(server: &str) -> String { | ||
| let cleaned: String = server | ||
| .chars() | ||
| .map(|c| if c == '-' || c == '_' { ' ' } else { c }) | ||
| .collect(); |
| fn build_responses_user_content(user_message: &str) -> Vec<InputContent> { | ||
| const MAX_IMAGES: usize = 4; | ||
| // Kept in sync with `MAX_AGENT_VISION_IMAGES` in the agent send path. | ||
| const MAX_IMAGES: usize = 16; |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Config::load() populates the Keychain, prompting for a password at app launch and on every hotkey-binding save even though bindings need no secrets. Use Config::load_without_keychain() for the binding-only seed and live-reload paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rites (PR #40 review) RecordingController::set_config had zero callers, so Settings writes never refreshed the live controller's config (language, AI formatting, hold delays stayed stale until restart; only env-backed atomics refreshed). The bridge config write paths (update_config, update_config_many, set_api_key) now schedule set_config(Config::load_without_keychain()) on the hotkey runtime the controller lives on. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#40 review) The Swift chat send hardcoded system_prompt/max_tokens = None, dropping the WORKSPACE-augmented assistive prompt and the configured ai_assistive_max_tokens that the in-app controller path applies via build_agent_stream_options. The bridge now composes the same system prompt (base assistive prompt + workspace_prompt_section) and token cap. Widens workspace_prompt_section to pub so the bridge crate can reuse it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…review)
The blind key-walk in collect_message_text recursed into structural fields,
leaking a restored image block's media_type ("image/png") and a tool_use
block's id/name into the displayed transcript. Switch to a type-aware
whitelist (text blocks + recursed tool_result content) mirroring
core::agent::thread_export::collect_text, preserving interior newlines.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ipt (PR #40 review) The authoritative sink (transcript_buffer -> paste/history) no-op'd ReplaceRange/InsertAnnotation, so under CODESCRIBE_LAYERED_TRANSCRIPTION=phase1 the overlay received a correction the pasted/saved text never did. Apply the bounded patch to the targeted committed utterance via the canonical EngineEvent::apply_to_committed_text (char-offset contract), re-rendering only when the buffer changed; out-of-range/uncommitted patches are dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…(PR #40 review) is_runtime_available only checked the macOS version, so AUTO always picked Apple even when codescribe-stt-bridge was absent, wasting a probe before silently falling back to Candle (a misleading selector). Add a cheap, process-cached is_bridge_resolvable (explicit override path exists, or the default command resolves on PATH) and gate the AUTO default_engine on it. Explicit CODESCRIBE_STT_ENGINE=apple still tries and fails loudly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tray_toggles read UserSettings::load() + Config::default(), skipping the .env/process-env tier — so SHOW_DOCK_ICON=0 (and the overlay/notes env knobs) were ignored in the menu. Use Config::load_without_keychain(), which merges the full tier stack while still never prompting the Keychain. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eview)
flatten_content collapsed every newline and fenced code block into one
paragraph via split_whitespace().join(" "); the earlier fix (0dce1e4) only
touched the bridge threads flatten, not this .md exporter. Preserve interior
whitespace, trimming block edges and joining blocks with a blank line, so
code fences and lists survive export.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…40 review) get_selected_text_length() (an expensive system-wide AX query) ran before the AX-selection early-return, though its result is used only in the Cmd+C fallback diagnostic. Move it into the fallback branch so the common AX-success path skips it entirely. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…#40 review) setup_done_refresh_target guards on !app_bundle_runtime, but the four permission_status() probes were evaluated as its call arguments beforehand, so dev/CLI runs paid four system-wide permission queries before bailing. Add an explicit app-bundle early-return before the probes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hide_hold_badge cleared state synchronously, but a show enqueued to the main queue before the hide would then create a window and set timer_running=true with nothing left to tear it down (stuck badge). Add a generation counter: a show captures it at request time, hide bumps it, and show_hold_badge_impl aborts (top-of-fn and again under the state lock) when the generation moved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add the layered transcription gate and per-lane LLM provider selectors to the env registry, then mirror only those three keys in the commented env example required by the registry E2E gate. Authored-By: codex <agents@vetcoders.io> session_id: f4c5e45c-2be3-4cb7-a12c-5f424f7636fe date: 2026-07-02 runtime: terminal
Update the current-facing docs so the user-facing app path is the SwiftUI bundle built by make app/install-app, and narrow the remaining command-line surface to the qube tools. Authored-By: codex <agents@vetcoders.io> session_id: f4c5e45c-2be3-4cb7-a12c-5f424f7636fe date: 2026-07-02 runtime: terminal
…+ body + trailery Authored-By: codex <agents@vetcoders.io> session_id: 5cf32d6a-8eb1-4c1b-a9a2-cb0b48dfad95 date: 2026-07-02 runtime: terminal
Port the PKCE, device-code, and loopback callback primitives from openai/codex with Apache-2.0 attribution. Store provider account tokens as serialized JSON through the existing Keychain bundle instead of writing auth.json. Authored-By: codex <agents@vetcoders.io> session_id: work-260702-160009-74000 date: 2026-07-02 runtime: Vibecrafted core runtime
Add lane-specific AuthMode resolution with ApiKey as the protected default. Expose an opt-in provider-account Authorization header helper that reads Keychain-backed account tokens and refreshes expired OpenAI tokens without changing request builders. Authored-By: codex <agents@vetcoders.io> session_id: work-260702-160009-74000 date: 2026-07-02 runtime: Vibecrafted core runtime
Expose account login status through the config bridge and add the Settings Keys row affordance for OpenAI. The Sign in with ChatGPT action stays disabled with the awaiting app registration tooltip until CODESCRIBE_OPENAI_OAUTH_CLIENT_ID is configured. Authored-By: codex <agents@vetcoders.io> session_id: work-260702-160009-74000 date: 2026-07-02 runtime: Vibecrafted core runtime
…ent id - LLM_OPENAI_OAUTH_CLIENT_ID promoted setting (settings.json V2 system section, non-secret app identity); empty value clears back to the "awaiting app registration" gate - client_id_for_provider resolves fresh settings → dev env CODESCRIBE_OPENAI_OAUTH_CLIENT_ID per call — a Keys-panel save applies without restart, no frozen env - account_status carries "signed in as <email>" from unverified id_token claims (display only; authorization rides the access token) - account_auth::access_token: raw auto-refreshing token for request builders that format their own Authorization header - tests: resolution order + no-restart pickup, id_token identity parse, settings.json roundtrip incl. empty-clears; hermetic settings dirs Authored-By: claude <agents@vetcoders.io>
… tokens - AssistiveLaneSnapshot.account_auth: OpenAI provider + official (key-requiring) endpoint + no API key + stored sign-in tokens ⇒ the lane is AVAILABLE with only a ChatGPT login; an explicit API key always wins; the account bearer never rides to non-official endpoints - availability reason now names the sign-in option alongside key/endpoint - OpenAiProvider fetches a fresh auto-refreshing account access token per request (account_auth::access_token); api-key path byte-identical - tests: account-only availability, key > account preference, no token leak to key-optional endpoints Note: includes interleaved uncommitted wave-w2 hunks in lane_truth.rs (formatting-lane identity refactor: model_for_provider, formatting_identity, anthropic_messages_endpoint) — inseparable at hunk level from this cut in the shared Living Tree; that work is wave-w2's, not this commit's author's. Authored-By: claude <agents@vetcoders.io>
…to-end - bridge: CsProviderOption.oauth_client_id (non-secret, editable); await_account_login blocks with a timeout and shuts the local callback server down on expiry (no zombie port; double-click cancels the previous attempt); cancel_account_login; sign_out_account - Keys panel ChatGPT row: client-id field (saves via the promoted LLM_OPENAI_OAUTH_CLIENT_ID key — applies without restart), live button states (gated → sign in → waiting spinner → signed in as <email>), sign-out affordance, honest timeout/failure notice inline - view-model: pending-login guard per provider, background await of the browser roundtrip, provider + agent-status refresh on completion Note: includes two small uncommitted wave-w2 hunks in bridge/src/config.rs (key_present → lane_truth::secret + import swap) — that work is wave-w2's. Authored-By: claude <agents@vetcoders.io>
… owner - cut thin wrappers: account_secret, openai_endpoint_for_account, openai_model_for_account — call-sites go straight to lane_truth - ai_formatting: get_mode_config/get_env_non_empty removed, semantic adapters over fresh lane_truth snapshots; one assistive_snapshot per send - one Anthropic endpoint normalizer (lane_truth), duplicates removed - contracts.rs: dead PostProcessor/PostprocessResult shells + ghost test cut - qube_report/thread_store: effective identity instead of raw boot env - fresh-settings child tests consolidated, all four assertions kept - worker-local balance +56/-118 (net -62); gates green: lint, cargo test 1146 passed, make check, semgrep 0 findings Authored-By: codex <agents@vetcoders.io>
…e (transcribing/final-pass/done/failed); non-activating preserved; auto-hide 12s on passive final; a11y ids on phases+transcript; zero reg on editable - OverlayState: committedSegments + utteranceId upsert ensures append (preview only replaces active tail); isFinalPass + statusText/meta/footer for phases; armAutoHide (12s const); copy now onClose; applySessionFinalised no longer collapses phase; listeningDisplay keeps assembly during final pass; waveform static in final pass. - DictationOverlayView: .accessibilityIdentifier on phase status, transcript area (live+formatted), phase footer. - Matches OverlayStateTests expectations and operator acceptance (append, phases visible, focus discipline, auto-hide, a11y). - Build: make app-bindings + make app → BUILD SUCCEEDED (no new Overlay warnings in compile). - Living tree: re-sliced, re-read before edits, small pack. Authored-By: codex <agents@vetcoders.io>
…th in Settings UI - LLMLaneEditor: consolidated twin endpoint/model override controls into local helpers (overrideTextField/saveOverrideButton/resetOverrideButton); same labels, actions, disable states, help and accessibility - SettingsViewModel: dead public surface cut — draftBinding(for:), setUseLocalStt(_:) (loct occurrences: definitions only) - no DesignSystem/Bridge/.rs changes; UI behavior identical - Settings balance: 3 files +282/-345 (net -63) - gates green: make app-bindings + make app BUILD SUCCEEDED, make lint, make check (Prettier, clippy, Semgrep 0 findings) Authored-By: codex <agents@vetcoders.io> Authored-By: junie <agents@vetcoders.io>
… record + lexicon candidates - New core/quality/overlay_quality.rs: QualityRecord, save to ~/.codescribe/quality/corrections.jsonl, extract_lexicon_candidates, append to lexicon.custom.jsonl, commit_overlay_correction. - Rust test: "uni agentka" → "Junie" produces candidate; commit writes record. - New bridge/src/quality.rs + wiring in lib.rs + CsError::Quality: commitOverlayQualityRecord FFI. - Swift OverlayState: deliveredText tracking at finalize, captureQualityIfEdited on Copy/Send/close calling the exact generated FFI. - Updated reexports, mod decls. - Zero Settings knobs, local only, builds on P0-B phases. - Verified: make lint clean, cargo test -p codescribe-core overlay_quality (4/4 inc. exact case), bindings gen succeeded (symbol matches), quality record + lexicon candidate emitted by the code path. Authored-By: grok <agents@vetcoders.io>
- route hold and toggle recording through the same event fanout\n- keep late corrections as patch events instead of duplicate finals\n- add regressions for hold preview/final and correction delivery Authored-By: junie <agents@vetcoders.io> Co-authored-by: Junie <junie@jetbrains.com>
- patch post-final corrections into the matching committed utterance - prove Fn hold lifecycle and one-buffer delivery with regressions - document shared hold/toggle engine and delivery parity Authored-By: codex <agents@vetcoders.io>
…matting default - Keep deterministic postprocess/lexicon ahead of RAW vs formatting delivery - Stop forcing toggle-adjudicated hands-off sessions to RAW when no hotkey override exists - Add controller pipeline tests for RAW lexicon behavior and settings-default mode resolution Authored-By: junie <agents@vetcoders.io> Co-authored-by: Junie <junie@jetbrains.com>
…ne menu label component - L2: discovery refresh through one provider/lane path; runtime rows and key rows read local lane/provider snapshots (no per-panel duplicates) - L3: SettingsMenuLabel shared component replaces EngineMenuLabel and Keys-private MenuLabel (post-cut literal: 0 hits each); chrome flag keeps prior visual shape (Engine chrome=false, Keys chrome=true) - no public SettingsViewModel signature churn (11 consumers) - gates green per L3: make app BUILD SUCCEEDED, make lint, make check Authored-By: junie <agents@vetcoders.io>
…n in account_auth - pre-PR audit P2-01 (PR #61): the EnvGuard test helper and the Drop restore arms carried bare `unsafe { env::set_var/remove_var }` blocks while every sibling module (lane_truth, key_liveness) documents them - test-side blocks get the canonical serialized-with-serial comment (verified: every EnvGuard-using test in this module is #[serial]) - production `clear_account_tokens` gets an honest comment: it clears the process-env token mirror on sign-out, a single user-driven action Authored-By: claude <agents@vetcoders.io>
…cy trade-off in hotkeys contract - app/controller/tests.rs: // SAFETY: comments on both unsafe env blocks in EnvVarGuard (set + Drop restore) — same pattern as account_auth/ai_formatting guards; the guard is only used from #[serial] tests (delta-audit D-03) - docs/HOTKEYS_CONTRACT.md: document the stop-latency trade-off that supersedes ADR 2026-05-28 Faza 1 force-RAW — formatting-on-stop is the user's Settings choice, surfaced as the 'final pass' overlay phase, with raw fallback on failure and two zero-latency escapes (formatting OFF / Dictation binding) (delta-audit D-01) Authored-By: claude <agents@vetcoders.io>
… + runnable XCTest scheme - OverlayState.captureQualityIfEdited: commitOverlayQualityRecord moves to Task.detached(priority: .utility) — the FFI appends quality JSONL on disk, and Copy/Send/Close must never block the main actor on file I/O (delta-audit D-02) - macos/project.yml: explicit Codescribe scheme with CodescribeTests as test target — xcodegen emits no scheme for bundle.unit-test targets and old-style -target builds cannot resolve SPM deps, so without this the staged test bundle was unreachable; verified with xcodebuild build-for-testing (TEST BUILD SUCCEEDED) - Known limit: xcodebuild test hosts the full app (CGEventTap/TCC bootstrap), so executing the suite needs an interactive TCC-granted session Authored-By: claude <agents@vetcoders.io>
…line - explicit Codescribe scheme (7afb253) displaced the autogenerated one and Release fell back to universal ARCHS; cargo emits a single-arch libcodescribe_ffi.dylib, so the x86_64 slice died on missing Rust symbols - verified: make app PROFILE=release + make install-app clean, build 801 installed and running Co-Authored-By: Klaudiusz <the1st@whoai.am>
…t handoff - agent/voice handoff: overlay fades out (0.18s) the moment on_turn_started opens the You-bubble in chat — no more lingering over the conversation it just fed (wired in VoiceDeliveryListener, OverlayController owns the fade) - placement is binary: six screen anchors (default top-right, under the tray) re-derived on every show(), OR free motion with the dragged origin persisted and clamped to the visible frame; picking an anchor exits free motion - config lives under the overlay's "…" icon (Position picker + Free motion toggle); prefs persisted via OverlayPlacement (UserDefaults) - default content size 470x330 → 320x600 portrait side-panel (size key v3 → v4 so the new default takes effect once); resize memory unchanged - OverlayPlacementTests: pure anchor/clamp geometry over a non-zero-origin visible frame (compiles into CodescribeTests; suite execution still blocked by the known host-app early-exit limit, same as 7afb253 / audit E-01) Co-Authored-By: Klaudiusz <the1st@whoai.am>
…): quality test isolation + coverage + XCTest build-for-testing + FFI endpoint normalize + raw_text wiring + test unwraps + timeout docs - P1-02: overlay_quality test now uses CODESCRIBE_DATA_DIR + EnvRestore + #[serial] + asserts path under temp (loct find verified no twin path logic). Proof of isolation in test. - D-02: added OverlayStateTests coverage for captureQualityIfEdited async non-blocking path on user edit (delivered != edited on .formatted). - E-01: xcodegen + xcodebuild build-for-testing -scheme Codescribe -destination platform=macOS succeeded (** TEST BUILD SUCCEEDED **). - P2-05: added normalize_openai_responses_endpoint to bridge/config (delegates lane_truth), wired through SettingsEngine (Real+Mock) + ViewModel (no suffix dupe, no new knobs). - D-05: wire delivered as rawText in commit (real best-effort; full raw requires listener sig change). - P2-07: replaced 2x .unwrap() with .expect in test helpers (app/controller/helpers.rs). - P2-08/P2-09: added rationale comments for 300s (OAuth human time, no knob) + cancel via supersede. - P3 triage: added SAFETY parity where cheap; documented non-touches and edges left (late correction to prior utterance, tooling nits). - Gates: cargo fmt/check/clippy targeted clean; xctest build ok; full cargo test + make check run in session (logs in transcript). - Boundaries respected: no EngineEvent/TranscriptDelta, no hand-edit Bridge/, no Settings knobs, no touch of junie-owned landing/README/CHANGELOG/PUBLIC... (concurrent mods left unstaged). - Loctree: slice/find/occurrences used before edits; no structural grep hak. Authored-By: grok <agents@vetcoders.io>
…ta FFI wiring + test coverage depth + endpoint dupe removal + SAFETY comments
- P1-02/P3: overlay_quality test already isolated via CODESCRIBE_DATA_DIR+serial+canon (loct verified); added SAFETY comment on unsafe set_var (parity with lane_truth etc); extended test asserts for record content.
- D-02: deepened coverage — test now verifies written JSONL contains action + delivered/edited (fire-and-forget exercised via copy path in OverlayStateTests).
- D-05 + P2-03 triage over-correct: commit_overlay_correction + FFI now take+forward action; meta carries {"source":"overlay-final","action":...}; only 1 source call site (loct confirmed); raw still best-effort delivered (listener surface extension out of scope per boundaries).
- P2-05 over-correct: removed 3-suffix list literal dupe from Swift fallback in resolvedOpenAIEndpoint (core normalize_openai... via engine is sole truth; fallback minimal /v1 strip only).
- FFI change: edited bridge/src/quality.rs + core; ran make app-bindings (generated updated, not hand-edited); Swift source updated to new throws sig + pass action.
- E-01/P2-08/P2-09/P2-07: verified landed in f48afad + worktree; xcodegen part of E-01 re-ran clean; docs comments for 300s/cancel already present; no .unwrap() left in helpers.
- Gates run: make lint (exit0 post-fmt), targeted cargo test (4/4 quality + env), clippy -D warnings clean on crates, xcode build-for-testing reached sign gate (known "Codescribe Dev" cert absent in env — **TEST BUILD FAILED** as expected per prior review).
- Boundaries: no EngineEvent/TranscriptDelta, no hand Bridge edit (only regen), no Settings knobs, junie files (README etc landing) left untouched in staging.
- Loctree: slice/impact/find --literal used before edits; no structural grep hak.
Authored-By: grok <agents@vetcoders.io>
…ality raw_text + expect in test helpers + discovery timeout docs - OverlayState.swift: track sttRawText from applyFinalTranscript / finalize (best-effort pre-format STT), pass as rawText in captureQualityIfEdited (D-05); clear on reset; delivered remains the pre-edit shown value. - model_discovery.rs: replace .unwrap() with .expect(...) in 5 test sites (P2-07); add justification comment on DISCOVERY_TIMEOUT=5s (P2-08/P2-09, no new knobs). - No FFI surface change → no app-bindings needed. - Verified: cargo check, quality tests, model_discovery tests, make check (semgrep 0, fmt clean). Gate: make check (exit 0); CODESCRIBE_DISABLE_KEYCHAIN=1 cargo test -p codescribe-core overlay_quality + model_discovery (all pass). Authored-By: grok <agents@vetcoders.io>
…st (raw+meta asserts, isolation proof) + expect in hotkey tests + fortify correction to patch any prior utterance (not only tail) - core/quality/overlay_quality.rs: stronger D-02 coverage; deserialize record; assert raw_text, action in meta; use expect - app/controller/tests.rs: P2-07 cheap over-correct: .await.unwrap() -> descriptive .expect() in several hold/toggle tests - app/presentation/emitter.rs: P3-03 over-correct: correction now scans committed from tail to patch matching previous_text (penultimate etc); added regression test that would have appended before Gates: - make lint: pass - CODESCRIBE_DISABLE_KEYCHAIN=1 cargo test -p codescribe-core overlay_quality + lib units: 725+ passed, 0 failed (targeted + full relevant) - make check: pass (fmt+clippy+semgrep 0) - E-01: xcodegen + xcodebuild build-for-testing -scheme Codescribe -destination platform=macOS (TEST BUILD FAILED only on missing "Codescribe Dev" cert — known, non-code) - specific new test correction_targets_penultimate_utterance_patches_instead_of_appending: ok Per spec: verified git log 878ff00.. (prior marbles landed P1-02/D-02/E-01/P2-05/D-05 etc); no dups; loct slice/impact/find before; no touch forbidden; EngineEvent untouched; no Settings knobs. Authored-By: grok <agents@vetcoders.io>
…tion+raw+action tests + expect in hotkeys + test mod move for clippy + E-01 evidence
- quality: 2 new serial tests for distinct raw, various actions (copy/send), long-edit no-lexicon; isolation proof + meta asserts (D-02/D-05/P2-03 over-correct)
- controller/tests: 4x unwrap() -> expect("... P2-07") in hotkey paths
- correction.rs: moved merge_tests mod to EOF (fixed items-after-test-module from concurrent edit); tests still pass
- E-01: executed xcodegen generate + xcodebuild build-for-testing (graph reached CodescribeTests; only signing cert blocker as documented prior)
- Verified via loct slices before edits; targeted cargo test -p codescribe-core overlay_quality: 6/6 green; cargo check clean
- Per operator: idempotent (no dups on landed from 878ff00), no forbidden files, no EngineEvent/Bridge edits, Living Tree adapted
Authored-By: grok <agents@vetcoders.io>
…ted text instead of replacing it - root cause of "append nie działa": the Refine lane re-decodes ONLY the audio slice taken by schedule_partial_pass (mem::take + 18s cap), but the receive arm did `accumulated_text = cleaned` — wiping everything before the correction window; a 52s hold session committed 37 chars (log 08:39, utterances=1 corrections=10) - window_text is now taken (and cleared) in LOCKSTEP with the audio buffer, so correction_expected_text always mirrors exactly the slice Refine sees - new merge_corrected_window(baseline, snapshot, corrected): rfind-anchors the snapshot inside the baseline and splices the corrected text in place (tail, or middle when previews landed while Refine ran); snapshot==baseline and snapshot-superset (post-boundary refine) keep whole-replace semantics; unanchored snapshot SUPPRESSES the correction instead of destroying text - Correction events now carry the merged full text, so Swift's replace semantics and the delivery lane both see the complete session - 7 merge unit tests + lockstep asserts in the schedule tests; streaming suite 104 passed, workspace green Co-Authored-By: Klaudiusz <the1st@whoai.am>
… in recent_entries - raw draft + post-processed final saved within one second collide into base.txt / base_1.txt (collision suffix in save_entry_with_timestamp_and_slug) and BOTH showed up in the tray history menu — half the rows were duplicates - recent_entries now dedups by (day dir, base stem, kind) after the newest-first sort, so the newest file of each family (the post-processed one) wins; different kinds at the same second stay separate rows - regression test: draft+final collapse to one row pointing at the _1 file, AssistantInterpretation at the same second survives as its own row Co-Authored-By: Klaudiusz <the1st@whoai.am>
…tive fade at finalize - default content size back to the LANDSCAPE 470x330 bar (portrait 320x600 was a mis-read of the spec); size key v4 -> v5 so the restored default takes effect once over the persisted portrait shape; resize memory intact - assistive sessions now fade the overlay at FINALIZE, not at on_turn_started (which lags by MCP registration + augmentation — 5-6s of a stale FINAL hanging over the chat); on_turn_started hide stays as the belt - OverlayController latches "session was assistive" across preparing/started/ stopped by polling the Rust tray status (CodescribeTrayStatus.currentStatus), because the controller clears its assistive flag right after the stop pipeline and mid-hold combo upgrades flip it while recording — a single read at finalize would race both Co-Authored-By: Klaudiusz <the1st@whoai.am>
…lost mid-request + roundtrip tests - server: ShutdownHandle::shutdown uses notify_one (stored permit) instead of notify_waiters — a cancel landing while the server task is mid-response (outside its select) was silently dropped, leaving a zombie callback port - tests: full sign-in roundtrip against a mock issuer (authorize URL → /auth/callback → code exchange → token store → block_until_done), forged-state rejection with cancellable pending login - tests: account_status three-state UI mapping (registration gate → not signed in → signed in as <email>) incl. mid-process settings pickup - bridge: start_account_login doc reflects settings-first client id resolution (LLM_OPENAI_OAUTH_CLIENT_ID, dev env fallback) Authored-By: claude <agents@vetcoders.io>
…ding retired for Astro site - README: "OpenAI Provider" section rewritten as "LLM Providers and Lanes" (formatting / assistive / Anthropic-compatible resolved via runtime lane truth, per-lane overrides in Settings -> Engine), gated Sign in with ChatGPT note, key liveness Test chips documented - CHANGELOG: 0.12.x unreleased section catches up (lane truth routing, quality loop MVP, overlay transcript phases, delivery idempotency, single-DMG truth) - PUBLIC_RELEASE_CHECKLIST: dual-DMG promise cut to the single notarized DMG; external-gaps dates refreshed to 2026-07-12 - docs/landing/* removed; pages.yml re-pointed at the Astro site/ workflow - KNOWN GAP (flagged by dispatcher): site/ does not exist in this branch yet — pages.yml only triggers on main, so nothing runs from this push, but the Astro site must land (or pages.yml revert) BEFORE this branch merges Authored-By: junie <agents@vetcoders.io>
…og + landing retired for Astro site" This reverts commit 80a8d36.
Sign in with ChatGPT + per-lane runtime truth + build provenance
This pull request introduces several improvements to tool execution handling, image attachment processing, and hotkey gating logic in the agent/controller pipeline. The changes focus on making tool names more user-friendly, improving the handling and error reporting of image attachments, and refining the logic for hotkey events during agent turns. Comprehensive unit tests are added for all new logic.
Tool Execution and Labeling Improvements:
friendly_tool_nameandtool_running_statusfunctions to map raw tool identifiers to concise, human-readable labels and provide user-friendly status messages during tool execution. Updated tool execution/result handling to use these labels and avoid showing raw wire names in the conversation timeline. [1] [2]Image Attachment Handling:
build_image_attachments_from_textto parse, load, and cap image attachments referenced in outgoing messages, ensuring images are properly forwarded as vision input and missing/unreadable images are reported to the user. [1] [2]Agent/Hotkey Gating Logic:
should_block_hotkey_during_agent_sendand related controller logic for clarity and testability. [1] [2] [3]Other Improvements:
These changes collectively improve user experience, reliability, and maintainability of the agent/controller interaction pipeline.## Summary
What changed, and why?
User Impact
What does this improve or prevent for a real CodeScribe user?
Runtime Impact
Verification
cargo fmt --allcargo clippy -- -D warningscargo testmake semgrepAdd targeted commands, screenshots, recordings, or release-artifact checks here:
Release Notes
Should this appear in
CHANGELOG.md?