fix(#77): deterministic near-green crossing — rotating-error detector + completion-only steer#170
Closed
agjs wants to merge 2 commits into
Closed
fix(#77): deterministic near-green crossing — rotating-error detector + completion-only steer#170agjs wants to merge 2 commits into
agjs wants to merge 2 commits into
Conversation
#77) At near-green a build can sit at a stable low count while the error SET rotates — each single-error fix spawns a different one (extract a component → its siblings/tests are now missing → revert → rotate) — so it never reaches 0. Count-only WS-B can't see it. build17 parked on this; build16 crossed only by luck (4/4 harness-diagnose: the count stays at best-ever while the fingerprint rotates). Detector (near-green-checkpoint.ts): errorSetSignature — sorted unique per-error tokens (rule|file family when present, else the key; stack-agnostic, line- independent). isNearGreenRotation requires BOTH a count PLATEAU (the window sits at one count — a moving count like 2→1→1 is progress, not rotation) AND a changing signature (not all-same, which is a stuck single). trackNearGreenRotation (turn.ts) records {count, sig} on near-green cycles, tolerates a bounded run of spikes between them (MAX_NEAR_GREEN_SPIKE_GAP — past it the stale window clears so far-apart episodes can't combine), does NOT record completion-phase churn, and clears on green. On rotation, injectFeedback prepends a GENERIC completion-only steer that cleanly separates the two poles (do NOT open new surface — no new components/ splits/features; but DO create the siblings + colocated test a current error requires, atomically, even though they aren't in the error list yet). Gated on FOUR conditions: flag on (authoritative at emit) AND detector fired AND count ≤N (not a spike) AND not completion phase. The near-green banner drops ONLY its 'do NOT create new files' lockdown clause when the steer fires (it would contradict the steer) but KEEPS the regression callout. Per-drive state cleared at both drive boundaries; a mid-run kill-switch flip clears the sticky flag. Flag nearGreenRotation() default ON, kill TSFORGE_NO_NEAR_GREEN_ROTATION. Generic — no stack strings. Tests cover the detector (plateau/stuck/descent/key-fallback), the tracker (rotation, spike- ignore, spike-gap-clear, completion-skip, green-clear, mid-run-kill), the flag, the drive-boundary clear, and the user-visible injection (prepend, banner lockdown-drop + regression-keep, flag-off/spike/completion/no-detector suppression).
…anel round-2) Panel major finding: trackNearGreenRotation recorded every gate result and judged rotation purely from gate OUTPUT (error-set signature), with no independent signal that the scope files actually changed between cycles. A flaky/stateful gate — or a re-evaluation of the same unedited files (the check-tool + settleGate double-run, #59) — could emit A->B->C signatures with no fix-one->spawn-another cycle and falsely stand the WS-B rollback net down. - Stamp each near-green sample with an INDEPENDENT worktree rev (a content hash over the glob-resolved scope files via the same snapshot substrate WS-B rolls back with, so scope can't drift). isNearGreenRotation now requires BOTH the signature AND the rev to change on every cycle -> a rotating signature over UNCHANGED files reads as not-rotating and leaves the rollback net engaged. rev is computed only on near-green cycles (the only branch that consumes it). - Extract the rotation-emit decision into rotationEmit() so injectFeedback stays under the cognitive-complexity ceiling (minor finding). - Fix stale near-green-banner.test.ts comment referencing a removed param (minor finding). - Tests: track() helper stamps a fresh rev per cycle (models a real edit); new constant-rev cases assert a flaky/re-run gate is NOT rotation; integration test now writes a genuine per-cycle edit.
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.
What
Closes the #77 near-green oscillation — the confirmed remaining barrier to deterministic green. A live 4-slice CRM build (build22, on current main with the acceptance gate + jsx-no-bind guide + nav-scope) parked all 4 slices: each REACHED green transiently, then sprayed back up (Company trace: 1↔red(40)↔GREEN↔red(56)…) and exhausted the escalation ladder without ever locking green. The per-idiom walls are down; this rotating-error tail is what's left.
How
At near green the model can clear its single remaining error, but the fix spawns a new one (extract a component → its required sibling set + colocated tests are now missing → fixing those creates more…). The error count stays low while the error set rotates, so the build never reaches 0. Count-only WS-B can't see it.
near-green-checkpoint.ts):errorSetSignature+isNearGreenRotationover a trailing window — fires only on a count and phase plateau with a per-cycle-changing signature.turn.ts): while rotation is active the model is steered to complete atomically, and the count-spray rollback stands down so the steered completion spike isn't reverted.TSFORGE_NO_NEAR_GREEN_ROTATION(default on).Genuine per-cycle rotation (panel round-2, the historical blocker)
The soundness concern that shelved this work before: the detector judged rotation purely from gate output, so a flaky/stateful gate — or the check-tool + settleGate double-run (#59) re-evaluating unchanged files — could emit A→B→C signatures with no real fix-one→spawn-another cycle and falsely stand the rollback net down.
Fixed at the root: each near-green sample now carries an independent worktree revision — a content hash over the glob-resolved scope files, via the same snapshot substrate WS-B reverts with (so scope can't drift).
isNearGreenRotationrequires both the signature and the rev to change every cycle. A rotating signature over unchanged files reads as not-rotating and leaves the rollback net engaged. The rev is computed only on near-green cycles, so the hot path is untouched on red/green.Review
major(above); round-2 returned PASS with zero findings. Also addressed:rotationEmit()extracted soinjectFeedbackstays under the cognitive-complexity ceiling; stale test comment removed.Every core-loop change is flag-gated and the other loop paths are pinned unchanged.