Skip to content

fix(scheduler): correct daemon_status recency, RTW-A provenance tiers, and eval_provider#6116

Merged
bug-ops merged 1 commit into
mainfrom
fix/6096-scheduler-daemon-status
Jul 12, 2026
Merged

fix(scheduler): correct daemon_status recency, RTW-A provenance tiers, and eval_provider#6116
bug-ops merged 1 commit into
mainfrom
fix/6096-scheduler-daemon-status

Conversation

@bug-ops

@bug-ops bug-ops commented Jul 12, 2026

Copy link
Copy Markdown
Owner

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, and last_run was always empty. ScheduledTaskInfo now carries last_run, list_jobs_full selects it, and daemon_status sorts by last_run descending (never-run jobs last) 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 (Static/UserAdded/External) 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 — a real strictness differential, without weakening the existing baseline for either tier.
  • Scheduled/cron experiment runs ignored eval_provider, judge equaled subject. The interactive /experiment command and --experiment-run CLI flag already resolved a distinct judge via build_eval_provider(); the scheduler path never did, silently defeating the self-judge-bias mitigation in the unattended code path. ExperimentTaskHandler now resolves eval_provider the 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 entire mod tests (including pre-existing tests, not just the new ones added here) was gated behind a vestigial #[cfg(feature = "testing")] that CI's actual build-tests archive 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::External differential 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 spoof provenance="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

Test plan

  • cargo +nightly fmt --check — clean
  • cargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warnings — clean
  • cargo nextest run --config-file .github/nextest.toml --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins — 12983 passed, 0 failed, 35 skipped
  • RUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --workspace --features "desktop,ide,server,chat,pdf,scheduler" — clean
  • cargo test --doc -p zeph-scheduler --features "sqlite,daemon" — 23 passed
  • New regression test experiment_task_handler_uses_distinct_eval_provider confirmed to fail on revert (two distinct MockProviders, asserts independent recorder traffic for subject vs. judge)
  • New daemon_status recency-ordering test (older/newer/never-run, asserts order and actual values)
  • Two new differential tests proving External vs UserAdded RTW-A treatment, plus unweakened baseline regression coverage
  • .local/testing/coverage-status.md and .local/testing/playbooks/scheduler-cli.md updated with scenarios for all three fixes (Untested status pending live-session verification in a future CI cycle)

Closes #6096
Closes #5950
Closes #5947

@bug-ops
bug-ops enabled auto-merge (squash) July 12, 2026 00:00
@github-actions github-actions Bot added documentation Improvements or additions to documentation rust Rust code changes labels Jul 12, 2026
@bug-ops
bug-ops force-pushed the fix/6096-scheduler-daemon-status branch from 1ad42dd to bb22109 Compare July 12, 2026 00:00
@github-actions github-actions Bot added bug Something isn't working size/L Large PR (201-500 lines) labels Jul 12, 2026
…, 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
bug-ops force-pushed the fix/6096-scheduler-daemon-status branch from bb22109 to 8db2780 Compare July 12, 2026 00:03
@bug-ops
bug-ops merged commit b7d301b into main Jul 12, 2026
43 checks passed
@bug-ops
bug-ops deleted the fix/6096-scheduler-daemon-status branch July 12, 2026 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation rust Rust code changes size/L Large PR (201-500 lines)

Projects

None yet

1 participant