Repository navigation
feat(improve): healOnce, one full self-healing cycle - #994
Merged
Merged
Conversation
Wires detect, capture, propose, gate, canary, apply or queue, corpus guard, reflection and audit into one function the CLI, the watcher and tests share. lousho improve now runs one cycle on an agent directory; --dry-run keeps the old print-only behaviour, and modules or non-instructions.md agents run dry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Guard rollbacks go to the cycle's own parent; a guard that throws or can't judge undoes the apply; an apply interrupted before its guard is guarded at the next cycle; tokens are checkpointed per step and the guard is costed before the write; the kill switch is rechecked before the editors and the canary; a switch to manual mid-cycle queues instead of applying; stale is not reflected on; inputs reach runCase as captured; the CLI stops on Ctrl-C between steps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LinuxDevil
force-pushed
the
s23-heal-once
branch
from
November 8, 2026 22:43
d0c3018 to
395a56e
Compare
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.
Why
S23 of the M4 plan (
m3-notes/m4/parity.md, section 2a).lousho improvestopped at a printed card. This wires every step of the self-healing loop into one function,healOnce, that the CLI, the watcher (S24) and tests share: kill switch, lock, cooldown and daily budgets, detect, capture, propose, gate, replay canary, apply (auto) or queue (manual), corpus guard, reflection and library vote, audit, and a cycle record. It also adds the per-editor-session wall timeout (Harness² row 7) and the corroboration rule (B3).R12: products W3.4 and WG.1–3 reuse this cycle through
applyProposal/rollbackHarness; nothing product-specific here.Changes
src/improve/heal.ts:healOnce(options): Promise<HealCycle>..lousho/improve.stop,LOUSHO_IMPROVE=off,enabled: false) and again just before the write, so a pause that lands mid-cycle applies nothing..lousho/cycle.locktaken withtimeoutMs: 0; a second caller getsbusy. Cooldown and the daily caps are checked inside the lock, so two callers can't both spend the last slot.cooldownHours. Budgets: cycles/day and tokens/day from today's cycle records, automatic applies/day from the audit log (gate/canary actors only). The cycle cap ismin(maxTokensPerDay − spent today, maxTokensPerCycle, maxTokens); protected-case admission and the canary run through a metered probe,improveAgentgets the remainder asmaxTokens.failuresneedsminNewFailuresfailed runs under the current revision since the last cycle;scoreandmanualskip that count.currentRevision(dir)(runsByRevision); recurring groups only whentrigger.requireRecurring(auto default) unless the trigger isscore.--protectpasses are admitted 6/6 and join the corpus (origin: 'protected'); holdout = the corpus.canaryReplayon training + corpus cases,firstRepeat: 100,runs: canary.replayRuns; auditedcanary-end. A failed canary applies nothing.budget-exceeded). ThenapplyProposal(…, { mode: 'auto', by: 'canary' }); a stale refusal (fieldbase) endsstale(not reflected on), any other refusalrefused. ThenguardCorpus(dir, { rollback: false, runs: guard.runs }), and on a regression, a guard that can't judge (inconclusive), or a guard that throws, the cycle rolls back to its own parent revision withexpectedFrom(fail closed). It does not userollback: true, becauselastGoodRevisionfalls back to the oldest baseline when noguard-passwas ever audited (an empty corpus audits none), which would throw away earlier kept edits. A refused rollback keeps the edit and says so; the monitor (S10) still checks it. The post-apply guard is not cut off by the budget, but its tokens count.improve.jsonswitches tomanualmid-cycle, the proposal is queued instead of applied.savePending(…, { maxAlwaysOnChars })→pending.reject; rejected and rolled-back cycles get one reflection; a reflection error is audited as the newreflect-failkind;voteOnOutcomeonce on a guard rollback..lousho/cycles/<id>.json; the record is written asinterruptedbefore work starts and replaced at the end.src/improve/cycles.ts(new writer of.lousho/cycles/; add it to the R10 list):HealCycle, cycle records,usageToday,cooldownEnds,pausedBy.casesFromFailurestakestraceIdsandrecurring;ImproveOptions.editorTimeoutMs(default 10 min inhealOnce) ends a session past it as anoutage; audit actionreflect-fail.lousho improve: runshealOnce(triggermanual) on an agent directory; Ctrl-C stops it between steps.--dry-runkeeps the old print-only behaviour exactly. An agent module, or an agent whose instructions are not ininstructions.md(or whose skills differ fromskills/*/SKILL.md), is forced to a dry run with a message. HELP and thedocs/cli.md/docs/improve.mdlines that said "applies nothing" are updated; the full docs are S27.R10 writers:
.lousho/cycles/bycycles.ts;.lousho/cycle.lockbyheal.ts; everything else through the existing writers.Review (independent): no blockers; the should-fix findings (guard rollback target, inconclusive guard, crash after apply, token checkpoints, recurring/HELP, approval re-read, stale reflection) and nits are fixed in the second commit.
Testing
src/improve/heal.test.ts(mock editor model, temp agent directory, real observe db with revision-stamped traces): auto applies, guards and audits; manual queues and writes nothing; a canary failure applies nothing; a guard regression rolls back in the same cycle and then the cooldown blocks; stale base refused; failures from another revision ignored; idle belowminNewFailures; recurring rule and the score trigger; stop file / env /enabled: falsepause; a pause mid-cycle stops before the write; concurrent cycle isbusy; a guard that throws or can't judge undoes the apply; a refused rollback keeps the edit; an interrupted apply is guarded at the next cycle; the kill switch flipping during admission stops before the editors; a switch to manual mid-cycle queues; a guard the budget can't pay for blocks the write; daily caps (cycles, applies, tokens);budget-exceeded; outage, no-failures, keep-base; gate rejection audited with a failed reflection audited; a re-proposed rejected proposal isrefused; an aborted cycle is leftinterrupted; nothing written outsideinstructions.md,skills/*/SKILL.mdand.lousho/. EveryHealCycle['outcome']is reached.src/improve/cycles.test.ts,cases.test.ts(traceIds/recurring),improve.test.ts(editor wall timeout).src/cli/improve.test.ts: existing print-only tests now pass--dry-run; new: auto applies, manual queues,--dry-run writes nothing, module target and model-specific instructions forced to dry run.api:update+api:check,docs:llms+docs:llms:check,docs:verify-snippets -- --skip-build,docs:ratchet(1724), vitestsrc/improve src/cli src/observe,npx fallow audit --base origin/mainafter scoped coverage: clean.🤖 Generated with Claude Code