Add Phase 0 test harness (vitest + ESLint) and fix build config
- Add vitest unit tests for Zustand app store state transitions (6 tests) - Add mock speech-engine WebSocket protocol integration test (spawns server.py --mock on a free port) - Add ESLint 10 flat config with typescript-eslint; add typecheck/lint/test scripts to package.json - Fix pre-existing tsc -b breakage via noEmit in tsconfig.node.json; add dedicated tsconfig.tests.json project for Node-side tests - Track src-tauri/Cargo.lock for reproducible Rust builds; enable protocol-asset Tauri feature - Ignore generated artifacts (*.tsbuildinfo, src-tauri/gen/, emitted vite.config.js/.d.ts); remove stale emitted vite config copies - Update AGENTS.md agent guide (demo scope lock, roadmap, verification commands) - Add HANDOFF.md documenting Phase 0 state and remaining steps for the next agent
This commit is contained in:
154
Wireframes/Desktop/MeetVault-Desktop-Demo/HANDOFF.md
Normal file
154
Wireframes/Desktop/MeetVault-Desktop-Demo/HANDOFF.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Phase 0 Handoff — MeetVault Desktop Demo
|
||||
|
||||
Session handoff for the **Phase 0 (test harness + runtime verification)** work defined in `AGENTS.md` §14.
|
||||
Read `PROJECT_SCOPE.md` and `AGENTS.md` first; this file is a continuation note, not a substitute.
|
||||
|
||||
**Delete or replace this file when Phase 0 is complete.**
|
||||
|
||||
---
|
||||
|
||||
## 1. Current verification status (verified 2026-10-03)
|
||||
|
||||
| Check | Command | Status | Detail |
|
||||
|---|---|---|---|
|
||||
| Typecheck | `npm run typecheck` | **RED** | 3 errors, all in `tests/speechProtocol.mock.test.ts`: TS2322 line 111 (`ChildProcessByStdio<null, Readable, Readable>` not assignable to `ChildProcessWithoutNullStreams`), TS18048 lines 115/116 (`server` possibly undefined) |
|
||||
| Lint | `npm run lint` | **RED** | 3 errors: `react-hooks/exhaustive-deps` rule definition not found ×2 (`src/pages/LiveMeeting.tsx` lines 51, 70 — pre-existing disable comments; plugin installed but NOT wired into config), and `preserve-caught-error` at test line ~120 (rethrow without `{ cause }`) |
|
||||
| Tests | `npm test` | **RED** | Unit: `src/store/useAppStore.test.ts` **6/6 pass** (with stderr noise — see §4.3). Integration: `tests/speechProtocol.mock.test.ts` **2/5 pass**; failures: close code got 1006 expected 1000; "rejects unsupported sample rates" and "reports malformed config as an error event" both time out waiting for the error event |
|
||||
| Rust | `cargo check --manifest-path src-tauri/Cargo.toml` | **RED** | All deps compile (MSVC toolchain works). Fails only in tauri-build: `` `src-tauri/icons/icon.ico` not found; required for generating a Windows Resource file during tauri-build`` |
|
||||
| Python | `.venv\Scripts\python.exe -m py_compile speech-engine/server.py` | **GREEN** | No changes to server.py planned in Phase 0 |
|
||||
|
||||
---
|
||||
|
||||
## 2. Completed work this session (done and verified)
|
||||
|
||||
1. **Removed `speech-engine/fresh_venv/`** — it was Python 3.14, incompatible with diart. The working venv is `speech-engine/.venv` (Python 3.12).
|
||||
2. **Fixed pre-existing build breakage**: added `"noEmit": true` to `tsconfig.node.json`. `tsc -b` now works; the composite+noEmit combo was verified.
|
||||
3. **Installed test/lint devDeps** (lockfile updated): vitest 3.2.7, eslint 10.12, @eslint/js ^10, typescript-eslint 8.71, @types/node ^26, **eslint-plugin-react-hooks ^7.1.1**.
|
||||
4. **Created the harness**:
|
||||
- `eslint.config.js` — ESLint 10 flat config: `@eslint/js` recommended + `typescript-eslint` recommended, scoped to `**/*.{ts,tsx}` via `.map()`; ignores dist/node_modules/target/.venv. (react-hooks plugin NOT yet wired in.)
|
||||
- `vitest.config.ts`, `tsconfig.tests.json` (ES2022, no DOM lib, `types: ["node"]`; includes `tests/**/*.ts` + `vitest.config.ts`) referenced from root `tsconfig.json`.
|
||||
- `src/test/setup.ts` — in-memory localStorage stub for zustand persist under Node. **Not working as intended; see §4.3.**
|
||||
- `src/store/useAppStore.test.ts` — 6 unit tests (createMeeting, appendSegment add/replace, renameSpeaker trim/fallback, finishMeeting commit, setSettings merge). All pass.
|
||||
- `tests/speechProtocol.mock.test.ts` — 5 integration tests; spawns `.venv\Scripts\python.exe speech-engine/server.py --mock --port <free-port>` (skips if venv missing), drives it with Node's native WebSocket + fetch.
|
||||
5. **package.json scripts added**: `typecheck` (`tsc -b`), `lint` (`eslint .`), `test` (`vitest run`), `test:watch` (`vitest`).
|
||||
6. **Toolchain unblocked**: installed VS C++ BuildTools workload; MSVC 14.51.36231 at `C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools`. cargo check compiles all dependencies successfully — only the icon step fails.
|
||||
7. **Verified `.venv` versions** (within requirements pins): fastapi 0.141.1, starlette **1.7.0**, uvicorn 0.54.0, pydantic 2.13.5.
|
||||
|
||||
---
|
||||
|
||||
## 3. Remaining work — do in this order
|
||||
|
||||
### Step 1 — Create the placeholder icon (unblocks cargo check)
|
||||
|
||||
The archive shipped without `src-tauri/icons/`; git confirms it was never tracked (`git ls-files src-tauri/icons/*` is empty). `tauri.conf.json` has no explicit icon array, so tauri-build requires the default `icons/icon.ico` on Windows.
|
||||
|
||||
```powershell
|
||||
New-Item -ItemType Directory "src-tauri\icons" # Node writeFileSync does NOT create parent dirs
|
||||
node C:\Users\supersanta\AppData\Local\Temp\opencode\make-ico.cjs "D:\workspace\MeetVault\Wireframes\Desktop\MeetVault-Desktop-Demo\src-tauri\icons\icon.ico"
|
||||
```
|
||||
|
||||
`make-ico.cjs` is a one-off generator in the temp dir (writes a 1150-byte solid-blue 16×16 32bpp ICO; takes output path as `argv[2]`). If it's gone, recreate it or hand-write any valid minimal ICO. **Commit the generated `icon.ico`** — the build requires it and it was never tracked.
|
||||
|
||||
Then re-run `cargo check --manifest-path src-tauri/Cargo.toml` → expect GREEN.
|
||||
|
||||
### Step 2 — Settle ground truth for WS teardown before touching test assertions
|
||||
|
||||
Facts verified by reading `speech-engine/server.py` (ws handler, lines ~318–415):
|
||||
|
||||
- **No explicit `websocket.close()` anywhere in the handler.** Both error paths do `send_json({"type":"error", ...})` then `return`; starlette tears down when the handler returns. Observed close code is **1006** (abnormal, no close frame) — consistent with starlette 1.7.0 behavior.
|
||||
- Bad sample rate → error message exactly: `"MeetVault speech engine currently requires 16 kHz mono PCM."` then return.
|
||||
- Malformed JSON hits the generic `except Exception` (~line 391): tries `send_json({"type":"error","message":str(exc)})`, swallows send failures, then `session.close()`.
|
||||
- Yet in tests those error events **never arrive** (client times out). Hypothesis: starlette drops/loses the pre-close send during teardown. Unconfirmed.
|
||||
|
||||
Do one of these to confirm before editing assertions:
|
||||
|
||||
1. Read `.venv\Lib\site-packages\starlette\websockets.py` (line 39+) for what happens on handler return, or
|
||||
2. Manual probe: start `server.py --mock --port <free>`, connect with a small Node WebSocket script, send (a) bad sample_rate config, (b) malformed JSON, (c) valid config + stop after finals; log every frame and the close code for each.
|
||||
|
||||
Then align the three failing assertions to actual behavior. If 1006/no-close-frame is confirmed, accept it in tests **with a comment** documenting that an explicit close/ready handshake is Phase 3 protocol debt (AGENTS.md §9 P1 "speech readiness/protocol"). Do NOT change `server.py` behavior in Phase 0 — tests only.
|
||||
|
||||
### Step 3 — Fix `tests/speechProtocol.mock.test.ts`
|
||||
|
||||
- Line ~6: import `ChildProcessByStdio` from `node:child_process` and `Readable` from `node:stream`.
|
||||
- Line ~104: `let server: ChildProcessByStdio<null, Readable, Readable> | undefined;` — fixes TS2322 (line 111) and both TS18048s (lines 115/116).
|
||||
- Line ~120: attach `{ cause: error }` to the rethrown `Error(...)` in `beforeAll` (ESLint 10 `preserve-caught-error`).
|
||||
- Close-code assertion per Step 2 findings.
|
||||
|
||||
### Step 4 — Wire eslint-plugin-react-hooks into `eslint.config.js`
|
||||
|
||||
Plugin is installed (`^7.1.1`) but absent from the config, so the pre-existing disable comments in `src/pages/LiveMeeting.tsx` (lines 51, 70) fail with "Definition for rule ... was not found". Check the v7 export shape first (`node_modules/eslint-plugin-react-hooks/package.json` + main entry — v7 is ESM), then add a flat-config entry registering the plugin with `rules-of-hooks: error` and `exhaustive-deps: warn` (or error). Keep the disable comments in LiveMeeting.tsx as-is; once the rule exists they become valid.
|
||||
|
||||
### Step 5 — Fix `src/test/setup.ts` localStorage stub
|
||||
|
||||
Current state: gated on `typeof globalThis.localStorage === 'undefined'`. Under vitest workers the gate passes (Node 26's getter returns undefined while emitting an ExperimentalWarning) yet zustand persist still logs `[zustand persist middleware] Unable to update item 'meetvault-demo-store-v1', the given storage is currently unavailable.` per state update. **Root cause undiagnosed.**
|
||||
|
||||
Planned fix: drop the gate — unconditionally `Object.defineProperty(globalThis, 'localStorage', { value: new MemoryStorage(), configurable: true, writable: true })`, then add a functional probe (setItem/getItem roundtrip) so a silent no-op is impossible. Verify success by running `npm test` and confirming BOTH the ExperimentalWarning lines AND all "storage is currently unavailable" messages are gone from output.
|
||||
|
||||
### Step 6 — Clean up stray artifacts
|
||||
|
||||
- **Delete `vite.config.js` and `vite.config.d.ts`.** Verified: `vite.config.js` is a tsc-emitted duplicate of `vite.config.ts` (identical config values, 4-space indent). Vite resolves `.js` before `.ts`, so the stale file can shadow the real one. The real config is `vite.config.ts`; keep it.
|
||||
- **`.gitignore`**: add `*.tsbuildinfo` (3 stray files at repo root: tsconfig.app/node/tests.tsbuildinfo) and `src-tauri/gen/` (tauri-build generated). Keep `src-tauri/Cargo.lock` tracked — this is an application, lockfile should be committed.
|
||||
|
||||
### Step 7 — Full verification (all four must be green)
|
||||
|
||||
```powershell
|
||||
npm run typecheck
|
||||
npm run lint
|
||||
npm test
|
||||
cargo check --manifest-path src-tauri/Cargo.toml
|
||||
```
|
||||
|
||||
Plus the mock-demo smoke path still works: `python speech-engine/server.py --mock` + `npm run desktop` (or at minimum `npm run dev`). AGENTS.md §15: do not claim completion without stating which checks actually ran.
|
||||
|
||||
### Step 8 — Documentation
|
||||
|
||||
- **Create `docs/SMOKE_TEST.md`**: the manual Tauri acceptance walkthrough from AGENTS.md §7 as a checklist (mock mode first; mic permission, levels, transcript segments, speaker rename main↔overlay, pause/resume, finish → Meeting Details playback + transcript, exact placeholder text on backend buttons, restart persistence).
|
||||
- **Update `README.md`**: add the new commands (`typecheck`, `lint`, `test`) and normalize the Python version note to 3.12 (README still says 3.10/3.11 — documented drift per AGENTS.md §6.1; the working venv is 3.12).
|
||||
- **Update `AGENTS.md`** (at workspace root `D:\workspace\MeetVault\AGENTS.md`, tracked in git): §6.6 verification commands now include typecheck/lint/test; repo map gains `tests/`, `src/test/`, `eslint.config.js`, `vitest.config.ts`, `tsconfig.tests.json`; note the placeholder icon situation if relevant.
|
||||
- **Delete this HANDOFF.md** (or replace with a one-line "Phase 0 complete" note).
|
||||
|
||||
---
|
||||
|
||||
## 4. Verified ground truth & environment notes
|
||||
|
||||
### 4.1 Mock speech protocol (`speech-engine/server.py`)
|
||||
|
||||
- `/health` → `{ok: true, mode: "mock", sample_rate: 16000}` (real mode reports `"real-capable"`).
|
||||
- WS endpoint `ws://127.0.0.1:<port>/ws/live`; default port 8765; test picks a free port via net server on port 0.
|
||||
- Flow: accept → first text frame is JSON config (`model`, `compute`, `language`, `diarization`, `sample_rate`) → `ready` event. Mock ready payload: `{"type":"ready","engine":"mock-whisper","diarization":"mock-diart"}` (real mode: engine `"Whisper {model}"`).
|
||||
- Mock emits one **final** segment per 38400 samples (2.4 s of 16 kHz int16); speaker cycle `speaker_1 → speaker_2 → speaker_1 → speaker_3`. Test streams 5 s of silence in 3200-byte chunks and expects finals at ~[0.3–2.4] and ~[2.7–4.8].
|
||||
- Client sends `{"type":"stop"}` to end; handler has no explicit close (see Step 2).
|
||||
|
||||
### 4.2 Toolchain / environment
|
||||
|
||||
- Node **v26.10.0**: global `WebSocket` + `fetch` available natively (used by the integration test — no extra deps).
|
||||
- MSVC **14.51.36231** at `C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools` (VS BuildTools install from this session). cargo check works with it.
|
||||
- `.venv`: Python 3.12, fastapi 0.141.1, starlette 1.7.0, uvicorn 0.54.0, pydantic 2.13.5.
|
||||
- **PowerShell quirks** (this shell is Windows PowerShell 5.x): no `&&` separator — use separate commands or `;`; call operator `& "path"` for paths with spaces; run vitest via `node_modules\.bin\vitest.cmd`; esbuild binary lives at `node_modules\@esbuild\win32-x64\esbuild.exe`, NOT in `.bin`; piping stderr (`*>&1`) produces cosmetic NativeCommandError noise — the command still ran fine.
|
||||
- **ESLint 10**: flat config only; recommended set includes `preserve-caught-error` (rethrows must attach `{ cause }`).
|
||||
|
||||
### 4.3 Known test-output noise (to be fixed in Step 5)
|
||||
|
||||
Unit tests pass but emit, per worker: `(node:...) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.` and one `[zustand persist middleware] Unable to update item 'meetvault-demo-store-v1', the given storage is currently unavailable.` line per state update. Both should disappear once the setup.ts stub actually takes effect.
|
||||
|
||||
### 4.4 Working-tree changes that predate or accompany this session (keep, don't revert)
|
||||
|
||||
- `src-tauri/Cargo.toml`: tauri feature list changed to `["protocol-asset"]` (from earlier work; needed for asset protocol).
|
||||
- Workspace-root `AGENTS.md` (`D:\workspace\MeetVault\AGENTS.md`): rewritten agent guide (82 → 944 lines); it is the current operating guide.
|
||||
|
||||
---
|
||||
|
||||
## 5. Constraints (from AGENTS.md / PROJECT_SCOPE.md — do not violate)
|
||||
|
||||
- Keep mock-mode desktop operation working at every checkpoint; no half-finished architecture states.
|
||||
- Placeholder text must remain exactly: `Processing (The job). Please wait a moment`
|
||||
- `tsconfig.app.json` has `strict: true` — never weaken TypeScript strictness to silence an error.
|
||||
- No new **runtime** dependencies (test devDeps are fine and already installed).
|
||||
- Do not invent backend endpoints, credentials, auth behavior, or billing rules; keep `src/lib/backend.ts` a placeholder.
|
||||
- Do not edit generated/dependency dirs: `node_modules/`, `speech-engine/.venv/`, `dist/`, `src-tauri/target/`.
|
||||
- **No commits** — all changes stay uncommitted unless the user asks otherwise.
|
||||
|
||||
## 6. Git snapshot (as of handoff, branch `main`)
|
||||
|
||||
Modified: `AGENTS.md` (workspace root), `package.json`, `package-lock.json`, `src-tauri/Cargo.toml`, `tsconfig.json` (+tests project reference), `tsconfig.node.json` (+noEmit)
|
||||
Untracked: `eslint.config.js`, `vitest.config.ts`, `tsconfig.tests.json`, `src/test/`, `tests/`, `src/store/useAppStore.test.ts`, `HANDOFF.md` (this file), stray `vite.config.js` + `vite.config.d.ts` (delete in Step 6), `*.tsbuildinfo` ×3, `src-tauri/Cargo.lock` (keep tracked), `src-tauri/gen/` (gitignore)
|
||||
Reference in New Issue
Block a user