NEXO 7.6.0 — enforcement parity: Brain catches up, after_tool goes per-instance, defaults hardened
Published 2026-04-22. Minor release over v7.5.0.
v7.5.0 handed Brain the canonical authority for session-end. v7.6.0 follows up on the surface the operator audit flagged next: the enforcement engines themselves were not actually honouring every rule type the map declared. This release closes that drift.
The drift before v7.6
tool-enforcement-map.json v2.1 declared eight rule types. Only five of them were dispatched by src/enforcement_engine.py::_build_indexes:
- Dispatched by Brain:
on_session_start,on_session_end,periodic_by_messages,periodic_by_time,after_tool. - Declared in the map but silently ignored by Brain:
before_tool,on_event,conditional.
Desktop (nexo-desktop/enforcement-engine.js) did dispatch before_tool and on_event — so the split engines disagreed about what the contract guaranteed. Nobody dispatched conditional. A Brain user reading the map saw obligations the runtime never checked.
What v7.6 changes
- Dispatch parity. Brain's
_build_indexesnow has branches forbefore_tool,on_event, andconditional. Desktop gainsconditionalsupport. Both engines now cover all eight types declared byfase2_schema.supported_rule_types_v2_0. - New public API on Brain. Three new methods:
on_tool_call_before(raw_name, tool_input)— firesbefore_toolrules BEFORE the destructive tool lands. Skips thenexo_guard_check → Edit/Writecase because R13 already covers that path in richer context.raise_event(event_name, context=None)— hooks / the engine call this when a semantic event fires. Supported events in v7.6:pre_compaction,post_compaction,factual_answer_with_high_stakes,user_correction_without_learning,multi_step_task_detected,done_claimed_with_open_task.reset_task_cycle(tool)— rearms conditional counters after a matching close tool. Without this, the firsttask_openof a session satisfied the rule forever — the exact "satisfied-by-once" defect the checklist flagged.
- after_tool satisfaction is per-instance, not per-session. Old code:
if target not in self.tools_called:. Once the target was called a single time, every subsequent trigger was silently satisfied forever. v7.6 compares a monotonic per-instance counter (target_last_instance > trigger_instance). Desktop mirrors the fix (toolLastInstance/toolInstanceCounterin session state). - Map v2.2 tightenings.
nexo_learning_addon_event rule:grace_messages: 3 → 0. Corrections now produce a learning in the same turn, not three messages later.nexo_task_openconditional rule:threshold: 10 → 4, levelshould → must, plus an explicitinject_promptso the dispatcher has concrete text to emit._check_conditionalhalves the threshold when the recent window showsEdit/Write/Tasksignals, so edit-heavy flows surface the obligation even earlier.
- guardian_default.json v1.4.0. Five rule-mode bumps where false-positive telemetry was low:
R15_project_context: soft → hardR17_promise_debt: soft → hardR22_personal_script: soft → hardR_CATALOG_before_artifact_create: soft → hardR34_identity_coherence: shadow → soft (identity denials without shared-brain lookup now surface a visible reminder instead of silent telemetry).
enforcement-engine.jstop-of-file) and the corresponding test (tests/guardian-runtime-overrides.test.js) are updated in lock-step.
New contract parity test
tests/test_v76_map_parity.py pins six invariants:
- Every rule type declared in the map is dispatched by both engines.
fase2_schema.supported_rule_types_v2_0cannot lie about engine coverage.task_openconditional threshold is ≤ 5 and carries aninject_prompt.learning_addgrace_messagesis 0.- Brain engine exposes
raise_event,reset_task_cycle, andon_tool_call_before. after_tooldispatch usestarget_last < current_instanceand maintains_tool_instance_counter.
Tests
Brain: pytest 2056 passing. Ten unrelated pre-existing failures in test_cli_scripts, test_client_sync, test_doctor, test_evolution persist (TTY / client-selection edge cases, confirmed against clean main and tracked separately). Desktop: 673/673 tests passing after adjusting the defaults parity assertion for R_CATALOG + R34.
Honest delta vs the checklist
The constructor-guardian-90 checklist asks for ~50 individual hardenings. v7.6 lands the nuclear ones — the drifts the map itself exposed, the per-instance satisfaction bug, the five default-mode bumps, and the new contract test. Remaining items (richer e2e coverage per critical rail, ambient detector for multi_step_task_detected, and a stronger task_close trigger vocabulary) will follow in subsequent point releases. The release notes for those will stay proportional — what ships is what ships.
Related: full v7.6.0 changelog · v7.5.0 release notes · source on GitHub