Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 23 additions & 41 deletions .claude/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,40 @@
"version": "0.0.1",
"configurations": [
{
"name": "web",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["--filter", "@commune/web", "dev"],
"port": 5173
},
{
"name": "landing",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["--filter", "@commune/landing", "dev"],
"port": 3001
},
{
"name": "mobile",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["--filter", "@commune/mobile", "dev"],
"port": 8081
},
{
"name": "mobile-v2",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["--filter", "@commune/mobile-v2", "dev", "--", "--port", "8082"],
"port": 8082
"name": "commune-web",
"runtimeExecutable": "node",
"runtimeArgs": ["apps/web/node_modules/vite/bin/vite.js", "apps/web", "--host", "127.0.0.1", "--port", "3000", "--strictPort"],
"port": 3000
},
{
"name": "supabase",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["db:start"],
"port": 54321
"name": "commune-web-qa",
"runtimeExecutable": "env",
"runtimeArgs": ["COMMUNE_API_PORT=3005", "node", "apps/web/node_modules/vite/bin/vite.js", "apps/web", "--host", "127.0.0.1", "--port", "3000", "--strictPort"],
"port": 3000
},
{
"name": "all (turbo)",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["dev"],
"port": 5173
"name": "commune-api",
"runtimeExecutable": "env",
"runtimeArgs": ["DATABASE_URL=postgres://127.0.0.1:5432/commune_loading_test_20260927", "node", "--import", "./apps/local-api/node_modules/tsx/dist/loader.mjs", "apps/local-api/src/server.ts"],
"port": 3002
},
{
"name": "design-mocks",
"runtimeExecutable": "python3",
"runtimeArgs": ["-m", "http.server", "5600", "--directory", "/tmp/commune-design-mocks", "--bind", "127.0.0.1"],
"port": 5600
"name": "website",
"runtimeExecutable": "node",
"runtimeArgs": ["web/node_modules/vite/bin/vite.js", "web", "--host", "127.0.0.1", "--port", "3004", "--strictPort"],
"port": 3004
},
{
"name": "commune-web",
"name": "website-backup",
"runtimeExecutable": "node",
"runtimeArgs": ["apps/web/node_modules/vite/bin/vite.js", "apps/web", "--host", "127.0.0.1", "--port", "3000", "--strictPort"],
"port": 3000
"runtimeArgs": ["web-backup/node_modules/vite/bin/vite.js", "web-backup", "--host", "127.0.0.1", "--port", "3003", "--strictPort"],
"port": 3003
},
{
"name": "commune-web-qa",
"runtimeExecutable": "env",
"runtimeArgs": ["COMMUNE_API_PORT=3005", "node", "apps/web/node_modules/vite/bin/vite.js", "apps/web", "--host", "127.0.0.1", "--port", "3000", "--strictPort"],
"port": 3000
"name": "mobile-v2",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["--filter", "@commune/mobile-v2", "dev", "--", "--port", "8082"],
"port": 8082
}
]
}
15 changes: 13 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
# web tests and builds the web app
# database builds disposable databases from the migrations on PostgreSQL 16
# and runs the database tests, the local API's HTTP tests and the
# reminder worker's tests on them
# reminder worker's tests on them, and checks that
# infra/neon/schema.sql is what the migrations produce
# secrets scans the files and every commit behind them for secrets
#
# Nothing here deploys, uses a secret or reaches Neon, Vercel or Resend. Each
Expand Down Expand Up @@ -93,14 +94,24 @@ jobs:
# The same install as the web job, so the two share one pnpm cache.
- name: Install
run: pnpm install --frozen-lockfile --filter @commune/web --filter @commune/local-api --filter @commune/reminder-worker
# psql for the tests, and pg_dump 16 (the server's version) for the
# schema dump check.
- name: PostgreSQL client
run: |
command -v psql >/dev/null || { sudo apt-get update -q && sudo apt-get install -y -q postgresql-client; }
if ! command -v psql >/dev/null || [ ! -x /usr/lib/postgresql/16/bin/pg_dump ]; then
sudo apt-get update -q && sudo apt-get install -y -q postgresql-client-16
fi
psql --version
/usr/lib/postgresql/16/bin/pg_dump --version
# Every test in infra/neon/tests, the lock order test included, each on
# a fresh database. The script's header explains the rest.
- name: Database tests
run: infra/neon/tests/run-local.sh
# Fails while infra/neon/schema.sql differs from a dump of a database
# built from the migrations. After changing a migration, run
# infra/neon/dump-schema.sh and commit the file it writes.
- name: Schema dump
run: infra/neon/dump-schema.sh --check
- name: Local API tests
run: |
db="${SQL_TEST_PREFIX}_api"
Expand Down
60 changes: 1 addition & 59 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,3 @@
# Commune — Codex Context

## What is this?
Commune is a shared communal expense management / and group management platform.

## Read first
- `docs/HANDOFF.md`: current local state, servers and what remains.
- `docs/DESIGN-LANGUAGE.md`: the approved design direction. Preserve it.
- `docs/MONEY-LIFECYCLE-2026-10-02.md` and `docs/WORKFLOW-COMPLETION-2026-10-02.md`: what is implemented and how it was verified.
- `docs/PRODUCT-DEPTH-ROADMAP-2026-10-02.md`: proposed next work.

`docs/VISION.md` and `docs/PRD.md` were the original product documents. They are not in this checkout; do not assume their contents.

## Current architecture
The application was migrated. The stack below is what runs today.

- **Web (active app):** `apps/web`. React 19 + Vite + TypeScript + wouter, plain CSS with shared tokens. pnpm.
- **Hosted data and auth:** Neon (Managed Auth, Postgres, Data API). Schema and functions live in `infra/neon/migrations`. Production builds use `apps/web/src/live/neon.ts`.
- **Local development backend:** `apps/local-api`, a loopback-only PostgreSQL API on port 3002 that calls the same database functions. It is never deployed.
- **Email:** `apps/reminder-worker`, an outbox sender for reminders and invitations.
- **Shared logic:** `packages/core` (split arithmetic).
- **Mobile:** `apps/mobile` and `apps/mobile-v2` (Expo). Separate work. `apps/mobile` still imports the historical Supabase client and is not a counterpart of the migrated web app.
- **Website:** `web/` is the marketing website: the editorial homepage and its company pages (moved from `apps/landing-monzo-study` on 5 October 2026). The previous v2 site is kept in `web-backup/`. Separate work from the app.

```
commune/
├── apps/web/ # The product: React 19 + Vite + wouter
├── apps/local-api/ # Local loopback API over PostgreSQL (dev only)
├── apps/reminder-worker/ # Reminder and invitation email sender
├── apps/mobile*/ # Expo apps (separate work)
├── apps/landing*/ # Landing studies (separate work)
├── web/ # v2 marketing website (separate work)
├── infra/neon/ # Migrations and SQL tests for the hosted database
├── infra/local/ # Local-only auth bootstrap and helpers
├── packages/core/ # Split logic and shared rules
└── docs/ # Handoff, design language, roadmap, verification
```

## Historical, do not restore
Earlier versions used Mantine 9, TanStack Router/Query, Zustand, Zod, Turborepo and a Supabase backend (`supabase/`, `packages/api`). That runtime was replaced. Do not reintroduce it, scaffold a replacement app, or follow old Supabase/Mantine guidance when changing `apps/web`.

## Key principles
1. **Production-grade from day one.** No throwaway MVP patterns.
2. **Split logic lives in `packages/core`**, never in UI components.
3. **The database is authoritative.** Money and membership change only through validated `SECURITY DEFINER` functions. Clients have no direct write grants on financial tables; reads go through RLS and membership-guarded functions.
4. **Financial records are never silently rewritten.** Repayments are ledger entries with reversals; corrections and voids keep a before/after revision with actor, time and reason; commands carry a request id so a retry cannot duplicate; edits carry the version they were based on.
5. **Recording is not moving money.** Commune records what people paid each other outside the app. Billing (Stripe) is not connected.
6. **Calm, clean UI** following `docs/DESIGN-LANGUAGE.md`. Do not redesign approved screens.

## Working rules
- Use pnpm. The primary folder's dependencies for `apps/web`, `apps/local-api` and `apps/reminder-worker` come from `.deps-web/` (see `docs/HANDOFF.md`). Do not run a whole-workspace install without checking disk space.
- One UI server only, on port 3000. Stop it before starting another.
- Test financial and permission changes on a disposable database and API port, never on the everyday local database.
- Do not push, deploy, apply hosted migrations or send real email without an explicit instruction.

## Pricing (historical intent, not implemented)
These were the original product plans. No entitlement or billing logic exists for them.
- Standard: £4.99/mo (8 members, 1 group)
- Pro: £9.99/mo (15 members, 3 groups)
- Agency: £29.99/mo (unlimited)
- 7-day free trial, no free tier
The instructions for this repository are in [`CLAUDE.md`](CLAUDE.md). They apply to every coding agent, Codex included: read that file first and follow it. This file only points there, so the two cannot drift apart.
46 changes: 30 additions & 16 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,40 @@ Commune is a shared communal expense management and group management platform.
- `docs/DESIGN-LANGUAGE.md`: the approved design direction. Preserve it.
- `docs/MONEY-LIFECYCLE-2026-10-02.md` and `docs/WORKFLOW-COMPLETION-2026-10-02.md`: what is implemented and how it was verified.
- `docs/PRODUCT-DEPTH-ROADMAP-2026-10-02.md`: proposed next work.
- `infra/neon/README.md`: the order migrations are released in, how a migration is written, and the hosted setup.

`docs/VISION.md` and `docs/PRD.md` were the original product documents. They are not in this checkout; do not assume their contents.
`docs/VISION.md` and `docs/PRD.md` were the original product documents. They are not in this checkout; do not assume their contents. Documents that describe the earlier Supabase app say so at the top.

## Current architecture
The application was migrated. The stack below is what runs today.

- **Web (active app):** `apps/web`. React 19 + Vite + TypeScript + wouter, plain CSS with shared tokens. pnpm.
- **Hosted data and auth:** Neon (Managed Auth, Postgres, Data API). Schema and functions live in `infra/neon/migrations`. Production builds use `apps/web/src/live/neon.ts`.
- **Local development backend:** `apps/local-api`, a loopback-only PostgreSQL API on port 3002 that calls the same database functions. It is never deployed.
- **Email:** `apps/reminder-worker`, an outbox sender for reminders and invitations.
- **Shared logic:** `packages/core` (split arithmetic).
- **Mobile:** `apps/mobile` and `apps/mobile-v2` (Expo). Separate work. `apps/mobile` still imports the historical Supabase client and is not a counterpart of the migrated web app.
- **Website:** `web/` is the marketing website: the editorial homepage and its company pages (moved from `apps/landing-monzo-study` on 5 October 2026). The previous v2 site is kept in `web-backup/`. Separate work from the app.
- **Web (active app):** `apps/web`. React 19 + Vite 7 + TypeScript + wouter, plain CSS with shared tokens, tested with Vitest. pnpm. Vercel serves the production build (`apps/web/vercel.json`).
- **Hosted data and auth:** Neon (Managed Auth, Postgres, Data API). Schema and functions live in `infra/neon/migrations`: 39 migrations, 0001 to 0039, applied in numeric order. `infra/neon/schema.sql` is the schema they produce, with every function in full. Production builds use `apps/web/src/live/neon.ts`.
- **Local development backend:** `apps/local-api`, a loopback-only PostgreSQL API on port 3002 that calls the same database functions. Vite proxies `/api` to it. Its database is set up as `infra/local/README.md` describes. It is never deployed.
- **Email:** `apps/reminder-worker`, a Neon Function that sends reminder and invitation emails from the outbox through Resend, with one-click unsubscribe and a bounce webhook.
- **Shared logic:** `packages/core` (split arithmetic, balances and money input), imported by relative path.
- **Mobile:** `apps/mobile-v2` (Expo). Separate work. It does not use Commune's database or share code with `apps/web`.
- **Website:** `web/` is the marketing website: the editorial homepage and its company pages (moved from `apps/landing-monzo-study` on 5 October 2026). The previous v2 site is kept in `web-backup/`. Neither is in the pnpm workspace. Separate work from the app.

```
commune/
├── apps/web/ # The product: React 19 + Vite + wouter
├── apps/local-api/ # Local loopback API over PostgreSQL (dev only)
├── apps/reminder-worker/ # Reminder and invitation email sender
├── apps/mobile*/ # Expo apps (separate work)
├── apps/landing*/ # Landing studies (separate work)
├── web/ # v2 marketing website (separate work)
├── infra/neon/ # Migrations and SQL tests for the hosted database
├── apps/mobile-v2/ # Expo app (separate work)
├── web/ # Marketing website (separate work)
├── web-backup/ # Previous v2 website, kept for reference
├── infra/neon/ # Migrations, schema dump and SQL tests for the hosted database
├── infra/local/ # Local-only auth bootstrap and helpers
├── packages/core/ # Split logic and shared rules
└── docs/ # Handoff, design language, roadmap, verification
├── docs/ # Handoff, design language, roadmap, verification
└── .github/workflows/ # CI
```

The other top-level folders (`artifacts/`, `lib/`, `scripts/`, `landing-replit/`, `designs/`, `design-mocks/`, `audit/`, `attached_assets/`, with `.replit` and `replit.md`) hold earlier prototypes, mock-ups, review evidence and the Replit-era packages. They are not part of the app; leave them as they are.

## Historical, do not restore
Earlier versions used Mantine 9, TanStack Router/Query, Zustand, Zod, Turborepo and a Supabase backend (`supabase/`, `packages/api`). That runtime was replaced. Do not reintroduce it, scaffold a replacement app, or follow old Supabase/Mantine guidance when changing `apps/web`.
Earlier versions used Mantine 9, TanStack Router/Query, Zustand, Zod, Turborepo and a Supabase backend (`supabase/`, `packages/api`, both since removed). That runtime was replaced. Do not reintroduce it, scaffold a replacement app, or follow old Supabase/Mantine guidance when changing `apps/web`.

## Key principles
1. **Production-grade from day one.** No throwaway MVP patterns.
Expand All @@ -48,11 +52,21 @@ Earlier versions used Mantine 9, TanStack Router/Query, Zustand, Zod, Turborepo
6. **Calm, clean UI** following `docs/DESIGN-LANGUAGE.md`. Do not redesign approved screens.

## Working rules
- Use pnpm. The primary folder's dependencies for `apps/web`, `apps/local-api` and `apps/reminder-worker` come from `.deps-web/` (see `docs/HANDOFF.md`). Do not run a whole-workspace install without checking disk space.
- One UI server only, on port 3000. Stop it before starting another.
- Use pnpm 10, pinned by `packageManager`. CI installs only the three apps: `pnpm install --frozen-lockfile --filter @commune/web --filter @commune/local-api --filter @commune/reminder-worker`. Do not run a whole-workspace install without checking disk space: it also pulls the mobile app and the Replit-era packages. On the owner's Mac these three apps' dependencies come from `.deps-web/` (see `docs/HANDOFF.md`).
- One UI server only, on port 3000. Stop it before starting another. To point it at an isolated test API instead of the everyday one on 3002, start it with `COMMUNE_API_PORT=<port>`.
- Test financial and permission changes on a disposable database and API port, never on the everyday local database.
- A migration writes the complete body of every function it adds or changes, and its commit includes the regenerated `infra/neon/schema.sql`.
- Do not push, deploy, apply hosted migrations or send real email without an explicit instruction.

## Tests and CI
`.github/workflows/ci.yml` runs on every pull request and every push to main. Nothing in it deploys or reaches Neon, Vercel or Resend, and each step is a command you can run locally:

- **Web:** in `apps/web`, `pnpm run typecheck`, `pnpm test` and `pnpm run build`, or the tools directly (`node node_modules/vitest/vitest.mjs run`). Tests run in jsdom by default. A test file that needs no browser (nothing rendered, no `localStorage`) starts with `// @vitest-environment node` and runs in Node, which is faster. `pnpm run typecheck` in `apps/local-api` and `apps/reminder-worker` checks those two.
- **Database:** `infra/neon/tests/run-local.sh` builds databases from the migrations on a loopback PostgreSQL 16 server (PG* variables; its header explains) and runs each test in `infra/neon/tests` on its own copy. `infra/neon/dump-schema.sh --check` fails while `infra/neon/schema.sql` differs from what the migrations produce; without `--check` it rewrites the file.
- **Local API:** the HTTP tests in `apps/local-api/tests` run against an API started on another port with a fresh database (`run-local.sh --create <name> local`); they refuse port 3002.
- **Worker:** `node --test tests/email.mjs tests/unsubscribe.mjs` in `apps/reminder-worker`, then its database tests on stub databases, as the CI step shows.
- **Secrets:** gitleaks scans the files and every commit.

## Pricing (historical intent, not implemented)
These were the original product plans. No entitlement or billing logic exists for them.
- Standard: £4.99/mo (8 members, 1 group)
Expand Down
1 change: 1 addition & 0 deletions apps/local-api/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"noUnusedLocals": true,
"noEmit": true,
"skipLibCheck": true,
"types": ["node"]
Expand Down
4 changes: 3 additions & 1 deletion apps/reminder-worker/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"lib": ["ES2022", "DOM"],
"types": ["node"],
"allowImportingTsExtensions": true,
"noEmit": true
"noEmit": true,
"strictFunctionTypes": true,
"noUnusedLocals": true
},
"include": ["src"]
}
Loading