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.

Journey tests exercise the full builder flow end-to-end: login → create table via AI → add CRUD data → generate custom page → verify it renders. They’re separate from the regular E2E suite because they hit the real OpenAI API and are slower.

Terminal window
pnpm test:journey # requires all 3 servers already running

Unlike test:e2e, the journey config does not start servers automatically — you must have pnpm dev, pnpm dev:web, and pnpm dev:chat running first. The test includes a preflight check that gives a clear error if any service is unreachable.

Journey tests live in e2e/journeys/ and have their own Playwright config (playwright.journey.config.ts), teardown, and screenshot directory. They are not included in pnpm test:e2e.

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.

The chat harness uses Claude Code + curl to test the AI builder’s chat API directly — no browser involved. It sends messages, parses SSE streams, observes tool calls, and analyzes the AI’s behavior. Useful for iterating on prompt/tool behavior without the overhead of a headless browser.

Terminal window
apps/chat/test/harness/run.sh smoke.md # interactive
apps/chat/test/harness/run.sh smoke.md -p # print mode (JSON)
apps/chat/test/harness/run.sh build-app.md --timeout 30m -p # full build with timeout
apps/chat/test/harness/run.sh smoke.md --schema myapp # with app schema
apps/chat/test/harness/run.sh smoke.md --tenant acme # multi-tenant mode

Requires the backend (pnpm dev) and chat (pnpm dev:chat) servers running. Config is in apps/chat/cli/config.ts. Prompt files describe test scenarios; the system prompt teaches Claude how to authenticate, call each endpoint, parse SSE events, and report findings. Reports are saved to apps/chat/test/harness/output/ (gitignored).

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 and format checks alongside typecheck and 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