summaryrefslogtreecommitdiffhomepage
path: root/specs
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-19 15:02:45 -0600
committerGitHub <[email protected]>2026-02-19 15:02:45 -0600
commit49cc872c4415f081b4208d16fd0d85e425a75eed (patch)
treeed78239c2a2bb941c5008f378a00592f5b10717a /specs
parentf8dad0ae170acb9667d9402c162f7c29980373c1 (diff)
downloadopencode-49cc872c4415f081b4208d16fd0d85e425a75eed.tar.gz
opencode-49cc872c4415f081b4208d16fd0d85e425a75eed.zip
chore: refactor composer/dock components (#14328)
Diffstat (limited to 'specs')
-rw-r--r--specs/session-composer-refactor-plan.md240
1 files changed, 240 insertions, 0 deletions
diff --git a/specs/session-composer-refactor-plan.md b/specs/session-composer-refactor-plan.md
new file mode 100644
index 000000000..08fb0d832
--- /dev/null
+++ b/specs/session-composer-refactor-plan.md
@@ -0,0 +1,240 @@
+# Session Composer Refactor Plan
+
+## Goal
+
+Improve structure, ownership, and reuse for the bottom-of-session composer area without changing user-visible behavior.
+
+Scope:
+
+- `packages/ui/src/components/dock-prompt.tsx`
+- `packages/app/src/components/session-todo-dock.tsx`
+- `packages/app/src/components/question-dock.tsx`
+- `packages/app/src/pages/session/session-prompt-dock.tsx`
+- related shared UI in `packages/app/src/components/prompt-input.tsx`
+
+## Decisions Up Front
+
+1. **`session-prompt-dock` should stay route-scoped.**
+ It is session-page orchestration, so it belongs under `pages/session`, not global `src/components`.
+
+2. **The orchestrator should keep blocking ownership.**
+ A single component should decide whether to show blockers (`question`/`permission`) or the regular prompt input. This avoids drift and duplicate logic.
+
+3. **Current component does too much.**
+ Split state derivation, permission actions, and rendering into smaller units while preserving behavior.
+
+4. **There is style duplication worth addressing.**
+ The prompt top shell and lower tray (`prompt-input.tsx`) visually overlap with dock shells/footers and todo containers. We should extract reusable dock surface primitives.
+
+---
+
+## Phase 0 (Mandatory Gate): Baseline E2E Coverage
+
+No refactor work starts until this phase is complete and green locally.
+
+### 0.1 Deterministic test harness
+
+Add a test-only way to put a session into exact dock states, so tests do not rely on model/tool nondeterminism.
+
+Proposed implementation:
+
+- Add a guarded e2e route in backend (enabled only when a dedicated env flag is set by e2e-local runner).
+ - New route file: `packages/opencode/src/server/routes/e2e.ts`
+ - Mount from: `packages/opencode/src/server/server.ts`
+ - Gate behind env flag (for example `OPENCODE_E2E=1`) so this route is never exposed in normal runs.
+- Add seed helpers in app e2e layer:
+ - `packages/app/e2e/actions.ts` (or `fixtures.ts`) helpers to:
+ - seed question request for a session
+ - seed permission request for a session
+ - seed/update todos for a session
+ - clear seeded blockers/todos
+- Update e2e-local runner to set the flag:
+ - `packages/app/script/e2e-local.ts`
+
+### 0.2 New e2e spec
+
+Create a focused spec:
+
+- `packages/app/e2e/session/session-composer-dock.spec.ts`
+
+Test matrix (minimum required):
+
+1. **Default prompt dock**
+ - no blocker state
+ - assert prompt input is visible and focusable
+ - assert blocker cards are absent
+
+2. **Blocked question flow**
+ - seed question request for session
+ - assert question dock renders
+ - assert prompt input is not shown/active
+ - answer and submit
+ - assert unblock and prompt input returns
+
+3. **Blocked permission flow**
+ - seed permission request with patterns + optional description
+ - assert permission dock renders expected actions
+ - assert prompt input is not shown/active
+ - test each response path (`once`, `always`, `reject`) across tests
+ - assert unblock behavior
+
+4. **Todo dock transitions and collapse behavior**
+ - seed todos with `pending`/`in_progress`
+ - assert todo dock appears above prompt and can collapse/expand
+ - update todos to all completed/cancelled
+ - assert close animation path and eventual hide
+
+5. **Keyboard focus behavior while blocked**
+ - with blocker active, typing from document context must not focus prompt input
+ - blocker actions remain keyboard reachable
+
+Notes:
+
+- Prefer stable selectors (`data-component`, `data-slot`, role/name).
+- Extend `packages/app/e2e/selectors.ts` as needed.
+- Use `expect.poll` for async transitions.
+
+### 0.3 Gate commands (must pass before Phase 1)
+
+Run from `packages/app` (never from repo root):
+
+```bash
+bun test:e2e:local -- e2e/session/session-composer-dock.spec.ts
+bun test:e2e:local -- e2e/prompt/prompt.spec.ts e2e/prompt/prompt-multiline.spec.ts e2e/commands/input-focus.spec.ts
+bun test:e2e:local
+```
+
+If any fail, stop and fix before refactor.
+
+---
+
+## Phase 1: Structural Refactor (No Intended Behavior Changes)
+
+### 1.1 Colocate session-composer files
+
+Create a route-local composer folder:
+
+```txt
+packages/app/src/pages/session/composer/
+ session-composer-region.tsx # rename/move from session-prompt-dock.tsx
+ session-composer-state.ts # derived state + actions
+ session-permission-dock.tsx # extracted from inline JSX
+ session-question-dock.tsx # moved from src/components/question-dock.tsx
+ session-todo-dock.tsx # moved from src/components/session-todo-dock.tsx
+ index.ts
+```
+
+Import updates:
+
+- `packages/app/src/pages/session.tsx` imports `SessionComposerRegion` from `pages/session/composer`.
+
+### 1.2 Split responsibilities
+
+- Keep `session-composer-region.tsx` focused on rendering orchestration:
+ - blocker mode vs normal mode
+ - relative stacking (todo above prompt)
+ - handoff fallback rendering
+- Move side-effect/business pieces into `session-composer-state.ts`:
+ - derive `questionRequest`, `permissionRequest`, `blocked`, todo visibility state
+ - permission response action + in-flight state
+ - todo close/open animation state
+
+### 1.3 Remove duplicate blocked logic in `session.tsx`
+
+Current `session.tsx` computes `blocked` independently. Make the composer state the single source for blocker status consumed by both:
+
+- page-level keydown autofocus guard
+- composer rendering guard
+
+### 1.4 Keep prompt gating in orchestrator
+
+`session-composer-region` should remain responsible for choosing whether `PromptInput` renders when blocked.
+
+Rationale:
+
+- this is layout-mode orchestration, not prompt implementation detail
+- keeps blocker and prompt transitions coordinated in one place
+
+### 1.5 Phase 1 acceptance criteria
+
+- No intentional behavior deltas.
+- Phase 0 suite remains green.
+- `session-prompt-dock` no longer exists as a large mixed-responsibility component.
+- Session composer files are colocated under `pages/session/composer`.
+
+---
+
+## Phase 2: Reuse + Styling Maintainability
+
+### 2.1 Extract shared dock surface primitives
+
+Create reusable shell/tray wrappers to remove repeated visual scaffolding:
+
+- primary elevated surface (prompt top shell / dock body)
+- secondary tray surface (prompt bottom bar / dock footer / todo shell)
+
+Proposed targets:
+
+- `packages/ui/src/components` for shared primitives if reused by both app and ui components
+- or `packages/app/src/pages/session/composer` first, then promote to ui after proving reuse
+
+### 2.2 Apply primitives to current components
+
+Adopt in:
+
+- `packages/app/src/components/prompt-input.tsx`
+- `packages/app/src/pages/session/composer/session-todo-dock.tsx`
+- `packages/ui/src/components/dock-prompt.tsx` (where appropriate)
+
+Focus on deduping patterns seen in:
+
+- prompt elevated shell styles (`prompt-input.tsx` form container)
+- prompt lower tray (`prompt-input.tsx` bottom panel)
+- dock prompt footer/body and todo dock container
+
+### 2.3 De-risk style ownership
+
+- Move dock-specific styling out of overly broad files (for example, avoid keeping new dock-specific rules buried in unrelated message-part styling files).
+- Keep slot names stable unless tests are updated in the same PR.
+
+### 2.4 Optional follow-up (if low risk)
+
+Evaluate extracting shared question/permission presentational pieces used by:
+
+- `packages/app/src/pages/session/composer/session-question-dock.tsx`
+- `packages/ui/src/components/message-part.tsx`
+
+Only do this if behavior parity is protected by tests and the change is still reviewable.
+
+### 2.5 Phase 2 acceptance criteria
+
+- Reduced duplicated shell/tray styling code.
+- No regressions in blocker/todo/prompt transitions.
+- Phase 0 suite remains green.
+
+---
+
+## Implementation Sequence (single branch)
+
+1. **Step A - Baseline safety net**
+ - Add e2e harness + new session composer dock spec + selector/helpers.
+ - Must pass locally before any refactor work proceeds.
+
+2. **Step B - Phase 1 colocation/splitting**
+ - Move/rename files, extract state and permission component, keep behavior.
+
+3. **Step C - Phase 1 dedupe blocked source**
+ - Remove duplicate blocked derivation and wire page autofocus guard to shared source.
+
+4. **Step D - Phase 2 style primitives**
+ - Introduce shared surface primitives and migrate prompt/todo/dock usage.
+
+5. **Step E (optional) - shared question/permission presentational extraction**
+
+---
+
+## Rollback Strategy
+
+- Keep each step logically isolated and easy to revert.
+- If regressions occur, revert the latest completed step first and rerun the Phase 0 suite.
+- If style extraction destabilizes behavior, keep structural Phase 1 changes and revert only Phase 2 styling commits.