Skip to content

T-0WNK-gs-pull-resolution

Status: planning/backlog · Impact: low · Complexity: small

Add the demand-driven variant graph_scheduler::pull: resolve(graph, state, goal, limit) — runnable work toward a goal, resolved over the goal’s ancestors instead of enumerating the global frontier. Behind an optional pull Cargo feature.

Deferred until after cutover. Not part of graph-scheduler v0.1. D-BPD8’s own open question asks whether to ship pull at all or defer to goal-directed push alone, and it is still open. The engine’s dispatcher tick is level-triggered — it recomputes the whole frontier every tick — which is the push model, so nothing in v0.1 has a reason to pull. Parked at planning/backlog.

need_human_review is set because this task may not survive: if T-8L0G-gs-goal-directed-frontier lands and no consumer asks for lazy resolution, the right outcome is closing this as superseded rather than building a second path to the same answer.

LocationRole today
packages/rust/graph-scheduler/src/pull.rsDoes not exist. Not scaffolded by T-ROJC-gs-test-corpus-and-disabled-suites
packages/rust/graph-scheduler/src/goal.rsonly_toward gives the same answer set eagerly; pull’s only claim is avoiding the global frontier
docs/planning/decisions/D-BPD8-graph-scheduler-api/README.mdOpen question: ship pull in v1, or defer to goal-directed push only

src/pull.rs, gated by an optional pull feature, mirroring the tracker’s treatment of a D-BPD8 subpath as a Cargo feature.

ItemShape
resolve(&Graph, &impl StatusSource, &NodeId, Option<usize>) -> Vec<ReadyNode>Runnable nodes toward the goal

The traversal walks the goal’s unfinished ancestors on demand and stops at satisfied prerequisites, rather than computing the global frontier and intersecting. The result must equal get_ready with where_fn: only_toward(goal) — the fixture asserts that equality, so the two paths cannot diverge in behaviour while differing in cost.

That equality is also the test of whether this task should exist. If the lazy walk is not measurably cheaper on a graph large enough to matter, it is a second implementation of an answer the crate already has.

  1. Add the optional pull feature and implement resolve as a lazy walk over Graph::ancestors, reusing the frontier predicate rather than restating it.
  2. Land the Layer 7 fixture table from T-ROJC’s matrix as a const table in tests/pull.rs, un-ignored, including the equality case against only_toward.
  3. Record a measurement against the eager path in the PR body, so the D-BPD8 open question is closed with evidence either way.
LocationKindChange
packages/rust/graph-scheduler/src/pull.rsnewDemand-driven resolve
packages/rust/graph-scheduler/Cargo.tomlmodifyAdd the optional pull feature
packages/rust/graph-scheduler/src/lib.rsmodifyFeature-gated module declaration and re-export
packages/rust/graph-scheduler/tests/pull.rsnewLayer 7 fixtures
  • AC-1: resolve(graph, state, goal, limit) returns the runnable nodes toward the goal, honouring limit.
  • AC-2: The traversal is lazy — it does not enumerate nodes outside the goal’s ancestor set, asserted by instrumenting node visits rather than by inspection.
  • AC-3: resolve returns the same id set as get_ready with where_fn: only_toward(goal).
  • AC-4: The layer is behind a default-off pull feature.
  • AC-5: Every row of T-ROJC’s Layer 7 fixture table passes under --all-features.
  • AC-6: The PR records the cost comparison against the eager path, closing D-BPD8’s open question with a measurement.
  • The stateful tracker (T-EJ3P-gs-stateful-tracker).
  • Any change to the push frontier. Pull reuses its predicate and does not fork it.

T-8L0G-gs-goal-directed-frontier


← Back to Tasks