Records that the /docs section and homepage fix are merged (PR #8) and live, the proxy.ts PUBLIC_ROUTES hotfix that made /docs reachable without a session, and the authoring/middleware gotchas for future public route groups.
9.6 KiB
HANDOFF — audit / cleanup / architecture engagement
Branch: audit/cleanup-architecture (40 commits ahead of main, not pushed).
Status: all gates green — tsc 0 errors · eslint 0 errors · 645 tests · next build ✓.
Full audit + plan + progress log: docs/AUDIT-2026-08.md. Architecture: ARCHITECTURE.md.
⚠️
mainauto-deploys to production on push (.github/workflows/deploy.yml→ SSH → dropletdeploy.sh). Do not push tomainuntil this branch is reviewed and you intend to deploy.
What was done
Delivered against the original brief (audit → fixes → tests → refactor → docs), then a second separation-of-concerns pass on top.
- Type safety. 586 → 0
tscerrors; removednext.configtypescript.ignoreBuildErrors, so every build/deploy now type-checks. Root cause was the@supabase/ssr↔supabase-jsversion drift (schema collapsed tonever), fixed by annotating the client factories (no dependency bump). - Tooling / CI. Added ESLint 9 flat config (
eslint.config.mjs), atypecheckscript, and a checks-only CI workflow (.github/workflows/ci.yml: typecheck + lint + unit tests), separate from deploy. - Confirmed bugs fixed (one commit + regression test each): invite
max_usesTOCTOU (atomic CAS), board invite re-arming,appealIncidentadmin-gate + orphan rollback, SSRF hex-IPv6, payment dues regression (advance-only) + paymentmember_idIDOR scoping, three null-deref crashes, broken member-directory visibility gate, equipment/proposals scoping + unverified-member gating, door-log parser, CSV-import hang, and a plaintext-secrets security bug (ops "Add Secret" wrote the value unencrypted from the browser — now viacreateSecret, AES-GCM). - Tests. Replaced a tautological
actions.test.ts(asserted string literals) with real authz/IDOR tests; added authz-primitive tests and per-fix regression tests. - Refactor (all 10 slices).
lib/validations.ts(143 exports) →lib/validations/*barrel;lib/types.ts→types/domain/*;lib/actions/{classes,door,forms}.ts→ per-capability folders behind thelib/actionsbarrel; the 663–956-lineops/settings/membersclient components decomposed into thin orchestrators +panels/+components/(also fixed a form-field-focus footgun); dead code removed.types/database.tsis left pristine (regenerate, don't hand-edit). - Separation of concerns. No client component writes to the DB directly anymore. The ops area-lead roster and the comms message send were the last holdouts — both now go through server actions (validated, authz-gated, space-scoped, audited); comms derives sender identity server-side (was impersonation-prone). Only browser-client usage left: auth (login/signup) and the comms realtime read.
- Verification. A re-audit found zero refactor regressions (every authz gate +
space_idscope + handler independently verified across the splits/decompositions). A final independent adversarial review of the separation-of-concerns commits found no introduced bug, security hole, or regression.
How to verify (from a clean checkout of the branch)
pnpm install --frozen-lockfile
pnpm run typecheck # 0 errors
pnpm run lint # 0 errors (warnings are tracked debt)
pnpm run test:run # 645 pass
pnpm build # compiles + type-checks all routes
What is NOT done / residual risk
- Full authenticated UI testing did not run — the dev environment had no Docker → no local Supabase.
Public pages + routing were browser-smoke-tested (render clean; authed routes redirect to
/login), and all routes compile vianext build, but the decomposed ops/settings/members screens were not clicked through against real data. Do a manual smoke-test against a real backend before merge. types/database.tsneeds regeneration against the live schema (supabase gen types typescript --schema public > types/database.ts). That removes 3 hand-added RPC entries and ~11// TODO(types)join casts, and lets the governance rows intypes/domain/governance.tscollapse toTables<>/Enums<>. Needs DB credentials. Highest-leverage next step.- Deferred bug L3 —
markOnboardingStepDoneread-modify-writes a JSON column; a concurrent double-submit can drop a step. Needs a DB-side atomic append (RPC / jsonb operator + migration). - CI runs only unit tests — the integration + Playwright e2e suites need a DB service wired in (and the run should fail on 0 executed integration tests so a missing DB can't masquerade as passing).
- Pre-existing comms realtime dedup edge case (
app/(app)/comms/comms-client.tsx~L82) — two identical messages fired in quick succession before the first realtime event lands can collapse onto one row (duplicate render / duplicate React key). Not introduced by this work (the old direct-insert path had the same race); the robust fix is a client-generated message id / nonce for dedup. - Commit granularity caveat — commits are logically split (one per fix), and most were gated before
commit; a few in the final SoC round were split from a combined working tree and were not each
independently
tsc/test-gated. The final tree is fully green. If you need per-commit bisectability, verify per commit or squash-merge.
Recommended merge path
Review Phases A + B + CI first (low-risk, high-value: type safety, bug fixes, tests). Then the refactor
slices (structural, verified by tsc + build + tests). Then the SoC pass. Merge via PR; a squash or
merge-commit is fine. After merge to main it deploys — have the manual UI smoke-test done first.
Update — 2026-08-05 (merged to main, live)
The audit/cleanup branch was merged (PR #7) and deployed. The four residual items above are now resolved:
- Manual UI smoke-test — DONE. The decomposed ops/settings/members screens (plus onboarding, dashboard, /me) were clicked through against a real seeded backend; a live mutation (area-lead create) and the certifications dialog were exercised. Zero console/server errors.
types/database.tsregenerated — DONE. Regenerated from the canonicalscripts/schema.sql(built on a local Supabase). All 11// TODO(types)join casts removed, hand-added RPCs now native, andtypes/domain/governance.tscollapsed toTables<>/Enums<>. Two embeds that resolved toany[]were traced to the deliberately-wideServerSupabasegate type and fixed by usingcreateClient()directly.- Deferred bug L3 — FIXED.
markOnboardingStepDonenow routes throughmark_onboarding_step_done(migration055): a single atomic, ownership-guarded dedup-append intoonboarding_progress. Covered byintegration/onboarding-progress.test.ts(incl. a 12-way concurrency case). - CI DB-backed suites — DONE.
.github/workflows/ci.ymlnow has anintegration-e2ejob that stands up a Supabase service, runs the integration suite and Playwright e2e, and fails if the integration suite executes 0 tests (so a missing DB can't masquerade as passing).
Items 5 (comms realtime dedup) and 6 (commit granularity) still stand as written.
Docs section + homepage fix (branch feat/docs-and-homepage-fix)
- Homepage redirect fix (
app/(landing)/page.tsx): it used toredirect('/dashboard')for any signed-in visitor, so the marketing site (and the new docs) were unreachable while logged in. The redirect is gone; the nav/hero/closing CTAs are now auth-aware (signed-in shows a "Dashboard" button). - Public
/docssection organized by Diátaxis (tutorials / how-to / reference / explanation), 34 pages covering every module. Source of truth islib/docs/nav.ts; content is markdown undercontent/docs/<category>/<slug>.md, read at build time (the routes are statically generated) and rendered bycomponents/docs/docs-markdown.tsx(reuses the existingreact-markdownstack, no new deps). The(docs)route group has its own theme (docs.css) matching the public site. Screenshots live inpublic/docs-media/and are embedded as captioned figures. Docs prose contains no em-dashes by house style. - Authoring notes: add a page = add it to
DOC_CATEGORIESinlib/docs/nav.tsand drop the matchingcontent/docs/<category>/<slug>.md. No H1 in the body (the page chrome renders the title). Cross-link docs as/docs/<category>/<slug>and app screens as/route. Screenshots go inpublic/docs-media/and embed as(the alt text becomes the figure caption). House style: no em-dashes. Every page was fact-checked against the code by an adversarial verify pass; the permission gotcha that recurs: server actions often allowboard, but the space-config UI (/settings,/customizeconfig panels, integrations) is admin-only. - Middleware gotcha (bit the first deploy):
proxy.ts(the Next 16 middleware) redirects any path not in itsPUBLIC_ROUTESallowlist to/login. Any new public route group must be added toPUBLIC_ROUTES, or it 307s unauthenticated visitors to login./docsis now listed;/docs-media/*static images already bypass via the image matcher. Test public pages with cookies cleared: a browser "new tab" keeps the session, so it is NOT a signed-out test.
Shipped: merged via PR #8 (+ a proxy.ts public-route hotfix) and deployed. Live at hackerspace.sh/docs
(verified public: /docs returns 200 with no session; /dashboard still redirects to /login). Gates:
tsc 0 · lint 0 errors · build ✓ (all 34 /docs pages prerendered) · CI green · deploy ✓.