Loop Pipeline Router
Overview
Loop pipelines introduce feedback for self-correction when perfect
first-attempt reasoning is unrealistic. The decision that makes a loop a loop
is the conditional edge after the validate node. Naively this is
"valid -> proceed, else -> retry" (Example 5-6), but production needs the
nuance of Example 5-9: distinguish correctable errors (refine) from
fundamental ones (stop), and handle the retries-exhausted case
(fall back to an alternative strategy) so the loop neither terminates
prematurely nor spins forever.
The unified decision table:
valid -> proceed
correctable + retries remaining -> refine (retry_count += 1, loop back)
correctable + retries exhausted -> fallback_strategy
fundamental -> terminate_with_partial
The finite retry budget (Example 5-6's retry_count < 3 / recursion_limit)
is the explicit termination guarantee. An invalid result with no error
diagnostic is treated as fundamental — you cannot safely refine what you cannot
diagnose.
In the DevOps latency investigation (account 123456789012), document
verification on a related claim returns "incomplete" — the operative report
lacks anesthesia-time records. That is a correctable error: refine
(request the specific missing documentation, re-verify), bounded to three
request cycles before escalating. A schema-level contradiction in the plan
("synchronous AND event-driven for the same operation") is fundamental:
terminate with partial results rather than burn the retry budget on an
unfixable error.
When to Use
- First-attempt success is unrealistic and a validator can flag correctable
errors
- Documentation-gap re-requests, plan refinement, transient-failure recovery
- You need a guaranteed-terminating self-correction loop with an audit trace
Phrases: "loop pipeline", "refine and re-validate", "retry with feedback",
"correctable vs fundamental error", "fallback planner", "recursion limit".
When NOT to Use
- Strict sequential pipeline with no feedback (use a sequential pipeline)
- Parallel-branch reconciliation (that is a merge/tree concern)
- The validator does not distinguish error kinds (then add severity to the
validator first; routing on undifferentiated errors collapses to blind retry)
Process
| Step |
Input |
Action |
Output |
Verification |
| 1 |
is_valid, error, retry_count, max_retries |
lib.route_after_validation(...) |
RouteResult(decision, retry_count, rationale) |
valid->proceed; correctable+budget->refine(+1); correctable+exhausted->fallback; fundamental->terminate |
| 2 |
attempt_fn, validate_fn, max_retries, fallback_fn |
lib.run_loop(...) |
dict (decision, candidate, iterations, trace) |
loop always terminates; iterations <= max_retries |
| 3 |
run_loop trace |
inspect trace |
per-iteration routing decisions |
each refine increments retry_count; one terminal decision |
Rationalizations
| Agent rationalization |
Documented rebuttal |
| "Just retry until it passes." |
Unbounded retry is the infinite-loop failure mode. The chapter bounds it explicitly (retry_count < 3, recursion_limit=10). After the budget, fall back — do not spin. |
| "Any failure should retry." |
No — fundamental errors do not become correct by retrying (Example 5-9 routes them to terminate_with_partial). Retrying a schema contradiction wastes the whole budget on an unfixable error. |
| "If retries run out, just give up entirely." |
Exhausted correctable retries route to a fallback strategy, not termination. The chapter distinguishes "retries exhausted" (try another approach) from "fundamental" (stop). |
| "An invalid result with no error detail — I'll guess and refine." |
You cannot safely refine what you cannot diagnose. Treat missing-diagnostic-on-invalid as fundamental and terminate with partial; surfacing it beats guessing. |
Red Flags
- Every error is classified correctable. The validator is not detecting
fundamental errors; the loop will exhaust the budget then fall back on
genuinely unfixable inputs.
iterations regularly hits max_retries then falls back. The refine
step is not actually improving the candidate — the feedback is not making it
back into attempt_fn.
- No terminal
proceed across many runs. The validator threshold may be
unsatisfiable, or refine is a no-op; the loop is theatre.
Non-Negotiable Verification
- Run the benchmark battery.
python cli.py benchmark must report:
- valid -> proceed; fundamental -> terminate_with_partial
- correctable with budget -> refine and increments retry_count
- correctable with budget exhausted -> fallback_strategy
- invalid-with-no-error -> terminate_with_partial
run_loop terminates and never exceeds max_retries iterations
- a candidate that becomes valid after N refines yields proceed at N
- Verify CLI help. Exits 0 and prints the SKILL.md description.
Security Posture
- Prompt injection. Validation results and error diagnostics are untrusted
input (often produced by an LLM validator over untrusted content). The
router only maps them onto a fixed decision table; a spoofed severity can
bias routing - mislabeling "fundamental" as "correctable" burns retries,
the reverse suppresses recovery - but nothing is executed.
- Data exfiltration. No network calls, no file writes. Candidates and
error details pass through in-process; the routing trace goes to stdout and
the caller owns downstream piping.
- Privilege escalation. No shell invocation, no eval. The bounded retry
budget is also a resource-abuse guard: an adversary who can keep validation
failing cannot force an infinite loop, and the fallback path must not carry
more privilege than the primary path.
Source Attribution
Distilled from Agentic GraphRAG (O'Reilly, by Anthony Alcaraz and Sam Julien) Ch5 — Reasoning &
Planning: "Loop Pipeline: Iterative Refinement" (Example 5-6,
check_plan_validity) and "Error-handling strategies" (Example 5-9,
route_after_validation). The bounded-loop / recursion_limit guarantee is
from Example 5-6.
1---2name: loop-pipeline-router3description: The conditional-edge routing that turns a validate node into a bounded self-correcting loop (Ch5 Loop Pipeline + Error-handling strategies, Examples 5-6/5-9). Consumes a validation result, an error severity (correctable vs fundamental), and a retry budget, and returns exactly one of: proceed, refine (loop back with a remaining retry), fallback (alternative strategy once retries are exhausted), or terminate-with-partial (fundamental error). The finite retry budget is the explicit bound that prevents infinite loops. Use when first-attempt success is unrealistic and validation can identify correctable errors — plan refinement, documentation-gap re-requests, transient-failure recovery. NOT for strict sequential pipelines with no feedback (use a sequential pipeline), NOT for parallel branch reconciliation (that is a merge/tree concern), NOT as a substitute for the validator itself (this routes on the validator's output; it does not validate).4---56# Loop Pipeline Router78## Overview910Loop pipelines introduce feedback for self-correction when perfect11first-attempt reasoning is unrealistic. The decision that makes a loop a loop12is the conditional edge after the validate node. Naively this is13"valid -> proceed, else -> retry" (Example 5-6), but production needs the14nuance of Example 5-9: distinguish **correctable** errors (refine) from15**fundamental** ones (stop), and handle the retries-exhausted case16(fall back to an alternative strategy) so the loop neither terminates17prematurely nor spins forever.1819The unified decision table:2021```22valid -> proceed23correctable + retries remaining -> refine (retry_count += 1, loop back)24correctable + retries exhausted -> fallback_strategy25fundamental -> terminate_with_partial26```2728The finite retry budget (Example 5-6's `retry_count < 3` / `recursion_limit`)29is the explicit termination guarantee. An invalid result with no error30diagnostic is treated as fundamental — you cannot safely refine what you cannot31diagnose.3233In the DevOps latency investigation (account `123456789012`), document34verification on a related claim returns "incomplete" — the operative report35lacks anesthesia-time records. That is a **correctable** error: refine36(request the specific missing documentation, re-verify), bounded to three37request cycles before escalating. A schema-level contradiction in the plan38("synchronous AND event-driven for the same operation") is **fundamental**:39terminate with partial results rather than burn the retry budget on an40unfixable error.4142## When to Use4344- First-attempt success is unrealistic and a validator can flag correctable45 errors46- Documentation-gap re-requests, plan refinement, transient-failure recovery47- You need a guaranteed-terminating self-correction loop with an audit trace4849Phrases: "loop pipeline", "refine and re-validate", "retry with feedback",50"correctable vs fundamental error", "fallback planner", "recursion limit".5152## When NOT to Use5354- Strict sequential pipeline with no feedback (use a sequential pipeline)55- Parallel-branch reconciliation (that is a merge/tree concern)56- The validator does not distinguish error kinds (then add severity to the57 validator first; routing on undifferentiated errors collapses to blind retry)5859## Process6061| Step | Input | Action | Output | Verification |62|------|-------|--------|--------|--------------|63| 1 | is_valid, error, retry_count, max_retries | `lib.route_after_validation(...)` | `RouteResult(decision, retry_count, rationale)` | valid->proceed; correctable+budget->refine(+1); correctable+exhausted->fallback; fundamental->terminate |64| 2 | attempt_fn, validate_fn, max_retries, fallback_fn | `lib.run_loop(...)` | dict (decision, candidate, iterations, trace) | loop always terminates; iterations <= max_retries |65| 3 | run_loop trace | inspect `trace` | per-iteration routing decisions | each refine increments retry_count; one terminal decision |6667## Rationalizations6869| Agent rationalization | Documented rebuttal |70|------------------------|--------------------|71| "Just retry until it passes." | Unbounded retry is the infinite-loop failure mode. The chapter bounds it explicitly (`retry_count < 3`, `recursion_limit=10`). After the budget, fall back — do not spin. |72| "Any failure should retry." | No — fundamental errors do not become correct by retrying (Example 5-9 routes them to `terminate_with_partial`). Retrying a schema contradiction wastes the whole budget on an unfixable error. |73| "If retries run out, just give up entirely." | Exhausted correctable retries route to a *fallback strategy*, not termination. The chapter distinguishes "retries exhausted" (try another approach) from "fundamental" (stop). |74| "An invalid result with no error detail — I'll guess and refine." | You cannot safely refine what you cannot diagnose. Treat missing-diagnostic-on-invalid as fundamental and terminate with partial; surfacing it beats guessing. |7576## Red Flags7778- **Every error is classified correctable.** The validator is not detecting79 fundamental errors; the loop will exhaust the budget then fall back on80 genuinely unfixable inputs.81- **`iterations` regularly hits `max_retries` then falls back.** The refine82 step is not actually improving the candidate — the feedback is not making it83 back into `attempt_fn`.84- **No terminal `proceed` across many runs.** The validator threshold may be85 unsatisfiable, or refine is a no-op; the loop is theatre.8687## Non-Negotiable Verification88891. **Run the benchmark battery.** `python cli.py benchmark` must report:90 - valid -> proceed; fundamental -> terminate_with_partial91 - correctable with budget -> refine and increments retry_count92 - correctable with budget exhausted -> fallback_strategy93 - invalid-with-no-error -> terminate_with_partial94 - `run_loop` terminates and never exceeds `max_retries` iterations95 - a candidate that becomes valid after N refines yields proceed at N962. **Verify CLI help.** Exits 0 and prints the SKILL.md description.9798## Security Posture99100- **Prompt injection.** Validation results and error diagnostics are untrusted101 input (often produced by an LLM validator over untrusted content). The102 router only maps them onto a fixed decision table; a spoofed severity can103 bias routing - mislabeling "fundamental" as "correctable" burns retries,104 the reverse suppresses recovery - but nothing is executed.105- **Data exfiltration.** No network calls, no file writes. Candidates and106 error details pass through in-process; the routing trace goes to stdout and107 the caller owns downstream piping.108- **Privilege escalation.** No shell invocation, no eval. The bounded retry109 budget is also a resource-abuse guard: an adversary who can keep validation110 failing cannot force an infinite loop, and the fallback path must not carry111 more privilege than the primary path.112113## Source Attribution114115Distilled from *Agentic GraphRAG* (O'Reilly, by Anthony Alcaraz and Sam Julien) Ch5 — Reasoning &116Planning: "Loop Pipeline: Iterative Refinement" (Example 5-6,117`check_plan_validity`) and "Error-handling strategies" (Example 5-9,118`route_after_validation`). The bounded-loop / `recursion_limit` guarantee is119from Example 5-6.