Skip to content

feat(timeline): agent activity timeline panel for a task#618

Draft
bborn wants to merge 2 commits into
mainfrom
task/4475-agent-activity-timeline-panel-for-a-task
Draft

feat(timeline): agent activity timeline panel for a task#618
bborn wants to merge 2 commits into
mainfrom
task/4475-agent-activity-timeline-panel-for-a-task

Conversation

@bborn

@bborn bborn commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary

Gives a task a compact, chronological activity timeline of its lifecycle, built from the event_log and updating live via the existing SSE feed. Borrows the "agent state timeline" idea from herdr-insight.

A task now shows an accurate, ordered event timeline with real timestamps — state transitions (queued → processing → blocked → done), retries, blocks, and completion — that updates live as new events land.

Changes

internal/db

  • New GetTaskTimeline(taskID, limit) builds chronological (oldest-first) TaskTimelineEntry values from event_log, mapping each row to a short label (Created, Completed, Blocked, Retried, and queued → processing style transitions).
  • Status changes now persist their {old,new} values in the event_log metadata column (recordEventMeta / marshalEventMetadata), so transition labels are accurate.
  • RetryTask records a distinct task.retry event so retries are visible on the timeline.

internal/ui (TUI detail view)

  • New Activity Timeline section in the detail view, each entry showing a real timestamp, an icon, and a short label.
  • Reloaded on the periodic Refresh() tick and folded into the render cache key, so it updates live as events land (no manual reopen).

internal/web

  • GET /api/tasks/{id}/timeline returns the timeline as JSON with real UTC timestamps.
  • The existing per-task SSE stream now also emits timeline events as new lifecycle events occur, so the web detail page can update live.

Tests

  • internal/db: timeline builder (chronological order, transitions, retry, limit, empty) + label mapping table.
  • internal/web: timeline endpoint (shape, RFC3339 timestamps, transition label) + invalid-id.
  • internal/ui: timeline renders in detail content and updates live after a new event on Refresh().

go build ./..., go test for the three touched packages, and golangci-lint run (v2.8.0) all pass.

Follow-ups

  • The web SPA (internal/web/ui/dist, built out-of-repo behind the ui build tag) needs a small frontend change to render the new endpoint/SSE timeline events; the backend API + stream are ready for it.

🤖 Generated with Claude Code

bborn and others added 2 commits June 23, 2026 08:58
Build a chronological, live-updating activity timeline of a task's
lifecycle from the event_log: state transitions (queued -> processing ->
blocked -> done), retries, blocks, and completion with real timestamps
and short labels.

- db: GetTaskTimeline() maps event_log rows into labeled, chronological
  entries; status transitions render as "queued -> processing". Status
  changes now persist their old/new values in event_log metadata, and
  RetryTask records a distinct task.retry event.
- ui: Activity Timeline section in the detail view, refreshed on the
  periodic Refresh() tick so it updates live as new events land.
- web: GET /api/tasks/{id}/timeline endpoint plus live "timeline" SSE
  events on the existing per-task stream for the web detail page.
- tests: db timeline builder + labels, web endpoint, and live TUI refresh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
QA surfaced that the blocked/done status transitions were each shown
twice — once as a "processing -> blocked" transition and again as a bare
"Blocked"/"Completed" lifecycle row emitted at the same second. Suppress
the redundant lifecycle rows (keeping any real blocked reason) so each
block/finish appears once, and key the TUI icon off the transition's
target status (done -> check, blocked -> stop, etc).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bborn

bborn commented Jun 23, 2026

Copy link
Copy Markdown
Owner Author

✅ QA: Agent activity timeline

Drove the real ty TUI + web API against an isolated throwaway instance (scripts/qa), seeding a task through a full lifecycle: backlog → queued → processing → blocked → retry → processing → done.

TUI detail view — Activity Timeline

Renders chronologically with real timestamps, status-aware icons, and the retry feedback. The redundant bare Blocked/Completed lifecycle rows are suppressed so each block/finish appears once.

timeline

Web API — GET /api/tasks/{id}/timeline

Returns the same chronological timeline as JSON with real UTC (RFC3339) timestamps:

[
  {"id":1,"event_type":"task.created","label":"Created","detail":"Add Stripe billing webhooks","created_at":"2026-06-23T22:40:52Z"},
  {"id":2,"event_type":"task.updated","label":"backlog → queued","created_at":"2026-06-23T22:40:52Z"},
  {"id":3,"event_type":"task.updated","label":"queued → processing","created_at":"2026-06-23T22:40:54Z"},
  {"id":4,"event_type":"task.updated","label":"processing → blocked","created_at":"2026-06-23T22:40:55Z"},
  {"id":6,"event_type":"task.retry","label":"Retried","detail":"Verify signature with the new secret","created_at":"2026-06-23T22:40:56Z"},
  {"id":7,"event_type":"task.updated","label":"blocked → queued","created_at":"2026-06-23T22:40:56Z"},
  {"id":8,"event_type":"task.updated","label":"queued → processing","created_at":"2026-06-23T22:40:57Z"},
  {"id":9,"event_type":"task.updated","label":"processing → done","created_at":"2026-06-23T22:40:58Z"}
]

(Raw event_log had 10 rows; the redundant task.blocked id=5 and task.completed id=10 are correctly suppressed.)

Web SSE — live updates

With the per-task stream (GET /api/tasks/{id}/stream) open, transitioning the task pushed timeline events in real time:

event: timeline
data: {"id":13,"event_type":"task.updated","label":"queued → processing","created_at":"2026-06-23T22:43:08Z"}
event: timeline
data: {"id":14,"event_type":"task.updated","label":"processing → blocked","created_at":"2026-06-23T22:43:09Z"}

Acceptance criteria

  • Accurate, chronological event timeline with real timestamps
  • State transitions (queued→processing→blocked→done), retries, blocks, completion
  • Updates live as new events land (TUI Refresh() tick + web SSE timeline events)
  • Tests: internal/db, internal/web, internal/ui — all green; golangci-lint v2.8.0 clean

Note

  • QA surfaced duplicate blocked/done rows; fixed in 3ecec1f9 (dedupe + status-aware icons). The web SPA frontend (out-of-repo, ui build tag) still needs a small change to render the new endpoint/SSE events — backend is ready.

QA via isolated ty instance; live daemon/DB untouched.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant