| applyTo | **/*.{js,ts,tsx} |
|---|
These instructions define the default engineering approach for building modern, production-grade TypeScript systems (services, CLIs, libraries, and web apps).
They must remain applicable to:
- Node.js services (REST/GraphQL/events), serverless, and workers
- Web apps (SPA/SSR), SDKs, and shared libraries
- Monorepos (workspaces) and single-package repos
- Mixed TS/JS repos during migration
They are non-negotiable unless an exception is explicitly documented (with rationale and expiry) in an ADR/decision record.
Identifier scheme. Every normative rule carries a unique tag in the form [TS-<prefix>-NNN], where the prefix maps to the containing section (for example OP for Operating Principles, LCL for Local-first developer experience, QG for Quality Gates, continuing through AI for AI-assisted expectations). Use these identifiers when referencing, planning, or validating requirements.
This section exists so humans and AI assistants can reliably apply the most important rules even when context is tight.
- [TS-QR-001] Specification first: treat the specification as the source of truth for behaviour ([TS-OP-001]).
- [TS-QR-002] Small, safe changes: prefer small, explicit, testable changes ([TS-OP-002], [TS-OP-004]).
- [TS-QR-003] Fast feedback: local development and tests must be quick and confidence-building ([TS-OP-005], [TS-LCL-001]–[TS-LCL-007]).
- [TS-QR-004] Run the quality gates after any code/test change and iterate to clean ([TS-QG-001]–[TS-QG-006]).
- [TS-QR-005] Strict TypeScript:
strict: trueis non-negotiable; treatanyas a last resort ([TS-TSC-001]–[TS-TSC-004]). - [TS-QR-006] Validate at boundaries and reject ambiguous inputs ([TS-DATA-001]–[TS-DATA-003], [TS-BEH-007]–[TS-BEH-008]).
- [TS-QR-007] Deterministic outputs: stable ordering, stable formatting, no hidden randomness ([TS-OP-003], [TS-BEH-001]–[TS-BEH-002]).
- [TS-QR-008] Correct CLI streams: stdout for primary output, stderr for diagnostics ([TS-BEH-016]–[TS-BEH-018]).
- [TS-QR-009] Async correctness: no fire-and-forget promises; explicit timeouts on outbound calls ([TS-BEH-003]–[TS-BEH-006]).
- [TS-QR-010] No secrets in args or logs: use env vars or secret managers; never log secrets ([TS-SEC-005]–[TS-SEC-008]).
- [TS-QR-011] Local by default: no real cloud/network by default; use fakes/emulators; explicit integration mode switches ([TS-EXT-001]–[TS-EXT-004]).
- [TS-QR-012] Operational visibility: correlation IDs and structured logs for services ([TS-OBS-001]–[TS-OBS-007]).
- [TS-QR-013] Accessible UI by default: meet WCAG 2.2 AA expectations; keyboard-first and semantic HTML ([TS-UI-001]–[TS-UI-013]).
- [TS-QR-014] UI performance: design for Core Web Vitals and fast interaction feedback ([TS-UI-020]–[TS-UI-027]).
- [TS-QR-015] Function size limit: split functions exceeding ~50 lines or 3 levels of nesting ([TS-CODE-017]).
- [TS-QR-016] Module size limit: keep modules under ~500 lines excluding tests ([TS-CODE-018]).
These principles extend constitution.md §3.
- [TS-OP-001] Treat the specification as the source of truth for behaviour. If behaviour is not specified, it does not exist.
- [TS-OP-002] Prefer small, explicit, testable changes over broad rewrites or large refactors.
- [TS-OP-003] Design for determinism (stable outputs for the same inputs) and:
- [TS-OP-003a] operability (clear errors, easy diagnosis) for CLIs
- [TS-OP-003b] observability (you can explain what happened and why) for services
- [TS-OP-004] Optimise for maintenance, usability/operability, and change safety, not cleverness.
- [TS-OP-005] Fast feedback is paramount: local development must be quick, automated, and confidence-building.
- [TS-OP-006] Avoid inventing requirements, widening scope, or introducing behaviour not present in the specification.
- [TS-OP-007] Prefer type-driven design: make illegal states unrepresentable, validate at boundaries, keep domain logic pure.
- [TS-OP-008] Avoid heavyweight work at import/module-load time (slow startup, surprises in tests). Initialise heavy resources lazily or behind explicit entrypoints.
- [TS-OP-009] Keep global state minimal and explicit. Prefer dependency injection and explicit wiring over hidden module-level singletons.
- [TS-OP-010] Record design trade-offs and assumptions when behaviour is not obvious so future engineers understand the intent.
The system must be fully developable and testable locally, even when it integrates with external services as part of a larger system.
Follow the shared local-first developer experience baseline for common expectations, plus the language-specific requirements below.
Provide repository-standard commands so an engineer can do the following quickly:
- [TS-LCL-001] Bootstrap:
make deps— installs tooling and dependencies, and prepares a usable local environment - [TS-LCL-002] Format:
make format - [TS-LCL-003] Lint:
make lint - [TS-LCL-004] Type-check:
make typecheck - [TS-LCL-005] Test (fast lane):
make test— must run quickly (aim: < 10 seconds for unit-only; provide another target for slower tests) and deterministically - [TS-LCL-006] Full suite:
make test-all— includes integration/e2e tiers where applicable - [TS-LCL-007] Run (dev):
make run— runs with safe defaults (no cloud dependencies by default)
- [TS-LCL-008] Pin the runtime version:
- [TS-LCL-008a] Node.js:
.nvmrc,.node-version, and/orengines.nodeinpackage.json
- [TS-LCL-008a] Node.js:
- [TS-LCL-009] Use a workspace-aware package manager where relevant (prefer
pnpmfor most repos). - [TS-LCL-010] Commit and enforce a lock file.
- [TS-LCL-011] Prefer
corepack(when supported) to pin package manager versions. - [TS-LCL-012] Keep the toolchain minimal and fast:
- TypeScript (
tsc) for typechecking - ESLint + typescript-eslint for linting (or the repository-approved alternative)
- Prettier or Biome for formatting (choose one; avoid double-formatting)
- A modern test runner (see §14)
- TypeScript (
Provide a pre-commit, Husky, or equivalent configuration that runs the same checks as CI in a fast, local-friendly way:
- [TS-LCL-013] formatting
- [TS-LCL-014] linting
- [TS-LCL-015] typecheck (when fast enough; otherwise CI-only)
- [TS-LCL-016] tests (fast lane)
- [TS-LCL-017] secret scanning (for example gitleaks)
Provide an OCI-based option so behaviour is consistent across laptops and CI:
-
[TS-LCL-018] A lightweight dev image that includes:
- [TS-LCL-018a] Node.js + locked dependencies
- [TS-LCL-018b] lint/test/typecheck tooling
- [TS-LCL-018c] any required system packages
-
[TS-LCL-019] Provide one command to use it:
make docker-test/make docker-run(or Dev Containers), etc.
-
[TS-LCL-020] OCI support must be optional (native dev still works), but it must be maintained.
-
[TS-LCL-021] Never bake secrets into images.
-
[TS-LCL-022] The same commands (
make lint,make typecheck,make test) must work inside and outside the container.
- [TS-LCL-023] Support watch mode when feasible (for example
make test-watch). - [TS-LCL-024] Support parallel tests where safe.
- [TS-LCL-025] Provide clear test markers and commands:
- [TS-LCL-025a]
make test-unit - [TS-LCL-025b]
make test-integration - [TS-LCL-025c]
make test-e2e
- [TS-LCL-025a]
Define clear tiers with predictable markers/commands:
- [TS-LCL-026] Unit (default): no network, no containers
- [TS-LCL-027] Integration: uses containers/emulators; still local and repeatable
- [TS-LCL-028] End-to-end (few): critical journeys only; time-boxed
- [TS-LCL-029] If the project needs OS/system packages, pin and document them (Dev Container, OCI dev image, or a clear install script).
- [TS-LCL-030] Keep local defaults safe: local dev must not require real cloud credentials, and must not mutate real cloud resources by default.
- [TS-LCL-031] Document the supported runtime execution modes (local native, containerised, CI) and keep the core commands consistent across them ([TS-LCL-022]).
- [TS-LCL-032] Where supported by your tooling, provide a single "environment check" command (for example
make doctor) that confirms local prerequisites and surfaces actionable fixes.
Per constitution.md §7.8, after making any change to implementation code or tests, you must run the repository's canonical quality gates:
-
Prefer:
- [TS-QG-001]
make format - [TS-QG-002]
make lint - [TS-QG-003]
make typecheck - [TS-QG-004]
make test
- [TS-QG-001]
-
If
maketargets do not exist, discover and run the equivalent commands (for examplepnpm lint,pnpm -s typecheck,pnpm -s test).- [TS-QG-005] Follow the shared quality gates baseline for iteration and warning handling rules.
- [TS-QG-006] Follow the shared quality gates baseline for command selection and equivalents.
- [TS-QG-007] Follow the shared quality gates baseline for repository-target usage.
TypeScript projects have contracts, even when they are "just code"; treat every boundary as intentional.
- [TS-CTR-001] Treat exported symbols as a stable interface contract.
- [TS-CTR-002] Changes to exports must be intentional, documented, and reviewable.
- [TS-CTR-003] Prefer additive evolution over breaking changes.
- [TS-CTR-004] Use
exportsinpackage.jsonfor libraries, with explicit entrypoints. - [TS-CTR-005] Publish type declarations (
.d.ts) and keep them accurate.
- [TS-CTR-006] Treat inputs and outputs as contracts:
- HTTP semantics, payload shapes, status codes, headers
- CLI flags, defaults, exit codes, stdout/stderr behaviour
- Event/message schemas (topics, attributes, versioning)
- [TS-CTR-007] Backwards-incompatible changes must be intentional, documented, and reviewable.
- [TS-CTR-008] When versioning is required, use one consistent scheme (document it).
- [TS-CTR-009] Treat the CLI as a stable interface contract:
- [TS-CTR-009a] command names and subcommands
- [TS-CTR-009b] flags/options and defaults
- [TS-CTR-009c] argument parsing rules
- [TS-CTR-009d] exit codes
- [TS-CTR-009e] stdout/stderr behaviour
- [TS-CTR-009f] output formats
- [TS-CTR-009g] CLI entrypoints must remain thin adapters per the shared CLI contract: parse + validate input, delegate to shared modules, and forward exit codes instead of duplicating business logic.
- [TS-CTR-009h] When CLIs run inside managed runtimes (Lambda, Cloud Run, Functions), follow the CLI contract cloud guidance: keep stdout/stderr compact, avoid ANSI noise unless supported, and flush streams explicitly before exit.
- [TS-CTR-010] Backwards-incompatible changes must be intentional, documented, and reviewable.
- [TS-CTR-011] Every command must have:
- [TS-CTR-011a] a clear one-line summary
- [TS-CTR-011b] detailed
--helptext (including examples) - [TS-CTR-011c] explicit argument/option descriptions
- [TS-CTR-012] Prefer consistent option patterns:
--verbose,--quiet--formatfor output formats--outputfor output paths--dry-runfor non-destructive preview
- [TS-CTR-013] Examples must match real usage and be copy-paste ready.
- [TS-CTR-014] Support both:
- [TS-CTR-014a] human-readable output (default), and
- [TS-CTR-014b] machine-readable output (when applicable), for example
--format jsonor--json.
- [TS-CTR-015] Never mix progress/status messages into structured output.
- [TS-CTR-016] Treat the external API as a stable contract (HTTP semantics, payload shapes, status codes, headers).
- [TS-CTR-017] Changes to the contract must be intentional, documented, and reviewable.
- [TS-CTR-018] Prefer backward-compatible changes. When breaking changes are necessary:
- [TS-CTR-018a] make them explicit
- [TS-CTR-018b] provide a migration path
- [TS-CTR-018c] version the API where appropriate
- [TS-CTR-019] Prefer non-breaking evolution (additive fields, new endpoints, optional query parameters).
- [TS-CTR-020] If versioning is required:
- use a consistent scheme (for example
/v1/path prefix or content negotiation) - avoid ad-hoc per-endpoint versioning
- use a consistent scheme (for example
- [TS-CTR-021] If the framework can generate OpenAPI or an equivalent schema, treat it as first-class:
- [TS-CTR-021a] accurate request/response models
- [TS-CTR-021b] clear summaries/descriptions
- [TS-CTR-021c] examples when they reduce ambiguity
- [TS-CTR-022] Do not leak internal persistence models or implementation details into the contract.
- [TS-TSC-001] Enable
strict: trueunless an ADR explicitly documents why not. - [TS-TSC-002] Treat
anyas a last resort:- [TS-TSC-002a] prefer
unknownat boundaries, then narrow - [TS-TSC-002b] use
neverto prove impossible states
- [TS-TSC-002a] prefer
- [TS-TSC-003] Prefer safer options for modern codebases (unless they cause disproportionate migration cost):
noUncheckedIndexedAccessexactOptionalPropertyTypesuseUnknownInCatchVariablesnoImplicitOverride(when using inheritance)
- [TS-TSC-004] Choose ESM vs CJS deliberately and document the choice.
- [TS-TSC-005] Prefer ESM for new code where the runtime/tooling supports it.
- [TS-TSC-006] Use consistent module resolution across the repo (
NodeNext/Bundler/Node) and avoid per-package drift.
- [TS-TSC-007] Use TypeScript project references where it materially improves build performance and layering.
- [TS-TSC-008] Avoid circular references between packages; treat them as architecture defects.
- [TS-TSC-009] Default to TypeScript 5.x compiling to an ES2022 output baseline unless the runtime explicitly requires a different target; document any deviation.
- [TS-TSC-010] Prefer native platform features over polyfills; if down-level transpilation is required, record the trade-off and the surface it affects.
- [TS-TSC-011] Use pure ES modules for new code and bundling paths; do not emit
require,module.exports, or other CommonJS helpers unless an ADR captures the exception.
- [TS-TSC-012] Model real-time events, finite states, and workflows with discriminated unions so illegal states remain unrepresentable.
- [TS-TSC-013] Centralise shared contracts and DTOs instead of duplicating structural types in multiple modules.
- [TS-TSC-014] Use expressive utility types (
Readonly,Partial,Record, etc.) to document intent and reduce bespoke helper types.
- [TS-LINT-001] Use exactly one formatter: Prettier or Biome (repository choice).
- [TS-LINT-002] Do not fight the formatter. Avoid style rules that duplicate formatting.
- [TS-LINT-003] Enforce formatting in CI and in pre-commit.
- [TS-LINT-004] Use ESLint with typescript-eslint for TypeScript-aware linting.
- [TS-LINT-005] Prefer the modern ESLint configuration style (flat config) where supported.
- [TS-LINT-006] Use type-aware linting selectively:
- enable type-aware rules for high-value packages/modules
- keep CI time bounded; avoid making every package "full type-aware lint" if it slows feedback loops
- [TS-LINT-007] Prefer rules that prevent real production incidents:
- unsafe promises / missing awaits
- floating promises
- unhandled rejections
- incorrect equality / coercions
- implicit any / unsafe casts
- [TS-LINT-008] Avoid rules that only enforce personal preference unless the repo agrees.
- [TS-LINT-009] Match the repository's indentation, quote, and trailing comma rules; do not override formatter intent locally.
- [TS-LINT-010] Keep functions and methods tightly scoped; extract helpers when branches or responsibilities multiply.
- [TS-LINT-011] Favour immutable data structures and pure functions when practical to keep reasoning simple.
Section summary (key subsections):
- 7.1 Runtime determinism — stable, repeatable outputs
- 7.2 Async correctness — promise handling, timeouts, cancellation
- 7.3 Boundary correctness — input validation and domain conversion
- 7.4 CLI behaviour rules — subcommands, exit codes, stdout/stderr
- 7.5 HTTP behaviour rules — methods, status codes, retries, pagination
- [TS-BEH-001] Outputs must be deterministic:
- [TS-BEH-001a] stable ordering
- [TS-BEH-001b] stable formatting
- [TS-BEH-001c] no hidden randomness
- [TS-BEH-002] Time and randomness must be controllable in tests.
- [TS-BEH-003] Never "fire and forget" promises unless explicitly specified and safe.
- [TS-BEH-004] Await or explicitly handle promise outcomes (including background tasks).
- [TS-BEH-005] Define timeouts for outbound calls (HTTP, DB, queues).
- [TS-BEH-006] Propagate cancellation where practical (AbortController).
- [TS-BEH-007] Do not trust external inputs:
- [TS-BEH-007a] request bodies, query params, headers
- [TS-BEH-007b] environment variables and config files
- [TS-BEH-007c] message/event payloads
- [TS-BEH-007d] file contents
- [TS-BEH-008] Validate at the boundary, then convert to domain types.
- [TS-BEH-009] Prefer a consistent shape:
tool <command> <subcommand> [options]. - [TS-BEH-010] Use subcommands to separate behaviours, not positional-argument tricks.
- [TS-BEH-011] Keep commands single-responsibility; avoid "do everything" commands.
- [TS-BEH-011a] If behaviour diverges between the CLI and its shared library implementation, refactor to restore a single source of truth before adding new features.
- [TS-BEH-012] Exit codes must follow the shared CLI contract (
0success,1general failure,2usage error) unless an ADR records a justified deviation. - [TS-BEH-013] Never signal failure only via text; exit codes must reflect outcomes.
- [TS-BEH-014] When no specific mapping exists, default to
1for operational failures per the CLI contract and document any reserved codes. - [TS-BEH-015] If automation depends on specific failure modes, define, document, and test those exit codes referencing the CLI contract expectations.
- [TS-BEH-016] Follow the CLI contract stream semantics: emit primary outputs on
stdout, diagnostics onstderr. - [TS-BEH-017] Diagnostics (progress, warnings, debug, human-readable errors) must never contaminate
stdout. - [TS-BEH-018] Commands must behave correctly when stdout is piped or redirected; no diagnostic leakage to
stdout. - [TS-BEH-018a] Flush
stdout/stderrexplicitly in short-lived serverless environments so hosted runtimes do not truncate diagnostics.
- [TS-BEH-019] Long-running operations must be interruptible:
- [TS-BEH-019a] handle
Ctrl+Cgracefully - [TS-BEH-019b] avoid leaving partial/corrupt outputs
- [TS-BEH-019a] handle
- [TS-BEH-020] All outbound calls must have explicit timeouts.
- [TS-BEH-021] Provide a
--timeoutoption where it materially affects UX.
- [TS-BEH-022] Use correct methods:
GET(read),POST(create),PUT(replace),PATCH(partial update),DELETE(remove). - [TS-BEH-023] Use correct status codes:
200/204for successful reads/updates/deletes as appropriate201for create with aLocationheader when applicable400for invalid input,401unauthenticated,403unauthorised,404not found409for conflicts (including concurrency conflicts)422only where the chosen framework's conventions make it the correct and consistent choice429for rate limiting5xxonly for server faults
- [TS-BEH-024] Design for safe retries:
- [TS-BEH-024a]
GET,PUT,DELETEmust be idempotent by design. - [TS-BEH-024b]
POSTshould be idempotent when the client may retry (support idempotency keys where appropriate).
- [TS-BEH-024a]
- [TS-BEH-025] Make concurrency rules explicit:
- [TS-BEH-025a] use optimistic concurrency controls (ETags/version fields) where relevant
- [TS-BEH-025b] return
409on conflicts with a clear error code
- [TS-BEH-026] For collections, prefer:
- [TS-BEH-026a] pagination (cursor-based when feasible)
- [TS-BEH-026b] explicit filtering (query parameters)
- [TS-BEH-026c] stable sorting with deterministic defaults
- [TS-BEH-027] Make ordering rules explicit and deterministic.
- [TS-BEH-028] All outbound calls must have explicit timeouts.
- [TS-BEH-029] Avoid unbounded waits in request/response flows.
- [TS-BEH-030] Use
async/awaitflows with explicittry/catchblocks so errors map to the structured logging/telemetry strategy. - [TS-BEH-031] Guard edge cases at the top of functions to avoid deeply nested control flow.
- [TS-BEH-032] Route errors through the shared logging, tracing, and notification utilities instead of ad-hoc
consolecalls. - [TS-BEH-033] Surface user-facing failures via the repository's standard notification pattern so behaviour stays consistent across features.
- [TS-BEH-034] Debounce configuration-driven updates and dispose acquired resources deterministically to avoid leaks.
Define and document precedence. Prefer this order:
- CLI flags/options (if applicable)
- Environment variables
- Configuration file(s)
- Built-in defaults
- [TS-CFG-001] Validate configuration at startup and fail fast with a clear error.
- [TS-CFG-002] Do not silently coerce ambiguous config values.
- [TS-CFG-003] Never log secrets.
- [TS-CFG-004] Use a consistent prefix where applicable (for example
APP_...). - [TS-CFG-005] Document each variable and how it maps to flags/options.
- [TS-CFG-006] Default local configuration must be safe: do not require real cloud credentials, and do not enable destructive behaviour unless explicitly requested.
- [TS-CFG-007] Reach configuration through the repository's shared helpers/utilities instead of scattering direct
process.envaccess. - [TS-CFG-008] Validate configuration objects with schemas or dedicated validators so
undefined/invalid values are caught immediately. - [TS-CFG-009] Guard optional config and secret reads with explicit error messages rather than letting them flow as
undefined. - [TS-CFG-010] Document any new configuration key (and update corresponding tests) whenever you introduce it.
If the system depends on external services (cloud APIs, databases, third-party services), local feedback must still be fast.
- [TS-EXT-001] Default local behaviour must not require network access.
- [TS-EXT-002] Provide integration modes, in order of preference:
- Fake (in-process implementation for fast unit tests)
- Emulator (external local service, for example Docker-based DB, local stub servers)
- Real cloud (only for explicit integration runs)
- [TS-EXT-003] Use dependency injection / adapters so the system can swap implementations cleanly.
- [TS-EXT-004] Tests must not hit real cloud by default.
- [TS-EXT-005] Instantiate external clients outside hot paths and inject them so tests can swap implementations cheaply.
- [TS-EXT-006] Apply retries, backoff, and cancellation policies to network or IO calls according to the specification.
- [TS-EXT-007] Normalise third-party responses and map their errors to domain-level shapes before they leak deeper into the system.
- [TS-DATA-001] Validate inputs at the boundary using a schema library (repository choice).
- [TS-DATA-002] Validation must be deterministic, explicit, and testable.
- [TS-DATA-003] Prefer producing a structured list of validation errors over a generic failure message.
- [TS-DATA-004] Keep these concepts separate:
- [TS-DATA-004a] API models (request/response, event schemas)
- [TS-DATA-004b] domain models (business concepts and invariants)
- [TS-DATA-004c] persistence models (database representations)
- [TS-DATA-005] Do not expose persistence models directly through public APIs.
- [TS-DATA-006] Prefer additive changes (new optional fields) over breaking changes.
- [TS-DATA-007] If a field becomes deprecated, keep it readable for a defined period and document the migration.
Section summary (key subsections):
- 11.1 Fail explicitly — silent failure forbidden
- 11.2 Error shape — consistent machine-parseable structure
- 11.3 Logging exceptions — once, with context
- 11.3a Error classification — validation, domain, auth, not-found, conflicts, dependency
- 11.4 CLI error semantics — exit codes, error formatting
- 11.5 API error semantics — HTTP status codes, response structure
- 11.6 Debugging modes — verbose/debug flags
- [TS-ERR-001] Silent failure is forbidden.
- [TS-ERR-002] Partial success must be explicit (and specified).
- [TS-ERR-003] Do not return "success" responses with embedded error messages.
- [TS-ERR-004] Use a consistent, machine-parseable error structure, for example:
error_code(stable)message(plain language)details(optional structured details)correlation_id(when available)
- [TS-ERR-005] Map errors to stable status codes / exit codes.
- [TS-ERR-006] Do not leak secrets or sensitive internals in error messages.
- [TS-ERR-007] Log exceptions once, close to the boundary:
- include
error_code,correlation_id,trace_id(if present) - include stack traces in server logs only (never in client responses)
- [TS-ERR-007a] Emit the exception log at
ERRORlevel even when the software can recover, so operators always see the failure signal.
- include
- [TS-ERR-008] Avoid duplicate logging at multiple layers unless each log adds new information.
- [TS-ERR-009] Distinguish between:
- validation errors
- domain/business rule errors
- authentication/authorisation errors
- not found
- conflicts/concurrency errors
- dependency/boundary errors (database, network)
- [TS-ERR-010] Do not leak sensitive internals in error messages.
- [TS-ERR-011] Error output must be:
- [TS-ERR-011a] concise
- [TS-ERR-011b] plain language
- [TS-ERR-011c] actionable ("what to do next")
- [TS-ERR-012] Avoid stack traces by default.
- [TS-ERR-013] Where helpful, include:
- a stable
error_code - a short "next steps" hint
- a reference to documentation/runbook (if available)
- a stable
- [TS-ERR-014] Use the standard error structure from [TS-ERR-004] for all non-2xx responses (including validation errors).
- [TS-ERR-015] Do not return
200with an embedded error. - [TS-ERR-016] Ensure correlation ids are returned in response headers and in the body where applicable.
- [TS-ERR-017] Support controlled diagnostics (see also §5.1 of the baseline for level hierarchy):
- [TS-ERR-017a]
--verboseincreases detail (CLIs) - [TS-ERR-017b]
--debugmay include stack traces and enables function/method entry logging (trace-like behaviour; still never secrets)
- [TS-ERR-017a]
- [TS-ERR-018] Diagnostics must not change behaviour, only observability.
Observability is non-negotiable.
Section summary (key subsections):
- 12.1 Correlation and tracing — request identity and propagation
- 12.2 Logging — structured logs, what to include, what never to log
- 12.3 Metrics — golden signals and cardinality rules
- 12.4 Runbooks — actionable error references
- 12.5 Browser/UI observability — RUM, errors, and cross-tier correlation
- [TS-OBS-001] Accept an inbound request id (prefer
X-Request-Id) or generate one if missing. - [TS-OBS-002] Propagate correlation ids and trace context to all outbound calls.
- [TS-OBS-003] Return correlation ids to callers where applicable.
- [TS-OBS-004] Prefer structured logs (JSON) and follow the Structured Logging Baseline for canonical field definitions.
- [TS-OBS-005] Service/API logs must include the required metadata from section 1; do not remove or rename those fields locally.
- [TS-OBS-006] CLI/worker logs that emit structured output must include the CLI invocation fields from section 2.
- [TS-OBS-007] Apply the secrecy and event taxonomy rules from sections 3–4; never log secrets or personal data, and keep event names (
request.*,dependency.*, etc.) stable for automation.- [TS-OBS-007a] When verbose or debug logging is enabled, emit a single function/method entry log for every call path with the operation name and a sanitised summary of arguments per section 5; never include sensitive payloads.
- [TS-OBS-008] Provide golden signals: latency, traffic, errors, saturation.
- [TS-OBS-009] Avoid high-cardinality labels (no raw ids in metrics).
- [TS-OBS-010] For operationally meaningful errors, include a runbook reference (link or identifier) in logs and alerts.
- [TS-OBS-011] UI error reporting must be explicit and actionable:
- [TS-OBS-011a] capture unhandled errors and unhandled promise rejections
- [TS-OBS-011b] include build/version, route, and correlation id (when available)
- [TS-OBS-011c] avoid sending personal data
- [TS-OBS-012] Cross-tier correlation is mandatory for systems with both UI and API:
- [TS-OBS-012a] propagate
X-Request-Id(or equivalent) from UI to API - [TS-OBS-012b] return it to the UI and include it in client-side error reports
- [TS-OBS-012a] propagate
- [TS-OBS-013] Collect Real User Monitoring (RUM) signals where appropriate:
- [TS-OBS-013a] web vitals
- [TS-OBS-013b] long tasks
- [TS-OBS-013c] error rates by route
- [TS-OBS-013d] frontend-to-backend latency
- [TS-OBS-014] Keep client telemetry safe and bounded:
- [TS-OBS-014a] sample in production
- [TS-OBS-014b] avoid high-cardinality labels
- [TS-OBS-014c] never include raw identifiers or payloads
Section summary (key subsections):
- 13.1 Dependencies and supply chain — minimal deps, scanning, lock files
- 13.2 Secrets — never commit, inject at runtime
- 13.3 Web security — HTTPS, headers, CORS
- 13.4 Content handling — CSP, sanitisation
- [TS-SEC-001] Keep dependencies minimal.
- [TS-SEC-002] Pin dependencies via lock file and update intentionally.
- [TS-SEC-003] Run dependency and code security scanning in CI (repository tool choice).
- [TS-SEC-004] Treat high/critical findings as blocking unless waived in an ADR (rationale + expiry).
- [TS-SEC-005] Never commit secrets.
- [TS-SEC-006] Do not pass secrets via CLI args unless unavoidable (they leak via shell history and process lists).
- [TS-SEC-007] Use a secret manager where available; inject secrets at runtime.
- [TS-SEC-008] Do not log secrets or full tokens.
- [TS-SEC-009] Enforce HTTPS.
- [TS-SEC-010] Use strict CORS allow-lists (no
*for authenticated endpoints). - [TS-SEC-011] Use CSRF protection for cookie-based sessions.
- [TS-SEC-012] Validate and normalise untrusted inputs (including headers).
- [TS-SEC-013] Treat the UI as part of the security boundary:
- [TS-SEC-013a] do not trust client-side validation
- [TS-SEC-013b] do not assume "internal users" makes the UI safe
- [TS-SEC-014] Prevent XSS by default:
- [TS-SEC-014a] prefer safe DOM APIs and framework escaping
- [TS-SEC-014b] minimise dynamic HTML injection; where unavoidable, sanitise and constrain
- [TS-SEC-015] Use Content Security Policy (CSP) where feasible to reduce XSS impact.
- [TS-SEC-016] Never store access tokens in places vulnerable to XSS (for example
localStorage) unless the threat model explicitly accepts it and documents mitigations. - [TS-SEC-017] Prefer short-lived tokens and least-privilege scopes.
- [TS-SEC-018] Keep security headers and transport rules consistent across environments.
- [TS-SEC-019] Validate and sanitise external inputs with schema validators or type guards before they reach domain logic.
- [TS-SEC-020] Avoid dynamic code execution or untrusted template rendering; when templating is required, stick to vetted, sandboxed engines.
- [TS-SEC-021] Encode or escape untrusted content before rendering HTML, even on the server.
- [TS-SEC-022] Use parameterised queries or prepared statements for all persistence access to block injection.
- [TS-SEC-023] Keep secrets in managed storage, rotate them regularly, and request only least-privilege scopes.
- [TS-SEC-024] Prefer immutable data flows and defensive copies for sensitive information so downstream code cannot mutate it accidentally.
- [TS-SEC-025] Use vetted cryptography libraries; never hand-roll crypto primitives.
- [TS-SEC-026] Patch dependencies promptly and monitor advisory feeds; treat high/critical advisories as blocking until resolved.
- [TS-IO-001] Use atomic writes for output files (write temp then rename).
- [TS-IO-002] Define deterministic traversal and ordering rules.
- [TS-IO-003] Make error strategy explicit (fail-fast vs continue-with-report).
- [TS-IO-004] Treat external dependencies (DB, HTTP, queues, filesystem) as boundaries behind adapters:
- [TS-IO-004a] databases
- [TS-IO-004b] HTTP clients
- [TS-IO-004c] queues and event buses
- [TS-IO-004d] filesystem
- [TS-IO-005] Make transaction boundaries explicit:
- [TS-IO-005a] avoid implicit global transactions
- [TS-IO-005b] ensure consistency rules are specified and tested
- [TS-IO-006] Do not let business logic depend on framework request objects.
Per constitution.md §3.6, follow a test-first flow for behaviour changes:
- Write/update a unit test from the specification.
- Confirm it fails for the right reason.
- Implement the minimal change to pass.
- Refactor after green, preserving behaviour.
- [TS-TST-001] Prefer the test pyramid:
- [TS-TST-001a] most tests: unit (fast, deterministic, behaviour-focused)
- [TS-TST-001b] some tests: integration (filesystem, DB/emulators, HTTP stubs)
- [TS-TST-001c] few tests: end-to-end (critical journeys only)
- [TS-TST-002] Tests must be deterministic:
- [TS-TST-002a] control time and randomness
- [TS-TST-002b] avoid real network by default
- [TS-TST-002c] avoid relying on wall-clock sleeps; use fakes or polling with timeouts
- [TS-TST-003] Do not couple tests to implementation details unnecessarily.
- [TS-TST-004] Prefer a modern, fast unit test runner (for example Vitest) for most new projects.
- [TS-TST-005] Use Jest when ecosystem constraints require it (legacy projects or specific integrations).
- [TS-TST-006] Use Playwright for browser end-to-end tests where applicable.
- [TS-TST-007] For UI component testing, prefer user-centric testing practices over implementation detail tests.
- [TS-TST-008] Add or update unit tests whenever behaviour changes, following the repository's naming and layout conventions.
- [TS-TST-009] Expand integration or end-to-end suites when work spans multiple modules or touches external platforms.
- [TS-TST-010] Run targeted test commands for fast feedback before submitting larger suites, then finish with the canonical quality gates.
- [TS-TST-011] Avoid brittle timing assertions; use fake timers or injected clocks to keep async flows deterministic.
- [TS-TST-012] UI implementation or behaviour changes must ship with both component-level tests using the framework's preferred runner and screen-level Playwright journeys that cover primary flows, validation, accessibility states, and keyboard navigation; document any new selectors/test IDs in the spec or plan before relying on them.
Per constitution.md §7:
- [TS-CODE-001] Keep business/domain logic independent of frameworks and delivery mechanisms.
- [TS-CODE-002] Separate:
- [TS-CODE-002a] boundary adapters (HTTP, DB, queues, filesystem)
- [TS-CODE-002b] orchestration/use-cases
- [TS-CODE-002c] domain logic and policies
- [TS-CODE-002d] presentation/API models
- [TS-CODE-003] Prefer small modules with explicit exports; avoid "god modules".
- [TS-CODE-004] Align naming to domain language; avoid ambiguous "util" dumping grounds.
- [TS-CODE-005] Keep functions small and single-purpose.
- [TS-CODE-006] Order code to aid navigation:
- [TS-CODE-006a] entrypoints and public APIs first
- [TS-CODE-006b] key behaviour next
- [TS-CODE-006c] helpers near the behaviour they support
- [TS-CODE-006d] shared utilities clearly grouped
- [TS-CODE-007] Keep framework objects at the edges: do not let request/response objects leak into domain or use-case logic.
- [TS-CODE-008] Follow the repository's established folder and responsibility layout; extend existing abstractions before inventing new ones.
- [TS-CODE-009] Use kebab-case filenames (for example
user-session.ts) unless the repository explicitly chooses a different convention. - [TS-CODE-010] Keep related tests, types, and helpers near their implementation when it improves discovery without bloating modules.
- [TS-CODE-011] Reuse or extend shared utilities before adding a new helper; document the rationale when a new abstraction is unavoidable.
- [TS-CODE-012] Use PascalCase for classes, interfaces, enums, and type aliases; camelCase for functions, variables, and instances.
- [TS-CODE-013] Skip interface prefixes such as
I; rely on descriptive names instead. - [TS-CODE-014] Name modules and symbols for the behaviour or domain meaning they deliver, not the underlying implementation detail.
- [TS-CODE-015] Follow the repository's dependency injection/composition approach so modules stay single-purpose and testable.
- [TS-CODE-016] Respect initialise/dispose sequences and provide lifecycle hooks (plus targeted tests) when wiring new services.
- [TS-BLD-001] Prefer a single source of truth for builds:
- typecheck (
tsc --noEmit) is always required - runtime build/bundle depends on target (Node service vs library vs web app)
- typecheck (
- [TS-BLD-002] Produce sourcemaps for production debugging where supported and safe.
- [TS-BLD-003] Ensure builds are reproducible (clean install + deterministic outputs).
- [TS-BLD-004] Decide and document supported runtimes (Node version range, browser targets).
- [TS-BLD-005] Provide correct
exportsand types for each entrypoint. - [TS-BLD-006] Verify declaration output matches runtime output (no missing exports).
- [TS-BLD-007] Use semantic versioning for public packages and stable CLIs/APIs.
- [TS-BLD-008] Breaking changes must be explicit, documented, and reviewable.
- [TS-PERF-001] Avoid premature optimisation, but do not allow unbounded inefficiency.
- [TS-PERF-002] Use timeouts, retries, and backoff for outbound calls (where safe):
- [TS-PERF-002a] explicit timeouts
- [TS-PERF-002b] retries with backoff
- [TS-PERF-002c] circuit-breaking patterns where appropriate
- [TS-PERF-003] Prefer streaming/pagination for large datasets.
- [TS-PERF-004] Avoid unbounded concurrency; cap parallelism where relevant.
- [TS-PERF-005] Make performance characteristics explicit and testable where relevant.
- [TS-PERF-006] Lazy-load heavy dependencies and dispose them when no longer needed.
- [TS-PERF-007] Defer expensive computation until a user or downstream system actually needs it.
- [TS-PERF-008] Batch or debounce high-frequency events to avoid thrashing the runtime and backend services.
- [TS-PERF-009] Track resource lifetimes (files, sockets, observers, timers) to prevent leaks.
Per constitution.md §3.5, when you create or modify code:
- [TS-AI-001] Follow the shared AI change baseline for scope, quality, and governance.
- [TS-AI-002] Update documentation/contracts only when required by the specification.
This section defines a framework-agnostic baseline for building maintainable, accessible, secure, and fast UIs with TypeScript.
- [TS-UI-001] Meet WCAG 2.2 Level AA expectations unless an ADR explicitly documents why not.
- [TS-UI-002] Keyboard accessibility is mandatory:
- every interactive element is reachable and operable with the keyboard
- focus order matches the visual order
- focus is always visible
- [TS-UI-003] Prefer semantic HTML first:
- buttons are
<button>, links are<a>, form controls use native elements - do not build custom widgets when native elements provide the behaviour
- buttons are
- [TS-UI-004] Use ARIA only when required, and only according to established patterns:
- roles and labels must match actual behaviour
- do not add ARIA that changes meaning incorrectly
- [TS-UI-005] When you implement custom widgets, follow standard keyboard interaction patterns (for example arrow-key navigation in composite widgets).
- [TS-UI-006] Forms must be accessible:
- label every input
- associate error messages to the field
- ensure errors are announced to assistive tech
- [TS-UI-007] Do not rely on colour alone to convey meaning.
- [TS-UI-008] Maintain adequate contrast and respect user preferences:
- reduced motion
- high contrast modes
- [TS-UI-009] Provide accessible names for controls (label/aria-label/aria-labelledby).
- [TS-UI-010] Manage focus intentionally:
- focus is moved after navigation/modals where appropriate
- trap focus only when needed (for example modal dialogs)
- provide an escape path (Esc closes modals where applicable)
- [TS-UI-011] Dynamic content changes must be perceivable:
- use live regions when necessary
- avoid aggressive announcements
- [TS-UI-012] Prefer predictable interaction patterns over novelty.
- [TS-UI-013] Provide clear feedback for async actions:
- loading states
- disabled states (with explanation where useful)
- error states with a recovery path
- [TS-UI-014] Avoid "toast-only" error reporting for critical failures; ensure errors are discoverable and persistent when needed.
- [TS-UI-015] Use consistent copy and content patterns:
- concise, plain language
- action-oriented button labels
- [TS-UI-016] Support responsive layouts (small screens and zoom).
- [TS-UI-017] Prefer progressive disclosure for complex flows.
- [TS-UI-018] Avoid dark patterns (misleading opt-ins, hidden fees, confusing defaults).
- [TS-UI-019] Treat UI as a composition of:
- presentation components (pure rendering)
- containers/orchestrators (data fetching, state, policies)
- adapters (API clients, storage, analytics)
- [TS-UI-020] Keep side effects at the edges:
- fetch, storage, navigation, analytics, timers
- do not hide side effects in "utility" functions
- [TS-UI-021] Prefer unidirectional data flow:
- state transitions are explicit
- avoid hidden shared mutable state
- [TS-UI-022] Define component contracts:
- explicit props interfaces
- stable event callback names (
onChange,onSubmit, etc.) - avoid "bag of options" props
- [TS-UI-023] Keep components small and single-responsibility.
- [TS-UI-024] Avoid prop drilling for cross-cutting concerns; use a deliberate state/context solution where needed.
- [TS-UI-025] Keep routing concerns at the edge (route params validated at the boundary).
- [TS-UI-026] Separate:
- server state (fetched data, caching, invalidation)
- UI state (local view state, transient UI flags)
- form state (validation, dirty tracking)
- [TS-UI-027] Prefer declarative data fetching and caching where appropriate (repository choice), but keep domain rules outside of framework hooks.
- [TS-UI-028] All client-to-server requests must have:
- explicit timeouts
- cancellation (AbortController) where practical
- retry behaviour that is safe and bounded
- [TS-UI-029] Never assume the UI is the only writer:
- handle stale data and concurrency (optimistic updates only with clear rollback)
- [TS-UI-030] Validate data from APIs at the boundary (schema validation) before use.
- [TS-UI-031] Design for fast interaction responsiveness and avoid long tasks on the main thread.
- [TS-UI-032] Treat Core Web Vitals as a first-class performance signal:
- responsiveness (INP)
- loading (LCP)
- visual stability (CLS)
- [TS-UI-033] Prefer performance budgets:
- bundle size budgets
- route-level loading budgets
- "no regressions" alerts in CI where feasible
- [TS-UI-034] Use code splitting and lazy loading intentionally:
- keep initial routes lean
- prefetch only when it improves real user outcomes
- [TS-UI-035] Optimise assets:
- responsive images
- compression
- caching headers and immutable asset fingerprints
- [TS-UI-036] Avoid unnecessary re-renders and expensive computations:
- memoise intentionally (do not cargo-cult)
- move expensive work off the main thread when appropriate (workers)
- [TS-UI-037] Prefer "measure then improve":
- use lab tooling (Lighthouse) and real-user telemetry (RUM) where appropriate
- [TS-UI-038] Prevent XSS:
- avoid rendering untrusted HTML
- sanitise and constrain if unavoidable
- [TS-UI-039] Use defence-in-depth:
- CSP where feasible
- dependency pinning and audits
- strict transport rules
- [TS-UI-040] Treat supply-chain risk as a UI risk:
- minimise dependencies
- prefer well-maintained packages
- audit transitive dependency changes on upgrades
- [TS-UI-041] Store credentials safely:
- prefer HttpOnly secure cookies for browser sessions where feasible
- avoid long-lived tokens in browser storage unless justified in an ADR
- [TS-UI-042] Avoid leaking sensitive information in:
- error messages
- client logs/telemetry
- page source / build artefacts
- [TS-UI-043] Use a layered testing strategy:
- unit tests for pure functions and reducers
- component tests for rendering and user interactions
- end-to-end tests for critical journeys (Playwright)
- [TS-UI-044] Prefer user-centric testing for UI components (what the user sees and does), not internal component details.
- [TS-UI-045] Accessibility checks must exist:
- automated linting where available
- automated checks in tests (for example axe-based checks) where practical
- manual keyboard and screen-reader smoke checks for key journeys
- [TS-UI-046] Test error and loading states explicitly.
- [TS-UI-047] Visual regressions are allowed where they provide real confidence, but keep snapshots small and meaningful.
- [TS-UI-048] Choose one styling strategy per repo (document it):
- CSS modules, utility CSS, CSS-in-JS, etc.
- [TS-UI-049] Use a design system (or component library) deliberately:
- centralise typography, spacing, colours, and component primitives
- keep the public "design API" stable
- [TS-UI-050] Theme must preserve accessibility:
- contrast requirements
- focus states
- reduced motion support
- [TS-UI-051] If the product is user-facing, i18n must be considered early:
- avoid hard-coded strings in components where translation is expected
- support pluralisation and date/number formatting
- [TS-UI-052] Do not couple UI layout to English-only text lengths.
- [TS-UI-053] Keep UI layers thin; push heavyweight business logic into services or state managers that can be tested independently.
- [TS-UI-054] Use messaging or event channels to decouple UI components from business logic so changes stay isolated.
- [TS-DOC-001] Add JSDoc (with
@remarks/@examplewhere useful) to public APIs so intent stays clear to both humans and tools. - [TS-DOC-002] Write comments that capture intent and trade-offs; remove or update them whenever the behaviour changes.
- [TS-DOC-003] Update architecture/design documentation when you introduce notable patterns, lifecycle hooks, or dependencies so the wider system view stays current.
- [TS-ANT-001] Do not use
anyexcept as a last resort with a justifying comment; preferunknownat boundaries. - [TS-ANT-002] Do not fire-and-forget promises; always await or explicitly handle outcomes.
- [TS-ANT-003] Do not make outbound calls without explicit timeouts.
- [TS-ANT-004] Do not log secrets, tokens, or PII.
- [TS-ANT-005] Do not pass secrets via CLI arguments; use environment variables or secret managers.
- [TS-ANT-006] Do not let functions exceed ~50 lines or 3 levels of nesting without extraction.
- [TS-ANT-007] Do not let modules exceed ~500 lines (excluding tests) without splitting by responsibility.
- [TS-ANT-008] Do not use
console.login production code; use structured logging utilities. - [TS-ANT-009] Do not ignore linter or type-checker warnings; fix them or explicitly waive with rationale.
- [TS-ANT-010] Do not expose persistence/internal models directly through public APIs.
- [TS-ANT-011] Do not return HTTP 200 with embedded error messages; use correct status codes.
- [TS-ANT-012] Do not use unbounded concurrency; cap parallelism explicitly.
- [TS-ANT-013] Do not rely on wall-clock sleeps in tests; use fake timers or polling with timeouts.
- [TS-ANT-014] Do not hit real cloud/network in unit tests by default.
- [TS-ANT-015] Do not store access tokens in
localStoragewithout documented threat-model acceptance.
Version: 1.6.0 Last Amended: 2026-01-20