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

9 KiB
Raw Permalink Blame 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

  • Task 1: Schema — c99be4d. Bridge: plan assumed getOrCreateCollection extended existing select values; it doesn't, so added ensureCardsCreatedByRoleValues and extended ensureActivityActionValues. Tests: 446 pass.
  • Task 2: Token generator — 36572d9. 24 bytes CSPRNG → base64url → 32 chars. isIntakeToken matches.
  • Task 3: Best-effort client IP — f608931. x-forwarded-for → x-real-ip → "unknown".
  • Task 4: Rate limiter — 70ce019. 60 req / 5 min per opaque key, in-memory, lossy on restart. 4 unit tests.
  • Task 5: Token resolver and daily counter — 3ece6cb. resolveIntakeLink, isLinkUsable with lazy date-reset, validateIntakeLinkForStep returns 3-valued LinkState.
  • Task 6: Middleware carve-out — 65b0cf4. /intake/:path* short-circuits before cookie check.
  • Task 7: Public Server Action — 9081659. token-shape → rate-limit → daily-cap → nextStep. Same InterviewStep shape as signed-in.
  • 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.
  • 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.
  • Task 10: Public intake page — c9be1d2. 404 on unknown/revoked/expired with same wording. IntakePublic placeholder kept build green between Tasks 10/11.
  • 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].
  • 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.
  • Task 13: Card submitted-by badge — bb78c9b. CardSubmittedBy: nothing for owner, "Requested" for client, "Submitted via public link" for anonymous. Renders nothing when accepted.
  • Task 14: Owner actions — c29f855. createIntakeLinkAction, rotateIntakeTokenAction, setIntakeLinkActiveAction, setIntakeLinkNotesAction. Owner-only via requireOwner(). URL shown once.
  • 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).
  • Task 16: Wire panel into admin — 4335af4. Reads intake_links sorted by -created, renders <IntakeLinksPanel> after SummaryPanel.
  • 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.
  • 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.
  • 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

  • Task 1: The source SVG — 8b40449. Rounded-square brand tile, single white !, brand colour matches src/lib/brand-default.ts.
  • 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.
  • 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.
  • 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.
  • 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.
  • Task 6: Manual install verification — browser-state; documented walkthrough below.
  • 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

  • Task 1: Schema — 7df7074. Optional projects.background_md, max 40000 chars. Migration idempotent (says "already present" on re-run).
  • 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.
  • 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.
  • Task 5: Owner editor — Server Action setProjectBackgroundMdAction (owner-only, slices to 40000) + BackgroundMdEditor (one textarea, live counter, explicit Save, repo URL as "see also").
  • Task 6: Wire into admin — 2432fff. Editor sits below IntakeLinksPanel on /admin/projects/[projectId].
  • 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.

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).

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.

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.