kanban-dev-webapp/PHASE-8-PROGRESS.md

89 lines
9 KiB
Markdown
Raw Permalink Normal View History

# Phase 8 — progress log
> Canonical record for Phase 8 (8a public route, 8b PWA shell, 8c markdown context).
> Mirrored to the Apple Notes note titled "Phase 8 — Public intake + PWA + project context".
> Workflow: a task moves `pending → in_progress → done` as I work it; TDD red/green noted where applicable.
## How to read
- `[ ]` pending
- `[~]` in progress
- `[x]` done — TDD green + commit landed
- `[!]` blocked, needs decision
---
## Plan 8a — Public intake links and route
- [x] Task 1: Schema — `c99be4d`. Bridge: plan assumed `getOrCreateCollection` extended existing select values; it doesn't, so added `ensureCardsCreatedByRoleValues` and extended `ensureActivityActionValues`. Tests: 446 pass.
2026-09-21 00:00:38 +00:00
- [x] Task 2: Token generator — `36572d9`. 24 bytes CSPRNG → base64url → 32 chars. `isIntakeToken` matches.
- [x] Task 3: Best-effort client IP — `f608931`. x-forwarded-for → x-real-ip → "unknown".
- [x] Task 4: Rate limiter — `70ce019`. 60 req / 5 min per opaque key, in-memory, lossy on restart. 4 unit tests.
- [x] Task 5: Token resolver and daily counter — `3ece6cb`. `resolveIntakeLink`, `isLinkUsable` with lazy date-reset, `validateIntakeLinkForStep` returns 3-valued LinkState.
- [x] Task 6: Middleware carve-out — `65b0cf4`. `/intake/:path*` short-circuits before cookie check.
- [x] Task 7: Public Server Action — `9081659`. token-shape → rate-limit → daily-cap → `nextStep`. Same `InterviewStep` shape as signed-in.
2026-09-21 00:19:49 +00:00
- [x] Task 8: Public submission route — `4004aec`. `POST /api/public/intake`. Sibling of `/api/client/intake` with `created_by_role='anonymous'` set in code. Dropped vestigial `tokenHeader` line per the plan's own comment.
2026-09-21 00:29:45 +00:00
- [x] Task 9: Minimal intake layout — `5137a6d`. Deviation: no `<html>`/`<body>` (Next.js root layout owns those). Intent preserved — no ThemeScript, ThemeSync, Inter, or globals.css.
- [x] Task 10: Public intake page — `c9be1d2`. 404 on unknown/revoked/expired with same wording. IntakePublic placeholder kept build green between Tasks 10/11.
- [x] Task 11: Public wizard + brief preview — `a3972f5`. Replaced Task 10 placeholder with the real wizard. POST to `/api/public/intake`, redirect to `/intake/[token]/sent/[cardId]`.
- [x] Task 12: Thanks page — `07ece87`. Resolves token only to confirm still alive — revoked tokens landing here still show "Thanks" so the submission cannot be probed.
- [x] Task 13: Card submitted-by badge — `bb78c9b`. CardSubmittedBy: nothing for owner, "Requested" for client, "Submitted via public link" for anonymous. Renders nothing when accepted.
- [x] Task 14: Owner actions — `c29f855`. `createIntakeLinkAction`, `rotateIntakeTokenAction`, `setIntakeLinkActiveAction`, `setIntakeLinkNotesAction`. Owner-only via requireOwner(). URL shown once.
- [x] Task 15: Intake links panel — `1e97655`. New-link button → label input → URL shown once with Copy. Rotate / Revoke / Re-activate buttons. Notes editor (saves on blur).
- [x] Task 16: Wire panel into admin — `4335af4`. Reads `intake_links` sorted by `-created`, renders `<IntakeLinksPanel>` after `SummaryPanel`.
2026-09-21 04:31:39 +00:00
- [x] Task 17: Integration tests — public route — `5fd1b41`. 9 tests cover happy path, counter bump, hostile-brief rejection, bad-token 404s (unknown/revoked/expired/malformed), deleted-project cascade, and the activity row. All pass.
- [x] Task 18: Integration tests — owner UI actions — `fda6f2c`. 2 placeholder tests as planned (Server Actions not directly driveable from vitest). Action coverage relies on Task 17's public-route tests + Task 19's manual walkthrough.
- [x] Task 19: Full verification — automated parts only. `npm run build` clean; color check empty (no banned tailwind greys or hex literals). All 11 new integration tests pass in isolation. **Pre-existing failures** in `admin.test.ts` (1) and `plan-puzzle.test.ts` (3) are unrelated to phase 8 — they predate this work and concern a different surface. Manual walkthrough steps for Tasks 19.3–19.8 are listed in the chat.
## Plan 8b — PWA shell
- [x] Task 1: The source SVG — `8b40449`. Rounded-square brand tile, single white `!`, brand colour matches `src/lib/brand-default.ts`.
- [x] Task 2: The icon build script — `2b8...`. `@resvg/resvg-js` renders source.svg into the four PNGs the manifest + iOS expect (192/512/maskable-512/apple-touch-180). Build is one-shot; outputs committed.
- [x] Task 3: The manifest — `2b61f5c`. `start_url=/intake/` (trailing slash) so the browser falls back to last-visited token, or install-time URL on first open.
- [x] Task 4: The service worker — `dfcd673`. Cache-first for static allowlist, network-first for /intake/* HTML with offline fallback to cached shell, pass-through for /api/public/intake and Server Actions. `CACHE_NAME=intake-v1`.
- [x] Task 5: Manifest link in the intake layout — `4c4dbaf`. Deviation: route pieces through Next `metadata` export (manifest, apple-touch-icon, appleWebApp); only SW registration stays inline. Verified rendered head: theme-color, manifest link, apple-touch-icon, SW script.
- [x] Task 6: Manual install verification — browser-state; documented walkthrough below.
- [x] Task 7: Final test run — `npm test` 450/450; new integration tests 11/11; `npm run build` clean.
## Phase 8b manual install walkthrough
PWA install behaviour is browser-state. To verify on your own device:
1. iOS Safari: open `/intake/<token>` → Share → Add to Home Screen. The icon should be the brand-coloured tile with `!`. Launching from the home screen opens standalone (no Safari chrome).
2. Android Chrome: open `/intake/<token>` → browser shows an install prompt at the bottom. Install. Icon appears in the launcher.
3. Once installed, kill the network briefly and reload — the cached shell should render with an offline-style fallback (the route's content fetch fails but the SW serves the cached HTML shell from the install).
4. The Wizard's POST to `/api/public/intake` and the Server Action both pass through the SW unchanged, so a flaky network only affects the SHELL load, not the submission.
## Plan 8c — Per-project markdown context
- [x] Task 1: Schema — `7df7074`. Optional `projects.background_md`, max 40000 chars. Migration idempotent (says "already present" on re-run).
- [x] Tasks 2 + 3: Intake context + prompt modules — `4a31b16`. `IntakeContext.backgroundMd`; `intakeContext` reads + truncates to 30000-char prompt cap with marker. Both prompts add a "Background on this app" section framed as the developer's own notes.
- [x] Task 4: Prompt tests — `5c6f82c`. 9 new tests in `prompt.test.ts` (was 11, now 20). **The byte-identical test is the day-one regression guard**: every existing project gets empty `background_md` after migration, and the empty path must produce exactly the pre-8c prompt.
- [x] Task 5: Owner editor — Server Action `setProjectBackgroundMdAction` (owner-only, slices to 40000) + `BackgroundMdEditor` (one textarea, live counter, explicit Save, repo URL as "see also").
- [x] Task 6: Wire into admin — `2432fff`. Editor sits below IntakeLinksPanel on `/admin/projects/[projectId]`.
- [x] Task 7: Full verification — `npm test` 459/459 (was 450, +9 prompt tests), 11/11 phase-8 integration tests, `npm run build` clean. Manual walkthrough documented below.
## Phase 8c manual walkthrough
1. Sign in as owner → `/admin/projects/<id>` → scroll past `Public intake links` to "Background notes for the assistant".
2. Type a short markdown doc, Save, reload — doc persists.
3. Paste 50000 chars, Save, reload. Stored value is the first 40000 chars (field cap); wizard receives first 30000 + truncation marker.
4. With an AI provider configured, run the wizard as a client manager on the same project. The first question should reference your doc (e.g. "the quotes area does X — is that what you mean?").
5. Sign out, open a public token for the same project, run the wizard. Same prompt fires — `intakeContext` is the single code path.
## Phase 8 — complete
All three sub-plans (8a public route, 8b PWA shell, 8c markdown context) are committed.
2026-09-21 11:28:19 +00:00
## Phase 9a — complete
All 7 tasks committed. Schema lands, helpers + DB read/write work, owner toggle ships on the project admin page, integration test confirms schema behaviour (composite unique index, per-(link, project) tracking, sensible route status). Unit: 466/466 pass. Phase 8 backwards compat: existing per-project links still resolve through the picker to the wizard URL (will be verified end-to-end in 9b).
2026-09-21 11:36:27 +00:00
## Phase 9b — complete
All 8 tasks committed. /admin/clients/[id] ships with the picker-friendly Request-links panel and the per-project opt-in toggles. /intake/[token] is now the picker (with single-project redirect + zero-projects fallback). /intake/[token]/p/[projectId] renders the wizard. The Server Action and POST /api/public/intake both honour projectId and verify it against the link's client. Phase 8 backwards compat preserved.
2026-09-21 12:09:50 +00:00
## Phase 9c — complete
All 12 integration tests pass. 466/466 unit tests pass. Build clean. The picker flow is end-to-end verified: card lands on the chosen project's inbox, cross-client projectId refused, hostile body fields ignored, per-(link, project) cap isolates.