Contract for coding agents (Claude Code, Codex, any AGENTS.md-aware tool) working in this repo. AGENTS.md is a symlink to CLAUDE.md — edit one, both update.
docs/architecture.md— single source of truth for the implementation shape.docs/creating-modules.md— add a new module + agent tool viapnpm gen module.docs/dev-quickstart.md— first tenant and accounts on a fresh DB.docs/hosting/— self-host (docker compose, AWS, scaling, upgrading).DESIGN.md— design tokens and thepackages/shared-uicontract./.env.example— every variable the stack reads.
When docs/architecture.md and the code disagree, the doc is the bug — fix it there. One version per doc: no Phase tags, no internal milestones, no ADR ledger.
- Runtime / build: Node 24 LTS, Turborepo + pnpm workspaces, Vite.
- Backend: Hono, Mastra (
@mastra/core@^1.35), graphile-worker. - Database: Postgres + pgvector, Drizzle ORM (
pgSchema+schemaFilter). No other ORM, no raw migration tool. - Event bus: transactional outbox in
core.events+LISTEN/NOTIFY+ 2s fallback poll. No SQS, no Kafka. - Frontend: React 19, TanStack Router, shadcn/ui, Tailwind 4, AI SDK v6 (
ai@^6+@ai-sdk/react@^3), assistant-ui v6-paired. - Auth: better-auth + Drizzle adapter, argon2id via
@node-rs/argon2. - Cloud: AWS — ECS Fargate, RDS, Secrets Manager, S3.
For @mastra/core API names, consult the sibling checkout at ../mastra/ instead of guessing from npm types. ../mastra/packages/playground-ui/ is the reference for chat/upload UX patterns in apps/web.
pnpm depcruise— cross-module imports must go throughpackages/<module>/src/index.tsor the/events,/rbac,/contracts,/agent-toolssubpaths.shared-*may not import from feature modules.agentis engine-only and may not import any feature or orchestrator module (agent-no-feature-imports).pnpm lint:raw-sql— rejectsFROM <other_module>./JOIN <other_module>.outsidepackages/core/src/{audit,events}/.pnpm lint:styles— rejects.css,tailwind.config.*,@theme/@layer/@applyoutsidepackages/shared-ui/(one shim allowed atapps/web/src/styles/globals.css).- Drizzle schema scoping — each
drizzle.config.tssetsschemaFilter: ['<module>']; cross-schema reads fail at codegen.
No cross-schema foreign keys. planner.tasks.assignee_id stores a uuid with no FK to identity.user.id. Consistency is event-driven via local read-model projections.
No cross-module data-handle sharing. A module never hands its Drizzle client to another module. Mutation crosses the boundary only through public-surface function calls (RBAC re-checked at the callee) or domain events.
The bus is the outbox. State change + event row commit in one transaction via core.emit() inside withEmit(session, ...). No separate publish path. LISTEN/NOTIFY wakes subscribers; the 2s poll covers dropped notifies. Audit lives in core.events alongside domain events.
Enforced by .dependency-cruiser.cjs:
- infra —
packages/shared-*andsdks/*. Leaf packages; may not import from feature/orchestrator modules. - module —
packages/<name>/. Cross-module imports go through the public surface only.
Declared via "setaTier" in package.json (informational, not a separate enforced layer):
- foundation — depended on by every module (
core,identity). - orchestrator — composes multiple feature modules (
staffing). Typically schemaless; workflow state lives inagent.workflow_runs. - engine —
agentonly. Composes module-owned agent tools/specs into a Mastra runtime.
- Tests run against real Postgres via
testcontainers— do not introduce DB mocks. Write the failing test first. - Verify before claiming done:
pnpm typecheck && pnpm lint && pnpm test(andpnpm test:e2eif UI changed). - Install deps via CLI only:
pnpm add <pkg>with no version specifier so the registry resolves latest. Never hand-editpackage.jsonversions orpnpm-lock.yaml. - Generate migrations via CLI only:
pnpm --filter @seta/<module> db:generate, thenpnpm db:migrate. Never hand-edit files underdrizzle/.- Exception — SQL Drizzle cannot model (partitioning, deferred constraint triggers,
pg_notifywiring, partitioned indexes): hand-written.sqlfiles live alongside generated ones indrizzle/migrations/. Each begins with a one-line comment naming the limitation. The runner walks lexically; both formats coexist. Never edit a committed migration — write a new numbered one.
- Exception — SQL Drizzle cannot model (partitioning, deferred constraint triggers,
- Module shape comes from
pnpm gen module— seedocs/creating-modules.md. Don't invent commands; thepnpmscripts in rootpackage.jsonare the contract. docs/superpowers/is gitignored — nevergit add -for push it. Specs and plans under that path are local working documents only. Commit design docs there freely; they will not appear in the remote repo.- Onboarding contract:
clone → install → db:up → db:migrate → bash scripts/tenant-bootstrap.sh → devyields a working demo in 5 min on a fresh machine. Don't break it.
- Inspect the DB (dev):
docker exec seta-ap-postgres-dev psql -U seta -d seta -c '<SQL>'. Postgres is also reachable atlocalhost:5442(mapped byinfra/docker/compose.dev.yml). Schemas:agent,core,identity,planner,notifications,staffing, etc. - HITL on every write tool. AI SDK v6
needsApproval: true+ assistant-ui Interactable confirmation card, wired viaregisterToolPermissionfrom@seta/agent-sdk. Read tools execute directly. - Subscribers must be idempotent, keyed on
event_id. At-least-once delivery; per-aggregate ordering only. - Production-grade only, never quick hacks. Diagnose the root cause and ship the optimized solution; "small patch now, real fix later" is rejected on review.