fix(statusline): self-heal stale version-pinned path on session start - #49
Merged
Conversation
wire-statusline.py writes an absolute, version-pinned statusline path into ~/.claude/settings.json, but nothing re-ran it after a plugin update — so updated installs kept invoking the old version's statusline.py: stale, and eventually a blank bar once that version's cache dir is pruned. SessionStart now refreshes a codeArbiter-owned pin to the current renderer path, persisting only on a real change (no steady-state churn), leaving a third-party line untouched, never wiring a fresh line where none exists, and degrading silently on any failure (corrupt settings.json included) so a wiring refresh can never crash startup. Adds a `refresh` action to wire-statusline.py and heal_statusline_wiring() in session-start.py, both regression-tested. Version bumped 2.1.0-beta.4 -> beta.5 (payload change on an already-published tag, per the CI version gate). CHANGELOG: the statusline now updates itself when the plugin updates, instead of silently running the old version until it eventually breaks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the coverage gap the coverage-auditor flagged on the self-heal fix: the actual fix point — heal_statusline_wiring(plugin) called from main() before the dormant gate — had no test, so every test would have passed even with that call deleted. Adds a main()-level regression test (dormant repo + fake HOME) that fails if the heal call is removed (mutation-verified), plus branch coverage for the missing-renderer guard, a bare-string statusLine, and the loader-failure short-circuit. Test-only; rides the unpublished 2.1.0-beta.5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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 & why
wire-statusline.pywrites an absolute, version-pinned path into~/.claude/settings.json(a plugin can't own astatusLine, and${CLAUDE_PLUGIN_ROOT}isn't expanded there). Nothing ever re-ran it after a plugin update — so an updated install kept invoking the old version'sstatusline.py: stale code, and eventually a blank bar once that version's cache dir is pruned. Observed live: an install pinned at2.0.1survived three version bumps.Change
wire-statusline.py— newrefreshaction +refresh_if_stale(). Narrow by design (notinstall): heals only a ca-owned, changed pin, persists only on a real change (no session-start churn), never touches a third-partystatusLine, never wires a fresh line where the user has none, and no-ops if the renderer is missing mid-update.session-start.py—heal_statusline_wiring()called on every SessionStart, before the dormant gate (the statusline is wired globally, so it must heal in every repo, not just arbiter-enabled ones). Fully guarded — corrupt/absentsettings.jsondegrades to a no-op; a wiring refresh can never crash startup.2.1.0-beta.4 → 2.1.0-beta.5(payload change on a published tag — CIversion-bumpgate).Test plan
python -m unittest discover -s tests(fromplugins/ca/hooks) — 368 pass..github/scripts/test_hook_guards.py(62/0),test_hooks_cold_install.py(131/0),check-plugin-refs.py(graph intact).main()-level test that is mutation-verified to fail if the heal call is removed.Tradeoff (conflict hierarchy)
Heal runs on every SessionStart, including non-arbiter repos — a correctness-over-performance call (L2 over L4): the cost is one
settings.jsonread per session (write only on change), in exchange for the pin self-healing everywhere the global statusline actually runs.No ADR implemented or contradicted.