acpmux: trust a worktree cmux made, and never gate a chat that already works

Merged
TE
16d ago
+234-14

On 2026-10-21 the directional-motion lane on huge-blue locked up. Its chat lives in /home/leo/.cmux/worktrees/cmux/directional-motion, and every send from the app failed with "Not sent: trust.pending" until someone ran cmux setup trust <folder> by hand. Two things in the acpmux daemon's trust gate caused it. This PR fixes both:

  • A worktree cmux made is trusted with no question.
    • Before: a worktree only took its repository's acpmux decision. If nobody had pressed Trust in cmux for that repository, every chat in ~/.cmux/worktrees/<project>/<name> waited on trust.pending. Claude's own trust of the main checkout didn't count, because Claude's level is per path.
    • After: a linked worktree that git wrote directly at ~/.cmux/worktrees/<project>/<name> is trusted the same way the agent-home folders cmux makes are. The check uses the same repository() check that already refuses forged .git files. This holds on every host, because each daemon checks its own home.
    • Still refused: an explicit Don't trust, for either the worktree or its repository, and an agent's own untrusted level for the worktree or its repository. A plain folder under ~/.cmux/worktrees is still asked about, and so is the repository itself.
    • Auto-trust Off: like the agent-home folders, the rule holds even with agents.trust.autoTrust off, because cmux made the folder. That setting covers only the person's project roots and organizations.
    • Web clients: the rule reports auto: "worktree", so like an auto-trust rule it is not a person's answer for a Web client. A worktree holds its repository's content, so a remote browser still waits for a person.
  • Trust never gates a chat that is already working.
    • Before: a chat that a coordinator started over the unix socket (which is not gated) in a folder nobody answered for refused the app pane's prompts with trust.pending, even while its agent was running turns there.
    • After: once a chat has run a turn on this daemon (it has a last_turn, or a turn is running), trust.pending no longer refuses its session/prompt, session/set_mode, session/set_config_option or _acpmux/set_tools. That includes the queued-prompt dispatch check.
    • Still refused:
      • An explicit Don't trust still refuses the chat (trust.untrusted).
      • A Web client still waits for a person's decision.
      • A chat moved or imported from another daemon keeps its last_turn, so it counts as running here too.
      • A chat that was only created (agent started, no prompt yet) still waits for the answer before the pane's first prompt. So does a chat whose only turns are an adopted conversation's replayed history or a fork's copy of its parent's.
      • A fork, a side session (btw) and a handoff still check their own folder, because each starts an agent of its own.

This is daemon side only. The app's Trust button belongs to the palette-audit lane, and the paused-queue item has its own PR.

Risk: risk:high (folder-trust policy in the daemon's trust gate).

Changelog

Fixed: a chat in a worktree cmux made, or one that is already working, no longer refuses your messages with "trust.pending".

Tests

  • cargo test -p acpmux --test local_app_trust_gate a_worktree_cmux_made and a_running_chat_is_not_held_by_an_open_trust_question: integration tests through the app's connection.
  • cargo test -p acpmux --lib a_worktree_cmux_made_is_trusted_with_no_question: the rule itself, including Don't trust on the repository or the worktree, an agent's untrusted level (worktree and repository), a Web client still waiting, a plain folder, and the repository staying unanswered.

Red on the test commits (base plus tests): ./scripts/verify-cmux-tui-hosted.sh --filter question at 940818a2f51ad9ab5284a44efbfc7d6abefa2f41: https://github.com/manaflow-ai/cmux/actions/runs/39686176119. test (linux) failed on trust::scope_tests::a_worktree_cmux_made_is_trusted_with_no_question, and the lib failure stops the integration targets. macOS was still queued.
Green on the head: same filter at 9477fa991491c1fde1e948a9d2d6afa40340c459: https://github.com/manaflow-ai/cmux/actions/runs/39687136572. test (linux) passed with all three new tests. macOS was still queued. The head c69158b4 changes only a comment after 9477fa99.

Observed / Not run

  • Observed on huge-blue: the three new tests fail on the red commits. The worktree read "level":"unknown", and the app's prompt to a running chat got {"reason":"trust.pending"}. They pass on the fix. local_app_trust_gate, subsession_trust, peer_integration and the trust unit tests pass locally on the head. cargo clippy -p acpmux --all-targets -D warnings and prepush-gate.py are clean. The full local cargo test -p acpmux run stopped on temp-folder permission tests (session_tmp, store::permission_tests), which refuse this host's group-writable TMPDIR; the hosted run and PR CI cover those. The folder name comes from cmux-git's new worktree_root_path, so the state-roots ratchet stays at its baseline.
  • Not run: no Swift code changes, so there was no app build. I didn't try the change against the live daemons, because they're shared and I didn't restart any.

Agent session: huge-blue

šŸ¤– Generated with Claude Code