Skip to content

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.

LocationRole today
packages/rust/graph-scheduler/src/schedule.rsStubbed by T-ROJC-gs-test-corpus-and-disabled-suites
packages/rust/graph-scheduler/src/ready.rsThe bare frontier, landed by T-I3QP-gs-push-readiness-frontier
docs/planning/decisions/D-VSLI-distributed-work-runner-architecture.mdNames the six filters this surface must be able to express, and fixes the port contract as ok / blocked(reason, retry-hint)

src/schedule.rs holds the rule-carrying wrapper over the pure frontier.

ItemShape
RetryOnChange / Never — the two-tier hint
VerdictOk / Blocked { reason: String, retry: Retry }
FilterCtxok(), blocked(reason, retry), chosen_and_in_flight(pred) -> usize
FilterTrait: name(), applies_to(&ReadyNode) -> bool (default true), evaluate(&ReadyNode, &FilterCtx) -> Verdict
ScheduleSchedule::new(&Graph), filter(impl Filter), ready(&impl StatusSource, &ReadyOptions), explain(&impl StatusSource, &NodeId) -> Explanation
Explanationeligible: bool, blocked_by: Vec<BlockedBy>
BlockedByrule: 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.

  1. Implement Verdict, Retry, FilterCtx, the Filter trait and the closure adapters.
  2. Implement Schedule carrying registered filters, and ready applying them over get_ready’s frontier in final sort order.
  3. Implement explain, reading the same verdicts ready produces so the two can never disagree about why a node is withheld.
  4. Un-ignore the Layer 4 suite in tests/schedule.rs and make it green.
LocationKindChange
packages/rust/graph-scheduler/src/schedule.rsmodifyReplace stubs with Schedule, filters, verdicts, explain
packages/rust/graph-scheduler/src/lib.rsmodifyRe-export Schedule, Filter, filter_fn, Verdict, Retry, Explanation, BlockedBy
packages/rust/graph-scheduler/tests/schedule.rsmodifyRemove #[ignore] from the Layer 4 suite
  • AC-1: A filter returns Verdict::Blocked carrying a reason and a Retry hint, and applies_to scopes it so a non-matching node is not gated.
  • AC-2: explain reports eligible plus every blocking rule’s name, reason and retry hint.
  • AC-3: explain and ready agree — a node ready withholds is reported ineligible by explain with the same rule and reason, asserted by a test.
  • AC-4: chosen_and_in_flight counts batch-chosen plus already-Running nodes, evaluated in final sort order, so a counting cap filter blocks the third of three impl candidates.
  • AC-5: The same predicate registered as a filter and passed as where_fn yields an equal frontier.
  • AC-6: Every row of T-ROJC’s Layer 4 fixture table passes and the suite in tests/schedule.rs has no #[ignore] left.
  • 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.

T-I3QP-gs-push-readiness-frontier


← Back to Tasks