Skip to content

T-I3QP-gs-push-readiness-frontier

Status: open/ready · Impact: high · Complexity: medium

Implement the push readiness engine as graph_scheduler::ready: get_ready is a pure function of graph and state returning the nodes runnable now. This is the load-bearing surface the E6 dispatcher tick calls in-process. On the v0.1 critical path.

LocationRole today
packages/rust/graph-scheduler/src/ready.rsStubbed by T-ROJC-gs-test-corpus-and-disabled-suites; get_ready is a todo!()
packages/rust/graph-scheduler/src/graph.rsThe DAG core, landed by T-JVXC-gs-dag-core
packages/rust/graph-scheduler/src/status.rsNodeStatus, TriggerRule, StatusSource, landed by T-CLV1-gs-status-model-and-trigger-rules
solutions/ontological/lib/model/entities/task/ops/next.tssdlc task next’s hand-written frontier — build edges, drop unsatisfied depends_on, sort, cap. Stays as-is; C4 rebases it later

src/ready.rs holds the frontier query.

ItemShape
ReadyNodeid: NodeId, attrs: NodeAttrs
ReadyOptionslimit: Option<usize>, where_fn: Option<Box<dyn Fn(&ReadyNode) -> bool>>, order: Option<Box<dyn Sorter>>, toward: Vec<NodeId>
get_ready(&Graph, &impl StatusSource, &ReadyOptions) -> Vec<ReadyNode>The frontier

A node is in the frontier exactly when its own status is Pending and its TriggerRule is satisfied by its parents’ statuses. Dependency satisfaction is not a separate check — it is the default AllSuccess rule. Running nodes are never returned, which is what makes a re-read-each-tick dispatcher unable to double-dispatch.

ReadyOptions declares order and toward from the start so later tasks fill them in rather than widening the struct. toward stays unread until T-8L0G-gs-goal-directed-frontier; order until T-BR6H-gs-priority-sorters. The struct is #[non_exhaustive] with a Default and builder setters, so adding a field later is not a breaking change. The setters are what make that true: a #[non_exhaustive] struct admits no struct expression outside its defining crate — functional update included — so ..Default::default() does not compile for a caller, and the integration suites are callers.

Determinism: with no sorter, the frontier comes back in add_node insertion order (T-ROJC ratified default R-1). A cyclic graph yields an empty frontier rather than an error — no node on a cycle can satisfy its rule.

  1. Implement get_ready over the DAG core and StatusSource: collect Pending nodes, test each node’s trigger rule against its parents’ statuses, apply where_fn, then truncate to limit.
  2. Keep it a pure function — no interior mutability, no caching, nothing that makes two calls with the same arguments disagree.
  3. Un-ignore the Layer 3 suite in tests/ready.rs and make it green.
LocationKindChange
packages/rust/graph-scheduler/src/ready.rsmodifyReplace stubs with the frontier engine
packages/rust/graph-scheduler/src/lib.rsmodifyRe-export get_ready, ReadyNode, ReadyOptions
packages/rust/graph-scheduler/tests/ready.rsmodifyRemove #[ignore] from the Layer 3 suite
packages/rust/graph-scheduler/tests/graph.rsmodifyRemove the #[ignore] on f_cycle_get_ready — a Layer 1 fixture that exercises get_ready, left tagged to this task by T-JVXC-gs-dag-core
  • AC-1: get_ready returns Pending, trigger-satisfied nodes only, truncated to limit when set.
  • AC-2: Running nodes never appear in the frontier.
  • AC-3: A node whose depends_on target is absent from the graph stays out of the frontier under the default rule (first-consumer requirement C, fail-safe).
  • AC-4: get_ready is pure — two calls with the same graph and state return the same ordering, asserted by a test.
  • AC-5: A cyclic graph returns an empty frontier and no error.
  • AC-6: Every row of T-ROJC’s Layer 3 fixture table passes and the suite in tests/ready.rs has no #[ignore] left.

T-CLV1-gs-status-model-and-trigger-rules


← Back to Tasks