Writer-first platform for profiles, scripts, competitions, submissions, peer feedback, and paid coverage.
Always reference AGENTS.md for the latest info and instructions
- Monorepo: pnpm 9.12, Turborepo 2.x, TypeScript 5.9 strict
- Backend: 11 Fastify microservices behind an API gateway
- Frontend: Next.js 16 (App Router), React 19.2, Tailwind CSS 3.4
- Storage: PostgreSQL 16 (includes FTS for search), MinIO, Redpanda (Kafka)
- Payments: Stripe Connect (coverage-marketplace-service)
pnpm test # All tests
pnpm typecheck # All typechecks
pnpm dev # Start all services
pnpm --filter @script-manifest/<name> test # Single package test
pnpm --filter @script-manifest/<name> typecheck # Single package typecheck| Service | Port | Package | Storage |
|---|---|---|---|
| api-gateway | 4000 | @script-manifest/api-gateway | — |
| profile-project | 4001 | @script-manifest/profile-project-service | PostgreSQL |
| competition-directory | 4002 | @script-manifest/competition-directory-service | PostgreSQL (FTS) |
| submission-tracking | 4004 | @script-manifest/submission-tracking-service | In-memory |
| identity | 4005 | @script-manifest/identity-service | PostgreSQL |
| feedback-exchange | 4006 | @script-manifest/feedback-exchange-service | PostgreSQL |
| ranking | 4007 | @script-manifest/ranking-service | PostgreSQL |
| coverage-marketplace | 4008 | @script-manifest/coverage-marketplace-service | PostgreSQL+Stripe |
| notification | 4010 | @script-manifest/notification-service | In-memory |
| script-storage | 4011 | @script-manifest/script-storage-service | MinIO |
| writer-web | 3000 | @script-manifest/writer-web | — |
Every Fastify service uses buildServer(options) with dependency injection:
export function buildServer(options: XServiceOptions = {}): FastifyInstance;Tests: buildServer({ logger: false, repository: new MemoryRepo() }) + server.inject().
DB-backed services (identity, profile-project, feedback-exchange, ranking, coverage-marketplace) define a repository interface with:
PgXRepositoryfor production (PostgreSQL via@script-manifest/db)MemoryXRepositoryfor tests (in-memory implementations within test files)
- Route modules in
services/api-gateway/src/routes/— one per domain GatewayContexttype passed to all route registrarsregisterXRoutes(server, ctx)pattern for each route moduleproxyJsonRequest()for upstream proxying with error wrappinggetUserIdFromAuth()resolves Bearer token → userId via identity serviceaddAuthUserIdHeader()injectsx-auth-user-idon downstream requests
All services expose: GET /health (deep), GET /health/live (liveness), GET /health/ready (readiness).
Browser session token lives in an HttpOnly cookie (sm_session) set by the writer-web BFF. Browser requests hit the writer-web BFF proxy (apps/writer-web/app/api/v1/_proxy.ts), which reads the cookie and forwards Authorization: Bearer <token> to the gateway. Frontend session/user state is held in the AuthProvider React context (apps/writer-web/app/lib/AuthProvider.tsx), which fetches /api/v1/auth/me via SWR — there is no localStorage session cache. Gateway validates the token via identity service and propagates x-auth-user-id downstream.
Shared Zod schemas + TypeScript types in packages/contracts/. All API request/response shapes defined here. Services import from @script-manifest/contracts.
- Services:
node:test+node:assert/strict(Node.js built-in test runner) - Frontend: Vitest + React Testing Library + jsdom
- Pattern: Tests co-located as
*.test.tsfiles alongside source
- NEVER commit or push directly to
main. All changes go through feature branches + PRs. - Branch format:
<change-type:feat,chore,sec,fix,docs>/<issue>-<short description> - Create from latest:
git fetch origin && git checkout main && git pull --ff-only - Task tracking: Linear project via
linearCLI
- Fastify empty JSON bodies:
content-type: application/jsonwith no body causesFST_ERR_CTP_EMPTY_JSON_BODY. Don't set content-type on bodyless POST requests. - pg TIMESTAMPTZ: PostgreSQL
pgdriver returnsTIMESTAMPTZas JSDateobjects, not strings. Zodz.string().datetime()will fail — useinstanceof Datecheck +.toISOString().
This project uses Linear for issue tracking. Default team: CHAOS
IMPORTANT: Always use the linear CLI over Linear MCP tools. The CLI is more efficient and consistent. Never use mcp__claude_ai_Linear__* tools when the linear CLI can accomplish the same task.
# Create a simple issue
linear issues create "Fix login bug" --team CHAOS --priority high
# Create with full details and dependencies
linear issues create "Add OAuth integration" \
--team CHAOS \
--description "Integrate Google and GitHub OAuth providers" \
--parent CHAOS-100 \
--depends-on CHAOS-99 \
--labels "backend,security" \
--estimate 5
# List and view issues
linear issues list
linear issues get CHAOS-123Available workflow skills (install with linear skills install --all):
/prd- Create agent-friendly tickets with PRDs and sub-issues/triage- Analyze and prioritize backlog/cycle-plan- Plan cycles using velocity analytics/retro- Generate sprint retrospectives/deps- Analyze dependency chains
Run linear skills list for details.