Eligibility filters
Status: open/planned · Kind: component · Audience: user
Summary
Section titled “Summary”- Registered filters that gate which ready nodes are returnable, each returning a reasoned verdict (eligible / ineligible-with-reason), not a bare bool.
- A two-tier rejection (retry-on-change vs never) powers
explain()and tells the caller whether to re-test the node next tick. - Sub-capability of C-D2GO-readiness-scheduling.
Statement
Section titled “Statement”A filter is a named predicate over a node (the Kubernetes Filter model). It
receives the node and a context — the batch so far, in-flight counts, a handle
to the caller’s store — and returns Verdict::Ok or Verdict::Blocked. Filters
are hard gates: a node the frontier would return is withheld if any
applicable filter blocks it. Because the verdict carries a reason, explain can
say why a node is not runnable. A per-category in-flight cap is just a filter
that counts running-by-category.
Filters are expressed as a Filter trait rather than a bare closure,
because a rule has to carry its own name — explain reports which rule blocked a
node, and a closure has none. A named closure adapter covers the ergonomic case.
What it provides
Section titled “What it provides”Schedule::filter(impl Filter)— named registration, withapplies_toas the matcher-scoping hook.filter_fn(name, predicate)and a variant taking anapplies_tomatcher — one Rust spelling for each TypeScript overload.- Verdicts:
ctx.ok(),ctx.blocked(reason, Retry::OnChange | Retry::Never). explain(state, id)— the blocking rules and reasons for a withheld node.
use graph_scheduler::{Retry, Schedule, filter_fn, filter_fn_scoped};
Schedule::new(&g) .filter(filter_fn_scoped( "no-lease", |n| n.attrs.category.as_deref() == Some("impl"), |node, ctx| match leases.is_held(&node.id) { true => ctx.blocked("lease held", Retry::OnChange), false => ctx.ok(), }, )) .filter(filter_fn("cap-impl", |_node, ctx| { match ctx.chosen_and_in_flight(|n| n.attrs.category.as_deref() == Some("impl")) >= 2 { true => ctx.blocked("impl cap (2)", Retry::OnChange), false => ctx.ok(), } }));Inputs
Section titled “Inputs”- Registered
Filterimplementations, each with a name and an optionalapplies_tomatcher. - Per query: the node and a
FilterCtx(batch-so-far, in-flight counts, store handle).
Outputs
Section titled “Outputs”- A pass/withhold decision per node;
explainreasons.
Hook points
Section titled “Hook points”- The primary extension surface. Callers register domain rules — leases, category caps, or scope-conflict via PR-0002-intersect.
- This is the seam D-VSLI-distributed-work-runner-architecture’s six scheduling filters plug into. The filters themselves are engine policy and do not live in this domain-blind crate; only the contract does.
Underlying implementation
Section titled “Underlying implementation”- No code realizes this yet — the capability is planned. It lands with the crate
at
packages/rust/graph-scheduler/, filters andexplaininsrc/schedule.rs. Until the crate exists there is no anchor to record here.
- The two-tier retry hint mirrors Kubernetes
UnschedulablevsUnschedulableAndUnresolvable; the reason stays with the verdict (avoiding Nomad’s metrics side-channel). Filter vs. sorter is the hard-vs-soft split (C-V44L-priority-ordering-sorters). chosen_and_in_flightonly means something given an evaluation order. Candidates are evaluated in final sort order, and “chosen” counts peers already selected this batch that passed every filter, plus nodes alreadyRunning. That is what lets a counting filter express an in-flight cap with no separate capacity primitive.