Input: Design documents from /specs/069-observability-usage-graphs/
Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/usage-endpoint.md
Tests: Included and required (Constitution V TDD + FR-011). Write the failing test first for every backend sub-task.
- [P]: Can run in parallel (different files, no dependency)
- Lanes: (BE) Backend/Go — Backend-engineer lane (
internal/,cmd/,oas/); (FE) Frontend/Vue — delegated to the Frontend engineer (frontend/src/).
Web app: Go backend under internal/; embedded Vue frontend under frontend/src/.
Purpose: Give the token-sink graphs a real byte source (research.md R1). Blocks all aggregate work.
- T001 [BE] Failing test in
internal/storage/activity_test.go:ActivityRecordround-trips newRequestBytes/ResponseBytes; legacy records (no fields) decode to0. - T002 [BE] Add
RequestBytes int(request_bytes) +ResponseBytes int(response_bytes) toActivityRecordininternal/storage/activity_models.go. - T003 [BE] Failing test in
internal/runtime/activity_service_test.go: byte sizes captured pre-truncation (truncatedResponsestill reports fullResponseBytes; request args measured). - T004 [BE] Populate
RequestBytes/ResponseBytesinActivityService.handleEventbefore truncation; make T001/T003 green.
Purpose: Actor-owned incremental aggregate + snapshot + persistence + cold-start + TTL (CN-002/CN-003/FR-005).
- T005 [BE] Failing unit tests in
internal/runtime/usage_aggregate_test.go: incremental update math (calls, errors, blocked, byte sums excluding 0-byte, latency-bucket → approx p50/p95, time buckets per window). - T006 [BE] Implement
UsageAggregate/ToolUsage/TimeBucket+ incrementalApply(record)ininternal/runtime/usage_aggregate.go(data-model.md §2); copy-on-write snapshot via atomic pointer. - T007 [BE] Wire aggregate into
ActivityService: own it on the goroutine,ApplyinsidehandleEvent; exposeUsageSnapshot()returning the immutable snapshot. Test: snapshot reflects writes, reads never block. - T008 [BE] Failing test for persistence/rebuild in
internal/storage/activity_stats_test.go: persist snapshot toactivity_statsbucket (versioned key) + load; cold start with no snapshot triggers exactly one full-scan rebuild. - T009 [BE] Implement
internal/storage/activity_stats.gopersist/load; periodic flush (default 30s) + flush-on-shutdown; cold-start load-or-rebuild (reuseAggregateToolUsagescan). Make T008 green. - T010 [BE] [P] Add
observability.usage_cache_ttl(5s) +usage_persist_interval(30s) tointernal/config/config.gowith defaults + hot-reload; test defaults.
Purpose: GET /api/v1/activity/usage reading the snapshot/TTL cache (contracts/usage-endpoint.md).
- T011 [BE] Failing API test in
internal/httpapi/activity_usage_test.go: ranking bysort,error_ratemath, avg excludes 0-byte calls,windowfilter (24h/7d/all), tool/server/status filters (FR-008), top-N +otherfold, empty-state 200 (FR-009), 400 on bad enum. - T012 [BE] Add
UsageAggregateResponse+ sub-structs tointernal/contracts/types.go. - T013 [BE] Implement
handleActivityUsageininternal/httpapi/activity.go; short-TTL read cache (usage_cache_ttl) for wide windows; echotokens_savedfromServerTokenMetrics. RegisterGET /api/v1/activity/usageininternal/httpapi/server.go. (Deviation: a dedicatedparseUsageParamsvalidates the usage-specificwindow/sort/topenums and returns 400 on bad input —parseActivityFilterssilently ignores bad input; the sharedserver/tool/statusparam names are kept consistent.) - T014 [BE] [P] Document endpoint in
oas/swagger.yaml(swag-generated from handler annotations);./scripts/verify-oas-coverage.shpasses in CI (note: local macOS BSD-sed lacks the\UGNU extension the script relies on, so the coverage % is wrong locally; the route + schemas are present and the documented count increments). - T015 [BE] Perf assertion (SC-005):
TestActivityUsage_NoFullScanPerRequestproveshandleActivityUsagenever calls the full-scan path (AggregateToolUsage) per request.
Purpose: Dashboard switcher + four chart.js visualizations + tokens-saved headline. Depends on T013 (endpoint/contract). Owned by the Frontend/Vue engineer via a child issue.
- T016 [FE] [US4] Add Overview↔Usage switcher to
frontend/src/views/Dashboard.vue, preserving Overview state on switch-back (SC-006); window selector (24h/7d/all);data-testattrs. - T017 [FE] Add
getActivityUsage()tofrontend/src/services/api.ts. - T018 [FE] [US1]
frontend/src/components/usage/CallHistogram.vue+ResponseSizeRanking.vue(token-sink, labeled size-based per FR-006) using vue-chartjs. - T019 [FE] [US2]
frontend/src/components/usage/ErrorRateChart.vue+ per-tool latency (p50/p95). - T020 [FE] [US3]
frontend/src/components/usage/Timeline.vuehonoring active filters (FR-008). - T021 [FE] [US5] Tokens-saved headline in
Usage.vuefrom existingServerTokenMetrics(FR-007); empty/low-data states (FR-009). - T022 [FE] Compose
frontend/src/views/Usage.vue; async-load graphs so Dashboard first paint is not blocked (SC-004);make build. - T023 [FE] Playwright sweep (CLAUDE.md Web-UI workflow) → switcher, window, four charts, empty-state; HTML report in
specs/069-observability-usage-graphs/verification/(kept local, not committed).
- T024 [BE][P]
./scripts/test-api-e2e.shgreen end-to-end with the new endpoint (65/65 passed). - T025 [BE][P] Run full
internal/runtimesuite-race(approval-hash canary safety) — all packages green. - T026 [P] Spec trace + swagger committed; conventional commits, no Claude attribution.
- A1 (T001–T004) → A2 (T005–T010) → A3 (T011–T015) — strict backend order.
- A3 (T013) → B1/B2 (T016–T023) — frontend needs the endpoint/contract.
- FR-010 (per-call token estimation) is an explicit phase-2 follow-on, not in this task set.
T001–T015 + T024–T026 are Backend-engineer tasks (my lane). T016–T023 are Frontend/Vue tasks delegated via a child issue blocked by A3. This split is the Gate-1 decomposition; child issues are created only after board acceptance.