Release 2026.7.20#237
Conversation
Give user messages a borderless tool-like shell with a header timestamp and copy control, deepen shared input wash, equalize header corner insets, standardize header icon buttons to icon-sm, and use a robot glyph for subtasks. Co-authored-by: Cursor <cursoragent@cursor.com>
Remove .agent-guides/ and .ori/ — stale local tooling state that doesn't belong in the repo.
whisper-rs now sits behind a whisper-native feature (whisper-metal implies it) instead of shipping in the default feature set, so a normal dev build no longer compiles whisper.cpp. Voice integration tests are gated on whisper-native accordingly. Runtime session creation no longer auto-downloads the Claude CLI if it's missing (auto_download_cli is now a deprecated no-op on the transport); provisioning goes through the explicit managed-agent install/repair flow instead. install-from-source.sh drops the now-unused auto-download build flag.
…thority Introduce the session event sequence as its own durable per-session delivery/claim watermark, allocated only by the SQLite session_event_sequence authority and kept separate from graph revision and transcript revision (documented in CONTEXT.md). Session state lookups now carry an explicit delivery_event_frontier instead of re-deriving it from last_event_seq, and live-turn evidence is judged from actual operation/interaction/transcript state rather than raw runtime presence. Add TranscriptScope (root vs. per-operation) so transcript row paging, projection, and the row ledger can be read scoped to a single subtask instead of only the root transcript — this is the backend half of scoped subtask transcript viewing; the frontend consumer lands separately. Also carries: session-creation-attempt idempotency for repeated "consumed" attempts, CLAUDE_HOME-relative source-path normalization for imported session files, and pure rustfmt reformatting in model_catalog.rs. This is a large, tightly-coupled cross-cutting change (60 files) — grouped as one commit because splitting the Rust module graph further would leave intermediate states that don't compile.
Add a dialog that pages through a subtask's own transcript, scoped via the
new backend TranscriptScope (kind: "operation"), instead of only showing
the root session transcript. TaskTranscriptDialogController tracks
open/loading state and revision-gated stale-page retries per (session,
panel, root row, operation) identity; TaskTranscriptDialogPresentation maps
that state to the AgentTaskDetailPresentation the row renderer consumes.
scene-content-viewport.svelte wires the controller in, syncing open dialogs
to graph revision changes and resolving a binding for each task row on
demand. transcript-viewport-row-renderer.svelte's props were reshaped to
take rowId/rowIndex/entry directly (plus taskDetailBindingFor) instead of a
single `rendered` blob, and transcript-rows-controller now requests root
row pages with an explicit `scope: { kind: "root" }`.
…reveal Retire the standalone attention-queue sidebar card (queue store, queue components, the "attention queue enabled" setting, and its persisted workspace/expanded state) in favor of inline session-attention reveal rows inside each project's session list, driven by the new store/session-attention and components/session-attention modules and the new panel-reveal in/out transition. Session-attention status icons use a new Cursor-theme status palette (--cursor-status-*) added to design-tokens.css/app.css. Also drops three other overlay surfaces that had accumulated alongside the queue: the legacy open-project-dialog (superseded by the unified add-repository/project-discovery flow), the file-explorer-modal (superseded by ProjectFileSystemDialog, already wired in main-app-view.svelte), and the source-control dialog / git-panel legacy panel state (github-badge and session-list no longer open a git dialog on click). main-app-view.svelte, main-app-view-state.svelte.ts, app-sidebar.svelte and related tests are touched by all of the above since they wired every one of these surfaces. This is a large, tangled UI-simplification pass touched by one team; kept as a single commit rather than force a false split between the queue rename and the dialog removals, since both land in the same call sites (main-app-view.svelte, panel-store.svelte.ts, workspace-store.svelte.ts).
Track hasLocalPendingSendIntent through the live-session presentation pipeline (tab-bar, urgency tabs, session-status-mapper, local-placeholder-mode) so a session that is still missing_canonical or lifecycle-"detached" right after an optimistic send shows a connecting/planning placeholder instead of an error state. Error surfacing now only fires on lifecycle.status === "failed", not on "detached" transiently. Also fixes graph-patch reducing so activity and the active-streaming-tail only reset to idle/null on an actual terminal turn state (Completed/Failed/ Cancelled) rather than whenever the command omits them, and makes composer config-commit completion apply the accepted provisional mode/model/autonomy values instead of re-reading committed state that may already have moved on. Renames the "planning_after_tool" local placeholder mode to "planning" and broadens when it shows (also triggers on a local pending send, not only a trailing completed tool).
Add a per-agent, provider-scoped "default model" that's pinned from the model selector (new AgentInputModelDefaultPin control, next to the favorite-star toggle) and persisted alongside favorites in agent-model-preferences-store. The default flows through toolbar model resolution (resolveToolbarModelId now falls back to it after the live and provisional model) and through session-connection-manager, which applies the agent's default model when switching modes if the current model isn't valid for the new mode. Also raises the favorites-affordance threshold from >=5 to >12 models and sorts model groups by catalog order instead of alphabetically.
Follow-up to the tool-call header polish pass: wrap AgentToolRead in the shared AgentToolCard surface, hide AgentToolTask's live "current tool" row once the task itself is no longer pending, and unify the chip shell's badge/inline radius on a single rounded-md surface (was rounded-sm for badges) via a new exported CHIP_SHELL_SURFACE constant.
Replace the fixed-line-clamp thinking viewport with a grid-template-rows
0fr<->1fr collapse animation (no JS measurement, spacing lives on the inner
element so the closed track never leaves a residual strip) and drop the now
-unused visibleLineCount/clampVisibleLineCount policy knobs.
Note: the duplicate desktop-side thinking-viewport-{policy,follow}.ts
modules (and their tests) were already deleted in the preceding
"drop local agent-tooling snapshots" commit — they were pre-staged in the
index before this cleanup pass started and got swept in there instead of
here.
The smooth token-reveal mode was missing its animation-name binding since a prior migration, so [data-sd-animate] elements never actually ran the reveal keyframes. Restore animation-name: var(--sd-animation, sd-acepeTokenReveal) plus explicit opacity/timing-function/fill-mode so the fade-in visibly plays, and rescope will-change to match. Also fixes a native-markdown text-node keying collision (nextKey wasn't applied to the word-index branch) and adds coverage for both.
Add a `select-project` CLI command that selects --project-path inside an exact --panel-id after read-only path resolution and rejects ambiguous project names, plus the supporting interact.ts driver (selectPanelProject) and schema types. Extends test coverage across the first-send and session-open probe summaries and the interact/cli tests.
WebKit can fire a contenteditable insertText with U+001D (group separator) on ArrowRight; block it in beforeinput and scrub any that still land in the DOM or the serialized message on input/focus/sync, so stray control characters never make it into a sent message.
The fallback formatter (used when a model has no canonical display metadata) was naively title-casing every word, turning "gpt-4" into "Gpt-4". Recognize common acronyms (GPT, AI, API, CLI, MCP, SDK, UI, URL) and keep version-like tokens (e.g. "5.6") glued to the preceding word, so "gpt-5.6-sol" reads as "GPT-5.6 Sol" instead of "Gpt-5.6-Sol".
Preview-pane launch config for running the desktop and website dev servers by name.
The per-word token-reveal pipeline was dead in production: its scene derived never evaluated (gated behind a dev-only trace flag), no reveal DOM was ever created, and streaming words painted instantly. Removed the whole system end to end ahead of a client-side presentation-buffer rebuild that needs no Rust-side timing. Deleted the AgentPanelScenePipelineController and its read models, the sceneEntries prop chain, buildTokenRevealCss / token-reveal-motion / token-reveal-scene-read-model, the markdown-prose sd-animate CSS, the native-markdown reveal props, and the Rust AssistantTextDelta pipeline (payload, envelope kind, live emission, runtime coalescing, RowTokenStream and clockAnchor projection state). Also removed reveal-text-projection and its scene-patch/stability helpers, which were collateral of the dead controller; their text-holding-across-canonical-blanks behavior is documented in docs/plans/2026-07-17-reveal-text-projection-spec-before-removal.md for the rebuild to reconsider. Kept the streaming-repro-lab shell (reveal calls neutralized) for the rebuild to re-point. activeStreamingTail, reduceTranscriptDelta, and the turnState-derived isStreaming are untouched — live text rendering is unaffected. Tree is knowingly red: pre-existing TranscriptScope-refactor Rust test failures (18 on the prior HEAD, 17 here) and the pre-existing model-selector display-name test remain; no new failures introduced.
Pure-TS engine that smooths bursty model output into a steady character drip. Accepts target-text updates and releases characters at a rate-adaptive cadence (msPerChar = min(cap, drainMs/remaining)) with sub-frame time-banking, a backlog snap cap, and drain-on-end / flush-on- boundary lifecycle. DOM-free with an injectable clock and frame scheduler; 13 deterministic pacing tests. Four modes: instant and block-fade pass text through whole, buffer and buffer-fade drip. Shared core for the website showcase and, later, the desktop transcript.
Design-review page at /design-system/streaming-reveal. Streams one simulated ~260-word assistant reply into four panels at once in identical ~15-word bursts so the reveal strategies can be compared in real time: instant, buffer (Zed-style adaptive drip), buffer+fade (drip + per-word opacity fade), and block-fade (whole blocks fade as they complete). Controls for drain-ms, burst interval, and reduced motion (defaults to the OS setting). Consumes @acepe/ui/streaming-reveal; no engine changes.
Adds a reveal="word"|"block" prop (default "none", so the transcript is unchanged). "word" fades each inline word as it first mounts; "block" fades each top-level block. Pure CSS, opacity-only, mount-driven — an element animates once when inserted, so with stable keys only newly revealed nodes fade, never the re-rendered tail. Word parts are now keyed by document position (word:index) rather than position+text, so a character-dripping tail word updates in place instead of re-mounting and re-firing its fade every keystroke. Rebuilds the streaming reveal the correct way: local, presentation-only, no clock sync or projection state.
All four panels now feed the engine's smoothed visibleText into the production NativeMarkdown component with a per-mode reveal prop, so the page shows each strategy against real markdown (code blocks, lists, emphasis) exactly as the app will render it. Replaces the three throwaway plain-text renderers and their block/word split helpers.
The text-inline key was minted from the global nextKey counter, which increments for every space and mark parsed before it — so a paragraph's key shifted each time it grew a word while streaming. That re-keyed the inline subtree and remounted every word span on every delta, restarting each word's reveal fade from zero (buffer-fade rendered as a perpetually washed-out, flickering block). Anchor the key to the first word's document position (firstWordIndex), which is stable as the paragraph streams in, so the subtree reconciles in place: only genuinely new words mount and fade. Fixes the long-failing "preserves existing word DOM nodes when streaming appends a new tail word" test, which was asserting exactly this. Verified live: tagged word nodes survive across frames and their fades complete and hold instead of restarting.
Top-level blocks were keyed by block.key, which derives from the global nextKey counter and shifts as an earlier or in-progress block gains content — so the streaming tail block remounted on later bursts and re-ran its block-fade animation (flicker). Key blocks by position instead: a streaming document only appends or grows its last block, so index keys keep each block's identity stable and only genuinely new blocks fade. Verified live: the in-progress list block now survives across bursts (faded once, holds opacity 1) where it previously remounted each burst.
block-fade was passthrough — it painted each burst just like instant, so its only distinction was a 250ms fade that read as noise. Now it drips like the buffer modes (paced, not burst) and its consumer reveals only the completed-block prefix (up to the last blank-line boundary), so a half-typed block stays hidden until it finishes, then surfaces and fades in as one unit. Result: blocks appear one at a time at the drip's pace — distinct from instant's burst and from buffer's character stream.
Interpose the presentation-buffer engine on the streaming assistant text at the markdown-text leaf: a per-tail controller mirrors the growing canonical text (setTarget, display-only — visibleText never flows back), and renders the smoothed prefix through NativeMarkdown with the matching reveal prop. Four modes selectable via a new "Streaming reveal" chat setting (persisted UserSettingKey chat_streaming_reveal_mode, default buffer): instant, buffer, buffer-fade (word), block-fade (whole blocks, completed-prefix only). Only live-streamed text animates — completed/replayed messages and the non-transcript MarkdownText uses (plan dialog, file panel) render raw. Honours reduced motion (engine + CSS), drains on stream end, flushes on tab-hidden, tears the controller down on unmount. Pure UI presentation; no canonical/GOD state touched.
Streaming the reveal fed a new, whole markdown string to NativeMarkdown every drip frame, which re-parsed AND re-rendered the entire document — O(message size) per frame. Measured live at ~26fps (avg 38ms/frame, p95 68ms), degrading as the reply grew (31ms → 44ms/frame at 7000 chars). createNativeMarkdownParser() reuses each block object whose source (token.raw) is unchanged since the previous parse, so a growing document yields referentially-stable blocks for its completed prefix; the position-keyed each then skips them and only the tail block re-renders. Also removes the vestigial `wordCount` prop (threaded everywhere, read nowhere) — it changed every frame and would have forced every block to re-render regardless. Per-frame work is now O(tail), not O(document).
The controller-creation effect seeded the buffer with `created.setTarget(text)`, which made the effect depend on `text` — so every streaming delta re-ran it, destroying and recreating the controller and restarting the drip from zero. The whole reply visibly reset and re-revealed ~hundreds of times per stream (measured: 223 text-shrink events in one focused stream). Seed via untrack() so the creation effect depends only on mode/reduced-motion; the feed effect keeps the controller current as text grows.
parseIncompleteMarkdown was a dead prop on NativeMarkdown — streaming text rendered partial syntax literally (`**bold` showed asterisks) then dropped the markers when the token closed, flashing on every bold/code/link as the buffer dripped through it. completeIncompleteMarkdown() now auto-closes the trailing dangling token (**, *, _, ~~, inline code, links, open code fence) before parsing, so partial syntax renders formatted immediately. Conservative by design: ambiguous cases (list markers, intraword _, space-flanked *, markers inside complete code spans/fences) are left as-is — a false auto-close that mangles text is worse than an occasional flash. 39 unit tests incl. anti-corruption + idempotency guards. Wired behind the prop markdown-text already passes as isStreaming.
The first auto-close pass blindly appended `**` on an odd count, which the character drip turned into flashes: an opener with no content yet (`x **`) became `x ****` (four literal asterisks), and a half-revealed close (`**bold*`) became `**bold***`. Net effect made streaming WORSE (marker shrinks 18 -> 25). completeTrailingRunMarker now has three cases: strip an opener that's immediately followed by whitespace/end (can't open emphasis per CommonMark left-flanking, avoids ****); finish a partial closing run by appending only the missing char; otherwise close normally. Adds 7 drip-state regression tests incl. a "no run of 3+ asterisks" guard.
A char drip through a multi-word bold (**every single**) hits "**every "
mid-token; case 3 appended the closer at the very end, producing "**every **"
whose space-before-`**` is an invalid close that renders literal asterisks.
Insert the closer after the last non-whitespace char instead, so the bold
region simply grows word by word ("**every** " then "**every single**") with
no marker flash. Adds multi-word drip regression cases.
The final drip artifact: the first `*` of a `**` opener is revealed alone
("reads *") for one frame before the second arrives, flashing a literal
asterisk. A marker followed by end-of-string can't open emphasis, so strip
a lone trailing `*`/`_` when it's preceded by whitespace (clearly an opener
attempt). Ambiguous closers ("word*"), space-flanked ("5 * 3"), list markers
and intraword underscores are left untouched. Live marker-shrinks now ~0.
…s enum The streaming-reveal-mode key was added to the TypeScript UserSettingKey union but never to the Rust UserSettingKey enum (the authority for get_user_setting/save_user_setting). Both load and save were failing silently with "unknown variant", so the picker never persisted and the mode was stuck on the in-memory default "buffer" (a drip with no fade) — selecting "Buffer + fade" appeared to do nothing and reset on reload. Add the variant, its as_str mapping, and its all-keys entry so the setting round-trips.
Scrolling the transcript micro-flickered near edit diffs. Instrumented live: no layout shift and no row position jumps, but <diffs-container> elements were added+removed ~2x/frame during scroll. The render effect tore down and rebuilt the pierre FileDiff (cleanUp + new FileDiff().render(), recreating <diffs-container>) whenever ANY tracked dep changed — and a fresh themeNames object reference threaded down through row reconciliation on each scroll frame re-triggered it. Guard the effect with a value-based signature (cacheKey + theme + theme names) plus container identity, so the diff rebuilds only on a real content/theme change or a genuinely new container, never as a side effect of scrolling. Predates the reveal work.
|
Too many files changed for review. ( |
There was a problem hiding this comment.
19 issues found across 852 files
Not reviewed (too large): packages/desktop/src/lib/acp/session-state/__tests__/agent-panel-graph-materializer.test.ts (~9,831 lines) - if these are generated or fixture files, add them to ignored paths to exclude them from future reviews.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".claude/launch.json">
<violation number="1" location=".claude/launch.json:7">
P1: The `desktop` launch profile starts `vite dev` instead of the Tauri app, so native commands and the Rust backend are unavailable during a launch. Using the package's `tauri` script here keeps this profile aligned with the documented desktop workflow.</violation>
<violation number="2" location=".claude/launch.json:13">
P2: The launch profiles are not portable because their working directories point at a developer-specific absolute checkout. Repository-relative working directories (or the launcher's workspace-root variable) would let other contributors use these profiles.</violation>
</file>
<file name="packages/desktop/src-tauri/src/db/repository/session_event_sequence.rs">
<violation number="1" location="packages/desktop/src-tauri/src/db/repository/session_event_sequence.rs:13">
P2: A valid zero frontier in `session_event_sequence` now makes every caller of `last_assigned_event_seq` fail instead of treating it as the initial frontier. The repository validation should match the database invariant (accept zero, or tighten/migrate the schema to guarantee that rows can never contain zero).</violation>
</file>
<file name="packages/desktop/src-tauri/src/acp/transcript_viewport/ledger.rs">
<violation number="1" location="packages/desktop/src-tauri/src/acp/transcript_viewport/ledger.rs:28">
P3: The projection constant now advertises v22, but the migration note for this change is labeled v20. Keeping those identifiers inconsistent makes future ledger invalidation history ambiguous; the note should use the version actually introduced here or document the intervening versions.</violation>
</file>
<file name="packages/desktop/src-tauri/src/cc_sdk/types/options.rs">
<violation number="1" location="packages/desktop/src-tauri/src/cc_sdk/types/options.rs:165">
P3: Rust callers can continue using `auto_download_cli` without any compiler deprecation warning, even though both public APIs are documented as deprecated. Adding `#[deprecated(...)]` to the field and builder method, or removing the deprecated wording, would keep the public API signal consistent.</violation>
</file>
<file name="packages/desktop/scripts/acepe-qa/first-send-probe-summary.ts">
<violation number="1" location="packages/desktop/scripts/acepe-qa/first-send-probe-summary.ts:71">
P2: The new fail check can report success while the planning row is hidden: any hidden row whose text remains in the target panel makes `bodyText.includes("Planning next moves")` true, so `!sample.planningVisible` is false. The sampler should derive this signal only from an actually visible planning row before this gate relies on it.</violation>
</file>
<file name="packages/desktop/src-tauri/src/acp/transcript_viewport/projection.rs">
<violation number="1" location="packages/desktop/src-tauri/src/acp/transcript_viewport/projection.rs:77">
P1: Nested transcript rows under non-Task operations are now projected into canonical operation scopes, but the ledger scope discovery still skips their provider-alias entries and does not add the rewritten scope. As a result, those rows are omitted from persisted/pageable transcript views; scope discovery should normalize the entry scope (or add the mapped operation scope) before applying the alias filter.</violation>
</file>
<file name="packages/desktop/src-tauri/src/lib.rs">
<violation number="1" location="packages/desktop/src-tauri/src/lib.rs:5">
P3: Every function in this crate now bypasses `clippy::too_many_arguments`, allowing unrelated or future command and persistence APIs to grow parameter lists without review feedback. Keeping the exemption on only the specific stable APIs preserves the lint elsewhere.</violation>
<violation number="2" location="packages/desktop/src-tauri/src/lib.rs:7">
P2: All enums in the crate now bypass `clippy::large_enum_variant`, so newly added serialized variants can introduce unnecessarily large enum layouts without a diagnostic. The exemption should remain on only the specific enums whose construction API must stay unboxed.</violation>
</file>
<file name="packages/desktop/src-tauri/src/acp/ui_event_dispatcher/dispatcher.rs">
<violation number="1" location="packages/desktop/src-tauri/src/acp/ui_event_dispatcher/dispatcher.rs:336">
P2: Interaction responses can now publish a non-advancing graph revision when the session projection exists but its runtime lifecycle checkpoint is missing, because the registry's default graph revision (0) replaces the event-sequence fallback. Preserving the `last_event_seq` fallback for this recovery path would keep the interaction delta ordered and consumable.</violation>
</file>
<file name="packages/desktop/src-tauri/src/acp/session_restore/tool_link_audit.rs">
<violation number="1" location="packages/desktop/src-tauri/src/acp/session_restore/tool_link_audit.rs:56">
P2: The stored-entry audit now reports `scoped_entry_count` as zero for sessions containing nested task operations, because `from_stored_entries` materializes only root-scoped entries and drops child scopes. This makes the new diagnostic inconsistent with the materialized audit; deriving the count from the scoped fold/projection or recursively materializing child entries would preserve the intended signal.</violation>
</file>
<file name="packages/desktop/src-tauri/Cargo.toml">
<violation number="1" location="packages/desktop/src-tauri/Cargo.toml:109">
P1: Rust CI and the desktop release build now fail before compilation because both workflows still pass `--features auto-download`, but this feature was removed from the manifest. Updating those invocations to the intended feature set (or retaining a compatibility alias) would keep the release gate buildable.</violation>
</file>
<file name="packages/desktop/src-tauri/src/db/repository/session_journal.rs">
<violation number="1" location="packages/desktop/src-tauri/src/db/repository/session_journal.rs:85">
P3: Session journal writes now have two independent transaction implementations, so future changes to sequence allocation, rollback, or insertion behavior can silently apply to only one event type. A shared helper that builds the payload after allocation would keep these paths consistent.</violation>
<violation number="2" location="packages/desktop/src-tauri/src/db/repository/session_journal.rs:88">
P3: Replay coverage does not verify the new `responded_at_event_seq` persistence. An assertion that the replayed question has `Some(3)` would guard the behavior this assignment is intended to provide.</violation>
</file>
<file name="packages/desktop/src-tauri/src/acp/commands/session_commands/state_lookup.rs">
<violation number="1" location="packages/desktop/src-tauri/src/acp/commands/session_commands/state_lookup.rs:100">
P1: Active assistant streaming turns are treated as stale when no tool or interaction is pending. The trailing-entry check should also recognize `TranscriptEntryRole::Assistant`, matching `select_active_streaming_tail`, so `acp_get_session_state` preserves the running turn and partial assistant output.</violation>
</file>
<file name="packages/desktop/src-tauri/src/voice/commands.rs">
<violation number="1" location="packages/desktop/src-tauri/src/voice/commands.rs:73">
P2: Multilingual voice users can no longer select languages other than English because `voice_list_languages` now returns only Auto and English. Restoring the backend-supported language enumeration, while retaining Auto, keeps the language picker consistent with multilingual model support.</violation>
</file>
<file name="packages/desktop/src-tauri/src/acp/projections/operations.rs">
<violation number="1" location="packages/desktop/src-tauri/src/acp/projections/operations.rs:169">
P2: An older durable response can overwrite a newer response in the live projection when concurrent reply tasks finish out of order: this unconditional insert replaces the current interaction regardless of `responded_at_event_seq`. Retaining the current snapshot when its response sequence is greater than or equal to `event_seq` would keep the projection consistent with journal order.</violation>
</file>
<file name="packages/desktop/src-tauri/src/acp/client_updates/mod.rs">
<violation number="1" location="packages/desktop/src-tauri/src/acp/client_updates/mod.rs:48">
P2: These terminal mappings are not applied by the production canonical transcript fold: terminal updates return before the fold path consumes `ingress_fold_event`. The added test therefore validates only helper output, not production folding; either route terminal events through the canonical fold or remove this pre-normalized event plumbing.</violation>
</file>
<file name="packages/desktop/src-tauri/src/acp/client/cc_sdk_client/mod.rs">
<violation number="1" location="packages/desktop/src-tauri/src/acp/client/cc_sdk_client/mod.rs:432">
P2: Stopping or reconnecting while the initial stream is waiting for provider identity can leave the creation attempt permanently pending. Because this line removes the ID from the client and the bridge's failure cleanup is skipped when its task is aborted, the abort path should retain and explicitly fail/hand off the attempt.</violation>
</file>
Note: This PR contains a large number of files. cubic only reviews up to 200 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
Re-trigger cubic
| { | ||
| "name": "desktop", | ||
| "runtimeExecutable": "bun", | ||
| "runtimeArgs": ["run", "--cwd", "/Users/alex/Documents/acepe/packages/desktop", "dev"], |
There was a problem hiding this comment.
P1: The desktop launch profile starts vite dev instead of the Tauri app, so native commands and the Rust backend are unavailable during a launch. Using the package's tauri script here keeps this profile aligned with the documented desktop workflow.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .claude/launch.json, line 7:
<comment>The `desktop` launch profile starts `vite dev` instead of the Tauri app, so native commands and the Rust backend are unavailable during a launch. Using the package's `tauri` script here keeps this profile aligned with the documented desktop workflow.</comment>
<file context>
@@ -0,0 +1,17 @@
+ {
+ "name": "desktop",
+ "runtimeExecutable": "bun",
+ "runtimeArgs": ["run", "--cwd", "/Users/alex/Documents/acepe/packages/desktop", "dev"],
+ "port": 1420
+ },
</file context>
| scope: &TranscriptScope, | ||
| ) -> Vec<TranscriptViewportRow> { | ||
| let operation_links_by_entry = operation_links_by_entry_id(entries, operations); | ||
| let normalized_entries = normalize_entry_scopes(entries, operations); |
There was a problem hiding this comment.
P1: Nested transcript rows under non-Task operations are now projected into canonical operation scopes, but the ledger scope discovery still skips their provider-alias entries and does not add the rewritten scope. As a result, those rows are omitted from persisted/pageable transcript views; scope discovery should normalize the entry scope (or add the mapped operation scope) before applying the alias filter.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/desktop/src-tauri/src/acp/transcript_viewport/projection.rs, line 77:
<comment>Nested transcript rows under non-Task operations are now projected into canonical operation scopes, but the ledger scope discovery still skips their provider-alias entries and does not add the rewritten scope. As a result, those rows are omitted from persisted/pageable transcript views; scope discovery should normalize the entry scope (or add the mapped operation scope) before applying the alias filter.</comment>
<file context>
@@ -74,10 +74,11 @@ fn project_transcript_viewport_entry_rows_for_scope(
scope: &TranscriptScope,
) -> Vec<TranscriptViewportRow> {
- let operation_links_by_entry = operation_links_by_entry_id(entries, operations);
+ let normalized_entries = normalize_entry_scopes(entries, operations);
+ let operation_links_by_entry = operation_links_by_entry_id(&normalized_entries, operations);
let interaction_links_by_operation = interaction_links_by_operation_id(interactions);
</file context>
| [features] | ||
| default = ["whisper-metal", "auto-download"] | ||
| auto-download = [] | ||
| default = [] |
There was a problem hiding this comment.
P1: Rust CI and the desktop release build now fail before compilation because both workflows still pass --features auto-download, but this feature was removed from the manifest. Updating those invocations to the intended feature set (or retaining a compatibility alias) would keep the release gate buildable.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/desktop/src-tauri/Cargo.toml, line 109:
<comment>Rust CI and the desktop release build now fail before compilation because both workflows still pass `--features auto-download`, but this feature was removed from the manifest. Updating those invocations to the intended feature set (or retaining a compatibility alias) would keep the release gate buildable.</comment>
<file context>
@@ -103,14 +106,13 @@ libc = "0.2"
[features]
-default = ["whisper-metal", "auto-download"]
-auto-download = []
+default = []
manual-test-targets = []
-websocket = []
</file context>
| || transcript_snapshot | ||
| .entries | ||
| .last() | ||
| .is_some_and(|entry| entry.role == TranscriptEntryRole::User) |
There was a problem hiding this comment.
P1: Active assistant streaming turns are treated as stale when no tool or interaction is pending. The trailing-entry check should also recognize TranscriptEntryRole::Assistant, matching select_active_streaming_tail, so acp_get_session_state preserves the running turn and partial assistant output.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/desktop/src-tauri/src/acp/commands/session_commands/state_lookup.rs, line 100:
<comment>Active assistant streaming turns are treated as stale when no tool or interaction is pending. The trailing-entry check should also recognize `TranscriptEntryRole::Assistant`, matching `select_active_streaming_tail`, so `acp_get_session_state` preserves the running turn and partial assistant output.</comment>
<file context>
@@ -77,6 +77,29 @@ pub(super) fn resolve_state_lookup_authority(
+ || transcript_snapshot
+ .entries
+ .last()
+ .is_some_and(|entry| entry.role == TranscriptEntryRole::User)
+}
+
</file context>
| .is_some_and(|entry| entry.role == TranscriptEntryRole::User) | |
| .is_some_and(|entry| { | |
| matches!( | |
| &entry.role, | |
| TranscriptEntryRole::User | TranscriptEntryRole::Assistant | |
| ) | |
| }) |
| { | ||
| "name": "website", | ||
| "runtimeExecutable": "bun", | ||
| "runtimeArgs": ["run", "--cwd", "/Users/alex/Documents/acepe/packages/website", "dev"], |
There was a problem hiding this comment.
P2: The launch profiles are not portable because their working directories point at a developer-specific absolute checkout. Repository-relative working directories (or the launcher's workspace-root variable) would let other contributors use these profiles.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .claude/launch.json, line 13:
<comment>The launch profiles are not portable because their working directories point at a developer-specific absolute checkout. Repository-relative working directories (or the launcher's workspace-root variable) would let other contributors use these profiles.</comment>
<file context>
@@ -0,0 +1,17 @@
+ {
+ "name": "website",
+ "runtimeExecutable": "bun",
+ "runtimeArgs": ["run", "--cwd", "/Users/alex/Documents/acepe/packages/website", "dev"],
+ "port": 5173
+ }
</file context>
| // v17 persists independently pageable canonical transcript scopes and widens | ||
| // parent Task rows with graph-derived child scope/action facts. | ||
| pub const TRANSCRIPT_ROW_LEDGER_PROJECTION_VERSION: &str = "transcript_viewport_row:v17"; | ||
| // v20 invalidates rows that were built before restored raw Claude sidechain |
There was a problem hiding this comment.
P3: The projection constant now advertises v22, but the migration note for this change is labeled v20. Keeping those identifiers inconsistent makes future ledger invalidation history ambiguous; the note should use the version actually introduced here or document the intervening versions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/desktop/src-tauri/src/acp/transcript_viewport/ledger.rs, line 28:
<comment>The projection constant now advertises v22, but the migration note for this change is labeled v20. Keeping those identifiers inconsistent makes future ledger invalidation history ambiguous; the note should use the version actually introduced here or document the intervening versions.</comment>
<file context>
@@ -25,7 +25,9 @@ use serde::{Deserialize, Serialize};
// v17 persists independently pageable canonical transcript scopes and widens
// parent Task rows with graph-derived child scope/action facts.
-pub const TRANSCRIPT_ROW_LEDGER_PROJECTION_VERSION: &str = "transcript_viewport_row:v17";
+// v20 invalidates rows that were built before restored raw Claude sidechain
+// files were discovered from Claude's per-session subagents directory.
+pub const TRANSCRIPT_ROW_LEDGER_PROJECTION_VERSION: &str = "transcript_viewport_row:v22";
</file context>
| // v20 invalidates rows that were built before restored raw Claude sidechain | |
| // v22 invalidates rows that were built before restored raw Claude sidechain |
| #[allow(clippy::type_complexity)] | ||
| pub stderr_callback: Option<Arc<dyn Fn(&str) + Send + Sync>>, | ||
| /// Automatically download Claude Code CLI if not found | ||
| /// Deprecated compatibility flag. |
There was a problem hiding this comment.
P3: Rust callers can continue using auto_download_cli without any compiler deprecation warning, even though both public APIs are documented as deprecated. Adding #[deprecated(...)] to the field and builder method, or removing the deprecated wording, would keep the public API signal consistent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/desktop/src-tauri/src/cc_sdk/types/options.rs, line 165:
<comment>Rust callers can continue using `auto_download_cli` without any compiler deprecation warning, even though both public APIs are documented as deprecated. Adding `#[deprecated(...)]` to the field and builder method, or removing the deprecated wording, would keep the public API signal consistent.</comment>
<file context>
@@ -162,24 +162,10 @@ pub struct ClaudeCodeOptions {
#[allow(clippy::type_complexity)]
pub stderr_callback: Option<Arc<dyn Fn(&str) + Send + Sync>>,
- /// Automatically download Claude Code CLI if not found
+ /// Deprecated compatibility flag.
///
- /// When enabled, the SDK will automatically download and cache the Claude Code
</file context>
| // consumers are target- or feature-specific. Keep these cohesive APIs available. | ||
| #![allow(dead_code)] | ||
| // Tauri commands and canonical persistence functions have stable, explicit argument lists. | ||
| #![allow(clippy::too_many_arguments)] |
There was a problem hiding this comment.
P3: Every function in this crate now bypasses clippy::too_many_arguments, allowing unrelated or future command and persistence APIs to grow parameter lists without review feedback. Keeping the exemption on only the specific stable APIs preserves the lint elsewhere.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/desktop/src-tauri/src/lib.rs, line 5:
<comment>Every function in this crate now bypasses `clippy::too_many_arguments`, allowing unrelated or future command and persistence APIs to grow parameter lists without review feedback. Keeping the exemption on only the specific stable APIs preserves the lint elsewhere.</comment>
<file context>
@@ -1,3 +1,11 @@
+// consumers are target- or feature-specific. Keep these cohesive APIs available.
+#![allow(dead_code)]
+// Tauri commands and canonical persistence functions have stable, explicit argument lists.
+#![allow(clippy::too_many_arguments)]
+// Boxing these serialized domain variants would change their public construction API.
+#![allow(clippy::large_enum_variant)]
</file context>
| let tx = db.begin().await?; | ||
| let event_seq = | ||
| SessionEventSequenceRepository::allocate_in_transaction(&tx, session_id).await?; | ||
| interaction.responded_at_event_seq = Some(event_seq.get()); |
There was a problem hiding this comment.
P3: Replay coverage does not verify the new responded_at_event_seq persistence. An assertion that the replayed question has Some(3) would guard the behavior this assignment is intended to provide.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/desktop/src-tauri/src/db/repository/session_journal.rs, line 88:
<comment>Replay coverage does not verify the new `responded_at_event_seq` persistence. An assertion that the replayed question has `Some(3)` would guard the behavior this assignment is intended to provide.</comment>
<file context>
@@ -78,14 +78,23 @@ impl SessionJournalEventRepository {
+ let tx = db.begin().await?;
+ let event_seq =
+ SessionEventSequenceRepository::allocate_in_transaction(&tx, session_id).await?;
+ interaction.responded_at_event_seq = Some(event_seq.get());
+ let event = SessionJournalRecord::new(
session_id,
</file context>
| db, | ||
| tracing::debug!(session_id = %session_id, "Appending interaction snapshot journal event"); | ||
|
|
||
| let tx = db.begin().await?; |
There was a problem hiding this comment.
P3: Session journal writes now have two independent transaction implementations, so future changes to sequence allocation, rollback, or insertion behavior can silently apply to only one event type. A shared helper that builds the payload after allocation would keep these paths consistent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/desktop/src-tauri/src/db/repository/session_journal.rs, line 85:
<comment>Session journal writes now have two independent transaction implementations, so future changes to sequence allocation, rollback, or insertion behavior can silently apply to only one event type. A shared helper that builds the payload after allocation would keep these paths consistent.</comment>
<file context>
@@ -78,14 +78,23 @@ impl SessionJournalEventRepository {
- db,
+ tracing::debug!(session_id = %session_id, "Appending interaction snapshot journal event");
+
+ let tx = db.begin().await?;
+ let event_seq =
+ SessionEventSequenceRepository::allocate_in_transaction(&tx, session_id).await?;
</file context>
There was a problem hiding this comment.
1 issue found across 8 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/desktop/src/lib/acp/components/agent-panel/components/agent-panel-resize-edge.svelte">
<violation number="1" location="packages/desktop/src/lib/acp/components/agent-panel/components/agent-panel-resize-edge.svelte:9">
P2: Keyboard and screen-reader users still cannot resize the agent panel: this exposes a static separator while the only resize path remains pointer dragging. If this is intended to be an accessible resizer, implement a focusable splitter with keyboard resizing and updated `aria-valuenow`/min/max (plus an accessible name); otherwise keep the pointer hit target from claiming an interactive separator role.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
|
||
| <!-- Wider invisible hit target, inset to avoid overlapping header/footer controls. --> | ||
| <div | ||
| role="separator" |
There was a problem hiding this comment.
P2: Keyboard and screen-reader users still cannot resize the agent panel: this exposes a static separator while the only resize path remains pointer dragging. If this is intended to be an accessible resizer, implement a focusable splitter with keyboard resizing and updated aria-valuenow/min/max (plus an accessible name); otherwise keep the pointer hit target from claiming an interactive separator role.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/desktop/src/lib/acp/components/agent-panel/components/agent-panel-resize-edge.svelte, line 9:
<comment>Keyboard and screen-reader users still cannot resize the agent panel: this exposes a static separator while the only resize path remains pointer dragging. If this is intended to be an accessible resizer, implement a focusable splitter with keyboard resizing and updated `aria-valuenow`/min/max (plus an accessible name); otherwise keep the pointer hit target from claiming an interactive separator role.</comment>
<file context>
@@ -6,6 +6,8 @@ let { isDragging, onPointerDown, onPointerMove, onPointerUp }: AgentPanelResizeE
<!-- Wider invisible hit target, inset to avoid overlapping header/footer controls. -->
<div
+ role="separator"
+ aria-orientation="vertical"
class="absolute top-7 bottom-7 right-0 z-10 flex w-3 cursor-col-resize justify-end touch-none"
</file context>
Summary
Verification
Summary by cubic
Ships a rebuilt streaming reveal, scoped task transcripts, and a hardened session event sequence, plus review/composer UX upgrades, updated build gates, and CI reliability fixes. Removes a retired release feature flag and improves performance, stability, and clarity across desktop and website.
New Features
acepe-qa.Migration
whisper-native(orwhisper-metal) to buildwhisper-rs. Use thewebsocketfeature when needed (addstokio-tungstenite,url,http).auto-downloadfeature flag.Written for commit 6ccbe5f. Summary will update on new commits.