Skip to content

Commit a8afd9a

Browse files
committed
chore(scripts): isolate HOME and assert exact version in verify-install-simulation (#1490)
Two correctness fixes uncovered while running the script after the v5.6.2 bump: 1. **HOME isolation** — the subprocess that ran the installed HUD inherited the developer's real HOME, so `read_installed_version` tier-1 lookup picked up `~/.claude/plugins/installed_plugins.json` from the developer machine (5.6.1) instead of the version we are about to ship. Output read `CB v5.6.1` even though plugin.json was already 5.6.2. The script now passes an explicit `env=` to subprocess.run with HOME pointing at the tmpdir. 2. **Fake installed_plugins.json** — Claude Code writes `~/.claude/plugins/installed_plugins.json` after every plugin install/update, and `hud_version.get_fresh_version` reads it as tier 1. The simulation now writes a stub manifest in the isolated tmpdir so the installed HUD's version-resolution path matches what a real user will hit on first session-start after `/plugin update`. 3. **Exact version assertion** — the script now reads the version from `plugin.json`, asserts `CB v<expected_version>` appears in the rendered status line, and verifies `~/.claude/hud/.version` contains the same value. This auto-tracks bump-version.sh and removes the previous risk of shipping a release where the version segment renders empty without anyone noticing. Local verification: $ bash packages/claude-code-plugin/scripts/verify-install-simulation.sh [verify-install-simulation] stdout='◕‿◕ CB v5.6.2 | Ready 🟢 | $0.42🔥$0.21/m | 2m | 0% | Opus 4.6' [verify-install-simulation] PASS: full status line rendered (v5.6.2) exit=0 Refs #1490
1 parent 2f85d9a commit a8afd9a

2 files changed

Lines changed: 61 additions & 3 deletions

File tree

packages/claude-code-plugin/namespace-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@
3434
"namespaced": "codingbuddy:plan"
3535
}
3636
],
37-
"generatedAt": "2026-04-11T16:03:33.849Z"
37+
"generatedAt": "2026-04-11T17:16:02.224Z"
3838
}

packages/claude-code-plugin/scripts/verify-install-simulation.sh

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,32 @@ settings_file = home / ".claude" / "settings.json"
6565
settings_file.parent.mkdir(parents=True, exist_ok=True)
6666
settings_file.write_text("{}")
6767
68+
plugin_root_path = Path(plugin_root)
69+
70+
# Mimic Claude Code's plugin manifest so the installed HUD's tier-1
71+
# version lookup (read_installed_version → ~/.claude/plugins/installed_plugins.json)
72+
# resolves to the version we are about to ship. Without this the
73+
# isolated tmpdir has no installed_plugins.json and the version
74+
# segment renders empty.
75+
plugin_json = plugin_root_path / ".claude-plugin" / "plugin.json"
76+
import json as _json
77+
expected_version = _json.loads(plugin_json.read_text())["version"]
78+
plugins_dir = home / ".claude" / "plugins"
79+
plugins_dir.mkdir(parents=True, exist_ok=True)
80+
(plugins_dir / "installed_plugins.json").write_text(
81+
_json.dumps({
82+
"plugins": {
83+
"codingbuddy@jeremydev87": [
84+
{
85+
"scope": "user",
86+
"installPath": str(plugin_root_path),
87+
"version": expected_version,
88+
}
89+
]
90+
}
91+
})
92+
)
93+
6894
# Run the installer the same way session-start hook would
6995
try:
7096
session_start._install_statusline(home, settings_file)
@@ -103,12 +129,24 @@ payload = json.dumps({
103129
"model": {"display_name": "Opus 4.6"},
104130
"cost": {"total_cost_usd": 0.42, "total_duration_ms": 120000},
105131
})
132+
# Isolate HOME so the installed script reads the tmpdir installation
133+
# instead of leaking the developer's real ~/.claude/plugins/installed_plugins.json.
134+
# Without this, hud_version.get_fresh_version's tier-1 lookup picks up
135+
# the real machine's plugin version and the assertion below cannot
136+
# verify that the v5.6.2 prep actually flows through the install path.
137+
isolated_env = {
138+
"HOME": str(home),
139+
"PATH": os.environ.get("PATH", ""),
140+
"LANG": os.environ.get("LANG", ""),
141+
"LC_ALL": os.environ.get("LC_ALL", ""),
142+
}
106143
result = subprocess.run(
107144
["python3", str(installed_script)],
108145
input=payload,
109146
capture_output=True,
110147
text=True,
111148
timeout=10,
149+
env=isolated_env,
112150
)
113151
out = result.stdout
114152
print(f"[verify-install-simulation] stdout={out!r}")
@@ -129,7 +167,13 @@ if out.strip() == "\u25d5\u203f\u25d5 CodingBuddy":
129167
)
130168
sys.exit(1)
131169
132-
required_tokens = ["CB v", "Opus 4.6", "$0.42"]
170+
# expected_version was resolved earlier from plugin.json so the
171+
# assertion auto-tracks bump-version.sh.
172+
required_tokens = [
173+
f"CB v{expected_version}", # version exactly matches plugin.json
174+
"Opus 4.6",
175+
"$0.42",
176+
]
133177
for token in required_tokens:
134178
if token not in out:
135179
print(
@@ -138,5 +182,19 @@ for token in required_tokens:
138182
)
139183
sys.exit(1)
140184
141-
print("[verify-install-simulation] PASS: full status line rendered")
185+
# Verify version stamp file
186+
stamp = home / ".claude" / "hud" / ".version"
187+
if not stamp.exists():
188+
print("[verify-install-simulation] FAIL: .version stamp not written", file=sys.stderr)
189+
sys.exit(1)
190+
stamp_value = stamp.read_text(encoding="utf-8").strip()
191+
if stamp_value != expected_version:
192+
print(
193+
f"[verify-install-simulation] FAIL: stamp {stamp_value!r} != "
194+
f"plugin.json version {expected_version!r}",
195+
file=sys.stderr,
196+
)
197+
sys.exit(1)
198+
199+
print(f"[verify-install-simulation] PASS: full status line rendered (v{expected_version})")
142200
PYEOF

0 commit comments

Comments
 (0)