feat(web): Pipeline list view at /app/monitor (M19-2) #191
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!191
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "boss/175"
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
Turn
/app/monitorinto an issue-centric Pipeline list driven byGET /issues/pipeline(from #M19-1, shipped in #183). Each row shows one open issue with a horizontal mini-pipeline of stage pills so operators can answer "where is issue N?" at a glance. The legacy M18-3 task-event view is preserved one click away under the Tasks tab.Closes #175.
Routing
/app/monitor/app/monitor/tasks/app/monitor/task/$taskIdThe AppShell nav grows Pipeline and Tasks tabs (Pipeline is
exact-match so/monitor/tasksdoesn't also light it up).Row-click pattern choice (per acceptance criteria)
Clicking an issue row's title sets up the href for
/app/monitor/issue/<repo>/<n>(the expanded per-issue view landing in #M19-3) butpreventDefaults until that route exists — so the URL shape is stable/bookmarkable today without 404-ing. Stage pills are the primary interaction and deep-link to/app/monitor/task/<task_id>verbatim from M18-3.Stage palette
Colours resolve through design-token CSS variables (
--stage-*intokens.css) — no raw hex. Round counter (↺N, purple) and force-merge (★, gold) glyphs survive compact mode so they stay visible at a glance.--stage-success--stage-running(pulses via@keyframes stage-pulse)--stage-pending--stage-failure--stage-stalled--stage-round(purple, reviewer loop)--stage-force-merge(gold)The running animation is a shared
@keyframes stage-pulsekeyframe — the Grid (#M19-4) and Gantt (#M19-5) views can reuse it without duplicating CSS.Live updates
pipeline_stageSSE events (already emitted frommain.tsandpipeline.tssince M19-1) patch the cached response in-place viapatchPipelineStage. Unmatched issues leave the cache unchanged — a freshly dispatched issue picks up on the next poll./issues/pipelineis the backstop. TanStack Query's defaultrefetchIntervalInBackground: false(spelled out in the query for audit clarity) satisfies the Page-Visibility acceptance criterion.resultevents also trigger a targetedinvalidateQueries(["pipeline"])solink/duration_msfields refresh without waiting 60 s.Filters
Filters (repo / milestone / assignee / label /
open|closed|all) sync to URL query params via TanStack Router'svalidateSearch+useSearch. Views are shareable —/app/monitor?repo=charles/claude-hooks&state=closed&label=area:dashboardround-trips cleanly.repoandstateforward to the server;milestone/assignee/labelfilter client-side against the 5-s-cached response so we don't fan out a separate fetch per combination.Tests
<StagePill />unit tests + 19<PipelineList />+ cache-patcher tests): renders 5 fixture issues spanning every stage state, asserts pilldata-role/data-state/ glyph + hrefs, exercises filter logic +patchPipelineStage's structural mutation (non-matching rows preserved by identity).e2e/pipeline.spec.ts:/app/monitor→ click stage pill → land on/app/monitor/task/$id→ browser Back returns to the pipeline list.e2e/monitor.spec.tsretargeted to/app/monitor/tasks(the legacy view's new home).Linkmocked once invitest.setup.tsxso component tests don't need a full<RouterProvider>in context (avoids theact(…)warning flood from async route transitions under happy-dom).Test plan
bun x turbo run typecheck— green across server + shared + webbun x biome check .— 129 files, no issuesbun x turbo run test— 761 server tests + 41 web tests passbun run build(apps/web) — bundle builds clean; routeTree regenerated/app/monitor, filters tostate=open, clicks a stage pill, lands on the task view, Back returnspipeline_stageevent updates a single row pill without a full table re-render/app/monitor?repo=x&label=area:dashboardreproduces the filtered view on reload🤖 Generated with Claude Code
Review — M19-2 Pipeline list view at /app/monitor
CI: ✅ green (run #1795, 3m17s)
Acceptance criteria — all met
/app/monitor→ pipeline list (default)/app/monitor/tasks→ legacy tasks view preserved/app/monitor/task/:idstandalone deep-linkpipeline_stagein-place cache patchingrefetchIntervalInBackground: false(Page Visibility)@keyframes stage-pulseshared keyframeNotable correctness details
patchPipelineStageidentity contract (monitor.index.tsx): non-matching rows are returned by reference, only the affectedIssuePipelineand its affectedStageEntryare rebuilt as fresh objects. This correctly triggers TanStack Query's shallow-equality check so only the pill for the changed issue re-renders — not the whole table.current_stageis only updated onrunningtransitions, not cleared onsuccess/failure. Intentional — the 60 s poll corrects it, and the comment in the code says so. No issue.Assignee filter reverse-lookup in
FilterBar: display names come fromroleBaseName(a), selection reverses viaassignees.find((a) => roleBaseName(a) === v)to recover the full login before writing to URL. Correct — URL stores the full login,applyFilterscompares againsti.assignee(the full login).vitest.setup.tsxglobalLinkmock: trades per-test router isolation for freedom fromact(…)warning floods. Components needing real router semantics useTestRouterfromlib/test-router.tsx. Acceptable trade-off, documented.Label facet only surfaces
area:/type:prefixed labels: deliberate narrowing to routing-relevant labels; keeps the dropdown from ballooning withpriority:*etc. The intent is clear even without a prose comment.__testablesexport pattern: component stays the primary export, internal helpers (applyFilters,worstStageState,uniqueSorted,STATE_RANK) are reachable for unit tests without structural refactoring. Clean.No bugs, no safety issues, no missing acceptance criteria. LGTM.