Skip to content

Release/v1.0.0#15

Merged
tpikachu merged 9 commits into
masterfrom
release/v1.0.0
Jun 25, 2026
Merged

Release/v1.0.0#15
tpikachu merged 9 commits into
masterfrom
release/v1.0.0

Conversation

@tpikachu

Copy link
Copy Markdown
Owner

No description provided.

tpikachu and others added 9 commits June 25, 2026 07:20
…ffer

Phase 1 of broader test coverage. The DB layer (better-sqlite3) is built for
Electron's ABI and can't load under vitest's node env, so these mock the DB/OpenAI
boundaries with vi.mock and test the pure logic + edge cases (40 new tests, 20→60):

- models: preset resolution + override precedence, isReasoningModel, reasoningParam
  (only attached to reasoning models), reasoningEffort defaults, Custom-divergence inputs.
- questions (classify): defensive defaulting (fails closed on uncertainty), input-text
  echo, malformed-JSON behavior.
- answer: per-length max_output_tokens caps (220/800), prompt assembly (format, type,
  pronunciation, context tagging, no-context note), streamed delta/usage/meta events.
- codingMode: multi-image buffer cap at 8 (drops oldest), clear, and solve-all-then-clear.

Also: vitest.config now mirrors the tsconfig path aliases (@shared/@main/@Renderer)
so modules with value imports from @shared (e.g. EVENTS) are testable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
More Phase 1 coverage (60→77 tests):
- parsing: resume/JD/company defensive defaulting (empty → safe defaults, partial →
  fill rest, overview defaults to "" not []), and the 24k input cap sent to the model.
- Extract the Cue Card answer-history logic out of Overlay.tsx into answerCards.ts
  (makeCard/patchLast/addCard/removeCard/toggleCollapsed) so it's unit-testable, and
  cover the edge cases: history OFF replaces, history ON collapses + keeps prior
  (answers preserved), patchLast no-ops on empty + doesn't mutate, remove/toggle by id.
  Overlay.tsx now imports these (behavior unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 2 foundation — runs against the BUILT app (real main + SQLite + IPC) for what
unit tests can't reach. You run it locally; CI-safe without a key.

- playwright.config.ts + e2e/fixtures.ts: launch out/main/index.js via _electron,
  resolve the dashboard window (the one without ?view=), auto-skip the first-run tour,
  load .env, and a key-gated `hasKey`/`setApiKey` helper.
- e2e/smoke.spec.ts: app launches, dashboard renders, navigation.
- e2e/data-integrity.spec.ts (no key): drives window.api against the real DB —
  interview-delete FK cascade (the flagged gap), profile-delete cascade, and a model
  preset + per-task override round-trip.
- e2e/live-openai.spec.ts (key-gated): load samples → real résumé parse + embeddings +
  RAG retrieval; structural assertions only.
- src/main/index.ts: honor E2E_USER_DATA so tests use an isolated data dir (never the
  real user DB). No effect in normal use.
- package.json: @playwright/test + dotenv devDeps, test:e2e / test:e2e:only scripts.
- e2e/README.md: setup, tiers, coverage, and gotchas. gitignore Playwright output.

NOTE: e2e specs are written but NOT yet executed here (no display/network in this env).
Run `npm install && npx playwright install && npm run test:e2e` and we iterate on any
selector/timing failures from the output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ran the suite and fixed what was broken:

- Playwright's _electron.launch() is broken on Electron 30+ (passes
  --remote-debugging-port=0, which Electron rejects — microsoft/playwright#39008).
  Replace it: the app opens a fixed CDP port via appendSwitch under BRAINCUE_E2E
  (src/main/index.ts), and the fixture spawns out/main/index.js directly + connects
  with chromium.connectOverCDP.
- global-setup copies drizzle/ → out/main/drizzle so the built app finds its
  migrations (else: "no such table: settings"). electron-builder does this when
  packaging; a bare out/ run didn't.
- Strip ELECTRON_RUN_AS_NODE from the spawned env (the Playwright runner sets it,
  which made Electron boot as plain Node → electron.app undefined).
- Skip the guided tour by setting the flag then reloading (it auto-starts on mount
  and its full-screen overlay intercepted clicks).
- Await Electron exit in teardown so the shared CDP port frees before the next test
  (fixes intermittent "target closed").
- Fix selectors/assertions: heading-scoped Settings checks; live test asserts via
  exposed APIs (loadSamples + reindexProfile) since window.api.rag isn't in the preload.

Result: 7/7 pass — smoke + data-integrity (incl. FK cascade) and the live OpenAI tier
(real résumé parse + embeddings, ~31s with the .env key). 77 unit tests still green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two utilities, excluded from the default run (testIgnore *.capture.spec.ts; opt in
with E2E_CAPTURE=1):
- screenshots.capture.spec.ts — drives the app to generate README screenshots.
- audit.capture.spec.ts — logs console/page errors per route + first-run/empty-state
  snapshots (used to verify the app boots clean).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The two most likely real-world live failures were invisible and left the Cue Card
stuck:

- B1: when a detected question's answer stream threw (auth, quota, network, or a
  model-not-found), generateAnswer re-threw with only a log — no sessionError, and
  answerDone never fired, so the card spun forever. Now the non-abort catch broadcasts
  sessionError (clean, key-redacted) + answerDone before re-throwing. Also moved
  retrieve() (the embeddings call) inside the try so a failure there is surfaced too.
- B2: a clean transcription WebSocket close (server drops after 401/quota, or a network
  drop with no 'error' event) only logged a warning while the mic kept streaming into a
  dead socket. Now it reports onError → the user is told to restart.

Regression test (e2e/error-handling.spec.ts): launches with the key stripped (new
noApiKey fixture option) so the first OpenAI call fails deterministically offline, and
asserts both sessionError AND answerDone fire — i.e. the failure surfaces and the card
stops spinning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cleanup + GA polish for v1.0:

- Remove the always-empty "expanded mode" meta panel (talking points / resume match /
  follow-up) from the Cue Card — that data was a permanent stub, so it rendered nothing.
  The streamed answer, risk warning, transcript, and audio meter stay.
- Remove the unwired `rag:search` channel (constant + IPC-map section): it had no
  handler and no preload method.
- a11y: Cue Card icon buttons get aria-labels; the Modal sets role="dialog"/aria-modal,
  moves focus in and restores it on close; the Ask box ignores Enter mid-IME-composition.
- MockPage: inline error banner instead of blocking alert()s.
- Coding-solve errors use normalizeOpenAIError (clean, key-redacted) not String(e).
- HiDPI screen capture: factor in scaleFactor so small code text stays legible.
- Cap the in-memory transcript (store + overlay) so long sessions don't grow unbounded.
- CI: add a typecheck · test · build gate on push/PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First stable release. Bump package.json 0.9.0 → 1.0.0, add the MIT LICENSE file, and
changelog/1.0.0.md (drives in-app "What's New" + APP_VERSION). Theme: reliability —
live failures now surface instead of hanging — plus a11y/UX polish and dead-code cleanup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A multi-agent review of the diff caught a real regression + polish items:

- BLOCKER: the new Modal focus effect depended on `onClose` (a fresh inline arrow each
  render), so during a live session the Cue Card's per-frame re-renders (audio meter)
  re-ran it and yanked focus out of the settings dropdowns — and corrupted the
  focus-restore target. Now it reads onClose via a ref and depends only on `open`, so
  focus-in/restore happens once per open/close.
- SHOULD: the realtime ws fires 'error' THEN 'close' for the same failure, so the new
  close→onError clobbered the specific message (e.g. 401) with the generic
  "disconnected" one. Now close skips it when a specific error already surfaced.
- Nits: failed manual ask no longer leaves an unhandled rejection (renderer .catch) and
  keeps its transcript line; removed stale `rag:search` references in docs/09-MVP-PLAN.md
  and e2e/README.md; collapsed a leftover double blank line.

typecheck · 77 unit · build · 7 e2e all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tpikachu tpikachu merged commit e3b750f into master Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant