Repository navigation
fix: ruvnet-brain heldRefresh can never self-clear, permanently blocking ak sync retries #271
Description
Summary
ak sync's ruvnet-brain refresh is gated by activeHeldRefresh() (src/lib/ruvnet-brain.mjs:245-248), which skips the refresh whenever the recorded (installed, latest) pair in kit.json's versionCheck.ruvnetBrain.heldRefresh still matches. That pair can only change when recordInstalledRelease() (line 215) runs after a successful install — but a successful install is exactly what the hold is preventing. The hold has no TTL, no manual-clear CLI flag, and nothing re-derives whether the condition that caused it (an oversized kb.bak-*/kb.install-preserved-* snapshot, per BRAIN_RECLAIM_STUCK, heal.mjs:339) is still true. Once tripped, ak sync reports the same refusal forever, even after the user manually fixes the underlying cause.
Observed live 2026-09-28/29: kit.json held {"detail":"[forge-update] ERROR: unresolved rollback state exists; refusing to create another full-KB copy.","installed":"4.3.34","latest":"4.3.35"}. The user had already run npx ruvnet-brain --uninstall (which left the KB entirely absent — npx ruvnet-brain --doctor reported "brain not found here") and re-run ak sync, but sync kept reporting the same v4.3.34 state because installedReleaseOnDisk() returned null (no kb/SOURCE.json to read) and fell back to the stale cur.installedRelease in kit.json — so the (installed, latest) pair never moved and the hold never lifted. Only manually deleting the heldRefresh object from kit.json let the next ak sync actually attempt (and this time succeed at) the install.
Fix shape
activeHeldRefresh()should expire a hold after a bounded TTL (e.g. 24h, matchingversionCheck.ttlHours) so sync periodically re-attempts instead of holding forever on stale disk state.- Give
ak synca way to force one retry regardless of a held pair (e.g.ak sync --retry-brain), for the case where the user has just fixed the underlying cause (freed disk, cleared the legacy snapshot) and doesn't want to wait for a TTL or hand-editkit.json. installRuvnetBrain()(heal.mjs:276) currently treats "plugin cache present, KB absent" the same as a normal reinstall (reinstall = present(),heal.mjs:300) and still routes through the same refusal-prone path. When the KB directory itself is missing, sync could report a clearer status than an inherited stale-version hold.
Also relevant: the comment at heal.mjs:330-336 claims uninstalling kb/ and reinstalling takes ruvnet-brain's obtainBundle() path, which isn't blocked by the legacy-snapshot reclaim check — that appears to be stale for the 4.3.x line. The refusal reproduced above happened via host-update.mjs/update-apply.mjs's own snapshot-retention gate (updateMayProceed: false when legacy snapshot bytes exceed a fixed budget), which fired even with no kb/ present, before the oversized snapshot was manually removed. Worth re-verifying against a current release rather than assuming the fresh-install bypass still holds.
Workaround
Delete the oversized ~/.cache/ruvnet-brain/kb.bak-* / kb.install-preserved-* snapshot(s), then remove the heldRefresh key from kit.json's versionCheck.ruvnetBrain, then re-run ak sync.
🤖 Generated with Claude Code
Activity
docs(plans): triage the completion program into the v4 program
docs(plans): v4 GA master plan, supersession ledger and v5 registers (#…
pacphi commentedon Oct 1, 2026
Card
A61-04: v4 GA programThis issue joined the v4.0.0 board. Its original text is unchanged.
Why
A RuvNet Brain refresh the installer refused stayed held forever (#271). The fix exists on fix/completion-m1.
In scope
Hold expiry after versionCheck.ttlHours, a missing-KB state, ak sync --retry-brain, the corrected heal.mjs comment and the README line.
Out of scope
Brain user mode on the new line (superseded in P4); re-verifying the installer escape hatch.
Acceptance criteria
Dependencies
Blocked by: None
Blocks: #320
Plan
4.0.0-alpha.61size:S· Workstream:ws:remediationA61-04in the card inventoryVerification
pnpm testnode scripts/run-tests.mjs focus tests/kit/brain-refresh-retry.test.mjs tests/kit/brain-held-refresh-recovery.test.mjsGenerated by Claude Code