Skip to content

the permission-mode menu lists only the modes; grants are revoked in Session Info (carry #28580) - #28617

Merged
teamleaderleo merged 14 commits into
feat-cmux-nextfrom
lane/ux-carry-28580
Nov 1, 2026
Merged

teamleaderleo merged 14 commits into
feat-cmux-nextfrom
lane/ux-carry-28580

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Leo, 2026-10-29: "What the hell is this? Why the f*** is this? Grant access, trust this. What is this bottom fing nonsense? Who the f? What did this? It's not fing good. What the f is this? And clicking out of a fing context menu is sometimes slow as fing balls. What the f***?"

Carries #28580 (original lane's commits kept with authorship); #28580 closes once this lands. The original lane can no longer push, so this lane branch carries the same commits unchanged.

Takes back #27579: on purpose. #27579 aligned the mode menu's Granted access section and added access-grants-columns.test.ts to measure it; this PR removes that section (grants move to Session Info), so the test has nothing left to measure.

What changed

The composer's permission-mode menu (the "Bypass permissions ⌄" chip) drew a Granted access section under its modes: a "Trusted " row and an "Allow for this chat" row, each with an ×. The section came from #20339 (fix(cx-gakp): make permission cards honest per harness), and #27579 later aligned its columns.

  • The mode menu now lists only the modes. The section, its CSS and its permission.grants string are gone.
  • Revoking still works, from a new place: both grants move to Session Info → Environment, as rows with Revoke. They use the same callbacks as before (trustAsk.undo for the folder's trust; chat.permission_chat.revoke, still guarded by ready === false, for the chat allowance). A subagent's Session Info doesn't list them, because they belong to the chat. The chat allowance also keeps its existing Revoke menu action (permissionRevoke).
  • A chat whose harness offers no modes no longer draws a lock-only Access chip just to hold a grant. It shows no mode chip, as it would with no grants.
  • access-grants-columns.test.ts (fix(composer): Access menu's allowed rules sit on the mode rows' columns, and only their × lights #27579) measured only the removed section's columns, so it goes. So does the LockIcon it leaves without a caller, and its gallery-coverage allowlist line.

Known gap, not changed here: Session Info sits behind Debug Settings agent.inspector, which is off by default. With default settings a grant is still revoked the way it was before it showed in this menu: the folder trust bar's Undo while it shows, and the Revoke menu action for a chat allowance. This PR doesn't change that default (the rulebook says never flip a default unasked). If Leo wants a default-visible home, Settings → Folder Trust is the natural one. It holds the trust rules but no per-folder answers today.

Spec: design/menus.md (this lane owns the mode-menu trust section) and design/session-info.md. After landing, menus.md gets the rule "the mode menu lists modes only; grants are revoked in Session Info".

Tests

  • test/access-chip-pending-warning.test.tsx: the access menu lists modes and draws no granted-access section and no "Granted access" text. This replaces the test that asserted the section.
  • test/chat-inspector-environment.test.tsx: new test. Environment lists both grants last, each with a Revoke button whose label names the grant, and each click calls its own revoke.
  • Galleries: ComposerPickers modes-only replaces access-grants; agent-pane.environment-save gains granted; bottomStack trustThenPermission now asserts the composer draws no Access menu for a grant.
  • bun run typecheck: no type errors in 3237 files, at 617a991, on Linux.
  • bun test access-chip, chat-inspector-environment, composer-row-layout, gallery-coverage and strings-build-output: 25 pass, 1 skip (browser lane). gallery-coverage.test.ts has 3 failures that are base reds: the same three tests fail at base 17368d2 with the same lists. The only difference is the component count (818 → 817, the removed LockIcon).

Carry re-run (cmux-ci test, Linux, at 617a991): typecheck clean; access-chip-pending-warning, chat-inspector-environment, composer-row-layout, quiet-access-chip, access-chip-refused-mode, agent-pane-bottom-stack and strings-build-output pass. gallery-coverage.test.ts fails 2 tests scoped to this PR's files (a RowFrame cover in App.tsx, and the stale ChatComposerChips and NewTabComposerChips allowlist lines). Both fail the same way, with the same lists, at base 17368d2: base reds, not changed here.

Screenshots

Gallery stills, Linux Chromium at 2x, synthetic fixtures, from a cmux-ci test gallery run at 617a991 (before: base 17368d2, entry agent-pane.composer-pickers variant access-grants; after: modes-only and agent-pane.environment-save granted). Each menu still is the play step right after clicking Mode. The before stage is 260 px tall, so the top of that menu (which opens upward) is cut off; the Granted access section under the modes is the part that matters.

light dark
Before: mode menu with Granted access before light before dark
After: mode menu, modes only after light after dark

The Session Info grants still was taken at 617a991, before 456cd87 made each grant's Revoke show only while its own row is hovered or focused (inspector.css, .acpmux-chat-inspector-row--grant), so it was removed as stale. Leo, 2026-10-31: "We don't need screenshots, just let me try the stuff out."

Viewed after image: yes
Visual verdict: The mode menu now holds only its mode rows, like the mode menus in ChatGPT and Claude, and the two grants sit as plain rows with Revoke at the end of Session Info's Environment list, in its own row style.

Review

Independent review (Codex gpt-6-astra, high, fast) at 234f147: SHIP, no correctness findings. The stale comments and the unused string it noted are fixed in 617a991. A delta review at the final head follows.

Agent session: agent session

🤖 Generated with Claude Code

teamleaderleo and others added 2 commits October 30, 2026 12:49
…Session Info

Leo, 2026-10-29: "What the hell is this? Why the fuck is this? Grant access,
trust this. What is this bottom fucking bullshit? ... It's not fucking good."

The composer's permission-mode menu drew a "Granted access" section under the
modes (a "Trusted <folder>" row and an "Allow for this chat" row, each with an
x), added in #20339. The menu now lists the modes alone. The two grants move
to Session Info's Environment section as rows with Revoke, through the same
callbacks (folder trust undo, chat.permission_chat.revoke). Chats that offer
no modes no longer draw a lock-only Access chip for a grant.

Removes access-grants-columns.test.ts (#27579), which measured only the
section's columns, and the LockIcon left without a caller.

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

Agent-Session: https://cmux.com/deeplink/agent?id=aid-f2206f5448805d85f14b
…rant comments

Adds the Environment gallery's granted variant (folder trust and a chat
allowance, each with Revoke), updates three comments that still placed grants
in the mode menu, and removes the now-unused permission.grants string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-f2206f5448805d85f14b
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@blacksmith-sh

blacksmith-sh Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Found 4 test failures on Blacksmith runners:

Failures

Test View Logs
main.DeveloperIdAppTests.test_download_prints_the_active_profile_only/
test_download_prints_the_active_profile_only
View Logs
stubs: /System/Library/Frameworks/Security.framework/Versions/A/Security has no stub/
Security has no stub
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_CFRetain']/
CoreFoundation, missing 1: ['_CFRetain']
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_log2']/
CoreFoundation, missing 1: ['_log2']
View Logs

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

@cmux-bot

cmux-bot Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

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

Review 4ee378d: 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=pr28617 -f ref=lane/ux-carry-28580 -f platform=macos -f backend_mode=local

Gallery before/after for 4ee378d: 16 states rendered, 13 images differ, 1791 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 41323076302 -R manaflow-ai/cmux -n gallery-advisory-28617-4ee378d433b4225ba90329ff63b66156efa2824e (then read gallery-review.json)

States
State Theme Status Diff Picked by
agent-pane.background-activity/duration-columns Apple System Colors Light unchanged 0% own sessionInfo.css
agent-pane.background-activity/duration-columns Apple System Colors unchanged 0% own sessionInfo.css
agent-pane.background-activity/pinned-live-tail-light Apple System Colors Light unchanged 0% own sessionInfo.css
agent-pane.background-activity/pinned-live-tail-light Apple System Colors unchanged 0% own sessionInfo.css
agent-pane.background-activity/pinned-live-tail-dark Apple System Colors Light unchanged 0% own sessionInfo.css
agent-pane.background-activity/pinned-live-tail-dark Apple System Colors unchanged 0% own sessionInfo.css
agent-pane.background-activity/after-latest-message-light Apple System Colors Light unchanged 0% own sessionInfo.css
agent-pane.background-activity/after-latest-message-light Apple System Colors unchanged 0% own sessionInfo.css
agent-pane.background-activity/after-latest-message-dark Apple System Colors Light unchanged 0% own sessionInfo.css
agent-pane.background-activity/after-latest-message-dark Apple System Colors unchanged 0% own sessionInfo.css
agent-pane.background-activity/compact-pill-light Apple System Colors Light unchanged 0% own sessionInfo.css
agent-pane.background-activity/compact-pill-light Apple System Colors unchanged 0% own sessionInfo.css
agent-pane.background-activity/compact-pill-dark Apple System Colors Light unchanged 0% own sessionInfo.css
agent-pane.background-activity/compact-pill-dark Apple System Colors unchanged 0% own sessionInfo.css
agent-pane.background-activity/side-panel-light Apple System Colors Light unchanged 0% own sessionInfo.css
agent-pane.background-activity/side-panel-light Apple System Colors unchanged 0% own sessionInfo.css
agent-pane.bottom-stack/pr-queue Apple System Colors Light changed 0.179% own bottomStack.gallery.ts
agent-pane.bottom-stack/pr-queue Apple System Colors changed 0.175% own bottomStack.gallery.ts
agent-pane.bottom-stack/trust-permission-queue Apple System Colors changed 0.0576% own bottomStack.gallery.ts
agent-pane.bottom-stack/trust-permission-queue Apple System Colors Light unchanged 0% own bottomStack.gallery.ts
agent-pane.bottom-stack/trust-individual-queue Apple System Colors Light unchanged 0% own bottomStack.gallery.ts
agent-pane.bottom-stack/trust-individual-queue Apple System Colors unchanged 0% own bottomStack.gallery.ts
agent-pane.bottom-stack/trust-then-permission Apple System Colors Light changed 6.04% own bottomStack.gallery.ts
agent-pane.bottom-stack/trust-then-permission Apple System Colors changed 5.92% own bottomStack.gallery.ts
agent-pane.bottom-stack/trust-then-individual Apple System Colors Light changed 4.63% own bottomStack.gallery.ts
agent-pane.bottom-stack/trust-then-individual Apple System Colors changed 4.56% own bottomStack.gallery.ts
agent-pane.bottom-stack/trusted-permission-queue Apple System Colors Light changed 0.179% own bottomStack.gallery.ts
agent-pane.bottom-stack/trusted-permission-queue Apple System Colors changed 0.175% own bottomStack.gallery.ts
agent-pane.bottom-stack/trusted-individual-queue Apple System Colors Light changed 0.179% own bottomStack.gallery.ts
agent-pane.bottom-stack/trusted-individual-queue Apple System Colors changed 0.175% own bottomStack.gallery.ts
agent-pane.bottom-stack/trusted-individual-waiting Apple System Colors Light changed 0.179% own bottomStack.gallery.ts
agent-pane.bottom-stack/trusted-individual-waiting Apple System Colors changed 0.175% own bottomStack.gallery.ts

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

Passes: CI passes on 4ee378d433.

CI passes on 4ee378d433 (run 41323076436 attempt 1).

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.

web and size checks: green

Shipped in:

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra high, next-review): BLOCK at 617a991

  • Blocking — ChatInspector.tsx:1137: Folder-trust revocation moves exclusively into Session Info, but useChatInspector returns no panel or opener when agent.inspector is disabled (line 447). Once the trust bar disappears, users lose the previously available revoke action unless they enable a debug feature. The chat-allowance Revoke command does not revoke folder trust. Preserve an accessible folder-trust revoke action outside the mode menu when the inspector is disabled.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Visual review (independent, Claude Opus): SHIP at 617a991

Viewed the after images (light and dark). The mode menu is two plain rows (Default with its hint and check, Full access with its warning shield), a single-purpose menu in line with ChatGPT and Claude, and it fits on screen. Session Info shows Trusted and Allow-for-this-chat as one row each with a quiet Revoke at the end, in the list's own row style, with no control repeated across rows. This is the visual verdict only; the Codex BLOCK at this head (folder-trust revoke unreachable when agent.inspector is off) is a separate blocking finding and stands.

…evoke shows on its row's hover

Review of #28617 at 617a991 (P1): with default settings Session Info is off (Debug Settings
`agent.inspector`), and once trust is decided the trust bar is gone, so a trusted folder had no
visible way back. The chat menu (…), in every chat, now ends its tools rows with Revoke Granted
Access when the chat has grants: a submenu naming each one (the folder's trust, a chat-wide
allowance), whose pick revokes it through the same callbacks. Never the composer's mode menu
(Leo, 2026-10-29: "What is this bottom fucking bullshit?").

header/grantsMenu.ts builds the grants once for both places. chat-menu-revoke-access.test.tsx:
with Session Info off and a decided trust grant, the chat menu lists the grant and picking it
revokes (red with no row, green with it).

P2: Session Info's Environment shows a grant's Revoke only while that row is hovered or holds
focus, at once with no fade, as the pull request chips' x does (#28533); it stays in the Tab order.

Gallery: header-tools `menu-revoke-access`; environment-save `granted-hover`. The design-token
baseline shrinks by the access-grants height the first commit removed.

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

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra high, next-review): SHIP at 456cd87

No blocking findings.

  • Non-blocking — pr.md:16: the “Known gap” paragraph is outdated. This head adds a default-visible “Revoke Granted Access” chat submenu for both grants; update the description accordingly.

Static review only; tests were not run and screenshots were not inspected.

@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

@cmux-bot cmux-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmux-bot approves 456cd87: an independent review passed this exact head (next-review receipt by teamleaderleo). A push needs a new review; a later BLOCK at this head dismisses this approval.

@cmux-bot cmux-bot Bot 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
…ry-28580

# Conflicts:
#	webviews/src/agent-session/acpmux/App.tsx
#	webviews/src/agent-session/acpmux/Localizable.xcstrings
#	webviews/src/agent-session/acpmux/composerLocation.css
@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 (codex, router-review): SHIP at c1fdf2e

Reviewer: codex gpt-6.1-sol xhigh, fast tier.

SHIP at c1fdf2e

Visual verdict: Light/dark renders show the compact modes-only menu and correctly placed Environment grant rows without clipping or overflow.

Follow-ups: none

@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
@cmux-bot cmux-bot Bot 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
Resolve App.tsx (keep the grants block and base's Remote Control row), Composer.tsx (chips no longer take permissionAccess) and ChatInspector.tsx (AccessGrant plus base's InspectorSectionList).

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

Pins inspector.css: a grant's Revoke is transparent at rest and lights only on
its own row's :hover/:focus-within (456cd87), so a list of grants never
repeats a lit Revoke on every row.

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

Copy link
Copy Markdown
Collaborator Author

Evidence on the BLOCK at 9fab10f ("Revoke repeated on every grant row"): the gallery still it was read from was captured at 617a991, before 456cd87 made each grant's Revoke transparent at rest and visible only while its own row is hovered or focused (webviews/src/agent-session/acpmux/inspector/inspector.css, .acpmux-chat-inspector-row--grant > .acpmux-chat-inspector-link). The stale still is removed from the body.

New head e89ba08 adds webviews/test/inspector-grant-revoke-hover.test.ts, which pins that rule: Revoke is opacity: 0 with no transition at rest, and the only rule that lights a grant's Revoke is that row's own :hover/:focus-within.

@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent October 31, 2026 18:29 Inactive
@cmux-bot
cmux-bot Bot dismissed their stale review October 31, 2026 18:32

A newer verdict blocks 2dc4161 (no independent SHIP at the head).

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

BLOCK at 2dc4161

Visual verdict: inspected all 18 after images and their before pairs. The compact modes-only picker follows Claude’s pattern; no visual blocker found.

  1. [P1] [blocking] New hover rule fails the required web check — inspector.css:618. check:popup-hover-reveals, included in bun run check, rejects this selector because it has neither a popup-open counterpart nor a NON_POPUP_REVEALS exemption. Executing the checker’s logic under Node reports one violation at head and zero with this PR’s CSS block removed. Add the exact exemption for this plain Revoke button.

  2. [P2] [follow-up] Bind cached Revoke actions to their originating chat — App.tsx:7626. ChatMenu caches callbacks when opened and remains mounted across session changes. If A’s menu stays open during a keyboard or programmatic switch to B, selecting the retained chat-allowance row calls client.permissions.revoke() for B’s current session. Guard the callback with the originating session and connection identity, or invalidate the menu on a switch.

Full Bun tests were unavailable because Bun and dependencies are absent.

Follow-ups:

@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
…eveal check

Revoke is a plain button that revokes on click and opens no popup, so it needs no
:has([data-popup-open]) twin; check:popup-hover-reveals was red at 2dc4161.

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

Copy link
Copy Markdown
Collaborator Author

Review (codex-review-box/gpt-6-astra high, lane-place): SHIP at c10205d

SHIP at c10205d

Visual verdict: Both after screenshots pass. The compact mode menu matches Claude’s focused picker pattern, with no repeated revoke controls or off-screen content.

  1. [P2] [follow-up] webviews/src/agent-session/acpmux/header/ChatHeaderTools.gallery.tsx:651 and inspector/EnvironmentSave.gallery.tsx:57: Screenshots omit the new revoke submenu and hover-only Revoke rows. Capture these states for visual verification.

No blocking code findings. String generation and duplicate-key validation passed. Runtime tests were unavailable because Bun and dependencies are absent.

Follow-ups:

  • Capture the revoke submenu and Environment grant hover/focus states at this head.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 24 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="webviews/src/agent-session/acpmux/header/grantsMenu.ts">

<violation number="1" location="webviews/src/agent-session/acpmux/header/grantsMenu.ts:39">
P3: The chat-allowance grant appears enabled while its refresh is unread, but its revoke callback is a no-op. Mark the grant unavailable in this state and propagate that disabled state to both the menu child and Session Info's Revoke button.</violation>
</file>

<file name="webviews/src/agent-session/acpmux/App.tsx">

<violation number="1" location="webviews/src/agent-session/acpmux/App.tsx:7660">
P2: Read-only subagent panes still receive `accessGrants` in Session Info and the chat menu. Guard both surfaces with `readOnlySubagent` so a subagent cannot display or revoke the parent chat's grants.</violation>
</file>

<file name="webviews/test/chat-inspector-environment.test.tsx">

<violation number="1" location="webviews/test/chat-inspector-environment.test.tsx:288">
P3: Please replace this profane personal quote with a neutral description; committed test comments should remain professional and focused on the behavior under test.</violation>

<violation number="2" location="webviews/test/chat-inspector-environment.test.tsx:302">
P2: This test does not verify the stated “listed last” behavior because the fixture has no non-grant Environment rows. Add another row, such as Remote Control or a repository row, and assert that the grant rows are the final list items.</violation>
</file>

<file name="webviews/test/chat-menu-revoke-access.test.tsx">

<violation number="1" location="webviews/test/chat-menu-revoke-access.test.tsx:29">
P3: This test does not actually verify that Session Info is hidden when `agent.inspector` is off; it never mounts the Inspector, so the null assertions are vacuous. Render the Inspector in the fixture or remove the Session Info claims and test only the chat-menu revocation path.</violation>
</file>

<file name="webviews/src/agent-session/acpmux/ComposerPickers.gallery.tsx">

<violation number="1" location="webviews/src/agent-session/acpmux/ComposerPickers.gallery.tsx:425">
P3: `modesOnly` can't catch a regression: this variant no longer seeds any grants, so the absence check passes even if the mode menu starts rendering grants. Give the variant a seeded grant source again, or drop the play's grant-absence check in favor of a stronger assertion.</violation>
</file>

<file name="webviews/test/access-chip-pending-warning.test.tsx">

<violation number="1" location="webviews/test/access-chip-pending-warning.test.tsx:228">
P2: This regression test no longer supplies any grant, so its “no Granted access section” assertion only covers the empty state and cannot verify the menu stays grant-free for a chat that actually has grants. Add an App-level fixture with folder/chat grants and assert those grants appear only in Session Info or the chat menu, never in this mode popup.</violation>
</file>

<file name="webviews/test/inspector-grant-revoke-hover.test.ts">

<violation number="1" location="webviews/test/inspector-grant-revoke-hover.test.ts:27">
P2: This negative assertion misses broader selectors that target the shared grant-button class, so a list-wide hover rule could regress the stated invariant while the test remains green. Include selectors targeting `acpmux-chat-inspector-link` or the grant row when collecting lit rules.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

{ id: "chat-permission", label: "Allow for this chat", onRemove: () => revoked.push("chat-permission") },
],
});
const rows = [...environment.querySelectorAll<HTMLElement>("li[data-grant]")];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This test does not verify the stated “listed last” behavior because the fixture has no non-grant Environment rows. Add another row, such as Remote Control or a repository row, and assert that the grant rows are the final list items.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/test/chat-inspector-environment.test.tsx, line 302:

<comment>This test does not verify the stated “listed last” behavior because the fixture has no non-grant Environment rows. Add another row, such as Remote Control or a repository row, and assert that the grant rows are the final list items.</comment>

<file context>
@@ -284,3 +284,28 @@ test("Environment Save also handles a synchronous callback failure", async () =>
+      { id: "chat-permission", label: "Allow for this chat", onRemove: () => revoked.push("chat-permission") },
+    ],
+  });
+  const rows = [...environment.querySelectorAll<HTMLElement>("li[data-grant]")];
+  expect(rows.map((row) => row.dataset.grant)).toEqual(["folder-trust", "chat-permission"]);
+  expect(rows[0]!.textContent).toContain("Trusted scratchpad");
</file context>

// Leo, 2026-10-29, on the mode menu's "Granted access" section: "What is this bottom fucking
// bullshit?" The menu lists the modes alone; grants are revoked in Session Info's Environment.
test("the access menu lists the modes alone, with no granted access section", async () => {
await render(reported("chat", "default"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This regression test no longer supplies any grant, so its “no Granted access section” assertion only covers the empty state and cannot verify the menu stays grant-free for a chat that actually has grants. Add an App-level fixture with folder/chat grants and assert those grants appear only in Session Info or the chat menu, never in this mode popup.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/test/access-chip-pending-warning.test.tsx, line 228:

<comment>This regression test no longer supplies any grant, so its “no Granted access section” assertion only covers the empty state and cannot verify the menu stays grant-free for a chat that actually has grants. Add an App-level fixture with folder/chat grants and assert those grants appear only in Session Info or the chat menu, never in this mode popup.</comment>

<file context>
@@ -227,21 +222,21 @@ test("a new chat acpmux starts in full access draws its warning chip on the firs
+// Leo, 2026-10-29, on the mode menu's "Granted access" section: "What is this bottom fucking
+// bullshit?" The menu lists the modes alone; grants are revoked in Session Info's Environment.
+test("the access menu lists the modes alone, with no granted access section", async () => {
+  await render(reported("chat", "default"));
   const chip = host.querySelector<HTMLElement>(".acpmux-mode")!;
-  expect(chip.querySelector(".acpmux-access-grant-count")).toBeNull();
</file context>

const shown = block(".acpmux-chat-inspector-row--grant:is(:hover, :focus-within) > .acpmux-chat-inspector-link");
expect(shown).toMatch(/opacity:\s*1;/);
// No other rule lights every grant's Revoke at once (a list-wide :hover, or the list itself).
const lit = [...css.matchAll(/([^{}]*row--grant[^{}]*)\{[^}]*opacity:\s*1/g)].map((m) => m[1].trim());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This negative assertion misses broader selectors that target the shared grant-button class, so a list-wide hover rule could regress the stated invariant while the test remains green. Include selectors targeting acpmux-chat-inspector-link or the grant row when collecting lit rules.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/test/inspector-grant-revoke-hover.test.ts, line 27:

<comment>This negative assertion misses broader selectors that target the shared grant-button class, so a list-wide hover rule could regress the stated invariant while the test remains green. Include selectors targeting `acpmux-chat-inspector-link` or the grant row when collecting lit rules.</comment>

<file context>
@@ -0,0 +1,29 @@
+  const shown = block(".acpmux-chat-inspector-row--grant:is(:hover, :focus-within) > .acpmux-chat-inspector-link");
+  expect(shown).toMatch(/opacity:\s*1;/);
+  // No other rule lights every grant's Revoke at once (a list-wide :hover, or the list itself).
+  const lit = [...css.matchAll(/([^{}]*row--grant[^{}]*)\{[^}]*opacity:\s*1/g)].map((m) => m[1].trim());
+  expect(lit).toEqual([".acpmux-chat-inspector-row--grant:is(:hover, :focus-within) > .acpmux-chat-inspector-link"]);
+});
</file context>
Suggested change
const lit = [...css.matchAll(/([^{}]*row--grant[^{}]*)\{[^}]*opacity:\s*1/g)].map((m) => m[1].trim());
const lit = [...css.matchAll(/([^{}]*(?:\.acpmux-chat-inspector-row(?:\b|--grant)|\.acpmux-chat-inspector-link\b)[^{}]*)\{[^}]*opacity:\s*1/g)].map((m) => m[1].trim());

machineName,
repository: githubRepository,
shell,
grants: accessGrants,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Read-only subagent panes still receive accessGrants in Session Info and the chat menu. Guard both surfaces with readOnlySubagent so a subagent cannot display or revoke the parent chat's grants.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/src/agent-session/acpmux/App.tsx, line 7660:

<comment>Read-only subagent panes still receive `accessGrants` in Session Info and the chat menu. Guard both surfaces with `readOnlySubagent` so a subagent cannot display or revoke the parent chat's grants.</comment>

<file context>
@@ -7635,6 +7657,7 @@ function AcpmuxPane() {
     machineName,
     repository: githubRepository,
     shell,
+    grants: accessGrants,
     onOpenOutput: quick ? undefined : inspectorSubagent ? openSubagentOutput : openOutput,
     onOpenSourceFile: quick || !inspectorLocal ? undefined : (path) => openSourceFile(path, callNative),
</file context>

Comment on lines +288 to +289
// Leo, 2026-10-29, on the composer mode menu's "Granted access" section: "What is this bottom
// fucking bullshit?" The grants moved here: each one is a row with its own Revoke.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Please replace this profane personal quote with a neutral description; committed test comments should remain professional and focused on the behavior under test.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/test/chat-inspector-environment.test.tsx, line 288:

<comment>Please replace this profane personal quote with a neutral description; committed test comments should remain professional and focused on the behavior under test.</comment>

<file context>
@@ -284,3 +284,28 @@ test("Environment Save also handles a synchronous callback failure", async () =>
   expect(container.querySelector('[role="alert"]')).not.toBeNull();
 });
+
+// Leo, 2026-10-29, on the composer mode menu's "Granted access" section: "What is this bottom
+// fucking bullshit?" The grants moved here: each one is a row with its own Revoke.
+test("what the chat was granted is listed last, each with Revoke", async () => {
</file context>
Suggested change
// Leo, 2026-10-29, on the composer mode menu's "Granted access" section: "What is this bottom
// fucking bullshit?" The grants moved here: each one is a row with its own Revoke.
// Grants moved here from the composer mode menu: each grant is a row with its own Revoke.

pretendToBeVisual: true,
});
// Debug Settings `agent.inspector` at its default: off, so Session Info never shows.
setHeaderState({ inspector: false });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This test does not actually verify that Session Info is hidden when agent.inspector is off; it never mounts the Inspector, so the null assertions are vacuous. Render the Inspector in the fixture or remove the Session Info claims and test only the chat-menu revocation path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/test/chat-menu-revoke-access.test.tsx, line 29:

<comment>This test does not actually verify that Session Info is hidden when `agent.inspector` is off; it never mounts the Inspector, so the null assertions are vacuous. Render the Inspector in the fixture or remove the Session Info claims and test only the chat-menu revocation path.</comment>

<file context>
@@ -0,0 +1,169 @@
+    pretendToBeVisual: true,
+  });
+  // Debug Settings `agent.inspector` at its default: off, so Session Info never shows.
+  setHeaderState({ inspector: false });
+  class FakeResizeObserver {
+    observe() {}
</file context>

await ctx.click({ selector: ".acpmux-access-grant-remove" });
await ctx.waitFor(() => ctx.document.querySelectorAll(".acpmux-access-grant").length === 1);
await ctx.waitFor(() => ctx.document.querySelector('[role="menuitemradio"]') !== null);
if (ctx.document.querySelector(".acpmux-access-grants, .acpmux-access-grant"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: modesOnly can't catch a regression: this variant no longer seeds any grants, so the absence check passes even if the mode menu starts rendering grants. Give the variant a seeded grant source again, or drop the play's grant-absence check in favor of a stronger assertion.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/src/agent-session/acpmux/ComposerPickers.gallery.tsx, line 425:

<comment>`modesOnly` can't catch a regression: this variant no longer seeds any grants, so the absence check passes even if the mode menu starts rendering grants. Give the variant a seeded grant source again, or drop the play's grant-absence check in favor of a stronger assertion.</comment>

<file context>
@@ -422,20 +417,13 @@ const managedMode: Play = async (ctx) => {
-  await ctx.click({ selector: ".acpmux-access-grant-remove" });
-  await ctx.waitFor(() => ctx.document.querySelectorAll(".acpmux-access-grant").length === 1);
+  await ctx.waitFor(() => ctx.document.querySelector('[role="menuitemradio"]') !== null);
+  if (ctx.document.querySelector(".acpmux-access-grants, .acpmux-access-grant"))
+    throw new Error("The mode menu shows granted access");
   await ctx.press("Escape");
</file context>

{
id: "chat-permission",
label: t("permission.allowChat"),
onRemove: () => (sources.allowanceUnread ? undefined : sources.revokeAllowance()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The chat-allowance grant appears enabled while its refresh is unread, but its revoke callback is a no-op. Mark the grant unavailable in this state and propagate that disabled state to both the menu child and Session Info's Revoke button.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/src/agent-session/acpmux/header/grantsMenu.ts, line 39:

<comment>The chat-allowance grant appears enabled while its refresh is unread, but its revoke callback is a no-op. Mark the grant unavailable in this state and propagate that disabled state to both the menu child and Session Info's Revoke button.</comment>

<file context>
@@ -0,0 +1,61 @@
+          {
+            id: "chat-permission",
+            label: t("permission.allowChat"),
+            onRemove: () => (sources.allowanceUnread ? undefined : sources.revokeAllowance()),
+          },
+        ]
</file context>

…ry-28580

# Conflicts:
#	webviews/scripts/check-popup-hover-reveals.ts
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (independent Codex gpt-6.1-sol, high, via the review router; full diff from merge base 704abda to this head): SHIP at 9479bcd.

No P1/P2 findings.

Resolves the conflicts from the Inspector -> Session Info rename (#29921) and
Codex's look composer row (#30395): grant rows, CSS, hover-reveal exemption and
tests move to the session-info names; the composer bar keeps base's layout.

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

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5): SHIP at b120bb2

Delta review of the merge resolution (Inspector -> Session Info rename #29921, composer bar #30395): nothing base added is dropped, the PR intent is intact, no stale inspector names.

Follow-ups:

  • rename test/inspector-grant-revoke-hover.test.ts to session-info-grant-revoke-hover.test.ts

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5): SHIP at f8e0741

Delta since b120bb2: test/session-info-environment.test.tsx calls renderSessionInfo (base renamed renderInspector, #29921). No other stale Inspector-era names in the PR files.

Follow-ups:

  • rename test/inspector-grant-revoke-hover.test.ts to session-info-grant-revoke-hover.test.ts

# Conflicts:
#	webviews/src/agent-session/acpmux/session-info/SessionInfo.tsx
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5): SHIP at 4ee378d

Delta: merge of base cb44deb (#30953 cloudRepositoryAvailable) kept beside the PR grants prop at every level; the subagent guard scope ? undefined : grants survives; no base line dropped.

Follow-ups:

  • permissions/Panel.gallery.tsx:174 comment still says inspector/EnvironmentSection.tsx
  • rename test/inspector-grant-revoke-hover.test.ts to session-info-grant-revoke-hover.test.ts

@teamleaderleo
teamleaderleo merged commit c3f5fba into feat-cmux-next Nov 1, 2026
167 of 170 checks passed
@teamleaderleo
teamleaderleo deleted the lane/ux-carry-28580 branch November 1, 2026 16:19

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 4ee378d4 Deployed Nov 1, 2026 by teamleaderleo via wait for same-key tree publisher #96779
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