|
|
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
phase: 01-stabilize-video-pipeline
|
|
|
|
|
|
|
|
plan: 02
|
|
|
|
|
|
|
|
subsystem: testing
|
|
|
|
|
|
|
|
tags: [vitest, tdd, red-tests, ring-buffer, codec-check, offscreen-handshake, port-keepalive]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Dependency graph
|
|
|
|
|
|
|
|
requires:
|
|
|
|
|
|
|
|
- phase: 01-stabilize-video-pipeline
|
|
|
|
|
|
|
|
provides: "Plan 01-01 doc cascade (DEC-003/DEC-010 amended; manifest swapped)"
|
|
|
|
|
|
|
|
provides:
|
|
|
|
|
|
|
|
- "Vitest 4.1.6 installed under devDependencies"
|
|
|
|
|
|
|
|
- "vitest.config.ts at repo root (Node env, tests/**/*.test.ts include)"
|
|
|
|
|
|
|
|
- "npm test script wired to vitest run"
|
|
|
|
|
|
|
|
- "4 RED test files at tests/offscreen/ pinning the contracts for Plans 03 and 04"
|
|
|
|
|
|
|
|
- "tests/fixtures/.gitkeep marker (Plan 07 will drop a known-good last_30sec.webm)"
|
|
|
|
|
|
|
|
affects: [01-03, 01-04, 01-07]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Tech tracking
|
|
|
|
|
|
|
|
tech-stack:
|
|
|
|
|
|
|
|
added:
|
|
|
|
|
|
|
|
- "vitest@^4 (devDep)"
|
|
|
|
|
|
|
|
patterns:
|
|
|
|
|
|
|
|
- "Vitest Node-environment unit tests (Blob shimmed via undici)"
|
|
|
|
|
|
|
|
- "RED-first TDD: failing tests committed BEFORE production code (Nyquist sampling)"
|
|
|
|
|
|
|
|
- "Hand-rolled chrome.runtime stub (no vitest-chrome dependency — minimize supply chain per T-1-NEW-02-01)"
|
|
|
|
|
|
|
|
- "`as unknown as T` cast pattern (no `as any`, no `@ts-ignore` per CLAUDE.md / tsconfig strict)"
|
|
|
|
|
|
|
|
- "vi.resetModules() between import-side-effect tests for isolation (handshake + port + codec all run module-load effects)"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
key-files:
|
|
|
|
|
|
|
|
created:
|
|
|
|
|
|
|
|
- "vitest.config.ts"
|
|
|
|
|
|
|
|
- "tests/offscreen/ring-buffer.test.ts"
|
|
|
|
|
|
|
|
- "tests/offscreen/codec-check.test.ts"
|
|
|
|
|
|
|
|
- "tests/offscreen/handshake.test.ts"
|
|
|
|
|
|
|
|
- "tests/offscreen/port.test.ts"
|
|
|
|
|
|
|
|
- "tests/fixtures/.gitkeep"
|
|
|
|
|
|
|
|
modified:
|
|
|
|
|
|
|
|
- "package.json (vitest devDep + npm test script)"
|
|
|
|
|
|
|
|
- "package-lock.json (npm install regenerated)"
|
|
|
|
|
|
|
|
- ".planning/REQUIREMENTS.md (reverted premature [x] + Complete marking on REQ-video-ring-buffer)"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
key-decisions:
|
|
|
|
|
|
|
|
- "Pinned vitest at ^4 (4.1.6 latest stable; 5.x still beta per npm view vitest versions on 2026-05-15)"
|
|
|
|
|
|
|
|
- "No vitest-chrome dependency added — hand-rolled minimal chrome stub in each test (4-file scope doesn't justify the supply-chain widening; T-1-NEW-02-01)"
|
|
|
|
|
|
|
|
- "Type cast pattern is 'as unknown as T' uniformly (CLAUDE.md / tsconfig strict): zero 'as any', zero '@ts-ignore' across all four test files (verified by grep)"
|
|
|
|
|
|
|
|
- "REQ-video-ring-buffer reverted to in-progress: Plan 01-01 (doc cascade) prematurely marked it Complete; the requirement is satisfied by Plans 03 + 04 + 07's ffprobe gate, not by RED test scaffolding"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
patterns-established:
|
|
|
|
|
|
|
|
- "Vitest config minimal: Node env, no globals, no path aliases, typecheck disabled (tsc runs separately in npm run build)"
|
|
|
|
|
|
|
|
- "Tests in tests/offscreen/*.test.ts; production code in src/offscreen/recorder.ts; tests reach into src via relative '../../src/...' import"
|
|
|
|
|
|
|
|
- "Module-load side-effect testing pattern: stub chrome + MediaRecorder on globalThis BEFORE await import(), use vi.resetModules() between tests so import effects re-fire"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements-completed: [] # NONE — REQ-video-ring-buffer is NOT satisfied by RED tests; Plans 03+04+07 satisfy it.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Metrics
|
|
|
|
|
|
|
|
duration: 4min
|
|
|
|
|
|
|
|
completed: 2026-05-15
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Phase 1 Plan 02: Wave-0 Test Infrastructure Summary
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Vitest 4.1.6 installed, vitest.config.ts wired for Node-environment tests, and four RED test files (ring-buffer, codec-check, handshake, port) committed against the not-yet-existing `src/offscreen/recorder.ts` — pinning the contracts Plans 03 and 04 must flip to GREEN.**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Performance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Duration:** ~4 min
|
|
|
|
|
|
|
|
- **Started:** 2026-05-15T15:21:24Z
|
|
|
|
|
|
|
|
- **Completed:** 2026-05-15T15:25:57Z
|
|
|
|
|
|
|
|
- **Tasks:** 5
|
|
|
|
|
|
|
|
- **Files modified:** 8 (1 modified existing + 6 created + 1 administrative revert)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Accomplishments
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Vitest 4.1.6 installed** as a devDependency; `node_modules/vitest/` materialized; `npx vitest --version` prints `vitest/4.1.6 linux-x64 node-v24.14.0`.
|
|
|
|
|
|
|
|
- **`vitest.config.ts` at repo root**: Node environment, scoped to `tests/**/*.test.ts`, typecheck disabled (separate `tsc --noEmit` runs in `npm run build`). No `globals: true`; tests explicitly `import { describe, it, expect } from 'vitest'`.
|
|
|
|
|
|
|
|
- **Four RED test files** all import from `'../../src/offscreen/recorder'` (which Plan 03 will create); `npx vitest run` reports `Test Files 4 failed (4); Tests 5 failed (5)`, every failure with `Error: Cannot find module '/src/offscreen/recorder' imported from ...`. This is the precise Nyquist TDD signal — contract pinned, implementation gap waiting to be filled.
|
|
|
|
|
|
|
|
- **`tests/fixtures/.gitkeep`** committed so the directory survives clean checkouts; Plan 07 drops a known-good `last_30sec.webm` there after the manual smoke pass.
|
|
|
|
|
|
|
|
- **Zero `as any` and zero `@ts-ignore`** across the four test files (verified by grep). The cast pattern is `as unknown as T`, which narrows progressively without bypassing the type-checker — CLAUDE.md compliant.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Task Commits
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Each task was committed atomically:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. **Task 1: Install Vitest, add npm test script** — `ebf015a` (test)
|
|
|
|
|
|
|
|
2. **Task 2: Create vitest.config.ts** — `57fa29e` (test)
|
|
|
|
|
|
|
|
3. **Task 3: Create tests/offscreen/ring-buffer.test.ts + tests/fixtures/.gitkeep** — `2e73a21` (test)
|
|
|
|
|
|
|
|
4. **Task 4: Create tests/offscreen/codec-check.test.ts** — `d7840a8` (test)
|
|
|
|
|
|
|
|
5. **Task 5: Create tests/offscreen/handshake.test.ts + tests/offscreen/port.test.ts** — `408aa33` (test)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Files Created/Modified
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- `vitest.config.ts` (12 lines) — defineConfig wrapper; Node env; include pattern; typecheck off.
|
|
|
|
|
|
|
|
- `tests/offscreen/ring-buffer.test.ts` (40 lines) — 4 RED tests for D-10 (header pinning) + D-11 (30 s trim).
|
|
|
|
|
|
|
|
- `tests/offscreen/codec-check.test.ts` (43 lines) — 2 RED tests for D-20 (strict-mode, no silent fallback).
|
|
|
|
|
|
|
|
- `tests/offscreen/handshake.test.ts` (67 lines) — 1 RED test for Pattern 4 (OFFSCREEN_READY emitted at module load after onMessage.addListener).
|
|
|
|
|
|
|
|
- `tests/offscreen/port.test.ts` (89 lines) — 2 RED tests for Pattern 5 / Pitfall 4 (port.connect on load + reconnect on onDisconnect).
|
|
|
|
|
|
|
|
- `tests/fixtures/.gitkeep` (0 bytes) — marker for the fixture dir.
|
|
|
|
|
|
|
|
- `package.json` — added `vitest@^4` devDep + `"test": "vitest run"` script.
|
|
|
|
|
|
|
|
- `package-lock.json` — regenerated by `npm install` (added 126 packages, 127 audited).
|
|
|
|
|
|
|
|
- `.planning/REQUIREMENTS.md` — reverted premature `[x]` + `Complete` marking on REQ-video-ring-buffer (deviation; see below).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Vitest RED Run Output (verbatim summary)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
RUN v4.1.6 /home/parf/projects/work/repremium
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FAIL tests/offscreen/ring-buffer.test.ts [ tests/offscreen/ring-buffer.test.ts ]
|
|
|
|
|
|
|
|
Error: Cannot find module '../../src/offscreen/recorder' imported from /home/parf/projects/work/repremium/tests/offscreen/ring-buffer.test.ts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FAIL tests/offscreen/codec-check.test.ts > codec strict mode > throws on unsupported vp9 and emits RECORDING_ERROR
|
|
|
|
|
|
|
|
Error: Cannot find module '/src/offscreen/recorder' imported from /home/parf/projects/work/repremium/tests/offscreen/codec-check.test.ts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FAIL tests/offscreen/codec-check.test.ts > codec strict mode > does not throw when vp9 IS supported
|
|
|
|
|
|
|
|
Error: Cannot find module '/src/offscreen/recorder' imported from /home/parf/projects/work/repremium/tests/offscreen/codec-check.test.ts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FAIL tests/offscreen/handshake.test.ts > OFFSCREEN_READY handshake > sends OFFSCREEN_READY after listener registration
|
|
|
|
|
|
|
|
Error: Cannot find module '/src/offscreen/recorder' imported from /home/parf/projects/work/repremium/tests/offscreen/handshake.test.ts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FAIL tests/offscreen/port.test.ts > port reconnect > connects on module load
|
|
|
|
|
|
|
|
Error: Cannot find module '/src/offscreen/recorder' imported from /home/parf/projects/work/repremium/tests/offscreen/port.test.ts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FAIL tests/offscreen/port.test.ts > port reconnect > reconnects when port disconnects
|
|
|
|
|
|
|
|
Error: Cannot find module '/src/offscreen/recorder' imported from /home/parf/projects/work/repremium/tests/offscreen/port.test.ts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Test Files 4 failed (4)
|
|
|
|
|
|
|
|
Tests 5 failed (5)
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Every failure is at the IMPORT step, not at the assertion step — that is the RED gate. Plans 03 and 04 flip these tests to GREEN by creating `src/offscreen/recorder.ts` with the contract specified in the plan's `<interfaces>` block.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Decisions Made
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Vitest at `^4`, not `@latest`:** Plan instructions said "pin Vitest at a major version" and the latest stable major on install day (2026-05-15) is 4.x (4.1.6); the 5.x line is still in beta. Pinning to `^4` gives deterministic resolution without locking us to a single patch.
|
|
|
|
|
|
|
|
- **No `vitest-chrome` package:** The four test files use a hand-rolled minimal chrome.runtime stub via interfaces, which is lighter than pulling a whole mock library for a four-file test setup. Aligns with threat T-1-NEW-02-01 (minimize supply chain).
|
|
|
|
|
|
|
|
- **`typecheck.enabled: false` in vitest.config.ts:** TypeScript checking via Vitest would duplicate the work `npm run build` already does via `tsc && vite build`. Faster feedback loop, single source of truth for the type errors.
|
|
|
|
|
|
|
|
- **`include: ['tests/**/*.test.ts']`:** Scoped strictly to `tests/`; production code under `src/` is never accidentally picked up as a test file even if a `*.test.ts` lands there.
|
|
|
|
|
|
|
|
- **No path aliases:** `tsconfig.json` does not define any; tests use relative imports (`'../../src/offscreen/recorder'`). Re-confirmed during review.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Deviations from Plan
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Auto-fixed Issues
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**1. [Rule 1 - Bug] Reverted premature REQ-video-ring-buffer marking left over from Plan 01-01**
|
|
|
|
|
|
|
|
- **Found during:** After Task 5, before SUMMARY (administrative correction noted in this plan's prompt context)
|
|
|
|
|
|
|
|
- **Issue:** Plan 01-01 (doc cascade) prematurely marked `REQ-video-ring-buffer` as `[x]` in `REQUIREMENTS.md` line 19 AND as `Complete` in the Traceability table at line 189. Per the Plan 01-01 SUMMARY frontmatter, it also listed `requirements-completed: [REQ-video-ring-buffer]`. This is incorrect — the requirement is satisfied by the implementation in Plans 03 (recorder) + 04 (handshake + port) and the ffprobe gate in Plan 07. The doc cascade in Plan 01-01 only amended decision/constraint wording.
|
|
|
|
|
|
|
|
- **Fix:** Reverted the two REQUIREMENTS.md markings:
|
|
|
|
|
|
|
|
- line 19: `- [x] **REQ-video-ring-buffer**: ...` → `- [ ] **REQ-video-ring-buffer**: ...`
|
|
|
|
|
|
|
|
- line 189: `| REQ-video-ring-buffer | Phase 1 | Complete |` → `| REQ-video-ring-buffer | Phase 1 | In Progress |`
|
|
|
|
|
|
|
|
- **Files modified:** `.planning/REQUIREMENTS.md`
|
|
|
|
|
|
|
|
- **Verification:** `grep '^- \[x\] \*\*REQ-video-ring-buffer\*\*' .planning/REQUIREMENTS.md` returns 0; `grep 'In Progress' .planning/REQUIREMENTS.md | grep REQ-video-ring-buffer` returns 1.
|
|
|
|
|
|
|
|
- **Committed in:** SUMMARY commit (administrative revert bundled with this plan's metadata commit, not a per-task commit, because it corrects prior-plan state outside the scope of any Task 1-5 file set).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**2. [Rule 3 - Blocking, micro] `package-lock.json` already existed despite plan wording**
|
|
|
|
|
|
|
|
- **Found during:** Task 1 (npm install)
|
|
|
|
|
|
|
|
- **Issue:** The plan's Task 1 action block said "If `npm install` produces a `package-lock.json` for the first time (it should — there is no committed lockfile today)". A `package-lock.json` was already in the working tree from a prior baseline commit (Plan 01-00 or earlier). This was not a blocker — `npm install` updated it in place (1186 line diff, refreshing transitive deps to add Vitest's tree).
|
|
|
|
|
|
|
|
- **Fix:** Committed the modified `package-lock.json` alongside `package.json` in Task 1's commit, as the plan instructed for the first-creation case. Behavior is identical.
|
|
|
|
|
|
|
|
- **Files modified:** None additional (already included in `ebf015a`).
|
|
|
|
|
|
|
|
- **Verification:** `test -f package-lock.json` returns 0; `grep '"vitest"' package-lock.json | head -1` returns the vitest entry.
|
|
|
|
|
|
|
|
- **Committed in:** `ebf015a` (Task 1 commit).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Total deviations:** 2 auto-fixed (1 prior-plan correction; 1 plan-wording mismatch with reality, no functional change).
|
|
|
|
|
|
|
|
**Impact on plan:** Both deviations are pure administrative cleanup; no code-path divergence from the plan. The REQUIREMENTS.md revert is critical to keep the requirements-traceability matrix honest — the in-progress REQ-video-ring-buffer status correctly reflects that the implementation lands in Plans 03/04/07.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Issues Encountered
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- None. The five tasks executed exactly as the plan specified. The RED gate fired as designed at every step; banned-pattern grep checks returned 0 every time; vitest config validation passed on first run.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## User Setup Required
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
None — no external service configuration required. Vitest is a pure dev-time dependency; tests run via `npm test` or `npx vitest run` on any machine that has cloned the repo and run `npm install`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Next Phase Readiness
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Plan 01-03 (offscreen recorder TDD) is unblocked.** It must export the following surface from `src/offscreen/recorder.ts` to flip the RED tests to GREEN:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
|
|
// Ring-buffer (pure functions; testable in Node)
|
|
|
|
|
|
|
|
export function addChunk(blob: { size: number }, timestamp: number): void;
|
|
|
|
|
|
|
|
export function trimAged(now: number): void;
|
|
|
|
|
|
|
|
export function getBuffer(): Array<{ data: { size: number }; timestamp: number; isFirst?: boolean }>;
|
|
|
|
|
|
|
|
export function resetBuffer(): void;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Codec strict-mode (D-20)
|
|
|
|
|
|
|
|
export function assertCodecSupported(): void; // throws Error("vp9 unsupported") + sendMessage({ type: 'RECORDING_ERROR' })
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Constants
|
|
|
|
|
|
|
|
export const VIDEO_BUFFER_DURATION_MS: number; // = 30_000
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Plan 01-04 (port keepalive + handshake) is also unblocked.** Its TDD contract: importing `src/offscreen/recorder.ts` MUST as a side-effect:
|
|
|
|
|
|
|
|
1. Call `chrome.runtime.onMessage.addListener(...)` at least once.
|
|
|
|
|
|
|
|
2. Call `chrome.runtime.sendMessage({ type: 'OFFSCREEN_READY' })` exactly once, AFTER step 1.
|
|
|
|
|
|
|
|
3. Call `chrome.runtime.connect({ name: 'video-keepalive' })` exactly once at module load.
|
|
|
|
|
|
|
|
4. On the connected port firing `onDisconnect`, immediately call `chrome.runtime.connect(...)` again (synchronous reconnect — Pitfall 4 mitigation).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **No outstanding blockers.** The doc cascade (Plan 01-01) is consistent; the test infrastructure (Plan 01-02) is wired and producing the expected RED signal; Plans 03 through 07 can execute in their declared waves.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Self-Check: PASSED
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
All claimed files exist on disk:
|
|
|
|
|
|
|
|
- `vitest.config.ts`, `tests/offscreen/{ring-buffer,codec-check,handshake,port}.test.ts`, `tests/fixtures/.gitkeep`, `package-lock.json`, `node_modules/vitest`, `01-02-SUMMARY.md` — all FOUND.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
All five task commits present in `git log`:
|
|
|
|
|
|
|
|
- `ebf015a` (Task 1: vitest install)
|
|
|
|
|
|
|
|
- `57fa29e` (Task 2: vitest.config.ts)
|
|
|
|
|
|
|
|
- `2e73a21` (Task 3: RED ring-buffer tests + fixtures)
|
|
|
|
|
|
|
|
- `d7840a8` (Task 4: RED codec-check tests)
|
|
|
|
|
|
|
|
- `408aa33` (Task 5: RED handshake + port tests)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
All plan-level verification gates pass:
|
|
|
|
|
|
|
|
- `npx vitest --version` → `vitest/4.1.6 linux-x64 node-v24.14.0`
|
|
|
|
|
|
|
|
- `ls tests/offscreen/*.test.ts | wc -l` → 4
|
|
|
|
|
|
|
|
- `npx vitest run 2>&1 | grep -cE "Failed to resolve|Cannot find module"` → 6 (one per test entry, 4 distinct files, all RED at module-resolution)
|
|
|
|
|
|
|
|
- `grep -cE "as any|@ts-ignore" tests/offscreen/*.test.ts` → 0 across all four files
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
*Phase: 01-stabilize-video-pipeline*
|
|
|
|
|
|
|
|
*Completed: 2026-05-15*
|