Skip to content

fix(ui): disable permission-mode picker while the composer awaits a permission decision - #822

Merged
Astro-Han merged 1 commit into
mainfrom
fix/composer-permission-select-disabled-during-wait
Jul 12, 2026
Merged

Astro-Han merged 1 commit into
mainfrom
fix/composer-permission-select-disabled-during-wait

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

  • Add props.disabled to the PermissionModeSelect disabled condition in the composer, so the mode picker is non-clickable while the composer is frozen awaiting a permission decision.
  • Update session-status-presentation contract to pin the new disabled expression.

Why

The composer's PermissionModeSelect disabled condition keyed off permissionModePending and permissionModeDisabledReason but not props.disabled. props.disabled is driven by Boolean(activePermission) in app-shell.tsx, while permissionModeDisabledReason is keyed off activeSessionForView?.status === 'waiting_for_user' — and the two are NOT fully coupled. A pending permission can set props.disabled before the session status flips to waiting_for_user, leaving a window where the composer is frozen (permission-hint pulsing, attach button + textarea disabled) but the mode picker stays clickable. Letting the user reconfigure the global permission mode mid-decision is incoherent (it doesn't resolve the pending tool call) and breaks the freeze-state symmetry. Found by the #546 Composer combined-state audit. Refs #546.

Scope

  • One prop on one component: disabled on PermissionModeSelect in packages/ui/src/composer.tsx. No other composer behavior or surface.
  • The disabledReason tooltip path is unchanged — it still explains mode-pending / streaming / running / waiting; props.disabled adds a direct freeze guard without changing tooltip copy.

Verification

  • RED→GREEN TDD: the session-status-presentation contract was updated to assert the new disabled expression first; it failed against the current source, then passed after the one-prop fix.
  • npm run -w @maka/desktop test — 2346/2346 pass.
  • npm run -w @maka/desktop typecheck + test:checks (console/a11y/copy) — clean.
  • CDP runtime verification on the live permission-destructive fixture (light-990-motion): before the fix the Select trigger read disabled=false, pointer-events:auto (clickable); after, disabled=true, pointer-events:none, opacity:0.45 (disabled). The model-processing (streaming) fixture is unchanged — still disabled via the disabledReason streaming branch, no regression. Substitute evidence for a state-dependent behavior change per the repo's "interaction behavior: disabled checks" guidance.
  • No screenshot: the change is a state-dependent disabled/pointer-events swap, not pixel appearance; CDP computed-style on the live render + the source-string contract are the locking layers.

Impact

User-visible: the permission-mode picker is no longer clickable while a tool permission is pending — consistent with the attach button and textarea, already disabled in that state. No API, storage, or migration change.

Reviewer notes

Ready for review

  • Verification is complete and the results above are current
  • Impact, compatibility, migration, documentation, and release-note needs are addressed
  • User-visible UI/UX changes include visual evidence, or Verification explains why it is not applicable (state-dependent disabled swap; substitute evidence above)

…ermission decision

The composer's PermissionModeSelect disabled condition keyed off
`permissionModePending` and `permissionModeDisabledReason` but not
`props.disabled`. `props.disabled` is driven by `Boolean(activePermission)`
in app-shell.tsx, while `permissionModeDisabledReason` is keyed off
`activeSessionForView?.status === 'waiting_for_user'` — and the two are NOT
fully coupled. A pending permission can set `props.disabled` before the
session status flips to `waiting_for_user`, leaving a window where the
composer is frozen (permission-hint pulsing, attach button + textarea
disabled) but the mode picker stays clickable. Letting the user reconfigure
the global permission mode mid-decision is incoherent (it doesn't resolve
the pending tool call) and breaks the freeze-state symmetry.

Add `props.disabled` to the Select's disabled condition so the picker
respects the composer's own frozen state directly, not only the
separately-computed disabledReason.

CDP-verified on the live `permission-destructive` fixture: before the guard
the Select trigger read `disabled=false`, `pointer-events:auto`; after,
`disabled=true`, `pointer-events:none`, `opacity:0.45`. The `model-processing`
(streaming) case is unchanged — still disabled via the disabledReason
streaming branch, no regression.

Updated `session-status-presentation` contract to pin the new disabled
expression with a comment documenting the gap.

Found by the #546 Composer combined-state audit. Refs #546
@Astro-Han
Astro-Han merged commit f9e265c into main Jul 12, 2026
3 checks passed
@Astro-Han
Astro-Han deleted the fix/composer-permission-select-disabled-during-wait branch July 14, 2026 05:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant