feat: confidence-gated Accept/Refine/Restart at the local_triage boundary (Closes #1339) - #1365
Merged
Merged
Conversation
…dary (#1339) Slice B of the AREX loop (#1330). Slice A (#1338) gave stages a uniform StageResult; this is the gate that acts on the confidence it carries. evolution/lib/stage_gate.py — decide() returns a GateDecision with one of: - Accept (confidence >= tau): proceed as normal - Refine (below tau, trajectory recoverable): carry the evidence forward and re-investigate only the gaps - Restart (below tau, nothing salvageable): discard and reinitialize tau defaults to 70, deliberately conservative per the issue's "conservative tau prevents runaway looping" criterion. This interacts on purpose with StageResult.wrap, which assigns confidence 50 when evidence exists but no self-assessment was made: such a result lands in Refine, so an un-assessed stage is never silently trusted as a confident one. The issue specifies the recoverable-vs-noisy call comes from the model running the stage. A deterministic script has no model, so decide() takes an explicit `recoverable` argument for callers that have that judgement, and falls back to a structural proxy when it is None — evidence present means there is something concrete to build on; no evidence is indistinguishable from noise. Wired at the same boundary slice A emits from: evolution_local_triage.py runs the tuple through the gate, records the branch in the analysis JSON as `stage_gate`, and prints the decision line. Advisory there rather than aborting, because local triage is a read-only pre-pass whose output the analysis stage consumes — the branch is surfaced for the next stage to act on. Verified not dead code: three call sites in evolution_local_triage.py outside the module and its tests. Deferred to slice C (#1340) as the issue specifies: stage_refine_rate / stage_restart_rate metrics and multi-boundary rollout. 50 passing across test_stage_gate.py, test_stage_result.py, test_evolution_analysis_gate.py and test_evolution_local_triage.py, with and without PYTHONPATH; ruff clean. Closes #1339
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.
Closes #1339 — slice B of the AREX loop (#1330), building directly on the
StageResultcontract merged for slice A (#1338).The gate
evolution/lib/stage_gate.py—decide()returns aGateDecision:confidence >= tautaudefaults to 70 — conservative, per the issue's "conservative τ prevents runaway looping" criterion.There is a deliberate interaction with
StageResult.wrap: it assigns confidence 50 when evidence exists but no self-assessment was made. That lands in Refine, not Accept — so a stage that never assessed itself is never silently trusted.On the recoverable-vs-noisy judgement
The issue specifies this call is "produced by the model running the stage". A deterministic script has no model, so
decide()takes an explicitrecoverableargument for callers that do have that judgement, and falls back to a structural proxy when it isNone: evidence present means something concrete to build on; no evidence is indistinguishable from noise. Both overrides are tested.Wiring — not dead code
Wired at the same boundary slice A emits from.
evolution_local_triage.pyruns the tuple through the gate, records the branch in the analysis JSON asstage_gate, and prints the decision line:Advisory at this boundary rather than aborting, because local triage is a read-only pre-pass whose output the analysis stage consumes — the branch is surfaced for the next stage to act on. The deterministic dead-code check finds three call sites in
evolution_local_triage.pyoutside the module and its tests.Success criteria
Deferred to slice C (#1340), as the issue specifies:
stage_refine_rate/stage_restart_ratemetrics and multi-boundary rollout.50 passing across
test_stage_gate.py,test_stage_result.py,test_evolution_analysis_gate.py,test_evolution_local_triage.py— with and withoutPYTHONPATH; ruff clean.