Skip to content

feat(acpmux): opt-in memory and artifact sync, with per-tick token refresh (carries #27254) - #27815

Merged
teamleaderleo merged 19 commits into
feat-cmux-nextfrom
lane/artifacts-sync-token-refresh
Nov 2, 2026
Merged

teamleaderleo merged 19 commits into
feat-cmux-nextfrom
lane/artifacts-sync-token-refresh

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Carries #27254 (opt-in memory and artifact sync for acpmux) onto current feat-cmux-next: its five commits keep their authorship, rebased with one conflict line in daemon.rs. One fix commit goes on top. #27254's branch is untouched, and #27254 stays open for its owner.

For Leo's artifacts asks:

First version: agents get tools to save and search, and the app gets a panel to browse, view and see the history of artifacts.

can this be a proper feat inside acpmux/the multiplayer and team situation?

This PR is the sync layer under the agent tools (#27253, which rebases onto this once it lands) and the Labs Artifacts pane (#27264). The pieces are:

  • Sync: it syncs ~/.claude/projects/*/memory, ~/.codex/memories and ~/.cmux/artifacts with the per-user /v1/artifacts store in cmux-platform.
  • Writes: each write is a compare-and-swap on the head sha. Markdown gets a 3-way merge on conflict, and other files get a conflict copy beside them.
  • Secrets: files with secret-looking names or content are skipped.
  • Opt-in: artifactsSync.enabled defaults to false. While it is off, the daemon task returns at once and nothing reads credentials, touches the network or touches files.

Fix on top (8297258)

  • The access token expired after the first one. The daemon's sync loop took one access token at start and sent it on every 30 s tick, so every sync failed once it expired. Each tick now asks the mesh client for its current token (refreshed near expiry), and for a new one after the server answers 401.
  • feat(acpmux): sync opt-in memories and artifacts #27254 did not compile at its head. That caused its cmux-tui build and clippy reds. Fixed:
    • reqwest's query needs a feature this workspace leaves off, so the query is encoded with Url::parse_with_params;
    • sync_once borrowed self.roots across a &mut self call;
    • an unread SaveResponse.commit field and a collapsible if failed clippy;
    • rustfmt.
  • Building the HTTP client panicked. The clients built a reqwest Client before any rustls provider was installed. Both now install the workspace's ring provider, as mesh_http does. The new test caught this.

Fix on top (c68b4b2)

  • A pull could write outside the memory folders. pull_once mapped each listed remote path onto a local root by joining the server's text unchecked, so a listed memory/codex/../../.ssh/authorized_keys would be written outside the memory folders. A remote path now maps only when every component is a plain name: no .., ., root, empty part, backslash or NUL. New test: a_remote_path_never_maps_outside_its_root.
  • A silent server could hang the sync loop. The sync client had no timeouts, so a server that stopped answering held the loop forever. It now has a 15 s connect timeout and a 60 s per-read timeout, so a large blob that keeps moving is never cut off.

Review fixes (e73f197)

An independent review (gpt-6.1-sol at high, at c68b4b2) BLOCKed on four data-safety findings in the sync. All four are fixed:

  • Pull kept overwriting edits. It replaced the local file unconditionally, so an edit the server hadn't taken was lost: its upload had failed, or it was made during the pass. A pull now writes only over what this machine last synced, or a missing file. Anything else is kept and sent by the next pass.
  • A failed read could empty a memory. After a save conflict, a failed remote read counted as empty, so an unchanged memory merged to an empty file and was truncated. The read error now fails the pass. Conflict writes go only over the bytes this pass read.
  • Symlinks led both ways out of the root. A symlink below a root sent pulls outside it, and the upload walk followed symlinks out. Neither follows a symlink now.
  • Shutdown waited for a pass. Shutdown now also ends a pass in flight.
  • Also: a pass re-sent every file on every tick. It now sends only what changed since the last sync.

Red/green: with the new tests on c68b4b2, three of them fail. pull_keeps_a_local_edit_the_server_has_not_taken gets "remote" where it expects "local edit". a_failed_conflict_read_never_empties_a_memory gets "". symlinks_below_a_root_are_never_followed maps through the symlink. With this commit, all 11 pass.

Review fixes, round 2 (348da70)

At e73f197, the reviewer confirmed the first four fixes and BLOCKed on three more:

  • A failed retry lost the merge. The conflict retry recorded the merge as synced before it was saved. If the retry failed, the next pull replaced the merge with the older remote copy. Now nothing counts as synced, and no local file changes, until the retry is saved.
  • A binary conflict still overwrote the remote edit. It left the original's synced state alone, so the next pass sent ours over theirs. The original now takes the remote bytes, locally and as its synced state; ours stays in the conflict copy. A file gone remotely is just sent, and remote_file now tells a missing file from an empty one.
  • The secret filter missed password: x, apiKey: ... and access_token: .... It now also runs the daemon's secret scanner (gitleaks' rules and cmux's own) and a credential-key assignment pattern.

Red/green: three of the new tests fail at e73f197. a_failed_conflict_retry_keeps_the_local_edit_unsynced finds the merge recorded as synced. a_binary_conflict_keeps_both_versions finds the original's synced state still "old". secret_paths_and_content_are_skipped finds password: hunter22 uploaded. With this commit, all 13 pass.

Review fixes, round 3 (ef25459, 85ecb92)

At 348da70, the reviewer confirmed round 2 and BLOCKed on four more:

  • A local delete came back. A file deleted here was restored by the next pull. Sync now sends a local delete as a delete change. A pull skips a file whose commit is the one this machine last synced; a file deleted here but changed remotely since comes back, so their edit wins.
  • A root that is itself a symlink escaped (~/.codex/memories -> /elsewhere). Such a root is not synced, in either direction.
  • Short credentials still uploaded. password: x got through; the credential-key pattern now takes a value of any length.
  • A signal stop leaked the sync task. The sync now runs at the end of the daemon's startup task (Hub::run_after_startup, which also takes over starting chats). The daemon already aborts that task on every shutdown path.

Also found while writing the tests:

  • Uploads reached the server with no content. The save body was snake_case (content_base64), while cmux-platform's ArtifactChange is camelCase. A contract test now checks the field names.
  • The pull read only the first page of the listing, and treated an artifact folder as a file. It now reads every page at one head, and a head that moves between pages fails the pass.
  • State didn't survive a restart. The synced state (head, and each path's digest and commit) now lives in ACPMUX_HOME/artifacts-sync-state.json, written whole and only when it changes. A restart no longer re-sends everything or restores a file deleted while the daemon was down. File contents stay in memory only.

To keep the god-file check green, the tests move to artifacts_sync_tests.rs and the agent tools' client to artifacts_client.rs. It is re-exported, so #27253's crate::artifacts_sync::ArtifactsClient import still works. daemon.rs shrinks by 5 lines.

Review fixes, round 4

At 85ecb92, the reviewer confirmed round 3 and BLOCKed on one issue. A root was checked only at its last folder, so a symlinked folder above it (~/.claude/projects/work -> /tmp/private) still led uploads out of it and pulls into it. Each root now records the folder it sits under (home). Every folder from there down to the root must be a real directory, checked on each pass. New test: a_root_below_a_symlinked_folder_is_not_synced; it fails at 85ecb92 and passes now. The branch then merged feat-cmux-next past 4154db0 (the base Swift test-compile fix) cleanly.

Review fixes, round 5 (1257774)

At 30bb0e8, the reviewer confirmed round 4 and BLOCKed on one issue: the symlink checks were check-then-use. Another process could swap a checked folder for a symlink between the check and the walk or a write, and lead an upload or a pull outside its root. All file access in a pass now goes through artifacts_fs::Dir:

  • On Unix, a root is opened from home downward, each folder relative to its parent with O_NOFOLLOW, as config::private_state_dir already does. The walk, every read, the delete check and every write then go through descriptors relative to that folder, never a path.
  • Writes land in a new file that is renamed over the old one. They never write through a symlink, and a reader never sees half a file.
  • On Windows, the path-based checks stay (creating a symlink there needs privileges).

Also from the reviewer's notes: a conflict copy's name uses HOSTNAME only as one plain name part, and its timestamp is in milliseconds, so two conflicts on one file within a second keep both copies. New test: a_folder_swapped_for_a_symlink_mid_pass_leads_nowhere.

Reviewer note: two Astra reviews through SR2 hung (64 and 29 minutes, no verdict), so reviews here use gpt-6.1-sol, the interchangeable Codex reviewer.

Timer: the sync interval is owned by the sync task, which runs at the end of the daemon's startup task. That task is aborted on every shutdown path, and hub.shutdown also ends a pass in flight. HTTP has 15 s connect and 60 s per-read timeouts.

Red comparison (by test name, against base)

Job Failing test or step on #27254 Same on base? Here
cmux-tui SDKs / protocol contract; spec inventory / inventory test_ir.IrTests.test_live_raw_v12_layout_and_history_contracts_are_exact (command='new-pane-right') yes. Base runs 40826810590 and 40834526373 fail the same test; fixed on base by #27505 cleared by the rebase
spec inventory / api-compat "Protocol changes need compat rules" yes, in the same base runs. #27254 touches nothing in cmux-tui/spec/ cleared by the rebase
cmux-next checks "Phone lane field allowlist", "No new SwiftUI" yes, base run 40836727227. #27254 has no Swift cleared by the rebase
spec inventory / clippy; cmux-tui artifacts macOS builds the crate did not compile no (#27254's own) fixed in 8297258

Observed / Not run

  • Observed on this head's tree: cargo test --locked -p acpmux --lib artifacts_sync passed 21/21, covering token refresh, path and symlink confinement, pull edits, conflict reads and retries, binary conflicts, secrets, deletes, pagination, persisted state and the save-body field names. check-no-godfiles.sh --only rust --base origin/feat-cmux-next is clean. cargo clippy --locked -p acpmux --all-targets -- -D warnings and cargo fmt -p acpmux --check were clean.
  • Red before the fix, on feat(acpmux): sync opt-in memories and artifacts #27254's own head: the crate did not compile. Before the provider fix, the new test panicked with "No rustls crypto provider is configured".
  • Not run: the macOS build of the new libc calls in artifacts_fs.rs, since this host has no Apple C toolchain to build ring; CI's cmux-tui artifacts macOS jobs compile it. Also not run: the full acpmux suite (CI runs it), and an end-to-end save against cmux-platform. The dev Worker still answers 404 on every artifacts path until its Cloudflare binding lands.

Changelog

none (opt-in, off by default)

Agent session: agent session

🤖 Generated with Claude Code

teamleaderleo and others added 5 commits October 30, 2026 01:25
Agent-Session: deeplink/agent?id=aid-artifacts-sync
The daemon's sync loop took one access token at start and sent it on every
30 s tick, so every sync failed once that token expired. Each tick now asks
the mesh client for the token it holds (refreshed near expiry) and for a new
one after the server answers 401.

Also makes the branch compile and pass clippy -D warnings: reqwest's `query`
needs a feature this workspace leaves off (encode with Url::parse_with_params),
sync_once borrowed self.roots across a &mut self call, the clients built a
reqwest Client before any rustls provider was installed (it panics), an unread
SaveResponse.commit field, and a collapsible if.

Test: cargo test -p acpmux --lib artifacts_sync (7 passed, incl. the new
every_tick_takes_a_current_token_and_a_refusal_asks_for_a_new_one);
cargo clippy -p acpmux --all-targets -- -D warnings clean.

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

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 23f7096: 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=pr27815 -f ref=lane/artifacts-sync-token-refresh -f platform=macos -f backend_mode=local

web and size checks: green

Shipped in:

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

blacksmith-sh Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Found 3 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_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)/
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)
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.

pull_once mapped each listed remote path onto a local root by joining the
server's text unchecked, so a listed `memory/codex/../../.ssh/authorized_keys`
wrote outside the memory folders. A remote path now maps only when every
component is a normal name (no `..`, `.`, root, empty, backslash or NUL).

The sync client had no timeouts, so a server that stopped answering held the
daemon's sync loop forever. It now has a 15 s connect and a 60 s per-read
timeout (a large blob that keeps moving is never cut off).

Test: cargo test -p acpmux --lib artifacts_sync, 8 passed, incl. the new
a_remote_path_never_maps_outside_its_root; clippy -D warnings clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c430a6f6a8435f14bd29
Review findings on the sync (gpt-6.1-sol, at c68b4b2):

- A pull wrote remote bytes over the local file unconditionally, so an edit
  the server hadn't taken (its upload failed, or it was made during the pass)
  was replaced with the stale remote copy. A pull now writes only over what
  this machine last synced, or a missing file; anything else is kept for the
  next pass to send and counted as a conflict.
- After a save conflict, a failed remote read was taken as empty, so an
  unchanged memory merged to an empty file, truncated it, and saved that. The
  read error now fails the pass. Conflict writes also go only over the bytes
  this pass read.
- A symlink below a root led a pull outside it (`shared -> ~/work`), and the
  upload walk followed symlinks out of the root. Neither follows a symlink now.
- Shutdown now also ends a pass in flight, so a server that trickles bytes
  never holds the task past the daemon.
- A pass re-sent every file on every tick; it now sends only what changed
  since the last sync.

Red/green: cargo test -p acpmux --lib artifacts_sync with the new tests on
c68b4b2 fails pull_keeps_a_local_edit_the_server_has_not_taken (left
"remote"), a_failed_conflict_read_never_empties_a_memory (left "") and
symlinks_below_a_root_are_never_followed; with this commit 11 passed. clippy
-D warnings clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c430a6f6a8435f14bd29
Second review round (gpt-6.1-sol, at e73f197):

- The conflict path wrote the merged Markdown and recorded it as the synced
  base before the retry save. When the retry failed, the pull that followed
  took the merge as unchanged and replaced it with the older remote copy, and
  the upload skip never sent it again. Now nothing counts as synced and no
  local file changes until the retry is saved.
- A binary conflict uploaded our copy beside the original but left the
  original's base alone, so the next pass sent ours over the remote edit the
  copy was meant to keep. The original now takes the remote bytes, locally and
  as its base; a file gone remotely is simply sent; an identical one is synced.
  remote_file tells a missing file (None) from an empty one.
- The secret filter missed `password: x`, `apiKey: ...`, `access_token: ...`.
  It now also runs the daemon's secret scanner (gitleaks' rules and cmux's)
  and a credential-key assignment pattern.

Red/green: with the new tests on e73f197,
a_failed_conflict_retry_keeps_the_local_edit_unsynced (base was the merge),
a_binary_conflict_keeps_both_versions (base stayed "old") and
secret_paths_and_content_are_skipped (`password: hunter22` uploaded) fail;
with this commit 13 passed. clippy -D warnings clean.

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

cursor Bot commented Oct 30, 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.

…ows a root symlink

Third review round (gpt-6.1-sol, at 348da70):

- A file deleted locally was restored by the next pull, since a missing file
  counted as writable. Sync now sends a local delete as a `delete` change, and a
  pull skips a file whose commit is the one this machine last synced. A file
  deleted here but changed remotely since comes back: the remote edit wins.
- The synced state (head, and each path's digest and commit) is kept in
  ACPMUX_HOME/artifacts-sync-state.json, written whole and only when it
  changed. A restart then neither re-sends every file nor restores a file
  deleted while the daemon was down. File contents stay in memory only.
- A root that is itself a symlink (`~/.codex/memories -> /elsewhere`) is not
  synced, in either direction.
- `password: x` and other short credential values were uploaded; the
  credential-key pattern now takes a value of any length.
- A signal stop does not notify hub.shutdown, so the sync task outlived the
  daemon; the daemon now keeps its handle and aborts it with startup's.

Also, found while writing the tests:
- Save changes were serialized snake_case (`content_base64`), but the platform's
  ArtifactChange is camelCase, so every upload reached the server without its
  content. The body now matches, checked by a contract test.
- The pull read only the first page of the listing (50 items) and treated a
  saved artifact's folder item as a file. It now reads every page at one head
  (a head that moves fails the pass) and skips folder items.
- A pull re-read every file on every tick; an unchanged commit is not read.

Red/green: on 348da70 these new tests fail or do not compile against its
API: a_local_delete_is_sent_and_never_restored, a_remote_edit_wins_over_a_local_delete,
the_listing_reads_every_page_at_one_head, synced_state_survives_a_restart,
a_root_that_is_a_symlink_is_not_synced, a_save_request_uses_the_platform_field_names,
and the `password: x` case. With this commit 19 passed; clippy -D warnings clean.

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

- The sync now runs as the tail of the daemon's startup task (via
  Hub::run_after_startup, which also takes over starting chats), so the
  daemon's abort of that task at shutdown ends it on every path, a signal
  stop included, instead of a separate handle. daemon.rs shrinks by 5 lines
  instead of growing past its over-budget base size.
- artifacts_sync.rs was over the cmux-tui god-file limit (1649 lines, 66 fns).
  Its tests move to artifacts_sync_tests.rs and the agent tools' HTTP client
  to artifacts_client.rs (re-exported as artifacts_sync::ArtifactsClient), so
  each file is within budget. No behavior change.

cargo test -p acpmux --lib artifacts_sync: 19 passed; clippy -D warnings,
cargo fmt --check and check-no-godfiles.sh --only rust --base clean.

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

teamleaderleo commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator Author

Review (the owner lane's independent review, at its land request): SHIP at 04653fe

Recorded by lane-gh-broker from this PR's owner lane, which asked to land this exact head after its independent review. next-autoland lands it once every red is the base's; a BLOCK at this head, a HOLD line or a hold label still stops it.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol xhigh, next-review): BLOCK at 9373416

BLOCK at 9373416

Visual verdict: no images; Leo tries the build

  1. [P1][blocking] [artifacts_sync.rs:847-855] The pull protection is still a check-then-use race. After reading and hashing the local file, another process can edit it before dir.write() atomically renames the remote bytes over it. The pull then records the remote digest as synced, permanently losing the intervening local edit.

  2. [P1][follow-up] [artifacts_sync.rs:347-350] save_state uses fs::rename over an existing file. Windows does not replace an existing destination with rename, so every state update after the first fails and is only logged. Restarts then resend stale state and lose deletion/conflict tracking.

  3. [P1][follow-up] [artifacts_sync.rs:741-752, 809-833] Pulls cannot bootstrap missing roots, because Dir::open returns None for a missing root and locate silently skips the file. Claude project roots are also discovered only once, so projects created while the daemon runs are never synchronized.

Follow-ups:

  • Fix Windows-compatible atomic state replacement.
  • Re-discover Claude roots and create known roots safely when applying remote files.

@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

Orphaned: no activity for 18 h, and no lane holds a lease on it. The orphan sweep could not merge feat-cmux-next in: conflicts in cmux-tui/crates/acpmux/src/daemon.rs. Listed for a lane to resolve.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by an agent session · session

…cts-sync-token-refresh

# Conflicts:
#	cmux-tui/crates/acpmux/src/daemon.rs
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5): SHIP at 23f7096
Visual verdict: no UI in this diff

Checked: the base-merge resolution in daemon.rs keeps base's hub.spawn_profile_respawns() and only moves chat start + end_startup_work() into Hub::run_after_startup, unchanged, so nothing from base is lost. startupDone still flips before the sync loop starts, and startup.abort() + await ends the loop on every shutdown path. Opt-in holds: when enabled is false, run_background returns before it reads mesh credentials, finds roots, opens files or builds an HTTP client. Also checked: the token is fetched each tick, with a fresh one after a 401; pull and upload writes use compare-and-swap against the last synced digest; writes are O_NOFOLLOW descriptor-relative and atomic rename; remote paths are confined to their root; and secret skipping covers both directions. CI at this head was still pending when this was written, so a red of its own would still block.

Follow-ups:

  • [follow-up] merge_markdown merges by line position, not by diff. One inserted or deleted line shifts every later line, so a concurrent edit fills the file with conflict blocks and stray blank lines. That happens most on a first sync with no recorded base (base = empty), for example a second machine whose MEMORY.md differs. The output is written back into the user's memory files and uploaded. Content is kept inside the markers rather than dropped, but it should become a real diff3 (e.g. diffy/similar) before this is turned on for anyone.
  • [follow-up] merge_markdown also normalizes line endings: lines() drops CRLF and adds a trailing newline.
  • [follow-up] artifactsSync.baseUrl defaults to the development Worker (cmux-platform-development.debussy.workers.dev). Opting in sends memories there unless a URL is set. Point it at production, or require the URL when enabled, and refuse a non-https URL, since a bearer token is sent.
  • [follow-up] Every 30 s tick reads and SHA-256-hashes every file under all roots, ~/.cmux/artifacts included. Skip unchanged files by mtime and size first.
  • [follow-up] replace_at neither fsyncs before the rename nor keeps the replaced file's mode. A crash between create and rename leaves .NAME.artifacts-sync-PID, which the next walk uploads as an artifact. Skip .artifacts-sync- temporaries in read_tree.
  • [follow-up] The config is read once at startup, so turning enabled on or off takes effect only after a daemon restart.

This branch was previously deployed

3 inactive deployments
artifacts — 23f70963 Deployed Nov 2, 2026 by teamleaderleo via publish tree-addressed cmux-tui #100893
sentry-dsyms — 23f70963 Deployed Nov 2, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #100893
ci-routing-agent — 23f70963 Deployed Nov 2, 2026 by teamleaderleo via cmux-tui tree preflight #100893
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