feat(web): /specs route — list + markdown editor (no AI drawer) #1118

Open
opened 2026-05-11 23:39:02 +00:00 by claude-desktop · 0 comments
Collaborator

As an operator,
I want /specs to list spec files and /specs/$specName to open a focused markdown editor,
so that spec authoring lives on its own page instead of sharing /workspace.

Replaces the redirect-only specs.tsx / specs.index.tsx / specs.$specName.tsx shells. AI drawer + breakdown toolbar land in follow-up stories.

Acceptance criteria

List (/specs)

  • routes/specs.index.tsx renders a list of specs/*.md entries fetched via fetchArchitectFiles(repo) filtered by root === "specs".
  • Empty state with "New spec" button creates specs/<kebab>.md then navigates to /specs/<name>.
  • Sticky ?repo=<o/n> selector mirrors the workspace pattern.
  • Each row links to /specs/$specName.

Editor (/specs/$specName)

  • routes/specs.$specName.tsx mounts SpecEditor with body loaded via fetchArchitectFileContent.
  • Save action calls saveSpec and surfaces role="status" aria-live="polite" toast.
  • Unsaved-changes guard: navigating away with dirty body prompts Base UI Dialog confirm.
  • Cmd+S / Ctrl+S saves (added to a new useSpecsKeymap hook).

Layout wrapper

  • routes/specs.tsx replaces the redirect stub with an <Outlet /> shell; validateSearch accepts repo.
  • AppShell chrome rendered on both list and editor.

Tests

  • specs.test.tsx covers: list renders rows, click navigates to editor; editor loads body, save fires PUT /architect/files; unsaved-changes guard fires on dirty navigate.
  • No regression in workspace.test.tsx (still passes pre-split).

Out of scope

  • AI drawer integration (story wsplit-3).
  • Breakdown toolbar + "Open in chat" (story wsplit-4).
  • Removing spec branches from /workspace (story wsplit-5).

Dependencies

  • Blocked by wsplit-1 (nav must list /specs first).

References

  • Spec: docs/specs/workspace-split.md § Surfaces > /specs.
**As an** operator, **I want** `/specs` to list spec files and `/specs/$specName` to open a focused markdown editor, **so that** spec authoring lives on its own page instead of sharing /workspace. Replaces the redirect-only `specs.tsx` / `specs.index.tsx` / `specs.$specName.tsx` shells. AI drawer + breakdown toolbar land in follow-up stories. ## Acceptance criteria ### List (`/specs`) - [ ] `routes/specs.index.tsx` renders a list of `specs/*.md` entries fetched via `fetchArchitectFiles(repo)` filtered by `root === "specs"`. - [ ] Empty state with "New spec" button creates `specs/<kebab>.md` then navigates to `/specs/<name>`. - [ ] Sticky `?repo=<o/n>` selector mirrors the workspace pattern. - [ ] Each row links to `/specs/$specName`. ### Editor (`/specs/$specName`) - [ ] `routes/specs.$specName.tsx` mounts `SpecEditor` with body loaded via `fetchArchitectFileContent`. - [ ] Save action calls `saveSpec` and surfaces `role="status" aria-live="polite"` toast. - [ ] Unsaved-changes guard: navigating away with dirty body prompts `Base UI Dialog` confirm. - [ ] `Cmd+S` / `Ctrl+S` saves (added to a new `useSpecsKeymap` hook). ### Layout wrapper - [ ] `routes/specs.tsx` replaces the redirect stub with an `<Outlet />` shell; `validateSearch` accepts `repo`. - [ ] `AppShell` chrome rendered on both list and editor. ### Tests - [ ] `specs.test.tsx` covers: list renders rows, click navigates to editor; editor loads body, save fires `PUT /architect/files`; unsaved-changes guard fires on dirty navigate. - [ ] No regression in `workspace.test.tsx` (still passes pre-split). ## Out of scope - AI drawer integration (story wsplit-3). - Breakdown toolbar + "Open in chat" (story wsplit-4). - Removing spec branches from `/workspace` (story wsplit-5). ## Dependencies - Blocked by wsplit-1 (nav must list `/specs` first). ## References - Spec: `docs/specs/workspace-split.md` § Surfaces > /specs.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#1118
No description provided.