Files
oysqn.app/CLAUDE.md
Patrick Toal 108c042921 fix(edge-fn): replace getClaims with adminClient.auth.getUser(token)
fix(edge-fn): use user.id instead of claims.sub; fixes 500s and false cert_required
fix(migrations): drop broad reservations SELECT policy; add reservation_slots view with security_invoker=false
fix(tests): correct weekSlot() keys from start/end to start_time/end_time
fix(tests): spread overlap test slots across separate ISO weeks
fix(tests): update e2e assertion to match actual authenticated home text
fix(app): hide IonMenu before user is authenticated
feat(dx): add test:all script running unit, integration, and e2e in sequence
docs(claude-md): document SELinux fix, Edge Function auth pattern, security_invoker behaviour
2026-04-20 14:32:37 -04:00

4.9 KiB

CLAUDE.md

Session Start

Read the latest handoff in docs/summaries/ if one exists. Load only the files that handoff references — not all summaries. If no handoff exists, ask: what is the project, what type of work, what is the target deliverable.

Before starting work, state: what you understand the project state to be, what you plan to do this session, and any open questions.

Identity

You work with Patrick, a Solutions Architect, on the OYS Borrow a Boat app (oysqn.app) — a mobile-first PWA for managing a Borrow a Boat program for a Yacht Club. Backend is Supabase.

Project Overview

  • App: OYS Borrow a Boat (oysqn.app) — rewrite of bab-app
  • Stack: Nuxt 4 (SSR=false), Ionic Vue (@ionic/vue), PrimeVue 4, TypeScript, Supabase (BaaS)
  • Purpose: Manage a Borrow a Boat program for a Yacht Club
  • Personas: BAB Member, Certified Skipper, Program Administrator, Boatswain, Volunteer, Instructor
  • Docs: docs/planning/ contains personas, user/role/permission model

Architecture

App Shell

  • app/app.vue — IonApp + IonMenu + IonRouterOutlet (NO NuxtLayout/NuxtPage)
  • Each page is self-contained: IonPage > IonHeader > IonContent
  • No Nuxt layout system — layouts handled at the page level via IonPage
  • app/plugins/ionic.client.ts — installs IonicVue with mode: 'md'

Routing

  • Nuxt file-based routing → Vue Router → IonRouterOutlet handles transitions
  • app/middleware/auth.ts — global auth guard via useSupabaseUser()
  • Auth pages use definePageMeta({ layout: false }) (effectively a no-op with IonRouterOutlet, but documents intent)

Auth

  • Supabase Auth — magic link + OTP only (no password auth)
  • useSupabaseUser() composable (from @nuxtjs/supabase)
  • app/stores/auth.ts — isAdmin computed from user_metadata.role

Backend

  • Supabase (replaces Appwrite)
  • Types in types/supabase.ts — regenerate with: npx supabase gen types typescript --project-id YOUR_ID > types/supabase.ts
  • useSupabaseClient<Database>() typed against types/supabase.ts

Edge Functions

  • Located in supabase/functions/<name>/ — each function has its own deno.json
  • Auth pattern: extract Bearer token → adminClient.auth.getUser(token) (pass JWT directly to service-role client). Do NOT create a separate userClient with the anon key.
  • Use SUPABASE_SERVICE_ROLE_KEY (adminClient) for all DB operations inside functions; the caller's identity comes from JWT claims (claims.sub = user ID).
  • SELinux (Fedora/RHEL local dev): Before running supabase functions serve, label the project directory for container access:
    sudo chcon -Rt container_file_t $(pwd)
    
    This must be applied after any git clone or directory move. Failure symptom: function bootstrap error with no useful stderr output.

Icons

  • Ionicons only (ionicons/icons) — no PrimeIcons
  • Always import individual icon names from ionicons/icons (tree-shakeable)

Rules

  1. Do not mix unrelated project contexts in one session.
  2. Write state to disk, not conversation. After completing meaningful work, write a summary to docs/summaries/ using templates from templates/claude-templates.md. Include: decisions with rationale, exact numbers, file paths, open items.
  3. Before compaction or session end, write to disk: every number, every decision with rationale, every open question, every file path, exact next action.
  4. When switching work types (research → writing → review), write a handoff to docs/summaries/handoff-[date]-[topic].md and suggest a new session.
  5. Do not silently resolve open questions. Mark them OPEN or ASSUMED.
  6. Do not bulk-read documents. Process one at a time: read, summarize to disk, release from context before reading next.
  7. Sub-agent returns must be structured, not free-form prose. Use output contracts from templates/claude-templates.md.

Where Things Live

  • templates/claude-templates.md — summary, handoff, decision, analysis, task, output contract templates (read on demand)
  • docs/summaries/ — active session state (latest handoff + project brief + decision records)
  • docs/context/ — reusable domain knowledge
  • docs/planning/ — original planning documents (personas, roles/permissions)
  • types/supabase.ts — Supabase generated types (placeholder until Supabase project configured)
  • app/ — Nuxt app source (Nuxt 4 structure)
  • app/plugins/ionic.client.ts — Ionic Vue plugin
  • app/stores/ — Pinia stores (Supabase-backed)
  • app/composables/ — shared composables

Error Recovery

If context degrades or auto-compact fires unexpectedly: write current state to docs/summaries/recovery-[date].md, tell the user what may have been lost, suggest a fresh session.

Before Delivering Output

Verify: exact numbers preserved, open questions marked OPEN, output matches what was requested (not assumed), claims backed by specific data, output consistent with stored decisions in docs/context/, summary written to disk for this session's work.