Non-negotiable structural rules that apply to every Claude Code session in
this repo and to every commit, enforced via three defense-in-depth layers:
1. PreToolUse hook in .claude/settings.json blocks any Write/Edit that
would push a file past the 500-line hard cap. Auto-loads for any
Claude session in this repo regardless of who launched it.
2. scripts/githooks/pre-commit (installed via scripts/install-hooks.sh)
enforces the LOC cap, freezes migrations/ unless [migration-approved],
and protects guardrail files unless [guardrail-change] is present.
3. .gitea/workflows/ci.yaml gets loc-budget + guardrail-integrity jobs,
plus mypy --strict on new Python packages, tsc --noEmit on Node
services, and a syft+grype SBOM scan.
Per-language conventions are documented in AGENTS.python.md / AGENTS.go.md /
AGENTS.typescript.md at the repo root — layering (router->service->repo for
Python, hexagonal for Go, colocation for Next.js), tooling baseline, and
explicit "what you may NOT do" lists.
Adds scripts/check-loc.sh (soft 300 / hard 500, reports 205 hard and 161
soft violations in the current codebase) plus .claude/rules/loc-exceptions.txt
(initially empty — the list is designed to shrink over time).
Per-service READMEs for all 10 services + PHASE1_RUNBOOK.md for the
backend-compliance refactor. Skeleton packages (compliance/{domain,
repositories,schemas}) are the landing zone for the clean-arch rewrite that
begins in Phase 1.
CLAUDE.md is prepended with the six non-negotiable rules.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.7 KiB
3.7 KiB
AGENTS.typescript.md — TypeScript / Next.js Conventions
Applies to: admin-compliance/, developer-portal/, breakpilot-compliance-sdk/, consent-sdk/, dsms-node/ (where applicable).
Layered architecture (Next.js 15 App Router)
app/
├── <route>/
│ ├── page.tsx # Server Component by default. ≤200 LOC.
│ ├── layout.tsx
│ ├── _components/ # Private folder; not routable. Colocated UI.
│ │ └── <Component>.tsx # Each file ≤300 LOC.
│ ├── _hooks/ # Client hooks for this route.
│ ├── _server/ # Server actions, data loaders for this route.
│ └── loading.tsx / error.tsx
├── api/
│ └── <domain>/route.ts # Thin handler. Delegates to lib/server/<domain>/.
lib/
├── <domain>/ # Pure helpers, types, schemas (zod). Reusable.
└── server/<domain>/ # Server-only logic; uses "server-only" import.
components/ # Truly shared, app-wide components.
Server vs Client: Default is Server Component. Add "use client" only when you need state, effects, or browser APIs. Push the boundary as deep as possible.
API routes (route.ts)
- One handler per HTTP method, ≤40 LOC.
- Validate input with
zod. Reject invalid → 400. - Delegate to
lib/server/<domain>/. No business logic inroute.ts. - Always return
NextResponse.json(..., { status }). Never throw to the framework.
export async function POST(req: Request) {
const parsed = CreateDSRSchema.safeParse(await req.json());
if (!parsed.success) return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
const result = await dsrService.create(parsed.data);
return NextResponse.json(result, { status: 201 });
}
Page components
- Pages >300 lines must be split into colocated
_components/. - Server Components fetch data; pass plain objects to Client Components.
- No data fetching in
useEffectfor server-renderable data. - State management: prefer URL state (
searchParams) and Server Components over global stores.
Types
lib/sdk/types.tsis being split intolib/sdk/types/<domain>.ts. Mirror backend domain boundaries.- All API DTOs are zod schemas; infer types via
z.infer. - No
any. Noas unknown as. If you reach for it, the type is wrong.
Tests
- Unit: Vitest (
*.test.ts/*.test.tsx), colocated. - Hooks:
@testing-library/reactrenderHook. - E2E: Playwright (
tests/e2e/), one spec per top-level page, smoke happy path minimum. - Snapshot tests sparingly — only for stable output (CSV, JSON-LD).
- Coverage target: 70% on
lib/, smoke coverage onapp/.
Tooling
tsc --noEmitclean (strict mode,noUncheckedIndexedAccess: true).- ESLint with
@typescript-eslint,eslint-config-next, type-aware rules on. prettier.next buildclean. No// @ts-ignore.// @ts-expect-erroronly with a comment explaining why.
Performance
- Use
next/dynamicfor heavy client-only components. - Image:
next/imagewith explicit width/height. - Avoid waterfalls —
Promise.allfor parallel data fetches in Server Components.
What you may NOT do
- Put business logic in a
page.tsxorroute.ts. - Reach across module boundaries (e.g.
admin-complianceimporting fromdeveloper-portal). - Use
dangerouslySetInnerHTMLwithout explicit sanitization. - Call backend APIs directly from Client Components when a Server Component or Server Action would do.
- Change a public API route's path/method/schema without updating SDK consumers in the same change.
- Create a file >500 lines.
- Disable a lint or type rule globally to silence a finding — fix the root cause.