NEXO 7.3.0 — Guardian wire hotfix: PreToolUse properly registered, post-sync runs the fresh tree, tool-enforcement-map.json now shipped via npm
Published 2026-04-22. Hotfix minor release over v7.2.0.
v7.3.0 is a same-day follow-up to v7.2.0 correcting three critical bugs that surfaced right after the Guardian-active-by-default train went live. Two of them (B11 Guardian wire and B10 post-sync ordering) were discovered while verifying the v7.2.0 rollout on Francisco's machine and would have silently disabled every Guardian gate on every fresh install. The third (B12) is a distribution gap that had existed quietly since tool-enforcement-map.json was born: nothing in the npm bundle, nothing in the Desktop DMG, nothing in ~/.nexo was shipping it, so Desktop could never discover it on a new machine. All three fixes ship here, together with the PE1 rapid items that were planned for this milestone.
B11 — PreToolUse hook actually wired
- New entrypoint:
src/hooks/pre_tool_use.pyparses the Claude Code stdin payload, delegates tohook_guardrails.process_pre_tool_event, and emitspermissionDecision: denyJSON when a rule is in hard mode. Before this commit the Guardian gate code existed but nothing in the hook wiring reached it, so every gate listed inguardian-runtime-overrides.jsonwas silently inert — hard mode looked enforceable but was a no-op. - Registered in both manifests:
src/hooks/manifest.json(NEXO Brain native) andhooks/hooks.json(Claude Code plugin mode) now listPreToolUsewithtimeout: 8.client_sync.HOOK_TIMEOUTS_BY_EVENTcarries the same value so the two paths agree. - SSH prescreen:
_classify_bash_operationreturns"other"for commands likessh host "cat > /etc/hosts"because remote mutation is invisible at the local shell classifier level. The new prescreen runs_classify_ssh_remote_writebefore the early return so G3-SSH in hard mode can actually block those commands. - Coverage: 8 integration tests in
tests/test_pre_tool_use_hook.pypin manifest registration,Readtool passes clean,rm -rfin hard mode emits the deny JSON, shadow does not deny, off is untouched,ssh host "cat > ..."in hard mode emits deny, malformed stdin fails open, and empty stdin passes clean.
B10 — post-sync hooks run from the freshly-copied tree
The first time nexo update introduces a new post-install hook, the hook dispatch runs inside the pre-upgrade module that is already loaded in memory. That module does not yet know about the new hook. Result: the hook silently no-op's on the upgrade that was supposed to introduce it. This bit us on v7.2.0 when _persist_guardian_hard_defaults and _maybe_promote_adaptive_weights_empirically failed to write guardian-runtime-overrides.json on the first nexo update that brought them into existence.
v7.3.0 introduces a whitelist _POST_INSTALL_FRESH_HOOKS and a helper _run_post_install_hooks_fresh(dest, env) that invokes each whitelisted hook in a clean subprocess with sys.path pointed at the newly copied tree (dest/core when packaged, dest when running from the repo). Each hook emits its verdict as JSON; the parent parses per-hook results and logs outcomes individually. 5 integration tests in tests/test_post_install_hooks_fresh.py cover the happy path (marker files written), missing function graceful skip, no-core fallback, broken import error surfacing, and per-hook exception isolation.
B12 — tool-enforcement-map.json is shipped via npm
Before v7.3.0, the Brain package.json whitelist did not include tool-enforcement-map.json, so npm publish never shipped it. Desktop's findEnforcementMap had a candidate pointing at /opt/homebrew/lib/node_modules/nexo-brain/ but the file was never there. Combined with the 23a4529 Desktop commit that removed the ~/Documents/_PhpstormProjects/nexo/ dev fallback, fresh installs silently loaded a null map and the session-end diary+stop policy never fired on close.
Brain v7.3.0 adds tool-enforcement-map.json to the files whitelist so every npm install -g nexo-brain gets the map. Desktop v0.23.0 (shipping alongside) copies the same file into the DMG's brain-bundle via extraResources, preflights it into ~/.nexo/core/ at app startup, and surfaces a visible warning if the map cannot be located after all candidates are exhausted — no more silent degradation.
PE1 rapid items — 0.4 preset entities + 0.25 guardian-metrics cron
- 0.4: five new
destructive_commandentries insrc/presets/entities_universal.json(curl_pipe_shell,dd_to_device,chmod_recursive_wide_open,ssh_remote_overwrite,scp_rsync_upload). Coverage floor raised from 7 to 12 entries. 8 contract tests intests/test_entities_universal_preset.pypin the JSON shape, regex compilation, the v7.3.0 coverage floor, and specific matches forssh_remote_overwrite(positivesssh host "cat > /etc/hosts", negativesscp /local host:/remote) andcurl_pipe_shell(matches bothcurl | bashandwget -qO- | sh). - 0.25:
guardian-metricscron wired insrc/crons/manifest.json. Runsscripts/guardian_metrics_aggregate.pydaily at 02:15 to aggregate capture rate, core-rule violations, declared-done without evidence, false-positive correction, and minutes betweenguard_checkfailures fromguardian-telemetry.ndjsonintoguardian-metrics.ndjson. Feeds the Fase C gate and the Guardian Proposals panel.
Desktop v0.23.0 — renderer-side of B12 + the Parar/queue recovery loop
Desktop v0.23.0 ships in lockstep. It picks up the server side of B12 (discovery order, bootstrap preflight, visible warning) plus two UX bugs Francisco hit on v0.22.10: B13 (clicking “Send queue” after a Stop did nothing when turnBusy stayed pinned because the previous turn's result/close events never reached the renderer) and B14 (no synthetic claude-exit floor, so a wedged Claude process froze the UI until force-quit). Both are now covered by unit tests and shape-match contracts so a future refactor cannot silently regress them.
What to do after updating
nexo update picks up the fix automatically. On the first update to v7.3.0 the post-install hooks will write guardian-runtime-overrides.json with G3-destructive, G3-SSH, and G4-guard_check in hard. Verify with cat ~/.nexo/personal/config/guardian-runtime-overrides.json after the update. Guardian is now actually enforcing. Installing Desktop v0.23.0 (separate DMG) adds the renderer-side map discovery and the “Send queue” recovery.
Related: full v7.3.0 changelog · v7.2.0 release notes · source on GitHub