feat: unify Specs and Planner into one page #557

Closed
opened 2026-04-29 22:54:51 +00:00 by claude-desktop · 2 comments
Collaborator

As an operator, I want a single workspace that handles both spec authoring and planner sessions so that I stop context-switching between two routes that already share most of their data.

Today the foreman surface is split:

  • /specs is document-first — list of .md files, full-page markdown editor, breakdown preview, "Create issues" button. Uses ForemanFileEntry[] + React Query.
  • /planner is session-first — three-column layout with sessions sidebar, transcript, composer. Uses usePlannerStore (Zustand) + SSE streaming. Has an inline ?spec=<name> mode that already opens a spec in the middle column.

These are two views of the same workflow (spec → breakdown → issues, optionally guided by a chat session). The unification follows the agents-page model: single route, accordion-style sections for each surface, drawers for transient detail, autosave instead of explicit Save buttons.

Acceptance criteria

Information architecture

  • One top-nav entry replaces "Specs" and "Planner". Working name: Workspace (/workspace). Final name decided during implementation.
  • Single page exposes three concerns:
    • Sessions list (left rail) — chronological foreman chat sessions, "+ New session" CTA.
    • Active editor (center) — switches between spec markdown editor and chat transcript depending on what the operator opened.
    • Composer / actions (bottom or right) — message input for chat mode, "Create issues" / breakdown preview controls for spec mode.
  • URL state encodes the selection: ?session=<id> and/or ?spec=<name>. Bookmarkable. Deep-linkable from issue comments.
  • When neither is selected, the editor pane shows an empty state with the two primary CTAs ("Open spec", "Start session").

Sessions

  • Sessions list collapses to a slide-in drawer below sm: (mobile pattern matches the agents page).
  • Each session row: title (first user message preview), agent role chip, last-active timestamp.
  • Click a session → loads transcript into the center pane; ?session=<id> replaces any prior ?spec.

Specs

  • Specs list lives in the same sidebar as sessions, separated by a labelled section header (Sessions / Specs).
  • Click a spec → loads markdown editor into the center pane; ?spec=<name> replaces any prior ?session.
  • Spec editor preserves current behaviour: live breakdown preview, "Create issues" action, hotkeys.
  • Spec autosaves on blur — no explicit Save button (mirror agents-page autosave + inline status pill).
  • "Create issues" is a primary CTA in the right-side action region; opens the existing breakdown preview drawer.

Cross-mode

  • Composer is only visible in chat mode.
  • Switching between spec and session is instant — no full page reload, no double fetch.
  • An inline action lets the operator promote a chat session to a spec ("Save as spec…") and lets a spec optionally open a session for clarification ("Discuss with foreman").
  • Drawers (history, breakdown preview, etc.) use the <Drawer> primitive from PR #555role="dialog" + focus trap + focus return baked in.

Components — reuse first

  • Reuse <Button> for every clickable. No raw <button> for new code paths.
  • Reuse <Drawer> for any slide-out overlay.
  • Reuse <ChipInput> if any tag/label input is needed.
  • Lucide icons everywhere — no Unicode glyphs (e.g. ▶, ✓, ⚙, ×).
  • Tokyo Night Storm tokens only — no raw hex.
  • If a primitive is missing (e.g. tabs, two-pane split), add it under apps/web/src/components/ with the same a11y baseline as <Drawer>.

Responsive + a11y (parity with the agents page)

  • Mobile (≤ 639px): sidebar collapses to a drawer; editor + composer stack vertically.
  • Tablet (640–1023px): single-column with a top tab strip for switching between sessions / specs / composer.
  • Desktop (≥ 1024px): three-column layout.
  • All form inputs have associated <label> (or aria-label) and link errors via aria-describedby + aria-invalid.
  • Save status (spec autosave + chat send) surfaces in a role="status" aria-live="polite" region.
  • All keyboard paths exercised: Tab through sidebar → editor → composer; Esc closes drawers; Enter sends message; Cmd/Ctrl+S no-ops (autosave).
  • Honours prefers-reduced-motion (already handled globally in index.css).

Migration

  • /specs and /planner redirect to the new route preserving query params (?spec= / ?session=).
  • Existing tests for spec editor + planner composer migrate to the new route or get rewritten to target the new IA.
  • docs/foreman.md updated to describe the unified surface; docs/breakdown.md follow-up if needed.

Out of scope

  • Server-side endpoint changes — /foreman/specs/*, /foreman/chat, /foreman/sessions/* stay as-is.
  • Adding new chat capabilities (slash commands, MCP wiring, etc.) — strictly a UX merge.
  • Multi-user collaboration / locking on specs — single-operator system today.
  • Removing the inline ?spec= mode that already exists in the planner — it gets superseded by the new IA but the URL contract stays.

References

  • Agents-page redesign as the model: PR #555 (feat/agents-history-drawer), commit da25428 for the <Drawer> primitive.
  • Survey: apps/web/src/routes/specs.index.tsx (230 LOC), apps/web/src/routes/specs.$specName.tsx (~5.8K LOC), apps/web/src/routes/planner.index.tsx (636 LOC), feature folders components/spec-editor/ (5 files, ~2.2K LOC) and components/planner/ (5 files, ~2.2K LOC).
  • Server endpoints: POST /foreman/chat, GET /foreman/sessions/:id, GET /foreman/stream/:task_id, POST /foreman/specs/:name, POST /foreman/breakdown-preview, POST /foreman/create-issues.
  • Existing milestone context: M22 — UI consolidation (already merged Monitor work).
As an operator, I want a single workspace that handles both spec authoring and planner sessions so that I stop context-switching between two routes that already share most of their data. Today the foreman surface is split: - **`/specs`** is document-first — list of `.md` files, full-page markdown editor, breakdown preview, "Create issues" button. Uses `ForemanFileEntry[]` + React Query. - **`/planner`** is session-first — three-column layout with sessions sidebar, transcript, composer. Uses `usePlannerStore` (Zustand) + SSE streaming. Has an inline `?spec=<name>` mode that already opens a spec in the middle column. These are two views of the same workflow (spec → breakdown → issues, optionally guided by a chat session). The unification follows the agents-page model: single route, accordion-style sections for each surface, drawers for transient detail, autosave instead of explicit Save buttons. ## Acceptance criteria ### Information architecture - [ ] One top-nav entry replaces "Specs" and "Planner". Working name: **Workspace** (`/workspace`). Final name decided during implementation. - [ ] Single page exposes three concerns: - **Sessions list** (left rail) — chronological foreman chat sessions, "+ New session" CTA. - **Active editor** (center) — switches between spec markdown editor and chat transcript depending on what the operator opened. - **Composer / actions** (bottom or right) — message input for chat mode, "Create issues" / breakdown preview controls for spec mode. - [ ] URL state encodes the selection: `?session=<id>` and/or `?spec=<name>`. Bookmarkable. Deep-linkable from issue comments. - [ ] When neither is selected, the editor pane shows an empty state with the two primary CTAs ("Open spec", "Start session"). ### Sessions - [ ] Sessions list collapses to a slide-in drawer below `sm:` (mobile pattern matches the agents page). - [ ] Each session row: title (first user message preview), agent role chip, last-active timestamp. - [ ] Click a session → loads transcript into the center pane; `?session=<id>` replaces any prior `?spec`. ### Specs - [ ] Specs list lives in the same sidebar as sessions, separated by a labelled section header (Sessions / Specs). - [ ] Click a spec → loads markdown editor into the center pane; `?spec=<name>` replaces any prior `?session`. - [ ] Spec editor preserves current behaviour: live breakdown preview, "Create issues" action, hotkeys. - [ ] Spec autosaves on blur — no explicit Save button (mirror agents-page autosave + inline status pill). - [ ] "Create issues" is a primary CTA in the right-side action region; opens the existing breakdown preview drawer. ### Cross-mode - [ ] Composer is only visible in chat mode. - [ ] Switching between spec and session is instant — no full page reload, no double fetch. - [ ] An inline action lets the operator promote a chat session to a spec ("Save as spec…") and lets a spec optionally open a session for clarification ("Discuss with foreman"). - [ ] Drawers (history, breakdown preview, etc.) use the `<Drawer>` primitive from PR #555 — `role="dialog"` + focus trap + focus return baked in. ### Components — reuse first - [ ] Reuse `<Button>` for every clickable. No raw `<button>` for new code paths. - [ ] Reuse `<Drawer>` for any slide-out overlay. - [ ] Reuse `<ChipInput>` if any tag/label input is needed. - [ ] Lucide icons everywhere — no Unicode glyphs (e.g. ▶, ✓, ⚙, ×). - [ ] Tokyo Night Storm tokens only — no raw hex. - [ ] If a primitive is missing (e.g. tabs, two-pane split), add it under `apps/web/src/components/` with the same a11y baseline as `<Drawer>`. ### Responsive + a11y (parity with the agents page) - [ ] Mobile (≤ 639px): sidebar collapses to a drawer; editor + composer stack vertically. - [ ] Tablet (640–1023px): single-column with a top tab strip for switching between sessions / specs / composer. - [ ] Desktop (≥ 1024px): three-column layout. - [ ] All form inputs have associated `<label>` (or `aria-label`) and link errors via `aria-describedby` + `aria-invalid`. - [ ] Save status (spec autosave + chat send) surfaces in a `role="status" aria-live="polite"` region. - [ ] All keyboard paths exercised: Tab through sidebar → editor → composer; Esc closes drawers; Enter sends message; Cmd/Ctrl+S no-ops (autosave). - [ ] Honours `prefers-reduced-motion` (already handled globally in `index.css`). ### Migration - [ ] `/specs` and `/planner` redirect to the new route preserving query params (`?spec=` / `?session=`). - [ ] Existing tests for spec editor + planner composer migrate to the new route or get rewritten to target the new IA. - [ ] `docs/foreman.md` updated to describe the unified surface; `docs/breakdown.md` follow-up if needed. ## Out of scope - Server-side endpoint changes — `/foreman/specs/*`, `/foreman/chat`, `/foreman/sessions/*` stay as-is. - Adding new chat capabilities (slash commands, MCP wiring, etc.) — strictly a UX merge. - Multi-user collaboration / locking on specs — single-operator system today. - Removing the inline `?spec=` mode that already exists in the planner — it gets superseded by the new IA but the URL contract stays. ## References - Agents-page redesign as the model: PR #555 (`feat/agents-history-drawer`), commit `da25428` for the `<Drawer>` primitive. - Survey: `apps/web/src/routes/specs.index.tsx` (230 LOC), `apps/web/src/routes/specs.$specName.tsx` (~5.8K LOC), `apps/web/src/routes/planner.index.tsx` (636 LOC), feature folders `components/spec-editor/` (5 files, ~2.2K LOC) and `components/planner/` (5 files, ~2.2K LOC). - Server endpoints: `POST /foreman/chat`, `GET /foreman/sessions/:id`, `GET /foreman/stream/:task_id`, `POST /foreman/specs/:name`, `POST /foreman/breakdown-preview`, `POST /foreman/create-issues`. - Existing milestone context: M22 — UI consolidation (already merged Monitor work).
Author
Collaborator

Dispatch coordination

Blocked by #558's foundation commit. Wait until #558's first commit (Tabs primitive at apps/web/src/components/tabs.tsx + apps/web/CLAUDE.md conventions update) lands on main. Then rebase off main and proceed.

Routing: boss (new IA — merging document-first + session-first mental models, drawer-pattern carry-over, autosave wiring).

Why the wait: the unification needs a tabs/section-switcher UI and the <Tabs> primitive is being designed in #558. If both tickets land tabs independently, two implementations diverge and one has to be rewritten. Pulling from #558 keeps the design system coherent.

Soft-parallel with #556 (stats removal — different feature folders entirely).

Conflict points to watch:

  • apps/web/src/components/app-shell.tsx — collapse the Specs + Planner nav entries into one Workspace entry. #556 will also drop /stats from the same file; rebase to pick up that change.
  • apps/web/CLAUDE.md#558 lands its conventions section first; append your specs/planner notes after rebase.
  • apps/web/src/components/tabs.tsx — IMPORTED, not authored. If you reach for it before #558 commits, fall back to base-ui-components/tabs or hand-roll inline (with a TODO to swap in once #558 lands).

Implementation note: the existing ?spec=<name> URL parameter in planner.index.tsx is the precedent for cross-mode deep-links. Preserve that contract, then add ?session=<id> per the AC.

## Dispatch coordination **Blocked by #558's foundation commit.** Wait until #558's first commit (Tabs primitive at `apps/web/src/components/tabs.tsx` + `apps/web/CLAUDE.md` conventions update) lands on `main`. Then rebase off main and proceed. **Routing:** boss (new IA — merging document-first + session-first mental models, drawer-pattern carry-over, autosave wiring). **Why the wait:** the unification needs a tabs/section-switcher UI and the `<Tabs>` primitive is being designed in #558. If both tickets land tabs independently, two implementations diverge and one has to be rewritten. Pulling from #558 keeps the design system coherent. **Soft-parallel with #556** (stats removal — different feature folders entirely). **Conflict points to watch:** - `apps/web/src/components/app-shell.tsx` — collapse the `Specs` + `Planner` nav entries into one `Workspace` entry. #556 will also drop `/stats` from the same file; rebase to pick up that change. - `apps/web/CLAUDE.md` — #558 lands its conventions section first; append your specs/planner notes after rebase. - `apps/web/src/components/tabs.tsx` — IMPORTED, not authored. If you reach for it before #558 commits, fall back to base-ui-components/tabs or hand-roll inline (with a TODO to swap in once #558 lands). **Implementation note:** the existing `?spec=<name>` URL parameter in `planner.index.tsx` is the precedent for cross-mode deep-links. Preserve that contract, then add `?session=<id>` per the AC.
Collaborator

🤖 Auto-assigned to boss (heuristic: area:dashboard + body 5840 bytes (> 2 KB) — boss (heavy)). Reply /unassign to reroute.

🤖 Auto-assigned to **boss** (heuristic: area:dashboard + body 5840 bytes (> 2 KB) — boss (heavy)). Reply `/unassign` to reroute.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
charles/claude-hooks#557
No description provided.