chore(backlog): TODO.md orchestration round 4 — category resolution, DB harness, ratchet - #137
Draft
EraPartner wants to merge 39 commits into
Draft
chore(backlog): TODO.md orchestration round 4 — category resolution, DB harness, ratchet#137EraPartner wants to merge 39 commits into
EraPartner wants to merge 39 commits into
Conversation
… to the 3-level pattern Closes the TODO.md finding 'Remaining 2-level category-resolution surfaces: monthly summary (live + MV), recurring detection, sankey'. mv_monthly_summary's definition, the monthly live path's CTE column, recurring detection's scan query, and sankey's category join + category exclusion all now resolve own -> recipient default -> PRIMARY recipient's default, matching transactionRepository and the fixed breakdown/pivot. Migration 0085 drops the stale MV on existing installs (same drop-and-rebuild-at-boot mechanism as 0045/0084; totals are output-invariant for the MV's only reader — this aligns the stored category grain, the user-visible fixes are sankey and recurring detection). Pinned by 5 live-DB tests (aliasCategoryResolution.db.test.js; 3 fail pre-fix) plus SQL-shape guards in the three mock suites. Adversarially verified on a live PG16: alias chains, primary-with-no-default, and own-category override all match the already-fixed surfaces; migration round-trips clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…; file 5 residue findings Ticked with the fix commit's SHA after independent adversarial verification. Filed the implementer's and verifier's noticed-but-not- fixed defects: sankey NULL-unsafe/non-alias-aware exclusions (live- reproduced income-zeroing), sankey missing is_transfer filter, transactionRepository id-vs-name precedence mismatch, recurring detection categoryId/grouping asymmetry, and a stale routes comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||
…suites (64 live-DB tests) Completes the migration list from the TODO.md harness finding: banks, forecast, monthly, the infoRepository barrel, and recipients now run their SQL against the real schema (advisory-lock serialized, self-skip without TEST_DATABASE_URL; mock suites kept per the increment-2 precedent). The monthly suite executes BOTH the generate_series live path and an actually-created mv_monthly_summary, proving live-vs-MV equivalence. The real DB exposed 9 mock-masked discrepancies, each pinned by a clearly-marked 'PIN:' test asserting current behavior (filed as findings in TODO.md, not fixed here): raw pg Date leakage on bank first/last_transaction, cross-currency summation before FX conversion, manual-only accounts missing from balance history, forecast ignoring transfer exclusion (ADR-083), populated-months-only average denominators (forecast + avg-vs-current), latest-vs-historical FX rate inconsistency in top merchants, alias pivot label/spend mismatch, and a net-worth chart step for manual-only accounts. 3247 pass with DB (deterministic x3), 3108 pass / 139 skip without. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…ile 12 findings from pins The harness item's DECIDED migration list is fully delivered across three increments. Filed the third increment's discoveries: nine pinned money-path discrepancies (banks date leakage / cross-currency summation / manual-only history gap, forecast transfer inclusion and denominator, recipients FX inconsistency and alias-pivot mismatch, avg-vs-current denominator, net-worth manual-only step), the latent planned-month local/UTC mix, and a minor-residue rollup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…47 errors to 0) Third checkJs-ratchet increment: importPipeline (17 files), portfolio- ImportPipeline (7), prices (3), info (3), currency (2), tax (2) are annotated to zero implicit-any errors and added to the ratchet's directory-prefix list, so new files under them are ratcheted from birth. 34 service files annotated; 6 new pg-accurate table-row typedefs in src/types/rows.js verified column-by-column against the Alembic migrations (NUMERIC/BIGINT as strings, DATE as Date), plus derived shapes incl. ParsedBankTransaction — the contract all 9 bank CSV adapters emit. HINT_SCOPE retargeted to src/services/ so the next round sees already-clean candidates. Ratchet bite re-proven (TS7006 + exit 1 on a removed annotation). Typing surfaced 5 genuine mismatches, typed as current reality and filed in TODO.md — notably import_batches BIGSERIAL ids served as string on POST /api/import/csv but number on the review-commit route. 75 ratcheted files clean; typecheck unchanged; tests byte-identical (3108 pass / 139 skip without DB). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…e 3 typing-surfaced findings Services/ taken subdirectory-wise: 6 subdirectories ratcheted at 0, remainder enumerated with per-directory error counts in the LEFT clause. Filed the batch-id wire-type split, the dual rate_date shapes feeding buildHistoricalRateIndex, and the snapshot ?? 0 mixed types. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…rate 8 money-path suites
First increment of the route-test finding: tests/helpers/routeApp.js
mounts the real router on a throwaway express() app with the
production middleware wired per main.js — requestId, requestMetrics,
json body limit, CSRF guard, ADR-026 wrapResponse, the per-mount
before slot, 404 funneling, and createErrorHandler with injectable
isProduction (making the 5xx-sanitization branch genuinely testable
for the first time). Deliberate omissions (CORS, gzip, app-level rate
limiters, security headers) documented inline with main.js line refs;
the legacy routeHarness.js is header-marked with its gaps.
Migrated: transactions (+8 tests incl. two PINs), the four bulk
suites, transactionsValidationPins, plannedTransactions (real
validateIdParam now runs), importValidationPins (csvUploadError-
Translator on the tested path for the first time). All hand-rolled
mockRouter/mockResponse and vi.mock('express') removed from these 8.
Newly exercised for real: CSRF 403s, 404 envelopes, id-param 400s,
CSV/NDJSON export headers.
Pinned (filed in TODO.md, not fixed): createErrorHandler only honours
err.status on AppError instances, so body-parser errors surface as
500 — malformed JSON (a 400-class client typo) and >1MB payloads
(413) both report INTERNAL_SERVER_ERROR, the latter with its reason
hidden in production.
22 route files remain on the legacy harness. Full suite 3117 pass /
139 skip / 0 fail (+9 from baseline, all in migrated files); lint
unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…rorHandler HttpError finding Harness built, 8 of 30 suites migrated, 22 enumerated as LEFT. Filed the pinned errorHandler bug: body-parser HttpErrors reported as 500 with the 413 reason production-sanitized away. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…he true window Closes two TODO.md findings from the harness third increment. Transfer exclusion: all 8 transactions-hitting queries across the four exported forecast functions now apply the sibling-identical conditional AND t.is_transfer = false (runtime user_settings read via getIncludeTransfers, matching infoRepositoryAverageVsCurrent). The 5 planned_transactions overlays deliberately do not — that table has no is_transfer column (verified against schema; pinned so a consistency edit can't add a 42703). No forecast query is balance-anchored, so no carve-out was needed. Also stops ADR-090 trade cash legs double- counting against the portfolio and transfer legs inventing category spend in the by-category forecast. Denominator: the average now divides by months elapsed from the ledger's first in-window transaction through the last complete month — empty months inside history count as real zeros (kills the 24x inflation from sparse history), months before the ledger existed are not charged (a 3-month-old install divides by 3). The divisor comes from an unfiltered MIN(date) ledger probe, adversarially hardened: a never-executed stale planned row cannot stretch it (was a 24x deflation), and category/recipient/transfer filters move the numerator only (a filtered-keys divisor flipped an average from -64.17 to +30). includeTransfers is now a Monte-Carlo cache-key input so toggling the setting misses the 6h cache instead of serving a wrong-polarity forecast. Adversarially verified end-to-end on live PG16 (independent before/ after: trade-leg -5050 to -50; sparse-month 240 to 10; both defect orientations reproduced then fixed). 3276 pass with DB, 3128/148-skip without; lint/typecheck/ratchet clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…and SQL-binding residues Both money fixes landed after adversarial verification with two verifier-found divisor defects fixed pre-commit. Filed the remaining noticed items: the APP_TIMEZONE vs CURRENT_DATE month-boundary drift and the string-interpolated validated integers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…y, wire-safe bank dates Closes four TODO.md findings from the harness third increment. Per-currency balances: getBankBalances no longer sums a multi- currency account's amounts across currencies before converting at one rate (100 EUR + 100 USD @0.5 now reports 150, not 100). A stamped statement balance anchors only its own currency's partition; each partition converts at its own rate and the account total sums after conversion. Single-currency accounts are byte-identical to before. COMPUTED_BALANCE_LATERAL itself is untouched, so the hub, reconcile, and cross-workspace consumers are unaffected. Gap-free history: both the banks balance-history walk and the net- worth history walk drop their WHERE balance IS NOT NULL gates, so manual-only accounts appear in the charts their headlines already count — no more non-zero headline above an empty chart, no more overnight net-worth step reported as a monthly gain. Implemented as a set-based span expansion (no per-day lateral); an opening-row fold keeps window cost independent of ledger age. Both surfaces pin the invariant headline == today's chart point, including under MOVING FX curves — the headline now converts at today's rate like the chart's last point (adversarial verification caught the last-activity keying regressing foreign-currency accounts 32%, and a planner merge-join degradation making the first series shape 27x slower; both fixed — end-to-end lands at ~2x HEAD while emitting the previously-dropped accounts' points, ~1.2x per emitted row). Unknown-currency FX lookups are memoized once per currency per request instead of once per day. Bank first/last_transaction now serialize as wire date strings, not raw pg Dates (day-shift east of UTC), matching the declared frontend type. Verified: 100+ randomized ledgers cross-checked against independent brute-force evaluation (every daily point + headline, both surfaces, incl. moving-rate curves); 3296 pass with DB, 3137/159-skip without; lint/typecheck/ratchet clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…etted residues All four landed after two-round adversarial verification (FX-keying regression and planner degradation fixed pre-commit). Filed: cross- currency SUM on the four remaining surfaces, the drift-badge convention mix, future-dated row divergence, the net-worth tracking- only fallback wrong-sum, and the over-broad mock dispatcher guard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…or, name==id categories Closes three TODO.md findings from the harness/ratchet passes. Top merchants now aggregate per (recipient, date, currency) and convert each row at its historical rate before the per-recipient reduction — the same shape by-year and pivot use, so one purchase no longer shows three different EUR figures across the recipients page. Counts, averages, and first/last-seen bounds re-reduce correctly (verified against a hostile fixture: same-day rows, multi-currency, alias roll-up; all three surfaces agree). avg_monthly_spending and avg_daily_spending now share one denominator: elapsed months from the ledger's first in-window transaction through the last complete month (the forecast's counting rule applied to this card's 6-month window, via the same unfiltered ledger probe). Income- only months no longer halve the spending average; a single busy month is no longer reported at full weight; young installs are not deflated. The old pin was non-discriminating (contiguous fixture) and is replaced by three tests that fail against the old code. CATEGORY_NAME_SQL and its three copies (sort column, getAll, getAllWithCount) now resolve the displayed name with the same precedence as EFFECTIVE_CATEGORY_ID_SQL (own -> recipient default -> primary default) — an alias with its own default no longer shows its primary's category name beside the alias's category id. Pinned across getById/getAll/getAllWithCount/create/update and the category sort. Adversarially verified on live PG16 (arithmetic re-derived, edge clamps probed, avg_daily consumers enumerated — none assumed the old fixed window). 3303 pass with DB, 3137/166-skip without; lint/ typecheck/ratchet clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…esidues, broaden TZ-drift item All three landed after adversarial verification (no defects; two comment/test-name notes fixed pre-commit). Filed the MoM latest-rate inconsistency, the remaining category-NAME sites (export/splits pc- first, planned 2-level, infoRepositoryPlanned 1-level — verifier- completed enumeration), and the denominator-fix residues. The filed TZ-drift finding now names both affected files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
… the canonical resolution Closes the TODO.md finding 'Remaining category-NAME resolution defects'. The CSV/NDJSON export (incl. bulk-export) and the owed- splits export swap their pc-before-rc CASE to the canonical own -> recipient default -> primary default order; plannedTransaction- Repository extends from 2 levels to 3 via a shared PLANNED_CATEGORY_ NAME_SQL constant (six query sites now share one definition); infoRepositoryPlanned's getPlannedExpensesNextMonth goes from 1 level to 3. planned_transactions carries its own recipient_id + category_id so the pattern maps 1:1. One adjacent correction, surfaced by the fix's own tests: planned search now matches against the resolved display label instead of ORing c/rc column pairs — adding pc pairs the old way made every row match its primary's category regardless of what it displays, and the old form missed pc-categorised rows entirely while matching rows whose own category overrode the term it hit. Pinned in the extended aliasCategoryResolution.db.test.js on the canonical alias-with-own-default topology, asserting name==resolved-id against transactionRepository rather than hardcoded strings; 5 pins fail pre-fix, 3 guards pass both ways. 3311 pass with DB, 3137/174- skip without; lint/typecheck/ratchet clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…e the recipient_name divergence Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
… wire type for batch_id Closes two TODO.md findings from the supertest and ratchet passes. Error handler: a non-AppError carrying an integer 4xx status/ statusCode now returns that status instead of 500 — but its message is echoed only when the error also carries a type from the body-parser allowlist; every other forwarded 4xx gets a generic per-status phrase, so an internal error that happens to hold a status can never leak its wording (the rule is documented in the handler and pinned both ways). Malformed JSON is now a 400 naming the parse problem and a >1MB body a 413 whose reason survives production mode; loan-schedule validation errors surface as 400 instead of 500. Codes reuse the existing ADR-026 vocabulary so nothing ripples into the shared types or i18n. batch_id: both import pipelines' createBatch now return Number(id) at the stage boundary, so POST /api/import/csv and the review-commit route emit the same wire type (was string vs number); openapi.yaml and the frontend types/generated client follow. This also fixes a live bug: the SSE review_required event relayed the string, which the frontend's z.number() schema rejects — any import needing review failed the progress stream. Route pins assert strict equality of batch_id across the csv and commit responses through the real createBatch over a primed pg mock. 23 tests added/flipped with fail-against-old proof (12 and 8 failures respectively on reverted sources). 3334 pass with DB, 3160/174-skip without, frontend 2120 pass; lint/typecheck/ratchet/endpoint-matrix clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…file two residues Filed the MSW import-mock shape divergence (root cause of the missed wire split) and the loanSchedule plain-Error genericization. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…R-083 transfer setting Closes two TODO.md findings. The hand-rolled exclusion clauses are replaced by the shared buildExclusionClauses (the pivot/monthly form): the -1 NULL sentinel means excluding a category no longer silently erases every uncategorised row (was: excluding one category removed a EUR 3000 uncategorised income row, rendering Income 0 with money still flowing out), and the recipient exclusion is now alias-aware. Sankey also applies the conditional is_transfer predicate governed by the runtime includeTransfers setting like every sibling aggregation — a savings transfer pair no longer inflates both sides of the flow graph (was: +900 fake income plus a phantom 900 spending node). The characterization test is split into four real pins on live PG16; fail-against-old shows exactly the filed shapes (income 0 vs 3000, alias row surviving, 3900 vs 3000). 3338 pass with DB, 3161/177-skip without; lint/typecheck/ratchet clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
… residues Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…llback, historical MoM FX Closes three TODO.md findings. Planned next month: the window is now built entirely from the ADR-009 string helpers (todayAppDateString/firstOfMonthYmd/addDaysYmd) with month/year parsed from the window start — east-of-UTC hosts (incl. the default Europe/Brussels) no longer name the previous month beside a correct period_start. Window arithmetic brute-forced over a decade by the verifier (leap/century/year boundaries); pinned under forced TZ=Asia/Tokyo mid-year and across the year boundary. Net-worth flow fallback: rows positively attributed to an in_net_worth=false account are excluded from both fallback CTEs via NOT EXISTS — an all-tracking ledger reports 0 instead of the tracking accounts' running total — while unattributed rows (the un-migrated shape the fallback exists for) stay counted, pinned via the UPDATE-relabel construction since the sync trigger auto-creates accounts on INSERT. Verifier probes confirmed the predicate matches the walk's resolution exactly (FK/NOT NULL make the edge cases unrepresentable) and caught the multi-currency variant (a tracking- only USD row polluting the FX-converted sum) — covered. Recipients MoM: aggregates per (recipient, period, date, currency) and converts at historical per-date rates like the other three recipient surfaces (a 2.9x cross-surface disagreement on a stressed fixture closes to one rounding cent); the reduction uses the Decimal accumulator to keep the multiplied row count exact. No-DB contract guard added on the convertRowsToEur options. Verifier notes folded in pre-commit: the TZ restore no longer writes the literal string "undefined" when TZ was unset, and the measured figure in two comments corrected to -143.25. 3344 pass with DB, 3163/181-skip without; lint/typecheck/ratchet clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…TZ, fallback, FX-flag residues Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
Closes the TODO.md finding on expandRecurringOccurrences mixing a pg local-midnight Date with APP_TIMEZONE strings and UTC comparisons — on hosts east of the app timezone every occurrence shifted a day back (a weekly cadence landed 6 days off across a month; a monthly row picked up a spurious permanent clamp to the wrong day). The expansion is now pure YMD-string calendar math on the ADR-009 helpers: day cadences step via addDaysYmd, month cadences via a first-of-month + clamp-to-last-day construction that reproduces the old sequential clamp cascade byte-for-byte (Jan 31 monthly still walks Feb 28 -> ... -> Jul 28), and the stale-row fast-forward works in whole- day diffs. Host-invariance pinned: a TZ x cadence it.each matrix (UTC/ Tokyo/LA, weekly/monthly/clamp-cascade) asserts identical occurrence days everywhere; fail-against-old reproduces the exact 6-day and clamp-shift failures under Tokyo. The whole planned suite is green under UTC, Tokyo, LA, and UTC+14. 3353 pass with DB, 3172/181-skip without; lint/typecheck/ratchet clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…al-stepper residue Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…column Closes the categoryId half of the recurring-detection finding: the scan query now selects the same 3-level COALESCE the categoryName is resolved from, and the pattern emits it — a recipient-default- categorised pattern no longer reports categoryId null beside a non-null name. Direct user-visible improvement: creating a planned payment from a detected pattern now carries the category the panel displays instead of silently creating it uncategorised (RecurringDetectionPanel POSTs pattern.categoryId). The alias grouping/labeling half stays open as a deliberate product decision. Pinned on live DB (alias topology, fails pre-fix with null) and in the mock suite (raw null + effective 7 -> categoryId 7). 3355 pass with DB, 3173/182-skip without; lint/typecheck/ratchet clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
Batch 2a of the supertest migration: accounts, aggregations, aggregationsForecastBacktest, attachments, categories, crossWorkspace, recipientBankAccounts, recipients, research, settings, tags, watchlist. validateIdParam is no longer stubbed anywhere in these suites; 8 new pins cover the guard now being on-path. The attachments multer stub now drives req.file through the real middleware chain instead of hand-building it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…s harness Batch 2b of the supertest migration: admin, ai, marketLookup, portfolioImport, portfolioImportValidationPins, savedCharts. The admin auth guard and ai enforceAiChatEnabled gate were unreachable under the legacy harness and are now genuinely exercised (6 new pins). SSE stream tests assert the buffered event-stream body instead of a stubbed res.write array. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…ness Batch 2c of the supertest migration: import, info, investments, splits. The import suite's production-sanitization test now actually pins the sanitized branch via an isProduction routeAgent; multer error paths run through the real csvUploadErrorTranslator chain via a hoisted uploadState. One structural test dropped (route-registration check against the mock router's handler map — no real-router equivalent; both routes keep their dedicated request tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…info-suite mock residue Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…ss.js Final batch of the supertest migration: settingsStorage's router-driven blocks and transactionPatchValidation move to the real-Express harness (promise-rejection assertions become real 400 envelopes through the error handler); rateLimiter and validation keep their middleware-unit intent with small local res stubs; main.test.js sheds a dead express mock it never invoked. routeHarness.js has zero consumers and is deleted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…tion complete Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
Top-level services slice of the noImplicitAny ratchet: the 31 files the gate reported ready are now listed, and 26 more top-level services files are annotated from 144 implicit-any errors to zero. New multer ambient shim in src/types/thirdPartyModules.d.ts (imported as a value, so TS7016 fires on the import itself); RecipientMatchPatternRow added to rows.js against migration 0015. 133 files now gated; bite re-proven. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…6bc150) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
Completes the src/services/ top level under the noImplicitAny ratchet (51/51 files, individual entries — prefix-collapse proven premature since subdirectories are still dirty). dbEditor typed faithfully to its generic nature (Record<string, unknown> rows, boundary-cast introspection); BelgianInflationRateRow verified against migration 0001. 140 files gated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
…re-type residue Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Continues working the TODO.md backlog queue (round 4). First commit closes out a bookkeeping gap from the previous round: the search-abort/min-length finding was fixed and stamped in #136 but its checkbox was left unticked — the fix was verified present in the tree (
SERVER_SEARCH_MIN_LENGTHinVirtualDataTable.tsxplus its tests) before ticking. Further findings from the queue will land on this branch as they are implemented and verified, one commit per fix plus a bookkeeping commit stamping each item.Type of change
chore/ci/test— tooling, build, or testsChecklist
type(scope): subject)console.log/ debug codebun run test) and lint is clean (bun run lint) — TODO.md-only so far; will be verified per code commitbun run typecheck) — samedocs/pages updated — n/a so farArchitecture & data
Verification
Bookkeeping commit only so far: verified the ticked finding's fix exists in the current tree (
apps/frontend/src/components/shared/VirtualDataTable.tsx:34and its test file) before ticking the box. Each subsequent fix commit will carry its own verification, run against the real-DB test harness where money paths are involved.Residual risk & follow-ups
The TODO.md queue still holds ~410 open findings; this branch works through them incrementally. Stamps in TODO.md reference pre-squash SHAs from earlier PR branches by established convention.
🤖 Generated with Claude Code
https://claude.ai/code/session_01HDqYKSgjakVo14SPZXVCJt
Generated by Claude Code