Document Date: 2026-06-24 Framework Version: 0.3.0 Status: IMPLEMENTATION SPECIFICATION
This document specifies the complete execution state machine for the Loop Engineering Framework, including explicit iteration lifecycle semantics, transition rules, and disabled-phase bypass paths.
| State | Description | Entry Criteria | Exit Criteria |
|---|---|---|---|
INITIALIZED |
Engine created, ready to start | Engine instantiation | First iteration begins |
SPECIFYING |
Task specification phase | Before planning | Task understood, ready to plan |
PLANNING |
Creating or revising plan | SPECIFYING or ITERATION_COMPLETE | Plan created/revised |
ACTING |
Executing current step | PLANNING | Step execution complete |
OBSERVING |
Capturing execution results | ACTING | Observation recorded |
EVALUATING |
Assessing step quality | OBSERVING | Evaluation complete |
VERIFYING |
Independent verification | EVALUATING | Verification complete |
ITERATION_COMPLETE |
End of iteration boundary | EVALUATING or VERIFYING | Next state determined |
RECOVERING |
Handling failure | EVALUATING or VERIFYING | Recovery action complete |
REPLANNING |
Correcting plan after failure | RECOVERING | New plan ready |
WAITING_FOR_HUMAN |
Awaiting human input | RECOVERING or policy trigger | Human response received |
| State | Description | When Reached |
|---|---|---|
COMPLETED |
Successful termination | All steps verified, goal achieved |
PARTIALLY_COMPLETED |
Partial success | Required steps done, optional skipped |
ABSTAINED |
Intentional non-completion | Policy decision to not complete |
BUDGET_EXHAUSTED |
Budget limit reached | Steps/time/tokens exceeded |
POLICY_TERMINATED |
Policy violation | Safety or policy termination |
FAILED |
Failure termination | Unrecoverable error or max attempts |
INITIALIZED
↓
SPECIFYING
↓
PLANNING
↓
ACTING
↓
OBSERVING
↓
EVALUATING
↓
VERIFYING
↓
ITERATION_COMPLETE
At ITERATION_COMPLETE, the runtime explicitly decides:
ITERATION_COMPLETE
├──→ COMPLETED (all steps done)
├──→ PLANNING (next iteration)
├──→ REPLANNING (plan correction needed)
├──→ RECOVERING (failure handling)
├──→ WAITING_FOR_HUMAN (escalation)
├──→ ABSTAINED (intentional stop)
├──→ FAILED (unrecoverable)
└──→ BUDGET_EXHAUSTED (resource limit)
| Source | Destination | Preconditions | Trigger | Side Effects | Invalid Behavior |
|---|---|---|---|---|---|
| INITIALIZED | SPECIFYING | Engine ready | run() called | state.initialized | Raise if already started |
| INITIALIZED | FAILED | Engine error | Setup failure | Log error | - |
| SPECIFYING | PLANNING | Task understood | Spec complete | - | Raise if no goal |
| SPECIFYING | FAILED | Spec error | Unparseable task | Log error | - |
| PLANNING | ACTING | Plan exists | Plan ready | Increment plan version | Raise if no steps |
| PLANNING | FAILED | Plan error | Unplannable | Create failure | - |
| ACTING | OBSERVING | Step executed | Actor returned | Step.status = EXECUTED | Raise on exception |
| ACTING | RECOVERING | Step failed | Actor exception | Create failure | - |
| ACTING | FAILED | Critical error | Unrecoverable | Create failure | - |
| OBSERVING | EVALUATING | Observation recorded | Observer done | - | Raise if no observation |
| EVALUATING | VERIFYING | Evaluation passed | passed=True | Step.status = EVALUATED | - |
| EVALUATING | RECOVERING | Evaluation failed | passed=False | Step.status = EVALUATION_FAILED | - |
| EVALUATING | REPLANNING | Evaluation requires change | Needs replan | - | - |
| VERIFYING | ITERATION_COMPLETE | Verification passed | verified=True | Step.status = VERIFIED_COMPLETED | - |
| VERIFYING | RECOVERING | Verification failed | verified=False | - | - |
| ITERATION_COMPLETE | PLANNING | More steps pending | has_next_step=True | Increment iteration | - |
| ITERATION_COMPLETE | COMPLETED | All steps verified | plan.complete=True | Finalize result | - |
| ITERATION_COMPLETE | REPLANNING | Plan needs update | plan.stale=True | - | - |
| ITERATION_COMPLETE | RECOVERING | Failure pending | has_unhandled_failure=True | - | - |
| ITERATION_COMPLETE | BUDGET_EXHAUSTED | Budget check | budget.exhausted=True | Set terminal | - |
| ITERATION_COMPLETE | FAILED | Unrecoverable | terminal_failure=True | Set terminal | - |
| Source | Destination | Preconditions | Trigger | Side Effects |
|---|---|---|---|---|
| RECOVERING | REPLANNING | Recovery = replan | Strategy selected | Preserve evidence |
| RECOVERING | ITERATION_COMPLETE | Recovery = retry succeeded | Step ready | Step.status = RETRY_PENDING → READY |
| RECOVERING | WAITING_FOR_HUMAN | Recovery = escalate | Human needed | - |
| RECOVERING | FAILED | Recovery failed | Max attempts reached | Mark terminal |
| REPLANNING | PLANNING | New plan ready | Plan revised | Keep completed steps |
| REPLANNING | FAILED | Replan failed | Cannot replan | Create failure |
| WAITING_FOR_HUMAN | RECOVERING | Human responded | Continue recovery | - |
| WAITING_FOR_HUMAN | REPLANNING | Human instructed replan | Follow instruction | - |
| WAITING_FOR_HUMAN | FAILED | Human aborted | Abort requested | Set terminal |
When enable_observer=False:
| Source | Destination | Preconditions | Trigger |
|---|---|---|---|
| ACTING | EVALUATING | Observer disabled | Skip observation |
When enable_evaluator=False:
| Source | Destination | Preconditions | Trigger |
|---|---|---|---|
| OBSERVING | ITERATION_COMPLETE | Evaluator disabled | Direct to boundary |
| ACTING | ITERATION_COMPLETE | Both disabled | Direct to boundary |
When enable_verification=False:
| Source | Destination | Preconditions | Trigger |
|---|---|---|---|
| EVALUATING | ITERATION_COMPLETE | Verifier disabled | Evaluation sufficient |
When enable_planner=False (fixed plan):
| Source | Destination | Preconditions | Trigger |
|---|---|---|---|
| SPECIFYING | ACTING | Planner disabled | Execute fixed plan |
Terminal states have no valid outgoing transitions:
_VALID_TRANSITIONS = {
ExecutionState.COMPLETED: set(),
ExecutionState.PARTIALLY_COMPLETED: set(),
ExecutionState.ABSTAINED: set(),
ExecutionState.BUDGET_EXHAUSTED: set(),
ExecutionState.POLICY_TERMINATED: set(),
ExecutionState.FAILED: set(),
}Any attempt to transition from a terminal state raises:
RuntimeError: Invalid state transition: {terminal} -> {target}.
Terminal states cannot transition.
All transitions must pass validation:
def _transition_to(self, new_state: ExecutionState) -> None:
if self.state is None:
raise RuntimeError("Cannot transition: state is None")
current_state = self.state.execution_state
# Terminal check
if current_state in TERMINAL_STATES:
raise RuntimeError(
f"Cannot transition from terminal state {current_state.value}"
)
# Valid transition check
valid_next_states = self._VALID_TRANSITIONS.get(current_state, set())
if new_state not in valid_next_states:
raise RuntimeError(
f"Invalid state transition: {current_state.value} -> {new_state.value}. "
f"Valid transitions: {[s.value for s in valid_next_states]}"
)
# Log transition
if self.config.verbose:
logger.info(f"State transition: {current_state.value} -> {new_state.value}")
self.state.execution_state = new_stateThe ITERATION_COMPLETE state serves as an explicit boundary between iterations.
async def _handle_iteration_complete(self, context: LoopContext) -> ExecutionState:
"""
Decide next state at iteration boundary.
Returns the state to transition to.
"""
# 1. Check budget
if self.budget and not self.budget.check_budget():
return ExecutionState.BUDGET_EXHAUSTED
# 2. Check for terminal failures
if any(f.status == FailureStatus.TERMINAL for f in self.state.failures):
return ExecutionState.FAILED
# 3. Check for unhandled failures requiring recovery
unhandled_failures = [
f for f in self.state.failures
if f.status == FailureStatus.UNHANDLED
]
if unhandled_failures:
return ExecutionState.RECOVERING
# 4. Check if plan is complete
if self.state.current_plan and self.state.current_plan.is_complete():
# Verify all required steps passed
if self._all_steps_verified():
return ExecutionState.COMPLETED
else:
return ExecutionState.FAILED
# 5. Continue to next iteration
return ExecutionState.PLANNINGasync def run(self, context: LoopContext) -> LoopResult:
self.state = LoopState(execution_state=ExecutionState.INITIALIZED)
try:
while True:
# Execute one iteration
await self._execute_iteration(context)
# At ITERATION_COMPLETE, decide next state
next_state = await self._handle_iteration_complete(context)
if next_state in TERMINAL_STATES:
self._transition_to(next_state)
break
elif next_state == ExecutionState.PLANNING:
self._transition_to(ExecutionState.PLANNING)
# Continue loop
elif next_state == ExecutionState.RECOVERING:
await self._execute_recovery(context)
# Recovery will transition to appropriate state
# ... etc
except Exception as e:
self._handle_exception(e)
return self._build_result()For every transition in _VALID_TRANSITIONS:
- Test that transition succeeds when preconditions met
- Test that resulting state is correct
- Test that side effects occur
For representative invalid transitions:
COMPLETED -> PLANNINGFAILED -> ACTINGEVALUATING -> PLANNING(direct - should go through ITERATION_COMPLETE)INITIALIZED -> COMPLETED
For each enable_*=False:
- Observer disabled: Verify
ACTING -> EVALUATINGpath - Evaluator disabled: Verify
OBSERVING -> ITERATION_COMPLETEpath - Verifier disabled: Verify
EVALUATING -> ITERATION_COMPLETEpath
- Two consecutive iterations complete successfully
- Three consecutive iterations complete successfully
- Iteration boundary correctly counts iterations
- Iteration boundary respects budget
- Iteration boundary detects completion
-
types.py:
- Add
ITERATION_COMPLETEtoExecutionState - Verify all states from section 2 are present
- Add
-
core.py:
- Update
_VALID_TRANSITIONSwith complete table - Add
_handle_iteration_complete()method - Modify
run()to use iteration boundary logic - Add bypass logic for disabled phases
- Update
_transition_to()for terminal state check
- Update
-
tests/:
- Add
test_state_transitions.pywith all valid transitions - Add
test_invalid_transitions.pywith invalid cases - Add
test_iteration_boundary.pywith boundary tests - Add
test_disabled_phases.pywith bypass tests
- Add
Current problematic transitions:
EVALUATING -> PLANNING(invalid, causes crash)
New correct path:
EVALUATING -> ITERATION_COMPLETE -> PLANNING
Migration:
- Replace direct iteration loop with boundary-based loop
- Insert
ITERATION_COMPLETEtransition at end of_execute_iteration() - Add boundary decision logic
- Update transition table
The state machine implementation is complete when:
- All states from section 2 are defined
- All transitions from section 4 are implemented
- Terminal states are immutable
- Invalid transitions raise
RuntimeError - Two consecutive iterations work
- Three consecutive iterations work
- Disabled phases have valid bypass paths
- Iteration count is accurate
- Boundary decision logic respects budget
- All tests pass