Skip to content

acpmux: search chats by what was said, not only titles (_acpmux/chats_search, chats search) - #28755

Merged
teamleaderleo merged 10 commits into
feat-cmux-nextfrom
lane/acpmux-chats-search
Nov 1, 2026
Merged

teamleaderleo merged 10 commits into
feat-cmux-nextfrom
lane/acpmux-chats-search

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Part of manaflow-ai/cmuxterm-hq#5333 (Search chats matches titles only, never the conversation): the daemon half (its step 2) and a CLI verb. The palette hookup is a follow-up, below.

What

acpmux can now find a chat by what was said in it, not only by its title or folder.

  • _acpmux/chats_search {query, harness?, folder?, account?, limit?, cursor?, budgetMs?}: returns the chats whose prompts or answers hold every word of query (any case, any order, within one prompt or one answer). Newest chat first. Each hit has:
    • chat: the same value _acpmux/chats gives;
    • turn: an index into _acpmux/chat_transcript's turns;
    • role: prompt or answer;
    • snippet: up to 160 characters around the words;
    • matches: how many of the chat's messages hold the words.
  • Bounded: the scan stops at limit hits (default 50, at most 200) or after budgetMs (default 2000 ms, 50 to 10000). The budget is checked between chats, and each chat's read is bounded too, so a search ends at most one read past it. partial says the time ran out, and nextCursor goes on from the last chat read. A chat whose store can't be read is skipped and counted in unreadable.
  • One chat's read is bounded: adopt's Claude Code/Codex reader now stops at the length the file had when opened (it already read only the newest 64 MiB). The JSONL readers in cmux-chat-index (Droid, Copilot CLI, older Goose) used to read to the end of the file. They now read the newest 64 MiB the file held when it was opened (fold_newest_lines), the same bound adopt's reader already puts on Claude Code and Codex. Whole-JSON stores were already capped at 8 MiB and SQLite stores at 2000 rows. This also bounds _acpmux/chat_transcript for those harnesses; its newest-200-turns answer doesn't change unless a file is over 64 MiB.
  • Nothing is indexed or kept. Each chat's turns are read from its harness's own store through the reader _acpmux/chat_transcript already uses (the newest 200 turns, 20k characters per message). Tool calls, tool results and injected <tags> context are not searched.
  • Local unix socket only, like the rest of _acpmux/chat*. Web, LocalApp and Peer get "Method not found", so no transcript text leaves the machine.
  • CLI: acpmux chats search WORDS… [--harness H] [--folder DIR] [-n N] (also cmux chats search) prints each chat's key and title, then the role and snippet of its newest matching message. It requests page after page until it has N chats or has searched every chat, so a page cut short by the time budget never hides an older chat.

Why not the palette in this PR

AgentChatsPalettePage.swift is being rewritten by lawrencecchen's open #28090 (with #28055): the page becomes a window over acpmux's own index (palette.chats). Wiring search into today's static page would conflict with that and be thrown away. The next step, once #28090 lands, is a second provider on that page: for a query of 3+ characters it calls _acpmux/chats_search and shows chats whose title didn't match, with the snippet as subtitle. Opening a hit can scroll to the message by turn.

Tests

  • cargo test -p acpmux --test chats_search (new). It covers:
    • a word from a later answer finds a Claude Code chat;
    • a prompt finds a Codex chat;
    • a tool result that names the word is not a match;
    • words split across messages are no match;
    • limit and cursor paging, and the harness filter;
    • an empty query is refused;
    • Web, LocalApp and Peer origins are refused.
    • Red on the test commit 27adacc (panicked at tests/chats_search.rs:139: no hits, method not found). Green on the head.
  • cargo test -p acpmux --lib -- chats::search cli::chats protocol: snippet bounds, whitespace folding, lowercasing that changes length (İ), refusal of empty and over-long queries, the CLI text, and protocol_ir_is_current (spec blessed, methods.ts regenerated with bun scripts/acpmux-protocol-codegen.ts).
  • cargo test -p cmux-chat-index: lines::tests::the_newest_lines_are_read_from_a_whole_line_and_no_further_than_the_file_was covers the cut inside a line, the cut on a line start, a trailing partial line, and the returned offset.
  • cargo test -p acpmux --test chats_transcript still passes for every harness's reader.
  • tests/chats_window.rs (from Chats: acpmux owns the chat list; the sidebar history holds only its window (P0 stall) #28055) a_window_over_three_thousand_chats_is_sorted_filtered_and_one_change_is_one_row times out at line 84 on this VM. It fails the same way on base 51ed666 (same line, 30 s), so it is not this PR's.
  • a_word_is_lowered_as_the_text_is: ΟΣ, ος and οσ all find each other.

Review

gpt-6-astra (high, fast) at 9f77928 gave BLOCK with three findings: a single huge JSONL transcript could run past the budget, the CLI couldn't continue a cut-short search, and query and text were lowercased differently. All three are fixed in 741f013. Its re-review at 741f013 found five more, all fixed in 1be6852:

  • adopt's Claude/Codex reader now stops at the length the file had when opened;
  • the final sigma ς folds as σ, so ΟΣ finds ος;
  • fold_newest_lines returns no resumable offset;
  • the unreadable contract is documented (a mid-read failure keeps the turns before it, as chat_transcript does);
  • the CLI sums scanned over its pages.

Review (gpt-6-astra high, fast): SHIP at 1be6852, then a delta review of 7a27a72 (its arithmetic made saturating for the panic ratchet): SHIP at 7a27a72, then a review of the base merge 828935c (conflicts with #28055 resolved by keeping both): SHIP at 828935c.

Panic ratchet

The clippy (changed cmux-tui packages) ratchet step is red on acpmux for base reasons: acpmux arithmetic_side_effects is 1494 on the merge base against a 1430 baseline (#28673 brings it back under), and the same step is red on #28772 and #28700. Its failures are deny-lint hits in other crates (cmux-automations indexing_slicing/string_slice as a new crate; cmux-tui indexing_slicing 832 > 831; cmux-tui-core indexing_slicing 856 > 853, string_slice 57 > 52), none in a file this PR touches. This branch adds no hits at 7a27a72: acpmux is still 1494 and cmux-chat-index is at its 65 baseline (rust_panic_ratchet.py over cargo clippy -p acpmux -p cmux-chat-index --lib --bins).

Observed / Not run

  • Ran on a Linux lane VM: the tests above, with results as stated, plus cargo clippy -p acpmux -p cmux-chat-index --all-targets with nothing new in the changed files.
  • Not run: the full acpmux suite runs in this PR's CI. No test drives the CLI's paging loop against a daemon; it was read, not run. No macOS run, since no Swift changed.

Changelog

  • Added: acpmux chats search (and the daemon's _acpmux/chats_search) finds agent chats by words from their prompts and answers, not only their titles.

Risk: normal. A new read-only, local-socket-only method; nothing existing changes.

Agent session: agent session

🤖 Generated with Claude Code

teamleaderleo and others added 3 commits October 31, 2026 01:48
…no _acpmux/chats_search)

HQ#5333: Search chats matches titles only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-80fea2c04bdd31e52544
…hats search)

HQ#5333: Search chats matched titles and folders only. The new local-socket
method reads each chat's turns from its harness's store (the reader
_acpmux/chat_transcript uses), returns the chats whose prompt or answer
holds every query word, newest first, each with its newest such message's
turn, role and snippet. The scan stops at limit hits or the time budget
(default 2 s) and says so; nothing is indexed or kept.

Red on 27adacc (tests/chats_search.rs:139, no hits), green here:
cargo test -p acpmux --test chats_search.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-80fea2c04bdd31e52544
…s as text

Astra review of 9f77928 (BLOCK):
- A JSONL store (Droid, Copilot CLI, older Goose) was read to its end, so
  one huge or growing transcript ran past budgetMs. It is now read from its
  newest 64 MiB, as adopt's reader reads Claude Code and Codex, up to the
  length it had when opened (fold_newest_lines).
- acpmux chats search sends page after page until --limit chats are found
  or every chat was searched, so a time-cut page never hides an older chat.
- Query words were lowered with str::to_lowercase and the text per char: a
  final sigma (ΟΣ) never found itself. Both now lower per char.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-80fea2c04bdd31e52544
@teamleaderleo teamleaderleo added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
… totals

Astra review of 741f013 (BLOCK):
- adopt's reader (Claude Code, Codex) sought the newest 64 MiB but read to
  EOF: it now stops at the length the file had when opened.
- Folding lowers each character and reads a final sigma (ς) as σ, so ΟΣ,
  ος and οσ find each other.
- fold_newest_lines is a whole read and returns no offset, so no caller can
  resume from one that falls inside a skipped line.
- chats_search says what unreadable counts (a Claude Code or Codex file
  that fails mid-read gives the turns before it, as chat_transcript does).
- The CLI sums scanned over its pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-80fea2c04bdd31e52544
@cursor

cursor Bot commented Oct 31, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (gpt-6-astra, high, fast): SHIP at 1be6852

Three rounds. BLOCK at 9f77928, three findings: per-chat read bound, CLI paging, case folding. BLOCK at 741f013, five findings: Claude/Codex read frozen at open length, final-sigma folding, no resumable offset from fold_newest_lines, the unreadable contract, CLI scanned total. SHIP at 1be6852: no remaining blockers.

@blacksmith-sh

blacksmith-sh Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Found 2 test failures on Blacksmith runners:

Failures

Test View Logs
test_a_picked_signing_label_has_a_rerun_alternative (main.RunsOnFallbackTests.test_
a_picked_signing_label_has_a_rerun_alternative)/
test_a_picked_signing_label_has_a_rerun_alternative (main.RunsOnFallbackTests.test_
a_picked_signing_label_has_a_rerun_alternative)
View Logs
test_no_runs_on_chain_ends_on_the_reserved_side_label (main.RunsOnFallbackTests.tes
t_no_runs_on_chain_ends_on_the_reserved_side_label)/
test_no_runs_on_chain_ends_on_the_reserved_side_label (main.RunsOnFallbackTests.tes
t_no_runs_on_chain_ends_on_the_reserved_side_label)
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

The panic ratchet's tracked arithmetic rows (acpmux, cmux-chat-index) rose
with this branch's counters and offsets: they saturate now, and the budget
is read as elapsed time. acpmux is back to base's 1494 (its row over the
1430 baseline is base's, cmux#28673); cmux-chat-index is at its baseline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-80fea2c04bdd31e52544
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (gpt-6-astra, high, fast): SHIP at 7a27a72

A delta review over the SHIP at 1be6852. The new commit only makes the arithmetic saturating and reads the budget as elapsed time, so the panic ratchet rows do not rise.

Conflicts with #28055 (chats_window, chat_projects) in chats/mod.rs and
server/chats.rs: both sides kept. Protocol IR re-blessed, methods.ts
regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-80fea2c04bdd31e52544
@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 828935c: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr28755 -f ref=lane/acpmux-chats-search -f platform=macos -f backend_mode=local

Gallery before/after for bb2f4f6: 16 states rendered, 0 images differ, 2683 more touched states not rendered. Images are in the run's artifact, not on this public PR; the independent review of this head reads them on its own, so the PR needs no hand-made screenshots of these states.

gh run download 41249798355 -R manaflow-ai/cmux -n gallery-advisory-28755-bb2f4f6d57210776ade81366f1e449cb9fe55bde (then read gallery-review.json)

States
State Theme Status Diff Picked by
agent-pane.live-chat-watch/choice-alone Apple System Colors Light unchanged 0% imports methods.ts (1 hops)
agent-pane.live-chat-watch/choice-alone Apple System Colors unchanged 0% imports methods.ts (1 hops)
agent-pane.live-chat-watch/view-only Apple System Colors Light unchanged 0% imports methods.ts (1 hops)
agent-pane.live-chat-watch/view-only Apple System Colors unchanged 0% imports methods.ts (1 hops)
agent-pane.approval-edits/single Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/single Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/grouped Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/grouped Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/single-multiedit Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/single-multiedit Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/grouped-multiedit Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/grouped-multiedit Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/single-long Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/single-long Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/grouped-long Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.approval-edits/grouped-long Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.auth-health/signed-in Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.auth-health/signed-in Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.auth-health/signed-out Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.auth-health/signed-out Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.auth-health/expired Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.auth-health/expired Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.auth-health/unknown Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.auth-health/unknown Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.chat-width/standard Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.chat-width/standard Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.chat-width/wide Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.chat-width/wide Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.chat-width/wide-blocks Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.chat-width/wide-blocks Apple System Colors unchanged 0% imports methods.ts (2 hops)
agent-pane.composer-edge/short-edge Apple System Colors Light unchanged 0% imports methods.ts (2 hops)
agent-pane.composer-edge/short-edge Apple System Colors unchanged 0% imports methods.ts (2 hops)

Pick other states with a Gallery: entry.id, entry.id/variant line in the PR body.

web and size checks: green

Shipped in:

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (gpt-6-astra, high, fast): SHIP at 828935c

This head is a merge of feat-cmux-next. Its conflicts with #28055 in chats/mod.rs and server/chats.rs were resolved by keeping both sides; the reviewer checked that nothing of #28055 was lost or doubled. The branch's own code is unchanged since the SHIP at 7a27a72.

Copy link
Copy Markdown
Collaborator Author

[P2 / fleet-scale resource bound] _acpmux/chats_search has a per-request time budget but no concurrent-search budget. Reviewed exact head 828935c590dc7cfcd4652cc7a6df99b10894ae07 at server/chats/search.rs:handle, server/chats.rs:blocking, and ChatService::listed_from.

Each local request calls super::blocking(move || {...}), which is a raw tokio::task::spawn_blocking without a semaphore. It clones all matching IndexedChat entries, then reads and parses up to the newest 64 MiB per JSONL chat while checking budgetMs only between chats. A request already running in a blocking thread continues after a client drops its wait; there is no cancellation check inside the scan. This means 30 quick repeated searches, or a few clients searching while a large index loads, may run 30 concurrent full-history scans and storage reads, despite each having a 2-second nominal budget. When the palette integration queries on each edit, obsolete queries can overlap the current one, compete with adoption and other spawn_blocking work, and undo the 100–3,000-session responsiveness goals. This is an unbounded concurrency path; it does not assert an observed production stall.

Small fix that preserves search semantics: add a dedicated shared tokio::sync::Semaphore (e.g. 2–4 concurrent fulltext readers) and acquire an owned permit before spawn_blocking, with the permit held through the scan; awaiting tasks remain cancellable before they begin expensive I/O. Stop/reject queued readers within a bounded wait, and propagate the partial/try-again result explicitly. Once inside, consider checking a cancellation flag per chat; the existing elapsed budget alone still allows one 64-MiB read over budget. If keeping budgetMs as a processing budget, start that clock on permit acquisition, not when the caller joins a queue.

Test: launch 30 requests with a stand-in transcript reader that blocks on a barrier; assert at most N simultaneous scans and that canceling 20 queued requests never starts them. At 3,000 indexed chats and 64-MiB transcripts, compare p95 chat-open/agent-host adoption latency with search traffic to no-search baseline. Keep the local-only authorization gate; it is correct here.

This is an independent follow-up suggestion on the owner's current head; no source ownership changed.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol xhigh, next-review): SHIP at 6942e40

SHIP at 6942e40

Visual verdict: all available before/after gallery pairs are unchanged; no UI regression.

No correctness findings.

Follow-ups: none

@teamleaderleo teamleaderleo removed the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (next-review patch-id; carried): SHIP at a30b728

The independent SHIP at 6942e40 carries to this head: the PR's own change (git diff $(git merge-base feat-cmux-next HEAD) HEAD) has the same git patch-id --verbatim (7834637a6b0b9185) at both heads, and no merge between them was resolved by hand. Nothing new to review. A newer BLOCK, or any change to the PR's own diff, ends the carry.

@teamleaderleo teamleaderleo removed the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (next-review patch-id; carried): SHIP at bb2f4f6

The independent SHIP at 6942e40 carries to this head: the PR's own change (git diff $(git merge-base feat-cmux-next HEAD) HEAD) has the same git patch-id --verbatim (7834637a6b0b9185) at both heads, and no merge between them was resolved by hand. Nothing new to review. A newer BLOCK, or any change to the PR's own diff, ends the carry.

@teamleaderleo
teamleaderleo merged commit 914d05f into feat-cmux-next Nov 1, 2026
256 of 258 checks passed
@teamleaderleo
teamleaderleo deleted the lane/acpmux-chats-search branch November 1, 2026 00:14
teamleaderleo added a commit that referenced this pull request Nov 1, 2026
…ples test) (#30025)

#28755 added `acpmux chats search` without help examples, so
cli::help_examples::tests::every_visible_command_has_an_about_line_and_examples
fails on feat-cmux-next whenever the acpmux tests job runs.


Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch was previously deployed

3 inactive deployments
artifacts — bb2f4f6d Deployed Oct 31, 2026 by cmux-bot[bot] via add the Windows daemon to the tree #91891
ci-routing-agent — bb2f4f6d Deployed Oct 31, 2026 by github-actions[bot] via cmux-next macOS placement #56041
sentry-dsyms — bb2f4f6d Deployed Oct 31, 2026 by cmux-bot[bot] via upload cmux-tui dSYMs to Sentry #91891
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant