Skip to content

fix: validator-time guard on inbound-to-terminal edges (#3)#85

Merged
mattleaverton merged 2 commits into
danshapiro:mainfrom
mattleaverton:fix/3-validator-terminal-condition-edge
Apr 27, 2026
Merged

fix: validator-time guard on inbound-to-terminal edges (#3)#85
mattleaverton merged 2 commits into
danshapiro:mainfrom
mattleaverton:fix/3-validator-terminal-condition-edge

Conversation

@mattleaverton
Copy link
Copy Markdown
Collaborator

@mattleaverton mattleaverton commented Apr 24, 2026

Summary

From docs/plans/2026-04-24-kilroy-fixes-from-feedback.md item #3 (option b — validator-time).

Bug: A run that follows an unconditional edge to a terminal node after a failed predecessor stage reports status=success with no actual work done. Every graph author hits this trap (the canonical example: pr-review PR #303 v1 had 4× transient failures in setup, followed setup -> done, reported success, no review produced).

Fix: New validator rule terminal_condition_edge (ERROR severity). Requires explicit condition= on every inbound edge to a terminal node (Msquare, doublecircle, or node id exit/end) unless the predecessor is success-only (Mdiamond, circle, diamond, loop.begin/end, concurrent.split/join). Catches the bug at validate time — cheaper than runtime reconciliation. The error message names both offending node IDs and suggests the exact condition to add.

Reconciled with all_conditional_edges

The natural fix for the new rule (adding condition="outcome=success" to flagged edges) used to trigger the existing all_conditional_edges rule, which fires on nodes whose outgoing edges are all conditional with no fallback. That tension is resolved: all_conditional_edges now exempts two patterns where there's no actual routing gap:

  1. All targets terminal — "no condition matched" terminates the run by intent, and terminal_condition_edge governs those edges. The classic report → done [condition="outcome=success"] pattern is now valid.
  2. Exhaustive outcome=X / outcome!=X pair — the pair covers the full outcome space, so the routing is provably complete. The classic prepare → next [condition="outcome=success"] + prepare → done [condition="outcome!=success"] pattern is now valid.

Existing workflow graphs

All three graphs in workflows/ (build-test, coding-loop, multi-tool-exercise) had latent unconditional inbound-to-terminal edges from fallible predecessors. This PR adds explicit conditions to all six flagged edges. All three graphs now validate clean.

Test plan

  • 11 new tests in internal/attractor/validate/terminal_condition_test.go covering all acceptance criteria for the new rule.
  • Updated existing all_conditional_edges tests to use non-terminal intermediate nodes (the old a → exit topology now correctly falls under the all-targets-terminal exemption).
  • New TestValidate_AllConditionalEdges_ExemptOnExhaustivePair and TestValidate_AllConditionalEdges_ExemptWhenAllTargetsTerminal.
  • go test ./internal/attractor/validate/... passes.
  • kilroy attractor validate --graph clean on all three workflow graphs.
  • go build ./... and go vet ./... clean.

Context

Produced by a dogfood quick-launch run against this repo. Initial agent flagged the rule tension but punted to reviewer; resolved in a follow-up commit on this branch.

🤖 Generated with Claude Code

mattleaverton and others added 2 commits April 24, 2026 17:52
…x existing graphs

The new terminal_condition_edge rule and the existing all_conditional_edges
rule were in tension: the natural fix for the new rule (adding
condition="outcome=success" to edges flagged by terminal_condition_edge)
triggered all_conditional_edges, which fires when a node has only
conditional outgoing edges with no fallback.

all_conditional_edges now exempts two patterns:

1. Every outgoing edge targets a terminal node — "no condition matched"
   terminates the run by intent; terminal_condition_edge governs those
   edges instead.

2. Conditions are exhaustive via an outcome=X / outcome!=X pair — the
   pair covers the full outcome space, so there is no runtime routing gap.

Also adds explicit conditions to the inbound-to-terminal edges in the three
existing workflow graphs (build-test, coding-loop, multi-tool-exercise) so
all three now validate clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mattleaverton mattleaverton merged commit 95637c9 into danshapiro:main Apr 27, 2026
1 check failed
@mattleaverton mattleaverton deleted the fix/3-validator-terminal-condition-edge branch April 27, 2026 17:57
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