Problem
The eval (Test 10) found that run_cycle conflates failed and empty probe cycles — a cycle that returns zero results because no new papers match the criteria is indistinguishable from a cycle that failed due to a network error, API timeout, or malformed response. Both produce the same return path. This is the core of the "Silent-Staleness Cascade" compound identified in the eval: when a probe cycle fails silently, the system falls back to stale data, and the operator cannot distinguish a genuine "no new papers" result from a failure that has stopped the pipeline. The health endpoint compounds this — it reports the last cycle's status but cannot tell the operator whether that status is genuine.
Acceptance Criteria
Implementation Notes
The simplest approach is a CycleResult dataclass with a status enum field (SUCCESS, EMPTY, FAILED) and optional results and error fields. The existing run_cycle function likely catches exceptions and returns an empty list — change this to return CycleResult(status=FAILED, error=str(e)) in the except block and CycleResult(status=EMPTY) when the probe returns zero results legitimately. This fix also addresses the "opaque failure path" planned item where config errors retry as transient — with a discriminated result, config errors can be classified as FAILED with a non-retriable error type.
References
- Eval finding: Test 10 (run_cycle conflates failed and empty cycles)
- Related files:
run_cycle function (likely in src/scheduler.py or src/core.py), health endpoint handler, test files for cycle logic
Problem
The eval (Test 10) found that
run_cycleconflates failed and empty probe cycles — a cycle that returns zero results because no new papers match the criteria is indistinguishable from a cycle that failed due to a network error, API timeout, or malformed response. Both produce the same return path. This is the core of the "Silent-Staleness Cascade" compound identified in the eval: when a probe cycle fails silently, the system falls back to stale data, and the operator cannot distinguish a genuine "no new papers" result from a failure that has stopped the pipeline. The health endpoint compounds this — it reports the last cycle's status but cannot tell the operator whether that status is genuine.Acceptance Criteria
run_cyclereturn type updated to a discriminated result (e.g., a dataclass/enum withSuccess(results=[...]),Empty(), andFailed(error=...)variants)run_cycleupdated to handle all three variants explicitlyImplementation Notes
The simplest approach is a
CycleResultdataclass with astatusenum field (SUCCESS,EMPTY,FAILED) and optionalresultsanderrorfields. The existingrun_cyclefunction likely catches exceptions and returns an empty list — change this to returnCycleResult(status=FAILED, error=str(e))in the except block andCycleResult(status=EMPTY)when the probe returns zero results legitimately. This fix also addresses the "opaque failure path" planned item where config errors retry as transient — with a discriminated result, config errors can be classified asFAILEDwith a non-retriable error type.References
run_cyclefunction (likely insrc/scheduler.pyorsrc/core.py), health endpoint handler, test files for cycle logic