feat(web): merge Specs route into Planner page (UC-1) #266
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!266
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "dev/262"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
/app/plannergains a collapsible Specs section alongside the Sessions list; clicking a spec row sets?spec=<name>in the URL and mountsSpecEditorin the middle column/app/specsis replaced with abeforeLoadredirect to/planner/?spec=<name>(preserves legacy?name=param for existing bookmarks)SpecEditorwhen?spec=is set, with Back to session, Draft with foreman, and Breakdown action buttons; clears back to the chat view otherwiseSpecsentry from the desktop nav inapp-shell.tsxpostBreakdown()helper tolib/foreman.tsfor the/breakdownendpointplanner-storewithspecsCollapsedstate (independent collapse per section)validateSearch, plus a Playwright e2e spec covering the redirect and spec editor mountTest plan
bun run typecheck— no new errorsbun run lint(Biome) — passessrc/routes/planner.test.tsx— redirect preservesname→spec,validateSearchacceptsspecparame2e/specs-redirect.spec.ts—/app/specs?name=foo→/app/planner?spec=foo, editor mounts; sidebar shows Specs section at/app/plannerCloses #262
🤖 Generated with Claude Code
TanStack Router's FileRoutesByTo maps the planner index route to '/planner' (the parent path), not '/planner/'. Using the trailing-slash form caused two TS2820/TS2353 errors: - '/planner/' not assignable to the to union type - 'spec' not known on the search type for '/planner/' Also consolidate the conditional search object — since spec accepts string|undefined, always pass { spec: search.name } rather than branching on an empty object literal that TypeScript can't type-check. Update the two affected test assertions to match. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>032d63430bb037066414b037066414ae2a0d0097TanStack Router only exposes index routes via the parent path in the `to` union — `"/planner/"` is not a valid redirect target; `"/planner"` is. Changing the target exposed a secondary TS2353: the `/planner` layout route had no `validateSearch` so `spec` was not a known search key. Fix: - `specs.tsx`: use `to: "/planner"` (drop trailing slash) and simplify search to always pass `{ spec: ... }` (undefined when name absent). - `planner.tsx` layout: add `validateSearch` declaring `spec` so the redirect is type-safe. This mirrors the index child's schema. - `planner.board.tsx`: spread `prev` in the filter-change navigate so parent-declared search params (spec) are preserved across filter updates — required now that the layout declares `spec`. - `planner.test.tsx`: fix assertions to expect `"/planner"` (correct) instead of `"/planner/"` (invalid). - `planner.index.tsx` + `specs-redirect.spec.ts`: Biome format/lint auto-fixes (template literal → string literal, whitespace). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>Review — UC-1 Specs → Planner merge
CI green ✅ (run #1948). The routing plumbing, redirect shim, store addition,
postBreakdownhelper, and e2e suite are all solid. Three issues need fixing before merge.1. AC gap — spec rows missing last-modified timestamp
File:
apps/web/src/routes/planner.index.tsx,specFiles.map(...)blockIssue #262 AC says:
The rendered row only shows
{sn}(the name). No timestamp is displayed.If
ForemanFileEntrydoesn't yet carry amodified_atfield, that's fine — add it to the shared type and thread it through. If the old/app/specsshowed it, this is a regression; if not, it's still an explicit AC. Either way it needs to be there.2. AC gap — spec rows missing
type:user-storybadgeFile:
apps/web/src/routes/planner.index.tsx,specFiles.map(...)blockSame AC item: each row should show a small
type:user-storybadge "when a tracking issue is linked in the file's front-matter or detected heuristically from the body."Currently there is no badge, no heuristic, and the spec content isn't loaded at list time anyway. Options:
ForemanFileEntry(e.g.has_tracking_issue: boolean) populated by scanning the file at list time.Either approach is fine, but the badge must exist.
3. Bug — silent "Loading…" when
?spec=references a nonexistent fileFile:
apps/web/src/routes/planner.index.tsx, spec editor panel blockactiveSpecFilequery'serror/isErroris not destructured. If the fetch returns 404 (spec name typo in URL, deleted file, stale bookmark) or any network error, TanStack Query will exhaust its retries and leaveactiveSpecFileasundefined— so the panel shows "Loading…" permanently with no way out other than manually editing the URL.Fix: destructure
isError/errorfrom the query and render an error state with a "Back to session" affordance:4. Stale comment (minor, fix while you're in the file)
File:
apps/web/src/components/app-shell.tsx,BOTTOM_TAB_ITEMSJSDocThe comment still reads:
"Specs" should be removed since the entry was dropped.
Not flagged
validateSearchduplication betweenplanner.tsxandplanner.index.tsxis intentional and documented — fine.priorSessionId === nullwhen arriving via a direct?spec=URL is expected and "Back to session" correctly falls back to the empty chat state.c07b5d7d1b3ffc553db8CI green ✅ (run #1953). All four findings from round 1 are resolved:
modified_atthreaded fromstat().mtimethrough the shared type to the spec row.type:user-storybadge —has_tracking_issuepopulated server-side by body regex; badge renders correctly.isError: specErrordestructured; error panel with "Could not load spec" + Back button replaces the permanent "Loading…".BOTTOM_TAB_ITEMScomment.