# Synthetic browser acceptance-plan audit

Use this when reviewing or designing browser acceptance coverage for a sensitive dashboard where runtime evidence must use synthetic data only.

## Sequence

1. Read the sprint acceptance criteria and existing acceptance matrix before judging the suite.
2. Inspect committed runner config, package/lock files, fixture builder, server/generator seam, browser specs, rendering code, and responsive/privacy/print CSS.
3. Check `git status --short` before testing. If files change concurrently during the review, report the later state explicitly and do not attribute those edits to the audit.
4. Build a new temporary fixture at a caller-selected path, generate the real dashboard with an explicit anchor date, hash the fixture before/after, and serve it through the real local route/assets/security headers.
5. If the repository has no local browser install, install the locked browser-test package into a temporary prefix and use `NODE_PATH`; do not create `node_modules` in a read-only repository audit.
6. Run existing unit/browser tests, then add temporary browser probes for uncovered criteria. Passing legacy tests is evidence, not acceptance.
7. Remove only temporary dependencies, fixture/output, screenshots, traces/results, and the server process. Re-check repository status.

## Required fixture profiles

Prefer validated fixture-builder variants over HTML interception, which can bypass backend contracts:

- populated canonical anchor fixture;
- no complete primary metric but another allowlisted metric, for deterministic fallback;
- low/partial coverage with visible null gaps and explicit zero;
- current, stale, future/check, and missing timestamps;
- schema-only empty state;
- long labels across medication, nutrition, labs, metrics, and events.

Future-dated observations must be visibly anomalous and must not become the ordinary latest/fresh value.

## Acceptance matrix

Run the exact product viewports, including every named desktop size—not merely one desktop approximation. For each relevant view and viewport, verify:

- exactly one visible main panel;
- no horizontal page overflow;
- runtime navigation mode and no fixed/sticky control occlusion;
- every semantic interactive element's own box is at least 44×44 CSS px;
- console/page/request failures and external requests are empty;
- keyboard order, visible focus, skip link, roving tabs (arrows/Home/End), More state reset, and dialog focus return;
- actual period-dependent chart labels/data and event membership change;
- charts initialize lazily, update rather than duplicate, preserve null gaps and explicit zero, and expose textual summaries;
- events remain a separate semantic lane and never become value-series datasets;
- privacy hides every sensitive text/canvas in every data-bearing view and remains transient;
- print retains required dates, units, references, provenance, missingness, and disclaimers while hiding actions/navigation;
- reduced motion across all views, actual 200% text reflow, long labels, and meaningful empty states.

Feature-specific acceptance must inspect behavior, not headings. Examples: a daily view remains useful without a complete primary observation; task/hint caps are counted; medication administered/planned states stay separate; core cards expose value/unit, date or age, prior-only baseline, sparkline gaps, and coverage/quality; the main chart uses the primary metric when eligible, a documented fallback when not, and an explicit empty state otherwise.

## Screenshot discipline

Capture deterministic synthetic-only screenshots for key views at every required viewport. Pin locale/timezone/anchor, disable animation, stabilize fonts/device scale, and mask request-specific tokens. Keep screenshots/reports under an explicitly ignored artifact directory; broad `*.png` or `*.zip` ignores do not necessarily cover HTML reports and metadata. Never refresh baselines from a non-synthetic server.

## False-confidence patterns

- Checking a period caption without comparing chart labels/data.
- Checking only a root privacy class or the first blurred child while nested text/canvases leak.
- Measuring controls only in the initially/finally active view.
- Simulating 200% text solely with a root font-size declaration.
- Scanning reduced-motion durations only on the landing view.
- Emptying arrays through response interception while skipping bundle validation.
- Treating DOM labels as proof of chart series, gaps, fallbacks, or event separation.
- Treating green legacy tests as proof that a new sprint's unimplemented features pass.

## Feature-flagged visualization spikes

When the sprint adds a chart engine or other heavy browser feature behind an experimental flag, the flag being `false` by default is necessary but insufficient.

### Synthetic-only generator boundary

- Use a fixed sandbox root (for example `/tmp`), not `tempfile.gettempdir()` when `TMPDIR`/`TEMP`/`TMP` must not redirect the boundary.
- Require an owner-controlled `0700` parent, a fixture-only database marker, a regular non-symlink DB, and a new output target. A path merely located under the sandbox is not proof of synthetic provenance.
- Bind the DB to an `O_NOFOLLOW` file descriptor and validate device/inode. Marker validation and bundle reads must use the same SQLite `mode=ro&immutable=1` connection.
- Publish output as `0600` relative to an inode-checked directory FD: `O_EXCL` temporary creation plus no-replace linking/rename semantics. Reject existing outputs, symlinks, symlinked ancestors, public parents, and parent swaps with deterministic tests.
- Keep the default rendered page free of prototype markup and prototype script references. Serving a pinned local asset is not the same as enabling the feature, but the default page must not load it.

### Prove chart behavior, not option labels

- Tooltip/crosshair: assert the option contract, then hover a real visible point and inspect rendered tooltip text.
- Pan: drag in a direction that is not clamped by the current zoom endpoint and assert the actual dataZoom window changes.
- Brush: perform a real pointer drag and assert the emitted coordinate/date range, not a self-set `data-brush-active` attribute. Line series may emit an area with empty selected point indices; derive the selected date range from `areas[].coordRange` when that is the intended contract.
- Point drill-down: click a plotted point by converting its data coordinate to pixels, then verify the exact ISO-date callback; repeat through the keyboard-accessible table.
- At 200% text, remeasure target boxes, page overflow, chart container/internal dimensions, height, ARIA description, and visible labels after ResizeObserver settles.
- In print media, open any details/table disclosure and verify row dates remain visible; a broad `button { display:none !important }` rule can erase dates implemented as accessible buttons.
- Use at least five fresh navigation/load runs and a separate interaction-latency threshold. Start internal initialization timing before data synthesis and series/marker construction; also measure full navigation-to-ready so asset loading/parsing is represented.
- Provide one repository command that runs the legacy and new browser specs together. A separate prototype command is useful, but it must not be the only route to the claimed total.

### Vendored-library license closure

For a pinned local browser bundle, archive and gate the complete upstream license set, not just the package's top-level SPDX value:

1. vendor the exact top-level LICENSE and NOTICE;
2. inspect the top-level license for referenced subcomponent texts (for example a D3 BSD-3-Clause file under `licenses/`);
3. vendor every referenced third-party license;
4. record SHA-256 for the JS bundle and every license/notice file in metadata;
5. test file presence and each recorded hash, and byte-compare against the locked package during release verification.

`npm audit` and a correct Apache/MIT identifier do not prove that redistribution notices are complete.

### Concurrent-review discipline

If independent reviews run while fixes continue, their findings may describe an earlier tree. Classify each finding as still valid, already fixed, or newly exposed; do not count a provider-filter interruption as approval. Re-run the blocked review with a narrow defensive code-correctness brief, then request an exact final re-review for any remaining license or release finding before committing.

## Reporting

Return a compact parent-agent summary with: existing commands/results, currently proven behavior, material acceptance gaps, proposed deterministic test groups, pitfalls, repository impact, and any concurrent changes observed. Keep implementation findings separate from the test-plan recommendation.