rewindNodePaths function

Set<String> rewindNodePaths(
  1. Circuit circuit,
  2. String circuitPath,
  3. Set<String> targetStepIds, {
  4. required String selfStepId,
  5. required Circuit? circuitById(
    1. String circuitId
    ),
})

The full node-path set ONE Rewind resets (tg-o90): the named targetStepIds ∪ their transitive dependents ∪ the rewinding selfStepId, each expanded to its whole subtree.

Every returned path gets state=pending + a bumped per-node rewindCount in ONE chokepoint write; the bump RE-KEYS each node, so keyed reconcile tears the old incarnations down (killing any live effect) and re-runs them virgin.

SCOPE: siblings of the rewinding node, in ITS circuit. A node in a PARENT circuit is never reset — it cannot have advanced, because its dependsOn on this circuit resolves to this circuit's terminal descendant, which is in the set (or downstream of it) and is going pending right now.

A dangling id in targetStepIds is SKIPPED here; the host validates first and routes a dangling/empty rewind to a LOUD supervised failure (a typo'd step id must never silently degrade into "re-run only myself, forever").

Implementation

Set<String> rewindNodePaths(
  Circuit circuit,
  String circuitPath,
  Set<String> targetStepIds, {
  required String selfStepId,
  required Circuit? Function(String circuitId) circuitById,
}) {
  final ids = <String>{
    ...targetStepIds,
    ...transitiveDependents(circuit, targetStepIds),
    selfStepId,
  };
  final paths = <String>{};
  for (final id in ids) {
    final step = circuit.stepById(id);
    if (step == null) continue; // dangling — the host already failed LOUD.
    paths.addAll(subtreeNodePaths(circuitPath, step, circuitById: circuitById));
  }
  return paths;
}