# Time-series cockpit contract and chart audit

Use for read-only reviews of dashboard metric cards, sparklines, main trends, coverage badges, baselines, and event lanes where missingness must remain explicit.

## Audit sequence

1. Re-read `git status --short` before and after verification. In an actively edited worktree, source and tests can change during the audit; treat late changes as new review inputs and rerun focused checks against the final snapshot.
2. Trace each requested display property end to end: provider → serialized contract → renderer shell → chart JavaScript → browser assertion. A valid bundle and green provider tests do not prove chart semantics.
3. Separate serialized facts from deterministic presentation derivatives:
   - Normally serialize `today`, metric metadata, ordered dated points, values, quality, baseline, and dated events.
   - Derive measurement age, signed baseline delta, factual period coverage, sparkline datasets, and fallback labels in the client.
   - Do not add redundant summary fields unless they cannot be derived without unavailable provenance.
4. Validate series invariants at the contract boundary:
   - dates are unique and strictly ascending;
   - measurement dates are not after the bundle's `today`;
   - incomplete quality implies a null value;
   - complete/observed quality implies a finite value;
   - baseline uses only prior observations in the configured lookback and appears only after `baseline_min_observations`.
5. Verify the provider actually honors registry metadata. In particular, pass both `baseline_days` and `baseline_min_observations`; do not silently rely on a helper default that contradicts the public registry.
6. Audit missingness in chart construction, not just source arrays. `spanGaps: false` is ineffective when missing calendar days were removed from the label axis. Densify the selected calendar range and insert explicit `null` slots, or use a time axis with equivalent tested gap semantics.
   - Treat observation-count limits and calendar-span limits as different invariants. A contract allowing 3,660 points may still allow two observations more than 10 years apart.
   - Never stop densification from the oldest date at a fixed cap without preserving the newest endpoint: that can make a current card disagree with a chart silently ending years earlier. Prefer a real time axis, or apply an explicit bounded window that includes the latest point and discloses truncation.
   - Add a deterministic sparse long-span probe and compare the latest card date/value with the chart's final represented date/value.
7. Keep cockpit roles deterministic without unnecessary schema growth. If registry order defines primary/fallback trend priority, document and test that order. Choose the first usable metric for the active period, update the visible title/context, and preserve an explicit all-empty state.
8. Keep events out of health-value datasets. A real event lane should share the selected date range or temporal scale while remaining a separate DOM/chart layer; a flat badge list is not a time-aligned lane.
   - Derive the lane domain from the chart's final rendered axis, not from pre-aggregation/raw observation dates. This matters especially for weekly/monthly modes where bucket anchors differ from first/last raw dates.
   - In every cadence, compare the chart's exact first/last represented dates with the event-axis first/last dates. A test that merely preserves an event at `100%` can lock in a misalignment when that event is actually beyond the chart endpoint.
   - Do not clamp the temporal marker itself inward merely to keep its label box visible. Preserve the true 0–100% time coordinate for a tick/anchor and independently shift, clip, or flip the label.
   - Probe events at the first date, last date, an interior measurement-gap date, and—when aggregation exists—a raw-date event after the final bucket anchor. Assert the marker exists only when inside the rendered domain, its date-derived position is correct, and it refreshes when period or cadence changes—not merely that an “event lane” heading is present.
9. Review generated copy for diagnosis, causality, treatment, urgency, reassurance, and evaluative direction. Baseline deltas and coverage should be neutral and factual: signed difference, observation count, denominator, and quality—not “better/worse”, “normal”, or traffic-light medical judgments.
10. Inspect rendered shell structure for concurrent regressions such as duplicate tablists, duplicate IDs, or duplicate navigation emitted in two layout positions.

## High-value tests

### Contract/provider

- Reject unordered, duplicate, and future measurement dates.
- Reject numeric incomplete points and null complete/observed points.
- Establish the baseline exactly after the configured minimum prior observations.
- Prove the current point and future points cannot influence its baseline.
- Preserve explicit zero while partial/missing days stay null or absent according to the contract.
- Lock the metric registry's deterministic cockpit/fallback order.

### Browser/Chart.js

- Use a synthetic series with an entirely absent calendar day between two observations; inspect the actual Chart.js labels/dataset and require an explicit null slot plus `spanGaps: false`.
- Require every core card to expose a sparkline, deterministic age derived from bundle `today`, neutral baseline delta, and exact `observed/expected` coverage.
- Empty the primary series and require the expected fallback metric label and data.
- Empty all series and require the explicit empty state without console errors.
- For selection-driven statistical panels, test fail-closed behavior across every disclosure surface, not only the primary visualization. After clearing selection, enumerate result cards/rows in expandable details, summaries, tables, accessible text, and legacy result containers; require zero aggregate rows everywhere. Trace independently invoked renderers (for example, a generic `renderResults()` called before the selection-aware update path), because a collapsed `<details>` element can still expose the same Lag/Phase statistics that the main panels correctly hide.
- Place an event on a measurement-gap day and require it only in the separate aligned event lane.
- Change periods and assert cards, coverage, sparklines, fallback selection, event positions, and accessible summaries all refresh.
- Assert exactly one visible main view, one navigation tablist, unique IDs, no horizontal overflow, and no forbidden medical copy.

## Final-release execution discipline

- Run browser acceptance against a server instance started by the review, on a freshly selected free port. A successful readiness request or synthetic sentinel on a fixed port does **not** prove that the responding process is the one just launched; another compatible server may already own the port.
- If Playwright intercepts the dashboard request and calls `route.fetch()`, the disposable review server must handle concurrent requests (for example Python `ThreadingHTTPServer`, preferably with explicit connection-close/HTTP 1.0 behavior). A single-threaded static server can block on a prior keep-alive connection and manufacture `route.fetch`/`page.goto` timeouts. Replace the review server and rerun the complete browser suite rather than reporting these as product failures.
- After launch, inspect the tracked process state/log before trusting browser results. If bind/startup failed, discard all results from that URL, choose a new free port, regenerate the synthetic bundle if needed, and rerun the complete browser gate.
- In an actively edited worktree, regenerate the served HTML/bundle immediately before the final browser run. A server reading a previously generated artifact can produce a green result against stale renderer copy or client contracts after source/tests changed.
- Keep Playwright output in an explicit disposable directory outside the repository. After verification, scan the repository for ignored as well as tracked report/trace/screenshot remnants; distinguish “not committed” from “not present.”
- Verify deterministic reproduction as an end-to-end property, not merely by supplying an anchor date. Generate two fresh fixtures and rendered outputs with identical inputs, hash/compare both, and inspect logical database row differences when hashes diverge. Common hidden nondeterminism includes schema `CURRENT_TIMESTAMP` defaults and a renderer-generated `generated_at`; require explicit injection or normalize only fields the release contract declares volatile.
- If concurrent edits landed after the first reproduction, rerun the deterministic hashes on the final audited snapshot. Report both hashes and byte equality; when SQLite bytes diverge, compare logical table rows before deciding whether the difference is contractually volatile.
- When documentation claims “no browser statistics,” trace client aggregation code too: weekly sums/averages are calculations even if they are not inferential statistics. Either narrow the claim precisely or move the derivative behind the validated contract.
- Re-read `git status --short` after every test phase and compare it with the initial snapshot. For a read-only approval review, report only tracked/untracked release-tree changes as code changes, while separately disclosing disposable ignored artifacts if any remain.
- When the release claim includes “v4 unchanged,” enumerate tracked v4 paths and verify each has no worktree diff; filename substring scans of the changed-file list are insufficient because test filenames can contain `v4` without changing v4 production code.

## Reporting gate

Do not approve because unit tests are green when browser/chart behavior is untested. Report exact file/line anchors, violated semantics, and the end-to-end consequence. Keep verification evidence separate from design findings. If the worktree changed during review, identify the final audited snapshot and disclose any browser gate that could not be exercised. For a binary release verdict, lead with exactly `APPROVE` or `CHANGES REQUIRED`, then give concise evidence; do not bury the gate behind a narrative.