# Unified observability reports implementation plan

**Date:** 2026-08-16
**Plan slug:** `observability-reports`
**Design:** `docs/specs/2026-08-16-observability-reports-design.md`

## Delivery rule

This is six deployments, not one large branch. Each slice must:

1. expose one complete owner-visible capability;
2. use authoritative data and hide future sections entirely;
3. pass focused, static, broad, and installed owner-flow verification in that order;
4. land on `origin/main` and deploy;
5. record its installed proof before the next slice starts.

No slice may add enabling infrastructure that is not exercised on `/reports` in the same deploy. No unfinished future section may appear as a disabled control, zero tile, sample chart, or “coming soon” panel.

## Shared acceptance rules

- Enter through the `Reports` sidebar item; direct-route smoke tests do not count.
- Every number has an authoritative source, unit, time window, and coverage status.
- Missing evidence yields `Partial` or `Unavailable`, never zero and never an inferred join.
- Selecting a number or record reaches supporting evidence or an owning focused page.
- Global filters survive reload and browser back/forward through URL state.
- Both themes and narrow viewport are verified on every visual slice.
- Source errors remain section-local; the last successful response is visibly stale after a refetch failure.
- New generic controls come from Astryx. Reuse deck-ui `SectionCard`, `ScoreCard`/`KpiTile`, `TimeSeriesChart`, `DataCoveragePanel`, `StatusChip`, `DetailDrawer`, `DataTable`-compatible patterns, formatters, tooltips, and safe-link helpers. Do not add a primitive without owner approval.

## Slice 1 — Activity and source trust

### Installed evidence

The installed `/reports` page shows a 24-hour activity report with real category volume, failures, source coverage, skipped records, freshness, and drill-down evidence. A fixture containing healthy, stale, malformed, and unreadable sources visibly produces `Partial` coverage and the exact named gaps.

### Contract and collector

1. Add the shared report contract under `packages/report-contract/` with:
   - query and serialized URL types;
   - activity-section envelope;
   - metric, series, source coverage, gap, record, and evidence-query types;
   - schema version `1` and explicit units/status vocabularies.
2. Add `collector/src/reports/query.ts`:
   - parse repeatable filters;
   - default to a fake-clock-driven last 24 hours;
   - validate ISO bounds, maximum seven-day live range, and IANA timezone;
   - produce owner-readable `400` reasons.
3. Add `collector/src/reports/activity-report.ts`:
   - consume the existing activity reader rather than re-reading source files;
   - aggregate bounded category buckets and failure counts;
   - project every activity source’s authority, freshness, storage, retention, range, counts, skips, and errors;
   - calculate the coverage lattice from required source status and full-window coverage;
   - emit attention records for error events, source errors, and skipped records;
   - emit bounded evidence queries that refer only to registered source IDs and record IDs.
4. Add the `GET /reports/observability` route to `collector/src/server.ts` for the Slice 1 envelope.
5. Reuse the existing `/activity/sources/:id/entries` endpoint for evidence detail; do not create a duplicate raw-read route in this slice.

### Web page

1. Add typed report fetch/query support to:
   - `apps/web/src/lib/collector-client.ts`;
   - `apps/web/src/lib/collector-queries.ts`;
   - a small URL-state helper in `apps/web/src/lib/report-query.ts`.
2. Add the thin `apps/web/src/pages/reports.astro` route and `Reports` sidebar entry immediately after `Overview`.
3. Add `apps/web/src/components/reports/ReportsApp.tsx` and `ReportsContent.tsx`:
   - range presets for 1 hour, 6 hours, and 24 hours;
   - project, source, severity, and text filters backed by URL state;
   - generated time and global coverage verdict;
   - real metric summaries for recorded events, failures, active sources, and source problems;
   - category volume chart using `TimeSeriesChart` only when trustworthy points exist;
   - needs-attention table;
   - source-health table with the full trust contract;
   - empty, partial, stale, unavailable, and refetch-failure states.
4. Add `ReportEvidenceDrawer.tsx` using `DetailDrawer`:
   - calculation/window/filters;
   - source and coverage gaps;
   - paginated supporting source records;
   - safe link to `/logs/:source` preserving supported filters.
5. Add page composition styles using existing tokens and Astryx styling conventions. Do not edit deck-ui unless an existing component defect blocks composition.

### Slice 1 tests

- Contract compile tests prove collector/web share one envelope.
- Collector query tests cover defaults under fake time, invalid ranges/timezones, repeatable filters, and seven-day bound.
- Projection tests cover complete/partial/stale/unavailable, skipped records, source error, unsupported filter, category buckets, zero values, truncation, and section-local failure.
- Route tests verify auth, query validation, content type, and partial `200` versus global `503`.
- Web client tests verify exact query serialization.
- Component tests verify every visible state, values from the envelope without recalculation, URL round-trip, keyboard drawer opening, safe links, and no future-domain labels.
- Installed Playwright owner flow enters through the sidebar, filters, opens an attention row, opens a source row, and proves all four source states from authoritative fixtures.

### Slice 1 delivery boundary

Commit only Slice 1 files, submit through the project ship queue, and stop. Do not create history tables, execution cards, reliability cards, usage cards, saved views, or exports in this branch.

## Slice 2 — Work execution and delivery

### Installed evidence

A recorded owner request with an explicitly linked factory run appears as one execution story from asked through deploy/proof. A second unlinked request remains visible with a named correlation gap rather than a guessed link.

### Work

1. Extend `report-contract` with the `execution` section while preserving schema version compatibility.
2. Add narrow readers for request registry and factory panel detail; calculate request state composition, run outcomes, phase/gate/tool durations, retries, attempts, models, accounts, sessions, hosts, diffs, land/deploy/proof.
3. Introduce one explicit correlation identifier at the source seam wherever the current registry omits it; migration/default behavior must preserve existing rows as unlinked.
4. Add execution summary, funnel, duration trend, failure breakdown, request/run records, and evidence story to `/reports`.
5. Link to `/requests` and `/factory/:id` with current context.
6. Verify explicit-ID joins and prove title/time/prose similarity never joins.
7. Land, deploy, and prove this slice before reliability work begins.

## Slice 3 — Reliability and recovery

### Installed evidence

One explicitly paired incident lifecycle reports a recovery duration; one unresolved incident reports age; repeated stable failure keys group as recurrence; an isolated error remains separate.

### Work

1. Extend the contract with the `reliability` section.
2. Read incident, CI, deploy, service, guard, reaper, and buildbox records through existing authoritative adapters.
3. Normalize stable recorded failure classes without parsing arbitrary prose.
4. Pair recovery only through explicit lifecycle identity.
5. Render unresolved age, resolved duration distributions, recurrence, and project/host/service breakdowns with links to focused pages.
6. Verify absence of a later error never implies recovery.
7. Land, deploy, and prove before capacity work begins.

## Slice 4 — Capacity, accounts, and spend

### Installed evidence

Attributed and unattributed usage produce honest model/account/host/project breakdowns; recorded provider cost is shown; missing cost remains unavailable rather than being estimated.

### Work

1. Extend the contract with the `capacity` section.
2. Project recorded factory usage, limits panel, seat identity, sessions, agents, cluster workload, and queue capacity.
3. Calculate tokens, recorded cost, billed state, account-window pressure, host workload, and cost per completed outcome only when numerator and denominator coverage are compatible.
4. Render usage trends and breakdowns with coverage for unattributed records.
5. Link to `/limits`, `/sessions`, `/agents`, and `/cluster`.
6. Verify account identity is explicit and credential path is never used as attribution.
7. Land, deploy, and prove before history work begins.

## Slice 5 — Durable history and comparison

### Installed evidence

The owner can select 7, 30, or 90 days and compare with the preceding period after a collector restart. A missing bucket renders as a visible gap, and an incompatible metric version suppresses the delta.

### Work

1. Add the versioned report-bucket contract and migration policy.
2. Add an append-only aggregate store under collector state, outside release trees, with atomic/idempotent writes keyed by bucket, metric version, and dimensions.
3. Persist source watermark and coverage with every bucket; quarantine corrupt rows while preserving healthy reads.
4. Add bounded backfill from still-authoritative records, marking every backfilled bucket.
5. Add prior-period comparison, long-range presets, and bucket-level gaps to existing sections.
6. Prove restart persistence, retention, idempotency, version compatibility, corruption behavior, and no line bridging across gaps.
7. Land, deploy, and prove before saved views/export work begins.

## Slice 6 — Lineage, saved views, and export

### Installed evidence

The owner opens a complete request-to-proof lineage, sees every missing link on an incomplete lineage, saves/reopens the current report, and exports CSV/JSON whose query, rows, and coverage match the screen.

### Work

1. Add the `lineage` section and stable cross-domain evidence references.
2. Project request → run → gate → change → land → deploy → proof only from explicit IDs.
3. Add owner-local saved views containing validated filters only.
4. Add CSV export for the selected visible table with spreadsheet-formula escaping.
5. Add JSON export of the exact rendered envelope and a concise printable view.
6. Preserve query context in every focused-page link where supported.
7. Verify auth, content types, export equality, formula escaping, safe links, local saved-view persistence, complete lineage, and named gaps.
8. Land, deploy, and run the complete six-slice installed proof matrix.

## Final completion evidence

The feature is complete when six deploy receipts and six installed owner-flow proofs exist. The final report must show all delivered domains with honest coverage and no future placeholder controls. The owner can move from high-level outcome to the recorded gate, tool, diff, account, host, source, and proof that supports it.
