Repository navigation
smoke: verify the governance gate actually installs on the released tree (#70) - #79
Conversation
…ree (#70) The release smoke proved the plugin registers and upgrades, but said nothing about the control that makes socxen safe to point at real alerts. Now that install.sh can install the gate rather than only warn about it, a release can regress the assisted merge and every existing leg would still report PASS. Leg 3 runs the shipped tree's plugin/install.sh --merge-permissions, then asserts the dismiss/close pair sits specifically in the `ask` tier — the same judgement gate_on() makes, so "settings.json mentions the tools somewhere" cannot read as installed — and that a re-run reports "already merged" rather than double-appending into the operator's file over successive releases. SOCXEN_SETTINGS_FILE is what makes the leg safe to run at all: without it install.sh writes the real ~/.claude/settings.json, and a release smoke that edits the maintainer's live governance config on every run is worse than no smoke. The containment is asserted rather than assumed — the real file's digest is compared before and after, so a PASS that quietly rewrote it fails instead. Both assertions negative-tested, not just observed passing: the gate check reports OFF when the two tools sit in `allow` instead of `ask`, and the digest guard detects a modified file. A leg that cannot fail is the #67 skip-guard bug in a new costume. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Matt W <matt.willems@exabeam.com>
virtualsteve-exa
left a comment
There was a problem hiding this comment.
APPROVE — verified hands-on, not just read.
Reproduced leg 3 against the dev tree in a scratch environment (stand-in HOME, throwaway SOCXEN_SETTINGS_FILE + CLAUDE_CONFIG_DIR):
- first run: installer exit 0, throwaway settings end at exactly
{allow: 18, ask: 4, deny: 68}, stand-in real settings byte-identical after (digest match); - re-run: exit 0 with "already merged" present in the log (three occurrences — the merger's own stdout plus
run_merge's ok line, so the grep has redundant coverage); - negative check: with the dismiss/close pair moved to
allow, the ask-tier assertion correctly reads OFF — the "mentions the tools somewhere" false-pass is genuinely excluded.
I also traced the flow you're relying on in install.sh: --merge-permissions reaches run_merge unconditionally when unblocked (the flag is the consent, even when the gate already reads ON), the noop exit 10 lands on the ok path, and fail() accumulates into the final [ "$FAIL" -gt 0 ] && exit 1 — so both exit-code assertions rest on real behavior. The layered containment is right too: if SOCXEN_SETTINGS_FILE were ignored, the write would fall to $CLAUDE_CONFIG_DIR/settings.json (throwaway) before ever reaching ~/.claude, and assertion 1 would catch it there — the digest guard is the second lock, not the only one. And leg 3 is network-free as written: empty config dir + --skip-update takes the "leaving everything as-is" path, so nothing installs.
One non-blocking suggestion. ${WT_CURRENT}/plugin/install.sh is a hardcoded path, and WT_CURRENT is origin/main@HEAD — which today is 0.6.9, root layout. Anyone running the smoke in the window before the promotion gets a generic "governance leg — installer exited non-zero" whose log tail is a file-not-found, which reads like a real regression. A fallback to root install.sh would be wrong (pre-#70 installers don't have the flag), so the version_at probe pattern doesn't transfer — but an explicit guard would:
[ -x "${WT_CURRENT}/plugin/install.sh" ] || { echo " FAIL: current release (origin/main) predates the plugin/ layout — leg 3 needs a >=0.7.0 main; run after the promotion" >&2; exit 1; }Fine to fold in here or land as-is and let the promotion moot it — the first real post-release run will have the new layout either way. Agreed on running the full smoke as part of the post-promotion release step; that run also gives legs 1–2 their first crossing of the #66 boundary fallbacks.
Per @virtualsteve-exa's review note. WT_CURRENT is origin/main@HEAD, which until the promotion is 0.6.9 in the root layout — so anyone running the smoke in this window got "installer exited non-zero" with a file-not-found log tail, which reads like a real regression rather than "not applicable yet". Deliberately NOT the version_at() probe pattern that fixed the same class of problem in the other two helpers: falling back to a root install.sh would run a PRE-#70 installer with no --merge-permissions at all, and the leg would pass having tested nothing. Refusing loudly is the honest answer for a release that cannot support this check. Verified both directions: fires with the explicit message against a fabricated root-layout worktree, and the three assertions still pass against the real tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Matt W <matt.willems@exabeam.com>
…otes Two release-record items: 1. The 2026-08-12 Praxen scan is 0.7.0's ABV gate artifact (maintainer determination). It ran against the dev tip as it stood (005fa4c) — the head of the line 0.7.0 was cut from, not a stale release tag — and every behavioural change merged since is either a remediation of one of that scan's own findings (001->#73, 007->#78, 009->#81) or the path-only plugin/ restructure. Recorded in security/praxen/README.md rather than only in the release PR, so a reader can tell which release the dated artifact verified. 2. #79 landed on dev after the version bump, so the changelog gains its entry: the release smoke now asserts the governance gate actually installs. Release machinery earns a changelog line by the 0.6.4 precedent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Steve Wilson <steve.wilson@exabeam.com>
Follow-up to #73, deliberately held until #66 landed so it wouldn't conflict with that PR's
plugin-smoke.shrewrite.Why
The release smoke proved the plugin registers and upgrades, but said nothing about the control that makes socxen safe to point at real alerts. Now that
install.shcan install the gate rather than only warn about it (#70), a release could regress the assisted merge and every existing leg would still report PASS.What leg 3 does
Runs the shipped tree's
plugin/install.sh --merge-permissions, then asserts:asktier. Same judgementgate_on()makes, so "settings.json mentions the tools somewhere" cannot pass as installed.~/.claude/settings.jsonis untouched — digest compared before and after.already mergedrather than double-appending into the operator's file over successive releases.The containment is the point
SOCXEN_SETTINGS_FILEis what makes this leg safe to run at all. Without itinstall.shwrites the real~/.claude/settings.json, and a release smoke that edits the maintainer's live governance config on every run is worse than no smoke. That's asserted, not assumed — assertion 2 exists so a PASS that quietly rewrote it fails instead.Verified
Leg 3 run in isolation against a fabricated post-promotion worktree with
HOMEpointed somewhere safe: all three assertions pass, the throwaway settings end at{allow: 18, ask: 4, deny: 68}, and the stand-in real settings file is byte-identical afterward.Both assertions negative-tested, not merely observed passing — the gate check reports OFF when the two tools sit in
allowinstead ofask, and the digest guard detects a modified file. A leg that cannot fail is #67's skip-guard bug in a new costume, and I'd rather not re-file that.Legs 1 and 2 can't run end-to-end until the promotion lands (they resolve
currentfromorigin/main, which is still pre-restructure), so this leg is verified standalone. Worth a fullscripts/release/plugin-smoke.shrun as part of the post-release smoke oncemaincarries the new layout — which is the first run that also exercises the prior-worktree fallbacks from #66.🤖 Generated with Claude Code