Thanks for jumping in. This guide gets you productive fast. The deep brief lives in AGENTS.md - read it before your first change; it is the canonical source for architecture, naming, boundaries, and the "where does X go?" decision tree.
| Tool | Version |
|---|---|
| Node | 26+ |
| pnpm | 11+ (via corepack enable) |
| Docker | Postgres + Redis - required, the test suite runs against both |
pnpm install
pnpm setup # boots Docker (Postgres + Redis), runs migrations, prints a summary
pnpm db:seed # demo data: admin + players + wallets + transactions + games
pnpm dev # api :3001, mcp dev serverBackoffice login: admin@oss.dev / password123 (see pnpm db:seed --help for flags).
Scripts are grouped by prefix: check:* reports, fix:* rewrites, gen:* emits, db:* touches Postgres, test:* runs suites. Every check:* and test:* task goes through turbo, so repeat runs hit the cache.
| Command | What it does |
|---|---|
pnpm dev |
turbo dev across docs, mcp |
pnpm verify |
the full gate - every check:* plus test:unit + test:tools, in parallel |
pnpm regen |
tsconfig paths + drizzle generate + catalog |
pnpm check:types |
tsc --noEmit across the workspace |
pnpm check:lint |
oxlint (incl. the oss-boundaries/* plugin) |
pnpm check:format |
oxfmt in check mode |
pnpm check:boundaries |
dependency-cruiser whole-graph boundary + cycle gate |
pnpm check:shape |
module layout conformance |
pnpm check:deprecations |
fails on any use of a @deprecated symbol |
pnpm check:drift |
regenerates the catalog and fails if the committed output is stale |
pnpm fix:lint |
oxlint --fix |
pnpm fix:format |
oxfmt write + final-newline pass |
pnpm test:unit |
vitest, no external services |
pnpm test:integration |
service/router tests against real Postgres |
pnpm test:tools |
node --test over tools/__tests__ |
pnpm test:scaffold |
scaffolds a throwaway module and verifies it, then cleans up |
pnpm gen:agents |
regenerate the per-tool agent files from .rulesync/ via rulesync |
pnpm db:migrate |
apply every module's migrations |
pnpm db:seed |
demo data (idempotent) |
pnpm db:setup:test |
provision the integration-test database |
check:deprecations excludes packages/core/src/pam/identity/adapters/identity-reader.service.ts: depretec mis-resolves Drizzle's SQL.as overloads and flags the non-deprecated as(alias: string) one. Drop the exclusion once depretec resolves overloads correctly.
pnpm gen module <domain> <name> # new module under packages/core/src/<domain>/<name>
pnpm gen plugin <name> # overlay extension under extensions/<name>/
pnpm gen route <module> <method> <path>Each scaffolded file marks editable regions with // AGENT: implement here - fill those,
leave the wiring alone. After scaffolding a module/table: pnpm regen && pnpm verify.
| Branch | Purpose |
|---|---|
dev |
Shared integration branch - the source of truth. Keep it green; MRs land here. |
stage |
Pre-prod / release-candidate. Promoted from dev; stable releases are cut here. |
feat/*, fix/*, chore/* |
Short-lived topic branches. Branch off dev, open an MR back into dev. |
Flow: feat/* -> MR -> dev (publishes canary) -> promote to stage -> tag vX.Y.Z-rc.N for a
release candidate (rc), vX.Y.Z for stable (latest). See Releasing below.
CI (.github/workflows/ci.yml) runs verify on every pull request and on
pushes to dev.
Stable and rc versions are driven by Changesets - you
never hand-edit a release version. Any PR that changes published behavior includes a changeset
(pnpm changeset -> pick patch/minor/major + a one-line summary); the tooling computes the stable
version from those. Canary builds ignore changesets and publish continuously (below). The two
published packages (@openora/core, @openora/mcp) move together as a fixed group.
flowchart LR
PR["feat/* PR<br/>+ pnpm changeset"] -->|merge| DEV[dev]
DEV -. push .-> CANARY(["npm canary<br/>x.y.z-canary.run"])
DEV ==>|promote| STAGE[stage]
STAGE -->|"tag vX.Y.Z-rc.N"| RC(["npm rc<br/>+ GitHub pre-release"])
STAGE -->|"tag vX.Y.Z"| LATEST(["npm latest<br/>+ GitHub Release"])
devpush ->canary(automatic, always). Every merge todevpublishes an immutable build (x.y.z-canary.<run>) under thecanarydist-tag, independent of changesets - the base is the latest published stable patch-bumped so canary always leads it, and<run>is the CI run number (a monotonic, sha-free counter). Install withpnpm add @openora/core@canary. Nothing is committed; this is the rolling dev channel consumers track.- rc + stable are tag-driven from
stage(.github/workflows/release.yml- the tag name IS the version):- rc:
git tag vX.Y.Z-rc.N && git push origin vX.Y.Z-rc.N-> publishes to thercdist-tag + a GitHub pre-release. A candidate for the upcomingX.Y.Z; changesets are left intact. - stable: first
pnpm changeset version(consumes changesets, bumps@openora/*, writes CHANGELOGs) -> commit + pushstage(and fast-forwarddevso the trunk stays in sync), thengit tag vX.Y.Z && git push origin vX.Y.Z-> publisheslatest+ a GitHub Release.
- rc:
Auth is a single NPM_TOKEN repo secret (an npm automation / 2FA-bypass token). The downstream consumer
and example-demo redeploys fire from the canary channel via repo secrets - all encrypted, none public.
Conventional Commits, enforced by commitlint (local commit-msg hook + CI) as
type(scope): summary. A non-conforming message blocks the merge.
- Types:
feat,fix,refactor,chore,docs,test,ci,perf,build,style,revert. - Scope is a fixed list, not free-form.
commitlint.config.cjsderives ascope-enumfrom the workspace - everypackages/*andpackages/core/src/*module dir, apps, plus meta scopes (ci,deps,rules,repo,tooling, ...). An unlisted scope fails the lint; omit the scope if none fits. Check a message withpnpm commitlint --from HEAD~1. - One MR = one concern; stage explicitly (
git add <paths>), don't sweep foreign changes withgit add -A. - No sensitive/internal data in messages - reference a ticket by bare key (
ABC-45), never a URL. - Sign off each commit (
git commit -s) per the DCO (see License below).
feat(wallet): atomic debit command port
fix(audit): guard double-record on retry
chore(deps): bump zod to 3.24.0
Full standard: .rulesync/rules/conventions.md > Git and delivery.
pnpm verifyMust pass. CI additionally runs a "no drift" check: it re-runs drizzle-kit + the catalog
generator and fails on an uncommitted diff. So if you touched schemas or routes, run
pnpm regen and commit the generated output.
- Zod-first. Every shape is a schema; types are
z.infer'd, never hand-written. - No
anyoutside*.test.ts. No inlinefetch/axios. No decorators. - Cross-module talk goes through events or contracts - never import another module's internals.
- Anything that touches the database is tested against real Postgres (
createTestDbfrom@openora/core/testing), never a faked query builder. Only external vendors and cross-module ports are doubled. - New functionality enters only via a plugin object. No auto-discovery, no magic.
- ASCII only in code. Short dashes (-) only.
- Don't hand-edit generated files: drizzle migrations,
docs/catalog.json, and the rulesync-generated agent files (AGENTS.md,CLAUDE.md,.codex/config.toml,.github/copilot-instructions.md, and the.claude/,.github/mirrors) - edit.rulesync/and runpnpm gen:agents.
This project is dual-licensed: AGPL-3.0-only (LICENSE) or a commercial license from Blurify (LICENSE-COMMERCIAL.md).
For the dual-license model to work, contributions must be available under both. By submitting a contribution (a commit, patch, or MR), you:
- License your contribution to the project and its users under the AGPL-3.0-only (inbound = outbound), and
- Grant Blurify a perpetual, worldwide, non-exclusive, royalty-free right to use, relicense, and sublicense your contribution - including under the commercial license above - so it can be offered to commercial licensees.
You confirm you have the right to grant this (the work is yours, or you have
permission), per the Developer Certificate of Origin.
Sign off your commits with git commit -s (adds a Signed-off-by line).
You retain copyright in your contribution. If you cannot grant the commercial relicensing right above, say so in your MR so we can discuss before merging.
The oss-dev MCP server (stdio, see .mcp.json) exposes
the schema registry, route catalog, plugin manifest, and scaffolders as tools. Start at
docs/agent-quickstart.md.