Ralph Loop 2026-03-16: Status Freshness Candidate Review Refresh
Why this change
python3 main.py research-status --json was hydrating a fresh live
candidate_review packet, but the returned freshness block still preferred the
older stored candidate_review_generated_at and latest_scan_finished_at
timestamps from research/latest_state.json.
That made the handoff internally inconsistent:
candidate_review.generated_atwas freshfreshness.candidate_review_generated_atstill pointed at the stale packetstatus_notescould still includecandidate_review_is_staleeven after the candidate-review packet had just been refreshed
This is a data-quality issue for PRD 003 and PRD 004 because Ralph uses the status handoff to decide whether live review quality is stale or current.
Change
Updated research/status.py so _build_freshness() now prefers the currently hydrated
payload["candidate_review"] timestamps over the older stored freshness values
when both are present.
Added a focused regression in tests/test_research_status.py covering the exact case:
- stale stored freshness timestamps
- fresh in-memory candidate-review refresh
- freshness block must follow the refreshed packet
Validation
pytest -q tests/test_research_status.pyResult:11 passed in 0.13spython3 main.py research-status --jsonResult: -candidate_review.generated_at = 2026-03-16T19:19:50.890990+00:00-freshness.candidate_review_generated_at = 2026-03-16T19:19:50.890990+00:00-freshness.candidate_review_age_seconds = 0-freshness.candidate_review_is_stale = false-status_notesno longer includescandidate_review_is_stale
Outcome
Status output now reports candidate-review freshness consistently after a live refresh, which makes stale-vs-current review quality visible without forcing Ralph to reconcile conflicting fields manually.