One gap in the checker layer: approved, reason is still binary. The checker can distinguish three things that aren't approval, but verify() compresses them into one False:
Output is wrong (real rejection — the maker should retry with the reason as a constraint)
Output can't be verified yet (tool trace is incomplete, external service is down — hold, don't reject)
Output is correct if a dependency holds (another agent's PR merges first — bind, don't reject)
All three return approved=False and the loop treats them identically. The maker regenerates work that wasn't wrong.
Thanks for taking the time to share this. I like the distinction you’re making.
The goal of my post was to explain the core implementation pattern and highlight the conceptual difference between Loop Engineering and Harness, rather than dive deep into the design space of the checker itself.
I can definitely see a richer verdict model being valuable once you’re dealing with external systems, asynchronous dependencies, or multi-agent workflows. Separating reject, wait, and blocked preserves intent, avoids unnecessary regeneration, and gives the loop much finer-grained control without changing the overall architecture.
Appreciate you sharing both the implementation and the write-up. Looking forward to reading through them.
One gap in the checker layer: approved, reason is still binary. The checker can distinguish three things that aren't approval, but verify() compresses them into one False:
Output is wrong (real rejection — the maker should retry with the reason as a constraint)
Output can't be verified yet (tool trace is incomplete, external service is down — hold, don't reject)
Output is correct if a dependency holds (another agent's PR merges first — bind, don't reject)
All three return approved=False and the loop treats them identically. The maker regenerates work that wasn't wrong.
Built a typed evaluator return for exactly this: https://github.com/MetaCortex-Dynamics/verdict4
The argument for why four values: https://metacortexdynamics.substack.com/p/your-loop-has-two-states-it-needs
Would slot directly into your checker.verify() — replace the bool with a Verdict and the loop gets exclusion memory for free.
Thanks for taking the time to share this. I like the distinction you’re making.
The goal of my post was to explain the core implementation pattern and highlight the conceptual difference between Loop Engineering and Harness, rather than dive deep into the design space of the checker itself.
I can definitely see a richer verdict model being valuable once you’re dealing with external systems, asynchronous dependencies, or multi-agent workflows. Separating reject, wait, and blocked preserves intent, avoids unnecessary regeneration, and gives the loop much finer-grained control without changing the overall architecture.
Appreciate you sharing both the implementation and the write-up. Looking forward to reading through them.