First slice of Phase-P increment 4/6. Ports the existing Next (dashboard)read-only Projects/Tasks
pages into the Vite SPA on react-router, and pins the canonical dev topology. View-only — edit UX is
deliberately split to a later increment (P4-1b).
Traceability: Phase P RFC §6.4 (authenticated data surfaces) + §2.4 (same-origin invariant). In-repo
anchors: apps/web/src/spa/routes.spec.tsx (v1-parity route set) + the Next (dashboard) reference pages.
Scope (IN)
/projects list, /projects/:id detail, /tasks list — new SPA pages under apps/web/src/spa/pages/,
page-local (no nav shell), reusing existing components/projects/* + components/tasks/*, @/lib/api
(relative /api/...), @/lib/types.
routes.tsx: three <Placeholder> replaced with the real pages, each with an errorElement boundary.
Dev-topology port pins: vite.config.tsstrictPort: true; Next dev/start scripts → -p 3101
(canonical: SPA=3100 · gateway=14242 · Next=3101).
Tests: per-page render/data/error tests + routes.spec.tsx assertions that the routes resolve to real
elements. Detail page follows the reference graceful-degradation contract — a missions/tasks fetch failure
renders the project with an empty tab; only a project-fetch failure drives the error state.
Scope (OUT)
No mutation/edit (P4-1b), no nav shell, no gateway/packages/** changes, no new deps (lockfile unchanged),
no WS/Origin changes, /chat untouched, Settings/Admin still Placeholder.
Independent gates (author self-reports discarded; re-derived on the pushed commit)
Integrator-verify (clean detached checkout): scope CONFINED to the allowed path set; tree clean;
typecheck/lint/test/build/frozen-lock/format-check all rc=0; lockfile unchanged; no new absolute origin; 154 tests pass (19 files).
Independent code+security review: APPROVE-WITH-NITS. One MEDIUM (detail-page Promise.all dropped the
reference's per-request .catch(() => [])) remediated in commit 29db2421 — restored graceful degradation
and corrected the test that had enshrined the regression; re-verified (delta = exactly 2 files, guard shape
correct, all gates rc=0). Two LOW/INFO findings accepted (React-escaped error text mirroring the reference;
sound PRD escaping in a pre-existing component).
Author: pi (gpt-5.6-sol:max). Integrator-verify + review + merge-gate: independent (author≠reviewer≠merge-gate).
## P4-1 — Read-only Projects + Tasks SPA pages (list/detail) + dev-port pins
First slice of Phase-P increment 4/6. Ports the existing Next `(dashboard)` **read-only** Projects/Tasks
pages into the Vite SPA on react-router, and pins the canonical dev topology. **View-only** — edit UX is
deliberately split to a later increment (P4-1b).
Traceability: Phase P RFC §6.4 (authenticated data surfaces) + §2.4 (same-origin invariant). In-repo
anchors: `apps/web/src/spa/routes.spec.tsx` (v1-parity route set) + the Next `(dashboard)` reference pages.
### Scope (IN)
- `/projects` list, `/projects/:id` detail, `/tasks` list — new SPA pages under `apps/web/src/spa/pages/`,
page-local (no nav shell), reusing existing `components/projects/*` + `components/tasks/*`, `@/lib/api`
(relative `/api/...`), `@/lib/types`.
- `routes.tsx`: three `<Placeholder>` replaced with the real pages, each with an `errorElement` boundary.
- Dev-topology port pins: `vite.config.ts` `strictPort: true`; Next `dev`/`start` scripts → `-p 3101`
(canonical: SPA=3100 · gateway=14242 · Next=3101).
- Tests: per-page render/data/error tests + `routes.spec.tsx` assertions that the routes resolve to real
elements. Detail page follows the reference graceful-degradation contract — a missions/tasks fetch failure
renders the project with an empty tab; only a project-fetch failure drives the error state.
### Scope (OUT)
No mutation/edit (P4-1b), no nav shell, no gateway/`packages/**` changes, no new deps (lockfile unchanged),
no WS/Origin changes, `/chat` untouched, Settings/Admin still Placeholder.
### Independent gates (author self-reports discarded; re-derived on the pushed commit)
- Integrator-verify (clean detached checkout): scope CONFINED to the allowed path set; tree clean;
typecheck/lint/test/build/frozen-lock/format-check all rc=0; lockfile unchanged; no new absolute origin;
**154 tests pass** (19 files).
- Independent code+security review: APPROVE-WITH-NITS. One MEDIUM (detail-page `Promise.all` dropped the
reference's per-request `.catch(() => [])`) remediated in commit `29db2421` — restored graceful degradation
and corrected the test that had enshrined the regression; re-verified (delta = exactly 2 files, guard shape
correct, all gates rc=0). Two LOW/INFO findings accepted (React-escaped error text mirroring the reference;
sound PRD escaping in a pre-existing component).
Author: pi (gpt-5.6-sol:max). Integrator-verify + review + merge-gate: independent (author≠reviewer≠merge-gate).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
P4-1 — Read-only Projects + Tasks SPA pages (list/detail) + dev-port pins
First slice of Phase-P increment 4/6. Ports the existing Next
(dashboard)read-only Projects/Taskspages into the Vite SPA on react-router, and pins the canonical dev topology. View-only — edit UX is
deliberately split to a later increment (P4-1b).
Traceability: Phase P RFC §6.4 (authenticated data surfaces) + §2.4 (same-origin invariant). In-repo
anchors:
apps/web/src/spa/routes.spec.tsx(v1-parity route set) + the Next(dashboard)reference pages.Scope (IN)
/projectslist,/projects/:iddetail,/taskslist — new SPA pages underapps/web/src/spa/pages/,page-local (no nav shell), reusing existing
components/projects/*+components/tasks/*,@/lib/api(relative
/api/...),@/lib/types.routes.tsx: three<Placeholder>replaced with the real pages, each with anerrorElementboundary.vite.config.tsstrictPort: true; Nextdev/startscripts →-p 3101(canonical: SPA=3100 · gateway=14242 · Next=3101).
routes.spec.tsxassertions that the routes resolve to realelements. Detail page follows the reference graceful-degradation contract — a missions/tasks fetch failure
renders the project with an empty tab; only a project-fetch failure drives the error state.
Scope (OUT)
No mutation/edit (P4-1b), no nav shell, no gateway/
packages/**changes, no new deps (lockfile unchanged),no WS/Origin changes,
/chatuntouched, Settings/Admin still Placeholder.Independent gates (author self-reports discarded; re-derived on the pushed commit)
typecheck/lint/test/build/frozen-lock/format-check all rc=0; lockfile unchanged; no new absolute origin;
154 tests pass (19 files).
Promise.alldropped thereference's per-request
.catch(() => [])) remediated in commit29db2421— restored graceful degradationand corrected the test that had enshrined the regression; re-verified (delta = exactly 2 files, guard shape
correct, all gates rc=0). Two LOW/INFO findings accepted (React-escaped error text mirroring the reference;
sound PRD escaping in a pre-existing component).
Author: pi (gpt-5.6-sol:max). Integrator-verify + review + merge-gate: independent (author≠reviewer≠merge-gate).