fix(cx-gakp): make permission cards honest per harness
Summary
- present a quiet, wrapped permission card with an expandable full command and human action label
- preserve harness supplied
allow_once,allow_always, andreject_oncewording and show the exact persistent rule suggestion - focus the composer after the No / feedback action and keep shortcuts subtle
Changelog
Fixed: permission cards now match Claude and Codex approval semantics without clipping commands or using the old heavy pill treatment.
Proof
Gallery entry: agent-pane.bottom-stack
- Claude-style permission flow: https://cmux-lawrences-mac-mini.tail137216.ts.net:18796/latest/?entry=agent-pane.bottom-stack#/agent-pane.bottom-stack/trust-then-individual
- Base/head live preview (when available): https://cmux-lawrences-mac-mini.tail137216.ts.net:18796/wt/op-next-permission-card/?entry=agent-pane.bottom-stack#/agent-pane.bottom-stack/trust-then-individual
The gallery play checks trust-before-permission sequencing, one card, action labels, and stack geometry. The live gallery host was unavailable from this checkout while opening the PR; CI can render the same entry at the pushed head.
Option layout: variants and pick (head d6c67fd4)
A first in-app recording (2ffcc46a) showed equal-height tiles: Claude's 3-line "No" stretched "Yes" into an 80 px black block, and the steer button clipped as "Interr…". Three layouts, rendered from the bottom-stack gallery on the Linux builder (Chromium, DejaVu fonts; 420 light, 420 dark, 360 light):
| A: numbered rows (picked) | B: inline buttons | C: rows, solid primary |
|---|---|---|
![]() | ![]() | ![]() |
Pick A: it mirrors Claude Code's own prompt; the numbers are real (1-9 already answer while the card has focus); 28 px rows (41 px with the rule line) instead of 77 px tiles; a 12% accent fill reads the same in light and dark; the full rule stays readable. B strands the long No on its own line; C's solid bar is heavy again and cuts the rule at 360 px.
Interrupt: .acpmux-send{width:32px} beat the steer button's width:auto; it now sizes to its label and the Access/model chips shrink instead (whole at 300/360/420/600 px; test composer-menu-width "Interrupt shows whole…" red then green).
Claude's "don't ask again" persistence landed in #20591 (which superseded #20407) with the rule kept for the session only, so the Claude labels now say "this session". The in-app recording below is of that head.
In-app before/after
Real cmux NEXT DEV builds on a fleet capture mini, --showcase mock daemon, light appearance. Script: open the Claude chat that is waiting on a permission, expand the command, move the pointer over the options, click the No option, type into the composer, then switch to the Codex chat that is waiting on a permission and open the Access menu. The mini's host name is masked.
- Before:
feat-cmux-nextb179607f (b179607f82c2, the PR's merge base; fleet job 03687e9b4c8c, mac builder) - After: PR head 2386acf8 (2386acf88a14; fleet job 40c1584eea31, mac builder). Its agent-pane
pane.jswas checked for this head's strings ("Yes, and don't ask again this session", "Granted access") before recording.

Full quality: · ·
Claude permission ask. Before: "Allow once (⌥⌘1) / Always allow / Deny (⌥⌘3)" black pills. After: numbered compact rows "1 Yes / 2 Yes, and don't ask again this session / 3 No, tell Claude what to do differently", the command in a block you can expand, and the Interrupt button shown whole.

Codex permission ask. After: the same numbered rows with the names the harness sent. The showcase mock sends "Allow / Always allow / Deny", so that is what shows.

After the No option, typed text goes into the composer on both sides:

Access menu on the Codex chat. After: a "Granted access" heading with the folder-trust grant and its remove button. Before: no grants section.

Command expanded (after): 
Seen in the recording:
- After: "Interrupt" is shown whole next to its arrow on both chats. Before, it also showed whole, but the mode and trust chips overlapped ("Trusted this chat's…" over the lock). After, they do not overlap.
- After: option rows are compact and numbered 1-3; "Yes" has a light grey fill. The 80 px black "Yes" block from the earlier recording is gone.
- After: the Claude card says "Yes, and don't ask again this session".
- After: while a turn runs, the effort chip next to the model icon shrinks to "M…" (Claude) and "H" (Codex) to make room for Interrupt. It shows "Medium" again once the turn ends.
- After: with the command expanded, it shows three times: in the title ("Run bun run lint:ci"), in the summary line and in the expanded block.
- After: the Codex card opens with its Deny row highlighted and its ⌥⌘3 hint shown. This looks like focus carried over from clicking No on the Claude card.
UNVERIFIED in app:
- Claude's rule line ("Allow … for this session"). The mock sends no
suggestions, so no rule line appears. - Codex's own option names. Real codex-acp names were not tested; the mock's generic names are shown.
- Shortcut hints on pointer hover. The synthetic pointer (
debug.mouse move) caused no hover state in the page. - Trust before permission ordering. The showcase mock has no folder-trust ask.
Capture note: the first after run hit the DEBUG main-thread watchdog inside the capture tool (debug.window_record creating a display link) and stopped before the Access menu. The run above is a clean rerun of the same build.
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Rebuilds the permission flow so folder trust and chat-wide grants become per-grant records in the composer's Access menu, and the transcript shows only pending asks.
- Claude
suggestionsand rule metadata map into localized labels (21 locales); "Yes" and "Yes, and don't ask again this session" apply only to single-option asks and state the session scope, while Codex keeps its own ACP names. - Only
reject_*kinds deny: custom and kind-less options keep their agent-supplied names and get no deny shortcut. - The Access menu mounts even when only grants exist, under a "Granted access" heading, so folder trust and chat allow stay revocable.
- Shows the full command in a click-to-expand block, adds per-option rule descriptions, and keeps action labels accessible via
aria-label. - Renders options as Claude Code's numbered prompt (1-9 number keys, quiet fill on Allow once) with per-row height, and replaces the permission status trigger with the Access menu's compact grant count.
- Makes the running action an explicit steer button sized to its label, reserves queue space below the stack, and scrolls a tall asks stack within a short pane.
Additional changes in this branch
- Agent pane can stop a running Claude subagent or workflow via
_acpmux/subagent_stop; sessions record their folder's git branch and HEAD commit at start. - Build identity moves to the
cmux-build-identityleaf crate so source edits don't recompilecmux-remote. ACPMUX_AGENT_LAUNCH=denyorCMUX_AGENTS_DISABLED=1disables agents with a localized Home notice.- Nightly recovery initializes the
ghostty-nextsubmodule before resuming accepted submissions.


