nexo-brain 7.23.8 → 7.23.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "7.23.7",
3
+ "version": "7.23.11",
4
4
  "description": "Local cognitive runtime for Claude Code \u2014 persistent memory, overnight learning, doctor diagnostics, personal scripts, recovery-aware jobs, startup preflight, and optional dashboard/power helper.",
5
5
  "author": {
6
6
  "name": "NEXO Brain",
package/README.md CHANGED
@@ -18,7 +18,9 @@
18
18
 
19
19
  [Watch the overview video](https://nexo-brain.com/watch/) · [Watch on YouTube](https://www.youtube.com/watch?v=i2lkGhKyVqI) · [Open the infographic](https://nexo-brain.com/assets/nexo-brain-infographic-v5.png)
20
20
 
21
- Version `7.23.6` is the current packaged-runtime line. Patch over v7.23.5 - `nexo update` clears safe legacy `cognitive.db` shadows and keeps superseded archives under runtime backup retention.
21
+ Version `7.23.11` is the current packaged-runtime line. Patch over v7.23.10 - older installed runtimes can update safely even when `cognitive_paths.py` has not been synced yet.
22
+
23
+ Previously in `7.23.6`: patch over v7.23.5 - `nexo update` clears safe legacy `cognitive.db` shadows and keeps superseded archives under runtime backup retention.
22
24
 
23
25
  Previously in `7.23.5`: patch over v7.23.4 - `nexo update` keeps external CLI maintenance summary copy in English.
24
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "7.23.8",
3
+ "version": "7.23.11",
4
4
  "mcpName": "io.github.wazionapps/nexo",
5
5
  "description": "NEXO Brain — Shared brain for AI agents. Persistent memory, semantic RAG, natural forgetting, metacognitive guard, trust scoring, 150+ MCP tools. Works with Claude Code, Codex, Claude Desktop & any MCP client. 100% local, free.",
6
6
  "homepage": "https://nexo-brain.com",
@@ -32,7 +32,23 @@ except ModuleNotFoundError as exc:
32
32
  sys.path.insert(0, core_path)
33
33
  from product_mode import enforce_desktop_product_contract
34
34
  from runtime_home import export_resolved_nexo_home, managed_nexo_home
35
- from cognitive_paths import cleanup_legacy_cognitive_db_artifacts
35
+
36
+ try:
37
+ from cognitive_paths import cleanup_legacy_cognitive_db_artifacts
38
+ except ModuleNotFoundError as exc:
39
+ if getattr(exc, "name", "") != "cognitive_paths":
40
+ raise
41
+
42
+ # Older installed runtimes may import this newer auto_update.py before
43
+ # cognitive_paths.py has been copied into place. Keep update importable so
44
+ # the sync can finish and deliver the missing module.
45
+ def cleanup_legacy_cognitive_db_artifacts(*, dry_run: bool = False) -> dict:
46
+ return {
47
+ "ok": True,
48
+ "skipped": True,
49
+ "reason": "cognitive_paths_unavailable_until_runtime_sync",
50
+ "dry_run": dry_run,
51
+ }
36
52
 
37
53
  try:
38
54
  from tree_hygiene import is_duplicate_artifact_name
@@ -331,14 +331,14 @@ async function sendMessage() {
331
331
  const data = await res.json();
332
332
  chatHistory.push({
333
333
  role: 'nexo',
334
- content: data.answer || 'Sin respuesta.',
334
+ content: data.answer || 'No response.',
335
335
  data: data.data || null,
336
336
  query_type: data.query_type || null
337
337
  });
338
338
  }
339
339
  } catch (err) {
340
340
  hideTyping();
341
- chatHistory.push({ role: 'nexo', content: `Error de conexion: ${err.message}` });
341
+ chatHistory.push({ role: 'nexo', content: `Connection error: ${err.message}` });
342
342
  }
343
343
 
344
344
  isWaiting = false;
@@ -6,24 +6,24 @@ from doctor.models import DoctorCheck
6
6
 
7
7
  VALID_DIAGNOSTIC_PLANES = {
8
8
  "product_public": {
9
- "label": "producto público",
10
- "use": "release contracts, artefactos publicados, compare/, docs y surfaces públicas del repo",
9
+ "label": "public product",
10
+ "use": "release contracts, published artifacts, compare/, docs, and public repo surfaces",
11
11
  },
12
12
  "runtime_personal": {
13
- "label": "runtime personal",
14
- "use": "~/.nexo, scripts personales, followups, reminders y hábitos operativos del operador",
13
+ "label": "personal runtime",
14
+ "use": "~/.nexo, personal scripts, followups, reminders, and operator work habits",
15
15
  },
16
16
  "installation_live": {
17
- "label": "instalación viva",
18
- "use": "runtime instalado, hooks activos, clientes conectados, cron sync y parity de la instalación local",
17
+ "label": "live installation",
18
+ "use": "installed runtime, active hooks, connected clients, cron sync, and local installation parity",
19
19
  },
20
20
  "database_real": {
21
- "label": "BD real",
22
- "use": "SQLite/MySQL reales, filas, schema, deudas, sesiones y evidencia persistida",
21
+ "label": "real database",
22
+ "use": "real SQLite/MySQL, rows, schema, debts, sessions, and persisted evidence",
23
23
  },
24
24
  "cooperator": {
25
- "label": "co-operador",
26
- "use": "comportamiento del agente, protocolo, comunicación y decisiones del asistente",
25
+ "label": "co-operator",
26
+ "use": "agent behavior, protocol, communication, and assistant decisions",
27
27
  },
28
28
  }
29
29
 
@@ -53,17 +53,17 @@ def diagnostic_plane_preflight(plane: str = "") -> tuple[str, DoctorCheck | None
53
53
  tier="orchestrator",
54
54
  status="critical",
55
55
  severity="error",
56
- summary=f"Plano diagnóstico desconocido: {raw_plane}",
56
+ summary=f"Unknown diagnostic plane: {raw_plane}",
57
57
  evidence=[
58
- f"planes válidos: {options}",
59
- "Usa `runtime_personal` para ~/.nexo y hábitos del runtime; `installation_live` para hooks/clientes/instalación; `database_real` para filas y schema reales.",
58
+ f"valid planes: {options}",
59
+ "Use `runtime_personal` for ~/.nexo and runtime habits; `installation_live` for hooks/clients/installation; `database_real` for real rows and schema.",
60
60
  ],
61
61
  repair_plan=[
62
- "Repite `nexo_doctor` o `nexo doctor` con `plane='runtime_personal'`, `plane='installation_live'` o `plane='database_real'`.",
63
- "Si el problema pertenece a producto público o al co-operador, usa el surface correcto en vez de NEXO Doctor.",
62
+ "Run `nexo_doctor` or `nexo doctor` again with `plane='runtime_personal'`, `plane='installation_live'`, or `plane='database_real'`.",
63
+ "If the issue belongs to the public product or the co-operator, use the correct surface instead of NEXO Doctor.",
64
64
  ],
65
65
  escalation_prompt=(
66
- "El plano elegido no existe. Repite el diagnóstico con un plano válido para evitar mezclar runtime, instalación, BD real o surfaces ajenas al doctor."
66
+ "The selected plane does not exist. Repeat the diagnosis with a valid plane to avoid mixing runtime, installation, real DB, or non-doctor surfaces."
67
67
  ),
68
68
  )
69
69
 
@@ -74,17 +74,17 @@ def diagnostic_plane_preflight(plane: str = "") -> tuple[str, DoctorCheck | None
74
74
  tier="orchestrator",
75
75
  status="degraded",
76
76
  severity="warn",
77
- summary=f"NEXO Doctor no es la superficie correcta para el plano {plane_info['label']}",
77
+ summary=f"NEXO Doctor is not the correct surface for the {plane_info['label']} plane",
78
78
  evidence=[
79
79
  f"plane: {clean_plane}",
80
- f"este plano se diagnostica mejor desde: {plane_info['use']}",
80
+ f"this plane is better diagnosed from: {plane_info['use']}",
81
81
  ],
82
82
  repair_plan=[
83
- "Si quieres diagnosticar runtime/instalación/BD, vuelve a lanzar el doctor con el plano correcto.",
84
- "Si el problema es del producto público o del co-operador, usa release checks, repo checks o herramientas de protocolo/sesión en vez de Doctor.",
83
+ "If you want to diagnose runtime/installation/DB, rerun the doctor with the correct plane.",
84
+ "If the issue belongs to the public product or the co-operator, use release checks, repo checks, or protocol/session tools instead of Doctor.",
85
85
  ],
86
86
  escalation_prompt=(
87
- "El plano elegido no corresponde al runtime doctor. Cambia de plano o de herramienta antes de seguir para no mezclar diagnóstico técnico con producto o comportamiento del agente."
87
+ "The selected plane does not belong to the runtime doctor. Change the plane or tool before continuing so technical diagnosis is not mixed with product or agent behavior."
88
88
  ),
89
89
  )
90
90
 
@@ -47,8 +47,8 @@ def _api_base() -> str:
47
47
  return raw.strip().rstrip("/") or DEFAULT_API_BASE
48
48
 
49
49
 
50
- def _normalize_locale(locale: str = "es") -> str:
51
- return "en" if str(locale or "").lower().startswith("en") else "es"
50
+ def _normalize_locale(locale: str = "en") -> str:
51
+ return "es" if str(locale or "").lower().startswith("es") else "en"
52
52
 
53
53
 
54
54
  def _shared_auth_candidates() -> list[Path]:
@@ -126,12 +126,12 @@ def _decode_body(raw: bytes) -> dict[str, Any]:
126
126
  return {"raw": raw.decode("utf-8", errors="replace")[:1000]}
127
127
 
128
128
 
129
- def _request_json(method: str, path: str, *, body: dict[str, Any] | None = None, locale: str = "es") -> dict[str, Any]:
129
+ def _request_json(method: str, path: str, *, body: dict[str, Any] | None = None, locale: str = "en") -> dict[str, Any]:
130
130
  token = _read_token()
131
131
  if not token:
132
132
  return _error(
133
133
  "not_authenticated",
134
- "No hay sesión NEXO Desktop disponible para consultar fichas.",
134
+ "No NEXO Desktop session is available to fetch protocol cards.",
135
135
  )
136
136
 
137
137
  data = json.dumps(body or {}, ensure_ascii=False).encode("utf-8") if body is not None else None
@@ -169,13 +169,13 @@ def _request_json(method: str, path: str, *, body: dict[str, Any] | None = None,
169
169
  return _error("network_error", str(exc))
170
170
 
171
171
 
172
- def handle_card_catalog(locale: str = "es") -> str:
172
+ def handle_card_catalog(locale: str = "en") -> str:
173
173
  """Return the visible official protocol card catalog. Never includes protocols."""
174
174
  params = urllib.parse.urlencode({"locale": _normalize_locale(locale)})
175
175
  return _json(_request_json("GET", f"/api/cards/catalog?{params}", locale=locale))
176
176
 
177
177
 
178
- def handle_card_get(slug: str, locale: str = "es") -> str:
178
+ def handle_card_get(slug: str, locale: str = "en") -> str:
179
179
  """Fetch one official protocol card, including protocol text, by slug."""
180
180
  clean_slug = str(slug or "").strip()
181
181
  if not clean_slug:
@@ -189,7 +189,7 @@ def handle_card_match(
189
189
  query: str,
190
190
  limit: int = 5,
191
191
  include_protocol: bool = True,
192
- locale: str = "es",
192
+ locale: str = "en",
193
193
  category: str = "",
194
194
  business_type: str = "",
195
195
  ) -> str:
@@ -1,11 +1,11 @@
1
1
  """r14_correction_learning — detect user corrections and demand a learning_add.
2
2
 
3
- Fase 2 Protocol Enforcer Fase C (Capa 2) item R14. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase C (Layer 2) item R14. Plan doc 1 reads:
4
4
 
5
- SI último user msg cognitive_sentiment.is_correction = true
6
- O valence < -0.4
7
- Y en los 3 tool calls siguientes NO aparece nexo_learning_add
8
- ENTONCES inyectar obligación.
5
+ IF the last user message -> cognitive_sentiment.is_correction = true
6
+ OR valence < -0.4
7
+ AND nexo_learning_add does NOT appear in the next 3 tool calls
8
+ THEN inject the obligation.
9
9
 
10
10
  Implementation contract:
11
11
 
@@ -1,10 +1,10 @@
1
1
  """r15_project_context — demand project context before editing a project path.
2
2
 
3
- Fase 2 Protocol Enforcer Fase D item R15. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase D item R15. Plan doc 1 reads:
4
4
 
5
- SI user msg menciona entidad type=project registrada
6
- Y en sesión no hay nexo_recall(proyecto) NI lectura atlas
7
- ENTONCES inyectar obligación.
5
+ IF the user message mentions a registered entity with type=project
6
+ AND the session has no nexo_recall(project) nor atlas read
7
+ THEN inject the obligation.
8
8
 
9
9
  This module exposes pure helpers:
10
10
 
@@ -1,10 +1,10 @@
1
1
  """r16_declared_done — detect "I'm done" outputs without nexo_task_close.
2
2
 
3
- Fase 2 Protocol Enforcer Fase C (Capa 2) item R16. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase C (Layer 2) item R16. Plan doc 1 reads:
4
4
 
5
- SI classifier detecta que último output afirma tarea completada
6
- Y hay protocol task abierta
7
- ENTONCES inyectar obligación task_close con evidence.
5
+ IF the classifier detects that the last output claims task completion
6
+ AND there is an open protocol task
7
+ THEN inject the task_close-with-evidence obligation.
8
8
 
9
9
  Exposes detect_declared_done(assistant_text, classifier=None) → bool and
10
10
  the reminder prompt template. The window-and-state tracking lives in
@@ -1,10 +1,10 @@
1
1
  """r17_promise_debt — detect assistant promises that were not executed.
2
2
 
3
- Fase 2 Protocol Enforcer Fase D item R17. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase D item R17. Plan doc 1 reads:
4
4
 
5
- SI classifier detecta promesa de acción futura en output al usuario
6
- Y en los 2 turnos siguientes no hay tool calls relevantes
7
- ENTONCES flag promise_debt + recordatorio.
5
+ IF the classifier detects a future-action promise in user-facing output
6
+ AND the next 2 turns have no relevant tool calls
7
+ THEN flag promise_debt + reminder.
8
8
 
9
9
  Exposes detect_promise(text, classifier) → bool. State (promise window
10
10
  countdown) lives in the caller — mirrors the R14 / R16 pattern.
@@ -13,7 +13,7 @@ Classifier path is the same as R14 / R16:
13
13
  semantic_router decision_kind ``r17_promise_debt``. Fail-closed on any
14
14
  unavailable backend (no promise flagged rather than a false positive).
15
15
 
16
- Mirror: nexo-desktop/lib/r17-promise-debt.js (bundled with Fase D JS
16
+ Mirror: nexo-desktop/lib/r17-promise-debt.js (bundled with Phase D JS
17
17
  twins at the end of the tranche).
18
18
  """
19
19
  from __future__ import annotations
@@ -1,17 +1,17 @@
1
1
  """r18_followup_autocomplete — suggest followup_complete retroactively.
2
2
 
3
- Fase 2 Protocol Enforcer Fase D item R18. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase D item R18. Plan doc 1 reads:
4
4
 
5
- Al abrir sesión: cargar followups activos
6
- SI acción ejecutada coincide (>70%) con followup activo
7
- ENTONCES sugerir followup_complete.
5
+ On session open: load active followups
6
+ IF the executed action matches an active followup (>70%)
7
+ THEN suggest followup_complete.
8
8
 
9
9
  R18 is a SUGGESTION rule (not a block). The enforcer emits a single
10
10
  reminder per followup hit per turn, and operators / Cortex decide
11
11
  whether to actually call nexo_followup_complete.
12
12
 
13
13
  Reuses the Jaccard matcher from tools_reminders_crud.find_completable_
14
- followups (Fase B R04 helper) rather than duplicating the matching
14
+ followups (Phase B R04 helper) rather than duplicating the matching
15
15
  logic. The trigger signal is the tool_name + tool_input of the agent's
16
16
  actions — we build a one-shot "action description" string from those
17
17
  and run the R04 matcher over it.
@@ -1,10 +1,10 @@
1
1
  """r19_project_grep — require Grep before Write on project paths with require_grep flag.
2
2
 
3
- Fase 2 Protocol Enforcer Fase D item R19. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase D item R19. Plan doc 1 reads:
4
4
 
5
- SI intent Write sobre path de entidad type=project con flag require_grep
6
- Y no hubo Grep buscando símbolo/función previamente
7
- ENTONCES inyectar obligación grep.
5
+ IF the intent is Write on a type=project entity path with require_grep
6
+ AND there was no prior Grep for the symbol/function
7
+ THEN inject the grep obligation.
8
8
 
9
9
  Pure decision module — state (projects list, recent_tool_records) is
10
10
  owned by the caller (HeadlessEnforcer).
@@ -1,10 +1,10 @@
1
1
  """r20_constant_change — demand grep-all-usages before changing a constant.
2
2
 
3
- Fase 2 Protocol Enforcer Fase D item R20. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase D item R20. Plan doc 1 reads:
4
4
 
5
- SI classifier detecta que Edit modifica constante/global/config compartida
6
- Y no hubo Grep del símbolo
7
- ENTONCES inyectar obligación grep TODOS los usos.
5
+ IF the classifier detects that Edit changes a shared constant/global/config
6
+ AND there was no Grep for the symbol
7
+ THEN inject an obligation to grep ALL usages.
8
8
 
9
9
  Decision logic is split in two:
10
10
 
@@ -1,10 +1,10 @@
1
1
  """r21_legacy_path — warn when a command / edit touches a legacy path.
2
2
 
3
- Fase 2 Protocol Enforcer Fase D item R21. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase D item R21. Plan doc 1 reads:
4
4
 
5
- SI intent Bash o Edit con path contiene entrada de entity_list
5
+ IF Bash or Edit intent path contains an entity_list entry
6
6
  type=legacy_path
7
- ENTONCES inyectar recordatorio canónico.
7
+ THEN inject the canonical reminder.
8
8
 
9
9
  Pure decision module. Legacy-path entities are seeded at `nexo init`
10
10
  from src/presets/entities_universal.json (the ~/claude → ~/.nexo mapping).
@@ -1,10 +1,10 @@
1
1
  """r22_personal_script — demand context lookup before creating a personal script.
2
2
 
3
- Fase 2 Protocol Enforcer Fase D item R22. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase D item R22. Plan doc 1 reads:
4
4
 
5
- SI intent nexo_personal_script_create O Write sobre scripts/
6
- Y no hubo personal_scripts_list + skill_match + learning_search
7
- ENTONCES inyectar obligación.
5
+ IF the intent is nexo_personal_script_create OR Write under scripts/
6
+ AND there was no personal_scripts_list + skill_match + learning_search
7
+ THEN inject the obligation.
8
8
 
9
9
  R22 fires BEFORE introducing a new personal script to make sure the
10
10
  agent first checked:
@@ -1,10 +1,10 @@
1
1
  """r23_ssh_without_atlas — demand atlas lookup before SSHing to an unknown host.
2
2
 
3
- Fase 2 Protocol Enforcer Fase D item R23. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase D item R23. Plan doc 1 reads:
4
4
 
5
- SI intent Bash con ssh|scp|rsync|curl hacia host
6
- Y host no está en entity_list type=host
7
- ENTONCES inyectar obligación leer atlas.
5
+ IF Bash intent uses ssh|scp|rsync|curl toward a host
6
+ AND the host is not in entity_list type=host
7
+ THEN inject the atlas-read obligation.
8
8
 
9
9
  Reuses the host-extraction primitive from r25 (imported lazily to avoid
10
10
  a hard dependency — same mirror is provided on the JS side via the r25
@@ -1,10 +1,10 @@
1
1
  """r24_stale_memory — warn when a stale memory is cited without verification.
2
2
 
3
- Fase 2 Protocol Enforcer Fase D item R24. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase D item R24. Plan doc 1 reads:
4
4
 
5
- SI en los últimos 3 turnos se citó dato de recall/memory con age>7
6
- Y no hubo tool call de verificación fresh
7
- ENTONCES warning.
5
+ IF the last 3 turns cited recall/memory data with age>7
6
+ AND there was no fresh verification tool call
7
+ THEN warn.
8
8
 
9
9
  Implementation approach:
10
10
 
@@ -1,16 +1,16 @@
1
1
  """r25_nora_maria_read_only — block destructive SSH/scp/rsync to read-only hosts.
2
2
 
3
- Fase 2 Protocol Enforcer Fase C (Capa 2) item R25. Plan doc 1 reads:
3
+ Phase 2 Protocol Enforcer Phase C (Layer 2) item R25. Plan doc 1 reads:
4
4
 
5
- SI intent Bash "ssh" hacia host con entity.access_mode=read_only
6
- Y comando incluye verbos destructivos (rm/mv/>/>>/sed -i)
7
- Y user message no contiene permiso explícito
8
- ENTONCES BLOQUEAR + pedir confirmación.
5
+ IF Bash "ssh" intent targets a host with entity.access_mode=read_only
6
+ AND the command includes destructive verbs (rm/mv/>/>>/sed -i)
7
+ AND the user message contains no explicit permission
8
+ THEN BLOCK + ask for confirmation.
9
9
 
10
10
  The enforcer runs in observer mode (headless subprocess, Desktop
11
11
  stream wrapper), so "blocking" in practice means enqueueing a hard,
12
12
  prominent injection that names the host and the destructive verb.
13
- The MCP-side guard (Capa 1 learnings #283 / #336 / #358) and the
13
+ The MCP-side guard (Layer 1 learnings #283 / #336 / #358) and the
14
14
  system-prompt rule R32 keep the agent from actually issuing the
15
15
  command when it honours the reminder.
16
16
 
@@ -890,15 +890,28 @@ def _mcp_client_readiness(
890
890
  ) -> dict:
891
891
  generation = runtime_generation(installed_version_value, installed_fp, str(active_runtime_root()))
892
892
  process_fp = str(state.get("process_fingerprint") or "").strip()
893
+ service_fp = str(service_status.get("runtime_fingerprint") or "").strip()
894
+ effective_process_fp = process_fp or service_fp
893
895
  service_ok = bool(service_status.get("ok", True))
894
896
  fingerprint_ready = (
895
897
  bool(installed_fp)
896
- and bool(process_fp)
897
- and process_fp != "unknown"
898
- and installed_fp == process_fp
898
+ and bool(effective_process_fp)
899
+ and effective_process_fp != "unknown"
900
+ and installed_fp == effective_process_fp
901
+ )
902
+ marker_recoverable_reason = str(state.get("reason") or "") in {
903
+ "marker_required",
904
+ "process_fingerprint_missing",
905
+ }
906
+ marker_recoverable_for_client = (
907
+ bool(client)
908
+ and bool(state.get("restart_required"))
909
+ and marker_recoverable_reason
910
+ and fingerprint_ready
911
+ and service_ok
899
912
  )
900
913
  global_ready = (
901
- not bool(state.get("restart_required"))
914
+ (not bool(state.get("restart_required")) or marker_recoverable_for_client)
902
915
  and fingerprint_ready
903
916
  and service_ok
904
917
  )
@@ -906,13 +919,13 @@ def _mcp_client_readiness(
906
919
  global_reason = "runtime_service_unavailable"
907
920
  elif not installed_fp:
908
921
  global_reason = "installed_fingerprint_missing"
909
- elif not process_fp or process_fp == "unknown":
922
+ elif not effective_process_fp or effective_process_fp == "unknown":
910
923
  global_reason = "process_fingerprint_missing"
911
- elif installed_fp != process_fp:
924
+ elif installed_fp != effective_process_fp:
912
925
  global_reason = "process_fingerprint_mismatch"
913
926
  else:
914
927
  global_reason = "ready"
915
- if state.get("restart_required"):
928
+ if state.get("restart_required") and not marker_recoverable_for_client:
916
929
  return {
917
930
  "runtime_generation": generation,
918
931
  "global_ready": False,
@@ -1003,6 +1016,11 @@ def build_mcp_status(*, client: str = "") -> dict:
1003
1016
  service_status=service_status,
1004
1017
  )
1005
1018
  client_states = read_mcp_client_states()
1019
+ service_fp = str(service_status.get("runtime_fingerprint") or "").strip()
1020
+ effective_process_fp = process_fp or service_fp
1021
+ restart_required = bool(state["restart_required"]) and not bool(
1022
+ readiness["client_ready"]
1023
+ )
1006
1024
  return {
1007
1025
  "ok": True,
1008
1026
  "schema_version": MCP_STATUS_SCHEMA_VERSION,
@@ -1013,14 +1031,14 @@ def build_mcp_status(*, client: str = "") -> dict:
1013
1031
  "process_fingerprint": process_fp,
1014
1032
  "fingerprint_match": (
1015
1033
  bool(installed_fp)
1016
- and bool(process_fp)
1017
- and process_fp != "unknown"
1018
- and installed_fp == process_fp
1034
+ and bool(effective_process_fp)
1035
+ and effective_process_fp != "unknown"
1036
+ and installed_fp == effective_process_fp
1019
1037
  ),
1020
1038
  "active_runtime_root": str(active_runtime_root()),
1021
1039
  "active_runtime_version": read_version_for_path(active_runtime_root()),
1022
- "restart_required": bool(state["restart_required"]),
1023
- "reason": state["reason"],
1040
+ "restart_required": restart_required,
1041
+ "reason": state["reason"] if restart_required else "",
1024
1042
  "client_action": readiness["client_action"],
1025
1043
  "reason_code": readiness["reason_code"],
1026
1044
  "global_ready": bool(readiness["global_ready"]),
@@ -1,43 +1,43 @@
1
1
  # Run NEXO Audit Phase
2
2
 
3
- Usa esta skill cuando haya que ejecutar una fase de auditoria de NEXO y el cuello de botella sea decidir el alcance de `evolution_apply` y arrancar una tanda de items con disciplina empirica.
3
+ Use this skill when a NEXO audit phase must be run and the bottleneck is deciding the `evolution_apply` scope and starting a batch of items with empirical discipline.
4
4
 
5
- ## Pasos
6
- 1. Abre `goal + workflow + task` y fija el terreno real antes de interpretar el informe:
7
- - repo/runtime activo
8
- - DB real
9
- - mecanismo de update
10
- - tests y estado git
11
- 2. Fija la regla de autonomia antes de empezar:
12
- - Francisco no quiere checkpoints uno-a-uno para trabajo mecanico
13
- - NEXO hace branches, PRs, merge y reporta despues con evidencia
14
- - solo un blast radius arquitectonico enorme merece checkpoint
15
- 3. Trata `evolution_apply` como una decision tecnica de implementacion, no como permiso humano:
16
- - el camino de apply ya existe via `evolution_log` + `_apply_accepted_proposals`
17
- - el sandbox/snapshot/rollback protege la materializacion del cambio aceptado
18
- - no dupliques ese mecanismo en deep sleep ni en el runner de auditoria
19
- 4. Lanza la verificacion empirica de todos los items en paralelo:
20
- - `grep + read` del codigo
21
- - SQL/schema real
22
- - AST/tests/imports/logs cuando aplique
23
- - asume FP hasta que la evidencia lo contradiga
24
- 5. Clasifica cada item:
5
+ ## Steps
6
+ 1. Open `goal + workflow + task` and establish the real surface before interpreting the report:
7
+ - active repo/runtime
8
+ - real DB
9
+ - update mechanism
10
+ - tests and git status
11
+ 2. Set the autonomy rule before starting:
12
+ - Francisco does not want one-by-one checkpoints for mechanical work
13
+ - NEXO creates branches, PRs, merges, and reports afterward with evidence
14
+ - only a huge architectural blast radius deserves a checkpoint
15
+ 3. Treat `evolution_apply` as a technical implementation decision, not as human permission:
16
+ - the apply path already exists through `evolution_log` + `_apply_accepted_proposals`
17
+ - sandbox/snapshot/rollback protects materialization of the accepted change
18
+ - do not duplicate that mechanism in deep sleep or the audit runner
19
+ 4. Launch empirical verification of all items in parallel:
20
+ - `grep + read` over code
21
+ - real SQL/schema
22
+ - AST/tests/imports/logs when applicable
23
+ - assume FP until evidence contradicts it
24
+ 5. Classify each item:
25
25
  - `real_gap`
26
- - `casi_fp`
26
+ - `near_fp`
27
27
  - `fp`
28
- 6. Ordena solo los `real_gap` por riesgo/blast radius y ejecutalos con worktree aislado si tocan core.
29
- 7. Por cada `real_gap`:
28
+ 6. Order only `real_gap` items by risk/blast radius and execute them with an isolated worktree if they touch core.
29
+ 7. For each `real_gap`:
30
30
  - `guard_check`
31
31
  - `track`
32
- - branch propia
33
- - implementacion minima
34
- - tests adyacentes
32
+ - dedicated branch
33
+ - minimal implementation
34
+ - adjacent tests
35
35
  - PR + auto-merge squash
36
- - seguir al siguiente sin esperar CI salvo bloqueo real
37
- 8. Para `fp` o `casi_fp`, captura learning/patron reusable en vez de reimplementar.
38
- 9. Cierra la fase con evidencia real: PRs, tests, merge status y resultados de verificacion.
36
+ - continue to the next item without waiting for CI unless there is a real blocker
37
+ 8. For `fp` or `near_fp`, capture a learning/reusable pattern instead of reimplementing.
38
+ 9. Close the phase with real evidence: PRs, tests, merge status, and verification results.
39
39
 
40
40
  ## Gotchas
41
- - Learning #198: no confundas "como trabaja NEXO" con "que puede aplicar evolution_apply". Lo primero ya esta resuelto: autonomia total.
42
- - `apply_findings.py` ya stagea `code_change` en `evolution_log`; `nexo-evolution-run.py` ya consume `accepted` con sandbox/snapshot/rollback. Si el item pide eso, primero verifica si ya existe.
43
- - En Fase 1+2 la auditoria automatica sobreestimo ~70% de gaps. Si no hay evidencia dura, no abras codigo.
41
+ - Learning #198: do not confuse "how NEXO works" with "what evolution_apply may apply". The first is already settled: full autonomy.
42
+ - `apply_findings.py` already stages `code_change` in `evolution_log`; `nexo-evolution-run.py` already consumes `accepted` with sandbox/snapshot/rollback. If the item asks for that, first verify whether it already exists.
43
+ - In Phase 1+2 the automated audit overestimated about 70% of gaps. If there is no hard evidence, do not open code.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "SK-RUN-NEXO-AUDIT-PHASE",
3
3
  "name": "Run NEXO Audit Phase",
4
- "description": "Workflow para ejecutar una fase de auditoria de NEXO con autonomia total y verificacion empirica. Cubre el patron repetido de decidir el scope de evolution_apply y arrancar una tanda de items sin checkpoints manuales.",
4
+ "description": "Workflow for running a NEXO audit phase with full autonomy and empirical verification. Covers the recurring pattern of deciding the evolution_apply scope and starting a batch of items without manual checkpoints.",
5
5
  "level": "published",
6
6
  "mode": "guide",
7
7
  "source_kind": "core",
@@ -17,26 +17,26 @@
17
17
  ],
18
18
  "trigger_patterns": [
19
19
  "evolution_apply sandbox scope",
20
- "fase 2 item 1",
21
- "arrancar verificacion empirica",
22
- "ejecutar fase de auditoria nexo",
20
+ "phase 2 item 1",
21
+ "start empirical verification",
22
+ "run nexo audit phase",
23
23
  "audit phase nexo",
24
24
  "7 items audit"
25
25
  ],
26
26
  "steps": [
27
- "Abrir goal, workflow y task antes de tocar la auditoria; resolver repo/runtime real, DB real, tests y estado git antes de interpretar el informe.",
28
- "Fijar la regla de autonomia: NEXO hace branches, PRs, merge y progreso periodico; no pedir checkpoints uno-a-uno a Francisco para trabajo mecanico.",
29
- "Para `evolution_apply`, separar dos planos: el sandbox es para materializar/aplicar cambios aceptados con snapshot/rollback; no es un freno para el modo de trabajo autonomo del agente.",
30
- "Lanzar verificacion empirica de TODOS los items en paralelo: grep+read, SQL/schema real, AST/tests, logs. Partir de la hipotesis de FP hasta tener evidencia.",
31
- "Clasificar cada item en `real_gap`, `casi_fp` o `fp` y ordenar solo los reales por blast radius/riesgo.",
32
- "Si hay que tocar core, usar worktree aislado; por item real: guard_check -> track -> branch -> implement -> tests adyacentes -> PR -> auto-merge squash.",
33
- "Para items FP o casi-FP, capturar learning o ajustar el patron reusable en vez de reimplementar.",
34
- "Cerrar con evidencia de PRs, tests y merge status reales; no usar diarios o workflow text como sustituto."
27
+ "Open the goal, workflow, and task before touching the audit; resolve the real repo/runtime, real DB, tests, and git status before interpreting the report.",
28
+ "Set the autonomy rule: NEXO creates branches, PRs, merges, and periodic progress reports; do not ask Francisco for one-by-one checkpoints for mechanical work.",
29
+ "For `evolution_apply`, separate two planes: the sandbox materializes/applies accepted changes with snapshot/rollback; it is not a brake on the agent's autonomous work mode.",
30
+ "Launch empirical verification of ALL items in parallel: grep+read, real SQL/schema, AST/tests, logs. Start from the FP hypothesis until evidence says otherwise.",
31
+ "Classify each item as `real_gap`, `near_fp`, or `fp`, and order only real items by blast radius/risk.",
32
+ "If core must be touched, use an isolated worktree; for each real item: guard_check -> track -> branch -> implement -> adjacent tests -> PR -> auto-merge squash.",
33
+ "For FP or near-FP items, capture a learning or adjust the reusable pattern instead of reimplementing.",
34
+ "Close with real PR, test, and merge-status evidence; do not use diaries or workflow text as substitutes."
35
35
  ],
36
36
  "gotchas": [
37
- "No pedir aprobacion manual a Francisco para cada PR: learning #198 confirma autonomia total con transparencia y reportes periodicos.",
38
- "No duplicar el sandbox de evolution en otros sitios: el apply de cambios aceptados ya reutiliza evolution_log + sandbox/snapshot/rollback.",
39
- "El ratio historico de FPs en esta clase de auditoria es alto; si no hay evidencia concreta, el item no se toca."
37
+ "Do not ask Francisco for manual approval on every PR: learning #198 confirms full autonomy with transparency and periodic reports.",
38
+ "Do not duplicate the evolution sandbox elsewhere: accepted-change apply already reuses evolution_log + sandbox/snapshot/rollback.",
39
+ "The historical FP rate for this audit class is high; if there is no concrete evidence, the item is not touched."
40
40
  ],
41
41
  "params_schema": {
42
42
  "audit_file": {
@@ -1,17 +1,17 @@
1
1
  # Run NEXO Core Fix Cycle
2
2
 
3
- Usa esta skill cuando haya que implementar y verificar un grupo pequeño de fixes del core de NEXO sin improvisar el plano ni repetir siempre la misma fase de descubrimiento y test.
3
+ Use this skill when a small group of NEXO core fixes must be implemented and verified without improvising the plane or repeating the same discovery and test phase every time.
4
4
 
5
5
  ## Steps
6
- 1. Fija primero el plano: repo público `nexo`, runtime instalado `~/.nexo` y claims/documentación pública. No mezcles esos tres mundos.
7
- 2. Abre `nexo_task_open(...)` y `nexo_workflow_open(...)` antes de tocar código. Si el fix es de acción, pasa también por `nexo_cortex_decide(...)`.
8
- 3. Ejecuta la skill con `areas` ajustadas al fix. El helper te devuelve el mapa de archivos y corre la batería de tests focalizada para `protocol`, `plane`, `guard`, `cortex` y/o `release`.
9
- 4. Implementa el cambio mínimo defendible solo en la superficie correcta. Si el problema es producto, se arregla en el repo; no en `~/.nexo`.
10
- 5. Reejecuta la skill para revalidar el clúster exacto de tests tocado por el fix.
11
- 6. Cierra con `nexo_task_close(...)` y evidencia real. Si hubo edición real, deja `change_log` y captura learning si cambió una regla canónica.
6
+ 1. First fix the plane: public `nexo` repo, installed `~/.nexo` runtime, and public claims/docs. Do not mix those three worlds.
7
+ 2. Open `nexo_task_open(...)` and `nexo_workflow_open(...)` before touching code. If the fix is action-related, also pass through `nexo_cortex_decide(...)`.
8
+ 3. Run the skill with `areas` adjusted to the fix. The helper returns the file map and runs the focused test battery for `protocol`, `plane`, `guard`, `cortex`, and/or `release`.
9
+ 4. Implement the smallest defensible change only on the correct surface. If the problem is product-level, fix it in the repo, not in `~/.nexo`.
10
+ 5. Rerun the skill to revalidate the exact test cluster touched by the fix.
11
+ 6. Close with `nexo_task_close(...)` and real evidence. If there was a real edit, leave `change_log` and capture a learning if a canonical rule changed.
12
12
 
13
13
  ## Gotchas
14
- - No uses diary, workflow text o intuición como sustituto de git/tests/runtime reales.
15
- - Si el fix toca doctor o claims públicos, fija el `plane` explícito antes de ejecutar diagnósticos.
16
- - Si el fix toca release o runtime update, usa la vía oficial (`nexo update`, doctor, skill de release final) y no scripts laterales.
17
- - Si el helper no encuentra un área, añade la superficie nueva de forma explícita en la skill en vez de seguir repitiendo grep manual disperso.
14
+ - Do not use diary, workflow text, or intuition as a substitute for real git/tests/runtime evidence.
15
+ - If the fix touches doctor or public claims, set the explicit `plane` before running diagnostics.
16
+ - If the fix touches release or runtime update, use the official path (`nexo update`, doctor, final release skill), not side scripts.
17
+ - If the helper does not find an area, add the new surface explicitly to the skill instead of continuing scattered manual grep.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "SK-RUN-NEXO-CORE-FIX-CYCLE",
3
3
  "name": "Run NEXO Core Fix Cycle",
4
- "description": "Mapea las superficies core impactadas y ejecuta la verificación focalizada para ciclos pequeños de fixes de NEXO sin mezclar repo público, runtime y claims no verificados.",
4
+ "description": "Map impacted core surfaces and run focused verification for small NEXO fix cycles without mixing the public repo, runtime, and unverified claims.",
5
5
  "level": "published",
6
6
  "mode": "hybrid",
7
7
  "source_kind": "core",
@@ -16,11 +16,11 @@
16
16
  "verification"
17
17
  ],
18
18
  "trigger_patterns": [
19
- "dos fixes core nexo",
19
+ "two nexo core fixes",
20
20
  "core fix cycle",
21
- "enforcement protocolario",
22
- "plano antes del diagnostico",
23
- "ciclo fix core nexo"
21
+ "protocol enforcement",
22
+ "plane before diagnosis",
23
+ "nexo core fix cycle"
24
24
  ],
25
25
  "params_schema": {
26
26
  "areas": {
@@ -4,8 +4,8 @@ Examples:
4
4
  + User: "Vamos a arreglar el bug del checkout" -> yes
5
5
  + User: "Hazme un refactor del login de CanaRirural" -> yes
6
6
  + User: "Revisa la PR del orchestrator" -> yes
7
- - User: "qué hora es" -> no
8
- - User: "gracias, ya está" -> no
7
+ - User: "what time is it" -> no
8
+ - User: "thanks, that's done" -> no
9
9
  - User: "dime un chiste" -> no
10
10
 
11
11
  Now decide. Input: