Skip to content

Convention Checks Reference ​

69 automated checks (C1--C143) enforced across all Mantle projects. Grouped by category. For detection patterns and exact fixes, see the agent-enforcement retrieval system (~/Repositories/agent-enforcement/rules/mantle/).

Module and Compiler (C1--C3) ​

CheckRule
C1ESM + strict TypeScript. "type": "module", strict: true, verbatimModuleSyntax, noUncheckedIndexedAccess, isolatedDeclarations. No require().
C2Stage 3 decorators only. Never set experimentalDecorators: true.
C3ES2025 target + Node.js 24. Lambda runtime: nodejs24.x.

Imports and Dependencies (C4--C6) ​

CheckRule
C4Import z from @j0nathan-ll0yd/validation, never zod directly. Use z.toJSONSchema(), never zod-to-json-schema.
C5All HTTP requests use fetchWithTimeout from @j0nathan-ll0yd/resilience. No raw fetch.
C6Use getRequiredEnv/getOptionalEnv from @j0nathan-ll0yd/env. No raw process.env. Never access env vars at module level.

Handler Patterns (C7--C9) ​

CheckRule
C7All handlers use define*Handler factories. No class-based handlers. No middy.
C8Observability via define*Handler (calls withObservability internally). Explicit wrapping only for non-factory handlers.
C9API responses use buildValidatedResponse. Errors use buildErrorResponse.

Database (C10--C11) ​

CheckRule
C10Entity queries: static class, @RequiresTable decorator, withQueryMetrics wrapping, exported as bound functions.
C11All delete() and update() must have .where(). Enforced by Drizzle ESLint rules.

Error Handling (C12) ​

CheckRule
C12CustomLambdaError subclasses for HTTP errors. ok()/err() Result type for service-layer expected failures. Never throw for expected paths.

Testing (C13--C15) ​

CheckRule
C13All tests in test/ mirroring src/. Integration tests use .integration.test.ts suffix.
C14Each code path tested at exactly one level -- unit OR integration, not both.
C15Vitest 4.0+ with @vitest/coverage-v8. Never Jest.

Formatting and Style (C16--C17) ​

CheckRule
C16dprint: lineWidth: 157, indentWidth: 2, semiColons: asi, trailingCommas: never, quoteStyle: preferSingle. Never Prettier.
C17Conventional commits (type(scope): description). No AI attribution lines.

Infrastructure (C18--C20) ​

CheckRule
C18HCL identifiers use snake_case. AWS name attributes use ${name_prefix}-PascalCase. Files: lambda_{snake_case}.tf.
C19One .tf file per Lambda. Each is a module block sourcing mantle/modules/lambda.
C20All Lambdas use arm64 + nodejs24.x. Exception: Lambda@Edge uses x86_64.

Infrastructure Generation (C21--C28) ​

CheckRule
C21mantle generate infra is the sole source for .tf files. Never hand-write what the CLI can generate.
C22Ejected .tf files (no header comment) must be documented in the project plan.
C23Use module outputs (module.x.output), never raw resource references.
C24Env var names in .tf must match getRequiredEnv() calls in source.
C25Build output directory names must match function_name in .tf files.
C26Resource relationships use references, never hardcoded literals. Verify attributes exist in provider docs.
C27No deferred infrastructure. No TODOs in .tf files. Implement resources now.
C28Lambda@Edge: no @j0nathan-ll0yd/* imports (except observability/edge-logging), no layers, no define*Handler.

Database Deployment (C29--C32) ​

CheckRule
C29DSQL deployment requires 3 steps: tofu apply, mantle db migrate, mantle db apply-permissions.
C30@RequiresTable with Insert or Update must also include Select (Drizzle RETURNING requires it).
C31Never use admin DSQL role to fix permission errors. Only MigrateDSQL may use admin.
C32Framework packages that query tables must export permission requirements (e.g. AUTH_TABLE_PERMISSIONS).

Package Scripts (C33) ​

CheckRule
C33package.json scripts delegate to mantle CLI where equivalent functionality exists. Always specify --stage in deploy scripts.

EventBridge (C34) ​

CheckRule
C34Prefer emitEvent/emitEvents from @j0nathan-ll0yd/core. Raw putEvents only when full control is needed.

Validation and Logging (C35--C37) ​

CheckRule
C35API handlers use schema/querySchema on defineApiHandler. No raw event.body parsing.
C36Use logger from @j0nathan-ll0yd/observability. No console.log anywhere. Lambda@Edge uses edge-logging.
C37Handler files should be thin orchestrators. Files over 150 lines warrant extraction to src/services/.

Framework Imports (C38) ​

CheckRule
C38Only import from declared @j0nathan-ll0yd/* exports. Allowed subpaths: auth/schema, aws/cloudfront-keyvaluestore, cli/config, database/orm, database/migrate, mcp/tools, observability/edge, testing/*.

Database OCC Retry (C39) ​

CheckRule
C39withQueryMetrics includes automatic OCC retry. Do not add manual retry around it. Opt out with { retry: false }.

Validation Result (C40) ​

CheckRule
C40validateSchema() returns an object. Check .success, never truthiness. Use .data instead of re-parsing.

Resource Naming (C41) ​

CheckRule
C41name_prefix is environment only. Globally unique resources (S3) include project name. Account-scoped resources use ${name_prefix}-PascalCase.

IAM Policy Generation (C42) ​

CheckRule
C42New services in service-extractor.ts must use import-based detection (Group B pattern), not env-var gating.

Integration Testing (C43--C49) ​

CheckRule
C43Test cleanup: truncateAllTables() in afterAll, never dropAllTables().
C44Integration test configs import TIMEOUTS from @j0nathan-ll0yd/testing/integration. No hardcoded timeouts.
C45Shared test constants (e.g. MAX_WORKERS) must be identical across files.
C46CI uses docker compose up -d --wait. No manual polling loops.
C47CI PostgreSQL: fsync=off, synchronous_commit=off, tmpfs for data dir.
C48Test infrastructure errors must include actionable diagnostics (available schemas, tables, connection state).
C49Sequential DDL in globalSetup. No Promise.all for schema/table creation.

Dependency Ownership (C50--C52) ​

CheckRule
C50Instance dependencies must not declare packages owned by framework (e.g. better-auth, @opentelemetry/api).
C51All repos use createMantleEslintConfig() from @j0nathan-ll0yd/eslint-config.
C52Shared dependency upgrades start in the Mantle pnpm catalog, not in instances.

OpenSpec Drift Tether (C128--C130) ​

CheckRule
C128Behavioral specs are tethered to tests: line-leading // covers: <capability>#<Requirement Name> in the covering test; tethered requirements read "SHALL ..., verified by <test file:line>"; every spec Purpose states it is only as true as its covering tests. Enforced by mantle check openspec (blocking in framework CI, advisory at instances).
C129Scope-match: a requirement may claim only what its covering test actually asserts. Broader behavior belongs in untethered requirements or prose. Multi-test requirements scope-label each verified by citation.
C130Specs are behavior-only: cite the owning schema (file:line) and defer to it -- never restate Zod/OpenAPI/TypeScript field shapes. Enforced by mantle check openspec + each instance's check-openspec-behavior-only.sh fence. Validate format with openspec validate --all --strict (pinned @fission-ai/openspec@1.10.0, telemetry off).