docs/developer.md
TriagoAI — Developer Guide
Everything a developer needs to run, build, test and navigate TriagoAI. For the
domain language see CONTEXT.md; for design/UI see docs/design.md; for known
issues and postmortems see docs/problem.md.
Stack
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router) |
| UI | React 19, TypeScript 5, Tailwind CSS 4 |
| Data | Prisma 7 + SQLite (libsql adapter) + vectors.db RAG sidecar |
| Auth | jose JWT (HS256) session cookie |
| Billing | Stripe (mocked when keys absent) |
| nodemailer (Ethereal test mode when no SMTP) | |
| Tests | vitest |
Commands
npm run dev # next dev -p 3004 → http://localhost:3004
npm run build # next build
npm run lint # eslint
npm test # vitest run
npx tsc --noEmit # typecheck
npx prisma migrate dev # create + apply migration locally (also regenerates client)
npx prisma generate # regenerate client to lib/generated/prismaFolder map
triagoai/
├── prisma/ # schema.prisma + migrations/ (SQL)
├── lib/
│ ├── agents/ # runtime.ts + 7 agents (triage/diagnosis/remediation/…)
│ ├── actions/ # "use server" actions (tickets, fleet, admin, auth, knowledge, …)
│ ├── fleet.ts # simulated 10-node SME fleet + remediation executor
│ ├── live-generator.ts # continuous SME incident flow (Live Mode)
│ ├── llm.ts # mock | kimi | deepseek | openrouter providers
│ ├── tools.ts # allowlisted tool registry (read/write/destructive) + chat tools
│ ├── audit.ts # SHA-256 tamper-evident audit chain
│ ├── rbac.ts # ITIL role → permission matrix
│ ├── login-security.ts # brute-force protection + account lockout
│ ├── kb-library.ts # sourced ITIL/ITSM article + report seed
│ ├── markdown.tsx # minimal markdown renderer
│ ├── generated/prisma/ # generated Prisma client (do not edit)
│ └── billing.ts / plans.ts / sla.ts / tickets.ts / sso.ts / …
├── app/
│ ├── (app)/ # authenticated console (queues, tickets, dashboards, admin, …)
│ ├── (marketing)/ # public pages (home, pricing, security, about)
│ ├── api/ # route handlers (ai/chat, auth/login, stripe/webhook, …)
│ ├── evidence/[id]/ # print-optimized DORA evidence pack (outside app shell)
│ └── middleware.ts # CSRF origin check on authenticated /api/* mutations
├── components/ # ui.tsx kit, sidebar, chat-client, demo-console, forms
├── docs/ # developer.md, design.md, problem.md, adr/, roadmap-90d.md
├── security/ # policies, audit-log-schema, pen-test-plan, vendor-risk
├── tests/ # vitest suite (tests/**/*.test.ts + lib/**/*.test.ts)
└── scripts/ # send-daily-reports.mts, backfill-kb-library.mjs, fine-tune.mjsTest conventions
vitest run with @ → repo-root alias (see vitest.config.mts).fileParallelism: falseserializes files to avoid SQLite lock flakes.
Date.now()-suffixed slugs and clean up in afterAll (tenant delete cascades to children).
lib/password.ts, lib/login-security.ts, lib/markdown.tsx).
ECC dev harness
The adopted ECC engineering system is wired locally and in CI:
npm run verify — the correctness gate (spec #80): lint (0 warnings) ·typecheck · vitest · build · e2e · e2e:visual. One command that proves a change.
The Definition of Done and the evidence each stage produces live in
docs/agents/verification.md.
npm run verify:ecc — the ECC gate (lib/ecc/verify.ts): typecheck · lint · security-scan · test; --full adds agent-eval and build.
npm run ecc:dev-setup — installs the committed .githooks/pre-push, so every push runs npm run verify:ecc.
npm run verify:agentshield — advisory AgentShield scan of agent config..opencode/rules/*.md) are injected into agent context by lib/ecc/rules.ts. See CONTEXT.md (ECC, Rule pack, Verify gate, Engineering
pipeline, Instinct) and docs/adr/0005, docs/adr/0006.
Gotchas (environment)
NODE_TLS_REJECT_UNAUTHORIZED=0 (for npm ci + Prisma engine downloads) — the
runner stage does NOT, so production outbound calls verify certificates.
@next/swc-*, lightningcss, @tailwindcss/oxide, sharp) can get truncated on npm install over the intercepted TLS — re-copy
from a known-good node_modules if a build suddenly fails.
docs/problem.md for the stale-cache and port-binding gotchas. localhost:3004 is the container, not a local
next dev, unless you explicitly run npm run dev.
Schema changes (checklist)
prisma/schema.prisma.npx prisma migrate dev --name <name> (creates SQL migration + regenerates client).npx prisma generate if the client didn't refresh.lib/generated/prisma.prisma migrate deploy in the Dockerfile CMD).
Billing dunning
Tiered failed-payment recovery lives in lib/billing/dunning.ts (registered as
the hourly dunning job). On invoice.payment_failed the webhook classifies the
decline from charge.outcome.reason (fetched via the charge):
stolen_card/lost_card/do_not_honor/pickup_card/fraudulent)→ cancel immediately, no retries.
retry days, then cancel. ENTERPRISE [1,3,7,14], PROFESSIONAL [3,7,14],
STARTER [3,7]. Stop rules clear the sequence the moment the invoice is paid
or the subscription is cancelled.
Manual Stripe config (one-time): enable retention coupons in the Stripe
Customer Portal (Settings → Billing → Customer portal → Subscriptions and tick
"Allow customers to apply promotion codes") so a retention discount can be
offered before a customer cancels. Coupons themselves are created in the Stripe
Dashboard (or via API) — no code changes required.
RMM/PSA integrations
lib/halopsa.ts, uses halopsa-rest. Register an app: HaloPSA → Config → Integrations → Halo PSA API → Applications → New, auth
method Client ID and Secret, scope all, and set an "Agent to log in as"
(the app is scoped to that agent's team). Store per-tenant on the Tenant row
(haloEnabled, haloCompanyUrl, haloClientId, haloClientSecret). Smoke-test
with npm run verify:halopsa (HALO_CLIENT_ID/HALO_CLIENT_SECRET/
HALO_COMPANY_URL in env, or pass a tenant id). Ships disabled (gated on
haloEnabled).
lib/ninjaone.ts. Same gating pattern on ninjaOneEnabled; npm run verify:ninjaone.
Prospecting (Hunter.io)
lib/prospect-finder.ts finds/verifies MSP owner emails via the Hunter API
(gated on HUNTER_API_KEY, free tier 50 credits/mo). npm run enrich:prospects
parses local/gtm/targets-global.md, prioritizes the first ~25 domains, and
writes data/leads.csv (--limit=N, --domains=a.com,b.io for a smoke test).
Conventions
CONTEXT.md — match it in code/comments. lib/audit.ts audit().
.env. specs/<service>.md and run tests/spec-contract.test.ts (see AGENTS.md).
tsc + eslint + vitest + next build before pushing.
docs/* and local/*) after each session — see the "Documentation map" section in CONTEXT.md.