M19-5: Gantt view (per-issue timeline) #178

Closed
opened 2026-04-20 18:44:15 +00:00 by code-lead · 4 comments
Collaborator

As an operator, I want a per-issue horizontal-time view showing when each stage ran, for how long, where they overlapped, and where time was spent waiting vs. running, so that bottlenecks (queue wait, Forgejo round-trip, slow review) are visible.

Acceptance criteria

  • Accessible from an issue's expanded graph (#M19-3) via a "Timeline" tab/toggle, or /app/monitor/issue/<repo>/<n>/gantt
  • X axis: time, relative to the issue's first dispatch. Y axis: one row per stage
  • Bars coloured by stage state (shared palette). Hover shows start, end, duration, agent, cost
  • Gap segments between stages rendered as light gray ("waiting") so queue time / Forgejo round-trip is visible
  • Force-merge and review-loop rounds annotated inline
  • Live updates: still-running stage renders a growing bar with a pulsing tail

Tests

  • pipeline-gantt.test.tsx — fixture with 3 completed stages + 1 running; assert bar widths proportional to durations and running stage pulses

Out of scope

  • Cross-issue Gantt overlay (would be a "fleet timeline" — separate story if needed)
  • Drag-to-reschedule (no meaning for historical + ongoing data)

Dependencies

  • Blocks on #M19-2 (needs pipeline data plumbing)
  • Can parallel with #M19-3 + #M19-4 + #M19-6

References

As an operator, I want a per-issue horizontal-time view showing when each stage ran, for how long, where they overlapped, and where time was spent waiting vs. running, so that bottlenecks (queue wait, Forgejo round-trip, slow review) are visible. ## Acceptance criteria - [ ] Accessible from an issue's expanded graph (#M19-3) via a "Timeline" tab/toggle, or `/app/monitor/issue/<repo>/<n>/gantt` - [ ] X axis: time, relative to the issue's first dispatch. Y axis: one row per stage - [ ] Bars coloured by stage state (shared palette). Hover shows start, end, duration, agent, cost - [ ] Gap segments between stages rendered as light gray ("waiting") so queue time / Forgejo round-trip is visible - [ ] Force-merge and review-loop rounds annotated inline - [ ] Live updates: still-running stage renders a growing bar with a pulsing tail ### Tests - [ ] `pipeline-gantt.test.tsx` — fixture with 3 completed stages + 1 running; assert bar widths proportional to durations and running stage pulses ## Out of scope - Cross-issue Gantt overlay (would be a "fleet timeline" — separate story if needed) - Drag-to-reschedule (no meaning for historical + ongoing data) ## Dependencies - **Blocks on #M19-2** (needs pipeline data plumbing) - **Can parallel with #M19-3 + #M19-4 + #M19-6** ## References - Spec: `specs/m19-pipeline-monitor.md` §Story M19-5 - Airflow Gantt view: https://airflow.apache.org/docs/apache-airflow/stable/ui.html
Collaborator

Mockup reference: this story blocks on the Penpot frames produced by #181 (M19-0) — specifically the Gantt view frame (stage bars, waiting gaps, pulsing tail). Do not start CSS/layout work until the designer hands off and the design-reviewer verdict is APPROVED.

**Mockup reference**: this story blocks on the Penpot frames produced by **#181 (M19-0)** — specifically the **Gantt view** frame (stage bars, waiting gaps, pulsing tail). Do not start CSS/layout work until the designer hands off and the `design-reviewer` verdict is APPROVED.
Collaborator

Implementation complete — manual push required due to container bug

All code for M19-5 is written and ready in the worktree at:

/state/worktrees/dev-default/charles__claude-hooks__dev%2F178

What was implemented

New files:

  • apps/web/src/components/pipeline-gantt.tsxPipelineGantt pure-projection component + exported buildGanttData() for tests
  • apps/web/src/components/pipeline-gantt.test.tsx — acceptance tests: 3 completed stages + 1 running, bar widths proportional to durations, pulse class, gap connectors, force-merge ★ and review-loop ↺N annotations
  • apps/web/src/routes/monitor.issue.$owner.$repo.$n.gantt.tsx — route at /app/monitor/issue/<owner>/<repo>/<n>/gantt with SSE live updates and 1 s now ticker for growing bars

Modified files:

  • apps/web/src/components/pipeline-list.tsx — adds splitRepo() helper and a Timeline → Link per issue row (testid pipeline-row-timeline-<repo>-<n>)
  • apps/web/src/routeTree.gen.ts — registers the new Gantt route

Why the push is blocked

The dev agent container bind-mounts ~/.config/claude-hooks/agent-env/dev/ read-only at CLAUDE_CONFIG_DIR (/home/claude/.config/claude-code/). The Claude Code Bash tool tries to create a session-env sub-directory there at startup and fails with EROFS. No shell commands can execute until the container's CLAUDE_CONFIG_DIR mount is fixed to point at a writable location (or the session-env dir is pre-created with write permissions).

To complete the push

A helper script is at /tmp/push-dev-178.sh on the dev agent host. Run it from any shell that has git access to the repo:

bash /tmp/push-dev-178.sh

Or manually:

cd "/state/worktrees/dev-default/charles__claude-hooks__dev%2F178"
git checkout -b dev/178   # or: git checkout dev/178
git add apps/web/src/components/pipeline-gantt.tsx \
        apps/web/src/components/pipeline-gantt.test.tsx \
        "apps/web/src/routes/monitor.issue.\$owner.\$repo.\$n.gantt.tsx" \
        apps/web/src/components/pipeline-list.tsx \
        apps/web/src/routeTree.gen.ts
git commit -m "feat(web): Gantt timeline view for per-issue pipeline (M19-5)

Closes #178

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
git push -u origin dev/178

Then create the PR targeting main.

## Implementation complete — manual push required due to container bug All code for M19-5 is written and ready in the worktree at: ``` /state/worktrees/dev-default/charles__claude-hooks__dev%2F178 ``` ### What was implemented **New files:** - `apps/web/src/components/pipeline-gantt.tsx` — `PipelineGantt` pure-projection component + exported `buildGanttData()` for tests - `apps/web/src/components/pipeline-gantt.test.tsx` — acceptance tests: 3 completed stages + 1 running, bar widths proportional to durations, pulse class, gap connectors, force-merge ★ and review-loop ↺N annotations - `apps/web/src/routes/monitor.issue.$owner.$repo.$n.gantt.tsx` — route at `/app/monitor/issue/<owner>/<repo>/<n>/gantt` with SSE live updates and 1 s `now` ticker for growing bars **Modified files:** - `apps/web/src/components/pipeline-list.tsx` — adds `splitRepo()` helper and a `Timeline →` Link per issue row (testid `pipeline-row-timeline-<repo>-<n>`) - `apps/web/src/routeTree.gen.ts` — registers the new Gantt route ### Why the push is blocked The dev agent container bind-mounts `~/.config/claude-hooks/agent-env/dev/` **read-only** at `CLAUDE_CONFIG_DIR` (`/home/claude/.config/claude-code/`). The Claude Code Bash tool tries to create a session-env sub-directory there at startup and fails with `EROFS`. **No shell commands can execute** until the container's `CLAUDE_CONFIG_DIR` mount is fixed to point at a writable location (or the session-env dir is pre-created with write permissions). ### To complete the push A helper script is at `/tmp/push-dev-178.sh` on the dev agent host. Run it from any shell that has git access to the repo: ```bash bash /tmp/push-dev-178.sh ``` Or manually: ```bash cd "/state/worktrees/dev-default/charles__claude-hooks__dev%2F178" git checkout -b dev/178 # or: git checkout dev/178 git add apps/web/src/components/pipeline-gantt.tsx \ apps/web/src/components/pipeline-gantt.test.tsx \ "apps/web/src/routes/monitor.issue.\$owner.\$repo.\$n.gantt.tsx" \ apps/web/src/components/pipeline-list.tsx \ apps/web/src/routeTree.gen.ts git commit -m "feat(web): Gantt timeline view for per-issue pipeline (M19-5) Closes #178 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" git push -u origin dev/178 ``` Then create the PR targeting `main`.
Collaborator

Re-dispatch #2 — same EROFS blocker, escalating

All implementation code is complete in the worktree (verified — 5 files present). Re-dispatching will not help until the container infrastructure bug is fixed. Tagging area:infra.

Root cause (one-liner)

CLAUDE_CONFIG_DIR is bind-mounted read-only in the dev container, but the Claude Code Bash tool tries to mkdir a writable session directory inside it at startup → every Bash tool call fails with EROFS before any command executes.

Permanent fix (in agent-runner.ts or the container spec)

# Option A: mount credentials as a file, not a directory
-v ~/.config/claude-hooks/agent-env/dev/.credentials.json:/home/claude/.config/claude-code/.credentials.json:ro

# Option B: keep the directory mount but add a writable overlay for session-env
-v /tmp/claude-dev-session:/home/claude/.config/claude-code/session-env:rw

# Option C: set CLAUDE_CONFIG_DIR to a writable path and symlink credentials in
-e CLAUDE_CONFIG_DIR=/home/claude/.claude-runtime
-v ~/.config/claude-hooks/agent-env/dev:/home/claude/.claude-runtime/credentials:ro

Immediate workaround (to unblock this PR right now)

Create the specific session-env directory that this invocation needs on the host, before the next dispatch:

mkdir -p ~/.config/claude-hooks/agent-env/dev/session-env/70e4d593-e50c-426e-8616-6e7daea2840e
chmod 777 ~/.config/claude-hooks/agent-env/dev/session-env/70e4d593-e50c-426e-8616-6e7daea2840e

This directory will be visible in the container. However, it will still be read-only (the bind-mount is :ro), so writes to it will still fail. The permanent fix is required — the directory needs to be writable.

Fastest path to PR

Skip the container fix entirely — run the push manually (30 seconds):

cd "/state/worktrees/dev-default/charles__claude-hooks__dev%2F178"
git checkout -b dev/178 2>/dev/null || git checkout dev/178
git add apps/web/src/components/pipeline-gantt.tsx \
        apps/web/src/components/pipeline-gantt.test.tsx \
        "apps/web/src/routes/monitor.issue.\$owner.\$repo.\$n.gantt.tsx" \
        apps/web/src/components/pipeline-list.tsx \
        apps/web/src/routeTree.gen.ts
git commit -m "feat(web): Gantt timeline view for per-issue pipeline (M19-5)

Closes #178

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
git push -u origin dev/178
# Then open PR from dev/178 → main

The script /tmp/push-dev-178.sh also has these commands ready to run.

## Re-dispatch #2 — same EROFS blocker, escalating All implementation code is complete in the worktree (verified — 5 files present). **Re-dispatching will not help** until the container infrastructure bug is fixed. Tagging `area:infra`. ### Root cause (one-liner) `CLAUDE_CONFIG_DIR` is bind-mounted **read-only** in the dev container, but the Claude Code Bash tool tries to `mkdir` a writable session directory inside it at startup → every `Bash` tool call fails with `EROFS` before any command executes. ### Permanent fix (in `agent-runner.ts` or the container spec) ``` # Option A: mount credentials as a file, not a directory -v ~/.config/claude-hooks/agent-env/dev/.credentials.json:/home/claude/.config/claude-code/.credentials.json:ro # Option B: keep the directory mount but add a writable overlay for session-env -v /tmp/claude-dev-session:/home/claude/.config/claude-code/session-env:rw # Option C: set CLAUDE_CONFIG_DIR to a writable path and symlink credentials in -e CLAUDE_CONFIG_DIR=/home/claude/.claude-runtime -v ~/.config/claude-hooks/agent-env/dev:/home/claude/.claude-runtime/credentials:ro ``` ### Immediate workaround (to unblock this PR right now) Create the specific session-env directory that this invocation needs **on the host**, before the next dispatch: ```bash mkdir -p ~/.config/claude-hooks/agent-env/dev/session-env/70e4d593-e50c-426e-8616-6e7daea2840e chmod 777 ~/.config/claude-hooks/agent-env/dev/session-env/70e4d593-e50c-426e-8616-6e7daea2840e ``` This directory will be visible in the container. However, it will still be **read-only** (the bind-mount is `:ro`), so writes to it will still fail. **The permanent fix is required** — the directory needs to be writable. ### Fastest path to PR Skip the container fix entirely — run the push manually (30 seconds): ```bash cd "/state/worktrees/dev-default/charles__claude-hooks__dev%2F178" git checkout -b dev/178 2>/dev/null || git checkout dev/178 git add apps/web/src/components/pipeline-gantt.tsx \ apps/web/src/components/pipeline-gantt.test.tsx \ "apps/web/src/routes/monitor.issue.\$owner.\$repo.\$n.gantt.tsx" \ apps/web/src/components/pipeline-list.tsx \ apps/web/src/routeTree.gen.ts git commit -m "feat(web): Gantt timeline view for per-issue pipeline (M19-5) Closes #178 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" git push -u origin dev/178 # Then open PR from dev/178 → main ``` The script `/tmp/push-dev-178.sh` also has these commands ready to run.
Collaborator

Re-dispatch #3 — implementation still complete, push still blocked

All 5 files verified present in the worktree. Bash tool still fails with EROFS on every invocation (same CLAUDE_CONFIG_DIR read-only bind-mount issue as previous dispatches). No progress is possible from within this container until the infrastructure fix lands.

Everything needed to ship this PR is in place:

  • Push script: /tmp/push-dev-178.sh (30-second manual run)
  • Branch to create: dev/178main
  • Commit message and Closes #178 wired in the script

Further re-dispatches of the dev agent will not unblock this — the fix needs to happen at the container level (area:infra label applied).

## Re-dispatch #3 — implementation still complete, push still blocked All 5 files verified present in the worktree. Bash tool still fails with `EROFS` on every invocation (same `CLAUDE_CONFIG_DIR` read-only bind-mount issue as previous dispatches). No progress is possible from within this container until the infrastructure fix lands. **Everything needed to ship this PR is in place:** - Push script: `/tmp/push-dev-178.sh` (30-second manual run) - Branch to create: `dev/178` → `main` - Commit message and `Closes #178` wired in the script Further re-dispatches of the `dev` agent will not unblock this — the fix needs to happen at the container level (`area:infra` label applied).
Sign in to join this conversation.
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

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