Component: ralph loop, C3 stall detection
Symptom
C3 fires only when two consecutive iterations record the same non-GREEN fingerprint.
A loop whose only remaining work is operator-only — a manual click-through, an interactive auth, a physical device step — writes GREEN every iteration, because every automated check genuinely passes. The detector never sees a stall. The loop burns every remaining iteration and then stops on the max_iterations backstop.
Impact
max_iterations becomes the stop condition. The contract itself says that is a bug rather than a converged run:
--max-iterations remains the backstop. C3 should fire first; if the counter is what stops the loop, that is a bug in the fingerprint, not a converged run.
The operator gets a run that looks exhausted rather than one that says what it is waiting for. The information they actually need — which human action unblocks this — is exactly what the loop cannot express.
Suggested fix
Add a third terminal verdict alongside green and stalled: blocked-on-human. An iteration declares it when its remaining work is outside the agent's reach. It halts immediately and names the human action.
This also gives the ledger a fingerprint value that is honest about the state, rather than GREEN standing in for "everything I am allowed to touch passes".
Provenance
Two plans in our project ended in exactly this state (operator-pending VSIX click gates). Both wrote GREEN on every pass while being genuinely unable to progress.
Component: ralph loop, C3 stall detection
Symptom
C3 fires only when two consecutive iterations record the same non-
GREENfingerprint.A loop whose only remaining work is operator-only — a manual click-through, an interactive auth, a physical device step — writes
GREENevery iteration, because every automated check genuinely passes. The detector never sees a stall. The loop burns every remaining iteration and then stops on themax_iterationsbackstop.Impact
max_iterationsbecomes the stop condition. The contract itself says that is a bug rather than a converged run:The operator gets a run that looks exhausted rather than one that says what it is waiting for. The information they actually need — which human action unblocks this — is exactly what the loop cannot express.
Suggested fix
Add a third terminal verdict alongside green and stalled: blocked-on-human. An iteration declares it when its remaining work is outside the agent's reach. It halts immediately and names the human action.
This also gives the ledger a fingerprint value that is honest about the state, rather than
GREENstanding in for "everything I am allowed to touch passes".Provenance
Two plans in our project ended in exactly this state (operator-pending VSIX click gates). Both wrote
GREENon every pass while being genuinely unable to progress.