refactor(goal): extract intercept to goalAdvanceResult for FSM output purity#6961
Open
erphenheimer wants to merge 1 commit into
Open
refactor(goal): extract intercept to goalAdvanceResult for FSM output purity#6961erphenheimer wants to merge 1 commit into
erphenheimer wants to merge 1 commit into
Conversation
… purity advance() previously wrote interceptMsg directly to the goalMachine struct field as a side effect. Multiple FSM mechanisms (todo intercept, self-check, idle detection) could each set interceptMsg, relying on sequential ordering within the switch statement to avoid contention. Extract intercept as an explicit field in goalAdvanceResult. The caller (advanceGoalAfterTurn) stores it via a new setIntercept() method, which takeIntercept() reads as before. This makes the data flow explicit: advance() produces output, the caller routes it. No behavior change. One theoretical edge case (auto-turn limit firing simultaneously with an intercept) has a different interceptMsg state after advance() returns, but the loop exits via cont=false immediately so the intercept is never consumed and gets cleared on the next goal lifecycle event. Related to esengine#6958 (FSM output model refinement). The concrete bug esengine#6956 was fixed separately in PR esengine#6960.
Contributor
Author
|
@SivanCola refactored the FSM output model to make intercept flow explicit. CI passed. Please take a look when you have time. Thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
advance()previously wroteinterceptMsgdirectly to thegoalMachinestruct field as a side effect. Multiple FSM mechanisms (todo incompleteness intercept, strict-mode self-check intercept, idle detection) each wrote to the same field, relying on sequential ordering within theswitchstatement to avoid contention — if a later mechanism overwrote what an earlier one set, the earlier loss was considered acceptable.This refactoring extracts
interceptas an explicit field ingoalAdvanceResult. The caller (advanceGoalAfterTurn) stores it via a newsetIntercept()method, whichtakeIntercept()reads as before. The data flow becomes explicit:advance()produces output, the caller routes it.Changes
internal/control/goal.go— +20/-11 lines:intercept stringtogoalAdvanceResultstructg.interceptMsg = Xassignments insideadvance()with localintercept = Xg.interceptMsg == ""tointercept == ""(checks the local output, not the struct field)g.interceptMsg = ""on the goal-complete path (already covered by local var)setIntercept()method — stores the returned intercept into the struct field fortakeIntercept()to consume on the next iterationg.interceptMsg = ""in the auto-turn-limit path (clears any pending struct-field intercept from a previous iteration)internal/control/turn_orchestrator.go— +3 lines:c.goals.advance(...), storeres.interceptviac.goals.setIntercept()when non-emptyBehavioral verification
Every FSM path was traced (normal running, todo intercept, self-check intercept, idle detection, auto-turn limit, blocked → idle reset, delivery follow-through, repeated intercept → complete). One theoretical edge case differs:
g.interceptMsgentirely; new code stores the intercept viasetIntercept()before the loop exits oncont=false. The stored intercept is never consumed (loop exits immediately) and gets cleared on the next goal lifecycle event (setGoal/resume/restoreFromState). No practical impact.Related
[goal:blocked]) was fixed separately in PR fix(goal): reset idleTurns on [goal:blocked] to prevent premature idle intercept #6960.