Skip to content

SOW-0001 Chunk 14: frontend scaffolding (Vite + React + TS SPA)#16

Merged
ktsaou merged 2 commits into
masterfrom
sow-0001-chunk-14-frontend-scaffold
May 28, 2026
Merged

SOW-0001 Chunk 14: frontend scaffolding (Vite + React + TS SPA)#16
ktsaou merged 2 commits into
masterfrom
sow-0001-chunk-14-frontend-scaffold

Conversation

@ktsaou
Copy link
Copy Markdown
Member

@ktsaou ktsaou commented May 28, 2026

Summary

Creates frontend/ — the React/TypeScript SPA served same-origin by ai-viewer-serve. Foundation only; Phase-1 pages (SessionsList, SessionDetail Overview/Logs, Sources) land next; Phase-2 routes are ComingSoon placeholders.

  • Stack (latest-stable-compatible, pinned): React 19, react-router 7, TanStack Query 5, Vite 8, TypeScript 6 (strict + noUncheckedIndexedAccess + exactOptionalPropertyTypes), Vitest 4 + RTL, Playwright (config), ESLint 9 + typescript-eslint (ESLint held at 9.x for eslint-plugin-react peer support — documented).
  • Theme: dark default + OS prefers-color-scheme + persisted manual override; pure resolveTheme + data-theme + no-flash inline script + aria-live.
  • Filters: URL-synced via React Router search params (useFilters) + FilterBar.
  • API: typed client over relative /api (error-envelope → ApiError, AbortSignal, HEAD/empty-body); types.ts mirrors the real backend wire JSON; TanStack Query hooks.
  • SSE: EventSource wrapper — POST /api/subscriptions/api/events?sub= → parse 5 frames → query invalidation → close()/DELETE; abort/unmount-safe at every timing (no leaked subscription or EventSource); malformed frames surfaced (no silent drop).

Same-origin (relative /api, no host hardcoded).

Review

Converged across 3 external-review rounds (codex + glm + minimax). Notable catches fixed: a connectSse unmount/abort leak race; the SSE client was untested and excluded from coverage (now tested at 98.68% and in the gate); and useStats(sessionId) hit /api/stats?session_id which the backend ignores (now cross-session-only; per-session aggregates come from /api/sessions/:id; ui-pages.md corrected).

Test plan

  • tsc --noEmit — 0 errors (strict + noUncheckedIndexedAccess + exactOptionalPropertyTypes)
  • eslint . --max-warnings 0 — 0 warnings
  • vitest run --coverage — 130 tests pass; ~97.6% lines / 93.3% branches (sse.ts 98.68%, stats.ts 100%)
  • vite build — main chunk 84.56 KB gzipped (budget ≤500 KB)
  • No secrets / no operator paths in frontend/ (relative /api)

ktsaou added 2 commits May 28, 2026 20:36
Creates frontend/ — the React/TypeScript single-page app served same-origin by
ai-viewer-serve. Foundation only; Phase-1 pages (SessionsList, SessionDetail
Overview/Logs, Sources) land next, Phase-2 routes are ComingSoon placeholders.

- Stack (pinned latest-stable-compatible): React 19, react-router 7, TanStack
  Query 5, Vite 8, TypeScript 6 (strict + noUncheckedIndexedAccess +
  exactOptionalPropertyTypes), Vitest 4 + RTL, Playwright (config), ESLint 9 +
  typescript-eslint (ESLint held at 9.x for eslint-plugin-react peer support).
- Theme: dark default + OS prefers-color-scheme + persisted manual override,
  pure resolveTheme + data-theme on <html> + no-flash inline script + aria-live.
- Filters: URL-synced via React Router search params (useFilters); FilterBar.
- API: typed client over relative /api (error-envelope -> ApiError, AbortSignal,
  HEAD/empty-body); types mirror the real backend wire JSON; TanStack Query hooks
  (useSessions/useSessionDetail/useStats[cross-session]/useSources/useHealth).
- SSE: EventSource wrapper — POST /api/subscriptions -> /api/events?sub= -> parse
  the 5 frames -> query invalidation -> close()/DELETE; abort/unmount-safe (no
  leaked subscription or EventSource at any timing); malformed frames surfaced.

Same-origin (relative /api; no host hardcoded). Gates: tsc 0, eslint 0, 130
vitest tests passing (sse.ts 98.68%, ~97.6% lines overall), vite build 84.56 KB
gzipped main chunk (budget 500 KB). Specs (frontend-architecture.md, ui-pages.md)
updated in lockstep; converged across 3 external-review rounds.
The CI frontend job runs `npm run e2e` whenever package.json defines an `e2e`
script; the scaffold defined one, but there are no E2E specs yet and the
playwright webServer (vite preview, with no prior build) timed out at 120s.
Remove the `e2e` script so CI skips the Playwright step until Chunk 18 adds
real specs + a webServer that boots the serve binary. Trim playwright.config.ts
to a skeleton (no webServer; baseURL :7710). Other frontend gates unchanged
(lint/typecheck/test/build green).
@ktsaou ktsaou merged commit 98dd2b4 into master May 28, 2026
5 checks passed
@ktsaou ktsaou deleted the sow-0001-chunk-14-frontend-scaffold branch May 28, 2026 17:50
ktsaou added a commit that referenced this pull request May 30, 2026
…, EndTs

Round-3 fixes completing the partially-fixed exec/patch enrichment and
web_search pairing plus two correctness gaps, all verified against the
real ~/.codex wire shapes.

- exec_command_end exit_code is now authoritative for op status in BOTH
  orders: exec-first applies at finalize; output-first emits a correcting
  OpFinalized(failed,command_failed) via the finalizedOps lookup (a
  non-zero exit no longer leaves a failed command marked completed).
- patch_apply_end is now order-independent (finalizedOps path) and merges
  patch_success/patch_status into op Extras; success=false -> failed.
- exec_duration_ms is now emitted (real duration is {secs,nanos} ->
  secs*1000 + nanos/1e6).
- web_search pairing uses a per-turn FIFO queue of open searches (oldest
  pairs with each web_search_end) so interleaved searches don't mis-pair;
  the end event's action object is now decoded and emitted alongside query.
- NativeID is taken from the authoritative session_meta.payload.id (the
  UUID parent_thread_id/forked_from_id reference); the filename UUID is
  only a fallback.
- old-format turn_context-only sessions now finalize their EOF turn at the
  turn's last-activity timestamp (deterministic), not the file mtime, so
  the golden is stable across runs; the new-format stale crash finalize
  still uses the stale mtime.

New fixtures l_exec_failed / n_patch_apply / m_multi_web_search /
o_payload_id_filename + regenerated f_exec_truncated / b_old_turncontext /
k_web_search, each line-checked against the spec and byte-identical across
repeated -update-golden runs. Spec pinned the order-independence + the
{secs,nanos} and {patch_success,patch_status} shapes (rules #14/#16/#23).
Gates green: golangci(0)/gosec(0)/vet; race 13/13; codex coverage 92.6%;
FuzzParseLine 0 crashes; secret + AI-attribution scans clean.
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