From 4d1580390014f17078bd21f59e6de41f7f814709 Mon Sep 17 00:00:00 2001 From: jp Date: Thu, 10 Sep 2026 17:35:26 -0700 Subject: [PATCH 1/2] fix(postgres): cached connection reconnects once instead of raising raw After any server-side disconnect -- DB restart, pg_terminate_backend, or the idle_session_timeout = 10min now set on the production palace DB -- the first call on an unguarded cached-connection path raised raw and surfaced as a 500; the call after that healed. Verified live on the prod daemon 2026-08-08, and observed during the 2026-08-07/08 outage on count() -> tool_status and on graph_stats. The mechanism, empirically confirmed on psycopg 3: closed before: False closed immediately after server-side kill: False | broken: False query after kill raised: AdminShutdown closed AFTER failed query: True | broken: True .closed is a client-side flag updated only on I/O, so _get_conn() hands the dead connection straight back and the statement that runs on it is what discovers the socket is gone. Fixed at the seam rather than per call site: every statement on the cached connection now goes through _cursor(), a _RetryingCursor that classifies the failure and, only for a connection-class error, drops the socket, reconnects and re-runs once. Statement errors (a bad query, a statement_timeout) are re-raised untouched and do not churn the connection -- retrying those would mask real faults and cost a second failure. Retrying is safe here because the backend connection is autocommit: each statement is its own transaction, so a reconnect mid-sequence loses no transactional state and cannot half-apply one. (knowledge_graph_age needs a rollback companion for the opposite reason -- its connection is not autocommit.) Reconnecting through _get_conn() rather than by hand also re-applies the session GUCs; a hand-rolled reconnect that dropped hnsw.iterative_scan (#446) would have turned wing-scoped search silently back into zero rows, so there is a test pinning that. _drop_conn() closes the dead socket rather than abandoning it -- two forever-cached idle connections are what pinned a smart shutdown open for 37.5 hours on 2026-08-07. Blast radius: mempalace/backends/postgres.py only. All 11 cached-connection cursor sites were routed through the new seam; the three connections that are deliberately not cached (session-settings bootstrap, the index-build connection, the backend health probe) are untouched. Tests: tests/test_postgres_reconnect.py, 7 cases -- count() and get() both survive a kill, the dead socket is closed not leaked, a statement error is re-raised without reconnecting, exactly one retry then the error surfaces, message-based classification for unfamiliar wrapper classes, and session settings re-applied on the replacement connection. Part of #385 Fixes #385 Co-Authored-By: Claude Opus 5 --- mempalace/backends/postgres.py | 178 ++++++++++++++++++++++-- tests/test_postgres_reconnect.py | 228 +++++++++++++++++++++++++++++++ 2 files changed, 395 insertions(+), 11 deletions(-) create mode 100644 tests/test_postgres_reconnect.py diff --git a/mempalace/backends/postgres.py b/mempalace/backends/postgres.py index f4ac935d27..4d4ba7b661 100644 --- a/mempalace/backends/postgres.py +++ b/mempalace/backends/postgres.py @@ -242,6 +242,137 @@ def _scrub_unstorable( meta.update(scrubbed_meta) +def _is_connection_error(exc: BaseException) -> bool: + """True when the socket is gone, false when the statement was merely bad. + + Only connection errors are worth retrying. A statement error means the + caller's query was wrong or too slow and the connection is perfectly + healthy; re-running it would mask a real fault, cost a second failure and + churn a good connection for nothing. + + This classification does *not* establish that a retry is safe — see + :class:`_RetryingCursor` for the invariant that does. A connection error + does not imply the statement never ran. + + Classified the same way ``knowledge_graph_age`` does (#405) — by + ``OperationalError``/``InterfaceError`` class, with a message fallback for + the wrappers that do not inherit from either. ``AdminShutdown``, which is + what a ``pg_terminate_backend`` or a smart shutdown actually raises, + subclasses ``OperationalError``. + """ + try: + psycopg2, _sql = _load_psycopg2() + except Exception: # pragma: no cover - the driver is a hard dependency here + psycopg2 = None + if psycopg2 is not None: + conn_errors = tuple( + candidate + for candidate in ( + getattr(psycopg2, name, None) for name in ("OperationalError", "InterfaceError") + ) + if isinstance(candidate, type) and issubclass(candidate, BaseException) + ) + if conn_errors and isinstance(exc, conn_errors): + return True + message = str(exc) + return "connection is closed" in message or "server closed the connection" in message + + +class _RetryingCursor: + """A cursor that reconnects once when the cached connection turns out dead. + + psycopg's ``.closed`` is a client-side flag updated only on I/O, so after + a server-side disconnect (DB restart, ``pg_terminate_backend``, or the + ``idle_session_timeout`` the production palace now sets) it stays ``False`` + and ``_get_conn()`` hands the dead connection straight back. Measured live + 2026-08-08:: + + closed before: False + closed immediately after server-side kill: False | broken: False + query after kill raised: AdminShutdown + closed AFTER failed query: True | broken: True + + So the connection is only *discovered* dead by the statement that fails on + it, and every restart cost one raw 500 per cached-connection path — + observed on ``count()`` → ``tool_status`` and on ``graph_stats``. + + **Retry safety — read this before routing anything new through** + ``_cursor()``. + + A connection error does *not* mean the statement never ran. This + connection is ``autocommit``, so each statement commits on its own, and + the socket can die after the server committed and before the + acknowledgement reaches the client. A retry in that window re-runs a + statement that already took effect. Autocommit buys only that there is no + half-applied multi-statement transaction to unwind; it says nothing about + the ambiguous window. (``knowledge_graph_age`` needs a ``rollback`` + companion for the opposite reason: its connection is *not* autocommit, so + a statement error leaves the transaction aborted.) + + So the retry is safe because of an invariant this class cannot enforce, + not because of anything the driver guarantees: **every statement routed + through this seam must be idempotent.** Today all of them are — + + - ``_insert_rows`` — ``INSERT ... ON CONFLICT DO UPDATE``/``DO NOTHING``; + - ``update`` — ``SET metadata = metadata || %s::jsonb WHERE id = %s``, a + fixed point under re-application; + - ``delete`` — ``DELETE ... WHERE id IN (...)``, keyed; + - ``rename_wing`` — each batch is ``WHERE wing = LIMIT n``, so a + re-run simply matches the rows still to move. A lost acknowledgement + under-counts the returned ``renamed`` total; it does not leave the + rename unfinished, because the loop keeps going while rows remain; + - ``count``, ``_estimated_count``, ``_table_exists``, ``get``, + ``_query_one`` — read-only; + - ``_detect_extensions`` — ``CREATE EXTENSION IF NOT EXISTS``. + + The one exception is the DDL in ``_create_table``: those are bare + ``CREATE TABLE``/``CREATE INDEX``, guarded by a preceding + ``_table_exists()`` rather than by ``IF NOT EXISTS``. A disconnect in the + ambiguous window during *first* creation therefore fails loudly on the + retry with "relation already exists" instead of silently doing the wrong + thing — acceptable, and the next call succeeds because the guard now sees + the table. + + Adding a non-idempotent statement here (a bare ``INSERT``, a + ``SET x = x + 1``, anything whose second application differs from its + first) would make this seam unsafe. Give it a fresh connection instead. + + Reconnecting through ``_get_conn()`` rather than by hand also re-applies + the session GUCs — losing ``hnsw.iterative_scan`` (#446) on a silently + replaced connection would turn wing-scoped search back into zero rows. + """ + + def __init__(self, collection: "PostgresCollection"): + self._collection = collection + self._cur = collection._get_conn().cursor() + + def execute(self, query, params=None): + try: + return self._run(query, params) + except Exception as exc: # noqa: BLE001 — classified, then re-raised or retried + if not _is_connection_error(exc): + raise + logger.info( + "postgres: cached connection was dead (%s); reconnecting once", + str(exc).splitlines()[0][:120], + ) + self._collection._drop_conn() + self._cur = self._collection._get_conn().cursor() + return self._run(query, params) + + def _run(self, query, params): + if params is None: + return self._cur.execute(query) + return self._cur.execute(query, params) + + def __getattr__(self, name): + # Guard the two real attributes so a lookup during __init__ cannot + # recurse into this delegate. + if name in ("_collection", "_cur"): + raise AttributeError(name) + return getattr(self._cur, name) + + def _validate_write_lengths( *, documents: list[str], @@ -412,7 +543,7 @@ def _insert_rows( row_embeddings = [row[4] for row in rows] row_metadatas = [row[5] for row in rows] - cur = self._get_conn().cursor() + cur = self._cursor() if self._table_am == "sorted_heap": cur.execute( self._sql.SQL( @@ -555,7 +686,7 @@ def _query_one( ) embedding = _vec_literal(query_embedding) - cur = self._get_conn().cursor() + cur = self._cursor() cur.execute( self._sql.SQL( "SELECT id, document, wing, room, metadata, " @@ -644,7 +775,7 @@ def get( self._sql.SQL(", embedding::text") if spec.embeddings else self._sql.SQL("") ) - cur = self._get_conn().cursor() + cur = self._cursor() cur.execute( self._sql.SQL("SELECT id, document, wing, room, metadata{} FROM {} {} {} {}").format( embedding_select, @@ -694,7 +825,7 @@ def delete( if not clauses: return - cur = self._get_conn().cursor() + cur = self._cursor() # Resolve the doomed ids BEFORE the DELETE so the delete-through # hook can propagate them to AGE. For id-only deletes this is just # ``ids``; for where-based deletes we have to query first because @@ -769,7 +900,7 @@ def update( _scrub_unstorable(ids=ids, metadatas=metadatas) self._ensure_setup(create=True) - cur = self._get_conn().cursor() + cur = self._cursor() for i, doc_id in enumerate(ids): meta = dict(metadatas[i]) if metadatas else {} raw_wing = meta.pop("wing", None) @@ -799,7 +930,7 @@ def rename_wing(self, *, from_wing: str, to_wing: str, batch_size: int = 500) -> # same guard as writes so a rename can't mint a new malformed wing. to_wing = _coerce_wing(to_wing) self._ensure_setup(create=True) - cur = self._get_conn().cursor() + cur = self._cursor() renamed = 0 # Batch the UPDATE to stay within statement_timeout. Each batch # auto-commits independently (connection has autocommit=True). @@ -820,7 +951,7 @@ def rename_wing(self, *, from_wing: str, to_wing: str, batch_size: int = 500) -> def count(self) -> int: self._ensure_setup(create=True) - cur = self._get_conn().cursor() + cur = self._cursor() # Public collection API: keep this exact. Use estimated_count() for # status/heuristic paths where stale PostgreSQL catalog stats are acceptable. cur.execute(self._sql.SQL("SELECT COUNT(*) FROM {}").format(self._table_id)) @@ -862,6 +993,31 @@ def _get_conn(self): self._apply_session_settings(self._conn) return self._conn + def _drop_conn(self) -> None: + """Release a connection we have established is dead. + + Abandoning it instead of closing it is how two forever-cached idle + connections pinned a smart shutdown open for 37.5 hours on + 2026-08-07; the close is best-effort because the socket is already + gone by definition. + """ + conn, self._conn = self._conn, None + if conn is None: + return + try: + conn.close() + except Exception: # noqa: BLE001 - the socket is already gone + logger.debug("postgres: closing a dead connection failed", exc_info=True) + + def _cursor(self) -> _RetryingCursor: + """Every statement on the cached connection goes through here. + + One seam rather than a guard at each call site: the failure is a + property of the cached connection, not of any particular query, so + anything that reaches the database through ``_conn`` needs it. + """ + return _RetryingCursor(self) + def _apply_session_settings(self, conn) -> None: """Per-connection GUCs the kNN path depends on. @@ -907,7 +1063,7 @@ def _detect_extensions(self, *, create: bool = False) -> None: if self._vec_type: return - cur = self._get_conn().cursor() + cur = self._cursor() cur.execute( "SELECT extname FROM pg_extension WHERE extname IN ('pg_sorted_heap', 'vector')" ) @@ -958,7 +1114,7 @@ def _open(self, *, create: bool) -> None: self._setup_done = True def _table_exists(self) -> bool: - cur = self._get_conn().cursor() + cur = self._cursor() cur.execute( "SELECT 1 FROM information_schema.tables " "WHERE table_schema = 'public' AND table_name = %s", @@ -975,7 +1131,7 @@ def _ensure_setup(self, *, create: bool = True) -> None: raise PalaceNotFoundError(f"PostgreSQL collection does not exist: {self.table_name}") if create: - cur = self._get_conn().cursor() + cur = self._cursor() self._create_table(cur) self._setup_done = True @@ -1178,7 +1334,7 @@ def _maybe_create_vector_index(self, *, inserted_rows: int = 0) -> None: ix_conn.close() def _estimated_count(self) -> int: - cur = self._get_conn().cursor() + cur = self._cursor() cur.execute( """ SELECT GREATEST( diff --git a/tests/test_postgres_reconnect.py b/tests/test_postgres_reconnect.py new file mode 100644 index 0000000000..6bcd52bc21 --- /dev/null +++ b/tests/test_postgres_reconnect.py @@ -0,0 +1,228 @@ +"""A cached postgres connection must survive a server-side disconnect (#385). + +Measured live on the production daemon 2026-08-08: after any server-side +disconnect (DB restart, ``pg_terminate_backend``, or the ``idle_session_timeout += 10min`` now set on the palace DB), the *first* call on an unguarded +cached-connection path raises raw and surfaces as a 500; the call after that +heals. + +The mechanism, empirically confirmed on psycopg 3:: + + closed before: False + closed immediately after server-side kill: False | broken: False + query after kill raised: AdminShutdown + closed AFTER failed query: True | broken: True + +``.closed`` is a client-side flag updated only on I/O, so ``_get_conn()`` +cheerfully hands back a dead connection and the next ``execute()`` raises. +These tests drive that exact sequence through a fake connection. +""" + +import pytest + +from mempalace.backends import postgres as pg + + +def _real_sql(): + """Real psycopg SQL composition — these tests drive real collection methods. + + The ``test-linux`` CI job installs mempalace without the ``postgres`` + extra, so psycopg is absent there and a module-scope import would fail + collection outright (it did, on main, for #453's bind-site tests). + """ + return pytest.importorskip("psycopg.sql", reason="requires the postgres extra") + + +class _FakePsycopgError(Exception): + """Stands in for ``psycopg.Error`` — the base of every driver error.""" + + +class _FakeOperationalError(_FakePsycopgError): + """Stands in for ``psycopg.OperationalError`` (AdminShutdown's base).""" + + +class _FakeInterfaceError(_FakePsycopgError): + """Stands in for ``psycopg.InterfaceError`` (the connection is closed).""" + + +class _FakeProgrammingError(_FakePsycopgError): + """A statement-level error: the query was wrong, the socket is fine.""" + + +class _FakeCursor: + def __init__(self, conn): + self.conn = conn + + def execute(self, sql, params=None): + text = str(sql) + self.conn.executed.append(text) + # Session GUCs land at connect time, before the server kills anything; + # scripting a failure onto them would test the wrong moment. + if text.startswith("SET "): + return self + exc = self.conn.raise_next.pop(0) if self.conn.raise_next else None + if exc is not None: + # A server-side kill marks the connection closed only *after* the + # failed statement — which is the whole bug. + if isinstance(exc, (_FakeOperationalError, _FakeInterfaceError)): + self.conn.closed = 1 + raise exc + return self + + def fetchone(self): + return self.conn.fetchone_value + + def fetchall(self): + return self.conn.fetchall_value + + +class _FakeConn: + def __init__(self, raise_next=None, fetchone_value=(7,)): + self.executed = [] + self.autocommit = None + self.closed = 0 + self.raise_next = list(raise_next or []) + self.fetchone_value = fetchone_value + self.fetchall_value = [] + self.close_calls = 0 + + def cursor(self): + return _FakeCursor(self) + + def close(self): + self.close_calls += 1 + self.closed = 1 + + +class _FakeMod: + """A psycopg stand-in that hands out a scripted series of connections.""" + + OperationalError = _FakeOperationalError + InterfaceError = _FakeInterfaceError + ProgrammingError = _FakeProgrammingError + Error = _FakePsycopgError + + def __init__(self, conns): + self.conns = list(conns) + self.handed_out = [] + + def connect(self, dsn, **kwargs): + conn = self.conns.pop(0) if self.conns else _FakeConn() + self.handed_out.append(conn) + return conn + + +@pytest.fixture() +def collection(monkeypatch): + """Return a factory: give it connections, get a wired-up collection.""" + + def _make(*conns): + mod = _FakeMod(conns) + monkeypatch.setattr(pg, "_load_psycopg2", lambda: (mod, _real_sql())) + col = pg.PostgresCollection.__new__(pg.PostgresCollection) + col.dsn = "postgresql://fake/db" + col.table_name = "mempalace_drawers" + col._conn = None + col._vec_type = "vector" + col._table_am = "heap" + col._index_am = "hnsw" + col._setup_done = True + col._vector_index_ready = True + col._rows_since_index_check = 0 + col._local_row_estimate = 0 + return col, mod + + return _make + + +# --------------------------------------------------------------------------- +# The reported symptom +# --------------------------------------------------------------------------- + + +def test_count_survives_a_server_side_disconnect(collection): + """``count()`` is the path that produced raw 500s during the outage.""" + dead = _FakeConn(raise_next=[_FakeOperationalError("server closed the connection")]) + live = _FakeConn(fetchone_value=(42,)) + col, mod = collection(dead, live) + + col._get_conn() # prime the cache with the soon-to-be-dead connection + assert col.count() == 42 + assert len(mod.handed_out) == 2, "expected exactly one reconnect" + + +def test_get_survives_a_server_side_disconnect(collection): + """Every cached-conn read path shares the seam, not just count().""" + dead = _FakeConn(raise_next=[_FakeInterfaceError("the connection is closed")]) + live = _FakeConn() + col, mod = collection(dead, live) + + col._get_conn() + col.get(ids=["d1"]) + assert len(mod.handed_out) == 2 + + +def test_dead_connection_is_closed_not_leaked(collection): + """The 37.5h postmaster wedge was two forever-cached idle connections.""" + dead = _FakeConn(raise_next=[_FakeOperationalError("terminating connection")]) + live = _FakeConn(fetchone_value=(1,)) + col, _mod = collection(dead, live) + + col._get_conn() + col.count() + assert dead.close_calls == 1, "the dead socket must be released, not abandoned" + + +# --------------------------------------------------------------------------- +# The retry must not mask real errors, and must not loop +# --------------------------------------------------------------------------- + + +def test_statement_error_is_reraised_without_reconnecting(collection): + """A bad query is the caller's fault — the connection is perfectly fine.""" + conn = _FakeConn(raise_next=[_FakeProgrammingError('relation "nope" does not exist')]) + col, mod = collection(conn) + + col._get_conn() + with pytest.raises(_FakeProgrammingError): + col.count() + assert len(mod.handed_out) == 1, "a statement error must not churn the connection" + + +def test_only_one_retry_then_the_error_surfaces(collection): + """If the database is genuinely down, fail — do not spin.""" + dead1 = _FakeConn(raise_next=[_FakeOperationalError("server closed the connection")]) + dead2 = _FakeConn(raise_next=[_FakeOperationalError("connection refused")]) + col, mod = collection(dead1, dead2) + + col._get_conn() + with pytest.raises(_FakeOperationalError): + col.count() + assert len(mod.handed_out) == 2, "exactly one retry, not a loop" + + +def test_connection_error_detected_by_message_when_class_is_unfamiliar(collection): + """Drivers wrap some disconnects in classes we do not import by name.""" + assert pg._is_connection_error(RuntimeError("server closed the connection unexpectedly")) + assert pg._is_connection_error(RuntimeError("the connection is closed")) + assert not pg._is_connection_error(RuntimeError("division by zero")) + + +# --------------------------------------------------------------------------- +# The replacement connection must be a *complete* connection +# --------------------------------------------------------------------------- + + +def test_reconnect_reapplies_session_settings(monkeypatch, collection): + """hnsw.iterative_scan (#446) is session-scoped — a silent loss is a + wing-scoped search that starts returning zero rows.""" + monkeypatch.delenv("MEMPALACE_PG_HNSW_ITERATIVE_SCAN", raising=False) + dead = _FakeConn(raise_next=[_FakeOperationalError("server closed the connection")]) + live = _FakeConn(fetchone_value=(1,)) + col, _mod = collection(dead, live) + + col._get_conn() + col.count() + + assert "SET hnsw.iterative_scan = relaxed_order" in live.executed + assert live.autocommit is True From c06bc8b98adccf019c77ad953d9fb246260a8c1e Mon Sep 17 00:00:00 2001 From: jp Date: Thu, 10 Sep 2026 18:20:37 -0700 Subject: [PATCH 2/2] docs(fork-changes): postgres cached-connection reconnect (#385) Entry + the three renderers, plus the README/CLAUDE test count moved 7050 -> 7057 for the 7 new cases. Rebased onto 823547f7; the entry's commit hash tracks the rebased code commit so check-docs's hash-resolution check stays green. The entry's retry-safety paragraph carries the reviewer's correction with the code: a connection error does not imply the statement never ran, so the justification is the per-statement idempotency invariant, not autocommit. Part of #385 Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 2 +- FORK_CHANGELOG.md | 43 ++++++ README.md | 255 +++++++++++++++++----------------- docs/fork-changes.yaml | 47 +++++++ website/public/llms-full.txt | 257 ++++++++++++++++++----------------- 5 files changed, 348 insertions(+), 256 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 4671441e79..c942d976cb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,7 +57,7 @@ TechEmpower's production fork of [MemPalace/mempalace](https://github.com/MemPal ```bash source .venv/bin/activate -python -m pytest tests/ -q # 7050 tests (benchmarks deselected) +python -m pytest tests/ -q # 7057 tests (benchmarks deselected) mempalace status # check palace state mempalace search "query" # test search python -m mempalace.mcp_server # run MCP server standalone diff --git a/FORK_CHANGELOG.md b/FORK_CHANGELOG.md index 8af66897d8..fa32d67231 100644 --- a/FORK_CHANGELOG.md +++ b/FORK_CHANGELOG.md @@ -134,6 +134,49 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Fixed +- **Cached postgres connection reconnects once after a server-side disconnect instead of raising raw** ([`4d15803`](https://github.com/techempower-org/mempalace/commit/4d15803)) + After any server-side disconnect — a DB restart, a + ``pg_terminate_backend``, or the ``idle_session_timeout = 10min`` set on + the production palace DB after the 2026-08-07 incident — the *first* call + on an unguarded cached-connection path raised raw and surfaced as a 500; + the call after that healed. Verified live on the prod daemon 2026-08-08, + and observed during the outage itself on ``count()`` → ``tool_status`` + and on ``graph_stats``. + + psycopg's ``.closed`` is a client-side flag updated only on I/O, so it + stays ``False`` after a server-side kill and ``_get_conn()`` hands the + dead connection straight back; the statement that runs on it is what + discovers the socket is gone (``closed`` and ``broken`` both flip only + *after* the failed query). + + Fixed at the seam rather than per call site: every statement on the + cached connection now goes through ``_cursor()``, a ``_RetryingCursor`` + that classifies the failure and — only for a connection-class error — + drops the socket, reconnects and re-runs once. Statement errors (a bad + query, a ``statement_timeout``) are re-raised untouched and do not churn + the connection. The retry is safe because of an invariant, not because + of the driver: a connection error does *not* mean the statement never + ran — this connection is ``autocommit``, so the socket can die after the + server committed and before the acknowledgement arrives — but every + statement routed through the seam is idempotent (``ON CONFLICT`` upserts, + keyed ``DELETE``/``UPDATE``, a jsonb merge that is a fixed point, and + reads). The docstring states that as a requirement for anything new + routed through ``_cursor()``, and records the one exception: the bare + ``CREATE TABLE``/``CREATE INDEX`` in ``_create_table`` are guarded by + ``_table_exists()`` rather than ``IF NOT EXISTS``, so a disconnect in the + ambiguous window during first creation fails loudly on the retry rather + than silently. Reconnecting through + ``_get_conn()`` also re-applies the session GUCs — a hand-rolled + reconnect that dropped ``hnsw.iterative_scan`` (#446) would have turned + wing-scoped search silently back into zero rows, so a test pins it. + ``_drop_conn()`` closes the dead socket rather than abandoning it: two + forever-cached idle connections are what pinned a smart shutdown open + for 37.5 hours. + + *Tests:* 7 (test_postgres_reconnect: count + get survive a kill, dead socket closed not leaked, statement error re-raised without reconnecting, exactly one retry, message-based classification, session settings re-applied) + *Files:* `mempalace/backends/postgres.py`, `tests/test_postgres_reconnect.py` + + - **`mempalace replay` asks the daemon to queue each mine instead of waiting on the palace write lock** ([`059fe0a`](https://github.com/techempower-org/mempalace/commit/059fe0a)) ``hooks_cli._post_daemon_mine`` has sent ``"background": true`` since #433 and gets a 202 in ~2.5 ms. ``cli._post_daemon_mine_cli`` — a diff --git a/README.md b/README.md index a1ba08706a..6f27e6d22f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ ## What this is -A verbatim-first local AI memory system. This fork tracks `upstream/develop` through the post-v3.9.0 sync (2026-09-01, commit `e8098348`) and runs in production on a **618K+ drawer Postgres + pgvector + Apache AGE palace** behind [palace-daemon](https://github.com/techempower-org/palace-daemon). It carries fork-ahead commits that compose with — not replace — bensig's release direction; the v3.3.5 release (2026-05-10) includes our co-authored `_get_collection` retry-once via upstream #1377. 7050 tests pass on `main`. +A verbatim-first local AI memory system. This fork tracks `upstream/develop` through the post-v3.9.0 sync (2026-09-01, commit `e8098348`) and runs in production on a **618K+ drawer Postgres + pgvector + Apache AGE palace** behind [palace-daemon](https://github.com/techempower-org/palace-daemon). It carries fork-ahead commits that compose with — not replace — bensig's release direction; the v3.3.5 release (2026-05-10) includes our co-authored `_get_collection` retry-once via upstream #1377. 7057 tests pass on `main`. The fork's architectural thinking — the four-layer memory model, the [verbatim-vs-derivative thesis](docs/research/verbatim-vs-derivative-axis.md), design principles, and the two-memory-layer pairing with Auto Dream — lives in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md). The new things here are *what we've learned*, not just what we've fixed. @@ -258,132 +258,133 @@ The full enumeration of fork-ahead changes. The canonical source is [`docs/fork- | # | Description | Upstream PR | Fork commit | |---|---|---|---| -| 1 | `mempalace replay` asks the daemon to queue each mine instead of waiting on the palace write lock | — | [`059fe0a`](https://github.com/techempower-org/mempalace/commit/059fe0a) | -| 2 | purge/sync work on Postgres palaces, and a zero-match purge no longer looks like a success | — | [`3b1a2af`](https://github.com/techempower-org/mempalace/commit/3b1a2af) | -| 3 | Auto-query recognizes a session-resumption ask ("where were we", "catch me up") in both the shell pre-filter and the signal set | — | [`c6872494`](https://github.com/techempower-org/mempalace/commit/c6872494) | -| 4 | Post-compaction recovery: clear the auto-query dedupe and re-inject wake-up content, not a pointer | — | [`22858070`](https://github.com/techempower-org/mempalace/commit/22858070) | -| 5 | Every search hit carries source_kind (+ staleness when decidable) and the CLI renders the caveat | — | [`5632dc2`](https://github.com/techempower-org/mempalace/commit/5632dc2) | -| 6 | mempalace mine --mode projects — targeted re-index of one curated document | — | [`787af46`](https://github.com/techempower-org/mempalace/commit/787af46) | -| 7 | Daemon-strict mine refuses to derive a wing from a document path it cannot classify locally | — | [`a30293b`](https://github.com/techempower-org/mempalace/commit/a30293b) | -| 8 | Postgres write path scrubs lone surrogates, nested metadata and ids, not just top-level NULs | — | [`66ca19f`](https://github.com/techempower-org/mempalace/commit/66ca19f) | -| 9 | Wing-scoped vector search no longer returns 0 rows: enable pgvector hnsw.iterative_scan per connection | — | [`2ea774d`](https://github.com/techempower-org/mempalace/commit/2ea774d) | -| 10 | Curated memory-file hits clear a lower confidence floor; harness prompt-echo drawers filtered as exhaust | — | [`31b74d9`](https://github.com/techempower-org/mempalace/commit/31b74d9) | -| 11 | mempalace doctor — one-screen health check of the memory workflow (bridge, daemon, wing, hooks, replay) | — | [`04dde83`](https://github.com/techempower-org/mempalace/commit/04dde83) | -| 12 | Only the MCP-server entrypoints parse argv at import; other importers get defaults (#409) | — | [`e3e0579`](https://github.com/techempower-org/mempalace/commit/e3e0579) | -| 13 | AGE knowledge-graph connection survives a bad query (rollback) and a dead socket (reconnect once) (#405) | — | [`2135df7`](https://github.com/techempower-org/mempalace/commit/2135df7) | -| 14 | Re-mine a transcript incrementally — embed only new or changed chunks; stored drawers are the watermark (#414) | — | [`cf1e997`](https://github.com/techempower-org/mempalace/commit/cf1e997) | -| 15 | Legacy hallways verb deprecated in favour of hallway list; now honours --json (#407) | — | [`1509c32`](https://github.com/techempower-org/mempalace/commit/1509c32) | -| 16 | Replay drops legacy whole-project convos requests instead of re-mining them forever (#426) | — | [`b5b774f`](https://github.com/techempower-org/mempalace/commit/b5b774f) | -| 17 | wake-up L1 skips bookkeeping drawers (AUTO-SAVE, manifests) and ranks curated memory files first (#421 #423) | — | [`a4012dd`](https://github.com/techempower-org/mempalace/commit/a4012dd) | -| 18 | tool_checkpoint forwards session_id to the diary write; declared in both tool schemas (#408) | — | [`e5e31f0`](https://github.com/techempower-org/mempalace/commit/e5e31f0) | -| 19 | Hooks ask the daemon to run checkpoint mines in the background (202 + serial drain) — no more 30s timeouts, journaled replays, and double mines | — | [`79b192b`](https://github.com/techempower-org/mempalace/commit/79b192b) | -| 20 | Thin-vector warning no longer tells pgvector users to rebuild an HNSW index | — | [`43d3a28`](https://github.com/techempower-org/mempalace/commit/43d3a28) | -| 21 | Checkpoint ingest mines THIS transcript, not the whole project dir (#414/#426 mechanism) | — | [`16a5236`](https://github.com/techempower-org/mempalace/commit/16a5236) | -| 22 | Auto-query signal quality: user's-words depth query + wing inventory, peer-block stripping, identifier signals via BM25 fast route, exhaust filter, 0.50 floor, per-session dedupe, curated-first, visible receipts (#419 #420 #422 #424) | — | [`0d92cef`](https://github.com/techempower-org/mempalace/commit/0d92cef) | -| 23 | Sync upstream/develop through v3.9.0 (e8098348): 101 commits — durable config writes, strategy-aware candidate pools, BM25-under-threshold admission, search hub-forward, embeddinggemma batch override, cli_compatible search | — | [`09713d9`](https://github.com/techempower-org/mempalace/commit/09713d9) | -| 24 | CLI wave 3/3: `mempalace diary`, `kg`, `walk` and `rate` first-class verbs (#354, #357, #359, #361) | — | [`ff9ee22`](https://github.com/techempower-org/mempalace/commit/ff9ee22) | -| 25 | CLI wave 2/3: `mempalace wings`, `taxonomy`, `aaak spec`, `hallway`, `checkpoint --dry-run` (#356, #358, #360, #362) | — | [`2848e96`](https://github.com/techempower-org/mempalace/commit/2848e96) | -| 26 | CLI wave 1/3: `mempalace drawer get\|add\|delete\|update` and `duplicate check` (#355, #363) | — | [`836d1a1`](https://github.com/techempower-org/mempalace/commit/836d1a1) | -| 27 | Sync upstream/develop through v3.8.0 (3e56979f): 166 commits — date-window search (since/before), openai-compat embeddings, RFC 002 adapter dispatch (#2062), mcp_proxy entry point, chunk_total metadata; ancestry repaired so future syncs replay only new commits | — | [`b3527b1`](https://github.com/techempower-org/mempalace/commit/b3527b1) | -| 28 | Sync upstream/develop through v3.7.0 (8516db7f): 433 commits — hub forward, temp-collection repair promote, round-trippable drawer IDs, kg_supersede, HNSW CLI fence, 62 conflict files resolved | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 29 | Postgres backend: _coerce_wing write guard — wing-less writes land in 'general' (not the '' column default) and separator/case variants normalize; scripts/wing_hygiene.py migrates the historical rows (dry-run default) | — | [`979da51`](https://github.com/techempower-org/mempalace/commit/979da51) | -| 30 | sync-outline: empty-secret env vars fall back to documented defaults; unreachable nightly cron retired (0-for-41 by construction) — workflow_dispatch stays | — | [`5227da4`](https://github.com/techempower-org/mempalace/commit/5227da4) | -| 31 | Searcher: restore mempalace_search on postgres — guard capability-path conversion in union merge (UnboundLocalError since the 2026-07-02 sync) | — | [`96f83d7`](https://github.com/techempower-org/mempalace/commit/96f83d7) | -| 32 | Auto-query: TTL cache for the deterministic depth-refresh injection — repeat fires 0ms vs ~850ms daemon round-trip | — | [`be903e6`](https://github.com/techempower-org/mempalace/commit/be903e6) | -| 33 | Sync upstream/develop through da5a48c (post-v3.5.0): remote MCP server w/ TLS + read-only, graph auto-population, Qdrant facets, list_drawers date filters, 213 commits | — | [`b46f18d`](https://github.com/techempower-org/mempalace/commit/b46f18d) | -| 34 | Auto-query firing fixes: frozen turn counter, dead wing scoring, lowercase entities, turn-1 cadence | — | [`fad3e27`](https://github.com/techempower-org/mempalace/commit/fad3e27) | -| 35 | Auto-query: periodic depth signal, unknown-entity 0->1 bump, broader temporal patterns | — | [`864d7a4`](https://github.com/techempower-org/mempalace/commit/864d7a4) | -| 36 | Sync upstream/develop through v3.5.0 (73e74bf): MCP HTTP transport, source_file filter, checkpoint tool, SessionEnd hook, 185 commits | — | [`8711e1c`](https://github.com/techempower-org/mempalace/commit/8711e1c) | -| 37 | Restore concurrent file mining via parallel-prepare/serial-write (regression from a dropped sync hunk); opt-in --workers | — | [`42a107b`](https://github.com/techempower-org/mempalace/commit/42a107b) | -| 38 | Sync upstream/develop through v3.4.0 (2ec4bae): RFC-001 backend stack, diary checkpoints restored, 113 commits | — | [`373fdf2`](https://github.com/techempower-org/mempalace/commit/373fdf2) | -| 39 | auto_wake: opt-in wake-on-demand for a sleeping palace-daemon host (wake command + /health poll + single retry) | — | [`8e0d896`](https://github.com/techempower-org/mempalace/commit/8e0d896) | -| 40 | AGE graph-walk: auto edge-endpoint indexes in backfill + bind anonymous RELATION targets (mempalace#335) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 41 | pluggable adaptmem_ft encoder backend selectable via MEMPALACE_EMBEDDING_MODEL (closes #308) | — | [`5fba6d8`](https://github.com/techempower-org/mempalace/commit/5fba6d8) | -| 42 | README.md landscape table — refresh upstream MemPalace star count from ~23K → ~53K (current 2026-05-28) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 43 | README.md + docs/ECOSYSTEM.md — soften 'engram-2 17% E2E QA' framing per the 2026-05-24 research doc's unsubstantiated finding (#319) | — | [`ddf00b4`](https://github.com/techempower-org/mempalace/commit/ddf00b4) | -| 44 | kg_llm_extractor rewrites AGE dollar-quote tag in triples so drawers indexing palace source code don't fail at add_triple (#313) | — | [`3fb9428`](https://github.com/techempower-org/mempalace/commit/3fb9428) | -| 45 | scripts/maintain-fork-changes.py + ship-prep step 1: resolve commit:HEAD placeholders and de-dup yaml entries (#316) | — | [`9060e09`](https://github.com/techempower-org/mempalace/commit/9060e09) | -| 46 | scripts/ship-prep.sh — one command bumps README test count and runs all three doc renderers (#312) | — | [`4677db8`](https://github.com/techempower-org/mempalace/commit/4677db8) | -| 47 | mempalace_search MCP input schema accepts fusion_mode (convex\|rrf) and forwards to search_memories (#302) | — | [`f753ec4`](https://github.com/techempower-org/mempalace/commit/f753ec4) | -| 48 | scripts/check-docs.sh finds pytest via main checkout when run from a worktree, fails hard instead of silently skipping test-count check (#311) | — | [`1d19a8b`](https://github.com/techempower-org/mempalace/commit/1d19a8b) | -| 49 | kg_triple_worker retries add_triple within-worker on transient psycopg errors instead of abandoning to lease-reclaim (#298) | — | [`36c0b02`](https://github.com/techempower-org/mempalace/commit/36c0b02) | -| 50 | mempalace_kg_stats returns structured backend-unavailable envelope on transient psycopg failures (#299) | — | [`8fd0b01`](https://github.com/techempower-org/mempalace/commit/8fd0b01) | -| 51 | mempalace why + tunnels — explain a drawer + inventory cross-wing tunnels (slice of #191) | — | [`fdcd0b4`](https://github.com/techempower-org/mempalace/commit/fdcd0b4) | -| 52 | RRF vs convex-blend rerank — A/B measurement on our corpus (#162) | — | [`ea5d567`](https://github.com/techempower-org/mempalace/commit/ea5d567) | -| 53 | KG triples gain SPOC context slot + worker auto-derives valid_from from drawer metadata (#161) | — | [`b87ce05`](https://github.com/techempower-org/mempalace/commit/b87ce05) | -| 54 | mempalace bulk-move — multi-drawer metadata relocation by source wing/room (#191) | — | [`1ca544b`](https://github.com/techempower-org/mempalace/commit/1ca544b) | -| 55 | mempalace move — fast direct-to-daemon single-drawer wing/room relocation (#191) | — | [`d007b6f`](https://github.com/techempower-org/mempalace/commit/d007b6f) | -| 56 | mempalace stats migrates to GET /stats REST + exposes graph/status sections (#191) | — | [`853bb25`](https://github.com/techempower-org/mempalace/commit/853bb25) | -| 57 | mempalace cypher — read-only Cypher query CLI (#191) | — | [`32a41b1`](https://github.com/techempower-org/mempalace/commit/32a41b1) | -| 58 | mempalace graph — fast direct-to-daemon KG structural snapshot (#191) | — | [`499f42d`](https://github.com/techempower-org/mempalace/commit/499f42d) | -| 59 | mempalace list — fast direct-to-daemon drawer browser (#191) | — | [`257137b`](https://github.com/techempower-org/mempalace/commit/257137b) | -| 60 | Recency decay weighting in search + mempalace prune --stale-days CLI (#158) | — | [`558d327`](https://github.com/techempower-org/mempalace/commit/558d327) | -| 61 | mempalace_rate_memory MCP tool + bounded rating signal in search ranking (#159) | — | [`583536c`](https://github.com/techempower-org/mempalace/commit/583536c) | -| 62 | Formalize wing/room derivation order; demote entity detector to last-resort hint (#157) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 63 | RRF fusion mode + convex-vs-RRF A/B harness (#162) | [#247](https://github.com/MemPalace/mempalace/pull/247) | [`6c9d10c`](https://github.com/techempower-org/mempalace/commit/6c9d10c) | -| 64 | mempalace stats: add ROOMS breakdown (drawer count by room) to the dashboard | — | [`1673465`](https://github.com/techempower-org/mempalace/commit/1673465) | -| 65 | Calibrated confidence field on search results + Brier-score eval column | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 66 | Evaluation doc: curated-authority vs auto-mined separation (#202) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 67 | Apply AGE statement_timeout in same transaction as cypher() (PR #228 follow-up) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 68 | LLM-based KG triple extraction: queue table, async worker, llama.cpp on familiar | — | [`59ac0bc`](https://github.com/techempower-org/mempalace/commit/59ac0bc) | -| 69 | Promote verbatim-vs-derivative essay from research/ to README (#170) | — | [`6a264d9`](https://github.com/techempower-org/mempalace/commit/6a264d9) | -| 70 | mempalace stats — palace analytics dashboard (#191) | — | [`6f994fb`](https://github.com/techempower-org/mempalace/commit/6f994fb) | -| 71 | CLI wiring: mempalace mine --source (#57) | — | [`5ed9fa7`](https://github.com/techempower-org/mempalace/commit/5ed9fa7) | -| 72 | Warp terminal source adapter (#62) | — | [`2e85585`](https://github.com/techempower-org/mempalace/commit/2e85585) | -| 73 | OpenCode adapter smoke test against real DB (#56) | — | [`a9ed72b`](https://github.com/techempower-org/mempalace/commit/a9ed72b) | -| 74 | Codex, Gemini, and Aider source adapters (#61, #59) | — | [`0c23165`](https://github.com/techempower-org/mempalace/commit/0c23165) | -| 75 | Filesystem + conversation source adapters (#63) | — | [`9a1facf`](https://github.com/techempower-org/mempalace/commit/9a1facf) | -| 76 | Widen auto-query signal patterns for natural recall phrases | — | [`33e780e`](https://github.com/techempower-org/mempalace/commit/33e780e) | -| 77 | Native rename_wing backend operation + CLI command (#154) | — | [`d045f83`](https://github.com/techempower-org/mempalace/commit/d045f83) | -| 78 | Standalone essay: the verbatim-vs-derivative axis (#47) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 79 | Research doc: uncertainty-aware retrieval analysis (#84) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 80 | Design doc: scope/collection filter on mempalace_search (#76) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 81 | Agent-shaped CLI surface — --json / --quiet for non-MCP integration | — | [`25ed900`](https://github.com/techempower-org/mempalace/commit/25ed900) | -| 82 | Design eval: multi-palace separation — curated vs auto-mined (#45) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 83 | Document .sh shim delegation to palace-daemon (counter-position to upstream #1069) | — | [`bf0a4d0`](https://github.com/techempower-org/mempalace/commit/bf0a4d0) | -| 84 | Honor ~/.mempalace/RETIRED marker — refuse default palace, surface retire message | — | [`798cf14`](https://github.com/techempower-org/mempalace/commit/798cf14) | -| 85 | Empty repo .opencode/opencode.json mcp block — disabled flag wasn't being respected | — | [`7133eee`](https://github.com/techempower-org/mempalace/commit/7133eee) | -| 86 | Drop \$comment from .opencode/opencode.json — schema rejects unknown root keys | — | [`637bb01`](https://github.com/techempower-org/mempalace/commit/637bb01) | -| 87 | Disable repo-level MCP entry by default + venv-python fallback | — | [`47018e5`](https://github.com/techempower-org/mempalace/commit/47018e5) | -| 88 | Stub resources/list + prompts/list so MCP clients stop ERROR-logging on connect | — | [`6ca0670`](https://github.com/techempower-org/mempalace/commit/6ca0670) | -| 89 | Bundled OpenCode live-capture plugin that bypasses option-K v1.2.1 bugs (filed upstream as #4, #5) | — | [`5522623`](https://github.com/techempower-org/mempalace/commit/5522623) | -| 90 | Documented OpenCode integration recipe (read-side MCP + push plugin + retrospective adapter) | — | [`60dc9e6`](https://github.com/techempower-org/mempalace/commit/60dc9e6) | -| 91 | .opencode/opencode.json — repo-root MCP config so opencode picks up mempalace automatically | [#1567](https://github.com/MemPalace/mempalace/pull/1567) (CLOSED) | [`ba16b82`](https://github.com/techempower-org/mempalace/commit/ba16b82) | -| 92 | OpenCodeSourceAdapter (RFC 002) — retrospective ingest of OpenCode SQLite sessions | [#1484](https://github.com/MemPalace/mempalace/pull/1484) (OPEN) | [`2ffe652`](https://github.com/techempower-org/mempalace/commit/2ffe652) | -| 93 | mempalace_walk_palace MCP tool — agent walks the palace via AGE Cypher | — | [`8022ecb`](https://github.com/techempower-org/mempalace/commit/8022ecb) | -| 94 | Backfill AGE graph from existing drawer table — restartable, checkpointed | — | [`b3f0206`](https://github.com/techempower-org/mempalace/commit/b3f0206) | -| 95 | Wing/Room/Drawer hierarchy as native AGE nodes; Cypher MATCH walks palace structure | — | [`ff583c0`](https://github.com/techempower-org/mempalace/commit/ff583c0) | -| 96 | Write-through middleware on PostgresCollection — entities populate AGE on every drawer write | — | [`3321d83`](https://github.com/techempower-org/mempalace/commit/3321d83) | -| 97 | KnowledgeGraphAGE API parity with SQLite KG: add_entity, invalidate, query_entity, query_relationship, timeline, seed_from_entity_facts | — | [`ff7187d`](https://github.com/techempower-org/mempalace/commit/ff7187d) | -| 98 | Pending-writes journal + replay so daemon outages stop being silent | — | [`0c34464`](https://github.com/techempower-org/mempalace/commit/0c34464) | -| 99 | MCP server distinguishes 'backend unreachable' from 'no palace found' | — | [`0c34464`](https://github.com/techempower-org/mempalace/commit/0c34464) | -| 100 | Defense-in-depth metadata sanitizer at the chromadb-client chokepoint | — | [`f499814`](https://github.com/techempower-org/mempalace/commit/f499814) | -| 101 | Route Stop/PreCompact hooks through palace-daemon/clients/hook.py | — | [`42ded2e`](https://github.com/techempower-org/mempalace/commit/42ded2e) | -| 102 | KnowledgeGraphAGE skeleton — Apache AGE graph bootstrap over psycopg2 | — | [`a3ee623`](https://github.com/techempower-org/mempalace/commit/a3ee623) | -| 103 | README pivots to the four-layer model + Auto Dream as vindication of the verbatim-vs-derivative axis | — | [`55b36ca`](https://github.com/techempower-org/mempalace/commit/55b36ca) | -| 104 | CI: gate postgres-backend tests against a pgvector service container | — | [`da0bdbb`](https://github.com/techempower-org/mempalace/commit/da0bdbb) | -| 105 | PostgreSQL backend via #665 cherry-pick + fork-side adaptations + smoke tests | [#665](https://github.com/MemPalace/mempalace/pull/665) (OPEN) | [`5e90c72`](https://github.com/techempower-org/mempalace/commit/5e90c72) | -| 106 | daemon-route `mempalace status` / `search` / `mine` when PALACE_DAEMON_URL is set | — | [`22ef562`](https://github.com/techempower-org/mempalace/commit/22ef562) | -| 107 | daemon-route `mcp_server.py` via the `handle_request` JSON-RPC chokepoint | — | [`41359ba`](https://github.com/techempower-org/mempalace/commit/41359ba) | -| 108 | Preserve dashed project names in transcript-derived wings | [#10](https://github.com/MemPalace/mempalace/pull/10) | [`d76134d`](https://github.com/techempower-org/mempalace/commit/d76134d) | -| 109 | Drop wing_ prefix from transcript-derived wings to converge with operator mines | [#9](https://github.com/MemPalace/mempalace/pull/9) | [`86d4700`](https://github.com/techempower-org/mempalace/commit/86d4700) | -| 110 | Retire mempalace_session_recovery collection + read tool | [#8](https://github.com/MemPalace/mempalace/pull/8) | [`0b945e1`](https://github.com/techempower-org/mempalace/commit/0b945e1) | -| 111 | mempalace mined + purge --source-file (mining management surface) | [#7](https://github.com/MemPalace/mempalace/pull/7) | [`2e6ced9`](https://github.com/techempower-org/mempalace/commit/2e6ced9) | -| 112 | Drop hook-side checkpoint diary writes — verbatim-only architecture | [#6](https://github.com/MemPalace/mempalace/pull/6) | [`69768fc`](https://github.com/techempower-org/mempalace/commit/69768fc) | -| 113 | Restore transcript ingest via daemon /mine when PALACE_DAEMON_URL is set | [#2](https://github.com/MemPalace/mempalace/pull/2) | [`09d2ca6`](https://github.com/techempower-org/mempalace/commit/09d2ca6) | -| 114 | `hook_verbatim_mode` config flag preserves system tags + full tool I/O during transcript ingest | — | [`ef98961`](https://github.com/techempower-org/mempalace/commit/ef98961) | -| 115 | Retire the `kind=` filter — structural split made it inert | — | [`7ba28dc`](https://github.com/techempower-org/mempalace/commit/7ba28dc) | -| 116 | Hoist CLOSET_RANK_BOOSTS to module level + record VecRecall ablation finding | — | [`3cb03f3`](https://github.com/techempower-org/mempalace/commit/3cb03f3) | -| 117 | Strip embedded API key from .claude-plugin/ manifests; rely on env inheritance | — | [`9f91e18`](https://github.com/techempower-org/mempalace/commit/9f91e18) | -| 118 | Cherry-pick #1094 — coerce None metadatas at chromadb boundary | [#1094](https://github.com/MemPalace/mempalace/pull/1094) (OPEN) | [`43d728d`](https://github.com/techempower-org/mempalace/commit/43d728d) | -| 119 | Cherry-pick #1087 rewrite — collection.delete(where=) instead of nuke-and-rebuild | [#1087](https://github.com/MemPalace/mempalace/pull/1087) (OPEN) | [`366a9ad`](https://github.com/techempower-org/mempalace/commit/366a9ad) | -| 120 | Canonical YAML manifest + renderer for fork-ahead docs | — | [`5a01aec`](https://github.com/techempower-org/mempalace/commit/5a01aec) | -| 121 | Phase D migration + PreCompact recovery write | — | [`42817d7`](https://github.com/techempower-org/mempalace/commit/42817d7) | -| 122 | Surface drawer_id in search/diary/recovery payloads | — | [`9a8bb77`](https://github.com/techempower-org/mempalace/commit/9a8bb77) | -| 123 | Cherry-pick #1085 — batch ChromaDB inserts in miner (10–30× faster) | [#1085](https://github.com/MemPalace/mempalace/pull/1085) (CLOSED) | [`6be6fff`](https://github.com/techempower-org/mempalace/commit/6be6fff) | -| 124 | scripts/deploy.sh — one-command Syncthing-aware redeploy | — | [`8252025`](https://github.com/techempower-org/mempalace/commit/8252025) | -| 125 | Phases A–C of the checkpoint collection split | — | [`e266365`](https://github.com/techempower-org/mempalace/commit/e266365) | -| 126 | kind= filter on search_memories excludes Stop-hook checkpoints (transitional) | — | [`f9f5cc4`](https://github.com/techempower-org/mempalace/commit/f9f5cc4) | +| 1 | Cached postgres connection reconnects once after a server-side disconnect instead of raising raw | — | [`4d15803`](https://github.com/techempower-org/mempalace/commit/4d15803) | +| 2 | `mempalace replay` asks the daemon to queue each mine instead of waiting on the palace write lock | — | [`059fe0a`](https://github.com/techempower-org/mempalace/commit/059fe0a) | +| 3 | purge/sync work on Postgres palaces, and a zero-match purge no longer looks like a success | — | [`3b1a2af`](https://github.com/techempower-org/mempalace/commit/3b1a2af) | +| 4 | Auto-query recognizes a session-resumption ask ("where were we", "catch me up") in both the shell pre-filter and the signal set | — | [`c6872494`](https://github.com/techempower-org/mempalace/commit/c6872494) | +| 5 | Post-compaction recovery: clear the auto-query dedupe and re-inject wake-up content, not a pointer | — | [`22858070`](https://github.com/techempower-org/mempalace/commit/22858070) | +| 6 | Every search hit carries source_kind (+ staleness when decidable) and the CLI renders the caveat | — | [`5632dc2`](https://github.com/techempower-org/mempalace/commit/5632dc2) | +| 7 | mempalace mine --mode projects — targeted re-index of one curated document | — | [`787af46`](https://github.com/techempower-org/mempalace/commit/787af46) | +| 8 | Daemon-strict mine refuses to derive a wing from a document path it cannot classify locally | — | [`a30293b`](https://github.com/techempower-org/mempalace/commit/a30293b) | +| 9 | Postgres write path scrubs lone surrogates, nested metadata and ids, not just top-level NULs | — | [`66ca19f`](https://github.com/techempower-org/mempalace/commit/66ca19f) | +| 10 | Wing-scoped vector search no longer returns 0 rows: enable pgvector hnsw.iterative_scan per connection | — | [`2ea774d`](https://github.com/techempower-org/mempalace/commit/2ea774d) | +| 11 | Curated memory-file hits clear a lower confidence floor; harness prompt-echo drawers filtered as exhaust | — | [`31b74d9`](https://github.com/techempower-org/mempalace/commit/31b74d9) | +| 12 | mempalace doctor — one-screen health check of the memory workflow (bridge, daemon, wing, hooks, replay) | — | [`04dde83`](https://github.com/techempower-org/mempalace/commit/04dde83) | +| 13 | Only the MCP-server entrypoints parse argv at import; other importers get defaults (#409) | — | [`e3e0579`](https://github.com/techempower-org/mempalace/commit/e3e0579) | +| 14 | AGE knowledge-graph connection survives a bad query (rollback) and a dead socket (reconnect once) (#405) | — | [`2135df7`](https://github.com/techempower-org/mempalace/commit/2135df7) | +| 15 | Re-mine a transcript incrementally — embed only new or changed chunks; stored drawers are the watermark (#414) | — | [`cf1e997`](https://github.com/techempower-org/mempalace/commit/cf1e997) | +| 16 | Legacy hallways verb deprecated in favour of hallway list; now honours --json (#407) | — | [`1509c32`](https://github.com/techempower-org/mempalace/commit/1509c32) | +| 17 | Replay drops legacy whole-project convos requests instead of re-mining them forever (#426) | — | [`b5b774f`](https://github.com/techempower-org/mempalace/commit/b5b774f) | +| 18 | wake-up L1 skips bookkeeping drawers (AUTO-SAVE, manifests) and ranks curated memory files first (#421 #423) | — | [`a4012dd`](https://github.com/techempower-org/mempalace/commit/a4012dd) | +| 19 | tool_checkpoint forwards session_id to the diary write; declared in both tool schemas (#408) | — | [`e5e31f0`](https://github.com/techempower-org/mempalace/commit/e5e31f0) | +| 20 | Hooks ask the daemon to run checkpoint mines in the background (202 + serial drain) — no more 30s timeouts, journaled replays, and double mines | — | [`79b192b`](https://github.com/techempower-org/mempalace/commit/79b192b) | +| 21 | Thin-vector warning no longer tells pgvector users to rebuild an HNSW index | — | [`43d3a28`](https://github.com/techempower-org/mempalace/commit/43d3a28) | +| 22 | Checkpoint ingest mines THIS transcript, not the whole project dir (#414/#426 mechanism) | — | [`16a5236`](https://github.com/techempower-org/mempalace/commit/16a5236) | +| 23 | Auto-query signal quality: user's-words depth query + wing inventory, peer-block stripping, identifier signals via BM25 fast route, exhaust filter, 0.50 floor, per-session dedupe, curated-first, visible receipts (#419 #420 #422 #424) | — | [`0d92cef`](https://github.com/techempower-org/mempalace/commit/0d92cef) | +| 24 | Sync upstream/develop through v3.9.0 (e8098348): 101 commits — durable config writes, strategy-aware candidate pools, BM25-under-threshold admission, search hub-forward, embeddinggemma batch override, cli_compatible search | — | [`09713d9`](https://github.com/techempower-org/mempalace/commit/09713d9) | +| 25 | CLI wave 3/3: `mempalace diary`, `kg`, `walk` and `rate` first-class verbs (#354, #357, #359, #361) | — | [`ff9ee22`](https://github.com/techempower-org/mempalace/commit/ff9ee22) | +| 26 | CLI wave 2/3: `mempalace wings`, `taxonomy`, `aaak spec`, `hallway`, `checkpoint --dry-run` (#356, #358, #360, #362) | — | [`2848e96`](https://github.com/techempower-org/mempalace/commit/2848e96) | +| 27 | CLI wave 1/3: `mempalace drawer get\|add\|delete\|update` and `duplicate check` (#355, #363) | — | [`836d1a1`](https://github.com/techempower-org/mempalace/commit/836d1a1) | +| 28 | Sync upstream/develop through v3.8.0 (3e56979f): 166 commits — date-window search (since/before), openai-compat embeddings, RFC 002 adapter dispatch (#2062), mcp_proxy entry point, chunk_total metadata; ancestry repaired so future syncs replay only new commits | — | [`b3527b1`](https://github.com/techempower-org/mempalace/commit/b3527b1) | +| 29 | Sync upstream/develop through v3.7.0 (8516db7f): 433 commits — hub forward, temp-collection repair promote, round-trippable drawer IDs, kg_supersede, HNSW CLI fence, 62 conflict files resolved | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 30 | Postgres backend: _coerce_wing write guard — wing-less writes land in 'general' (not the '' column default) and separator/case variants normalize; scripts/wing_hygiene.py migrates the historical rows (dry-run default) | — | [`979da51`](https://github.com/techempower-org/mempalace/commit/979da51) | +| 31 | sync-outline: empty-secret env vars fall back to documented defaults; unreachable nightly cron retired (0-for-41 by construction) — workflow_dispatch stays | — | [`5227da4`](https://github.com/techempower-org/mempalace/commit/5227da4) | +| 32 | Searcher: restore mempalace_search on postgres — guard capability-path conversion in union merge (UnboundLocalError since the 2026-07-02 sync) | — | [`96f83d7`](https://github.com/techempower-org/mempalace/commit/96f83d7) | +| 33 | Auto-query: TTL cache for the deterministic depth-refresh injection — repeat fires 0ms vs ~850ms daemon round-trip | — | [`be903e6`](https://github.com/techempower-org/mempalace/commit/be903e6) | +| 34 | Sync upstream/develop through da5a48c (post-v3.5.0): remote MCP server w/ TLS + read-only, graph auto-population, Qdrant facets, list_drawers date filters, 213 commits | — | [`b46f18d`](https://github.com/techempower-org/mempalace/commit/b46f18d) | +| 35 | Auto-query firing fixes: frozen turn counter, dead wing scoring, lowercase entities, turn-1 cadence | — | [`fad3e27`](https://github.com/techempower-org/mempalace/commit/fad3e27) | +| 36 | Auto-query: periodic depth signal, unknown-entity 0->1 bump, broader temporal patterns | — | [`864d7a4`](https://github.com/techempower-org/mempalace/commit/864d7a4) | +| 37 | Sync upstream/develop through v3.5.0 (73e74bf): MCP HTTP transport, source_file filter, checkpoint tool, SessionEnd hook, 185 commits | — | [`8711e1c`](https://github.com/techempower-org/mempalace/commit/8711e1c) | +| 38 | Restore concurrent file mining via parallel-prepare/serial-write (regression from a dropped sync hunk); opt-in --workers | — | [`42a107b`](https://github.com/techempower-org/mempalace/commit/42a107b) | +| 39 | Sync upstream/develop through v3.4.0 (2ec4bae): RFC-001 backend stack, diary checkpoints restored, 113 commits | — | [`373fdf2`](https://github.com/techempower-org/mempalace/commit/373fdf2) | +| 40 | auto_wake: opt-in wake-on-demand for a sleeping palace-daemon host (wake command + /health poll + single retry) | — | [`8e0d896`](https://github.com/techempower-org/mempalace/commit/8e0d896) | +| 41 | AGE graph-walk: auto edge-endpoint indexes in backfill + bind anonymous RELATION targets (mempalace#335) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 42 | pluggable adaptmem_ft encoder backend selectable via MEMPALACE_EMBEDDING_MODEL (closes #308) | — | [`5fba6d8`](https://github.com/techempower-org/mempalace/commit/5fba6d8) | +| 43 | README.md landscape table — refresh upstream MemPalace star count from ~23K → ~53K (current 2026-05-28) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 44 | README.md + docs/ECOSYSTEM.md — soften 'engram-2 17% E2E QA' framing per the 2026-05-24 research doc's unsubstantiated finding (#319) | — | [`ddf00b4`](https://github.com/techempower-org/mempalace/commit/ddf00b4) | +| 45 | kg_llm_extractor rewrites AGE dollar-quote tag in triples so drawers indexing palace source code don't fail at add_triple (#313) | — | [`3fb9428`](https://github.com/techempower-org/mempalace/commit/3fb9428) | +| 46 | scripts/maintain-fork-changes.py + ship-prep step 1: resolve commit:HEAD placeholders and de-dup yaml entries (#316) | — | [`9060e09`](https://github.com/techempower-org/mempalace/commit/9060e09) | +| 47 | scripts/ship-prep.sh — one command bumps README test count and runs all three doc renderers (#312) | — | [`4677db8`](https://github.com/techempower-org/mempalace/commit/4677db8) | +| 48 | mempalace_search MCP input schema accepts fusion_mode (convex\|rrf) and forwards to search_memories (#302) | — | [`f753ec4`](https://github.com/techempower-org/mempalace/commit/f753ec4) | +| 49 | scripts/check-docs.sh finds pytest via main checkout when run from a worktree, fails hard instead of silently skipping test-count check (#311) | — | [`1d19a8b`](https://github.com/techempower-org/mempalace/commit/1d19a8b) | +| 50 | kg_triple_worker retries add_triple within-worker on transient psycopg errors instead of abandoning to lease-reclaim (#298) | — | [`36c0b02`](https://github.com/techempower-org/mempalace/commit/36c0b02) | +| 51 | mempalace_kg_stats returns structured backend-unavailable envelope on transient psycopg failures (#299) | — | [`8fd0b01`](https://github.com/techempower-org/mempalace/commit/8fd0b01) | +| 52 | mempalace why + tunnels — explain a drawer + inventory cross-wing tunnels (slice of #191) | — | [`fdcd0b4`](https://github.com/techempower-org/mempalace/commit/fdcd0b4) | +| 53 | RRF vs convex-blend rerank — A/B measurement on our corpus (#162) | — | [`ea5d567`](https://github.com/techempower-org/mempalace/commit/ea5d567) | +| 54 | KG triples gain SPOC context slot + worker auto-derives valid_from from drawer metadata (#161) | — | [`b87ce05`](https://github.com/techempower-org/mempalace/commit/b87ce05) | +| 55 | mempalace bulk-move — multi-drawer metadata relocation by source wing/room (#191) | — | [`1ca544b`](https://github.com/techempower-org/mempalace/commit/1ca544b) | +| 56 | mempalace move — fast direct-to-daemon single-drawer wing/room relocation (#191) | — | [`d007b6f`](https://github.com/techempower-org/mempalace/commit/d007b6f) | +| 57 | mempalace stats migrates to GET /stats REST + exposes graph/status sections (#191) | — | [`853bb25`](https://github.com/techempower-org/mempalace/commit/853bb25) | +| 58 | mempalace cypher — read-only Cypher query CLI (#191) | — | [`32a41b1`](https://github.com/techempower-org/mempalace/commit/32a41b1) | +| 59 | mempalace graph — fast direct-to-daemon KG structural snapshot (#191) | — | [`499f42d`](https://github.com/techempower-org/mempalace/commit/499f42d) | +| 60 | mempalace list — fast direct-to-daemon drawer browser (#191) | — | [`257137b`](https://github.com/techempower-org/mempalace/commit/257137b) | +| 61 | Recency decay weighting in search + mempalace prune --stale-days CLI (#158) | — | [`558d327`](https://github.com/techempower-org/mempalace/commit/558d327) | +| 62 | mempalace_rate_memory MCP tool + bounded rating signal in search ranking (#159) | — | [`583536c`](https://github.com/techempower-org/mempalace/commit/583536c) | +| 63 | Formalize wing/room derivation order; demote entity detector to last-resort hint (#157) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 64 | RRF fusion mode + convex-vs-RRF A/B harness (#162) | [#247](https://github.com/MemPalace/mempalace/pull/247) | [`6c9d10c`](https://github.com/techempower-org/mempalace/commit/6c9d10c) | +| 65 | mempalace stats: add ROOMS breakdown (drawer count by room) to the dashboard | — | [`1673465`](https://github.com/techempower-org/mempalace/commit/1673465) | +| 66 | Calibrated confidence field on search results + Brier-score eval column | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 67 | Evaluation doc: curated-authority vs auto-mined separation (#202) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 68 | Apply AGE statement_timeout in same transaction as cypher() (PR #228 follow-up) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 69 | LLM-based KG triple extraction: queue table, async worker, llama.cpp on familiar | — | [`59ac0bc`](https://github.com/techempower-org/mempalace/commit/59ac0bc) | +| 70 | Promote verbatim-vs-derivative essay from research/ to README (#170) | — | [`6a264d9`](https://github.com/techempower-org/mempalace/commit/6a264d9) | +| 71 | mempalace stats — palace analytics dashboard (#191) | — | [`6f994fb`](https://github.com/techempower-org/mempalace/commit/6f994fb) | +| 72 | CLI wiring: mempalace mine --source (#57) | — | [`5ed9fa7`](https://github.com/techempower-org/mempalace/commit/5ed9fa7) | +| 73 | Warp terminal source adapter (#62) | — | [`2e85585`](https://github.com/techempower-org/mempalace/commit/2e85585) | +| 74 | OpenCode adapter smoke test against real DB (#56) | — | [`a9ed72b`](https://github.com/techempower-org/mempalace/commit/a9ed72b) | +| 75 | Codex, Gemini, and Aider source adapters (#61, #59) | — | [`0c23165`](https://github.com/techempower-org/mempalace/commit/0c23165) | +| 76 | Filesystem + conversation source adapters (#63) | — | [`9a1facf`](https://github.com/techempower-org/mempalace/commit/9a1facf) | +| 77 | Widen auto-query signal patterns for natural recall phrases | — | [`33e780e`](https://github.com/techempower-org/mempalace/commit/33e780e) | +| 78 | Native rename_wing backend operation + CLI command (#154) | — | [`d045f83`](https://github.com/techempower-org/mempalace/commit/d045f83) | +| 79 | Standalone essay: the verbatim-vs-derivative axis (#47) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 80 | Research doc: uncertainty-aware retrieval analysis (#84) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 81 | Design doc: scope/collection filter on mempalace_search (#76) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 82 | Agent-shaped CLI surface — --json / --quiet for non-MCP integration | — | [`25ed900`](https://github.com/techempower-org/mempalace/commit/25ed900) | +| 83 | Design eval: multi-palace separation — curated vs auto-mined (#45) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 84 | Document .sh shim delegation to palace-daemon (counter-position to upstream #1069) | — | [`bf0a4d0`](https://github.com/techempower-org/mempalace/commit/bf0a4d0) | +| 85 | Honor ~/.mempalace/RETIRED marker — refuse default palace, surface retire message | — | [`798cf14`](https://github.com/techempower-org/mempalace/commit/798cf14) | +| 86 | Empty repo .opencode/opencode.json mcp block — disabled flag wasn't being respected | — | [`7133eee`](https://github.com/techempower-org/mempalace/commit/7133eee) | +| 87 | Drop \$comment from .opencode/opencode.json — schema rejects unknown root keys | — | [`637bb01`](https://github.com/techempower-org/mempalace/commit/637bb01) | +| 88 | Disable repo-level MCP entry by default + venv-python fallback | — | [`47018e5`](https://github.com/techempower-org/mempalace/commit/47018e5) | +| 89 | Stub resources/list + prompts/list so MCP clients stop ERROR-logging on connect | — | [`6ca0670`](https://github.com/techempower-org/mempalace/commit/6ca0670) | +| 90 | Bundled OpenCode live-capture plugin that bypasses option-K v1.2.1 bugs (filed upstream as #4, #5) | — | [`5522623`](https://github.com/techempower-org/mempalace/commit/5522623) | +| 91 | Documented OpenCode integration recipe (read-side MCP + push plugin + retrospective adapter) | — | [`60dc9e6`](https://github.com/techempower-org/mempalace/commit/60dc9e6) | +| 92 | .opencode/opencode.json — repo-root MCP config so opencode picks up mempalace automatically | [#1567](https://github.com/MemPalace/mempalace/pull/1567) (CLOSED) | [`ba16b82`](https://github.com/techempower-org/mempalace/commit/ba16b82) | +| 93 | OpenCodeSourceAdapter (RFC 002) — retrospective ingest of OpenCode SQLite sessions | [#1484](https://github.com/MemPalace/mempalace/pull/1484) (OPEN) | [`2ffe652`](https://github.com/techempower-org/mempalace/commit/2ffe652) | +| 94 | mempalace_walk_palace MCP tool — agent walks the palace via AGE Cypher | — | [`8022ecb`](https://github.com/techempower-org/mempalace/commit/8022ecb) | +| 95 | Backfill AGE graph from existing drawer table — restartable, checkpointed | — | [`b3f0206`](https://github.com/techempower-org/mempalace/commit/b3f0206) | +| 96 | Wing/Room/Drawer hierarchy as native AGE nodes; Cypher MATCH walks palace structure | — | [`ff583c0`](https://github.com/techempower-org/mempalace/commit/ff583c0) | +| 97 | Write-through middleware on PostgresCollection — entities populate AGE on every drawer write | — | [`3321d83`](https://github.com/techempower-org/mempalace/commit/3321d83) | +| 98 | KnowledgeGraphAGE API parity with SQLite KG: add_entity, invalidate, query_entity, query_relationship, timeline, seed_from_entity_facts | — | [`ff7187d`](https://github.com/techempower-org/mempalace/commit/ff7187d) | +| 99 | Pending-writes journal + replay so daemon outages stop being silent | — | [`0c34464`](https://github.com/techempower-org/mempalace/commit/0c34464) | +| 100 | MCP server distinguishes 'backend unreachable' from 'no palace found' | — | [`0c34464`](https://github.com/techempower-org/mempalace/commit/0c34464) | +| 101 | Defense-in-depth metadata sanitizer at the chromadb-client chokepoint | — | [`f499814`](https://github.com/techempower-org/mempalace/commit/f499814) | +| 102 | Route Stop/PreCompact hooks through palace-daemon/clients/hook.py | — | [`42ded2e`](https://github.com/techempower-org/mempalace/commit/42ded2e) | +| 103 | KnowledgeGraphAGE skeleton — Apache AGE graph bootstrap over psycopg2 | — | [`a3ee623`](https://github.com/techempower-org/mempalace/commit/a3ee623) | +| 104 | README pivots to the four-layer model + Auto Dream as vindication of the verbatim-vs-derivative axis | — | [`55b36ca`](https://github.com/techempower-org/mempalace/commit/55b36ca) | +| 105 | CI: gate postgres-backend tests against a pgvector service container | — | [`da0bdbb`](https://github.com/techempower-org/mempalace/commit/da0bdbb) | +| 106 | PostgreSQL backend via #665 cherry-pick + fork-side adaptations + smoke tests | [#665](https://github.com/MemPalace/mempalace/pull/665) (OPEN) | [`5e90c72`](https://github.com/techempower-org/mempalace/commit/5e90c72) | +| 107 | daemon-route `mempalace status` / `search` / `mine` when PALACE_DAEMON_URL is set | — | [`22ef562`](https://github.com/techempower-org/mempalace/commit/22ef562) | +| 108 | daemon-route `mcp_server.py` via the `handle_request` JSON-RPC chokepoint | — | [`41359ba`](https://github.com/techempower-org/mempalace/commit/41359ba) | +| 109 | Preserve dashed project names in transcript-derived wings | [#10](https://github.com/MemPalace/mempalace/pull/10) | [`d76134d`](https://github.com/techempower-org/mempalace/commit/d76134d) | +| 110 | Drop wing_ prefix from transcript-derived wings to converge with operator mines | [#9](https://github.com/MemPalace/mempalace/pull/9) | [`86d4700`](https://github.com/techempower-org/mempalace/commit/86d4700) | +| 111 | Retire mempalace_session_recovery collection + read tool | [#8](https://github.com/MemPalace/mempalace/pull/8) | [`0b945e1`](https://github.com/techempower-org/mempalace/commit/0b945e1) | +| 112 | mempalace mined + purge --source-file (mining management surface) | [#7](https://github.com/MemPalace/mempalace/pull/7) | [`2e6ced9`](https://github.com/techempower-org/mempalace/commit/2e6ced9) | +| 113 | Drop hook-side checkpoint diary writes — verbatim-only architecture | [#6](https://github.com/MemPalace/mempalace/pull/6) | [`69768fc`](https://github.com/techempower-org/mempalace/commit/69768fc) | +| 114 | Restore transcript ingest via daemon /mine when PALACE_DAEMON_URL is set | [#2](https://github.com/MemPalace/mempalace/pull/2) | [`09d2ca6`](https://github.com/techempower-org/mempalace/commit/09d2ca6) | +| 115 | `hook_verbatim_mode` config flag preserves system tags + full tool I/O during transcript ingest | — | [`ef98961`](https://github.com/techempower-org/mempalace/commit/ef98961) | +| 116 | Retire the `kind=` filter — structural split made it inert | — | [`7ba28dc`](https://github.com/techempower-org/mempalace/commit/7ba28dc) | +| 117 | Hoist CLOSET_RANK_BOOSTS to module level + record VecRecall ablation finding | — | [`3cb03f3`](https://github.com/techempower-org/mempalace/commit/3cb03f3) | +| 118 | Strip embedded API key from .claude-plugin/ manifests; rely on env inheritance | — | [`9f91e18`](https://github.com/techempower-org/mempalace/commit/9f91e18) | +| 119 | Cherry-pick #1094 — coerce None metadatas at chromadb boundary | [#1094](https://github.com/MemPalace/mempalace/pull/1094) (OPEN) | [`43d728d`](https://github.com/techempower-org/mempalace/commit/43d728d) | +| 120 | Cherry-pick #1087 rewrite — collection.delete(where=) instead of nuke-and-rebuild | [#1087](https://github.com/MemPalace/mempalace/pull/1087) (OPEN) | [`366a9ad`](https://github.com/techempower-org/mempalace/commit/366a9ad) | +| 121 | Canonical YAML manifest + renderer for fork-ahead docs | — | [`5a01aec`](https://github.com/techempower-org/mempalace/commit/5a01aec) | +| 122 | Phase D migration + PreCompact recovery write | — | [`42817d7`](https://github.com/techempower-org/mempalace/commit/42817d7) | +| 123 | Surface drawer_id in search/diary/recovery payloads | — | [`9a8bb77`](https://github.com/techempower-org/mempalace/commit/9a8bb77) | +| 124 | Cherry-pick #1085 — batch ChromaDB inserts in miner (10–30× faster) | [#1085](https://github.com/MemPalace/mempalace/pull/1085) (CLOSED) | [`6be6fff`](https://github.com/techempower-org/mempalace/commit/6be6fff) | +| 125 | scripts/deploy.sh — one-command Syncthing-aware redeploy | — | [`8252025`](https://github.com/techempower-org/mempalace/commit/8252025) | +| 126 | Phases A–C of the checkpoint collection split | — | [`e266365`](https://github.com/techempower-org/mempalace/commit/e266365) | +| 127 | kind= filter on search_memories excludes Stop-hook checkpoints (transitional) | — | [`f9f5cc4`](https://github.com/techempower-org/mempalace/commit/f9f5cc4) | ### Recently merged into upstream diff --git a/docs/fork-changes.yaml b/docs/fork-changes.yaml index 2dce596d97..d94ff9e88f 100644 --- a/docs/fork-changes.yaml +++ b/docs/fork-changes.yaml @@ -23,6 +23,53 @@ # Order in this file is presentation order in the changelog (newest first). entries: + - id: postgres-cached-conn-reconnect + date: 2026-09-10 + bucket: Fixed + commit: 4d15803 + area: Reliability + summary: "Cached postgres connection reconnects once after a server-side disconnect instead of raising raw" + body: | + After any server-side disconnect — a DB restart, a + ``pg_terminate_backend``, or the ``idle_session_timeout = 10min`` set on + the production palace DB after the 2026-08-07 incident — the *first* call + on an unguarded cached-connection path raised raw and surfaced as a 500; + the call after that healed. Verified live on the prod daemon 2026-08-08, + and observed during the outage itself on ``count()`` → ``tool_status`` + and on ``graph_stats``. + + psycopg's ``.closed`` is a client-side flag updated only on I/O, so it + stays ``False`` after a server-side kill and ``_get_conn()`` hands the + dead connection straight back; the statement that runs on it is what + discovers the socket is gone (``closed`` and ``broken`` both flip only + *after* the failed query). + + Fixed at the seam rather than per call site: every statement on the + cached connection now goes through ``_cursor()``, a ``_RetryingCursor`` + that classifies the failure and — only for a connection-class error — + drops the socket, reconnects and re-runs once. Statement errors (a bad + query, a ``statement_timeout``) are re-raised untouched and do not churn + the connection. The retry is safe because of an invariant, not because + of the driver: a connection error does *not* mean the statement never + ran — this connection is ``autocommit``, so the socket can die after the + server committed and before the acknowledgement arrives — but every + statement routed through the seam is idempotent (``ON CONFLICT`` upserts, + keyed ``DELETE``/``UPDATE``, a jsonb merge that is a fixed point, and + reads). The docstring states that as a requirement for anything new + routed through ``_cursor()``, and records the one exception: the bare + ``CREATE TABLE``/``CREATE INDEX`` in ``_create_table`` are guarded by + ``_table_exists()`` rather than ``IF NOT EXISTS``, so a disconnect in the + ambiguous window during first creation fails loudly on the retry rather + than silently. Reconnecting through + ``_get_conn()`` also re-applies the session GUCs — a hand-rolled + reconnect that dropped ``hnsw.iterative_scan`` (#446) would have turned + wing-scoped search silently back into zero rows, so a test pins it. + ``_drop_conn()`` closes the dead socket rather than abandoning it: two + forever-cached idle connections are what pinned a smart shutdown open + for 37.5 hours. + tests: "7 (test_postgres_reconnect: count + get survive a kill, dead socket closed not leaked, statement error re-raised without reconnecting, exactly one retry, message-based classification, session settings re-applied)" + files: [mempalace/backends/postgres.py, tests/test_postgres_reconnect.py] + - id: cli-mine-background-replay-drain date: 2026-09-10 bucket: Fixed diff --git a/website/public/llms-full.txt b/website/public/llms-full.txt index 7cfdacc2d7..a304e6faf1 100644 --- a/website/public/llms-full.txt +++ b/website/public/llms-full.txt @@ -46,7 +46,7 @@ Files included, in order: ## What this is -A verbatim-first local AI memory system. This fork tracks `upstream/develop` through the post-v3.9.0 sync (2026-09-01, commit `e8098348`) and runs in production on a **618K+ drawer Postgres + pgvector + Apache AGE palace** behind [palace-daemon](https://github.com/techempower-org/palace-daemon). It carries fork-ahead commits that compose with — not replace — bensig's release direction; the v3.3.5 release (2026-05-10) includes our co-authored `_get_collection` retry-once via upstream #1377. 7050 tests pass on `main`. +A verbatim-first local AI memory system. This fork tracks `upstream/develop` through the post-v3.9.0 sync (2026-09-01, commit `e8098348`) and runs in production on a **618K+ drawer Postgres + pgvector + Apache AGE palace** behind [palace-daemon](https://github.com/techempower-org/palace-daemon). It carries fork-ahead commits that compose with — not replace — bensig's release direction; the v3.3.5 release (2026-05-10) includes our co-authored `_get_collection` retry-once via upstream #1377. 7057 tests pass on `main`. The fork's architectural thinking — the four-layer memory model, the [verbatim-vs-derivative thesis](docs/research/verbatim-vs-derivative-axis.md), design principles, and the two-memory-layer pairing with Auto Dream — lives in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md). The new things here are *what we've learned*, not just what we've fixed. @@ -277,132 +277,133 @@ The full enumeration of fork-ahead changes. The canonical source is [`docs/fork- | # | Description | Upstream PR | Fork commit | |---|---|---|---| -| 1 | `mempalace replay` asks the daemon to queue each mine instead of waiting on the palace write lock | — | [`059fe0a`](https://github.com/techempower-org/mempalace/commit/059fe0a) | -| 2 | purge/sync work on Postgres palaces, and a zero-match purge no longer looks like a success | — | [`3b1a2af`](https://github.com/techempower-org/mempalace/commit/3b1a2af) | -| 3 | Auto-query recognizes a session-resumption ask ("where were we", "catch me up") in both the shell pre-filter and the signal set | — | [`c6872494`](https://github.com/techempower-org/mempalace/commit/c6872494) | -| 4 | Post-compaction recovery: clear the auto-query dedupe and re-inject wake-up content, not a pointer | — | [`22858070`](https://github.com/techempower-org/mempalace/commit/22858070) | -| 5 | Every search hit carries source_kind (+ staleness when decidable) and the CLI renders the caveat | — | [`5632dc2`](https://github.com/techempower-org/mempalace/commit/5632dc2) | -| 6 | mempalace mine --mode projects — targeted re-index of one curated document | — | [`787af46`](https://github.com/techempower-org/mempalace/commit/787af46) | -| 7 | Daemon-strict mine refuses to derive a wing from a document path it cannot classify locally | — | [`a30293b`](https://github.com/techempower-org/mempalace/commit/a30293b) | -| 8 | Postgres write path scrubs lone surrogates, nested metadata and ids, not just top-level NULs | — | [`66ca19f`](https://github.com/techempower-org/mempalace/commit/66ca19f) | -| 9 | Wing-scoped vector search no longer returns 0 rows: enable pgvector hnsw.iterative_scan per connection | — | [`2ea774d`](https://github.com/techempower-org/mempalace/commit/2ea774d) | -| 10 | Curated memory-file hits clear a lower confidence floor; harness prompt-echo drawers filtered as exhaust | — | [`31b74d9`](https://github.com/techempower-org/mempalace/commit/31b74d9) | -| 11 | mempalace doctor — one-screen health check of the memory workflow (bridge, daemon, wing, hooks, replay) | — | [`04dde83`](https://github.com/techempower-org/mempalace/commit/04dde83) | -| 12 | Only the MCP-server entrypoints parse argv at import; other importers get defaults (#409) | — | [`e3e0579`](https://github.com/techempower-org/mempalace/commit/e3e0579) | -| 13 | AGE knowledge-graph connection survives a bad query (rollback) and a dead socket (reconnect once) (#405) | — | [`2135df7`](https://github.com/techempower-org/mempalace/commit/2135df7) | -| 14 | Re-mine a transcript incrementally — embed only new or changed chunks; stored drawers are the watermark (#414) | — | [`cf1e997`](https://github.com/techempower-org/mempalace/commit/cf1e997) | -| 15 | Legacy hallways verb deprecated in favour of hallway list; now honours --json (#407) | — | [`1509c32`](https://github.com/techempower-org/mempalace/commit/1509c32) | -| 16 | Replay drops legacy whole-project convos requests instead of re-mining them forever (#426) | — | [`b5b774f`](https://github.com/techempower-org/mempalace/commit/b5b774f) | -| 17 | wake-up L1 skips bookkeeping drawers (AUTO-SAVE, manifests) and ranks curated memory files first (#421 #423) | — | [`a4012dd`](https://github.com/techempower-org/mempalace/commit/a4012dd) | -| 18 | tool_checkpoint forwards session_id to the diary write; declared in both tool schemas (#408) | — | [`e5e31f0`](https://github.com/techempower-org/mempalace/commit/e5e31f0) | -| 19 | Hooks ask the daemon to run checkpoint mines in the background (202 + serial drain) — no more 30s timeouts, journaled replays, and double mines | — | [`79b192b`](https://github.com/techempower-org/mempalace/commit/79b192b) | -| 20 | Thin-vector warning no longer tells pgvector users to rebuild an HNSW index | — | [`43d3a28`](https://github.com/techempower-org/mempalace/commit/43d3a28) | -| 21 | Checkpoint ingest mines THIS transcript, not the whole project dir (#414/#426 mechanism) | — | [`16a5236`](https://github.com/techempower-org/mempalace/commit/16a5236) | -| 22 | Auto-query signal quality: user's-words depth query + wing inventory, peer-block stripping, identifier signals via BM25 fast route, exhaust filter, 0.50 floor, per-session dedupe, curated-first, visible receipts (#419 #420 #422 #424) | — | [`0d92cef`](https://github.com/techempower-org/mempalace/commit/0d92cef) | -| 23 | Sync upstream/develop through v3.9.0 (e8098348): 101 commits — durable config writes, strategy-aware candidate pools, BM25-under-threshold admission, search hub-forward, embeddinggemma batch override, cli_compatible search | — | [`09713d9`](https://github.com/techempower-org/mempalace/commit/09713d9) | -| 24 | CLI wave 3/3: `mempalace diary`, `kg`, `walk` and `rate` first-class verbs (#354, #357, #359, #361) | — | [`ff9ee22`](https://github.com/techempower-org/mempalace/commit/ff9ee22) | -| 25 | CLI wave 2/3: `mempalace wings`, `taxonomy`, `aaak spec`, `hallway`, `checkpoint --dry-run` (#356, #358, #360, #362) | — | [`2848e96`](https://github.com/techempower-org/mempalace/commit/2848e96) | -| 26 | CLI wave 1/3: `mempalace drawer get\|add\|delete\|update` and `duplicate check` (#355, #363) | — | [`836d1a1`](https://github.com/techempower-org/mempalace/commit/836d1a1) | -| 27 | Sync upstream/develop through v3.8.0 (3e56979f): 166 commits — date-window search (since/before), openai-compat embeddings, RFC 002 adapter dispatch (#2062), mcp_proxy entry point, chunk_total metadata; ancestry repaired so future syncs replay only new commits | — | [`b3527b1`](https://github.com/techempower-org/mempalace/commit/b3527b1) | -| 28 | Sync upstream/develop through v3.7.0 (8516db7f): 433 commits — hub forward, temp-collection repair promote, round-trippable drawer IDs, kg_supersede, HNSW CLI fence, 62 conflict files resolved | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 29 | Postgres backend: _coerce_wing write guard — wing-less writes land in 'general' (not the '' column default) and separator/case variants normalize; scripts/wing_hygiene.py migrates the historical rows (dry-run default) | — | [`979da51`](https://github.com/techempower-org/mempalace/commit/979da51) | -| 30 | sync-outline: empty-secret env vars fall back to documented defaults; unreachable nightly cron retired (0-for-41 by construction) — workflow_dispatch stays | — | [`5227da4`](https://github.com/techempower-org/mempalace/commit/5227da4) | -| 31 | Searcher: restore mempalace_search on postgres — guard capability-path conversion in union merge (UnboundLocalError since the 2026-07-02 sync) | — | [`96f83d7`](https://github.com/techempower-org/mempalace/commit/96f83d7) | -| 32 | Auto-query: TTL cache for the deterministic depth-refresh injection — repeat fires 0ms vs ~850ms daemon round-trip | — | [`be903e6`](https://github.com/techempower-org/mempalace/commit/be903e6) | -| 33 | Sync upstream/develop through da5a48c (post-v3.5.0): remote MCP server w/ TLS + read-only, graph auto-population, Qdrant facets, list_drawers date filters, 213 commits | — | [`b46f18d`](https://github.com/techempower-org/mempalace/commit/b46f18d) | -| 34 | Auto-query firing fixes: frozen turn counter, dead wing scoring, lowercase entities, turn-1 cadence | — | [`fad3e27`](https://github.com/techempower-org/mempalace/commit/fad3e27) | -| 35 | Auto-query: periodic depth signal, unknown-entity 0->1 bump, broader temporal patterns | — | [`864d7a4`](https://github.com/techempower-org/mempalace/commit/864d7a4) | -| 36 | Sync upstream/develop through v3.5.0 (73e74bf): MCP HTTP transport, source_file filter, checkpoint tool, SessionEnd hook, 185 commits | — | [`8711e1c`](https://github.com/techempower-org/mempalace/commit/8711e1c) | -| 37 | Restore concurrent file mining via parallel-prepare/serial-write (regression from a dropped sync hunk); opt-in --workers | — | [`42a107b`](https://github.com/techempower-org/mempalace/commit/42a107b) | -| 38 | Sync upstream/develop through v3.4.0 (2ec4bae): RFC-001 backend stack, diary checkpoints restored, 113 commits | — | [`373fdf2`](https://github.com/techempower-org/mempalace/commit/373fdf2) | -| 39 | auto_wake: opt-in wake-on-demand for a sleeping palace-daemon host (wake command + /health poll + single retry) | — | [`8e0d896`](https://github.com/techempower-org/mempalace/commit/8e0d896) | -| 40 | AGE graph-walk: auto edge-endpoint indexes in backfill + bind anonymous RELATION targets (mempalace#335) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 41 | pluggable adaptmem_ft encoder backend selectable via MEMPALACE_EMBEDDING_MODEL (closes #308) | — | [`5fba6d8`](https://github.com/techempower-org/mempalace/commit/5fba6d8) | -| 42 | README.md landscape table — refresh upstream MemPalace star count from ~23K → ~53K (current 2026-05-28) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 43 | README.md + docs/ECOSYSTEM.md — soften 'engram-2 17% E2E QA' framing per the 2026-05-24 research doc's unsubstantiated finding (#319) | — | [`ddf00b4`](https://github.com/techempower-org/mempalace/commit/ddf00b4) | -| 44 | kg_llm_extractor rewrites AGE dollar-quote tag in triples so drawers indexing palace source code don't fail at add_triple (#313) | — | [`3fb9428`](https://github.com/techempower-org/mempalace/commit/3fb9428) | -| 45 | scripts/maintain-fork-changes.py + ship-prep step 1: resolve commit:HEAD placeholders and de-dup yaml entries (#316) | — | [`9060e09`](https://github.com/techempower-org/mempalace/commit/9060e09) | -| 46 | scripts/ship-prep.sh — one command bumps README test count and runs all three doc renderers (#312) | — | [`4677db8`](https://github.com/techempower-org/mempalace/commit/4677db8) | -| 47 | mempalace_search MCP input schema accepts fusion_mode (convex\|rrf) and forwards to search_memories (#302) | — | [`f753ec4`](https://github.com/techempower-org/mempalace/commit/f753ec4) | -| 48 | scripts/check-docs.sh finds pytest via main checkout when run from a worktree, fails hard instead of silently skipping test-count check (#311) | — | [`1d19a8b`](https://github.com/techempower-org/mempalace/commit/1d19a8b) | -| 49 | kg_triple_worker retries add_triple within-worker on transient psycopg errors instead of abandoning to lease-reclaim (#298) | — | [`36c0b02`](https://github.com/techempower-org/mempalace/commit/36c0b02) | -| 50 | mempalace_kg_stats returns structured backend-unavailable envelope on transient psycopg failures (#299) | — | [`8fd0b01`](https://github.com/techempower-org/mempalace/commit/8fd0b01) | -| 51 | mempalace why + tunnels — explain a drawer + inventory cross-wing tunnels (slice of #191) | — | [`fdcd0b4`](https://github.com/techempower-org/mempalace/commit/fdcd0b4) | -| 52 | RRF vs convex-blend rerank — A/B measurement on our corpus (#162) | — | [`ea5d567`](https://github.com/techempower-org/mempalace/commit/ea5d567) | -| 53 | KG triples gain SPOC context slot + worker auto-derives valid_from from drawer metadata (#161) | — | [`b87ce05`](https://github.com/techempower-org/mempalace/commit/b87ce05) | -| 54 | mempalace bulk-move — multi-drawer metadata relocation by source wing/room (#191) | — | [`1ca544b`](https://github.com/techempower-org/mempalace/commit/1ca544b) | -| 55 | mempalace move — fast direct-to-daemon single-drawer wing/room relocation (#191) | — | [`d007b6f`](https://github.com/techempower-org/mempalace/commit/d007b6f) | -| 56 | mempalace stats migrates to GET /stats REST + exposes graph/status sections (#191) | — | [`853bb25`](https://github.com/techempower-org/mempalace/commit/853bb25) | -| 57 | mempalace cypher — read-only Cypher query CLI (#191) | — | [`32a41b1`](https://github.com/techempower-org/mempalace/commit/32a41b1) | -| 58 | mempalace graph — fast direct-to-daemon KG structural snapshot (#191) | — | [`499f42d`](https://github.com/techempower-org/mempalace/commit/499f42d) | -| 59 | mempalace list — fast direct-to-daemon drawer browser (#191) | — | [`257137b`](https://github.com/techempower-org/mempalace/commit/257137b) | -| 60 | Recency decay weighting in search + mempalace prune --stale-days CLI (#158) | — | [`558d327`](https://github.com/techempower-org/mempalace/commit/558d327) | -| 61 | mempalace_rate_memory MCP tool + bounded rating signal in search ranking (#159) | — | [`583536c`](https://github.com/techempower-org/mempalace/commit/583536c) | -| 62 | Formalize wing/room derivation order; demote entity detector to last-resort hint (#157) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 63 | RRF fusion mode + convex-vs-RRF A/B harness (#162) | [#247](https://github.com/MemPalace/mempalace/pull/247) | [`6c9d10c`](https://github.com/techempower-org/mempalace/commit/6c9d10c) | -| 64 | mempalace stats: add ROOMS breakdown (drawer count by room) to the dashboard | — | [`1673465`](https://github.com/techempower-org/mempalace/commit/1673465) | -| 65 | Calibrated confidence field on search results + Brier-score eval column | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 66 | Evaluation doc: curated-authority vs auto-mined separation (#202) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 67 | Apply AGE statement_timeout in same transaction as cypher() (PR #228 follow-up) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 68 | LLM-based KG triple extraction: queue table, async worker, llama.cpp on familiar | — | [`59ac0bc`](https://github.com/techempower-org/mempalace/commit/59ac0bc) | -| 69 | Promote verbatim-vs-derivative essay from research/ to README (#170) | — | [`6a264d9`](https://github.com/techempower-org/mempalace/commit/6a264d9) | -| 70 | mempalace stats — palace analytics dashboard (#191) | — | [`6f994fb`](https://github.com/techempower-org/mempalace/commit/6f994fb) | -| 71 | CLI wiring: mempalace mine --source (#57) | — | [`5ed9fa7`](https://github.com/techempower-org/mempalace/commit/5ed9fa7) | -| 72 | Warp terminal source adapter (#62) | — | [`2e85585`](https://github.com/techempower-org/mempalace/commit/2e85585) | -| 73 | OpenCode adapter smoke test against real DB (#56) | — | [`a9ed72b`](https://github.com/techempower-org/mempalace/commit/a9ed72b) | -| 74 | Codex, Gemini, and Aider source adapters (#61, #59) | — | [`0c23165`](https://github.com/techempower-org/mempalace/commit/0c23165) | -| 75 | Filesystem + conversation source adapters (#63) | — | [`9a1facf`](https://github.com/techempower-org/mempalace/commit/9a1facf) | -| 76 | Widen auto-query signal patterns for natural recall phrases | — | [`33e780e`](https://github.com/techempower-org/mempalace/commit/33e780e) | -| 77 | Native rename_wing backend operation + CLI command (#154) | — | [`d045f83`](https://github.com/techempower-org/mempalace/commit/d045f83) | -| 78 | Standalone essay: the verbatim-vs-derivative axis (#47) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 79 | Research doc: uncertainty-aware retrieval analysis (#84) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 80 | Design doc: scope/collection filter on mempalace_search (#76) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 81 | Agent-shaped CLI surface — --json / --quiet for non-MCP integration | — | [`25ed900`](https://github.com/techempower-org/mempalace/commit/25ed900) | -| 82 | Design eval: multi-palace separation — curated vs auto-mined (#45) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | -| 83 | Document .sh shim delegation to palace-daemon (counter-position to upstream #1069) | — | [`bf0a4d0`](https://github.com/techempower-org/mempalace/commit/bf0a4d0) | -| 84 | Honor ~/.mempalace/RETIRED marker — refuse default palace, surface retire message | — | [`798cf14`](https://github.com/techempower-org/mempalace/commit/798cf14) | -| 85 | Empty repo .opencode/opencode.json mcp block — disabled flag wasn't being respected | — | [`7133eee`](https://github.com/techempower-org/mempalace/commit/7133eee) | -| 86 | Drop \$comment from .opencode/opencode.json — schema rejects unknown root keys | — | [`637bb01`](https://github.com/techempower-org/mempalace/commit/637bb01) | -| 87 | Disable repo-level MCP entry by default + venv-python fallback | — | [`47018e5`](https://github.com/techempower-org/mempalace/commit/47018e5) | -| 88 | Stub resources/list + prompts/list so MCP clients stop ERROR-logging on connect | — | [`6ca0670`](https://github.com/techempower-org/mempalace/commit/6ca0670) | -| 89 | Bundled OpenCode live-capture plugin that bypasses option-K v1.2.1 bugs (filed upstream as #4, #5) | — | [`5522623`](https://github.com/techempower-org/mempalace/commit/5522623) | -| 90 | Documented OpenCode integration recipe (read-side MCP + push plugin + retrospective adapter) | — | [`60dc9e6`](https://github.com/techempower-org/mempalace/commit/60dc9e6) | -| 91 | .opencode/opencode.json — repo-root MCP config so opencode picks up mempalace automatically | [#1567](https://github.com/MemPalace/mempalace/pull/1567) (CLOSED) | [`ba16b82`](https://github.com/techempower-org/mempalace/commit/ba16b82) | -| 92 | OpenCodeSourceAdapter (RFC 002) — retrospective ingest of OpenCode SQLite sessions | [#1484](https://github.com/MemPalace/mempalace/pull/1484) (OPEN) | [`2ffe652`](https://github.com/techempower-org/mempalace/commit/2ffe652) | -| 93 | mempalace_walk_palace MCP tool — agent walks the palace via AGE Cypher | — | [`8022ecb`](https://github.com/techempower-org/mempalace/commit/8022ecb) | -| 94 | Backfill AGE graph from existing drawer table — restartable, checkpointed | — | [`b3f0206`](https://github.com/techempower-org/mempalace/commit/b3f0206) | -| 95 | Wing/Room/Drawer hierarchy as native AGE nodes; Cypher MATCH walks palace structure | — | [`ff583c0`](https://github.com/techempower-org/mempalace/commit/ff583c0) | -| 96 | Write-through middleware on PostgresCollection — entities populate AGE on every drawer write | — | [`3321d83`](https://github.com/techempower-org/mempalace/commit/3321d83) | -| 97 | KnowledgeGraphAGE API parity with SQLite KG: add_entity, invalidate, query_entity, query_relationship, timeline, seed_from_entity_facts | — | [`ff7187d`](https://github.com/techempower-org/mempalace/commit/ff7187d) | -| 98 | Pending-writes journal + replay so daemon outages stop being silent | — | [`0c34464`](https://github.com/techempower-org/mempalace/commit/0c34464) | -| 99 | MCP server distinguishes 'backend unreachable' from 'no palace found' | — | [`0c34464`](https://github.com/techempower-org/mempalace/commit/0c34464) | -| 100 | Defense-in-depth metadata sanitizer at the chromadb-client chokepoint | — | [`f499814`](https://github.com/techempower-org/mempalace/commit/f499814) | -| 101 | Route Stop/PreCompact hooks through palace-daemon/clients/hook.py | — | [`42ded2e`](https://github.com/techempower-org/mempalace/commit/42ded2e) | -| 102 | KnowledgeGraphAGE skeleton — Apache AGE graph bootstrap over psycopg2 | — | [`a3ee623`](https://github.com/techempower-org/mempalace/commit/a3ee623) | -| 103 | README pivots to the four-layer model + Auto Dream as vindication of the verbatim-vs-derivative axis | — | [`55b36ca`](https://github.com/techempower-org/mempalace/commit/55b36ca) | -| 104 | CI: gate postgres-backend tests against a pgvector service container | — | [`da0bdbb`](https://github.com/techempower-org/mempalace/commit/da0bdbb) | -| 105 | PostgreSQL backend via #665 cherry-pick + fork-side adaptations + smoke tests | [#665](https://github.com/MemPalace/mempalace/pull/665) (OPEN) | [`5e90c72`](https://github.com/techempower-org/mempalace/commit/5e90c72) | -| 106 | daemon-route `mempalace status` / `search` / `mine` when PALACE_DAEMON_URL is set | — | [`22ef562`](https://github.com/techempower-org/mempalace/commit/22ef562) | -| 107 | daemon-route `mcp_server.py` via the `handle_request` JSON-RPC chokepoint | — | [`41359ba`](https://github.com/techempower-org/mempalace/commit/41359ba) | -| 108 | Preserve dashed project names in transcript-derived wings | [#10](https://github.com/MemPalace/mempalace/pull/10) | [`d76134d`](https://github.com/techempower-org/mempalace/commit/d76134d) | -| 109 | Drop wing_ prefix from transcript-derived wings to converge with operator mines | [#9](https://github.com/MemPalace/mempalace/pull/9) | [`86d4700`](https://github.com/techempower-org/mempalace/commit/86d4700) | -| 110 | Retire mempalace_session_recovery collection + read tool | [#8](https://github.com/MemPalace/mempalace/pull/8) | [`0b945e1`](https://github.com/techempower-org/mempalace/commit/0b945e1) | -| 111 | mempalace mined + purge --source-file (mining management surface) | [#7](https://github.com/MemPalace/mempalace/pull/7) | [`2e6ced9`](https://github.com/techempower-org/mempalace/commit/2e6ced9) | -| 112 | Drop hook-side checkpoint diary writes — verbatim-only architecture | [#6](https://github.com/MemPalace/mempalace/pull/6) | [`69768fc`](https://github.com/techempower-org/mempalace/commit/69768fc) | -| 113 | Restore transcript ingest via daemon /mine when PALACE_DAEMON_URL is set | [#2](https://github.com/MemPalace/mempalace/pull/2) | [`09d2ca6`](https://github.com/techempower-org/mempalace/commit/09d2ca6) | -| 114 | `hook_verbatim_mode` config flag preserves system tags + full tool I/O during transcript ingest | — | [`ef98961`](https://github.com/techempower-org/mempalace/commit/ef98961) | -| 115 | Retire the `kind=` filter — structural split made it inert | — | [`7ba28dc`](https://github.com/techempower-org/mempalace/commit/7ba28dc) | -| 116 | Hoist CLOSET_RANK_BOOSTS to module level + record VecRecall ablation finding | — | [`3cb03f3`](https://github.com/techempower-org/mempalace/commit/3cb03f3) | -| 117 | Strip embedded API key from .claude-plugin/ manifests; rely on env inheritance | — | [`9f91e18`](https://github.com/techempower-org/mempalace/commit/9f91e18) | -| 118 | Cherry-pick #1094 — coerce None metadatas at chromadb boundary | [#1094](https://github.com/MemPalace/mempalace/pull/1094) (OPEN) | [`43d728d`](https://github.com/techempower-org/mempalace/commit/43d728d) | -| 119 | Cherry-pick #1087 rewrite — collection.delete(where=) instead of nuke-and-rebuild | [#1087](https://github.com/MemPalace/mempalace/pull/1087) (OPEN) | [`366a9ad`](https://github.com/techempower-org/mempalace/commit/366a9ad) | -| 120 | Canonical YAML manifest + renderer for fork-ahead docs | — | [`5a01aec`](https://github.com/techempower-org/mempalace/commit/5a01aec) | -| 121 | Phase D migration + PreCompact recovery write | — | [`42817d7`](https://github.com/techempower-org/mempalace/commit/42817d7) | -| 122 | Surface drawer_id in search/diary/recovery payloads | — | [`9a8bb77`](https://github.com/techempower-org/mempalace/commit/9a8bb77) | -| 123 | Cherry-pick #1085 — batch ChromaDB inserts in miner (10–30× faster) | [#1085](https://github.com/MemPalace/mempalace/pull/1085) (CLOSED) | [`6be6fff`](https://github.com/techempower-org/mempalace/commit/6be6fff) | -| 124 | scripts/deploy.sh — one-command Syncthing-aware redeploy | — | [`8252025`](https://github.com/techempower-org/mempalace/commit/8252025) | -| 125 | Phases A–C of the checkpoint collection split | — | [`e266365`](https://github.com/techempower-org/mempalace/commit/e266365) | -| 126 | kind= filter on search_memories excludes Stop-hook checkpoints (transitional) | — | [`f9f5cc4`](https://github.com/techempower-org/mempalace/commit/f9f5cc4) | +| 1 | Cached postgres connection reconnects once after a server-side disconnect instead of raising raw | — | [`4d15803`](https://github.com/techempower-org/mempalace/commit/4d15803) | +| 2 | `mempalace replay` asks the daemon to queue each mine instead of waiting on the palace write lock | — | [`059fe0a`](https://github.com/techempower-org/mempalace/commit/059fe0a) | +| 3 | purge/sync work on Postgres palaces, and a zero-match purge no longer looks like a success | — | [`3b1a2af`](https://github.com/techempower-org/mempalace/commit/3b1a2af) | +| 4 | Auto-query recognizes a session-resumption ask ("where were we", "catch me up") in both the shell pre-filter and the signal set | — | [`c6872494`](https://github.com/techempower-org/mempalace/commit/c6872494) | +| 5 | Post-compaction recovery: clear the auto-query dedupe and re-inject wake-up content, not a pointer | — | [`22858070`](https://github.com/techempower-org/mempalace/commit/22858070) | +| 6 | Every search hit carries source_kind (+ staleness when decidable) and the CLI renders the caveat | — | [`5632dc2`](https://github.com/techempower-org/mempalace/commit/5632dc2) | +| 7 | mempalace mine --mode projects — targeted re-index of one curated document | — | [`787af46`](https://github.com/techempower-org/mempalace/commit/787af46) | +| 8 | Daemon-strict mine refuses to derive a wing from a document path it cannot classify locally | — | [`a30293b`](https://github.com/techempower-org/mempalace/commit/a30293b) | +| 9 | Postgres write path scrubs lone surrogates, nested metadata and ids, not just top-level NULs | — | [`66ca19f`](https://github.com/techempower-org/mempalace/commit/66ca19f) | +| 10 | Wing-scoped vector search no longer returns 0 rows: enable pgvector hnsw.iterative_scan per connection | — | [`2ea774d`](https://github.com/techempower-org/mempalace/commit/2ea774d) | +| 11 | Curated memory-file hits clear a lower confidence floor; harness prompt-echo drawers filtered as exhaust | — | [`31b74d9`](https://github.com/techempower-org/mempalace/commit/31b74d9) | +| 12 | mempalace doctor — one-screen health check of the memory workflow (bridge, daemon, wing, hooks, replay) | — | [`04dde83`](https://github.com/techempower-org/mempalace/commit/04dde83) | +| 13 | Only the MCP-server entrypoints parse argv at import; other importers get defaults (#409) | — | [`e3e0579`](https://github.com/techempower-org/mempalace/commit/e3e0579) | +| 14 | AGE knowledge-graph connection survives a bad query (rollback) and a dead socket (reconnect once) (#405) | — | [`2135df7`](https://github.com/techempower-org/mempalace/commit/2135df7) | +| 15 | Re-mine a transcript incrementally — embed only new or changed chunks; stored drawers are the watermark (#414) | — | [`cf1e997`](https://github.com/techempower-org/mempalace/commit/cf1e997) | +| 16 | Legacy hallways verb deprecated in favour of hallway list; now honours --json (#407) | — | [`1509c32`](https://github.com/techempower-org/mempalace/commit/1509c32) | +| 17 | Replay drops legacy whole-project convos requests instead of re-mining them forever (#426) | — | [`b5b774f`](https://github.com/techempower-org/mempalace/commit/b5b774f) | +| 18 | wake-up L1 skips bookkeeping drawers (AUTO-SAVE, manifests) and ranks curated memory files first (#421 #423) | — | [`a4012dd`](https://github.com/techempower-org/mempalace/commit/a4012dd) | +| 19 | tool_checkpoint forwards session_id to the diary write; declared in both tool schemas (#408) | — | [`e5e31f0`](https://github.com/techempower-org/mempalace/commit/e5e31f0) | +| 20 | Hooks ask the daemon to run checkpoint mines in the background (202 + serial drain) — no more 30s timeouts, journaled replays, and double mines | — | [`79b192b`](https://github.com/techempower-org/mempalace/commit/79b192b) | +| 21 | Thin-vector warning no longer tells pgvector users to rebuild an HNSW index | — | [`43d3a28`](https://github.com/techempower-org/mempalace/commit/43d3a28) | +| 22 | Checkpoint ingest mines THIS transcript, not the whole project dir (#414/#426 mechanism) | — | [`16a5236`](https://github.com/techempower-org/mempalace/commit/16a5236) | +| 23 | Auto-query signal quality: user's-words depth query + wing inventory, peer-block stripping, identifier signals via BM25 fast route, exhaust filter, 0.50 floor, per-session dedupe, curated-first, visible receipts (#419 #420 #422 #424) | — | [`0d92cef`](https://github.com/techempower-org/mempalace/commit/0d92cef) | +| 24 | Sync upstream/develop through v3.9.0 (e8098348): 101 commits — durable config writes, strategy-aware candidate pools, BM25-under-threshold admission, search hub-forward, embeddinggemma batch override, cli_compatible search | — | [`09713d9`](https://github.com/techempower-org/mempalace/commit/09713d9) | +| 25 | CLI wave 3/3: `mempalace diary`, `kg`, `walk` and `rate` first-class verbs (#354, #357, #359, #361) | — | [`ff9ee22`](https://github.com/techempower-org/mempalace/commit/ff9ee22) | +| 26 | CLI wave 2/3: `mempalace wings`, `taxonomy`, `aaak spec`, `hallway`, `checkpoint --dry-run` (#356, #358, #360, #362) | — | [`2848e96`](https://github.com/techempower-org/mempalace/commit/2848e96) | +| 27 | CLI wave 1/3: `mempalace drawer get\|add\|delete\|update` and `duplicate check` (#355, #363) | — | [`836d1a1`](https://github.com/techempower-org/mempalace/commit/836d1a1) | +| 28 | Sync upstream/develop through v3.8.0 (3e56979f): 166 commits — date-window search (since/before), openai-compat embeddings, RFC 002 adapter dispatch (#2062), mcp_proxy entry point, chunk_total metadata; ancestry repaired so future syncs replay only new commits | — | [`b3527b1`](https://github.com/techempower-org/mempalace/commit/b3527b1) | +| 29 | Sync upstream/develop through v3.7.0 (8516db7f): 433 commits — hub forward, temp-collection repair promote, round-trippable drawer IDs, kg_supersede, HNSW CLI fence, 62 conflict files resolved | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 30 | Postgres backend: _coerce_wing write guard — wing-less writes land in 'general' (not the '' column default) and separator/case variants normalize; scripts/wing_hygiene.py migrates the historical rows (dry-run default) | — | [`979da51`](https://github.com/techempower-org/mempalace/commit/979da51) | +| 31 | sync-outline: empty-secret env vars fall back to documented defaults; unreachable nightly cron retired (0-for-41 by construction) — workflow_dispatch stays | — | [`5227da4`](https://github.com/techempower-org/mempalace/commit/5227da4) | +| 32 | Searcher: restore mempalace_search on postgres — guard capability-path conversion in union merge (UnboundLocalError since the 2026-07-02 sync) | — | [`96f83d7`](https://github.com/techempower-org/mempalace/commit/96f83d7) | +| 33 | Auto-query: TTL cache for the deterministic depth-refresh injection — repeat fires 0ms vs ~850ms daemon round-trip | — | [`be903e6`](https://github.com/techempower-org/mempalace/commit/be903e6) | +| 34 | Sync upstream/develop through da5a48c (post-v3.5.0): remote MCP server w/ TLS + read-only, graph auto-population, Qdrant facets, list_drawers date filters, 213 commits | — | [`b46f18d`](https://github.com/techempower-org/mempalace/commit/b46f18d) | +| 35 | Auto-query firing fixes: frozen turn counter, dead wing scoring, lowercase entities, turn-1 cadence | — | [`fad3e27`](https://github.com/techempower-org/mempalace/commit/fad3e27) | +| 36 | Auto-query: periodic depth signal, unknown-entity 0->1 bump, broader temporal patterns | — | [`864d7a4`](https://github.com/techempower-org/mempalace/commit/864d7a4) | +| 37 | Sync upstream/develop through v3.5.0 (73e74bf): MCP HTTP transport, source_file filter, checkpoint tool, SessionEnd hook, 185 commits | — | [`8711e1c`](https://github.com/techempower-org/mempalace/commit/8711e1c) | +| 38 | Restore concurrent file mining via parallel-prepare/serial-write (regression from a dropped sync hunk); opt-in --workers | — | [`42a107b`](https://github.com/techempower-org/mempalace/commit/42a107b) | +| 39 | Sync upstream/develop through v3.4.0 (2ec4bae): RFC-001 backend stack, diary checkpoints restored, 113 commits | — | [`373fdf2`](https://github.com/techempower-org/mempalace/commit/373fdf2) | +| 40 | auto_wake: opt-in wake-on-demand for a sleeping palace-daemon host (wake command + /health poll + single retry) | — | [`8e0d896`](https://github.com/techempower-org/mempalace/commit/8e0d896) | +| 41 | AGE graph-walk: auto edge-endpoint indexes in backfill + bind anonymous RELATION targets (mempalace#335) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 42 | pluggable adaptmem_ft encoder backend selectable via MEMPALACE_EMBEDDING_MODEL (closes #308) | — | [`5fba6d8`](https://github.com/techempower-org/mempalace/commit/5fba6d8) | +| 43 | README.md landscape table — refresh upstream MemPalace star count from ~23K → ~53K (current 2026-05-28) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 44 | README.md + docs/ECOSYSTEM.md — soften 'engram-2 17% E2E QA' framing per the 2026-05-24 research doc's unsubstantiated finding (#319) | — | [`ddf00b4`](https://github.com/techempower-org/mempalace/commit/ddf00b4) | +| 45 | kg_llm_extractor rewrites AGE dollar-quote tag in triples so drawers indexing palace source code don't fail at add_triple (#313) | — | [`3fb9428`](https://github.com/techempower-org/mempalace/commit/3fb9428) | +| 46 | scripts/maintain-fork-changes.py + ship-prep step 1: resolve commit:HEAD placeholders and de-dup yaml entries (#316) | — | [`9060e09`](https://github.com/techempower-org/mempalace/commit/9060e09) | +| 47 | scripts/ship-prep.sh — one command bumps README test count and runs all three doc renderers (#312) | — | [`4677db8`](https://github.com/techempower-org/mempalace/commit/4677db8) | +| 48 | mempalace_search MCP input schema accepts fusion_mode (convex\|rrf) and forwards to search_memories (#302) | — | [`f753ec4`](https://github.com/techempower-org/mempalace/commit/f753ec4) | +| 49 | scripts/check-docs.sh finds pytest via main checkout when run from a worktree, fails hard instead of silently skipping test-count check (#311) | — | [`1d19a8b`](https://github.com/techempower-org/mempalace/commit/1d19a8b) | +| 50 | kg_triple_worker retries add_triple within-worker on transient psycopg errors instead of abandoning to lease-reclaim (#298) | — | [`36c0b02`](https://github.com/techempower-org/mempalace/commit/36c0b02) | +| 51 | mempalace_kg_stats returns structured backend-unavailable envelope on transient psycopg failures (#299) | — | [`8fd0b01`](https://github.com/techempower-org/mempalace/commit/8fd0b01) | +| 52 | mempalace why + tunnels — explain a drawer + inventory cross-wing tunnels (slice of #191) | — | [`fdcd0b4`](https://github.com/techempower-org/mempalace/commit/fdcd0b4) | +| 53 | RRF vs convex-blend rerank — A/B measurement on our corpus (#162) | — | [`ea5d567`](https://github.com/techempower-org/mempalace/commit/ea5d567) | +| 54 | KG triples gain SPOC context slot + worker auto-derives valid_from from drawer metadata (#161) | — | [`b87ce05`](https://github.com/techempower-org/mempalace/commit/b87ce05) | +| 55 | mempalace bulk-move — multi-drawer metadata relocation by source wing/room (#191) | — | [`1ca544b`](https://github.com/techempower-org/mempalace/commit/1ca544b) | +| 56 | mempalace move — fast direct-to-daemon single-drawer wing/room relocation (#191) | — | [`d007b6f`](https://github.com/techempower-org/mempalace/commit/d007b6f) | +| 57 | mempalace stats migrates to GET /stats REST + exposes graph/status sections (#191) | — | [`853bb25`](https://github.com/techempower-org/mempalace/commit/853bb25) | +| 58 | mempalace cypher — read-only Cypher query CLI (#191) | — | [`32a41b1`](https://github.com/techempower-org/mempalace/commit/32a41b1) | +| 59 | mempalace graph — fast direct-to-daemon KG structural snapshot (#191) | — | [`499f42d`](https://github.com/techempower-org/mempalace/commit/499f42d) | +| 60 | mempalace list — fast direct-to-daemon drawer browser (#191) | — | [`257137b`](https://github.com/techempower-org/mempalace/commit/257137b) | +| 61 | Recency decay weighting in search + mempalace prune --stale-days CLI (#158) | — | [`558d327`](https://github.com/techempower-org/mempalace/commit/558d327) | +| 62 | mempalace_rate_memory MCP tool + bounded rating signal in search ranking (#159) | — | [`583536c`](https://github.com/techempower-org/mempalace/commit/583536c) | +| 63 | Formalize wing/room derivation order; demote entity detector to last-resort hint (#157) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 64 | RRF fusion mode + convex-vs-RRF A/B harness (#162) | [#247](https://github.com/MemPalace/mempalace/pull/247) | [`6c9d10c`](https://github.com/techempower-org/mempalace/commit/6c9d10c) | +| 65 | mempalace stats: add ROOMS breakdown (drawer count by room) to the dashboard | — | [`1673465`](https://github.com/techempower-org/mempalace/commit/1673465) | +| 66 | Calibrated confidence field on search results + Brier-score eval column | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 67 | Evaluation doc: curated-authority vs auto-mined separation (#202) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 68 | Apply AGE statement_timeout in same transaction as cypher() (PR #228 follow-up) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 69 | LLM-based KG triple extraction: queue table, async worker, llama.cpp on familiar | — | [`59ac0bc`](https://github.com/techempower-org/mempalace/commit/59ac0bc) | +| 70 | Promote verbatim-vs-derivative essay from research/ to README (#170) | — | [`6a264d9`](https://github.com/techempower-org/mempalace/commit/6a264d9) | +| 71 | mempalace stats — palace analytics dashboard (#191) | — | [`6f994fb`](https://github.com/techempower-org/mempalace/commit/6f994fb) | +| 72 | CLI wiring: mempalace mine --source (#57) | — | [`5ed9fa7`](https://github.com/techempower-org/mempalace/commit/5ed9fa7) | +| 73 | Warp terminal source adapter (#62) | — | [`2e85585`](https://github.com/techempower-org/mempalace/commit/2e85585) | +| 74 | OpenCode adapter smoke test against real DB (#56) | — | [`a9ed72b`](https://github.com/techempower-org/mempalace/commit/a9ed72b) | +| 75 | Codex, Gemini, and Aider source adapters (#61, #59) | — | [`0c23165`](https://github.com/techempower-org/mempalace/commit/0c23165) | +| 76 | Filesystem + conversation source adapters (#63) | — | [`9a1facf`](https://github.com/techempower-org/mempalace/commit/9a1facf) | +| 77 | Widen auto-query signal patterns for natural recall phrases | — | [`33e780e`](https://github.com/techempower-org/mempalace/commit/33e780e) | +| 78 | Native rename_wing backend operation + CLI command (#154) | — | [`d045f83`](https://github.com/techempower-org/mempalace/commit/d045f83) | +| 79 | Standalone essay: the verbatim-vs-derivative axis (#47) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 80 | Research doc: uncertainty-aware retrieval analysis (#84) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 81 | Design doc: scope/collection filter on mempalace_search (#76) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 82 | Agent-shaped CLI surface — --json / --quiet for non-MCP integration | — | [`25ed900`](https://github.com/techempower-org/mempalace/commit/25ed900) | +| 83 | Design eval: multi-palace separation — curated vs auto-mined (#45) | — | [`43547c4`](https://github.com/techempower-org/mempalace/commit/43547c4) | +| 84 | Document .sh shim delegation to palace-daemon (counter-position to upstream #1069) | — | [`bf0a4d0`](https://github.com/techempower-org/mempalace/commit/bf0a4d0) | +| 85 | Honor ~/.mempalace/RETIRED marker — refuse default palace, surface retire message | — | [`798cf14`](https://github.com/techempower-org/mempalace/commit/798cf14) | +| 86 | Empty repo .opencode/opencode.json mcp block — disabled flag wasn't being respected | — | [`7133eee`](https://github.com/techempower-org/mempalace/commit/7133eee) | +| 87 | Drop \$comment from .opencode/opencode.json — schema rejects unknown root keys | — | [`637bb01`](https://github.com/techempower-org/mempalace/commit/637bb01) | +| 88 | Disable repo-level MCP entry by default + venv-python fallback | — | [`47018e5`](https://github.com/techempower-org/mempalace/commit/47018e5) | +| 89 | Stub resources/list + prompts/list so MCP clients stop ERROR-logging on connect | — | [`6ca0670`](https://github.com/techempower-org/mempalace/commit/6ca0670) | +| 90 | Bundled OpenCode live-capture plugin that bypasses option-K v1.2.1 bugs (filed upstream as #4, #5) | — | [`5522623`](https://github.com/techempower-org/mempalace/commit/5522623) | +| 91 | Documented OpenCode integration recipe (read-side MCP + push plugin + retrospective adapter) | — | [`60dc9e6`](https://github.com/techempower-org/mempalace/commit/60dc9e6) | +| 92 | .opencode/opencode.json — repo-root MCP config so opencode picks up mempalace automatically | [#1567](https://github.com/MemPalace/mempalace/pull/1567) (CLOSED) | [`ba16b82`](https://github.com/techempower-org/mempalace/commit/ba16b82) | +| 93 | OpenCodeSourceAdapter (RFC 002) — retrospective ingest of OpenCode SQLite sessions | [#1484](https://github.com/MemPalace/mempalace/pull/1484) (OPEN) | [`2ffe652`](https://github.com/techempower-org/mempalace/commit/2ffe652) | +| 94 | mempalace_walk_palace MCP tool — agent walks the palace via AGE Cypher | — | [`8022ecb`](https://github.com/techempower-org/mempalace/commit/8022ecb) | +| 95 | Backfill AGE graph from existing drawer table — restartable, checkpointed | — | [`b3f0206`](https://github.com/techempower-org/mempalace/commit/b3f0206) | +| 96 | Wing/Room/Drawer hierarchy as native AGE nodes; Cypher MATCH walks palace structure | — | [`ff583c0`](https://github.com/techempower-org/mempalace/commit/ff583c0) | +| 97 | Write-through middleware on PostgresCollection — entities populate AGE on every drawer write | — | [`3321d83`](https://github.com/techempower-org/mempalace/commit/3321d83) | +| 98 | KnowledgeGraphAGE API parity with SQLite KG: add_entity, invalidate, query_entity, query_relationship, timeline, seed_from_entity_facts | — | [`ff7187d`](https://github.com/techempower-org/mempalace/commit/ff7187d) | +| 99 | Pending-writes journal + replay so daemon outages stop being silent | — | [`0c34464`](https://github.com/techempower-org/mempalace/commit/0c34464) | +| 100 | MCP server distinguishes 'backend unreachable' from 'no palace found' | — | [`0c34464`](https://github.com/techempower-org/mempalace/commit/0c34464) | +| 101 | Defense-in-depth metadata sanitizer at the chromadb-client chokepoint | — | [`f499814`](https://github.com/techempower-org/mempalace/commit/f499814) | +| 102 | Route Stop/PreCompact hooks through palace-daemon/clients/hook.py | — | [`42ded2e`](https://github.com/techempower-org/mempalace/commit/42ded2e) | +| 103 | KnowledgeGraphAGE skeleton — Apache AGE graph bootstrap over psycopg2 | — | [`a3ee623`](https://github.com/techempower-org/mempalace/commit/a3ee623) | +| 104 | README pivots to the four-layer model + Auto Dream as vindication of the verbatim-vs-derivative axis | — | [`55b36ca`](https://github.com/techempower-org/mempalace/commit/55b36ca) | +| 105 | CI: gate postgres-backend tests against a pgvector service container | — | [`da0bdbb`](https://github.com/techempower-org/mempalace/commit/da0bdbb) | +| 106 | PostgreSQL backend via #665 cherry-pick + fork-side adaptations + smoke tests | [#665](https://github.com/MemPalace/mempalace/pull/665) (OPEN) | [`5e90c72`](https://github.com/techempower-org/mempalace/commit/5e90c72) | +| 107 | daemon-route `mempalace status` / `search` / `mine` when PALACE_DAEMON_URL is set | — | [`22ef562`](https://github.com/techempower-org/mempalace/commit/22ef562) | +| 108 | daemon-route `mcp_server.py` via the `handle_request` JSON-RPC chokepoint | — | [`41359ba`](https://github.com/techempower-org/mempalace/commit/41359ba) | +| 109 | Preserve dashed project names in transcript-derived wings | [#10](https://github.com/MemPalace/mempalace/pull/10) | [`d76134d`](https://github.com/techempower-org/mempalace/commit/d76134d) | +| 110 | Drop wing_ prefix from transcript-derived wings to converge with operator mines | [#9](https://github.com/MemPalace/mempalace/pull/9) | [`86d4700`](https://github.com/techempower-org/mempalace/commit/86d4700) | +| 111 | Retire mempalace_session_recovery collection + read tool | [#8](https://github.com/MemPalace/mempalace/pull/8) | [`0b945e1`](https://github.com/techempower-org/mempalace/commit/0b945e1) | +| 112 | mempalace mined + purge --source-file (mining management surface) | [#7](https://github.com/MemPalace/mempalace/pull/7) | [`2e6ced9`](https://github.com/techempower-org/mempalace/commit/2e6ced9) | +| 113 | Drop hook-side checkpoint diary writes — verbatim-only architecture | [#6](https://github.com/MemPalace/mempalace/pull/6) | [`69768fc`](https://github.com/techempower-org/mempalace/commit/69768fc) | +| 114 | Restore transcript ingest via daemon /mine when PALACE_DAEMON_URL is set | [#2](https://github.com/MemPalace/mempalace/pull/2) | [`09d2ca6`](https://github.com/techempower-org/mempalace/commit/09d2ca6) | +| 115 | `hook_verbatim_mode` config flag preserves system tags + full tool I/O during transcript ingest | — | [`ef98961`](https://github.com/techempower-org/mempalace/commit/ef98961) | +| 116 | Retire the `kind=` filter — structural split made it inert | — | [`7ba28dc`](https://github.com/techempower-org/mempalace/commit/7ba28dc) | +| 117 | Hoist CLOSET_RANK_BOOSTS to module level + record VecRecall ablation finding | — | [`3cb03f3`](https://github.com/techempower-org/mempalace/commit/3cb03f3) | +| 118 | Strip embedded API key from .claude-plugin/ manifests; rely on env inheritance | — | [`9f91e18`](https://github.com/techempower-org/mempalace/commit/9f91e18) | +| 119 | Cherry-pick #1094 — coerce None metadatas at chromadb boundary | [#1094](https://github.com/MemPalace/mempalace/pull/1094) (OPEN) | [`43d728d`](https://github.com/techempower-org/mempalace/commit/43d728d) | +| 120 | Cherry-pick #1087 rewrite — collection.delete(where=) instead of nuke-and-rebuild | [#1087](https://github.com/MemPalace/mempalace/pull/1087) (OPEN) | [`366a9ad`](https://github.com/techempower-org/mempalace/commit/366a9ad) | +| 121 | Canonical YAML manifest + renderer for fork-ahead docs | — | [`5a01aec`](https://github.com/techempower-org/mempalace/commit/5a01aec) | +| 122 | Phase D migration + PreCompact recovery write | — | [`42817d7`](https://github.com/techempower-org/mempalace/commit/42817d7) | +| 123 | Surface drawer_id in search/diary/recovery payloads | — | [`9a8bb77`](https://github.com/techempower-org/mempalace/commit/9a8bb77) | +| 124 | Cherry-pick #1085 — batch ChromaDB inserts in miner (10–30× faster) | [#1085](https://github.com/MemPalace/mempalace/pull/1085) (CLOSED) | [`6be6fff`](https://github.com/techempower-org/mempalace/commit/6be6fff) | +| 125 | scripts/deploy.sh — one-command Syncthing-aware redeploy | — | [`8252025`](https://github.com/techempower-org/mempalace/commit/8252025) | +| 126 | Phases A–C of the checkpoint collection split | — | [`e266365`](https://github.com/techempower-org/mempalace/commit/e266365) | +| 127 | kind= filter on search_memories excludes Stop-hook checkpoints (transitional) | — | [`f9f5cc4`](https://github.com/techempower-org/mempalace/commit/f9f5cc4) | ### Recently merged into upstream @@ -677,7 +678,7 @@ TechEmpower's production fork of [MemPalace/mempalace](https://github.com/MemPal ```bash source .venv/bin/activate -python -m pytest tests/ -q # 7050 tests (benchmarks deselected) +python -m pytest tests/ -q # 7057 tests (benchmarks deselected) mempalace status # check palace state mempalace search "query" # test search python -m mempalace.mcp_server # run MCP server standalone