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>
71 lines
3.4 KiB
Markdown
71 lines
3.4 KiB
Markdown
# 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:
|
||
|
||
```typescript
|
||
// 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:
|
||
|
||
1. Use the `pg` library's exported `QueryConfig`, `QueryResultRow`, and overload signatures
|
||
to type the query wrapper correctly without resorting to `any`.
|
||
2. Use generic types: `<T extends QueryResultRow>(...args: Parameters<Pool['query']>) => Promise<QueryResult<T>>`
|
||
3. If the shim cannot be typed without `any` due 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:
|
||
- `originalQuery` is typed via `pool.query.bind(pool)` — no cast needed
|
||
- The replacement function uses `unknown[]` rest params and `Promise<unknown>` — zero `any` types
|
||
- TypeScript compiles clean (`npx tsc --noEmit` — 0 errors)
|
||
- Pool's typed interface (`Pool['query']`) unchanged for all callers
|