Monorepo Testing / CI / DX Review — 2026-06-25
Section titled “Monorepo Testing / CI / DX Review — 2026-06-25”Ground-truth run of every test/lint/typecheck command at HEAD 9d9430f.
Toolchain: node v24.12.0, pnpm 10.22.0, uv 0.9.24, Python 3.12.3. node_modules present (no install needed).
RESULTS — green/red at HEAD (real numbers)
Section titled “RESULTS — green/red at HEAD (real numbers)”| Command | Status | Numbers / evidence |
|---|---|---|
pnpm test (design-tokens vitest) | GREEN | 1 file, 26/26 passed (473ms) |
pnpm test:sd-app (vitest) | GREEN | 1 file, 3/3 passed (655ms) — still only 1 test file |
pnpm test:py → sd-math pytest | GREEN | 218 passed in 0.67s |
pnpm test:py → sd-api pytest | RED | dependency resolution fails — 0 tests run. Chain aborts. |
pnpm lint | RED | 10 errors, 7 warnings, exit 1 |
pnpm check (typecheck, recursive) | RED | 61 errors in sd-app (missing paraglide keys) |
template astro check | GREEN | 0 errors, 0 warnings, 9 hints (150 files) |
mbr astro check | GREEN | 0 errors, 0 warnings, 1 hint (24 files) |
Bottom line: Of the 4 commands in pnpm test:all (test && test:sd-app && test:py && lint), two are RED (test:py via sd-api, and lint). pnpm test:all does NOT pass at HEAD, and the .husky/pre-push hook would fail (it runs pnpm lint, pnpm test:sd-app, and sd-api pytest). Pushes currently only succeed via --no-verify or because the repo is “local-only” (never pushed).
Quoted failures
Section titled “Quoted failures”1. pnpm lint (10 errors):
sites/mbr/src/pages/focus.astro 298:9 error Unexpected var, use let or const instead no-var 298:13 error 'GAP' is assigned a value but never used @typescript-eslint/no-unused-vars 306:9 / 308:9 / 315:9 / 324:9 / 428:5 / 432:5 error Unexpected var, use let or constsites/sdc/src/pages/focus.astro 385:5 / 389:5 error Unexpected var, use let or const instead no-var✖ 17 problems (10 errors, 7 warnings) — 9 auto-fixable with --fixNote GAP (focus.astro:298 var GAP = 6;) is genuine dead code — assigned, never referenced by positionTooltip. Both offending files (focus.astro on mbr + sdc) are part of the 61-commit delta (Tooltip/focus-page work).
2. pnpm test:py → sd-api:
× No solution found when resolving dependencies:╰─▶ Because sd-math was not found in the package registry and your project depends on sd-math, we can conclude that your project's requirements are unsatisfiable. ELIFECYCLE Command failed with exit code 1.3. pnpm check (sd-app svelte-check, 61 errors, sample):
src/routes/[country]/[lang]/term-loan/+page.svelte 35:13 "Property 'tl_snapshot_title' does not exist on type 'typeof import("$paraglide/messages")'."... (61 ERRORS, 2 FILES_WITH_PROBLEMS: int-only + term-loan +page.svelte)Prior-review (2026-05-14) resolution status
Section titled “Prior-review (2026-05-14) resolution status”Strong progress — most 2026-05-14 findings are FIXED:
| Prior # | Finding | Status now |
|---|---|---|
| 1 | CI case-mismatch sites/Template | FIXED — test.yml rewritten, single 3-min pipeline, lowercase paths |
| 2 | pnpm lint broken (brands.astro no-var) | Regressed elsewhere — original fixed, but new no-var errors in focus.astro (see RED #1) |
| 3 | Python tests orphaned from pipeline | FIXED (partly) — test:py script added + in pre-push; but sd-api leg is broken (see RED #2) |
| 4 | /test-all incomplete | Improved — test:all now spans tokens+sd-app+py+lint |
| 5 | pre-push only ran Template E2E | FIXED — pre-push now runs test, test:sd-app, lint, template check+E2E, sd-math+sd-api pytest |
| 9 | sd-app only 1 test file | NOT resolved — still exactly 1 test file (calculators/ca/index.test.ts, 3 tests) |
Findings
Section titled “Findings”[CRITICAL] [effort: 0.5h] sd-api pytest cannot resolve sd-math — test:py + pre-push are RED
Section titled “[CRITICAL] [effort: 0.5h] sd-api pytest cannot resolve sd-math — test:py + pre-push are RED”- Path:
packages/sd-api/pyproject.toml - What:
dependencies = [... "sd-math"]is a bare name with no[tool.uv.sources]entry, andpackages/sd-api/has nouv.lock(sd-math has one; sd-api doesn’t). Souv run pytestaborts at resolution before any test runs. This kills the second half ofpnpm test:pyand the sd-api step in.husky/pre-pushandtest.yml. - Evidence: Reproduced the failure (RED #2). Then confirmed the 31 sd-api tests actually PASS when sd-math is path-resolvable:
uv run --no-project --with ../sd-math --with fastapi --with "uvicorn[standard]" --with httpx --with pytest python -m pytest -q→31 passed, 1 warning in 1.53s. So it’s purely a packaging/resolution defect, not test failures. - Why it matters: sd-api wraps the financial-math engine as HTTP endpoints. 31 working tests are invisible to the pipeline; the whole
test:pychain reports failure, masking sd-math’s green result behind a red exit. Anyone runningtest:allor pushing sees a confusing dependency error, not a test report. - Fix: Add to
packages/sd-api/pyproject.toml:Then[tool.uv.sources]sd-math = { path = "../sd-math", editable = true }cd packages/sd-api && uv lock && uv syncand commit the resultinguv.lock(matches the SSoT/lockfile-committed dev rule). Verifyuv run pytest→ 31 passed.
[HIGH] [effort: 0.25h] pnpm lint RED — 10 errors in new focus pages (blocks pre-push + test:all)
Section titled “[HIGH] [effort: 0.25h] pnpm lint RED — 10 errors in new focus pages (blocks pre-push + test:all)”- Path:
sites/mbr/src/pages/focus.astro,sites/sdc/src/pages/focus.astro - What: 8
no-var+ 1 unused-var (GAP) + 1 moreno-var(see RED #1). 9 of 10 auto-fixable. - Why:
pnpm lintis a step in bothpnpm test:alland.husky/pre-push; this makes both fail. TheGAPunused var is a real (if harmless) dead-code smell shipped in the Tooltip positioning logic — the tooltipGAPoffset is defined but never applied, so tooltips may be mispositioned vs intent. - Fix:
pnpm lint:fixclears the 8var→const. Manually resolveGAP: either wire it intopositionTooltip(if a 6px offset was intended) or delete it. This is repeated inline-script code in two focus.astro files — consider extracting the tooltip JS to a shared.tsmodule so it lints once and is testable (it currently can’t be unit-tested as inline Astro script).
[HIGH] [effort: 2h] MBR rates helper — 14 exported functions incl. financial math, ZERO unit tests
Section titled “[HIGH] [effort: 2h] MBR rates helper — 14 exported functions incl. financial math, ZERO unit tests”- Path:
sites/mbr/src/lib/rates.ts(98 lines, delta surface from commits 4cb2f63 / fafce34 / cdd11c3) - What: Pure, trivially-testable exports with no
.test.ts:annualGain(principal, candidate, baseline),annualGainPer100k(...),getTopProducts(limit)(sort+filter),getFullTableProducts(),getBestSavingsRate(),getStandardCreditCardRate(),getBestLowRateCard(),formatCurrency,formatRate,getBaselineRate/getPrimeRate/getPublicProducts(reference_only filter). - Why it matters: This is the math behind the active commercial site’s rate comparison and “annual gain” claims — exactly the FSS-mission correctness surface. The single mbr E2E (
smoke.spec.ts) checks that the gain cell changes when the bank rate changes, but nothing pins the value ofannualGain/annualGainPer100k, the sort order ofgetTopProducts, thereference_onlyexclusion, or currency/rate formatting. A regression that flips a sign or mis-sorts would ship green. - Fix: Add
sites/mbr/src/lib/rates.test.ts(vitest — no DOM needed; these are pure functions importing JSON). Wire mbr into the test pipeline: mbr currently has no vitest config and notest(unit) script — add one and include intest:all. Target: every exported fn inrates.tshas an assertion, especially the 3 financial-math fns with known fixtures.
[MEDIUM] [effort: 1.5h] New Reverse Mortgages / Investment Loans sections have no E2E assertions
Section titled “[MEDIUM] [effort: 1.5h] New Reverse Mortgages / Investment Loans sections have no E2E assertions”- Path:
sites/mbr/src/pages/rates.astro,sites/mbr/tests/smoke.spec.ts - What: Commits cdd11c3 (Reverse Mortgages — 4 lenders), fafce34 (Investment Loans), d5cfece (mortgage Term/Type cols, Dealerhop auto loans), 4cb2f63 (dashboard overhaul) added substantial new rates-page surface.
smoke.spec.tsonly asserts the original HISA “annual gain” interaction. No test renders or asserts the new sections exist / show their lenders. - Why: These are the most-changed user-facing additions in the delta and are entirely unguarded by E2E. A broken data shape or removed section would not be caught.
- Fix: Extend
smoke.spec.tswith section-presence assertions (heading + at least one lender row per new section). Cheap; rides the existing mbr playwright config.
[MEDIUM] [effort: 0.5h] pnpm check RED — 61 sd-app typecheck errors (paraglide keys)
Section titled “[MEDIUM] [effort: 0.5h] pnpm check RED — 61 sd-app typecheck errors (paraglide keys)”- Path:
apps/sd-app/src/routes/[country]/[lang]/{int-only,term-loan}/+page.svelte - What: 61
svelte-checkerrors:snapshot_*/tl_snapshot_*message keys referenced but absent from$paraglide/messages. CI already comments-out the sd-app check step (acknowledged in UPGRADES.md), butpnpm check(the documented DX command) is fully red. - Why: These pages render at runtime (paraglide falls back to the key string), but the typecheck gate is permanently disabled, so any real type regression in sd-app is now invisible. DX rot: developers learn to ignore
pnpm check. - Fix: Reconcile the message catalogue — add the ~30 missing keys to
messages/en.json(+ locales), regenerate, re-enable the CITypecheck — sd-appstep. Until thenpnpm checkwill mask new errors among the 61 noise lines.
[MEDIUM] [effort: 1h] sd-app still has only 1 test file despite being the calculator product
Section titled “[MEDIUM] [effort: 1h] sd-app still has only 1 test file despite being the calculator product”- Path:
apps/sd-app/src/— onlysrc/lib/calculators/ca/index.test.ts(3 tests) - What: Unchanged since 2026-05-14 (prior #9). No tests for the US calculator path, formatters, i18n locale/currency, or the
int-only/term-loanroute logic. Note the Pythonsd-mathengine is well-covered (218 tests), but the TS-side calculators/formatters that feed the PWA UI are not. - Fix: Add sibling
.test.tsfor each file undersrc/lib/calculators/and formatters; one playwright smoke over the/[country]/[lang]/route matrix.
[LOW] [effort: 0.25h] CI exists but is documented as “never runs” — sd-api step would fail if it did
Section titled “[LOW] [effort: 0.25h] CI exists but is documented as “never runs” — sd-api step would fail if it did”- Path:
.github/workflows/test.yml - What: Well-structured single pipeline (good — prior #1 fixed). But the file itself says “this repo is local-only (no GitHub push), so CI never runs,” while containing a
Pytest — sd-apistep (uv run pytestinpackages/sd-api) that would fail exactly as reproduced (CRITICAL above) plus aFormat checkandLintstep that are currently red. So CI is simultaneously (a) configured and (b) declared inert. If the repo is ever pushed to GitHub, CI goes red on day one. - Fix: Resolve the sd-api/lint reds (above) so the workflow is actually green-able, then either enable push-based CI or delete
.github/workflows/to match the “local-only” story. Don’t keep a red, inert workflow.
[LOW] [effort: 0.25h] Tooltip / Glossary components have no test
Section titled “[LOW] [effort: 0.25h] Tooltip / Glossary components have no test”- Path:
packages/components/src/ui/Tooltip.astro,sites/template/src/components/showcase/TooltipShowcase.astro, glossary viasites/mbr/src/utils/load-focus-content.ts - What: New Tooltip component (commits 1ee1a5f / a741aa2) has a showcase page but no spec; the only “tooltip” reference in any test is incidental (
search.spec.ts). Glossary() tooltip syntax and focus-page bfcache/cache-control handlers (focus.astropageshow/visibilitychangereload logic, commit 42d76a3) are untested. - Why: Lower stakes than rates math, but the bfcache reload handler is exactly the kind of browser-behavior logic that benefits from a playwright assertion (it caused the stale-PWA bug it fixes).
- Fix: Add a tooltip showcase spec in
sites/template/tests/(hover → tooltip visible + positioned in-viewport, matching the viewport-clamp fix 3b54a12). Optionally a focus-page reload assertion.
DX summary
Section titled “DX summary”- Lint: RED (10 errors) — but 9 auto-fixable; debt is small and localized to 2 new files.
- Typecheck:
astro checkGREEN for both shipping sites (template, mbr).pnpm checkRED only via sd-app paraglide keys (known, CI-suppressed). Root tsconfig still lacksnoUncheckedIndexedAccess(prior #6, unaddressed) — relevant for TS-side calculators. - Pipeline integrity:
pnpm test:alland.husky/pre-pushboth currently fail (sd-api resolution + lint). The hooks are good in design (comprehensive, changed-aware would be even better) but a red HEAD means they’re being bypassed. - Coverage of correctness-critical math: sd-math (Python) is excellent — 218 tests across CA/US, term/interest-only, historical/monthly. The TS-side financial math (
sites/mbr/src/lib/rates.ts, sd-app calculators/formatters) is the gap — that’s where the active-site claims live and it’s untested. - Flaky/slow: All ran fast (<1s each for unit suites). Did not run Playwright E2E this pass (browser-driven, time-boxed out); template E2E is the historically flaky surface (visual snapshots, WSL-vs-CI rendering — prior #13 still undocumented).
Severity counts
Section titled “Severity counts”- 1 CRITICAL (sd-api pytest resolution — breaks test:py + pre-push)
- 2 HIGH (lint red in focus pages; mbr rates.ts financial math untested)
- 3 MEDIUM (new rates sections no E2E; pnpm check 61 errors; sd-app 1-test-file)
- 3 LOW (inert-but-red CI; Tooltip/Glossary untested; — )