From 3b008916149540d4a35127c27b1cc622989b1c5b Mon Sep 17 00:00:00 2001 From: Perplexity Computer Date: Wed, 8 Apr 2026 10:32:52 +0000 Subject: [PATCH 1/2] =?UTF-8?q?refactor:=20wire=20retrieval=5Fenforcer=20L?= =?UTF-8?q?1=20to=20SovereignStore=20=E2=80=94=20eliminate=20subprocess/ON?= =?UTF-8?q?NX=20dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the subprocess call to mempalace CLI binary with direct SovereignStore import. L1 palace search now uses SQLite + FTS5 + HRR vectors in-process. No ONNX, no subprocess, no API calls. Removes: import subprocess, MEMPALACE_BIN constant Adds: SovereignStore lazy singleton, _get_store(), SOVEREIGN_DB path Closes #383 Depends on #380 (sovereign_store.py) --- .../mempalace/retrieval_enforcer.py | 91 +++++++++++++------ 1 file changed, 62 insertions(+), 29 deletions(-) diff --git a/hermes-sovereign/mempalace/retrieval_enforcer.py b/hermes-sovereign/mempalace/retrieval_enforcer.py index f5030737..a8ca5aa1 100644 --- a/hermes-sovereign/mempalace/retrieval_enforcer.py +++ b/hermes-sovereign/mempalace/retrieval_enforcer.py @@ -1,28 +1,37 @@ """Retrieval Order Enforcer — L0 through L5 memory hierarchy. Ensures the agent checks durable memory before falling back to free generation. -Gracefully degrades if any layer is unavailable (ONNX issues, missing files, etc). +Gracefully degrades if any layer is unavailable (missing files, etc). Layer order: - L0: Identity (~/.mempalace/identity.txt) - L1: Palace rooms (mempalace CLI search) - L2: Session scratch (~/.hermes/scratchpad/{session_id}.json) - L3: Gitea artifacts (API search for issues/PRs) - L4: Procedures (skills directory search) - L5: Free generation (only if L0-L4 produced nothing) + L0: Identity (~/.mempalace/identity.txt) + L1: Palace rooms (SovereignStore — SQLite + FTS5 + HRR, zero API calls) + L2: Session scratch (~/.hermes/scratchpad/{session_id}.json) + L3: Gitea artifacts (API search for issues/PRs) + L4: Procedures (skills directory search) + L5: Free generation (only if L0-L4 produced nothing) -Refs: Epic #367, Sub-issue #369 +Refs: Epic #367, Sub-issue #369, Wiring: #383 """ - from __future__ import annotations import json import os import re -import subprocess from pathlib import Path from typing import Optional +# --------------------------------------------------------------------------- +# Sovereign Store (replaces mempalace CLI subprocess) +# --------------------------------------------------------------------------- +try: + from .sovereign_store import SovereignStore +except ImportError: + try: + from sovereign_store import SovereignStore + except ImportError: + SovereignStore = None # type: ignore[misc,assignment] + # --------------------------------------------------------------------------- # Constants # --------------------------------------------------------------------------- @@ -30,7 +39,7 @@ from typing import Optional IDENTITY_PATH = Path.home() / ".mempalace" / "identity.txt" SCRATCHPAD_DIR = Path.home() / ".hermes" / "scratchpad" SKILLS_DIR = Path.home() / ".hermes" / "skills" -MEMPALACE_BIN = "/Library/Frameworks/Python.framework/Versions/3.12/bin/mempalace" +SOVEREIGN_DB = Path.home() / ".hermes" / "palace" / "sovereign.db" # Patterns that indicate a recall-style query RECALL_PATTERNS = re.compile( @@ -42,6 +51,23 @@ RECALL_PATTERNS = re.compile( r")\b" ) +# Singleton store instance (lazy-init) +_store: Optional["SovereignStore"] = None + + +def _get_store() -> Optional["SovereignStore"]: + """Lazy-init the SovereignStore singleton.""" + global _store + if _store is not None: + return _store + if SovereignStore is None: + return None + try: + _store = SovereignStore(db_path=str(SOVEREIGN_DB)) + return _store + except Exception: + return None + # --------------------------------------------------------------------------- # L0: Identity @@ -62,25 +88,33 @@ def load_identity() -> str: # --------------------------------------------------------------------------- -# L1: Palace search +# L1: Palace search (now via SovereignStore — zero subprocess, zero API) # --------------------------------------------------------------------------- -def search_palace(query: str) -> str: - """Search the mempalace for relevant memories. Gracefully degrades on failure.""" +def search_palace(query: str, room: Optional[str] = None) -> str: + """Search the sovereign memory store for relevant memories. + + Uses SovereignStore (SQLite + FTS5 + HRR) for hybrid keyword + semantic + search. No subprocess calls, no ONNX, no API keys. + + Gracefully degrades to empty string if store is unavailable. + """ + store = _get_store() + if store is None: + return "" try: - bin_path = MEMPALACE_BIN if os.path.exists(MEMPALACE_BIN) else "mempalace" - result = subprocess.run( - [bin_path, "search", query], - capture_output=True, - text=True, - timeout=10, - ) - if result.returncode == 0 and result.stdout.strip(): - return result.stdout.strip() - except (FileNotFoundError, subprocess.TimeoutExpired, OSError): - # ONNX issues (#373) or mempalace not installed — degrade gracefully - pass - return "" + results = store.search(query, room=room, limit=5, min_trust=0.2) + if not results: + return "" + lines = [] + for r in results: + trust = r.get("trust_score", 0.5) + room_name = r.get("room", "general") + content = r.get("content", "") + lines.append(f" [{room_name}] (trust:{trust:.2f}) {content}") + return "\n".join(lines) + except Exception: + return "" # --------------------------------------------------------------------------- @@ -177,7 +211,6 @@ def search_skills(query: str) -> str: try: content = skill_md.read_text(encoding="utf-8").lower() if any(t in content for t in terms): - # Extract title from frontmatter title = skill_dir.name matches.append(f" skill: {title}") except OSError: @@ -236,7 +269,7 @@ def enforce_retrieval_order( result["context"] += f"## Identity\n{identity}\n\n" result["layers_checked"].append("L0") - # L1: Palace search + # L1: Palace search (SovereignStore — zero API, zero subprocess) palace_results = search_palace(query) if palace_results: result["context"] += f"## Palace Memory\n{palace_results}\n\n" From 11736e58cda42e8896195c258e4c66feed51686d Mon Sep 17 00:00:00 2001 From: Perplexity Computer Date: Wed, 8 Apr 2026 10:58:55 +0000 Subject: [PATCH 2/2] docs: add disambiguation header to SOUL.md (Bitcoin inscription) This SOUL.md is the Bitcoin inscription version, not the narrative identity document. Adding an HTML comment header to clarify. The canonical narrative SOUL.md lives in timmy-home. See: #388, #378 --- SOUL.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/SOUL.md b/SOUL.md index 4b73b1fc..807c49fa 100644 --- a/SOUL.md +++ b/SOUL.md @@ -1,3 +1,13 @@ + + # SOUL.md ## Inscription 1 — The Immutable Conscience