Follow-ups from the code-review remediation cycle (4 deferred findings)
Deferred from the #902 → #906 → #908 code-review remediation cycle. All four were raised by AI reviewers on #908 and verified real, but deliberately not fixed there — two need a design decision rather than a patch, and the release window was the priority.
None is a regression against main: dev is strictly better than main on every one of these. They are gaps in the new code, not breakage of old code.
1. Reset baseline is clobbered by the in-flight save it was compensating for
P1 · apps/frontend/components/builder/resume-builder.tsx (cubic, on #908)
handleReset now stays dirty when a save is in flight, so autosave converges the server on the reset state. But the in-flight save still completes and overwrites lastSavedData with the pre-reset content. A second Reset then restores the discarded edits and clears the local draft, while those edits remain on the server.
Fix direction: hold the reset baseline separately from lastSavedData until the compensating save lands, rather than reusing lastSavedData as both "last server state" and "what Reset restores to".
Related, still open from the original review: L-03 (Reset does not cancel an in-flight save) and the broader question of what Reset should mean now that autosave rewrites lastSavedData every few seconds. The pre-autosave semantic — "revert to my last explicit save" — is gone with nothing replacing it. This wants a product decision.
2. Malformed port silently retargets the endpoint
P2 · apps/backend/app/llm.py (cubic, on #908)
_normalize_api_base drops an unparseable port and rebuilds from the hostname. That fixed a credential leak (the previous fallback returned the raw URL including userinfo), but introduced a quieter problem: the URL now falls back to the scheme default. If the host answers on 443, health checks and completions can silently succeed against a different endpoint than the user configured.
Fix direction: strip credentials and fail explicitly — a malformed Base URL should probably be rejected at save time (extending the PROVIDERS_REQUIRING_BASE_URL validation) rather than quietly rewritten at call time. Also a design decision: silent-wrong-endpoint vs hard-fail.
3. get_llm_config() still has the present-but-null api_base bug
apps/backend/app/llm.py:481 (Kilo, on #908) — mechanical, low risk
api_base=stored.get("api_base", settings.llm_api_base),dict.get(key, default) returns None when the key is present with a null value — which is exactly what an explicit "clear the Base URL" writes. #908 introduced _effective_api_base() in routers/config.py for this, and routed every site there, but missed llm.py, which is the actual runtime path for every LLM call. So a cleared Base URL still reaches LiteLLM as None instead of falling back to LLM_API_BASE.
Fix: stored.get("api_base") or settings.llm_api_base or None, matching the router.
4. Invalid customSections entries are guarded but not dropped
apps/frontend/lib/utils/resume-normalization.ts (Copilot + Kilo, on #908) — mechanical, low risk
normalizeCustomSection no longer throws on a null/primitive section value, but returns it unchanged. Since normalizeResumeForSave builds the PATCH payload, those values still reach the backend (where they fail CustomSection validation) and still crash any consumer doing Object.entries(customSections) → .sectionType.
Fix: filter invalid entries out of the dict in normalizeResumeForSave rather than passing them through.
Suggested order
3 and 4 are small and independently mergeable. 1 and 2 should wait on the two design calls noted above.
Context
Full remediation history: #902 (4 blockers, 9 high, 8 medium, 6 test gaps), #906 (robustness), #908 (1 P0 + 6 P1 from the release review). Original review: CODE-REVIEW-dev-2026-07-28.md.
Also still open from that review and not addressed: L-04 was fixed in #908 (it was a P0 cross-resume corruption path, not cleanup as originally classified); L-06 (multi-tab last-writer-wins) and L-07 (client-side navigation guard) remain, both pre-existing and non-blocking.
Known coverage gap: the P0 cross-resume race fixed in #908 has no regression test. Several attempts either passed with the fix reverted or failed with it applied — they tested the harness, not the behaviour. The gap is recorded in apps/frontend/tests/resume-builder-autosave.test.tsx. Manual check: open a resume, navigate quickly to a different one, confirm the second resume's content is what is shown and saved.
Source: srbhr/Resume-Matcher