This page summarizes how the LTL codebase is organized and the conventions that keep it consistent. The canonical, full-detail source is the design document in the LTL repository.

Repository Layout

LTL is an npm-workspaces monorepo:

ltl/
├── apps/
│   └── mobile/          # Expo app
├── packages/
│   ├── shared/          # Shared TypeScript types (incl. generated DB types)
│   ├── ui/              # Component library
│   └── prompts/         # Prompt library
├── supabase/
│   ├── migrations/      # SQL migrations
│   ├── functions/       # Edge Functions (Deno)
│   └── seed/            # Seed data (prompt library)
└── docs/                # Design document and runbooks

The dependency flow is one-way: shared types feed both the app and the backend functions.

Tooling and Quality Gates

Three checks run locally before a pull request and in CI on every PR:

  • npm run typecheck across all workspaces (TypeScript strict, no any in new code).
  • npm run lint (ESLint flat config at the repo root).
  • npm run format:check (Prettier; npm run format auto-fixes).

Dependencies

Any expo-* or react-native-* package must be added with npx expo install <pkg>, never plain npm install. Expo pins exact peer-dependency versions per SDK, and a plain install grabs latest, which diverges and fails at runtime with opaque errors. Run npx expo install --check from the app workspace to audit drift; CI does the same. Repo-root devDependencies that are not part of the Expo runtime (ESLint, Prettier, TypeScript) can use a normal install.

Supabase Workflow

The local stack boots with the Supabase CLI, which applies every migration on start. Environment variables are read from the app workspace’s local env file rather than the repo root. Database types are regenerated from the local or linked schema after each migration, with named aliases kept in a separate file so they survive regeneration.

Code Style

  • TypeScript strict throughout; functional components with hooks only.
  • All dates as ISO 8601 strings.
  • UUIDs from a crypto source on the client and gen_random_uuid() in Postgres.
  • Comments explain non-obvious why, never restate the code.

Load-Bearing Constraints

These come straight from the product principles and are enforced in code:

  • Grief-sensitive UX: nothing may punish a user for stepping away. Streak logic softens on memorialization. Memorial prompts must pass the next-week and stranger tests.
  • The deceased’s voice is frozen: content authored before memorialization is never editable by anyone; the schema enforces this with an is_frozen flag.
  • Offline-first: every core read path works from SQLite without a network; writes queue and sync.
  • Death-transition integrity: Path A and Path B transitions never run client-only; server-side Edge Functions enforce cooldown, notification fan-out, and objection handling, and every state change appends to the immutable audit log.

Conventions

  • Commits follow <type>(<scope>): <subject>, with types feat, fix, refactor, docs, chore.
  • main is protected; work lands through prefixed branches and pull requests.
  • No AI attribution of any kind in commits, PRs, or files; the owner is the sole author.