# Test rot triage — 2026-06-16

**Branch:** `fix/test-rot-cleanup` @ `cd44d5d13` (pglite dep commit)  
**Base:** `main` @ `a72dcb8b9`  
**Runner:** `pnpm --filter web test --run` (vitest 4.1.6, jsdom default)

## Run metadata

| Metric | Value |
|--------|------:|
| Test files | **112 failed** / 257 passed / 369 total |
| Tests | **395 failed** / 1767 passed / 2217 total |
| Unit files failed | **35** |

**Harness notes (not failures):**

- Worktree initially lacked `vitest.config.ts`, `tests/setup.ts`, and gitignored tests under `apps/web/tests/**`. Full suite synced from main working tree for this triage (read-only; not committed).
- `apps/web/scripts/cpu-limit.sh` was missing from worktree; copied from main for the run.
- Step 1 commit: `cd44d5d13` — `test(deps): add @electric-sql/pglite for fraud integration suites`
- `pnpm --filter web typecheck` — exit 0 after Step 1.

---

## Root cause clusters

### RC_BYTES_TO_HEX_CROSS_REALM

**One-line:** `bytesToHex(ArrayBuffer)` returns `''` in vitest/jsdom because Web Crypto digest buffers fail `instanceof ArrayBuffer` (cross-realm); shipped auth/feed hashing code is likely fine on CF Workers.

**symptom_files:**

- `tests/unit/jwt.test.ts`
- `tests/unit/filter-hash.test.ts`
- `tests/unit/feed-query.test.ts`
- `tests/unit/translation/normalize.test.ts`
- `tests/unit/auth/email-verification.test.ts`

**Code actually does** (`src/lib/encoding.ts`):

```ts
export function bytesToHex(bytes: Uint8Array | ArrayBuffer): string {
  const arr = bytes instanceof ArrayBuffer ? new Uint8Array(bytes) : bytes;
  return Array.from(arr).map((b) => b.toString(16).padStart(2, '0')).join('');
}
```

`hashRefreshToken` / `hashUnit` / `canonicalFilterHash` all call `crypto.subtle.digest` then `bytesToHex(buf)`:

```ts
// src/server/auth/tokens.ts:158-160
const buf = await crypto.subtle.digest('SHA-256', enc.encode(rt));
return bytesToHex(buf);
```

**Vitest/jsdom evidence:** digest `buf.byteLength === 32` but `buf instanceof ArrayBuffer === false` (constructor name `ArrayBuffer`). Branch treats buffer as `Uint8Array`, `Array.from` yields `[]`, hex length 0. `bytesToHex(new Uint8Array(buf))` returns 64 chars.

**Test expects:** non-empty 64-char lowercase hex (`jwt.test.ts:90-94`), distinct hashes (`jwt.test.ts:84-87`), email token hash length 64 (`email-verification.test.ts`).

**recommended_direction:** `code_wrong` — harden `bytesToHex` to normalize via `new Uint8Array(bytes as ArrayBufferLike)` without relying on `instanceof`. Low-risk defensive fix; also unblocks auth-hash unit coverage.

**evidence_for_direction:** Auth refresh-token hashing ships (`session.ts`, `refresh.ts`, `logout.ts`). Cross-realm `instanceof` is a known jsdom footgun; production CF isolate likely same-realm. Still worth fixing encoding helper because `hashIp` (`src/server/security/ip.ts:25-27`) has the same pattern.

**blast_radius:** `src/lib/encoding.ts` — every HMAC/hash hex path (auth tokens, feed filter hash, translation memory, IP buckets). **SEV-1 if any production runtime hits the broken branch** (suspect test-only today).

**sev-1:** y (auth hashing path — verify on CF before dismissing)

---

### RC_ZOD_FIXTURE_STALE

**One-line:** Unit tests pass obsolete field names/shapes; Zod schemas were updated, fixtures were not.

**symptom_files:**

- `tests/unit/cart-schemas.test.ts`
- `tests/unit/schemas/vendor.test.ts`
- `tests/unit/db/queries/visibility.test.ts`

**Code actually does:**

```ts
// src/server/schemas/cart.ts:23-27
export const addCartItemBodySchema = z.object({
  dealSkuId: uuidSchema,
  qty: qtySchema,
  csrfToken: z.string().min(1).optional(),
});
```

```ts
// src/server/schemas/vendor.ts:78-98 — imageSet required on create
export const createDealBodyShape = z.object({
  ...
  imageSet: imageSetSchema,
});
```

**Test expects:** `dealId` in cart body (`cart-schemas.test.ts:17`), `validCreate` without `imageSet` (`vendor.test.ts:15-23`), visibility query shapes that no longer match.

**recommended_direction:** `test_wrong` — update fixtures to `dealSkuId`, add minimal `imageSet`, refresh visibility mocks.

**evidence_for_direction:** Schema comments state zod-at-boundary law; field rename `dealId` → `dealSkuId` is intentional API shape. Vendor `imageSet` required since image-set work landed.

**blast_radius:** Tests only; production validates correct shapes at API boundary.

**sev-1:** n

---

### RC_SCHEMA_CONTRACT_INTENTIONAL

**One-line:** Admin support PATCH schema intentionally allows empty body; tests still assert throw.

**symptom_files:**

- `tests/unit/admin-support/schemas.test.ts`

**Code actually does:**

```ts
// src/server/schemas/support/admin.ts:158-166
export const supportSettingsPatchSchema = z.object({
  support_vendor_window_hours: z.coerce.number().int().min(1).max(720).optional(),
  ...
}); // Empty body is a valid no-op PATCH — caller treats it as 200 no-op
```

**Test expects:** `supportSettingsPatchSchema.parse({})` throws (`schemas.test.ts:178-179`); `ai_confidence_threshold > 1` throws (field removed from schema).

**recommended_direction:** `test_wrong` — align tests with documented no-op PATCH contract; drop removed-field assertions.

**evidence_for_direction:** Inline comment is explicit product intent.

**blast_radius:** Admin support settings API — shipped behavior allows empty PATCH.

**sev-1:** n

---

### RC_MOCK_BUILDER_GAP

**One-line:** Unit/integration tests use hand-rolled Drizzle mocks missing chained methods the real queries call.

**symptom_files:**

- `tests/unit/workflows/cart-checkout.test.ts`
- `tests/unit/workflows/redemption.test.ts`
- `tests/unit/support/kb.test.ts`
- `tests/unit/cron/alarm-reconciliation.test.ts`
- `tests/unit/server/db/notifications-resolved-at-propagation.test.ts`
- `tests/unit/auth/applyRateLimitFor.test.ts`
- `tests/unit/page-layout/loader.test.ts` (missing `prefetchPageDeals` on FeedDataLoader mock)
- `tests/integration/translation/enqueue.int.test.ts`
- `tests/integration/db/queries/categories-with-translations.int.test.ts`

**Code actually does:** Real modules chain Drizzle builders, e.g.:

```ts
// kb.test failure site
db.select(...).from(...).innerJoin is not a function
// cart-checkout
db.select(...).from(...).where(...).orderBy is not a function
// redemption
db.update(...).set(...).where(...).returning is not a function
```

**Test expects:** Mock `db` to behave like Drizzle; incomplete mocks throw before assertions run.

**recommended_direction:** `test_wrong` — extend shared mock factory or use pglite/integration for these paths.

**evidence_for_direction:** Production code paths are structurally valid Drizzle; failures are `TypeError` on mock, not assertion mismatches on business output.

**blast_radius:** Test-only unless mocks hide real regressions — cart checkout and redemption are **money paths**; gaps mean shipped logic is **untested**, not proven broken.

**sev-1:** y (money paths lack coverage — not proven code bug)

---

### RC_ORPHANED_AI

**One-line:** `resolveAiForJob` / `resolveAiCredentials` removed in `05f821b46`; replaced by `resolveQueueForJobType`; tests still import/spy deleted APIs.

**symptom_files:**

- `tests/unit/server/ai/runner.test.ts`
- `tests/unit/server/ai/credentials.test.ts`

**Code actually does:**

```ts
// src/server/ai/credentials.ts:20-32 — only export left
export async function resolveQueueForJobType(
  db: DrizzleClient,
  piiKey: string,
  jobType: LlmJobType,
): Promise<{ provider: ChainedLlmProvider; model: string }> { ... }
```

```ts
// src/server/ai/runner.ts:153
const resolution = await resolveQueueForJobType(db, env.PII_KEY, jobRow.jobType);
```

**Test expects:**

```ts
// runner.test.ts:53
vi.spyOn(credsMod, 'resolveAiForJob').mockResolvedValue({...})
// credentials.test.ts:2
import { resolveAiCredentials, resolveAiForJob } from '@/server/ai/credentials';
```

**recommended_direction:** `obsolete_test` — rewrite credentials/runner tests around `resolveQueueForJobType` + `getLlmQueue` chain (commit `05f821b46` confirms rename/fold, not accidental deletion).

**evidence_for_direction:** `git show 05f821b46` — "add resolveQueueForJobType, remove resolveAiForJob". Runner already calls new API.

**blast_radius:** AI job runner ships; behavior changed, tests stale.

**sev-1:** n (moderation LLM, not payments)

---

### RC_AI_REGISTRY_POPULATED

**One-line:** Registry test expects empty `JOB_KIND_REGISTRY`; production registry pre-registers 4+ kinds.

**symptom_files:**

- `tests/unit/server/ai/kinds/types.test.ts`

**Code actually does:**

```ts
// src/server/ai/kinds/registry.ts:26-30
export const JOB_KIND_REGISTRY: Record<LlmJobType, JobKind<...> | undefined> = {
  IMAGE_APPROVAL: imageApprovalKind,
  DEAL_MODERATION: dealModerationKind,
  TRANSLATION: translationKind,
  ...
};
```

**Test expects:** `Object.keys(JOB_KIND_REGISTRY)).toHaveLength(0)` (`types.test.ts:6-7`).

**recommended_direction:** `obsolete_test` — delete or rewrite "starts empty" assertion; registry is intentionally populated at import.

**blast_radius:** AI runner kind dispatch — shipped.

**sev-1:** n

---

### RC_BROKEN_IMPORT

**One-line:** Tests import moved/deleted modules.

**symptom_files:**

- `tests/unit/admin/search.test.ts` → `@/server/admin/_shared/route-manifest` (deleted)
- `tests/components/features/VendorLanding.test.tsx` → `@/features/vendor-onboarding/VendorLanding` (missing)
- `tests/integration/api/admin/languages.int.test.ts` → `../../e2e/fixtures/auth-helper` (gitignored path)

**Code actually does:**

```ts
// src/server/admin/search.ts:24
import { ADMIN_ROUTES } from './_shared/admin-nav.js';
```

```ts
// src/server/admin/_shared/admin-nav.ts:21
// Backward-compat aliases (importers of route-manifest.ts used these names)
```

**Test expects:** `route-manifest` module (`search.test.ts:7`).

**recommended_direction:** `test_wrong` — update imports to `admin-nav`; restore or stub e2e fixtures for integration tests.

**blast_radius:** Admin search ships with `admin-nav`; test import stale only.

**sev-1:** n

---

### RC_I18N_TEST_HARNESS

**One-line:** Component tests don't initialize i18n bundles or partial `vi.mock` of `@/lib/i18n/react` missing `LocaleProvider` / `useLocale`.

**symptom_files (37 total):**

*Bundles not initialized (23):*

- `tests/components/CountdownTimer.test.tsx`
- `tests/components/DealCard.test.tsx`
- `tests/components/DraftsBanner.test.tsx`
- `tests/components/HydratedIsland.test.tsx`
- `tests/components/LoadingOverlay.test.tsx`
- `tests/components/Spinner.test.tsx`
- `tests/components/a11y/AccessibilityTrigger.test.tsx`
- `tests/components/a11y/SkipLink.test.tsx`
- `tests/components/domain/AdminDetailPage.test.tsx`
- `tests/components/domain/BusinessProfile.test.tsx`
- `tests/components/domain/CountdownTimer.test.tsx`
- `tests/components/features/DealDetail.test.tsx`
- `tests/components/features/DesignSystem.test.tsx`
- `tests/components/feedback/ErrorBoundary.test.tsx`
- `tests/components/feedback/InlineNotice.test.tsx`
- `tests/components/feedback/RetryPanel.test.tsx`
- `tests/components/feedback/Spinner.test.tsx`
- `tests/components/layout/BottomNav.test.tsx`
- `tests/components/layout/ManifestoHero.test.tsx`
- `tests/components/layout/VendorCenteredShell.test.tsx`
- `tests/components/overlays/Dialog.test.tsx`
- `tests/components/primitives/LabelWithTooltip.test.tsx`
- `tests/components/primitives/NumberInput.test.tsx`

*Partial i18n mock — missing exports (14):*

- `tests/components/admin/AiVerdictPanel.test.tsx`
- `tests/components/admin-support/SupportSettings.test.tsx`
- `tests/components/domain/WishlistButton.test.tsx`
- `tests/components/features/AddDeal.test.tsx`
- `tests/components/features/OtpLogin.test.tsx`
- `tests/components/features/Profile.test.tsx`
- `tests/components/features/QrScreen.test.tsx`
- `tests/components/features/VendorQrScan.test.tsx`
- `tests/components/features/adddeal-step-transition.test.tsx`
- `tests/components/features/admin-dealdetail-lazy.test.tsx`
- `tests/components/features/admin-vendordetail-lazy.test.tsx`
- `tests/components/layout/VendorBottomNav.test.tsx`
- `tests/components/layout/VendorDealCard.test.tsx`
- `tests/components/layout/VendorSidebar.test.tsx`

**Code actually does:** `useT()` throws if `window.__I18N__` / server bundles unset (error: `i18nMiddleware must call setBundles()`).

**Test expects:** Components render without test-harness i18n init.

**recommended_direction:** `test_wrong` — add `setBundles` / `LocaleProvider` to `tests/setup.ts` or per-file wrappers; extend `vi.mock('@/lib/i18n/react')` with full export surface.

**blast_radius:** UI components ship with real i18n in Astro layout; test harness gap only.

**sev-1:** n

**RESOLUTION (2026-06-17, commit `408a0cacb`):** harness fixed — `tests/setup.ts` `setBundles({he,en})` + `tests/helpers/i18n-react-mock.tsx` shared mock (full `@/lib/i18n/react` surface). **20/37 green (96 tests), committed.** Whole-component-tree regression run: **zero green→red** from the global `setBundles` (verified against this doc's baseline; lone out-of-baseline red = `onboarding/BirthdayField.test.tsx`, a pre-existing missing-`useLocale`-mock-export rot the survey missed — see below, NOT a regression). 17 remainder routed:

- **Bucket 1 — harness-class, follow-on wave (7):** `DealCard`, `features/admin-vendordetail-lazy`, `layout/VendorSidebar` (no QueryClient), `features/DealDetail` (IntersectionObserver undef), `domain/WishlistButton` (mock missing `WishlistContext`), `features/Profile` (mock missing `useAddAddress`), `features/VendorQrScan` (BarcodeDetector undef). Fix the generic harness gap (QueryClient provider wrapper + IntersectionObserver polyfill + complete the 2 mock exports), then **match-then-triage** the now-running assertions — any that fail on real drift drop to Bucket 2.
- **Bucket 2 — leave IGNORED (tracked debt, NOT in KEEP set, 10):** `CountdownTimer`, `DraftsBanner`, `LoadingOverlay`, `domain/AdminDetailPage`, `feedback/Spinner`, `layout/VendorDealCard` (component assertion drift); `a11y/AccessibilityTrigger` + `primitives/NumberInput` (assert English/keys, component renders real Hebrew — prod default locale = `he` per `z.setErrorMap(createZodErrorMap('he'))`, so **test_wrong**); `features/OtpLogin` (placeholder-not-text query); `admin-support/SupportSettings` (asserts removed AI section — removal deliberate per `887cfc193 feat(cs-agent-ui): remove per-definition fields`, so **test_wrong stale**).

**NEW pre-existing failure (survey-missed, 2026-06-17):** `tests/components/onboarding/BirthdayField.test.tsx` — renders `Select` which calls `useLocale()`; the test's own local `vi.mock('@/lib/i18n/react')` exports only `useT`, so `No "useLocale" export on the mock`. Same missing-mock-export class as `WishlistButton`/`Profile`. `Select.useLocale` predates this work (in HEAD); structurally independent of `setBundles`. → Bucket 1 harness-class follow-on (complete its local mock), NOT in current KEEP set.

---

### RC_COMPONENT_ASSERTION_DRIFT

**One-line:** Component tests assert removed copy, missing providers, or stale selectors (17 files).

**symptom_files:**

- `tests/components/FraudDashboard.smoke.test.tsx`
- `tests/components/admin/AdminSystem.test.tsx`
- `tests/components/admin/ApprovalInbox.test.tsx`
- `tests/components/admin/LanguageRegistryTable.test.tsx`
- `tests/components/admin/UsersMgmtDrawer.test.tsx`
- `tests/components/domain/AdminListPage.test.tsx`
- `tests/components/domain/DealCard.test.tsx`
- `tests/components/domain/MetricTile.test.tsx`
- `tests/components/domain/ShareButton.test.tsx`
- `tests/components/domain/VendorCard.test.tsx`
- `tests/components/features/ApprovalQueue.test.tsx`
- `tests/components/features/BusinessPage.test.tsx`
- `tests/components/features/ReviewRemovalQueue.test.tsx`
- `tests/components/features/Search.test.tsx`
- `tests/components/features/VendorDashboard.test.tsx`
- `tests/components/features/VendorWelcome.test.tsx`
- `tests/components/vendor/VendorShell.test.tsx`

**Pattern:** `Unable to find` literal keys like `page_title`, missing `QueryClientProvider`, `[data-testid="deal-card"]` not rendered.

**recommended_direction:** `test_wrong` — refresh selectors/copy; wrap with QueryClient + i18n providers.

**blast_radius:** Varies per component; failures are DOM assertion, not server exceptions.

**sev-1:** n (except FraudDashboard touches affiliate money UI — still test assertion drift)

---

### RC_REFUND_CALENDAR_STALE

**One-line:** Refund business-day test uses April 2026 dates that are Pesach holidays in updated `il-business-days.ts`.

**symptom_files:**

- `tests/unit/workflows/refund.test.ts`

**Code actually does:**

```ts
// src/server/calendar/il-business-days.ts:20
'2026-04-02', '2026-04-03', '2026-04-04', '2026-04-05', '2026-04-06', '2026-04-07', '2026-04-08',
```

`businessDaysBetween('2026-04-05', '2026-04-12')` → **2** (Sun–Sun window minus Pesach + Shabbat).

**Test expects:** 5 business days (`refund.test.ts:152-154`).

**recommended_direction:** `test_wrong` — pick non-holiday fixture dates or update expected count.

**evidence_for_direction:** Code calendar sourced from Hebcal API (file header); test predates 2026 holiday set.

**blast_radius:** `calculateRefundAmount` / cancellation windows ship on this calendar. **SEV-1** if calendar wrong — here calendar looks authoritative; test is stale.

**sev-1:** y (refund timing — code likely correct, test wrong)

---

### RC_FRAUD_INTEGRATION (pglite)

**One-line:** After pglite install, fraud suites run but fail on seed gaps, DDL drift (`clicked_at`), and decision-point storage casing.

**symptom_files:**

| Sub-cause | Files |
|-----------|-------|
| Missing `referral_settings` seed | `tests/integration/referrals/fraud/_probe-wave1.int.test.ts`, `tests/integration/referrals/fraud/maturation-sweep.int.test.ts` |
| PGLite DDL missing `clicked_at` on `referrals` | `tests/integration/referrals/fraud/_probe-wave1c.int.test.ts`, `tests/integration/referrals/fraud/resolve-autosuspend.int.test.ts` |
| Decision point casing | `tests/integration/referrals/fraud/decision-points.int.test.ts` |

**Passing fraud-related (5 files):**

- `tests/integration/referrals/fraud/adapters-db.int.test.ts`
- `tests/integration/referrals/fraud/fraud-admin.int.test.ts`
- `tests/integration/referrals/fraud/ledger-clawback.int.test.ts`
- `tests/unit/referrals/fraud/clawback-logic.test.ts`
- `tests/unit/referrals/fraud/guardrails.test.ts`
- `tests/unit/referrals/fraud/maturation-logic.test.ts`

**Code actually does:**

```ts
// src/server/db/queries/fraud-events.ts:22
decisionPoint: toStoredDecisionPoint(input.decisionPoint),
// types.ts:14
EARN: 'earn',
```

```ts
// schema.ts:2822 — production has clicked_at
clickedAt: timestamp('clicked_at', { withTimezone: true }),
```

```ts
// pglite-db.ts:74-92 — DDL omits clicked_at
CREATE TABLE IF NOT EXISTS referrals ( ... qualified_at ... created_at ... )
```

**Test expects:** `row.decision_point === 'EARN'` (`decision-points.int.test.ts:529`); inserts via Drizzle against PGLite DDL; `sweepMaturation` without seed in `_probe-wave1`.

**recommended_direction:**

- Seed / DDL gaps: `test_wrong` (fix PGLite harness)
- Decision point `'earn'` vs `'EARN'`: `test_wrong` — code explicitly maps to lowercase storage (`toStoredDecisionPoint`)

**blast_radius:** Referral fraud pipeline + maturation sweep ship to users. DDL drift is **test-only**; storage casing is **intentional shipped contract**.

**sev-1:** y (referral fraud / commission holds)

---

### RC_STRIPE_MOCK_EXPORT

**symptom_files:**

- `tests/unit/payments/stripe-webhook-account-updated.test.ts`

**Code/test gap:** `vi.mock` of `@/server/db/queries/stripe-webhook-events.js` missing `claimWebhookEvent` export the handler now calls.

**recommended_direction:** `test_wrong`

**blast_radius:** Stripe Connect webhook handler ships — **SEV-1** if handler broken; failure is mock-only.

**sev-1:** y (payments webhook — mock gap, not proven code bug)

---

### RC_AI_BROWSER_SDK_GUARD

**symptom_files:**

- `tests/unit/server/ai/providers/stubs.test.ts`

**Failure:** Anthropic SDK refuses browser-like env (`dangerouslyAllowBrowser`).

**recommended_direction:** `test_wrong` — mock provider or set `@vitest-environment node`.

**sev-1:** n

---

## NEEDS_NEON / live env

No `ECONNREFUSED` in this run. Candidates that require live DB / `.dev.vars`:

| File | Failure |
|------|---------|
| `tests/api-migration/auth-snapshots/_capture.test.ts` | `Failed query: insert into rate_limit_buckets` (live Neon) |
| `tests/integration/tmp-deal-moderation-repro.test.ts` | `missing .dev.vars` |

**recommended_direction:** `env` — convert to pglite or gate behind explicit live-DB opt-in.

---

## UNCLASSIFIED (24 files — per-file notes)

| File | Failure gist | Direction |
|------|--------------|-----------|
| `tests/components/CategoriesTabsNav.test.tsx` | `nav_aria_label` undefined — i18n keys | test_wrong |
| `tests/components/useAuthHint.test.tsx` | loggedIn false vs true | test_wrong / investigate auth hint API |
| `tests/components/i18n/LanguageToggle.test.tsx` | `aria-pressed` attribute | test_wrong |
| `tests/components/icons/MDLogo.test.tsx` | alt text undefined | test_wrong |
| `tests/components/layout/StickyCTA.test.tsx` | CSS class `translate-y-full` | test_wrong |
| `tests/components/overlays/Drawer.test.tsx` | title element not null | test_wrong |
| `tests/components/support/SupportMessageComposer.test.tsx` | empty div not null | test_wrong |
| `tests/components/features/admin-page-organizer/toolbar.test.tsx` | unsaved-changes copy | test_wrong |
| `tests/components/features/CartCheckout.test.tsx` | missing `cart-stale-toast` | test_wrong |
| `tests/components/admin-support/SupportSettings.test.tsx` | (also i18n mock) | test_wrong |
| `tests/unit/auth/policies.test.ts` | `auth.verify-send` max 5 vs test expects 3 | test_wrong (code: `rate-limit-policies.ts:23`) |
| `tests/unit/auth/get-ip.test.ts` | fallback `0.0.0.0` vs test `unknown` | test_wrong (`ip.ts:17`) |
| `tests/unit/auth/applyRateLimitFor.integration.test.ts` | expected Response, got null | investigate — may be env |
| `tests/unit/deals-facets-histogram.test.ts` | count 0 vs 4 | code_wrong or test — needs query trace |
| `tests/unit/queries/deals-list.test.ts` | `axesCount` field added | test_wrong |
| `tests/unit/page-layout/registry.test.ts` | missing `club-offers` module | test_wrong or feature removed |
| `tests/unit/page-layout/modules/deal-row-category.test.ts` | zod parse throws | test_wrong |
| `tests/unit/features/cart/anonCartStore.test.ts` | empty cart vs items | test_wrong (`dealSkuId` model) |
| `tests/unit/features/cart/useCart.test.ts` | `dealId` vs `dealSkuId` | test_wrong |
| `tests/unit/translation/budget.test.ts` | empty metrics array | test_wrong |
| `tests/unit/workflows/review.test.ts` | TypeError vs NOT_REVIEW_ELIGIBLE | test_wrong (mock gap) |
| `tests/unit/observability/with-sentry.test.ts` | captureCalledWith shape | test_wrong |
| `tests/integration/translation/search-tsvector.int.test.ts` | sanitization assertion | code_wrong if SQL injectable — **investigate** |
| `tests/api-migration/effect-fixtures/refund.equivalence.test.ts` | extra workflow step in array | test_wrong (workflow evolved) |

**Not found:** `api/contract-client.types` test — no such file in synced suite.

---

## Git-tracked unit files (15) — status after full harness

All **PASS** when `vitest.config.ts` + aliases present:

- `tests/unit/api/admin-outbox-redrive.test.ts`
- `tests/unit/cron/daily-notif-stats.test.ts`
- `tests/unit/db/confirm-checkout-earn-outbox.test.ts`
- `tests/unit/db/cron-cursors.test.ts`
- `tests/unit/db/outbox-dead.test.ts`
- `tests/unit/db/resilience-schema.test.ts`
- `tests/unit/monitor/dlq-depth-enabled.test.ts`
- `tests/unit/monitor/ledger-wallet-consistency-repair.test.ts`
- `tests/unit/monitor/outbox-abandoned.test.ts`
- `tests/unit/monitor/page-cooldown-delivery.test.ts`
- `tests/unit/payments/reconcile-wallet.test.ts`
- `tests/unit/payments/tax-doc-idempotent.test.ts`
- `tests/unit/referrals/analytics-rollup.test.ts`
- `tests/unit/workflows/referral-earn-handler.test.ts`
- `tests/unit/workflows/sweep-telemetry.test.ts`

Initial worktree run failed with `Cannot find package '@/server/...js'` — **harness/config**, not code regression.

---

## Priority summary (money / auth)

| Cluster | Shipped risk | Default fix target |
|---------|--------------|-------------------|
| RC_BYTES_TO_HEX_CROSS_REALM | Auth hash empty in jsdom; verify CF | **code** (`encoding.ts`) |
| RC_MOCK_BUILDER_GAP | Money paths untested | **tests** (mocks/pglite) |
| RC_REFUND_CALENDAR_STALE | Calendar authoritative | **tests** |
| RC_FRAUD_* | Referral fraud ships | **tests** (harness) + verify casing |
| RC_ZOD_FIXTURE_STALE | API boundary correct | **tests** |
| RC_ORPHANED_AI | Runner uses new API | **obsolete tests** |
