Imported from AryanPatil5/CompanyBrain (
AGENTS.md). Install upstream withnpx skills add AryanPatil5/CompanyBrain. Copyright stays with the author.
AGENTS.md
Company Brain: AI knowledge engine (SOP extraction, governance, FastMCP skills). Three deployable services in one repo, no root package.json — run commands per directory.
Layout
server/— Express REST API (port 5001) + FastMCP (port 8080) + BullMQ/Temporal workers + crawlers. ESM ("type": "module", NodeNext); all relative imports must end in.js.client/— TanStack Start (SSR) + React Router + Vite + Tailwind v4. Port 3000 (8080 is reserved for FastMCP). Uses@/*path alias tosrc/.demo-oauth-proxy/— separate deployed service holding real Slack/Google OAuth secrets; the server never sees them. Configured viascripts/setup-demo-proxy.mjs.server/supabase/*.sql— migration files in filename order, applied bynpm run migrate(runner:server/src/db/migrator.ts, ADR-T1). The runner owns theschema_migrationsledger and can also be run manually in the Supabase SQL Editor. Supabase is the primary DB + auth.- Migrations are runner-owned and additive: each schema change is a NEW numbered file after the highest applied; the runner refuses duplicate version numbers and checksum-differing re-applies.
deploy/helm/company-brain/— Kubernetes Helm charts.docker-compose.ymlruns local Postgres + Redis.deploy/docs/runbooks/— operations runbooks (migrations, DLQ replay, re-upload recovery).
Commands
# Server (needs server/.env + docker compose up -d for Redis/Postgres)
# Boot is PROCESSES-driven (server/src/bootstrap.ts): PROCESSES unset in dev
# boots ALL processes in one process; set PROCESSES=api,mcp for isolation.
npm run dev --prefix server # tsx watch src/bootstrap.ts (all processes)
npm run dev:api --prefix server # tsx watch src/bootstrap.ts with PROCESSES=api
npm run dev:mcp --prefix server # ... PROCESSES=mcp
npm run dev:crawler --prefix server # ... PROCESSES=crawler
npm run dev:ingestion-worker --prefix server # ... PROCESSES=ingestion-worker
npm run dev:github-sync-worker --prefix server # ... PROCESSES=github-sync-worker
npm run dev:temporal-worker --prefix server # ... PROCESSES=temporal-worker
npm run dev:claims-backfill-worker --prefix server # ... PROCESSES=claims-backfill-worker (claims re-derivation sweeps, Phase 3 backfill)
npm run build --prefix server # tsc typecheck + emit
# Client
npm run dev --prefix client
npm run lint --prefix client # eslint (client; server has its own `npm run lint` — see server/package.json)
npm run build --prefix client
# CI (Phase 1 Task 4)
# .github/workflows/ci.yml gates every push/PR: lint, typecheck, builds (+ reproducibility),
# hermetic `npm test`, migration verification (pgvector service), helm lint/template,
# gitleaks secret scan, npm audit gate (scripts/ci-audit.mjs). See CI_TESTING_REPORT.md.
# Tests: custom console runners via tsx, NO test framework. Self-execute when
# run directly (pattern: `if (import.meta.url === file://${process.argv[1]})`).
npm test --prefix server # hermetic CI run: tsx test/run-all.ts (71 suites, see test/HERMETIC_TESTING_REPORT.md)
npm run test:e2e --prefix server # end-to-end suite
npx tsx server/test/<path>.test.ts # any single suite (all under server/test/**)
Gotchas
npm test(run-all) is hermetic: no live Redis/Postgres/Supabase/LLM/network —test/harness/stubs ioredis (in-memory),global.fetch(deterministic Ollama router + loopback passthrough) and supabase client (fakeSupabase.ts). Per-suite hard timeouts; exits nonzero on any failure. Suites importing app modules that read env must import the harness first (import { installHarness } from './harness/index.js'before app imports). Some suites (e.g.infra/health.test.ts) only pass through loopback passthrough; suites that touch real infra still need it (seetest/HERMETIC_TESTING_REPORT.md).- HARMLESS-LOOKING HANG GOTCHA: never statically import app modules that transitively pull
src/queue/ingestionQueue.ts(e.g.routes/documents.ts,workers/ingestionWorker.ts) in a suite that runs standalone (npx tsx test/x.test.ts). The queue module constructs BullMQ queues at module-eval, which starts a real ioredis connect; ifinstallHarness()'s sendCommand stub lands mid-handshake, the connect never completes and everyqueue.add()hangs forever with zero output. Defer such imports withawait import()AFTERawait installHarness()(pattern intest/routes/documents.test.ts,test/workers/documentJob.test.ts). Underrun-all.tsthe harness is installed before any suite module is imported, so static imports are safe there — the hazard is standalone runs only. client/src/routeTree.gen.tsis auto-generated by TanStack Start — never hand-edit; add routes as files underclient/src/routes/.server/src/bootstrap.tsdispatches byPROCESSESto per-process entrypoints (api,mcp,crawler,ingestion-worker,github-sync-worker,temporal-worker,claims-backfill-worker);server/src/index.tsis the API process module only (startApiServer). Production requires an explicitPROCESSESlist (single-processnpm startremoved).- Webhook durability (Phase 2 Task 1): webhook routes (
/api/ingestion/webhook*) persist deliveries toraw_source_events(migration 035, dedupe key = sha256 of workspace+provider+external_id+event timestamp) and answer202 {event_id, status}; thewebhook-ingestionBullMQ queue is consumed bywebhookEventWorkerinside theingestion-workerprocess (processWebhookEventJobinsrc/ingestion/webhookPipeline.ts, exactly-once via event status + the Phase 1 idempotency ledger). Event status is polled viaGET /api/ingestion/events/:event_id. Do not callprocessThreadCoresynchronously from routes — it lives insrc/services/ingestion/webhookService.tsand runs in the worker only. - Thread ingestion tail (Phase 3, ADR-T15): ALL thread-based ingestion funnels through
processThreadTailinsrc/ingestion/documentPipeline.ts— used byprocessThreadCore(durable webhooks) AND by all six legacy crawlers (src/services/crawlers/{slack,github,linear,zendesk,email,database}.ts). The tail persists source document + chunks → groundedknowledge_claims+claim_evidence(char offsets) → schema-validated SOP extraction. Each crawler keeps its ownskills_sopsinsert shell (per-provider defaults, +confidence_scorefrom migration 037) and then links claims vialinkSopClaimsBestEffort(warn-only; the webhook path uses the strict throwinglinkDocumentClaimsToSopso the event ledger retries). Every crawled/webhook thread therefore produces documents/chunks/claims exactly like the upload pipeline, andGET /api/sops/:id/claimsreturns real data. Entity/relationship confidence inentityResolver.tsis DERIVED from sighting volume (migration 038:times_seen), never hardcoded 1.0. - Upload pipeline (Phase 3):
POST /api/documents/upload→ content-addressed storage →document-ingestionqueue →processDocumentIngestionJob(src/workers/ingestionWorker.ts), which checkpointsextraction_stagethrough parsing → chunking → embedding → claims → completed (real stage markers, migration 036 enum). Duplicate re-uploads return202+deduplicated: truewith the existingdocument_id(23505 caught insrc/routes/documents.ts) — never a 500; re-upload is the recovery path for failed rows. Scanned/empty PDFs land in the explicitocr_requiredterminal stage; text is never fabricated. Similarity insearchVectorContextDLACis honest (?? null), never a fake 0.9 fallback. - Claims backfill worker (Phase 3, ADR-T15):
claims-backfill-workerprocess owns theclaims-backfillBullMQ queue (onebatchjob per sweep via a BullMQ v6 job scheduler,CLAIMS_BACKFILL_INTERVAL_MSdefault 60s, health port 5007CLAIMS_BACKFILL_WORKER_HEALTH_PORT). Core logic insrc/ingestion/claimsBackfill.ts: scanssource_documentswhereextraction_stage='completed'ANDclaims_derived_at IS NULLANDclaims_backfill_failures < 3(migration 039), re-derivesknowledge_claims/claim_evidencefrom storeddocument_chunksvia the same idempotent store the pipeline uses, then stampsclaims_derived_at/claims_derived_version. Progress lives in the DB checkpoint, not the queue — crashed/restarted sweeps re-pick the same rows and the(workspace_id, source_document_id, chunk_id, claim_text_hash)unique key makes re-derivation a no-op; poisoned docs are quarantined after 3 failures. Both live ingestion paths (processThreadTailandprocessDocumentIngestionJob) stampclaims_derived_atafter successful claim extraction so only genuinely-missing docs become candidates. - Embedding backfill (Phase 4 T2): the
embedding-backfillBullMQ queue (job schedulerevery EMBEDDING_BACKFILL_INTERVAL_MSdefault 60s) runs INSIDE the ingestion-worker process (no topology change). Core logic insrc/ingestion/embeddingBackfill.ts(queue-free, hermetically tested intest/workers/embeddingBackfill.test.ts; the processor seamprocessEmbeddingBackfillJoblives in the core, the worker modulesrc/workers/embeddingBackfillWorker.tsis the thin queue citizen). Each sweep is ONE bounded keyset page (id DESC,WHERE id < cursor,EMBEDDING_BACKFILL_BATCH_SIZEdefault 100, cap 500) that re-embeds only stale chunks:embedding_model/embedding_version≠ current provider's (fromgetEmbeddingProvider(), never env), missing/malformed vector, orcontent_hash≠ canonicalhashContent(content)(forcebypasses). Safety invariants: the ONLY write is an atomic conditional UPDATE (WHERE id + workspace + content + content_hash as observed) so concurrent ingestion writes are never overwritten (concurrent_modificationsreported); current chunks make zero provider calls; per-chunk failures are isolated and counted honestly (retryable → retried next sweep; non-retryable → in-process bounded quarantine cleared at worker start); cursor is an optimization only — resume persists via scheduler-template re-upsert, a stale cursor just re-scans and skips; cost metering is best-effortrecordUsagewith honest zeros (never fabricated). No migration needed (columns from 027 + 036). - Health endpoints (Phase 0 Task 2,
server/src/services/health.ts): every process servesGET /healthwith structured JSON —{status, process, version, uptime, pid, startedAt, dependencies}— always HTTP 200 while the process is alive; dependencies fail individually (ok/unavailable), never crash, never expose secrets. Ports (env-overridable): API 5001, crawler 5002 (CRAWLER_PORT), MCP 5003, ingestion-worker 5004, github-sync-worker 5005, temporal-worker 5006, claims-backfill-worker 5007 (CLAIMS_BACKFILL_WORKER_HEALTH_PORT). - Observability (Phase 0 Task 9):
server/src/logger.tsis the only logging path (noconsole.*in runtime code) — structured JSON to stdout with{timestamp, level, service, process, correlationId, pid}; secrets redacted by key name and value pattern (Bearer/API keys/passwords/cookies/OAuth tokens/provider keys/Slack/GitHub tokens/JWTs/PEMs/URL creds);LOG_LEVELfilters. Correlation IDs flow viacorrelationIdMiddleware(preservesx-correlation-id/x-request-id, echoes response header) through AsyncLocalStorage (runWithCorrelationId).server/src/config/otel.tsstarts the OTel SDK only whenOTEL_ENABLED=true(OTLP HTTP exporter toOTEL_EXPORTER_OTLP_ENDPOINT); otherwise no-op;shutdownOpenTelemetry()runs on graceful shutdown. Metrics/Prometheus remain Phase 9. - Cost meter (Phase 0 Task 10,
server/src/services/costMeter.ts): every successful LLM request records provider/model/token counts/estimated cost/latency/workspaceId/correlationId intousage_meters(needs migration031_usage_meters_detail.sqlapplied — on cloud Supabase it must be run via the SQL Editor; until then persistence degrades to the minimal row and logs).recordUsageNEVER throws on persistence failure (best-effort, 2s timeout).checkQuotais report-only viaCOST_QUOTA_<WORKSPACE_ID_UPPER_WITH_NON_ALPHANUMERICS_AS_UNDERSCORES>env (unset = no quota); it never blocks. Billing/budgets are Phase 9. Hermetic tests live inserver/test/infra/costMeter.test.tsand swap in an in-memory store viasetUsageStoreForTest(no live infra). - Env:
server/.env(see.env.example) needsSUPABASE_URL,SUPABASE_SERVICE_ROLE_KEY,VAULT_SECRET_KEY(openssl rand -hex 32),OPENROUTER_API_KEY. Prod refuses the zero-workspace seed ID (DEV_SEED_WORKSPACE_ID). - Auth relies on Supabase Custom Access Token Hook (
public.custom_access_token_hook, migration 015) — must be enabled in dashboard or API auth breaks. - Migrations are runner-owned and additive: each schema change is a NEW numbered file after the highest applied; the runner refuses duplicate version numbers and checksum-differing re-applies.
COMPANY_BRAIN_CRITICAL_REVIEW.md(root) is a candid architectural audit of known limitations (e.g., OpenAPI skills returncompiled_skill_dispatchedwithout real execution). Read it before claiming features work as-advertised.- OAuth demo mode:
scripts/setup-demo-proxy.mjswritesdemo-oauth-proxy/.env+ updatesserver/.env(DEMO_PROXY_* vars). Both.envs are gitignored.