fix(scheduler): correct daemon_status recency, RTW-A provenance tiers, and eval_provider#6116
Merged
Merged
Conversation
bug-ops
enabled auto-merge (squash)
July 12, 2026 00:00
bug-ops
force-pushed
the
fix/6096-scheduler-daemon-status
branch
from
July 12, 2026 00:00
1ad42dd to
bb22109
Compare
…, and eval_provider Three independent zeph-scheduler bugs surfaced by live-testing, grouped into one PR per project triage convention: - daemon_status()'s recent_runs was alphabetically ordered instead of recency-ordered, and last_run was always empty despite the database tracking it. ScheduledTaskInfo now carries last_run, list_jobs_full selects it, and daemon_status sorts by last_run descending before truncating to recent_n; the CLI printer renders last: alongside next:. - TaskProvenance::is_external() was dead code: every RTW-A reentry-defense mechanism gated on != TaskProvenance::Static, collapsing the documented three-tier trust model into a binary one. The injection-pattern-check mechanism now applies unconditionally to External-provenance tasks regardless of the injection_pattern_check config toggle, while UserAdded still respects the toggle, without weakening the existing baseline for either tier. - Scheduled/cron experiment runs used the primary agent provider as both judge and subject, silently defeating the eval_provider self-judge-bias mitigation that the interactive /experiment command and --experiment-run flag already honored. ExperimentTaskHandler now resolves eval_provider via build_eval_provider() at both scheduler-bootstrap call sites, falling back to the primary provider only when unset. Also regates src/scheduler.rs's test module off a vestigial feature = "testing" gate that CI's build-tests archive step never enabled, which had made the entire module (including pre-existing tests) invisible to real CI runs regardless of correctness. Closes #6096 Closes #5950 Closes #5947
bug-ops
force-pushed
the
fix/6096-scheduler-daemon-status
branch
from
July 12, 2026 00:03
bb22109 to
8db2780
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three independent zeph-scheduler bugs surfaced by live-testing, grouped into one PR per project triage convention (same subsystem, no cross-dependencies):
daemon_status()recent_runs was alphabetical, not recent, andlast_runwas always empty.ScheduledTaskInfonow carrieslast_run,list_jobs_fullselects it, anddaemon_statussorts bylast_rundescending (never-run jobs last) before truncating torecent_n. The CLI printer renderslast:alongsidenext:.TaskProvenance::is_external()was dead code. Every RTW-A reentry-defense mechanism gated on!= TaskProvenance::Static, collapsing the documented three-tier trust model (Static/UserAdded/External) into a binary one. The injection-pattern-check mechanism now applies unconditionally toExternal-provenance tasks regardless of theinjection_pattern_checkconfig toggle, whileUserAddedstill respects the toggle — a real strictness differential, without weakening the existing baseline for either tier.eval_provider, judge equaled subject. The interactive/experimentcommand and--experiment-runCLI flag already resolved a distinct judge viabuild_eval_provider(); the scheduler path never did, silently defeating the self-judge-bias mitigation in the unattended code path.ExperimentTaskHandlernow resolveseval_providerthe same way at both scheduler-bootstrap call sites (src/runner.rs,src/acp.rs), falling back to the primary provider only when unset.Incidental discovery, fixed in scope:
src/scheduler.rs's entiremod tests(including pre-existing tests, not just the new ones added here) was gated behind a vestigial#[cfg(feature = "testing")]that CI's actualbuild-testsarchive step (.github/workflows/ci.yml:211) never enables — the module was invisible to real CI regardless of correctness. Regated to#[cfg(all(test, feature = "scheduler"))]; this is what makes the new #5947 regression test actually protect CI rather than being cosmetic. Zero runtime behavior change, test-code only.Known residual limitation (not fixed here, follow-up filed): the
TaskProvenance::Externaldifferential added for #5950 is only meaningful against an attacker who honestly self-labels — provenance is trusted-as-read from the DB with no re-verification at hydration, so a direct-SQL external actor can spoofprovenance="user_added"/"static"to dodge it. Not a regression (pre-fix both non-Static tiers were equally bypassable); tracked as #6114.Follow-up issues filed
Externalprovenance on all DB-hydrated rows instead of trusting the stored column (closes the spoofing gap noted above)daemon_status'sORDER BY last_run DESC LIMITinto SQL instead of fetching all jobs and sorting in Rust (current approach avoids sqlite/postgresNULLS LASTportability risk but doesn't scale query-side)Test plan
cargo +nightly fmt --check— cleancargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warnings— cleancargo nextest run --config-file .github/nextest.toml --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins— 12983 passed, 0 failed, 35 skippedRUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --workspace --features "desktop,ide,server,chat,pdf,scheduler"— cleancargo test --doc -p zeph-scheduler --features "sqlite,daemon"— 23 passedexperiment_task_handler_uses_distinct_eval_providerconfirmed to fail on revert (two distinctMockProviders, asserts independent recorder traffic for subject vs. judge)daemon_statusrecency-ordering test (older/newer/never-run, asserts order and actual values)ExternalvsUserAddedRTW-A treatment, plus unweakened baseline regression coverage.local/testing/coverage-status.mdand.local/testing/playbooks/scheduler-cli.mdupdated with scenarios for all three fixes (Untested status pending live-session verification in a future CI cycle)Closes #6096
Closes #5950
Closes #5947