diff --git a/AGENTS.md b/AGENTS.md index 3e69fcb..35eb3b3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -195,8 +195,9 @@ Merge `webcomponent` → `main` only when a phase is complete and reviewed. | 5 | Build & distribution (esbuild, ESM + IIFE, worker/processor inlining, cross-browser test) | Complete | | 6 | Documentation & demo (+ `buffer-size` attribute for AudioWorklet) | In progress | | 7 | npm publishing (`@adasp/latency-test`) — see `agents/CLAUDE_REVIEW.md` Phase 7 checklist | Complete | -| 8 | Signal types (chirp logarithmic sweep, Golay complementary sequences) | Pending | -| 9 | Experimentation toolkit — optional visualization layer (graphs, waveforms, comparisons) | Pending | +| 8 | Experimentation toolkit — optional visualization layer (graphs, waveforms, comparisons, host-side histogram — absorbed from Phase 4 on 2026-06-12) | Pending, optional | + +Signal types (chirp logarithmic sweep, Golay complementary sequences) are deferred to v2 scope and are no longer a numbered phase. TypeScript declaration file (`src/index.d.ts`, typed events) ships with the package as of v1.0.0. @@ -209,7 +210,7 @@ Do not re-open these unless the user explicitly asks: - `inputStream`: host-required. Must be set via `element.inputStream = stream` before calling `start()`. The component never calls `getUserMedia()` or stops the stream. Emits `latency-error` if missing. - Shadow DOM: open mode, empty root by default. No built-in visible UI in v1 (headless-first). -- `recording-mode` attribute: `"mediarecorder"` (v1 default) | `"audioworklet"` (v2 default). +- `recording-mode` attribute: `"mediarecorder"` (v1 default, 2-channel) | `"mediarecorder-1ch"` (single-channel fallback) | `"audioworklet"` (v2 default). - Lifecycle events emitted: `latency-start`, `latency-recording`, `latency-processing`, `latency-result`, `latency-error`, `latency-complete`. All events must set `bubbles: true` and `composed: true`. diff --git a/CLAUDE.md b/CLAUDE.md index e8b51ad..3e64a61 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -78,6 +78,7 @@ docs/ index.md — VitePress home page (hero + features layout) api.md — Full API reference (attributes, methods, events, signal types) install.md — Installation: npm, CDN, AudioContext sharing + build-output.md — dist/ file reference per build command (linked from install.md CDN section, not in sidebar) examples/ vanilla-js.md react.md @@ -223,7 +224,7 @@ Phases 1–3b are complete. Previous design issues are resolved. Remaining known 3. **`recording-mode="mediarecorder"` is now 2-channel (Phase 3b complete)** — Default mode uses `ChannelMergerNode` + `MediaStreamDestinationNode` to capture mic and reference in one stereo stream, removing start-timing bias. Emits `latency-error` if the browser downmixes to mono. **`recording-mode="mediarecorder-1ch"`** is the single-channel fallback (direct mic stream, start-timing bias present) — use when the default fails due to mono downmix, or to deliberately measure the direct-mic pipeline. `"mediarecorder-2ch"` as an attribute value no longer exists. -4. **No histogram** — `latency-complete` fires with aggregate stats (mean/std/min/max). Host-side histogram rendering is a Phase 4 item. +4. **No histogram** — `latency-complete` fires with aggregate stats (mean/std/min/max). Host-side histogram rendering is part of the Phase 8 experimentation toolkit (moved from Phase 4). 5. **`buffer-size` flush not yet implemented** — The attribute is wired through to `recorder-processor.js` and the value reaches the processor, but nonzero values do not trigger intermediate flushes. Only the final stop flush is implemented. Deferred to v2. @@ -240,7 +241,7 @@ Phases 1–3b are complete. The `` Custom Element is implemented w - `worker.js` cross-correlates two buffers: in the audioworklet path these are `{ mic, ref }` Float32 PCM; in the mediarecorder (2ch) path these are `ch0` (mic) and `ch1` (reference) from the decoded stereo recording; in the mediarecorder-1ch path these are the decoded mono recording vs the pre-generated MLS AudioBuffer **Still in progress:** -- Phase 4: histogram, browser verification matrix across all three modes +- Phase 6: framework example end-to-end verification against the published `@adasp/latency-test@1.2.0` package — the only remaining v1 item. (Phase 4 is complete: browser verification matrix done across all three modes; host-side histogram moved to the Phase 8 experimentation toolkit.) **Planned configurable attributes (beyond `number-of-tests`, `mls-bits`, `max-lag-ms`):** diff --git a/agents/CLAUDE_REVIEW.md b/agents/CLAUDE_REVIEW.md index 632edf7..15d3988 100644 --- a/agents/CLAUDE_REVIEW.md +++ b/agents/CLAUDE_REVIEW.md @@ -293,9 +293,9 @@ Cross-correlate ch0 against ch1 — worker contract unchanged: `{ command: 'corr ### Phase 4 — Demo page & integration - [x] Rewrite `src/index.html` as a minimal demo: a plain button that calls `element.start()` and a `` element - [x] Demo page listens for `latency-result` and `latency-error` and updates its own UI -- [ ] Demo page optionally listens for `latency-complete` and renders a histogram (host-side) — aggregate stats (mean/std/min/max) are shown; histogram not yet implemented -- [ ] Verify `worker.js` works correctly with Float32 PCM coming from the AudioWorklet path -- [ ] Test `number-of-tests` > 1 loop driven by the component +- ~~Demo page optionally listens for `latency-complete` and renders a histogram (host-side)~~ — moved to Phase 8 experimentation toolkit (2026-06-12); aggregate stats (mean/std/min/max) are shown on the demo page +- [x] Verify `worker.js` works correctly with Float32 PCM coming from the AudioWorklet path — covered by the completed browser verification matrix (AudioWorklet mode verified across repeated runs; confirmed 2026-06-12) +- [x] Test `number-of-tests` > 1 loop driven by the component — covered by the completed browser verification matrix (multi-run sequences verified in both modes; confirmed 2026-06-12) ### Phase 5 — Build & distribution @@ -321,17 +321,15 @@ Cross-correlate ch0 against ch1 — worker contract unchanged: `{ command: 'corr - [x] The component bundle is a prerequisite for the live demo page (Phase 6). ### Phase 6 — Documentation & demo - -### Phase 6 — Documentation & demo -- [ ] Update README.md to stay short and repo-oriented once the docs site is live (Decision #7) -- [ ] Remove component API details from README — those belong in the VitePress docs -- [ ] Document public API (attributes, properties, events, methods) in `docs/api.md` — mark planned items clearly -- [ ] Document CSS custom properties for theming if Shadow DOM is open -- [ ] Create `demo/index.html` — standalone showcase gallery (no framework, no build step) that pairs code snippets with live working component instances. Patterns to show: (1) minimal headless — `start()` + `latency-result` event; (2) AudioContext injection — host creates the context and passes it via `element.audioContext`; (3) input-gain usage — demonstrating Safari compensation; (4) all lifecycle events — showing `latency-start`, `latency-recording`, `latency-processing`, `latency-result`. Each pattern shows the code alongside a rendered, clickable `` element. -- [ ] Deploy `demo/` alongside the VitePress build in the GitHub Actions workflow (update `.github/workflows/docs.yml` to copy `demo/` into the Pages output) -- [ ] Link the live demo prominently from `docs/index.md` ("Try it live →" on the hero and install page) -- [ ] Add a `docs/demo.md` page that embeds the live demo via a same-origin iframe (the demo is hosted on the same GitHub Pages deployment, not a third-party sandbox) and explains what to expect -- [ ] **Pre-release gate:** before removing the `> **Draft.**` notice from any framework example page, verify a working end-to-end example in that framework against the actual installed published package — not against the local source. Draft labels stay until that verification is done. +- [x] Update README.md to stay short and repo-oriented once the docs site is live (Decision #7) — done; README carries the npm badge and points to the docs site +- [x] Remove component API details from README — done; API reference lives in `docs/api.md` +- [x] Document public API (attributes, properties, events, methods) in `docs/api.md` — done; planned items marked +- ~~Document CSS custom properties for theming~~ — dropped: the component is headless with an empty shadow root; there is no built-in UI to theme in v1 +- [x] Create `demo/index.html` — done: shipped as a 9-panel showcase gallery (minimal, multi-run, context share, mode toggle, AudioWorklet, MediaRecorder 1ch, lifecycle, debug, host gain). Note: the originally planned "input-gain usage" pattern was superseded by the host-gain pattern (`input-gain` was removed entirely — gain is host responsibility via a processed `inputStream`) +- [x] Deploy `demo/` alongside the VitePress build in the GitHub Actions workflow — done: `.github/workflows/docs.yml` copies `demo/` into the Pages output +- [x] Link the live demo prominently from `docs/index.md` — done: hero link + "Try it live" section pointing at the deployed `/demo/` +- ~~Add a `docs/demo.md` page that embeds the live demo via a same-origin iframe~~ — superseded: the docs link directly to the live `/demo/` page instead of embedding it in an iframe page +- [ ] **Verification gate (the only remaining Phase 6 item):** verify each of the six framework example pages (vanilla-js, React, Vue, Svelte, Angular, Next.js) end-to-end against the actual installed published package `@adasp/latency-test@1.2.0` — not against the local source. Draft labels were already removed; if an example is found wrong during verification, patch the docs. The host-gain pattern page is out of this gate's scope — it is exercised by the demo's Host Gain panel against the built bundle (decision 2026-06-12). ### Phase 7 — npm publishing @@ -554,7 +552,7 @@ git push --follow-tags # pushes tag → triggers the publish workflow - [ ] Create a standalone HTML page (or mini-app) that imports `@adasp/latency-test` and adds rich visualizations: - Autocorrelation graph: render the correlation array as a chart (canvas or SVG) - Audio waveform: display captured mic + reference signals as waveform graphs - - Latency histogram: aggregate results across multiple runs and render a distribution chart + - Latency histogram: aggregate results across multiple runs and render a distribution chart (absorbed from Phase 4 on 2026-06-12 — was previously a demo-page item) - Side-by-side comparison panel: run tests with different params and compare results visually - [ ] Config export: snapshot of all test parameters + results as downloadable JSON - [ ] Platform comparison: save/load result sets to compare across browsers, OS versions, or hardware diff --git a/agents/CODEX_REVIEW.md b/agents/CODEX_REVIEW.md index c5f21b3..6a1dd47 100644 --- a/agents/CODEX_REVIEW.md +++ b/agents/CODEX_REVIEW.md @@ -1,5 +1,7 @@ # CODEX_REVIEW.md +> **Historical document (banner added 2026-06-12).** This file is the original Codex review log, preserved as a record of the migration planning. Statements below describe the repository as it was at review time and several are now superseded — notably: `input-gain` was later removed entirely (gain is host responsibility via the host-gain pattern); Draft labels on framework examples were removed after publication, with verification against the installed package tracked as the Phase 6 gate; a `node:test` suite now exists (`tests/`); and the package is published as `@adasp/latency-test` (v1.2.0). For current status, read `agents/CLAUDE_REVIEW.md` and `agents/SESSION_HANDOFF.md`. + ## Purpose This document captures the current repository assessment from Codex, plus the main migration actions and open questions for converting this proof-of-concept app into a reusable Web Component with AudioWorklet-based recording. @@ -308,7 +310,7 @@ The following points were clarified in discussion and should be treated as curre 7. The component uses **open Shadow DOM with an empty shadow root by default**. No built-in visible UI is required in v1. 8. v1 should emit lifecycle + result events: `latency-start`, `latency-recording`, `latency-processing`, `latency-result`, `latency-error`, and `latency-complete`. 9. `inputStream` ownership: **host-required**. Must be set via `element.inputStream = stream` before `start()`. The component never calls `getUserMedia` or stops tracks — emits `latency-error` if missing. -10. Safari-specific automatic browser detection should be removed from the component. Gain compensation becomes a host-controlled/general `input-gain` behavior instead of an internal Safari-only workaround. +10. Safari-specific automatic browser detection should be removed from the component. Gain compensation becomes a host-controlled/general `input-gain` behavior instead of an internal Safari-only workaround. *(Superseded 2026-06-12: `input-gain` was removed entirely and never wired — gain compensation is host responsibility via a processed `inputStream`; see `docs/examples/host-gain.md`.)* ## Ongoing Review Notes diff --git a/agents/KNOWN_ISSUES.md b/agents/KNOWN_ISSUES.md index 0ed21c3..e94fdb9 100644 --- a/agents/KNOWN_ISSUES.md +++ b/agents/KNOWN_ISSUES.md @@ -10,7 +10,7 @@ Each entry records the source, severity, current status, and what would be neede ### Framework examples not verified against published package **Source:** `agents/CLAUDE_REVIEW.md` Phase 6 gate · **Severity:** Low -**Detail:** The six framework example pages (`docs/examples/*.md`) were updated based on the local source but have not been verified end-to-end against the actual installed npm package. Draft notices were removed trusting the code to be correct — if an example has a subtle error it would only surface during a consumer's actual use. +**Detail:** The six framework example pages (`docs/examples/*.md`: vanilla-js, React, Vue, Svelte, Angular, Next.js) were updated based on the local source but have not been verified end-to-end against the actual installed npm package. (The seventh page, `host-gain.md`, is a pattern page outside this gate — it is exercised by the demo's Host Gain panel against the built bundle.) Draft notices were removed trusting the code to be correct — if an example has a subtle error it would only surface during a consumer's actual use. **Fix:** For each framework: install `@adasp/latency-test` in a fresh project, follow the example page instructions exactly, confirm the component registers and emits events correctly. Only then is the example considered fully verified. --- diff --git a/agents/SESSION_HANDOFF.md b/agents/SESSION_HANDOFF.md index 2c15b59..2d7b61f 100644 --- a/agents/SESSION_HANDOFF.md +++ b/agents/SESSION_HANDOFF.md @@ -10,23 +10,31 @@ See `agents/KNOWN_ISSUES.md` for open findings from code reviews (Codex, DeepSee ## Current Repo State -- Working branch: `webcomponent`. All PRs merged — branch is ahead of `main` with active development. -- Phases 1–7 complete. **v1.1.0** is live on npm as `@adasp/latency-test`. +- Stable base: `main` (branch protection: PR + "ci" status check required — work on a branch and open a PR). Current working branch: `docs/install-dedup-build-output` (PR #25 — docs cleanup, histogram move to Phase 8, and Codex consistency fixes; merge pending CI). `webcomponent` fully merged via PRs #14–24. +- Phases 1–7 complete. **v1.2.0** is live on npm as `@adasp/latency-test` (released 2026-06-12). - `dist/` remains gitignored — generated with `npm run build:component`. - `demo/` validates the built IIFE bundle via `../dist/latency-test.legacy.iife.js`. Run with `npm run build:component:legacy && npm run demo`. - `src/dev-test/` contains development test pages served by `npm run dev` (no build needed), also published to GitHub Pages under `/dev/`. - `src/experiments/` contains research-only experiment pages (not part of the component test suite), also published to GitHub Pages under `/dev/`. - GitHub Pages deploys the VitePress docs site from `docs/.vitepress/dist/` via `.github/workflows/docs.yml`. `src/` is also copied to `dist/dev/` — dev and experiment pages accessible at `https://idsinge.github.io/latency-test/dev/`. - VitePress base is `/latency-test/` → site at `https://idsinge.github.io/latency-test/`. -- `.nvmrc` pins Node 22. `docs.yml` CI uses Node 24. CDN URLs in `docs/install.md` are pinned to `@1.1.0`. +- `.nvmrc` pins Node 22. `docs.yml` CI uses Node 24. CDN URLs in `docs/install.md`, `docs/index.md`, and `docs/examples/vanilla-js.md` are pinned to `@1.2.0`. --- ## Recently Completed (2026-06-12) -### input-gain removal + 1.2.0 release preparation (working tree — commit/PR pending at session end) +### Docs cleanup + npm badge (post-release, same day) -**If you are reading this before v1.2.0 is on npm, the release is mid-flight: follow the "Release checklist (per release)" in `agents/CLAUDE_REVIEW.md` Phase 7.** In particular: the CHANGELOG `[Unreleased]` → `[1.2.0]` stamp and the CDN pin bump (`@1.1.0` → `@1.2.0`, 10 occurrences: `docs/install.md` ×8, `docs/index.md` ×1, `docs/examples/vanilla-js.md` ×1) happen in a separate commit on `main` and must NOT be pushed before `npm publish` — `docs.yml` deploys Pages on every push to `main` and the docs would reference a 404ing CDN version. +- `README.md`: npm version badge added (commit `c666ccf`). Decision: npmjs-only — no GitHub Packages dual-publish; the repo About link already points at the docs site. +- `docs/install.md`: "Providing AudioContext and stream" deduplicated — the code block duplicating Basic usage removed, replaced with an anchor pointer to `#basic-usage`; the AC-before-`getUserMedia` ordering rule promoted from code comment to prose; the existing-AudioContext/DAW snippet kept. Codex-reviewed. +- `docs/build-output.md` de-orphaned (it was the docs site's only orphan page — all 11 pages verified): linked from the install.md CDN section; kept out of the sidebar per Codex review (sidebar stays integration-focused). Accuracy fixes: legacy-browser claim aligned with install.md's Legacy IIFE scoping (Chrome 74–79, Firefox < 72, Safari 14, `recording-mode="mediarecorder"` only — it previously contradicted it); "All files are minified" corrected to the JS bundles only. +- `CLAUDE.md` file map: `docs/build-output.md` entry added (its omission is why the orphan went unnoticed). +- `npm run docs:build` green (validates internal links). + +### input-gain removal + 1.2.0 release — shipped ✅ + +**v1.2.0 released 2026-06-12 — release fully complete.** PR #24 merged (`f36c636`), prep commit `8975f31` (CHANGELOG stamp + CDN pin bump ×10), version commit `6747646` + tag `v1.2.0`, `npm publish`, `git push --follow-tags`, GitHub Release created. All post-publish checks passed: npm shows 1.2.0 (13 files), published `dist/index.d.ts` clean of `inputGain`, CHANGELOG ships in the tarball, all 4 CDN URLs return 200, Pages deploy green. The Phase 7 checklist in `agents/CLAUDE_REVIEW.md` worked end-to-end — reuse it verbatim for future releases. - `input-gain` / `inputGain` removed entirely (decision: permanent drop, not deferred — gain is host responsibility via the host-gain pattern). Touched: `src/scripts/latency-test-element.js` (field + `observedAttributes`), `src/index.d.ts`, `docs/api.md`, `docs/index.md`, `docs/examples/{react,nextjs,host-gain}.md`, `CLAUDE.md`, `AGENTS.md`, this file, `agents/CLAUDE_REVIEW.md` (top supersession note + two inline "superseded" markers + closed checkbox). Historical review records (`CODEX_REVIEW.md`, `KNOWN_ISSUES.md`) intentionally untouched. - Version decision: next release is **1.2.0** (minor, pure removal). Policy: removal of documented-but-never-functional API treated as minor, called out explicitly in the changelog. 1.1.1 rejected (patch promises invisible fixes; semver §7 requires minor even for deprecation). 2.0.0 stays reserved for the audioworklet-default switch. @@ -104,10 +112,10 @@ See `agents/KNOWN_ISSUES.md` for open findings from code reviews (Codex, DeepSee - [x] `startMediaRecorder2chCapture()`, `#cleanup2chNodes()`, `displayAudioTagElem2ch()` implemented in `test.js`; TypeScript declarations and all docs updated. ### Phase 4 remainder -- [ ] Host-side histogram — `latency-complete` fires with `{ results[], mean, std, min, max }`. Demo page should render a simple histogram from the results array. +- Host-side histogram — moved to Phase 8 experimentation toolkit (2026-06-12). `latency-complete` already fires with `{ results[], mean, std, min, max }`; rendering a histogram from the results array is now a toolkit item, not a demo-page requirement. Nothing else remains in Phase 4. ### Phase 6 remainder -- [ ] Framework example end-to-end verification — before treating any framework example as verified, test it against the installed published package (not local source). All Draft labels are already removed; if examples are found wrong during verification, a patch is needed. +- [ ] Framework example end-to-end verification — before treating any framework example as verified, test it against the installed published package `@adasp/latency-test@1.2.0` (not local source). Scope: the six framework pages (vanilla-js, React, Vue, Svelte, Angular, Next.js). `host-gain.md` is out of scope — it is a pattern page exercised by the demo's Host Gain panel against the built bundle (decision 2026-06-12). All Draft labels are already removed; if examples are found wrong during verification, a patch is needed. ### Independent - [x] **CI Node version divergence** — Intentional and closed. `.nvmrc=22` (local dev), `ci.yml=20` (test/build job), `docs.yml=24` (Pages build/deploy workflow). All satisfy `engines: >=18`. See `agents/KNOWN_ISSUES.md`. diff --git a/docs/build-output.md b/docs/build-output.md index 80dc812..f3340af 100644 --- a/docs/build-output.md +++ b/docs/build-output.md @@ -9,7 +9,7 @@ | `index.d.ts` | TypeScript declarations — types for `LatencyTestElement`, events, and payloads | | `*.map` | Source maps — automatically loaded by browser devtools | -`npm run build:component:legacy` produces a transpiled build for older browsers (Safari 14 / Chrome 78 — any browser with MediaRecorder support): +`npm run build:component:legacy` produces a transpiled build for older browsers (Chrome 74–79, Firefox < 72, Safari 14 — `recording-mode="mediarecorder"` only; see the Legacy IIFE note in [Installation & Setup](./install.md#cdn-no-build-step) for details): | File | Purpose | |------|---------| @@ -18,7 +18,7 @@ `npm run build:component:all` runs both builds in sequence and is used by CI and `prepublishOnly`. -All files are **minified by default**. For development (unminified, easier debugging): +The JS bundles are **minified by default**. For development (unminified, easier debugging): ```bash npm run build:component:dev diff --git a/docs/install.md b/docs/install.md index d8fd1fe..f7253df 100644 --- a/docs/install.md +++ b/docs/install.md @@ -59,6 +59,8 @@ Choose the approach that fits your project: All three register the `` element globally. Place either in the `` of your HTML file. +For the full list of files in `dist/` and which build command produces each, see [Build Output](./build-output.md). + --- ## Basic usage @@ -105,32 +107,9 @@ All three register the `` element globally. Place either in the `< Before calling `start()`, both `audioContext` and `inputStream` must be assigned. The component never creates audio resources itself — it emits `latency-error` if either is missing. -Both must come from a user gesture (browsers require it for microphone access and AudioContext creation): - -```html - - - - +Both must come from a user gesture (browsers require it for microphone access and AudioContext creation). Create the `AudioContext` synchronously inside the gesture handler, **before** `await getUserMedia()` — an `AudioContext` created after an `await` starts in `suspended` state in Firefox. - -``` +For a complete runnable page — including the click handler that assigns both properties and calls `start()` — see [Basic usage](#basic-usage) above. If your application already has an `AudioContext` (e.g. a DAW), pass it directly — no need to create a second one: