mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
The surface guard grows a write leg. Membership is decided by the argument a module passes to the record resolver, not by the identifier, because a delegable read and a delegable write name the same symbol and mean opposite things. The call matcher resolves aliased and namespace-qualified calls back to the original name, and proves it can against synthetic modules. Two assertions carry the decision not to add a provenance column to eleven tables. Every module in the write list has to call auditLog, which is the only writer that stamps the acting account, and none of them may reach the audit table through the client instead. That decision rested on the trail existing; it now rests on a test. The prose on the read list said the write arms keep refusing under a switch. Eleven of them no longer do, so those sentences are rewritten rather than left to lie. The integration suite drives the shipped POST exports through the real handler, resolver, grant table and Prisma. Five legs per verb: the write lands under the owner and nowhere else, a read grant is refused with nothing written, a caller who was never granted is refused the same way, the request after a revocation is refused with no re-login, and a caller who never switched is indistinguishable from before. The grant is minted through the invitation flow rather than planted, and the audit assertion reads both ends of the row.
src/
Application source. HealthLog is a Next.js 16 App Router project (output: "standalone", React 19, TypeScript strict). The path alias @/* resolves to this directory. Everything a request touches lives under here; the layout below follows the request flow rather than alphabetical order.
Request flow
client → src/proxy.ts → src/app/<route>/route.ts → src/lib/api-handler.ts → handler body → src/lib/api-response.ts → client
proxy.ts— the HTTP edge (Next.js 16's renamedmiddleware). Public-path allowlist, demo-mode mutation block, server-side onboarding redirect, worker-only refusal,x-request-id+ CSP nonce generation, every security header.app/— App Router pages (RSC by default) and API routes. Every API route wraps its handler inapiHandler. Seeapp/README.md.lib/— the server/runtime toolkit:apiHandler, auth, crypto, rollups, AI, notifications, pg-boss jobs, integrations, rate limiting. Seelib/README.md.components/— React components (shadcn/ui new-york, kebab-case files). Seecomponents/README.md.
Other directories
hooks/— shared React hooks (use-prefix, kebab-case).types/— shared TypeScript declarations.generated/prisma/— the generated Prisma client (do not edit; regenerated byprisma generate). Imported as@/generated/prisma.instrumentation.ts— Next.js startup hook; boots the pg-boss worker registry and boot-time backfill queues.__tests__/— cross-cutting unit tests; most tests live in__tests__/folders next to the code they cover.
Conventions
See ../CLAUDE.md for the full set. The load-bearing ones: RSC by default ("use client" only when a hook/state/browser API needs it); every API route wraps apiHandler and parses its body with Zod safeParse; userId is always narrowed from session or Bearer, never accepted as a body field; TanStack Query keys come only from src/lib/query-keys/; outbound HTTP goes through src/lib/safe-fetch.ts.