---
kind: record
status: active
---

# WebUI Phase P — P4-1 Projects + Tasks SPA Scratchpad

**Task ID:** P4-1
**Tracking ref:** Phase P RFC §6.4 / §2.4 author brief; no provider issue supplied
**Mission context:** `mvp-20260312` (active)
**Branch:** `feat/webui-p4-1` from `origin/next`
**Started:** 2026-08-11
**Role:** orchestrator-controlled author worker; `docs/TASKS.md` remains orchestrator-only and is not part of this increment

## Original tasking

Implement the bounded P4-1 SPA parity slice exactly as briefed: real authenticated `/projects`, `/projects/:id`, and `/tasks` React Router pages ported from the existing Next baseline; reuse existing project/task components; preserve relative same-origin REST access; support only the existing project/task PATCH edit flows; add route error boundaries and page tests; pin Vite strict port 3100 and legacy Next dev/start to 3101; make no gateway, package, dependency, nav-shell, chat, settings, or admin changes; verify, commit, and push only `feat/webui-p4-1` (no PR or merge). If any required REST endpoint is absent, stop as `BLOCKED:` rather than inventing a workaround.

## Objective and acceptance map

- A: `/projects` list with loading, cards, empty, and surfaced API-error states; defer MissionStatus side panel.
- B: `/projects/:id` detail with overview/tasks/missions tabs, parallel project/mission/task load, bounded project and task PATCH edits.
- C: `/tasks` list/kanban with bounded task PATCH edits.
- D: replace the three route placeholders and add route error boundaries without changing chat.
- E: use only `@/lib/types` and `@/lib/api`; relative `/api/...` REST paths only.
- F: Vite `strictPort: true`; Next dev/start pinned to 3101; no proxy/dependency changes.
- G: fixture-backed Vitest coverage for lists, states, tabs/toggles, PATCH calls, and real route elements; all named verification gates pass.

## Plan

1. Verify the required gateway REST routes and inspect the exact `origin/next` SPA, Next baseline pages, shared components, types, API helper, and P3 test conventions.
2. Record the pre-change web test count and add required page/route tests first, observing expected RED failures.
3. Port the three pages and shared route error boundary, then wire routes and bounded PATCH flows.
4. Apply only the two dev-port pin changes.
5. Run focused tests, all user-required gates, lockfile and same-origin checks, and `git diff --check`.
6. Obtain independent spec/code/security review; remediate and repeat affected gates until clear.
7. Commit conventionally, run the required pre-push queue guard, push only the feature branch, and record exact evidence here.

## Testing strategy

TDD is applied because this adds user-visible data/edit behavior. Component tests mock only the existing API boundary and exercise rendered behavior and PATCH payloads. Primary situational evidence is the required page interaction suite plus route-resolution checks; baseline evidence is typecheck, lint, full web test, build, frozen install, formatting/diff hygiene, and same-origin grep.

## Budget

No explicit token cap was supplied. Working soft cap: **50K tokens**, derived from three coupled React pages, route/error wiring, interaction tests, port config, review/remediation, and full verification. One Codex implementation worker and independent review workers will be used serially to avoid worktree collisions.

## Base evidence

- `git fetch origin` rc=0.
- `origin/next` and branch start: `e00cc475a2b1e9866bd4e2f8df80aff640c3a543`.
- Branch created: `feat/webui-p4-1` tracking `origin/next`.
- Harness-owned `.mosaic/orchestrator/session.lock` is dirty and must remain unstaged/uncommitted.

## Progress / evidence

- [x] Loaded active mission manifest, latest scratchpad, top-level tasks, PRD, orchestration/delivery/frontend/QA/documentation/review/TypeScript guides, and matching implementation skills.
- [x] Confirmed exact base SHA and created the feature branch.
- [x] Required REST endpoints verified in Gateway source: project list/detail/PATCH, task list/filter/detail/PATCH, and mission list all exist.
- [x] Scope assumption check found a blocking contradiction before source implementation.
- [ ] Pre-change web test count recorded.
- [ ] RED tests observed.
- [ ] Implementation complete.
- [ ] Independent review clear.
- [x] Required verification gates run against the unchanged web baseline; exact leak-grep expectation is independently blocked by 12 pre-existing matches.
- [x] Blocker record committed as `5ede86a5` and feature branch pushed; no PR opened and no merge performed.

## Blocker — 2026-08-11

`P4-1` is blocked because the bounded edit UX asserted by the brief does not exist at the confirmed `origin/next` base (`e00cc475`):

- `apps/web/src/components/tasks/task-detail-modal.tsx` is read-only. Its props are only `task` and `onClose`; it contains no input/select/textarea, update callback, or `api()`/PATCH call.
- `apps/web/src/app/(dashboard)/projects/[id]/page.tsx` contains no project edit controls and no project/task PATCH call.
- `apps/web/src/app/(dashboard)/tasks/page.tsx` does not open `TaskDetailModal`; its click handler only logs that a future detail view will be added.
- A repository grep over those reference/component paths finds zero `method: 'PATCH'` calls and zero edit/save form controls.
- The required leak grep also disproves the brief's baseline claim that `apps/web/src` is clean: the exact command returns rc=0 with 12 pre-existing matches at `origin/next` (external font links, an avatar placeholder, an SVG namespace, and test fixtures). There are zero new matches in the worktree, but making the exact grep empty would require unrelated changes the brief forbids.

The brief simultaneously requires tests proving task/project PATCH edits and forbids inventing edit UX, fields, or flows beyond what those references expose. Adding controls or callback contracts would therefore be new design outside the authorized port/wiring scope. Per the explicit stop rule, no SPA source, route, config, package, lockfile, or test implementation was started.

## Verification evidence on unchanged web baseline

These gates were still run to distinguish the brief contradiction from a broken base:

- `pnpm --filter @mosaicstack/web typecheck` — rc=0.
- `pnpm --filter @mosaicstack/web lint` — rc=0.
- `pnpm --filter @mosaicstack/web test` — rc=0; 15 files, 133 tests passed. Before→after remains 133→133 because implementation was halted.
- `pnpm --filter @mosaicstack/web build` — rc=0; Next production build completed, 10/10 static pages generated.
- `pnpm install --frozen-lockfile` — rc=0; lock SHA-256 remained `9acaa89d213b3281e757b6edf6fdb8727176570d725b78a0de234c61a7f3c332`; `git diff` for the lockfile rc=0.
- `git grep -nE "http://|https://" -- apps/web/src` — rc=0 with 12 pre-existing matches; `origin/next` count=12, current HEAD count=12, new worktree additions=0.
- `pnpm format:check` — rc=0.
- `git diff --check` — rc=0.

## Delivery evidence

- Initial queue-guard invocation through shell `~` failed rc=127 because this Distrobox resolves `HOME` to `/home/jwoltje/distrobox-homes/mosaic-dev`, where the injected fleet status already reports the tools installation missing.
- Correct supported host-tool invocation `/home/jwoltje/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push -B feat/webui-p4-1` — rc=0; branch absent remotely and queue clear.
- `git push -u origin feat/webui-p4-1` — rc=0. The push hook additionally ran repository preflight, typecheck (45/45 tasks), lint (25/25 tasks), and format check successfully.
- No PR was opened and no merge was attempted, per the brief.

## Risks / blockers

- The active harness mutates `.mosaic/orchestrator/session.lock`; it is excluded from staging.
- The Phase P §6.4 prose is absent from this checkout, so the brief-named route spec and existing Next pages are the bounded implementation anchors.
- The MissionStatus `/api/coord/status` panel is explicitly deferred to a follow-up and must not enter P4-1.

## REV 2 continuation — 2026-08-10T21:59:17-05:00

REV 2 supersedes the original tasking above. The independent check confirmed that the Next reference pages and `task-detail-modal.tsx` are read-only, so project/task editing is deliberately deferred to P4-1b. Do not re-litigate or implement the former PATCH requirements.

### Revised objective and acceptance map

- A: `/projects` read-only SPA list with loading, cards, empty, and surfaced API-error states; card navigation to `/projects/:id`; no MissionStatus panel.
- B: `/projects/:id` read-only SPA detail using `useParams`, `useNavigate`, and the specified three-request `Promise.all`; preserve reference overview/tasks/missions tabs and read-only task modal.
- C: `/tasks` read-only SPA list/kanban view with the existing read-only task modal.
- D: replace only the three route placeholders and add page-local route error boundaries without modifying chat.
- E: use only `@/lib/types` and `@/lib/api` with relative `/api/...` REST paths.
- F: add Vite `strictPort: true` and pin legacy Next dev/start to 3101 without dependency or proxy changes.
- G: add fixture-backed page tests and route-resolution assertions; run every user-specified verification gate and the narrowed new-code origin check.

### Revised plan

1. Reset `feat/webui-p4-1` to the exact `origin/next` base and independently verify the read-only reference/component/API assumptions.
2. Record the pre-change web suite count; write and run focused page/route tests first to observe expected RED failures.
3. Port the three read-only pages, add independent route error boundaries, wire routes, and apply only the two dev-port pins.
4. Run focused tests, full required web gates, frozen install/lockfile proof, origin check, format/diff hygiene, and accessibility/state-transition sanity checks.
5. Obtain independent spec/code/security review, remediate every blocker, and repeat affected gates.
6. Commit conventionally, run the supported pre-push queue guard, and push only `feat/webui-p4-1`; no PR and no merge.

### Revised budget and session state

- No explicit token cap was supplied. Working soft cap remains **50K tokens**.
- TDD is required by the user and frontend skill for the new SPA behavior; tests must fail for missing pages before production implementation.
- Documentation assessment: this is a parity port of existing read-only behavior, not a new public workflow or API contract; the task scratchpad is the required delivery record, with no user/developer/API documentation changes in this bounded increment.
- Exact base confirmed after fresh fetch/reset: `e00cc475a2b1e9866bd4e2f8df80aff640c3a543`.
- Remote `feat/webui-p4-1` is absent; the eventual push is a fresh branch creation.
- Harness-owned `.mosaic/orchestrator/session.lock` remains excluded from staging.

## REV 2 implementation pass — 2026-08-11T22:05:00Z

### Startup verification

- Loaded required startup files: `/home/jwoltje/.config/mosaic/CONSTITUTION.md`, `/home/jwoltje/.config/mosaic/SOUL.md`, project `AGENTS.md`, `/home/jwoltje/.config/mosaic/guides/E2E-DELIVERY.md`, `docs/PRD.md`, and this scratchpad.
- Loaded required skills: `test-driven-development`, `vitest`, `vite`, `next-best-practices`, and `verification-before-completion`.
- Loaded required runtime guide: `/home/jwoltje/.config/mosaic/runtime/codex/RUNTIME.md`.
- Structured reasoning tool availability verified through the harness before planning.
- `git rev-parse HEAD` confirmed exact required base: `e00cc475a2b1e9866bd4e2f8df80aff640c3a543`.
- `git status --short` at startup showed only the expected untracked append-only scratchpad; `.mosaic/orchestrator/session.lock` did not appear and must remain unstaged if it changes later.

### Current plan

1. Record baseline evidence.
2. Add focused failing SPA page/route specs first.
3. Run focused RED command and record the missing-page failure.
4. Implement the bounded read-only SPA pages, boundaries, route wiring, and the two dev-port pins.
5. Run the required verification matrix and inspect diff hygiene.

### Pre-change baseline

- Command: `pnpm --filter @mosaicstack/web test`
- rc=0
- File/test totals before changes: `15 files / 133 tests`
- Notes: baseline already includes `/chat` SPA route coverage and raw React `createRoot`/`act` page specs that this pass should mirror.

### RED evidence before production implementation

- Command: `pnpm --filter @mosaicstack/web test -- src/spa/pages/projects.spec.tsx src/spa/pages/project-detail.spec.tsx src/spa/pages/tasks.spec.tsx src/spa/pages/resource-route-boundaries.spec.tsx src/spa/routes.spec.tsx`
- rc=1
- Expected missing-feature reason confirmed:
  - `src/spa/pages/projects.spec.tsx`, `src/spa/pages/project-detail.spec.tsx`, `src/spa/pages/tasks.spec.tsx`, and `src/spa/routes.spec.tsx` fail import resolution because the SPA page modules do not exist yet.
  - `src/spa/pages/resource-route-boundaries.spec.tsx` fails because the current `/projects`, `/projects/:id`, and `/tasks` routes still render placeholders without route-level alert fallbacks.

### Implementation summary

- Added SPA pages:
  - `apps/web/src/spa/pages/projects.tsx`
  - `apps/web/src/spa/pages/project-detail.tsx`
  - `apps/web/src/spa/pages/tasks.tsx`
- Added page-local route boundary components in `apps/web/src/spa/pages/resource-route-error-boundaries.tsx`.
- Wired `/projects`, `/projects/:id`, and `/tasks` in `apps/web/src/routes.tsx` with real elements and `errorElement`s.
- Added fixture-backed raw React Vitest coverage plus route assertions for the three pages and their boundaries.
- Applied only the requested dev topology pins:
  - `apps/web/vite.config.ts`: `server.strictPort = true`
  - `apps/web/package.json`: `next dev -p 3101`, `next start -p 3101`

### Post-implementation verification

- Focused changed specs:
  - Command: `pnpm --filter @mosaicstack/web exec vitest run src/spa/pages/projects.spec.tsx src/spa/pages/project-detail.spec.tsx src/spa/pages/tasks.spec.tsx src/spa/pages/resource-route-boundaries.spec.tsx src/spa/routes.spec.tsx`
  - rc=0
  - Result: `5 files / 26 tests` passed.
- `pnpm --filter @mosaicstack/web typecheck` — rc=0.
- `pnpm --filter @mosaicstack/web lint` — rc=0.
- `pnpm --filter @mosaicstack/web test` — rc=0; post-change totals `19 files / 153 tests`.
- `pnpm --filter @mosaicstack/web build` — rc=1.
  - Limitation: Next/Turbopack hit a sandbox/runtime failure while processing `apps/web/src/app/globals.css`: `creating new process`, `binding to a port`, `Operation not permitted (os error 1)`. This appears environmental, not route-code-specific.
- `pnpm --filter @mosaicstack/web build:vite` — rc=0.
- `pnpm install --frozen-lockfile` — rc=1.
  - Limitation: repo `prepare` hook attempted to lock `/home/jwoltje/distrobox-homes/mosaic-dev/src/stack/.git/config`, which is read-only in this harness.
- `pnpm-lock.yaml` SHA-256 before/after install attempt: `9acaa89d213b3281e757b6edf6fdb8727176570d725b78a0de234c61a7f3c332`.
- `origin/next` `pnpm-lock.yaml` SHA-256: `9acaa89d213b3281e757b6edf6fdb8727176570d725b78a0de234c61a7f3c332`.
- `git diff -- pnpm-lock.yaml` — rc=0 (unchanged).
- `git diff -- apps/web/src | grep -nE '^\+.*(fetch|io|api)\(\s*[\x27\"]https?://'` — rc=1 (empty, as required).
- `pnpm format:check` — rc=0.
- `git diff --check` — rc=0.

### Final worktree check

- `git status --short` shows only the authorized web files plus this scratchpad.
- `.mosaic/orchestrator/session.lock` remains unstaged.
- Current changed file set:
  - `apps/web/package.json`
  - `apps/web/src/routes.tsx`
  - `apps/web/src/spa/routes.spec.tsx`
  - `apps/web/vite.config.ts`
  - `apps/web/src/spa/pages/page-errors.ts`
  - `apps/web/src/spa/pages/page-fixtures.ts`
  - `apps/web/src/spa/pages/project-detail.spec.tsx`
  - `apps/web/src/spa/pages/project-detail.tsx`
  - `apps/web/src/spa/pages/projects.spec.tsx`
  - `apps/web/src/spa/pages/projects.tsx`
  - `apps/web/src/spa/pages/resource-route-boundaries.spec.tsx`
  - `apps/web/src/spa/pages/resource-route-error-boundaries.tsx`
  - `apps/web/src/spa/pages/tasks.spec.tsx`
  - `apps/web/src/spa/pages/tasks.tsx`
  - `docs/scratchpads/webui-p4-1.md`

## P4-1 REV 2 Final Delivery Verification — 2026-08-10T22:23:00Z

### Spec/Security Review Verdicts (Independent Reviews)

Both independent reviews cleared P4-1 REV 2 implementation without blockers:
- **Spec review:** approved; 0 blockers, 0 should-fix findings
- **Code/security review:** approved; 0 blockers, 0 critical/high findings, 0 should-fix recommendations

### Final Fresh Gate Verification (This Session)

All verification gates executed sequentially with rc=0 (except where noted):

1. `pnpm --filter @mosaicstack/web typecheck` — rc=0
2. `pnpm --filter @mosaicstack/web lint` — rc=0
3. `pnpm --filter @mosaicstack/web test` — rc=0; **19 files / 153 tests** (baseline: 15 files / 133 tests; added: 4 files / 20 tests)
4. `pnpm --filter @mosaicstack/web build` — rc=0; Next production build completed, 10/10 static pages generated
5. `pnpm --filter @mosaicstack/web build:vite` — rc=0; Vite production bundle generated
6. Lockfile integrity:
   - Before install: SHA-256 `9acaa89d213b3281e757b6edf6fdb8727176570d725b78a0de234c61a7f3c332`
   - After frozen install: SHA-256 `9acaa89d213b3281e757b6edf6fdb8727176570d725b78a0de234c61a7f3c332`
   - Equality: ✓ verified
   - `git diff --quiet origin/next -- pnpm-lock.yaml` rc=0 ✓
7. `pnpm format:check` — rc=0; all matched files use Prettier code style
8. `git diff --check` — rc=0; no trailing whitespace or merged conflict markers
9. New-code origin check: `git diff -- apps/web/src | grep -nE '^\+.*(fetch|io|api)\(\s*[\x27\"]https?://'` — rc=1 (empty result, as required)
10. `pnpm --filter @mosaicstack/web exec vitest run src/spa/pages/projects.spec.tsx src/spa/pages/project-detail.spec.tsx src/spa/pages/tasks.spec.tsx src/spa/pages/resource-route-boundaries.spec.tsx src/spa/routes.spec.tsx` — rc=0; 5 files / 26 tests passed

### Scope Audit

**Authorized in-scope changes:**
- ✓ `apps/web/package.json` — dev port pins only
- ✓ `apps/web/vite.config.ts` — strictPort flag only
- ✓ `apps/web/src/routes.tsx` — route wiring with errorElements
- ✓ `apps/web/src/spa/pages/projects.tsx` — read-only SPA list
- ✓ `apps/web/src/spa/pages/project-detail.tsx` — read-only SPA detail
- ✓ `apps/web/src/spa/pages/tasks.tsx` — read-only SPA list/kanban
- ✓ `apps/web/src/spa/pages/resource-route-error-boundaries.tsx` — page-local error components
- ✓ `apps/web/src/spa/pages/*.spec.tsx` — fixture-backed tests (4 new)
- ✓ `docs/scratchpads/webui-p4-1.md` — append-only record

**Out-of-scope verification:**
- `.mosaic/orchestrator/session.lock` — not modified/staged ✓
- `pnpm-lock.yaml` — not modified ✓
- `apps/gateway/**` — not modified ✓
- `packages/**` — not modified ✓
- Chat SPA — not modified ✓
- Settings, admin, navigation, or auth flow — not modified ✓

### Next Action

Commit, run authorized queue guard, and push only `feat/webui-p4-1` (no PR, no merge).
