Empty fetch results are routine, expected behavior (filtered sources,
quiet windows, small subreddits with no qualifying items), not handler
failures. The job already terminates in the success-family
completed_no_items status and RunMetrics already records
result: no_content.
- FetchStep::execute() now logs the empty-packets branch at info
instead of error. The is_wp_error() handler-failure branch above it
is untouched and still logs at error, correctly.
- ExecuteStepAbility::logStepExecutionResult() now skips the paired
"Step execution did not produce a successful result" WARNING when
the classified status is completed_no_items. That WARNING already
gets its own downstream INFO log ("Step completed with no new items
to process"); firing a WARNING on top of that for a successful empty
window was pure noise. This is a clean, bounded gate on one
well-defined status value inside a single call site (not tangled
into generic step-failure handling), so it's a safe accompanying fix
rather than a separate, deferred change.
- Added tests/fetch-no-content-log-severity-smoke.php following the
existing pure-PHP smoke test pattern, asserting both the log-level
change and the WARNING-skip behavior (while confirming genuine
failures still warn).
Fixes #2873
Summary
Fixes #2873.
FetchStep::execute()logged'Fetch handler returned no content'at ERROR level for every empty fetch result — a routine, expected outcome (filtered sources, quiet windows, small subreddits with no qualifying items today), not a handler failure. On wire.extrachill.com's ~27 daily reddit flows this produced ~16 ERROR rows/day of pure noise, diluting the error channel and triggering false-positive investigation via the agent wake briefing.Severity rationale
completed_no_items.RunMetrics::recordStepResult()already recordsresult: no_content— the machine-readable outcome was never wrong, only the log severity.is_wp_error( $packets )branch immediately above ('Fetch handler failed: ...') is the actual handler-failure path and correctly stays aterror— untouched by this PR.info(notdebug) because it's a meaningful per-flow-run event worth keeping visible in normal log output, just not error-worthy. The existing downstream'Step completed with no new items to process'INFO log already covers the human-readable summary; this is the step-level detail one layer below it.Paired WARNING decision
The issue also flagged the paired
'Step execution did not produce a successful result'WARNING fired fromExecuteStepAbility::logStepExecutionResult(). I traced this to a single call site that already receives the classified$execution_result['status'](viaStepExecutionResult::classify()/fromStepOutput()). Gating on'completed_no_items' === $statusthere is a clean, one-line, well-bounded change — it is not tangled into generic step-failure handling, sincecompleted_no_itemsis already a distinct, explicitly-modeled status value (not inferred from packet count at that call site). Skipped it for that one case; every other non-success status (failed,blocked, etc.) still warns exactly as before.Net effect for a routine empty-fetch day: one INFO log from FetchStep + one INFO log from the engine ("no new items to process"). Previously it was ERROR + WARNING + INFO for the same event.
Changes
inc/Core/Steps/Fetch/FetchStep.php— empty-packets branch logs atinfoinstead oferror;is_wp_error()failure branch unchanged.inc/Abilities/Engine/ExecuteStepAbility.php—logStepExecutionResult()early-returns (skips the WARNING) when$execution_result['status'] === 'completed_no_items'.tests/fetch-no-content-log-severity-smoke.php— new pure-PHP smoke test (follows the existingtests/*-smoke.phpconvention) asserting: FetchStep logs at info not error, the handler-failure error log is untouched, RunMetricsno_contentrecording is untouched, the paired WARNING is skipped forcompleted_no_items, and the WARNING still fires for genuinefailedclassifications.Verification
homeboy review data-machine --changed-since origin/main— audit: 0 findings/passed, lint: 0 findings/passed. Theteststage failed in this environment due to a WP Codebox PHPUnit recipe-builder module being unavailable (buildWordPressPhpunitRecipemissing) — a sandbox/harness infra gap, not a code failure; confirmed by running the repo's actual portable test pattern directly instead:fetch-handler-failure-status-smoke.php,job-status-accounting-smoke.php,job-outcome-metrics-smoke.php,step-execution-result-contract-smoke.php,fetch-item-dispositions-smoke.php,run-metrics-smoke.php) viaphp tests/<name>.php. New test: 9/9 pass. Pre-existing failures injob-status-accounting-smoke.php,job-outcome-metrics-smoke.php, andfetch-item-dispositions-smoke.php(missingwp_json_encode()stub for a newerStepResult/RunResultcodepath, and an unrelatednormal completion still marks processedassertion) reproduce identically onorigin/mainbefore this change — confirmed viagit stash. Not introduced by this PR.Not released, not deployed — PR only per task constraints.