Skip to content

acpmux: a paired device's Deny (and the person's proved Allow) counts under approve-reads; no Allow drawn that cannot count - #24233

Merged
teamleaderleo merged 7 commits into
feat-cmux-nextfrom
leo/paired-approval-answers
Oct 27, 2026
Merged

teamleaderleo merged 7 commits into
feat-cmux-nextfrom
leo/paired-approval-answers

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Adopted and rebased (2026-10-25)

The lane that opened this is gone. Its change (e01aa4e..cdfb86c) is squashed onto feat-cmux-next, now ec66898. Conflicts: webviews/test/permission-ask-row.test.tsx (this PR's test now comes after base's newer grouped-row tests); hub/permissions.rs, where base's respond_permission_annotated (question annotations, #24356) now holds this PR's Web answer check in place of the plain web_control_check; and server/peer_forward.rs, which keeps base's attach presence beside this PR's allowRefused. The commits added on top are tests and rustfmt only. The daemon and pane code is the original lane's.

Rebased again onto a8d57c9, over #25424, which lets a question answered in the pane count on another machine under the person's proof. There was one conflict, in respond_permission_annotated:

  • Base's needs_proof and its annotations refusal are kept.
  • The proof check goes through this PR's answer_proof. It now takes the annotations and passes the answers and annotations to decision_proves, as base does.
  • A phone's signed allow still counts only with no answeredBy and no annotations.
  • The early Web check passes the same annotations.

What changes, plainly: a paired device (a Web connection: the iPhone, the dashboard, a peer that serves this Mac as Web) can now:

  • Deny a prompt of a session whose mode or policy does not ask, such as approve-reads. Before, every answer was refused.
  • Allow such a prompt once, but only with the person's proof or the person's enrolled phone's signature for that exact prompt.

An Allow with neither is refused exactly as before. The pane no longer draws an Allow that can't count.

Landing terms (2026-10-25), and where each holds

  1. Replay binding.
    • What is signed: the phone's ECDSA P-256 signature (hub/approvers.rs message) covers the daemon run (personInstance), the Mac id, the approver, the session id, the permission id, the option (the decision), the prompt's one-time decisionNonce, expiresAt (at most 300 s ahead) and a digest of any answers. The person's HMAC proof (person.rs decision_proof) covers the run, session, permission id, option and nonce, and since acpmux: a question answered in the pane counts on another machine under the person's proof (re-cut of #23869, daemon side) #25424 any answers and annotations.
    • No tool-call id: the signed fields do not name the tool-call id. The tool call is bound through the permission id. The daemon mints one (Uuid::now_v7) for each session/request_permission it receives, and each request carries exactly one toolCall. A re-ask is a new request with a new id.
    • After a restart: the next daemon adopts the agent's still-open request and keeps that same id for that same request (reregister_permission). It gives the request a new nonce and a new run id, so a proof from before the restart counts nothing.
    • Single use: the answer that counts spends the nonce under the prompt's lock (spend_decision), and a second use is refused.
    • Later option: signing the toolCallId itself, or a digest of the tool call's input, would be a later cnx-ios + acpmux change. This PR doesn't need it.
  2. A one-time allow only.
    • A Web answer to an allow_always option is refused (remote.lasting_grant_refused), signed or not.
    • A group's allow_chat from the Web is refused.
    • A proved Allow changes no mode and no policy.
  3. An unsigned Web Allow is still refused under approve-reads (remote.policy_not_asking). A Deny from any client counts.
  4. Unpairing the phone stops its Allows at once. verifies looks the approver up when the answer arrives, so a signature made before _acpmux/approvers_set removed the phone counts nothing.
    • The revocation race (review round 2): a revocation could land between the signature check and the nonce spend, so a signature checked just before the phone was removed still counted. ApproverSet::verifies_then now holds the approvers lock through the ECDSA check and the commit, and Person::approver_spends spends the nonce inside it. A revocation either lands first, and the signature counts nothing, or waits until the decision is spent.
    • Lock order (in a comment on verifies_then): the prompt's lock first, then the approvers lock, then the spent-nonce store. Nothing awaits while either lock is held; both are std mutexes taken and released inside one synchronous call.
    • Test: unit a_revocation_during_a_commit_waits_for_it_and_then_the_signature_counts_nothing shows the race itself. A commit blocks inside verifies_then while another thread revokes the phone; the revocation waits for the commit, and the same signature then counts nothing. It is red at 0159569 (the test with today's semantics, run 40359378490: the revocation went through mid-commit) and green at 2e7593c (run 40359378532). 7a718b7 is rustfmt only (fmt and clippy clean, run 40360085403).
  5. The signing key lives only on the phone. _acpmux/approvers_set takes only the 65-byte public key. The set lives in the daemon's memory, never in a file, and only the person's app may set or read it (person_required). So another agent on the Mac can't sign as the phone. The person's HMAC key stays in the app's and the daemon's memory (the keyed daemon is not dumpable).

Tests:

  • tests/web_mode_current.rs a_phones_signed_allow_from_a_paired_device_counts_once_never_lasts_and_stops_when_unpaired, under approve-reads from a Web connection, for points 1-4:
    • a signed allow_always is refused;
    • an expired signature is refused;
    • a signed allow counts once and is refused when sent again;
    • the previous prompt's signature is refused on the next prompt, with either nonce;
    • an unsigned allow is still refused as approve-reads, so the policy is unchanged;
    • a signature made before the phone was unpaired is refused;
    • a deny counts.
  • a_proved_allow_from_a_paired_device_changes_no_mode (point 2): a session whose mode does not ask. The person's proved allow counts once, the mode stays normal, and the next unproved Web allow is still refused.
  • The existing test now also checks that the person's proof counts once.
  • tests/durable_agents.rs an_adopted_prompt_gets_a_new_decision_nonce_that_a_relayed_allow_can_use (on base) covers replay after a restart: the same id comes back with a new nonce and run, the old proof is refused, and the new one counts.
  • tests/person_allow.rs a_phones_signed_allow_counts_once_and_stops_when_revoked (on base) covers another option, Mac, run or key over the unix socket.
  • These checks are enforced by the PR's code, so the new tests pass at the commit that adds them. At base every Web allow under approve-reads was refused, so their "counts once" steps fail there.

Runs:

  • At the head 7a718b7: fmt and clippy are clean (run 40360085403). At 2e7593c (the same code before rustfmt; run 40359378532), these pass: approvers 5/5, durable_agents 53, peer_integration 44, person_allow 25, web_mode_current 13, web_mode_drift 3, and hub_integration permission 25.
  • At cb1878d, before the race fix (run 40351450169), the whole acpmux suite passed. Lib was 1099/1100.
  • Reds that fail the same way on base:
    • lib agent_tools::tests::own_servers_run_under_the_daemon_prefix (run 40294994012);
    • hermetic_daemon_guard's two tests, which flag base's new tests/linux_sandbox_init.rs (base run 40353228261).
  • Webview at the head (run 40351450087): the protocol codegen writes no change, permission-ask-row and permission-groups-switch-keeps-last pass 7/7, and tsc is clean.
  • Before the second rebase, at 0d3bec5: runs 40294544823, 40294993663 and 40294545098. Before the first: runs 40267204426 and 40265977717.

Review: a fresh Codex gpt-6-astra xhigh review at 7a718b7 found landing terms 1, 1(a), 1(b), 1(c), 2, 3 and 4 hold, and said SHIP for this PR's own scope, with term 5's base hole set aside for #25535 (above). After the seal landed, the branch took a merge of feat-cmux-next (no conflicts), and a fresh Codex gpt-6-astra xhigh review of the merged head b04671c found terms 1 to 5 hold and said SHIP. At that head: cargo fmt and clippy -D warnings are clean; lib 1236/1237 (the one failure, own_servers_run_under_the_daemon_prefix, fails on base too); web_mode_current, web_mode_drift, peer_integration, person_allow, full_access_managed and hub_integration pass; the webview permission suites pass 9/9 and tsc is clean.

UI: the gallery's new Panel variant allow-refused shows the pane state: Deny only, plus where to approve. For the before/after, see the PR's gallery-advisory run.

No capture: the only native-mapped file is the generated protocol types.ts (a new optional field); the visible change is the webview permission panel, shown by the gallery variant above.

Overlap with the #23027 recut (same file, webviews/.../permissions/client.ts): this PR's hunks there only carry allowRefused in the per-chat lastRead cache and in the refresh result. They neither read nor set loading, ready or error, and the reselect restore keeps base's loading: false and error: undefined, so they do not depend on the disconnected-error or loading state that recut changes. Whichever lands second rebases.

Original description

Leo, 2026-10-21: "this literally doesn't work".

What broke

Leo had a chat open that runs on his peer machine air-blue, under the approve-reads policy. The grouped ask showed Allow / Always / Deny. Every click came back with this error: "this session's permission settings do not ask before it acts (policy approve-reads); a paired device cannot prompt it or answer its permissions until the local user sets ask or deny-all". The chat stayed stuck.

Why it refused:

  • air-blue's acpmux served this Mac's daemon as a Web connection, the way it serves a paired device. acpmux: an ssh peer that connected before the remote wrote its peer token no longer stays served as Web #24105 fixes one way that happens: a reconnect before the remote writes its peer token.
  • web_control_verdict holds every Web permission answer to the asking rule, Deny included. A session whose policy doesn't ask therefore refuses every answer.
  • Even served as a Peer, a group allow sent through a peer link never counts. The peer's person rule (hub/person.rs) allows only from its own app, and a forwarding daemon is never that app. Allow and Always were drawn anyway, and those buttons could never work.

The fix

  1. A Deny always goes through. A Web answer that only narrows (a reject option, a group deny, or a cancel with no option) is no longer held to the asking rule (Hub::web_answer_check). It can only stop a tool call, and the person rule already lets any client deny.
  2. The person's own click goes through under approve-reads. This covers a Web allow that carries the person's decision proof (personDecision) for that exact pending prompt. It also covers an allow signed by an enrolled approver, such as the person's phone. Such an allow no longer gets the mode, policy and lasting-grant refusals. Nothing else changes:
    • The proof is checked again and its nonce is spent under the prompt's lock, as before.
    • A Web answer still allows once only.
    • A Claude session the Mac started (D13) and an agent outside the sandbox stay refused.
  3. No Allow is drawn that can't count.
    • _acpmux/permission_groups now returns allowRefused: "permission.person_required" to a reader that is not the person.
    • A daemon forwarding a peer chat's read always sets it, whatever the peer's build, because a forwarded group allow never counts there.
    • The pane then shows only Deny (which works) plus cmux NEXT agent pane: an approval only the cmux app may give says so plainly #23890's sentence: "Only the cmux app can approve this. Approve it in cmux on the Mac that runs this chat."
    • The allow shortcuts do nothing in that state.
    • No new strings.

Security reasoning

  • The asking rule exists so that a paired device, which holds only the dashboard token, can't steer a session that acts without asking.
  • A deny makes the session do less, so it needs no gate.
  • An allow with a decision proof is HMAC'd with the person key of that daemon run. An approver allow is signed by an enrolled approver. Either way the proof binds the exact session, prompt, option and one-time nonce. So it is the person's own click for one request: the same answer the person could give at that machine. The asking rule adds nothing there.
  • Nothing lets a non-person client allow anything it couldn't before. An allow with no proof keeps today's refusal: remote.policy_not_asking from a Web client, permission.person_required from a peer.

Still open

From Leo's Mac, an Allow for a chat on a peer Mac still can't count. That peer's acpmux only trusts its own app's key, and nothing proves this Mac's click to it. That needs the device-bound proof that the peer test names (cx-aocz). It is a new cross-machine trust grant, so it isn't built here. Until then the pane says where to approve and offers the Deny that works, instead of dead buttons. The single-ask card (PermissionCard) still learns this only after a click, through its "Remote approvals are off" notice. That is not changed here.

Tests

  • tests/web_mode_current.rs a_paired_device_answers_an_approve_reads_prompt_with_a_deny_or_the_persons_proof. On one daemon with an approve-reads session and a Web (paired device) connection:
    • the Web read says allowRefused and the person's read doesn't;
    • an allow with no proof is still refused with remote.policy_not_asking;
    • a deny counts (the agent hears no);
    • an allow with the person's decision proof counts (the agent hears yes).
  • tests/peer_integration.rs a_peer_chats_deny_goes_through_under_approve_reads_and_its_read_refuses_allows reproduces Leo's setup:
    • daemon B runs under approve-reads and serves A as Web;
    • A's app reads B's group through the peer and gets allowRefused;
    • A's group allow is refused;
    • A's group deny now goes through and B's agent hears it.
  • tests/web_mode_current.rs and tests/web_mode_drift.rs previously asserted that a Web group deny is refused. They now assert that no asking rule holds it.
  • Webviews:
    • permission-ask-row.test.tsx: an ask with allowRefused offers only Deny, says where to approve, and ignores the allow shortcuts.
    • permission-groups-switch-keeps-last.test.ts: the refused allows stay with their chat across a switch.

Red on the test-only commit and green at the head on the fleet.

Original lane: cosmic-gray

Agent session: agent session
🤖 Generated with Claude Code

@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 22, 2026 05:47 — with GitHub Actions Inactive
@teamleaderleo

teamleaderleo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Owned by hb-acpmux on big-red · session

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 7a718b7: 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=pr24233 -f ref=leo/paired-approval-answers -f platform=macos -f backend_mode=local

Unclear: web / react-apps-check failed: no known signature; failed step: Check webview test shards. This PR merged before its CI finished: fix forward on main.

CI failed on b04671c96a (run 40439792119 attempt 1): 4 code, 1 unknown.

Job Verdict Why
web / react-apps-base-check (1) code a web test failed
web / react-apps-base-check (2) code a web test failed
web / react-apps-base-check (3) code a web test failed
web / react-apps-base-check (4) code a web test failed
web / react-apps-check unknown no known signature; failed step: Check webview test shards

Failures:

  • seen on other PRs accent-glass-invariants.test.ts no orange or system accent and no opaque glass on 2229 gallery stages: failed (job)
  • seen on other PRs agent-pane-agents-pill.test.ts by default the running agents sit in the tray above the composer, not in the transcript: failed (job)
  • seen on other PRs agent-pane-bottom-stack.test.ts in a short pane the transcript keeps room and the queue scrolls inside its height: failed (job)
  • seen on other PRs agent-pane-older-rows-fade.test.ts Reduce Motion draws rows of an older page in full ink at once: failed (job)
  • seen on other PRs agent-pane-queue-collapse.test.ts a long queue > an opened long queue keeps Show less and the focused row in sight, never a half row (...: failed (job)
  • seen on other PRs agent-pane-queue-collapse.test.ts a long queue > an opened queue with a two-line row shows whole rows only, no row under another (capt...: failed (job)
  • seen on other PRs agent-pane-queue-collapse.test.ts a long queue > a folded queue with a long row opened shows only whole rows above the fold (capture, ...: failed (job)
  • seen on other PRs agent-pane-suggested-tasks.test.ts messages from other agents > a long message's card shows its first six lines; the chevron or a click...: failed (job)
  • seen on other PRs agent-pane-attach-huge-files.test.ts 0 .mov 300 MB paste local: failed (job)
  • seen on other PRs agent-pane-attach-huge-files.test.ts 0 .mov 300 MB drop remote: failed (job)
  • ... 22 more in the job logs
Matched log lines
web / react-apps-base-check (1): (fail) no orange or system accent and no opaque glass on 2229 gallery stages [445125.76ms]
web / react-apps-base-check (2): (fail) 0 .mov 300 MB paste local [2415.88ms]
web / react-apps-base-check (3): (fail) the summary card lists the chat's background tasks: running ones first, the finished folded [921.33ms]
web / react-apps-base-check (4): (fail) a chat on an SSH machine > counts its running turn from the prompt and offers no machine switch mid-chat [30892.69ms]

Not re-run automatically: web / react-apps-base-check (1), web / react-apps-base-check (2), web / react-apps-base-check (3), web / react-apps-base-check (4), web / react-apps-check are not machine failures.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

red: web / Web status; base red: the same failure on the base 7c2a29c; does not block
red: web / react-apps-base-check (1); no finished base run on 7c2a29c to compare; do not merge until green; see lane-rules
red: web / react-apps-base-check (2); no finished base run on 7c2a29c to compare; do not merge until green; see lane-rules
red: web / react-apps-base-check (3); no finished base run on 7c2a29c to compare; do not merge until green; see lane-rules
red: web / react-apps-base-check (4); no finished base run on 7c2a29c to compare; do not merge until green; see lane-rules
red: web / react-apps-check; base red: the same failure on the base 7c2a29c; does not block

Tour of b04671c96a (run): no media. The tour job failed before the tour ran (build or setup); the run log has the output.

Gallery before/after for 7a718b7: 16 states rendered, 2 images differ, 1967 more touched states not rendered. Images are in the run's artifact, not on this public PR.

gh run download 40360681178 -R manaflow-ai/cmux -n gallery-advisory-24233-7a718b7b3831f164f923de82ec3f0142b592a778 (then read gallery-review.json)

States
State Theme Status Diff Picked by
agent-pane.permission-panel/allow-refused Apple System Colors Light new 0% own Panel.gallery.tsx
agent-pane.permission-panel/allow-refused Apple System Colors new 0% own Panel.gallery.tsx
agent-pane.permission-panel/buttons-current Apple System Colors Light unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/buttons-current Apple System Colors unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/buttons-under Apple System Colors Light unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/buttons-under Apple System Colors unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/buttons-segmented Apple System Colors Light unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/buttons-segmented Apple System Colors unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/buttons-text Apple System Colors Light unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/buttons-text Apple System Colors unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/pending Apple System Colors Light unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/pending Apple System Colors unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/expanded Apple System Colors Light unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/expanded Apple System Colors unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/allow-once Apple System Colors Light unchanged 0% own Panel.gallery.tsx
agent-pane.permission-panel/allow-once Apple System Colors unchanged 0% own Panel.gallery.tsx
agent-pane.header-tools/idle Apple System Colors Light unchanged 0% names Panel.tsx
agent-pane.header-tools/idle Apple System Colors unchanged 0% names Panel.tsx
agent-pane.header-tools/panels-open Apple System Colors Light unchanged 0% names Panel.tsx
agent-pane.header-tools/panels-open Apple System Colors unchanged 0% names Panel.tsx
agent-pane.header-tools/panel-open Apple System Colors Light unchanged 0% names Panel.tsx
agent-pane.header-tools/panel-open Apple System Colors unchanged 0% names Panel.tsx
agent-pane.header-tools/panel-icon-list Apple System Colors Light unchanged 0% names Panel.tsx
agent-pane.header-tools/panel-icon-list Apple System Colors unchanged 0% names Panel.tsx
agent-pane.header-tools/panel-icon-info Apple System Colors Light unchanged 0% names Panel.tsx
agent-pane.header-tools/panel-icon-info Apple System Colors unchanged 0% names Panel.tsx
agent-pane.header-tools/panel-icon-session Apple System Colors Light unchanged 0% names Panel.tsx
agent-pane.header-tools/panel-icon-session Apple System Colors unchanged 0% names Panel.tsx
agent-pane.header-tools/plain-icons Apple System Colors Light unchanged 0% names Panel.tsx
agent-pane.header-tools/plain-icons Apple System Colors unchanged 0% names Panel.tsx
agent-pane.header-tools/browser-preview Apple System Colors Light unchanged 0% names Panel.tsx
agent-pane.header-tools/browser-preview Apple System Colors unchanged 0% names Panel.tsx

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

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

blacksmith-sh Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Found 2 test failures on Blacksmith runners:

Failures

Test View Logs
test_the_nightly_is_covered (main.NightlyStepCoverageTests.test_the_nightly_is_cove
red)/
test_the_nightly_is_covered (main.NightlyStepCoverageTests.test_the_nightly_is_cove
red)
View Logs
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)/
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)
View Logs

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Orphaned: no activity for 13 h, and no lane holds a lease on it. Adopted by the orphan sweep: merged feat-cmux-next in at 13b6a71. A later pass queues it for the merge hub if it is reviewed, small and red only on base reds.

@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 22, 2026 19:38 — with GitHub Actions Inactive
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / nightly-guards / workflow-guard-tests / release-notary is red on 18 of 19 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › Checks job structure is red on 20 of 20 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › No fleet host names in gallery fixtures is red on 17 of 17 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › No new SwiftUI (macOS UI is AppKit) is red on 17 of 17 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next swift test is red on 12 of 17 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

teamleaderleo added a commit that referenced this pull request Oct 26, 2026
…ever lasts, changes no mode or policy, and stops when the phone is unpaired (#24233)

Tests for the five landing terms of #24233: a phone's signed Allow from a
Web connection under approve-reads is refused for allow_always and when
expired, counts once for its own prompt and nonce, is refused when replayed
on the next prompt, and is refused when the phone is unpaired after it
signed; a deny still counts. The person's proved Allow on a session whose
mode does not ask counts once and leaves the mode as it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo force-pushed the leo/paired-approval-answers branch from 13b6a71 to 7655146 Compare October 26, 2026 04:04
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next fast gates / post cmux-next/fast-gates is red on 20 of 26 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

high-signal review link / link is red on 18 of 25 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo and others added 3 commits October 26, 2026 16:27
…ted (#24233, term 4; red)

ApproverSet::verifies_then keeps the current check: the key is copied
under the lock, and the signature check and the commit run outside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ne of its signatures counts (#24233, term 4)

The approver set's lock is held from the key lookup through the nonce
spend (approver_spends), so _acpmux/approvers_set returns only after an
in-flight commit, and no signature of a removed phone counts after it.
Lock order: the prompt's lock, the approver set's, the spent-nonce store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra xhigh, next-review): SHIP at 7a718b7

No blocking findings.

@teamleaderleo teamleaderleo removed the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 27, 2026
@github-actions github-actions Bot added the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 27, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra xhigh, next-review): SHIP at b04671c

No blocking findings.

Reviewed the diff and supplied surrounding code. Tests were not run because the snapshot lacks build manifests and dependencies.

@teamleaderleo teamleaderleo removed the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 27, 2026
@github-actions github-actions Bot added the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 27, 2026
@teamleaderleo
teamleaderleo merged commit 88fc1b6 into feat-cmux-next Oct 27, 2026
221 of 243 checks passed
@teamleaderleo
teamleaderleo deleted the leo/paired-approval-answers branch October 27, 2026 02:42
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

This branch had an error being deployed

2 failed (1 outdated) and 2 inactive deployments
sentry-dsyms — b04671c9 Deployed Oct 27, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #61238
artifacts — b04671c9 Deployed Oct 27, 2026 by teamleaderleo via publish to R2 #61238
ci-routing-agent — b04671c9 Deployed Oct 27, 2026 by teamleaderleo via wait for same-key tree publisher #61238
cmux-tui-merge-tree — 7a718b7b Deployed Oct 26, 2026 by teamleaderleo via publish the merge tree #6584
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant