fix: 2026-05-30 full-review fixes — staged-run lifecycle, contract drift, error boundary - #125
Conversation
slittycode
left a comment
There was a problem hiding this comment.
Verdict: APPROVE (self-review — can't formally approve own PR)
Summary
Two-commit PR addressing the P1/P2 cluster from the 2026-05-30 full audit: orphaned subprocess lifecycle, interpretation/measurement stranded-running on setup failure, unbounded frontend poll loop, four parser-dropped Phase 1 fields breaking the citation-existence check, the normalizeKey "majoror" typo, and the latent NameError in the cache-eviction path. Every new logical branch has a test. Phase boundary invariants are clean. The author claims 1069 backend + 716 frontend unit tests green; I couldn't run them in this environment (no venv / node_modules installed), but reviewed all test code in the diff and it's correct.
Findings
Blocking: none.
Tracking only (not introduced or worsened by this PR):
url_ingest.py:159— SSRF DNS-rebinding TOCTOU is a P1 in any hosted/cloud deployment. Acknowledged in the audit and left open. Needs a tracking issue before hosted ships to cloud infra.analyze_core.py:252—truePeakrounded to 1 decimal hides inter-sample overs in the (1.0, 1.05] band, defeating the loudness guardrail. Trivial fix (round(..., 6)to match the fast path).analyze_core.py:633— PLR subtracts dB LUFS from linear true-peak. Systematic inflation, inverts the mixDoctor dynamics verdict on unmastered tracks.
Worth considering:
_enqueue_requested_followupsre-check: closing the cross-transaction interrupt race with a status re-read is the right call — theif updated:gate in the caller can't see that window. The comment explains it well._execute_interpretation_attemptouter wrapper: thefail_interpretation_attemptcall is also safe if the run is already interrupted (WHERE-clause guard makes it a no-op), so the wrapper is defensive in all cases, not just setup-failure cases. Not a bug.
Test results
Not executable in this environment. Reviewed all test code in the diff:
StagedRunInterruptResurrectionTests(5 cases): assertion on DB state directly — correct, meaningful. Tests the measurement/pitch-note/MT3 resurrection paths and the cross-transaction enqueue race.TempFileCacheTests(3 cases): drives the previously-latent NameError path. Correct.- MT3 timeout assertion extension (
assertIn("timed out", ...)): verifies the timeout branch is actually reached after the bytes→str coercion fix rather than dying earlier on a TypeError. This is the right accompanying test. raceWithDeadline(3 cases): covers the abandoned-work unhandled-rejection case — the one most likely to be missing. Correct.analyzeAudio polling timeouts(2 cases): per-poll deadline and wall-clock budget with never-settling and negative-budget scenarios. Correct.ErrorBoundaryunit + smoke: unit testsgetDerivedStateFromErrordirectly (correct for node-env per CLAUDE.md tripwire #5); Playwright smoke aborts the chunk, asserts fallback renders. PR says it fails without the boundary — that's the bar.normalizeKey3-case suite: Maj abbreviation, Min abbreviation, genuine contradiction still detected. Third test is the critical one.- Parser-drop regression guards: all four previously-dropped fields covered through both parse and citation-existence paths.
Phase boundary check
Clean. Parser changes (perBandRt60, preDelayMs, stemEnergyRatio, stemOtherCorrelation) restore fields already emitted by the backend and declared in measurement.ts — additive, no schema change. No Phase 2/3 code touches Phase 1 measurement values. analysis_runtime.py changes are pure state-machine guards.
Generated by Claude Code
Follow-up to the 2026-05-30 review fixes (PR #125). The resurrection guards and the #12 cache path already had regressions, but the two terminalizers this branch ADDED — #7 (_execute_measurement_run source-artifact guard) and #3/#6 (_execute_interpretation_attempt setup wrapper) — rode on the pre-existing MT3 sibling test (test_source_artifact_failure_terminalizes_attempt) with no regression of their own. A reverted guard would have stayed green. Add StageSetupFailureTerminalizationTests, one test per stage, mirroring the MT3 sibling: reserve the stage, make source/grounding resolution raise, and assert the stage terminalizes to 'failed' with the expected error code instead of wedging in 'running'. Verified meaningful — both error out when the terminalizer's except is neutered. Backend 1071 green (+2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
From the 2026-05-30 full review (audits/full-review-2026-05-30.md). Staged-run lifecycle (P1/P2 reliability cluster): - Register pitch-note + MT3 subprocesses for interruption so interrupt/delete actually terminate them instead of orphaning multi-GB children up to their 600s/1800s timeout; route both through the proven _run_streamed_subprocess. - Terminalize interpretation and measurement attempts on setup failure (were left 'running' forever with no reaper). - Guard every attempt/measurement transition against resurrecting an interrupted row, and gate the measurement follow-up enqueue on the guarded update so a cancelled run can't spawn a fresh pipeline. - Frontend monitorAnalysisRun: overall wall-clock budget + per-poll deadline (was an unbounded poll loop with no per-request timeout). Chain-of-custody + quick wins: - Phase 1 parser now carries reverbDetail.perBandRt60 / preDelayMs and vocalDetail.stemEnergyRatio / stemOtherCorrelation -- prompt-citable fields it silently dropped, which broke the citation-existence check. - normalizeKey no longer rewrites "major" into "majoror" (false key contradiction on a valid "Maj" abbreviation). - Define _FILE_CACHE_LOCK / _FILE_CACHE_TTL_SECONDS and import timedelta -- latent NameError in the (currently unwired) temp-file cache path. - Add a React ErrorBoundary around the lazy AnalysisResults Suspense so one render or chunk-load failure can't blank the whole app. Tests: backend resurrection + temp-file-cache regressions (+ caught a second undefined name, timedelta); frontend parser/validator/citation contract, normalizeKey, and polling-timeout regressions. Backend 1068 green, frontend 713 green, lint + production build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bundles the P3/LOW findings from the sequential review of e3e0f69 (verdict: ship-as-is) plus the ErrorBoundary fallback-render coverage. 1. Backend orphan guard (analysis_runtime.py): _enqueue_requested_followups now re-asserts measurement.status == 'completed' before creating follow-up rows. Closes the cross-transaction window where an interrupt committing between complete_measurement's guarded update and the (separate) enqueue transaction would leave inert 'queued' rows on a cancelled run. 2. Faithful MT3 mock-timeout (server.py): the mock branch of _run_streamed_subprocess now coerces bytes streams to text via _stream_text, matching the real branch's text=True contract — so a TimeoutExpired carrying bytes reaches the timeout-specific branch instead of a str-in-bytes TypeError that the broad except masked. Test tightened to assert the timeout message. 3. Bound the mid-loop interpretation POST (analyzer.ts): extracted the race-with-neutralized-fetch logic into an exported raceWithDeadline<T> helper and wrapped the stem-summary createInterpretationAttempt POST in it, closing the last unbounded await in the poll loop. 4. ErrorBoundary docstring: corrected the "Try again" claim — React.lazy caches a rejected import, so it recovers a render-throw but not a chunk-load failure ("Reload page" does). No behavior change. 5. ErrorBoundary fallback smoke test (tests/smoke/error-boundary.spec.ts): drives a mocked run to the results view, aborts the lazy AnalysisResults chunk, and asserts the fallback renders (alert + Try again/Reload). Verified meaningful: it fails when the boundary is removed. Updates the unit-test caveat comment. Tests: backend 1069 green (+1 orphan-race regression), frontend 716 green (+3 raceWithDeadline) + lint clean + the new smoke spec passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the 2026-05-30 review fixes (PR #125). The resurrection guards and the #12 cache path already had regressions, but the two terminalizers this branch ADDED — #7 (_execute_measurement_run source-artifact guard) and #3/#6 (_execute_interpretation_attempt setup wrapper) — rode on the pre-existing MT3 sibling test (test_source_artifact_failure_terminalizes_attempt) with no regression of their own. A reverted guard would have stayed green. Add StageSetupFailureTerminalizationTests, one test per stage, mirroring the MT3 sibling: reserve the stage, make source/grounding resolution raise, and assert the stage terminalizes to 'failed' with the expected error code instead of wedging in 'running'. Verified meaningful — both error out when the terminalizer's except is neutered. Backend 1071 green (+2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10b5b61 to
821175d
Compare
Summary
Fixes from the 2026-05-30 full bug + architecture review (audit
f5b3870b,audits/full-review-2026-05-30.md). Two commits: a reliability/correctness cluster, then the review-driven follow-ups + the missing error-boundary coverage.Commit 1 — staged-run lifecycle + chain-of-custody/contract fixes (
e3e0f69a)Staged-run lifecycle (P1/P2):
_run_streamed_subprocessso interrupt/delete can kill them (were orphaned, multi-GB RAM, until their 600s/1800s timeout), with a status guard so a killed orphan can't resurrect an interrupted row.runningforever, no reaper).monitorAnalysisRungained an overall wall-clock budget + per-poll deadline (was an unbounded poll loop).Chain-of-custody / quick wins:
normalizeKeyno longer rewrites"major"→"majoror"(false key contradiction).timedelta(latent NameError).ErrorBoundaryaround the lazyAnalysisResults.Commit 2 — review follow-ups + ErrorBoundary smoke test (
e0e414f9)A sequential 3-agent review of commit 1 returned ship-as-is; its P3/LOW follow-ups are bundled here:
_enqueue_requested_followupsre-assertsmeasurement.status == 'completed', closing the cross-transaction interrupt window the review found._stream_textbytes→str coercion) so the timeout branch is actually exercised, not masked by astr in bytesTypeError.raceWithDeadlinehelper.AnalysisResultschunk, and asserts the fallback renders. Verified meaningful: it fails when the boundary is removed.Verification
unittest discover).tsclint clean.Notes
No correctness or invariant violations introduced; the changes strictly harden the staged-run lifecycle and the chain of custody. One pre-existing hosted-only edge — a passive multi-client observer polling to the 90-min budget on interrupt — is now bounded by this work and left as an optional follow-up.
🤖 Generated with Claude Code