# CLAUDE.md — Kern project rules Rules for anyone (human or AI agent) working on Kern repositories. These apply to every repo in the KernAIO org. ## We build in the open The repositories are **public**, so every commit is visible the moment it is pushed: - Never commit secrets, tokens, personal data, or machine-specific paths. Use `.env` (gitignored) + `.env.example`. - Write READMEs, docs, and issue/PR text for external contributors, not for ourselves. - Keep commit history clean and meaningful — it is part of what people judge the project by. - Every repo carries LICENSE (AGPL-3.0), CLA.md, CODE_OF_CONDUCT.md, SECURITY.md, CONTRIBUTING.md. ## Git - Author identity: `Navid Mirzaaghazadeh ` (already set in each repo's local git config — plain `git commit` is correct; do not override with `-c`). - **Do not add `Claude-Session:`, `Co-Authored-By: Claude`, "Generated with", or any AI trailer/branding to commit messages, PRs, or code comments.** - Conventional Commits (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`, with optional scope). Imperative mood, ≤ 72-char subject. - Push to `origin main`. Never force-push. If `git pull --rebase` complains about unstaged files that aren't yours (parallel agents share worktrees), use `git -c rebase.autoStash=true pull --rebase`. ## Layout & workflow - Umbrella dev workspace: `kern/` with sibling repos cloned under `kern/repos/` (gitignored there). pnpm links all `@kernhq/*` packages via the umbrella workspace. - Install dependencies ONLY via `kern/scripts/pnpm-install-locked.sh` (serialises pnpm at the umbrella root). - Node 24 (`nvm use 24`), pnpm 10, TypeScript ~5.9, ESM/NodeNext, Biome for lint+format (run `pnpm exec biome check --write ` before committing), Vitest. - Contracts first: changes to `@kernhq/contracts` / module contracts land (and build) before their consumers. - Modules own their data: Postgres schema `mod_`, `workspace_id` + RLS on every tenant table, cross-module access only via `kernel.call()` and events. See `modules` repo `packages/_template`. - Ports: app 5173 · core 4000 · chat 4100 · mail 4200 · collab 4300 · docs 4400. - Dev DB on this machine: Homebrew Postgres 18 at `localhost:5432` (`kern`/`kern`); the compose Postgres listens on `${KERN_PG_PORT:-5432}` (5433 here). ## CI Every service repository's CI runs the real suites, so the workflow starts the infrastructure they need as service containers: Postgres (`pgvector/pgvector:pg18`) everywhere, Valkey for `chat`, Mailpit for `mail`. Things learned the hard way: - Address a service container as **127.0.0.1**, never `localhost` — a runner resolves `localhost` to `::1` first, where the published port is not listening, and `fetch` does not retry over IPv4. - Do not set `registry-url` on `actions/setup-node` in an install job. It writes an `.npmrc` with a placeholder token, and npm answers a bad token with **404**, so public packages appear to vanish. - A repository is built **standalone** in CI. `workspace:*` only resolves inside the umbrella workspace; depend on the published version instead. - Skipping a test because its infrastructure is missing is fine on a laptop and dishonest in CI. Fail when `process.env.CI` is set. ## Writing Documentation — READMEs, guides, runbooks, `docs/`, and any procedure someone follows — uses the `adhd-friendly-ste-technical-writer` skill in `.claude/skills/`: goal first, one action per step, short sentences, conditions before commands, an observable result after every important action. It is a house style inspired by ASD-STE100, not certified compliance — do not claim otherwise. It governs documents for readers. Code comments and commit messages keep the voice they have. ## Quality bar - `pnpm typecheck && pnpm lint && pnpm test && pnpm build` must pass before pushing. - UI follows `app/DESIGN.md` (Ink/paper design system) and must work in RTL (fa/ar) and dark mode. - All user-facing strings go through i18n (Paraglide) — no hardcoded English in components. ## Keeping this file current This file is how the next person — or the next agent — avoids repeating what we already worked out. When you learn something durable, add it here **in the same commit as the change that taught you**: - a trap that cost you time (a silent failure, a misleading error, a tool that lies about success) - a convention you had to infer from reading several files - a decision and the reason behind it, especially where the obvious choice is wrong Keep it specific and short. Delete anything that stops being true — a stale note is worse than none. --- # This repository: modules (first-party feature modules) Every feature ships as a module: `@kernhq/module-chat`, `-mail`, `-tracker`, plus `@kernhq/workflow` (the reusable state machine) and `packages/_template` to copy from. **Things worth knowing** - A module has three entry points: `./contract` (Zod + oRPC, no runtime), `./server` (`defineServerModule`: schema, migrations, router, procedures, jobs, subscriptions, search, resolvers) and `./client` (`defineClientModule`: nav, routes, commands, presenters, slots). - A module owns its data in its **own Postgres schema** (`mod_`) with `workspace_id` and RLS on every tenant table. Cross-module access goes through `kernel.call()` and events — never a join across schemas. - Generated migrations must use `CREATE SCHEMA IF NOT EXISTS`: the kernel creates the schema before running them, so the bare form fails on boot. - The client is published as **source**, not compiled: consumers build the Svelte components with their own toolchain, so `tsconfig` excludes `src/client`. - `@kernhq/workflow`'s registry uses `any` deliberately — it holds rules whose config types differ, and `unknown` would break variance. The reasoning is at the top of `src/registry.ts`; each rule validates its own config with Zod before it runs.