All findings from the inaugural LeadValidator audit resolved and confirmed. Release gate: PASS. VV_ISSUE_002 (BLOCKER): 15 OpenAPI specs verified present covering all 20 route groups (46 endpoints documented in docs/openapi/) VV_ISSUE_003 (MAJOR): Remove any types from src/db/pool.ts — replaced pool.query shim with unknown[] + Object.defineProperty, zero any types, eslint-disable suppressions removed VV_ISSUE_004 (MAJOR): Remove raw Pool from ScaffoldController and HealthDetailedController — injected AgentRepository/CredentialRepository and DbProbe interface respectively; added CredentialRepository.findActiveClientId() VV_ISSUE_005 (MAJOR): Add unit tests for 5 untested services — ComplianceStatusStore, EventPublisher, MarketplaceService, OIDCTrustPolicyService, UsageService VV_ISSUE_006 (MAJOR): Add integration tests for 7 missing route groups — analytics, billing, tiers, webhooks, marketplace, oidc-trust-policies, oidc-token-exchange VV_ISSUE_001 (MINOR): Create missing design.md and tasks.md in 4 OpenSpec archives — all archives now complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.4 KiB
VV_ISSUE_003 — any type usage in src/db/pool.ts
Status: RESOLVED Severity: MAJOR Category: TYPE_VIOLATION Logged by: LeadValidator Date: 2026-04-07 Audit phase: Phase C — TypeScript Standards Audit
Finding
The PRD (Sections 6.4 and 4.5) states: "No any types — ever." and "TypeScript strict mode:
zero any types." The tsconfig.json correctly enables noImplicitAny: true and all strict
flags.
Despite this, src/db/pool.ts contains explicit any type casts on two lines (lines 89 and
91), with ESLint suppression comments (eslint-disable-next-line @typescript-eslint/no-explicit-any)
added to bypass the linting rule. While the developer included a comment explaining the
technical reason (wrapping the pg query method in a shim that is difficult to type precisely),
the PRD standard is absolute: zero any types, with no exceptions granted.
The intent of the standard is to eliminate unsafe escape hatches that weaken TypeScript's
type safety guarantees. Using any in the database pool layer — a critical path component —
is particularly concerning since this wraps every database query in the application.
Evidence
File: src/db/pool.ts, lines 88–91:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const originalQuery = pool.query.bind(pool) as (...args: any[]) => Promise<any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(pool as any).query = async (...args: any[]): Promise<any> => {
There are 5 any occurrences on these two lines:
(...args: any[]) => Promise<any>— line 89(pool as any)— line 91(...args: any[]): Promise<any>— line 91
The surrounding comment (lines 84–87) acknowledges the issue and explains the rationale, but does not resolve it per PRD standards.
Required Action
Replace the any types in src/db/pool.ts with properly typed alternatives. Options:
- Use the
pglibrary's exportedQueryConfig,QueryResultRow, and overload signatures to type the query wrapper correctly without resorting toany. - Use generic types:
<T extends QueryResultRow>(...args: Parameters<Pool['query']>) => Promise<QueryResult<T>> - If the shim cannot be typed without
anydue to pg's type definitions, document the specific technical constraint and seek CEO approval for a formal exemption. An exemption does NOT mean leaving it as-is — it means a tracked, acknowledged deviation.
Remove the eslint-disable-next-line suppression comments once the any types are resolved.
CTO Response
Agreed. The any types in pool.ts were a sanctioned workaround for pg's overloaded Pool.query signature. The correct solution is to use unknown[] rest parameters and Object.defineProperty to replace the method without widening the pool reference to any. The pool's typed interface is preserved at the type level for all callers; only the internal shim uses unknown as the safe alternative to any.
Resolution
Fixed in: src/db/pool.ts
Replaced the two any-typed lines and two eslint-disable-next-line suppressions with a clean Object.defineProperty shim:
originalQueryis typed viapool.query.bind(pool)— no cast needed- The replacement function uses
unknown[]rest params andPromise<unknown>— zeroanytypes - TypeScript compiles clean (
npx tsc --noEmit— 0 errors) - Pool's typed interface (
Pool['query']) unchanged for all callers