diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..0430104 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,238 @@ +# Working on PIG + +Onboarding for an agent or engineer joining this codebase cold. Read this +first, then [`docs/build-plan.md`](./docs/build-plan.md) for what to build. + +Named `AGENTS.md` because that is the file coding agents look for by +convention. Everything here applies equally to humans. + +--- + +## 1. What PIG is, in three sentences + +A company that aggregates GPU capacity and resells it runs **two pipelines**, +and its business is the spread between them. Generic CRMs model one pipeline +against companies — they have no concept of inventory, no concept of a +commitment you already paid for, and so cannot answer the only question that +matters: *which contracted capacity is sold, to whom, at what margin, and what +is idle right now.* + +PIG is built around that question, it is Apache 2.0, and it is designed to be +self-hosted by the customer. + +**The load-bearing table is `allocations`**, which joins a `capacity_commitment` +(what we bought, at a known cost) to a `demand_deal` (what we sold, at a known +price). Margin, utilisation and idle capacity all fall out of that one join. +Everything else is plumbing that exists to keep that ledger honest. + +--- + +## 2. Orientation + +``` +packages/core Ontology (stages, tiers, enums) + margin arithmetic + palette +packages/db Drizzle schema, migrations, seeds +packages/prime Typed client for the Prime Intellect compute API +apps/api Hono HTTP API, auth, capacity service +apps/web React + Vite + Tailwind + shadcn-idiom components +apps/mcp MCP server (stdio) — 9 tools +docs/ ontology.md, build-plan.md, agents.md, deploy.md, seed-data.md +``` + +~11,000 lines. 39 tests. Node 22+. + +| | | +|---|---| +| Repo | `PIG/pig` on git.karti.ai (Gitea) | +| Live | https://primeintellectgrowth.com | +| CI | Gitea Actions, `.gitea/workflows/ci.yml`, ~2 min, must stay green | +| Deploy | `bash scripts/deploy.sh` on the host — deliberately manual | + +--- + +## 3. Running it + +```bash +npm install + +# Postgres. PIG needs its own database — never point it at a shared one. +docker run -d --name pig-dev -p 5432:5432 \ + -e POSTGRES_USER=pig -e POSTGRES_PASSWORD=pig -e POSTGRES_DB=pig \ + postgres:16-alpine + +export DATABASE_URL=postgres://pig:pig@localhost:5432/pig +npm run db:migrate +npm run db:seed # sourced, cited people — optional +npm run db:demo # a plausible demo book — optional, prefixed "DEMO — " + +npm run dev:api # :8920 +npm run dev:web # :5173, proxies /api to 8920 +``` + +With no `SUPABASE_URL` set, **authentication is disabled in development** and +every request runs as the first user in the table. `loadConfig` refuses to start +in production without it, so this cannot leak. + +Before pushing: + +```bash +npm run typecheck && npm test +``` + +--- + +## 4. Rules that must not be broken + +These are load-bearing. Breaking one produces a subtle failure, not an error. + +**Intelligence never lives in the API.** Handlers validate, authorize, call a +service, serialise. Research, enrichment, scoring and matching heuristics live +in the service layer or in the agent. The API signals the agent by *writing a +row to `agent_tasks`*, never by calling it — so the queue survives the agent +being down and no request thread ever blocks on a model. + +**Authentication is not authorization.** A verified JWT proves someone has an +account in an identity provider that PIG *shares with another application*. It +does not prove they belong here. Access requires a row in PIG's own `users` +table. A token without one gets `403 needs_profile`, which the front end turns +into the join flow — never a login screen they have already completed. + +**Cost is charged against the full commitment, not the hours that sold.** +Unsold hours are already paid for. Any other treatment reports a healthy margin +on a block that is losing money, which is precisely the failure PIG exists to +prevent. There is a test pinning this; if you "fix" it, read the test first. + +**Sold and held are different.** A live hold removes capacity from everyone +else's availability — otherwise two sellers promise the same GPUs — but it is +not revenue and must never count toward utilisation. + +**Money is integer cents.** Never floats. Round, never truncate: 2.43 is +2.4299999 in binary and a lost cent compounds across millions of GPU-hours. + +**Agent claims carry evidence.** Enrichment writes to `facts` with a score, a +band, a source URL and a status — never directly to the record. Only `verified` +self-applies; anything weaker waits for a human. An agent allowed to write +unattributed claims will eventually write a wrong one and nobody will be able +to tell which. + +**Never invent data about real people.** Seed records carry a confidence grade +and a source URL, both shown in the UI. No email address is ever inferred. +Authorship is not employment — contributors, residency participants and alumni +are recorded as what the evidence shows. Demo data is prefixed `DEMO — ` and +its customers are fictional. + +--- + +## 5. Traps that have already bitten + +Each of these cost real time. None produced an error message. + +**`onConflictDoNothing()` is a no-op without a matching unique constraint.** +It has silently duplicated seed data twice. If there is no unique index to +conflict on, do an existence check instead. + +**`z.coerce.boolean()` turns the string `"false"` into `true`.** Every feature +flag set to false was silently on. Use the `envBoolean` helper in +`apps/api/src/lib/config.ts`. + +**Grid children that truncate need `min-w-0`.** Grid items default to +`min-width: auto` and `truncate` sets `nowrap`, so a long title becomes +unshrinkable content and the page scrolls sideways on a phone. + +**Drizzle-generated migrations are not always valid SQL.** A `jsonb → integer` +cast was emitted without the `USING` clause Postgres requires. Always apply a +new migration to a real empty database before pushing — CI does this, but find +out before CI does. + +**The proxy allows exactly one inline script, by hash.** `index.html` carries a +pre-paint theme script that prevents a white flash for dark-mode users. Editing +it changes its hash and the browser silently blocks it. CI asserts the hash; +if it fails, update the CSP in `deploy/Caddyfile.example`, on the server, and +the expected value in the workflow. + +**`prices.onDemand` from the Prime Intellect API is the TOTAL FOR THE NODE.** +Verified: 1× A100 at 1.79, 2× A100 at 3.58. `gpuMemory` is likewise a node +total. There is an open bug for this — the mapper currently stores both as if +per-GPU, so an 8-GPU node reads eight times too expensive. + +**Prime Intellect has two API hosts.** `api.primeintellect.ai` is compute and +pods. Inference is `api.pinference.ai/api/v1`, OpenAI-compatible. + +**Piggy's default model thinks aloud.** `nvidia/nemotron-3-nano-30b-a3b` is a +hybrid reasoning model; under a tight `max_tokens` it rambles and truncates. +Pass `reasoning_effort: "none"` for tool use, routing and extraction. + +**Deployment traps** live in `deploy/README.md` — chiefly that every Caddy site +block on that host needs `bind 10.0.0.2`, and that the CI runner uses +`container.network: host` so dependencies must be published on `127.0.0.1`. + +--- + +## 6. Conventions + +**Comments explain *why*, never *what*.** The code says what it does. Comments +carry the reasoning that would otherwise be lost — why this treatment and not +the obvious one, what breaks if it changes, what was tried and rejected. Match +the density already in the file; do not add narration. + +**Match the surrounding style.** British spelling in prose and comments +(`utilisation`, `normalise`). Types are explicit at module boundaries. No +default exports. + +**Enums come from `@pig/core`.** Never retype a stage list or a tier into a zod +schema — import it, so removing a value stops validating rather than silently +persisting. + +**Tests pin decisions, not implementations.** The valuable cases are the ones +that would pass under a plausible-but-wrong version. Look at +`packages/core/test/margin.test.ts` for the register. + +**Commits explain the reasoning**, including what was tried and rejected, and +say plainly when something was found by running the code rather than reading +it. Read `git log` for the register. + +**Verify by running, not by assuming.** Several of the worst defects here were +found only by fetching a URL from another machine or applying a migration to a +real database. "It should work" has been wrong repeatedly. + +--- + +## 7. Where to start + +[`docs/build-plan.md`](./docs/build-plan.md) has 24 tasks in three waves with +real dependency edges. + +**Do these first, alone, before anything fans out:** + +- **F1** — install the shadcn primitive set +- **F3** — the RBAC permission model +- **F2** — the shared API write-path convention (needs F3 to call into) + +They are small and they are the interface every other track codes against. +Starting parallel work before they settle is how it turns into merge conflict. + +**Then the two that unblock a demo:** + +- **A1** — allocation and commitment write paths. Today the core table can only + be populated by seed, so a visitor can look at the demo book but cannot enter + a deal of their own. +- **A2** — API keys. Nothing mints one, so the MCP server — the headline + feature — is unreachable in production. + +**A4 (Piggy) is fully independent** and can start immediately alongside the +foundation. It touches no UI and no shared API conventions. + +--- + +## 8. What not to do + +- Do not copy component files from `trycompai/crm`. Most are shadcn/ui + originals — take them from upstream where they are canonical. Borrow the + compositions as ideas; the debt is credited in `NOTICE`. +- Do not open self-registration on the identity provider. It is shared with + another application. PIG mints accounts itself, gated on an invite. +- Do not put a production SSH key on the CI runner. Deployment is manual on + purpose. +- Do not weaken the production guard that refuses to start without an identity + provider. +- Do not seed or infer email addresses for real people. diff --git a/README.md b/README.md index e773fc3..5b82530 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,10 @@ deploy/ Compose files and reverse-proxy snippets ## Documentation +- **[AGENTS.md](./AGENTS.md) — start here if you are joining this codebase.** + Architecture rules, the traps that have already bitten, conventions, and + where to start. +- [Build plan](./docs/build-plan.md) — what remains, in dependency order - [Ontology](./docs/ontology.md) — the domain model, and why it is shaped this way - [Seed data provenance](./docs/seed-data.md) — every claim, graded and cited - [Agent integration](./docs/agents.md) — Claude Code, Codex, prime-agent, Buzz