feat(foreman): bounded fix iteration on reviewer NO-GO instead of terminal failure#959
feat(foreman): bounded fix iteration on reviewer NO-GO instead of terminal failure#959joryirving wants to merge 1 commit into
Conversation
…minal failure A reviewer NO-GO used to terminally fail the Workload, discarding the review's structured findings; external retry systems could only delete + recreate the Workload and re-run the coder blind (4 production issues burned 3 attempts each reproducing the same rejected patch). The WorkloadReconciler now appends a bounded fix iteration on NO-GO: - new coder step code-<n>-r<k> (same coderAgentRef, same branch, payload.allowOverwrite=true so the coder can replace its own prior attempt via force-with-lease) whose payload.prompt distills the NO-GO reviewers' summary + structured findings from status.result.extra.modelExtra - fresh verify-<n>-r<k> + review-<n>-<i>-r<k> steps chained behind it - bounded by the new Workload.spec.maxReviewIterations (*int32; nil defaults to 1, explicit 0 restores fail-on-first-NO-GO); exhausting the budget fails the Workload as before - status.reviewIterations records the emitted iteration count and a ReviewIterationTriggered condition names the re-dispatched issues Emission mirrors emitEscalations: pure step-synthesis function, idempotency by step-name existence (partial creates repaired on the next reconcile), MaxTasks accounting (MaxTasksIterationCap), and the sovereignty gates on iteration reviewers. Rollup and the escalation trigger now judge each issue by its latest iteration only (activeChildren), so a converged retry completes the Workload while a superseded round's NO-GO no longer pins it at Failed, and the escalation tier defers until iterations settle. patchReviewAdvisories wires each iteration's review to its own round's coder task (and the escalation tier to the latest one) instead of always reading code-<n>. Fixes defilantech#946 Signed-off-by: Jory Irving <jory.irving@stackadapt.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
Interaction note for merge ordering: this PR and #956 (open-PR-on-review-GO) touch adjacent payload/synthesis code, and this branch intentionally does not stamp |
ReviewRequest changes. The controller state-machine work is excellent — but the feature isn't functional yet because the execution half (restoring the prior attempt) is missing, and as written the retry can destroy the base attempt's work. Revision coder rebuilds from Related: the retry also loses the original ask — Recommendation: land this as the loop skeleton, but it does not supersede #951 — keep #951 open, rescoped to the two load-bearing pieces (executor-owned Verified clean and genuinely well done: the bound truly bounds (round |
What
On a reviewer NO-GO the WorkloadReconciler now appends a bounded fix iteration instead of terminally failing the Workload: a new coder step
code-<n>-r<k>(same coder AgentRef, same branch,payload.allowOverwrite: true) whosepayload.promptcarries the reviewer's summary + structured findings, chained to freshverify-<n>-r<k>+review-<n>-<i>-r<k>steps. Bounded by newWorkload.spec.maxReviewIterations(*int32; unset defaults to 1, explicit 0 restores fail-on-first-NO-GO).status.reviewIterationsrecords the emitted count and aReviewIterationTriggeredcondition names the re-dispatched issues.Why
The review result carries exactly what a fix needs (findings + summary + branch), but a NO-GO discarded it; external retry systems could only delete + recreate the Workload and re-run the coder blind, reproducing the same rejected patch.
Fixes #946
How
internal/foreman/controller/workload_iteration.go: emission modeled onemitEscalations— pure step-synthesis function (reviewIterationSteps), idempotency by step-name existence (partial creates repaired next reconcile), MaxTasks accounting (MaxTasksIterationCap), sovereignty gates on iteration reviewers, steady-state condition short-circuit, labeled in-flight placeholders so rollup/escalation see the new round in the same pass.status.result.extra.modelExtraviapkg/foreman/agent/reviewer.ParseFindings, with a raw-JSON fallback for non-conforming (legacy map-shaped) findings.activeChildren): a converged retry completes the Workload, a superseded round's NO-GO no longer pins it at Failed, and the escalation tier defers until iterations settle. Escalation semantics are unchanged for workloads that opt out (existing [FEATURE] Foreman v0.2 / v0.8.0: close the reviewer-step empirical gap #546 tests now setmaxReviewIterations: 0since the default changed to 1).patchReviewAdvisorieswires each iteration's reviews to their own round's coder task (escalate reads the latest) instead of alwayscode-<n>.Notable default change: unset
maxReviewIterationsmeans one fix iteration, so a NO-GO now fails the Workload one round later than before.Verification:
KUBEBUILDER_ASSETS=... go test ./internal/foreman/controller/ -count=1(envtest specs for NO-GO-with-budget → r1 steps with feedback prompt + allowOverwrite, exhausted budget → Failed/ChildrenIncomplete, GO → no iteration, iteration count in status, idempotency; plus table tests for step synthesis, prompt rendering, and superseded-round filtering);golangci-lint run ./internal/foreman/... ./api/...0 issues.AI assistance: authored by Claude (Opus 4.8) operating under the maintainer's direction; reviewed before submitting.
Checklist
make testpasses locallymake lintpasses locallygit commit -s) per DCO