T-7OJL-gs-eligibility-filters-and-explain
Status: open/ready · Impact: high · Complexity: medium
Implement eligibility filters — hard gates returning a reasoned verdict — and
explain, both carried by a Schedule in graph_scheduler::schedule. This is
the extension point D-VSLI’s six scheduling filters plug into, and the source of
the per-filter explain() the read-only status API renders. On the v0.1 critical
path.
| Location | Role today |
|---|---|
packages/rust/graph-scheduler/src/schedule.rs | Stubbed by T-ROJC-gs-test-corpus-and-disabled-suites |
packages/rust/graph-scheduler/src/ready.rs | The bare frontier, landed by T-I3QP-gs-push-readiness-frontier |
docs/planning/decisions/D-VSLI-distributed-work-runner-architecture.md | Names the six filters this surface must be able to express, and fixes the port contract as ok / blocked(reason, retry-hint) |
Proposed
Section titled “Proposed”src/schedule.rs holds the rule-carrying wrapper over the pure frontier.
| Item | Shape |
|---|---|
Retry | OnChange / Never — the two-tier hint |
Verdict | Ok / Blocked { reason: String, retry: Retry } |
FilterCtx | ok(), blocked(reason, retry), chosen_and_in_flight(pred) -> usize |
Filter | Trait: name(), applies_to(&ReadyNode) -> bool (default true), evaluate(&ReadyNode, &FilterCtx) -> Verdict |
Schedule | Schedule::new(&Graph), filter(impl Filter), ready(&impl StatusSource, &ReadyOptions), explain(&impl StatusSource, &NodeId) -> Explanation |
Explanation | eligible: bool, blocked_by: Vec<BlockedBy> |
BlockedBy | rule: String, reason: String, retry: Retry |
A Filter is a trait rather than a bare closure so a rule carries its own name
— explain reports which rule blocked a node, and a closure has no name. A
closure adapter (filter_fn(name, predicate), plus a variant taking an
applies_to matcher) covers the ergonomic case and gives D-BPD8’s
.filter(name, opts, predicate) overloads one Rust spelling each.
Filters are hard gates: a node the frontier would return is withheld if any applicable filter blocks it. Sorters defer, filters exclude.
chosen_and_in_flight needs an evaluation order to be meaningful. Per T-ROJC
ratified default R-5, candidates are evaluated in final sort order, and “chosen”
counts peers already selected this batch that passed every filter, plus nodes
already Running. That makes a counting filter — an in-flight cap — expressible
without a separate capacity primitive.
ReadyOptions::where_fn and a registered filter must produce the same frontier
for the same predicate. The difference is only that a filter is named,
reusable, and visible to explain.
Approach
Section titled “Approach”- Implement
Verdict,Retry,FilterCtx, theFiltertrait and the closure adapters. - Implement
Schedulecarrying registered filters, andreadyapplying them overget_ready’s frontier in final sort order. - Implement
explain, reading the same verdictsreadyproduces so the two can never disagree about why a node is withheld. - Un-ignore the Layer 4 suite in
tests/schedule.rsand make it green.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
packages/rust/graph-scheduler/src/schedule.rs | modify | Replace stubs with Schedule, filters, verdicts, explain |
packages/rust/graph-scheduler/src/lib.rs | modify | Re-export Schedule, Filter, filter_fn, Verdict, Retry, Explanation, BlockedBy |
packages/rust/graph-scheduler/tests/schedule.rs | modify | Remove #[ignore] from the Layer 4 suite |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: A filter returns
Verdict::Blockedcarrying a reason and aRetryhint, andapplies_toscopes it so a non-matching node is not gated. - AC-2:
explainreportseligibleplus every blocking rule’s name, reason and retry hint. - AC-3:
explainandreadyagree — a nodereadywithholds is reported ineligible byexplainwith the same rule and reason, asserted by a test. - AC-4:
chosen_and_in_flightcounts batch-chosen plus already-Runningnodes, evaluated in final sort order, so a counting cap filter blocks the third of threeimplcandidates. - AC-5: The same predicate registered as a filter and passed as
where_fnyields an equal frontier. - AC-6: Every row of T-ROJC’s Layer 4 fixture table passes and the suite in
tests/schedule.rshas no#[ignore]left.
Out of scope
Section titled “Out of scope”- Sorters (T-BR6H-gs-priority-sorters) — this task registers no ordering.
- Goal-direction (T-8L0G-gs-goal-directed-frontier).
- The six concrete D-VSLI filters (scope conflict, in-flight caps, kind-has-process, leaf-only). They are policy built on this seam, later in E3, and they live with the engine rather than in this domain-blind crate.
Dependencies
Section titled “Dependencies”- T-I3QP-gs-push-readiness-frontier — filters gate the frontier the push engine produces.
Depends on
Section titled “Depends on”T-I3QP-gs-push-readiness-frontier