Skip to content

Contributing

How we develop Alchemify — conventions for anyone contributing code (human or AI).

Milestones use plain numbers: 0.1, 0.2, 0.3. No “v” prefix, no patch number. Semver formality comes later when there’s something to ship.

GitHub milestones track which issues belong to which release. Current state:

  • 0.1 — Core backend (auth, SQL proxy, schema introspection) + frontend app shell
  • 0.2 — Docker, architecture hardening, runtime security
  • 0.3 — Reserved for future work
  • Framework: Vitest + Supertest
  • Database: Tests run against the real alchemify database, not mocks
  • Isolation: Tables are truncated between test runs (beforeEach)
  • Pattern: Test helpers in test/setup.ts handle seeding (orgs, users, magic links)
  • Run: pnpm test from repo root

Write tests for new endpoints and SQL functions. Follow the existing pattern in test/auth.test.ts.

End-to-end tests use Playwright and live in the root e2e/ directory. They exercise the full stack (frontend + server + database).

Terminal window
pnpm test:e2e # run all E2E tests (headless)
pnpm test:e2e:ui # open Playwright's visual test runner

The webServer config in e2e/playwright.config.ts starts both the backend and frontend dev servers automatically. If servers are already running, they’ll be reused.

Tests use unique emails (*@test.local) to avoid collisions with manual data. A global teardown deletes all @test.local users after each run.

The diagnose tool launches a headless browser, logs in as a test user, navigates to a page, and reports console errors — useful for debugging AI-generated custom pages without opening DevTools manually.

Terminal window
pnpm diagnose /our-employees # admin role (default)
pnpm diagnose /dashboard --role staff # specific role
pnpm diagnose / --role member # root page as member

Requires the backend (pnpm dev) and frontend (pnpm dev:web) servers running, plus the test seed applied (psql alchemify dba -f apps/server/test-seed.sql). Screenshots are saved to tools/diagnose/screenshots/ (gitignored). Exit code 0 = no errors, 1 = errors found.

Console messages are categorized (compilation, render, execution) and sandbox infrastructure noise (CSP, Vite HMR, WebSocket) is filtered automatically.

The explorer uses Claude Code + Playwright MCP for open-ended exploratory testing of the builder pipeline. Unlike scripted tests, it evaluates non-deterministic AI output, explores freely, and finds issues that deterministic tests can’t.

Terminal window
e2e/explorer/run.sh prompt.md # interactive (env=local)
e2e/explorer/run.sh poc.md --env staging -p # staging, print mode
e2e/explorer/cron.sh # batch run all tests
e2e/explorer/cron.sh --env staging # batch run against staging

The --env flag selects the target environment (local or staging). Config is in e2e/explorer/config.ts. Staging requires EXPLORER_EMAIL and EXPLORER_TOKEN env vars.

Requires all 3 dev servers running (for local) plus the test seed applied. The prompt instructs the AI tester to log in, create tables and data, request custom pages, verify rendering, and check console errors. Batch results go to e2e/explorer/results/ (gitignored).

See e2e/explorer/README.md for full setup and cleanup instructions.

Chat tests use Claude Code + pnpm chat CLI to test the AI builder directly — no browser involved. Claude reads plain-English scenario files, uses the CLI to send messages, then verifies results via SQL queries.

Terminal window
apps/chat/test/run.sh scenarios/poc.md # interactive
apps/chat/test/run.sh scenarios/poc.md -p # print mode (JSON)
apps/chat/test/run.sh scenarios/tier1-simple.md --timeout 30m -p # with timeout
apps/chat/test/run.sh scenarios/tier2-choice-fields.md --max-turns 50 -p

Requires the backend (pnpm dev) and chat (pnpm dev:chat) servers running. Config is in apps/chat/cli/config.ts. Scenarios are in apps/chat/test/scenarios/. Reports are saved to apps/chat/test/output/ (gitignored). See apps/chat/cli/README.md for CLI documentation.

The docs site lives at apps/docs/ (Starlight/Astro). Two sections:

  • User Guide — Non-technical. How to use the app. Written for people who don’t code.
  • Developer Guide — Technical. Architecture, setup, API, extension points.

Convention: When making user-visible changes, update the relevant docs. User-facing features go in the User Guide. Technical changes go in the Developer Guide.

Terminal window
pnpm dev:docs --host # dev server (port 4321)
pnpm build:docs # static build

The base schema lives in apps/server/schema.sql (includes numbered files from schema/). Migrations live in apps/server/migrations/ using dbmate format.

Fresh installs use the base schema (schema.sql) — it’s idempotent and produces a complete database.

Existing databases are upgraded via migrations. This repo defines the migration files; the admin repo runs them against HQ and tenant databases.

Dual maintenance: every migration must also update the corresponding base schema file, so reset-dev.sh and migrations produce the same result.

Terminal window
# Dev: full reset from base schema (no migrations needed)
./scripts/reset-dev.sh
# Base schema conventions
# - IF NOT EXISTS for roles and tables
# - CREATE OR REPLACE for functions
# - Re-apply: psql -U dba -d alchemify -f apps/server/schema.sql

See apps/server/migrations/CONVENTIONS.md for migration format, naming, and rules around breaking changes.

  1. Discuss the change
  2. Create a GitHub issue (requirements, not implementation details)
  3. Implement on the backend branch
  4. Write or update tests
  5. Run pnpm lint and pnpm format before committing
  6. Update docs if user-visible
  7. Commit with a clear message

Issues created from the backend branch should include the backend label.

The project uses ESLint (with type-checked rules) and Prettier for consistent code quality. Both are configured at the repo root and apply to all workspaces.

Terminal window
pnpm lint # ESLint — check for errors
pnpm format # Prettier — auto-format all files
pnpm format:check # Prettier — check without writing

Key rules enforced:

  • No floating promisesawait or void every promise
  • No unused variables — prefix with _ if intentionally unused
  • Consistent type importsimport { type Foo } style
  • Double quotes, semicolons, trailing commas — handled by Prettier (100-char line width)

The ops/check.sh script runs lint, format checks, typecheck, unit tests, and E2E tests.

  • Simple over clever — direct solutions, no over-engineering
  • No extras — don’t add error handling, comments, types, or refactoring beyond what’s needed
  • Delete, don’t deprecate — remove unused code completely, no backwards-compat shims
  • Security first — watch for injection, XSS, and OWASP top 10; flag immediately