openrewrite / openrewrite/rewrite
RecipeRun does not distinguish a fixpoint from an exhausted cycle budget
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 3.7k
- Forks
- 570
- Avg merge
- 13h 12m
- Merged PRs (30d)
- 261
Description
Hi — an observation about RecipeScheduler.runRecipeCycles and RecipeRun, not a bug report. It comes from a methodological result on iterated structural repair I deposited recently, where this exact distinction turned out to be the difference between a measurement and an artifact.
What the code does.
runRecipeCycles leaves its loop by three different paths:
- fixpoint —
i >= minCycles && (cycle.getMadeChangesInThisCycle().isEmpty() || !anyRecipeCausingAnotherCycle) - panic —
ctx.getMessage(PANIC) != null - budget — the loop condition
i <= maxCyclesfails
All three reach the same return new RecipeRun(after.getChangeset(), ...), and RecipeRun has two fields: changeset and dataTableStore. Neither records the cycle reached nor which exit was taken.
So a run that reached a fixpoint and a run that was still making changes when the budget ran out are indistinguishable to the caller.
Why the distinction is load-bearing.
I built an engine that repairs a typed graph by iterated descent — each step strictly reduces an integer count of rule violations — and measured how often it fails to reach a clean state. Its halt states are: clean; stuck, meaning no available step reduces the count; and exhausted, meaning the step budget was reached.
The finding was that the third is not an outcome, it is an artifact control. Only measurements where exhaustion is zero say anything about the system. Where it is nonzero, "did not finish clean" is a statement about the budget, not about the input. In my sweep an apparent regularity in preliminary data dissolved into sampling noise once that control was enforced across every cell — without it I would have reported a false result.
The same asymmetry holds here, and it is not only a measurement concern. "The recipe made no further changes" is a property of the recipe and the codebase. "The recipe was cut off at cycle N" is a property of the configuration. Collapsing the two means a fully applied migration and a partially applied one report identically, and the difference surfaces only when someone runs the recipe again and gets a fresh diff.
- This is visible in the project's own history. #2415 was closed with:
recipe execution is designed to continue processing until either no changes are detected, or the max number of cycles is exceeded
which is accurate, and states the two exits as if they were the same kind of ending.
Concrete question.
Would you consider adding to RecipeRun either:
(a) the cycle count reached; and/or
(b) the termination reason — fixpoint, panic, or cycle budget exhausted.
(b) is the one that carries the information. Both values exist inside runRecipeCycles at the point of return: the loop variable i, and which branch was taken.
It would let a build warn, or fail, on "changes were still being made when the budget ran out" — which today can only be discovered by running again and seeing a new diff.
Reference, if the argument is of interest: Two Regimes of Failure in Structural Repair, https://doi.org/10.5281/zenodo.21249907 — §4.2 defines the halt states and §4.6 is the case where the control caught a result that would otherwise have been reported.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with RecipeScheduler.runRecipeCycles and RecipeRun, then inspect how RecipeRun is constructed and consumed. Trace the three loop exit paths—fixpoint, panic, and exhausted cycle budget—and determine how the cycle count or termination reason should be exposed. Done means callers can distinguish these outcomes instead of seeing only the changeset and dataTableStore.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- devtools
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100