Pre-M6 Type Safety — Backend any Elimination
This release completes the pre-M6 type safety pass across the backend. Every any type in the route layer, service layer, and infrastructure files has been addressed — either eliminated with a concrete type, or suppressed with a documented justification where TypeScript cannot express the boundary.
What Changed
AuditLogger interface introduced. The request-scoped audit decorator previously used a null as any placeholder. A typed AuditLogger interface now gives the decorator a proper zero value — a no-op implementation that silently discards calls on unauthenticated routes and is overridden with the real RLS-aware logger for all tenant-scoped requests.
SQL parameter arrays typed throughout. Dynamic query builders that accumulated parameters into any[] now use (string | number | boolean | null)[], matching the actual values pushed into each array.
Stored JSON blob interfaces. The ODCE document import pipeline (bill imports and invoice imports) now has a typed StoredDocumentRow interface covering both the standalone upload path and the migration wizard nested path. The aging report contact maps now use a typed AgingInvoiceEntry interface.
Stripe SDK gaps handled correctly. Fields absent from the Stripe TypeScript definitions (current_period_end, trial_end, subscription on Invoice) are now accessed via targeted intersection types rather than as any casts.
Helper function signatures corrected. Standalone async helpers taking a database client now use PoolClient rather than ReturnType<typeof Object> with inline casts. The awkward (dbClient as { query: Function }) double-cast pattern is eliminated across the ODCE route files.
Catch clauses typed. All catch (err: any) clauses are now catch (err: unknown) with appropriate narrowing via instanceof Error or structural checks for database error codes.
Two justified suppressions documented. decimal-config.ts retains as any for the ESM/CJS interop boundary — no TypeScript construct can express “this import may carry a .default wrapper” without losing the constructable class type on one branch. invoice-service.ts retains any[] on the invoices return field — the caller performs a deliberate cast to an internal row type, and tightening the return breaks that boundary. Both are dual-suppressed with lint-ignore and eslint-disable comments explaining the rationale.
Files Affected
20 backend files across routes, services, and infrastructure. No SQL, no financial arithmetic, no transaction boundaries, no schema changes, no API response shape changes. Build passes clean. Lint reduces from 115 errors to 1 (suppressed, irreducible).
Kernel Integrity
No kernel invariants were modified. All changes are type annotations applied above the financial kernel. The compiled JavaScript output is identical to the previous release in all affected files.