Skip to content

fix(fleet-data): cycle-safe toposort::waves#162

Merged
NagyVikt merged 1 commit into
mainfrom
fix/toposort-cycle-safety
May 16, 2026
Merged

fix(fleet-data): cycle-safe toposort::waves#162
NagyVikt merged 1 commit into
mainfrom
fix/toposort-cycle-safety

Conversation

@NagyVikt
Copy link
Copy Markdown
Contributor

Summary

fleet-data::toposort::waves resolves each subtask's wave level via a recursive resolve() helper. The module doc previously warned that cycles in depends_on would overflow the stack and trusted callers to guarantee acyclic input. That was fragile for a dashboard that parses plan.json straight off disk — a hand-edited file, a partial write, or a future schema bug could crash fleet-plan-tree and fleet-waves outright.

This patch adds an in-flight visiting: &mut HashSet<u32> set threaded through the recursion. On entry, if visiting.insert(idx) returns false we're already resolving that node further up the stack, so we return 0 and break the back-edge. On exit (memoised or not) we visiting.remove(&idx) so siblings are evaluated independently.

Acyclic behaviour is preserved verbatim (all six original tests still pass).

Changes

  • resolve() signature gains visiting: &mut HashSet<u32>.
  • waves() allocates a fresh HashSet per outer-loop iteration and threads it in.
  • Module doc updated: cycles are now broken at level 0 instead of overflowing; producer-side acyclic guarantee dropped from a warning to "defence in depth" context.

Test plan

  • cargo test -p fleet-data — 68 passed, 0 failed (60 prior + 2 new + the existing toposort suite).
  • cargo check -p fleet-waves -p fleet-plan-tree — both compile against the new signature (call-site is internal, unchanged).
  • New cycle_broken_to_level_zero test exercises a 2-cycle (A→B→A) and asserts no panic, both nodes emitted, wave count bounded by node count.
  • New self_cycle_broken test exercises A→A and asserts node lands in wave 1 exactly once with no overflow.

fixes review item from PR feedback: a cyclic depends_on graph (hand-edited
plan.json, partial write, future-schema bug) used to overflow the stack in
the recursive resolver and crash fleet-plan-tree / fleet-waves. Track an
in-flight visiting set; on re-entry treat the back-edge as level 0 instead
of recursing. New tests cover a 2-cycle and a self-cycle.
@NagyVikt NagyVikt merged commit 06ac890 into main May 16, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant