Produce → deterministic gate → feed diagnostics to LLM fixer → bounded cycle
State: open/active · Genre: analysis
Use case: deterministic-gate-fix-cycle RFC mapping: UC4 (+UC3) Source: cross-repo
survey of sksizer/* (2026-07-05), companion to the workflow-constellation RFC (PR #682).
The need
Section titled “The need”The single most replicated pattern in the entire survey: run deterministic checks (cargo fmt/clippy/test, just full-check, prd validate), on failure splice the captured diagnostics into a fixer prompt, invoke the agent, re-run the check, bounded at MAX_RETRIES. It exists as a byte-identical vendored bash script across ~10 Rust repos, and is independently re-implemented in Python (DarkFactory), Rust (ontological), and prose (dev, ralph).
Evidence
Section titled “Evidence”rust-fluent-git
Section titled “rust-fluent-git”quality_check.sh: ordered CHECKS tuples, per-check while-loop with MAX_RETRIES=3, role.md + fix_*.md
- raw check output composed into a fixer prompt piped to claude-code, stop-on-first-unrecoverable — vendored byte-identically (md5-verified) into rust-cmd-spec, rust-mcp-proxy, rust-dir-aspect, rust-repo-checkout, rust-markdown, rust-obsidian, rust-agent-adapt, rust-ontogen, and rust-evented-worker.
Where: scripts/quality_check.sh · scripts/quality_check/fix_lint.md ·
scripts/downstream.txt
Pain: The retry counter, diagnostics splicing, gate ordering, and dry-run are hand-rolled once and then copy-distributed to ~10 repos because the workflow cannot be a library dependency.
DarkFactory
Section titled “DarkFactory”on_failure=‘retry_agent’: the shell-task dispatcher captures failed-check output, binds it to {{CHECK_OUTPUT}}, synthesizes a retry AgentTask from the last agent step, re-invokes once, re-runs the check — with implicit last-agent-task coupling threaded through the dispatch loop.
Where: python/darkfactory/runner.py ·
python/darkfactory/workflow/definitions/prd/default/workflow.py
Pain: The cycle is entangled in dispatcher state instead of being a declarative combinator.
ontological
Section titled “ontological”Default workflow setup→delegate→validate with CycleBack policy: cargo/pnpm check failures aggregate into failure_context, string-appended to the next agent prompt (‘Previous attempt failed… fix the issues above’), bounded by max_attempts=3.
Where: src-tauri/src/engine/validation.rs · src-tauri/src/engine/agent.rs ·
src-tauri/src/engine/session.rs
Pain: UC4 as index-jumping inside a while loop, with diagnostics-to-prompt plumbing by string concatenation.
polish
Section titled “polish”bring_up_to_date.sh run_with_quality_gate: LLM apply, then a just full-check loop feeding errors back via claude —continue up to 3 attempts, then PR; the deps scripts push the same gate-retry-revert loop entirely into prompt prose (MAX_RETRIES declared but unused).
Where: scripts/bring_up_to_date.sh · scripts/deps_update/task.md
Pain: Where the harness gives up, the retry loop migrates into the prompt and is enforced only by model compliance.
task-work Step 7: run the quality executor, on FAIL re-run with —log, fix, re-invoke, ‘do not proceed until OK’ — unbounded and in prose — with SHA-keyed finding baselines built solely to scope the gate to branch-introduced drift.
Where: plugin/lib/services/quality/run-checks.ts · plugin/lib/services/quality/baseline.ts ·
plugin/skills/task-work/SKILL.md
Pain: The cycle has no enforced bound, and the baseline machinery exists to compensate for the gate lacking typed, scoped diagnostics.
The typecheck/lint/test-before-commit gate exists only as prompt prose inside one opaque agent turn; the README concedes broken code compounds without real feedback loops.
Where: CLAUDE.md · prompt.md · README.md
Pain: The gate is honor-system: nothing outside the LLM verifies the checks ran.
Flowline fit
Section titled “Flowline fit”@flow/workflow’s cycle combinator with a deterministic gate task is UC4 verbatim. Evidence-driven requirements: a typed diagnostics channel from gate to fixer (today it is string splicing everywhere), per-attempt structured reporting and cost bounds (absent in every implementation), and an enforced iteration cap (dev’s prose version is unbounded).
Proposed API
Section titled “Proposed API”Builds on: 04 Introduces: cycle()
The implement → lint/test → feed-diagnostics-back → re-check loop as one combinator:
import { cycle } from "flowline/workflow";
const implementLoop = cycle("implement-loop", { do: implement, // agent step (tools: Read/Edit/Bash) -> writes Diff check: lintAndTest, // deterministic -> { ok, diagnostics } onFail: fixFromDiagnostics, // agent step: reads diagnostics, edits, loops to check maxIterations: 5, // stops on first passing check, else typed failure});The iteration counter, diagnostics threading, and stop-on-first-pass exit — the parts every repo rebuilds — live in the combinator, not the call site.