Architecture Reference
CC reads this on demand. For rules and workflow, see
CLAUDE.md.
Project Overview
Nquiry (codebase: nquir) — A Next.js application for managing compliance-critical work including investigations, evaluations, audits, reviews, and assessments. Users can create projects, upload evidence, define investigation topics, generate AI-powered analysis, and create professional reports.
Tech Stack
- Framework: Next.js 16 (App Router) with React 19
- Database: Amazon RDS PostgreSQL
- Authentication: Amazon Cognito
- File Storage: Amazon S3 (server-side encryption, signed URLs)
- Hosting: AWS ECS Fargate (Next.js standalone) behind CloudFront + ALB, CI/CD via GitHub Actions → ECR → ECS
- Styling: Tailwind CSS + shadcn/ui
- AI: AWS Bedrock (Claude via @anthropic-ai/bedrock-sdk)
- Infrastructure: Terraform configs in
infrastructure/terraform/ - Runtime: Node.js 24
Multi-Tenant Architecture
Every user belongs to at least one organization:
- Personal orgs: Auto-created on signup, type='personal', slug
personal-{user_id} - Team orgs: Created manually, type='team'
- Investigations belong to organizations, not directly to users
- User roles: owner > admin > member > viewer
Key tables: organization, organization_member, organization_invitation, investigation
Application-layer authorization (RLS is dead code — see lib/db/client/server.ts):
hasOrgRole(orgId, minRole)inlib/organization/index.ts— API routesverifyInvestigationAccess(investigationId)inlib/investigation/access.ts— server actionscreateProtectedRoute({ requiredRole, organizationId })inlib/api/route-wrapper.ts— route wrapper
Storage Paths
{organization_id}/{investigation_id}/{evidence_id}/{filename}
Buckets: evidence, evaluation-guides, framework-documents
API Route Requirements
Every API route MUST have:
- Authentication check — Return 401 if
!user - Organization authorization — Verify user has access via
hasOrgRole() - Input validation — Check required fields, types, bounds
- Error handling — try/catch with specific error messages
- Audit logging — Log state changes (success AND failure)
- Tests — Route must have corresponding test coverage
Use createProtectedRoute from lib/api/route-wrapper.ts — it handles auth, rate limiting, org context, and error handling automatically.
Before creating any new route, ask:
- What could a malicious authenticated user do with this endpoint?
- Is organization membership verified before accessing resources?
- Are failures logged to the audit trail?
Common Patterns
Organization access:
import { hasOrgRole, getCurrentOrganization } from "@/lib/organization";
const currentOrg = await getCurrentOrganization();
const canEdit = await hasOrgRole(currentOrg.organization_id, "member");
Filtering queries by organization:
const { data } = await supabase
.from("investigation")
.select("*")
.eq("organization_id", currentOrg.organization_id);
Audit logging:
import {
auditInvestigation,
auditEvidence,
auditAnalysis,
logFailure,
} from "@/lib/audit";
await auditInvestigation.create(investigationId, orgId, title);
await auditEvidence.create(evidenceId, orgId, title, investigationId);
await auditAnalysis.generate(investigationId, orgId, "comprehensive");
await logFailure(
"investigation.update",
"investigation",
"Permission denied",
investigationId,
);
Audit logs include: user, organization, timestamp, action, resource, IP, user agent, success/failure, metadata.
AI Quality & Metrics
Nquiry's value depends on AI accuracy and reliability. Every AI output must be traceable.
For any analysis, we must be able to answer:
- Which prompt version produced this? →
analysis.prompt_id+analysis.prompt_version - Did the output pass validation? →
analysis.validation_passed+analysis.validation_errors - What evidence was retrieved? →
analysis_evidence_retrievaltable - What were the similarity scores? → Logged in retrieval metadata
When Building AI Features
- Log the execution — Record prompt version, input context, validation result, latency
- Track the outcome — Did user accept, edit, or reject the output?
- Link the chain — Prompt → Execution → Output → User Action → Finding
Quality Metrics
| Metric | Question | Implementation |
|---|---|---|
| Validation pass rate | Output match expected schema? | analysis.validation_passed |
| Retrieval quality | Relevant evidence found? | Similarity scores in retrieval log |
| Prompt version performance | Is version N better than N-1? | Aggregate by prompt_version |
| User acceptance rate | Do users trust the output? | Track edits/regenerations |
Key AI Files
| File | Purpose |
|---|---|
lib/ai/analysis-output-validators.ts | Zod schemas for validating AI output |
lib/ai/analysis-output-schema.ts | TypeScript types for structured output |
app/api/analysis/generate/route.ts | Analysis generation with prompt tracking |
prompt_template + prompt_template_history | Prompt versioning and rollback |
See docs/claude_ai_work/research/rag-quality-metrics-research.md for RAG evaluation framework.
Key Files
| File | Purpose |
|---|---|
lib/organization/index.ts | Organization utilities (getCurrentOrganization, hasOrgRole) |
lib/db/client/server.ts | Server database client |
lib/db/client/client.ts | Browser database client |
lib/billing/plans.ts | Billing tiers (trial, professional, team, enterprise) |
lib/billing/trial.ts | Trial status checks and logic |
lib/audit/index.ts | Audit logging utilities |
app/(auth)/actions.ts | Auth actions (signup creates personal org) |
components/organization-switcher.tsx | Org dropdown in header |
Communication Standards
Confidence Levels:
- "Based on [service] documentation..." = Verified from official docs
- "In my experience with similar [services/patterns]..." = Pattern recognition, not verified
- "This is typically how [concept] works..." = General knowledge, verify if high-stakes
For high-stakes decisions (production changes, compliance):
- Search official documentation FIRST
- Present options with explicit confidence: "I'm certain" / "This is likely" / "I'm unsure, let me search"
- For AWS/federal applications, verify against official requirements before advising
- If something "should work" but doesn't, stop and research
Environment Variables
See .env.example for required variables. Copy to .env.local and fill in values.
Testing & CI/CD
npm run test # All tests
npm run test:unit # Unit only
npm run test:integration # Integration only
npm run test:watch # Watch mode
Test locations: __tests__/unit/, __tests__/integration/, __tests__/e2e/
CI/CD (.github/workflows/ci.yml): lint → type-check → build → test. Runs on push to main and PRs.