From 2a9997dd10eda8e2692e2d53c6c79e34f392f0dc Mon Sep 17 00:00:00 2001 From: chouswei <104312007+chouswei@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:07:07 +0800 Subject: [PATCH 1/2] Bump package identity to 0.19.22. Hatch, project.toml, changelog, and the version map name the safe serve upgrade path (#208, MN-REQ-06.14). Not 0.20. --- AGENTS.md | 2 +- CHANGELOG.md | 14 ++++++++++++++ docs/ROADMAP.md | 12 ++++++------ docs/operations/product-gateway-contract.md | 6 +++--- parts/common/memnet/memnet/__init__.py | 2 +- project.toml | 2 +- tests/test_doc_gate_readiness.py | 4 ++-- tests/test_sysml_cluster_vs_slice.py | 4 ++-- tests/test_sysml_lan_mcp_front.py | 4 ++-- tests/test_sysml_product_gateway.py | 4 ++-- 10 files changed, 34 insertions(+), 20 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 674387a..7701049 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ LLM hub for this system repo. Prefer in-repo skills and docs over ad-hoc inventi ## Mission -**MemNet** (Net of Memory) is **mission working memory** — a session graph (GQL **node**/vertex, **edge**/relationship, **property**) **between** LLM call pipelines and data search, not the corpus and not GraphRAG. Agents read a bounded **live pin map** each turn and write in the same **GQL (openCypher-shaped)** family — redefined **Write = display** via shaped subgraph emit ([`docs/grammar/gql-wire-profile.md`](docs/grammar/gql-wire-profile.md)). In-session recall is **serial**: kind/keyword cue, then `pin_map` neighbourhood. Primary read: MCP `pin_map` / CLI `query pin-map`; leftover `query_warm` / `query warm` are leftover aliases. Aims (MN-REQ-00): save wall-clock time and tokens while keeping factual accuracy. Aids **system**, **programme**, **software**, **firmware**, **hardware**, and **documentation**. Transport: **in-process first** (single-agent; TCP fallback). **Multitask** requires TCP serve or streamable-http MCP — see Multitask policy below. This repo is **engine + generic memnet-mcp** only — novel-writer dropped. Repo product **0.19.21** (Hatch SSOT; last published PyPI **`memnet-llm==0.19.20`** until this cut is uploaded; 0.19.21 honesty `c`: serve per-request output isolation, LF-only statement split, undeclared-property snapshot save, label case-fold, and housekeep GQL endpoint orphans with `prune_referenced` refusal (#206). Earlier `c` cuts include 0.19.20 memnet-mcp product gateway `--transport gateway` (#203, MN-REQ-06.12), 0.19.19 doc-gate readiness (#201) and snapshot / value cap / WHERE / ACL who / expire-save fixes (#202), 0.19.18 snap_model bounded session grain (#199), 0.19.17 product-gate cap contract (#196) and admin-only serve usage report (#197), 0.19.16 shared Path-B ingest defaults 2000 nodes / 2000 edges, 0.19.15 storage role labels (#186), Path-B SysML ingest kinds (#188), Neo4j retired (#189), 0.19.14 TTL expire restore by known sid, and 0.19.11 catalog cross-session satisfy locators + CousinSysMLEdge mustNotInventUploadBind). **1.0** = 0.5–0.8 claimed (unclaimed). See `README.md`, [`docs/SHAPE.md`](docs/SHAPE.md), and `docs/grammar/`. +**MemNet** (Net of Memory) is **mission working memory** — a session graph (GQL **node**/vertex, **edge**/relationship, **property**) **between** LLM call pipelines and data search, not the corpus and not GraphRAG. Agents read a bounded **live pin map** each turn and write in the same **GQL (openCypher-shaped)** family — redefined **Write = display** via shaped subgraph emit ([`docs/grammar/gql-wire-profile.md`](docs/grammar/gql-wire-profile.md)). In-session recall is **serial**: kind/keyword cue, then `pin_map` neighbourhood. Primary read: MCP `pin_map` / CLI `query pin-map`; leftover `query_warm` / `query warm` are leftover aliases. Aims (MN-REQ-00): save wall-clock time and tokens while keeping factual accuracy. Aids **system**, **programme**, **software**, **firmware**, **hardware**, and **documentation**. Transport: **in-process first** (single-agent; TCP fallback). **Multitask** requires TCP serve or streamable-http MCP — see Multitask policy below. This repo is **engine + generic memnet-mcp** only — novel-writer dropped. Repo product **0.19.22** (Hatch SSOT; last published PyPI **`memnet-llm==0.19.21`** until this cut is uploaded; 0.19.22 honesty `c`: safe serve upgrade — admin `upgrade-prepare` drain, upgrade manifest, startup restore, client retry, `memnet-upgrade` with rollback (#208, MN-REQ-06.14). Earlier `c` cuts include 0.19.21 serve per-request output isolation, LF-only statement split, undeclared-property snapshot save, label case-fold, and housekeep GQL endpoint orphans with `prune_referenced` refusal (#206), 0.19.20 memnet-mcp product gateway `--transport gateway` (#203, MN-REQ-06.12), 0.19.19 doc-gate readiness (#201) and snapshot / value cap / WHERE / ACL who / expire-save fixes (#202), 0.19.18 snap_model bounded session grain (#199), 0.19.17 product-gate cap contract (#196) and admin-only serve usage report (#197), 0.19.16 shared Path-B ingest defaults 2000 nodes / 2000 edges, 0.19.15 storage role labels (#186), Path-B SysML ingest kinds (#188), Neo4j retired (#189), 0.19.14 TTL expire restore by known sid, and 0.19.11 catalog cross-session satisfy locators + CousinSysMLEdge mustNotInventUploadBind). **1.0** = 0.5–0.8 claimed (unclaimed). See `README.md`, [`docs/SHAPE.md`](docs/SHAPE.md), and `docs/grammar/`. ## Where to look diff --git a/CHANGELOG.md b/CHANGELOG.md index 9842909..1940916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,20 @@ This project uses Semantic Versioning as **interpreted for MemNet**: package `a. - **Invent only — ClusterRoute vs SliceHandCarry (#191 / #47 cousin)** — `MemNetTwoMoves` outside `MemNetSystem` (`MN-REQ-06.9` + `MN-REQ-06.10` / `MN-VER-06-S08`). ClusterRoute = where the session lives (`MemNetLanMcpFront`; one owner; `pin_map` / `find` SHALL NOT span backends). SliceHandCarry = explicit copy into another session (`export_pin_map` or `session_save` → LAN file copy → dest import/`session_load`; `import_slice` same-serve only). Not a live hop. `import_slice(from_url)` not shipped. tip≠face. `inventOnly=true`; `implemented=false`; no engine code; no SemVer bump. Wire: [`docs/operations/cluster-route-vs-slice-hand-carry.md`](docs/operations/cluster-route-vs-slice-hand-carry.md). - **Invent only — LAN MCP front over several serves (#191)** — `MemNetLanMcpFront` outside `MemNetSystem` (`MN-REQ-06.9` / `MN-VER-06-S07`). One MCP catalogue, N LAN `memnet serve` backends; `SessionOwnerRegistry` is owner (explicit pin allowed; silent hash is not sole routing). One owner per session; `pin_map` / `find` SHALL NOT span backends. Cousin of #47 (peer sid handoff), not the same invent. tip≠face. `inventOnly=true`; `implemented=false`; no engine code; no SemVer bump. Wire: [`docs/operations/memnet-lan-mcp-front.md`](docs/operations/memnet-lan-mcp-front.md). +## [0.19.22] - 2026-10-09 + +### Added +- **Safe serve upgrade (MN-REQ-06.14, #208)** — `memnet admin upgrade-prepare --state-dir "$MEMNET_STATE_DIR"` (admin credential `MEMNET_ADMIN_TOKEN`; not an agent MCP tool) drains a running serve: new `session_open` gets the retryable refusal `@ERR: serve_draining|retry_after_s=`, in-flight commands finish, then every loaded session is written with the lossless snapshot writer. Ready only on `@STAT: upgrade_prepare|ready|`. If any session cannot be saved (`snapshot_unsaveable` or another save error), it is named in `upgrade-blocked.json`, no ready manifest is written, and the serve keeps working; `--allow-unsaved` is an explicit override. +- **Upgrade manifest** — `upgrade-manifest.json` and `upgrade-snapshots/` under `MEMNET_STATE_DIR` record session ids, row and edge counts, sha256 checksums, the serve version, and snapshot format `1`. An ACL and clock passport rides as a `# upgrade-passport` line that older v1 loaders skip, so a rollback can still read the graph. +- **Startup restore** — a new serve reads the manifest, reloads each session under the same id (ACL bindings, expiry instant, and house kept), checks counts and checksums, and prints `@STAT: upgrade_restore|ok||failed|`. An unsupported format or checksum/parse failure exits `3` without touching the files. `memnet admin upgrade-retire` removes the snapshots after a clean restore. +- **Client retry** — memnet-mcp and the product gateway retry `serve_draining` and a brief connection refusal for `MEMNET_UPGRADE_RETRY_S` (default 30 s; `0` disables). A command the serve already accepted is not retried on timeout. +- **`memnet-upgrade` helper** — side-by-side venv swap: refuses without `--clients-ready`, preflights the new interpreter's snapshot formats, runs the drain, swaps `ExecStart=` (with `.bak`), optionally moves a gateway product's `pinned_version` after the drain and before the new process listens, restarts, and on any restore failure automatically puts back the previous unit and pin and restarts again. +- **Operations doc** — [`docs/operations/safe-upgrade.md`](docs/operations/safe-upgrade.md) (order: clients first, then serve; rpi5-syson, Endleaf engine, droplet gateway; rollback). + +### Changed +- **Upgrading from 0.19.21 still uses a manual save and reload** — 0.19.21 has no drain command, so the move from 0.19.21 to 0.19.22 must save every session, swap the venv, and reload by hand as before. The built-in `upgrade-prepare` → restore path applies to upgrades from 0.19.22 onward. +- **Package identity 0.19.22** — Hatch / `project.toml` / `memnet.__version__` honesty cut on **0.19**. Agent loop (`cue → pin_map → mutate`) unchanged. + ## [0.19.21] - 2026-10-09 ### Fixed diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 1dd18d5..53a7c0a 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -4,9 +4,9 @@ **Audience:** product developers. Dialect teach = **GQL** ([`grammar/gql-wire-profile.md`](grammar/gql-wire-profile.md)). Product shape: [`SHAPE.md`](SHAPE.md). British English. -**Package now:** Hatch **0.19.21** (`memnet.__version__`). Last published PyPI wheel is **`memnet-llm==0.19.20`** until this cut is uploaded. Numbered extras **0.10–0.19** are in this package (unchanged). **0.19.21** honesty `c` is serve per-request output isolation, LF-only statement split, undeclared-property snapshot save, label case-fold, and housekeep GQL endpoint orphans with `prune_referenced` refusal (#206). **0.19.20** honesty `c` was the memnet-mcp product gateway (`--transport gateway`, #203, MN-REQ-06.12). **0.19.19** honesty `c` was the one-session-per-document readiness probe (#201) plus lossless snapshot round-trip, one decoded value cap, honoured-or-refused WHERE, ACL who on save/load/close, and expire-save failures kept live (#202). **0.19.18** honesty `c` was snap_model bounded session grain (#199). **0.19.17** honesty `c` was the product-gate cap contract (#196) and admin-only serve usage report (#197). **0.19.16** honesty `c` was shared Path-B ingest defaults (2000 nodes / 2000 edges). **0.19.15** honesty `c` was storage role labels (#186), Path-B SysML ingest kinds (#188), and Neo4j retired (#189). **0.19.14** honesty `c` was TTL expire restore by known sid. **0.19.11** honesty `c` was the tip roll for [#168](https://github.com/chouswei/MemNet/pull/168) (catalog cross-session satisfy locators + `CousinSysMLEdge` `mustNotInventUploadBind`; do not claim tip=face). **0.19.10** honesty `c` keeps CueConflict for MATCH_L `|Q|>1` only; codebook miss emits CueMiss / Peak_L. **0.19.9** honesty `c` maps Path-B SysML `connection` / `link` to `:CON` (teach already said CON; ingest had mapped defs to PRT). **0.19.8** honesty `c` mints leftover nicknames on **all** snapshot records (not only catalog PKG) and accepts camelCase product relations (`inFile`) so `session_save` → `session_load` round-trips mission graphs. GraphGlot parse front is on master (#109 @ 73a63c9b). Neo4j cabinet is retired (#187); **0.7** is adapter + operator round trip (no runtime caller). **1.0** is still unclaimed (0.5–0.8). +**Package now:** Hatch **0.19.22** (`memnet.__version__`). Last published PyPI wheel is **`memnet-llm==0.19.21`** until this cut is uploaded. Numbered extras **0.10–0.19** are in this package (unchanged). **0.19.22** honesty `c` is the safe serve upgrade path (#208, MN-REQ-06.14): admin `upgrade-prepare` drain, upgrade manifest, startup restore, client retry, and `memnet-upgrade` with automatic rollback. **0.19.21** honesty `c` was serve per-request output isolation, LF-only statement split, undeclared-property snapshot save, label case-fold, and housekeep GQL endpoint orphans with `prune_referenced` refusal (#206). **0.19.20** honesty `c` was the memnet-mcp product gateway (`--transport gateway`, #203, MN-REQ-06.12). **0.19.19** honesty `c` was the one-session-per-document readiness probe (#201) plus lossless snapshot round-trip, one decoded value cap, honoured-or-refused WHERE, ACL who on save/load/close, and expire-save failures kept live (#202). **0.19.18** honesty `c` was snap_model bounded session grain (#199). **0.19.17** honesty `c` was the product-gate cap contract (#196) and admin-only serve usage report (#197). **0.19.16** honesty `c` was shared Path-B ingest defaults (2000 nodes / 2000 edges). **0.19.15** honesty `c` was storage role labels (#186), Path-B SysML ingest kinds (#188), and Neo4j retired (#189). **0.19.14** honesty `c` was TTL expire restore by known sid. **0.19.11** honesty `c` was the tip roll for [#168](https://github.com/chouswei/MemNet/pull/168) (catalog cross-session satisfy locators + `CousinSysMLEdge` `mustNotInventUploadBind`; do not claim tip=face). **0.19.10** honesty `c` keeps CueConflict for MATCH_L `|Q|>1` only; codebook miss emits CueMiss / Peak_L. **0.19.9** honesty `c` maps Path-B SysML `connection` / `link` to `:CON` (teach already said CON; ingest had mapped defs to PRT). **0.19.8** honesty `c` mints leftover nicknames on **all** snapshot records (not only catalog PKG) and accepts camelCase product relations (`inFile`) so `session_save` → `session_load` round-trips mission graphs. GraphGlot parse front is on master (#109 @ 73a63c9b). Neo4j cabinet is retired (#187); **0.7** is adapter + operator round trip (no runtime caller). **1.0** is still unclaimed (0.5–0.8). -**Last updated:** 2026-10-09 (`a.b.c` law locked; package **0.19.21** honesty `c` — serve request isolation and housekeep endpoint fixes (#206); extras **0.10–0.19** unchanged; Hatch **0.19.21**; last published PyPI **`memnet-llm==0.19.20`**; do not claim **1.0**; do not invent a **0.20** extra; do not claim tip=face). +**Last updated:** 2026-10-09 (`a.b.c` law locked; package **0.19.22** honesty `c` — safe serve upgrade (#208); extras **0.10–0.19** unchanged; Hatch **0.19.22**; last published PyPI **`memnet-llm==0.19.21`**; do not claim **1.0**; do not invent a **0.20** extra; do not claim tip=face). Patch notes: [`../CHANGELOG.md`](../CHANGELOG.md). CHANGELOG still follows Keep a Changelog. This file is how MemNet **interprets** SemVer — not a silent switch to npm-strict major=breaking for 0.x extras. @@ -45,7 +45,7 @@ Pure efficiency / speed on the current loop is **`0.19.c`**, not `0.20`. ### Consequences -- Package is **0.19.21**. Extras **0.10–0.19** stay the owns table below. **1.0** stays unclaimed. +- Package is **0.19.22**. Extras **0.10–0.19** stay the owns table below. **1.0** stays unclaimed. - **1.0 does not wait** on 0.19.c, efficiency, HostSearch, Peak_L, catalog Snap, N-server, or GraphGlot. - A new cabinet adapter does **not** move `a`. Hosted Agens as a product service, first-class `PORT`, and full ACL modes / `session_token` stay Later / unnumbered until a cut exists. @@ -99,8 +99,8 @@ Handoff = **session id** (+ cue / write scope). Peers **re-`pin_map`** from labe | **0.7.0** | Adapter + operator round trip (no runtime caller); `liveCabinetClaimed=true`. Server not vendored. Fake + skip unless `MEMNET_AGENSGRAPH_URL`. No runtime hydrate/flush caller | **Shipped** (`v0.7.0`); claim narrowed (#187) | | **0.8.0** | GQL-only **teach** + product **shape for people** (`SHAPE.md`, playbook, application-note contract, Multitask honesty). Docs only. **No** engine cut. Cabinet stays claimed | **Shipped** (`v0.8.0`) | | **0.9.0** | Neo4j `DurableStoreAdapter` client (`memnet-llm[neo4j]`); factory both-URL rule; [`cabinet/neo4j-buffer.md`](cabinet/neo4j-buffer.md). Live round-trip claimed later as extra **0.14**. Cabinet extra, **not** a 1.0 gate | **Shipped** (`v0.9.0` era; extras later packaged as 0.19.0) | -| **0.10–0.19** | Numbered extras (table below). Each row is one `b` (usage-method revision). Same pattern as 0.9: **not** 1.0 gates | **Packaged** (Hatch **0.19.21**; last published PyPI **`memnet-llm==0.19.20`**; extras first shipped as 0.19.0) | -| **1.0.0** | **Claim** of **0.5 + 0.6 + 0.7 + 0.8**. Shape mature for people. Not GraphRAG. Not cabinet-only. Not a new engine. This is `a=1` | **Claim when coordinator tags** — package **0.19.21** does not claim 1.0 | +| **0.10–0.19** | Numbered extras (table below). Each row is one `b` (usage-method revision). Same pattern as 0.9: **not** 1.0 gates | **Packaged** (Hatch **0.19.22**; last published PyPI **`memnet-llm==0.19.21`**; extras first shipped as 0.19.0) | +| **1.0.0** | **Claim** of **0.5 + 0.6 + 0.7 + 0.8**. Shape mature for people. Not GraphRAG. Not cabinet-only. Not a new engine. This is `a=1` | **Claim when coordinator tags** — package **0.19.22** does not claim 1.0 | | **Later** | Grammar Open / hosted product / leftover ACL; N-server research (#47); LAN MCP front invent (#191). GraphGlot parse-front is **shipped**. If **1.0 tags first**, remaining extras become **1.1, 1.2, …** with the same owns (`b` after the claim) | **Out** of 1.0 | **1.0 MAY ship from 0.9** (claim only). **0.10+ MAY ship before 1.0** as extras (`b` on `a=0`). Do not wait for the other. User-pack GQL rewrite is **sibling** (`chouswei/cursor-user-skills`), not this repo. @@ -135,7 +135,7 @@ Do **not** treat leftover 0.9 identity as a live-Neo4j claim. Do **not** claim * ## Numbered extras (0.10–0.19) -One concern per **`b`** (usage-method revision). Dependency order. **In package 0.19.0** (git tag by coordinator). Skip a `b` only if the coordinator writes the skip in CHANGELOG; do not fuse two usage-method changes into one `b`. Cuts on the same method are **`c`** (0.19.1–0.19.21). There is **no** 0.20 extra row. +One concern per **`b`** (usage-method revision). Dependency order. **In package 0.19.0** (git tag by coordinator). Skip a `b` only if the coordinator writes the skip in CHANGELOG; do not fuse two usage-method changes into one `b`. Cuts on the same method are **`c`** (0.19.1–0.19.22). There is **no** 0.20 extra row. | Version | Owns | Depends on | MUST NOT | |---------|------|------------|----------| diff --git a/docs/operations/product-gateway-contract.md b/docs/operations/product-gateway-contract.md index c47a2cc..2106c61 100644 --- a/docs/operations/product-gateway-contract.md +++ b/docs/operations/product-gateway-contract.md @@ -4,7 +4,7 @@ The gateway is `memnet-mcp --transport gateway`. It is not a separate droplet shim. The #191 catalogue (MCP tool union, SnapshotHandCarry) stays invent-only on the parent part. -Online products reach a `memnet serve` only through this process. Sample pin below is **0.19.21**. Backend ids are arbitrary strings. The live Endleaf backend id is `pi-endleaf`. +Online products reach a `memnet serve` only through this process. Sample pin below is **0.19.22**. Backend ids are arbitrary strings. The live Endleaf backend id is `pi-endleaf`. stdio and streamable-http do not read the registry. With no `MEMNET_GATEWAY_CONFIG`, one memnet-mcp process stays a single in-process or streamable-http server. @@ -91,7 +91,7 @@ The gateway body ceiling is `body_max_bytes`, default **4194304** (4 MiB), the s ## Version pin -Each product has `pinned_version` (sample below, `0.19.21`). Before a forward, the gateway calls `version` on that backend and requires `@VER: memnet|`. A mismatch is `gateway_backend_version_mismatch` and the argv is not sent. Upgrading a serve without changing the pin refuses the product. Changing the pin is how the owner is notified: edit the config and restart the gateway. +Each product has `pinned_version` (sample below, `0.19.22`). Before a forward, the gateway calls `version` on that backend and requires `@VER: memnet|`. A mismatch is `gateway_backend_version_mismatch` and the argv is not sent. Upgrading a serve without changing the pin refuses the product. Changing the pin is how the owner is notified: edit the config and restart the gateway. `version_cache_s` defaults to 15. Set `0` to check every call. A serve upgraded inside a non-zero window can still be reached until the cache expires. @@ -190,7 +190,7 @@ The product host exports the plaintext bearer as `MEMNET_GATEWAY_TOKEN` (for exa "endleaf": { "backends": ["pi-endleaf"], "houses": {"syson": "pi-endleaf"}, - "pinned_version": "0.19.21", + "pinned_version": "0.19.22", "credentials": [ {"id": "endleaf-1", "sha256": "", "revoked": false} ] diff --git a/parts/common/memnet/memnet/__init__.py b/parts/common/memnet/memnet/__init__.py index f6b380b..5237858 100644 --- a/parts/common/memnet/memnet/__init__.py +++ b/parts/common/memnet/memnet/__init__.py @@ -1,3 +1,3 @@ """MemNet — mission working memory for LLM agents (session graph + pin_map).""" -__version__ = "0.19.21" +__version__ = "0.19.22" diff --git a/project.toml b/project.toml index e31aa93..ec97a37 100644 --- a/project.toml +++ b/project.toml @@ -1,7 +1,7 @@ [project] name = "memnet" repo = "MemNet" -version = "0.19.21" +version = "0.19.22" description = "Mission working memory for LLM agents: session graph + pin_map, not a RAG corpus" # No pcba-libs pins — software-only system (see LAYOUT.md). diff --git a/tests/test_doc_gate_readiness.py b/tests/test_doc_gate_readiness.py index 9588379..38b56bb 100644 --- a/tests/test_doc_gate_readiness.py +++ b/tests/test_doc_gate_readiness.py @@ -51,8 +51,8 @@ from memnet.snapshot import SNAPSHOT_MAGIC -def test_version_is_0_19_21(): - assert __version__ == "0.19.21" +def test_version_is_0_19_22(): + assert __version__ == "0.19.22" def test_cap_contract_needles_unchanged(): diff --git a/tests/test_sysml_cluster_vs_slice.py b/tests/test_sysml_cluster_vs_slice.py index 50b5a18..546befe 100644 --- a/tests/test_sysml_cluster_vs_slice.py +++ b/tests/test_sysml_cluster_vs_slice.py @@ -111,5 +111,5 @@ def test_teach_one_screen_contrast(): def test_no_semver_bump_in_this_invent(): text = _PROJECT.read_text(encoding="utf-8") - assert 'version = "0.19.21"' in text - assert "0.19.22" not in text + assert 'version = "0.19.22"' in text + assert "0.19.23" not in text diff --git a/tests/test_sysml_lan_mcp_front.py b/tests/test_sysml_lan_mcp_front.py index ab153f9..e1cef72 100644 --- a/tests/test_sysml_lan_mcp_front.py +++ b/tests/test_sysml_lan_mcp_front.py @@ -120,5 +120,5 @@ def test_teach_contrasts_47_and_worth_building(): def test_no_semver_bump_in_this_invent(): text = _PROJECT.read_text(encoding="utf-8") - assert 'version = "0.19.21"' in text - assert "0.19.22" not in text + assert 'version = "0.19.22"' in text + assert "0.19.23" not in text diff --git a/tests/test_sysml_product_gateway.py b/tests/test_sysml_product_gateway.py index 8cd715f..2164ccf 100644 --- a/tests/test_sysml_product_gateway.py +++ b/tests/test_sysml_product_gateway.py @@ -64,5 +64,5 @@ def test_contract_is_not_draft_and_no_semver_bump(): assert "worthBuildingVisible=true" in nest assert "MemNetProductGateway" in nest project = PROJECT.read_text(encoding="utf-8") - assert 'version = "0.19.21"' in project - assert "0.19.22" not in project + assert 'version = "0.19.22"' in project + assert "0.19.23" not in project From fa9e03ba5937884f98e525e63d8a109d3f17a3aa Mon Sep 17 00:00:00 2001 From: chouswei <104312007+chouswei@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:39:24 +0800 Subject: [PATCH 2/2] Rewrite the 0.19.22 changelog to the trimmed safe upgrade scope (#210). --- AGENTS.md | 2 +- CHANGELOG.md | 11 +++++------ docs/ROADMAP.md | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7701049..b6520f3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ LLM hub for this system repo. Prefer in-repo skills and docs over ad-hoc inventi ## Mission -**MemNet** (Net of Memory) is **mission working memory** — a session graph (GQL **node**/vertex, **edge**/relationship, **property**) **between** LLM call pipelines and data search, not the corpus and not GraphRAG. Agents read a bounded **live pin map** each turn and write in the same **GQL (openCypher-shaped)** family — redefined **Write = display** via shaped subgraph emit ([`docs/grammar/gql-wire-profile.md`](docs/grammar/gql-wire-profile.md)). In-session recall is **serial**: kind/keyword cue, then `pin_map` neighbourhood. Primary read: MCP `pin_map` / CLI `query pin-map`; leftover `query_warm` / `query warm` are leftover aliases. Aims (MN-REQ-00): save wall-clock time and tokens while keeping factual accuracy. Aids **system**, **programme**, **software**, **firmware**, **hardware**, and **documentation**. Transport: **in-process first** (single-agent; TCP fallback). **Multitask** requires TCP serve or streamable-http MCP — see Multitask policy below. This repo is **engine + generic memnet-mcp** only — novel-writer dropped. Repo product **0.19.22** (Hatch SSOT; last published PyPI **`memnet-llm==0.19.21`** until this cut is uploaded; 0.19.22 honesty `c`: safe serve upgrade — admin `upgrade-prepare` drain, upgrade manifest, startup restore, client retry, `memnet-upgrade` with rollback (#208, MN-REQ-06.14). Earlier `c` cuts include 0.19.21 serve per-request output isolation, LF-only statement split, undeclared-property snapshot save, label case-fold, and housekeep GQL endpoint orphans with `prune_referenced` refusal (#206), 0.19.20 memnet-mcp product gateway `--transport gateway` (#203, MN-REQ-06.12), 0.19.19 doc-gate readiness (#201) and snapshot / value cap / WHERE / ACL who / expire-save fixes (#202), 0.19.18 snap_model bounded session grain (#199), 0.19.17 product-gate cap contract (#196) and admin-only serve usage report (#197), 0.19.16 shared Path-B ingest defaults 2000 nodes / 2000 edges, 0.19.15 storage role labels (#186), Path-B SysML ingest kinds (#188), Neo4j retired (#189), 0.19.14 TTL expire restore by known sid, and 0.19.11 catalog cross-session satisfy locators + CousinSysMLEdge mustNotInventUploadBind). **1.0** = 0.5–0.8 claimed (unclaimed). See `README.md`, [`docs/SHAPE.md`](docs/SHAPE.md), and `docs/grammar/`. +**MemNet** (Net of Memory) is **mission working memory** — a session graph (GQL **node**/vertex, **edge**/relationship, **property**) **between** LLM call pipelines and data search, not the corpus and not GraphRAG. Agents read a bounded **live pin map** each turn and write in the same **GQL (openCypher-shaped)** family — redefined **Write = display** via shaped subgraph emit ([`docs/grammar/gql-wire-profile.md`](docs/grammar/gql-wire-profile.md)). In-session recall is **serial**: kind/keyword cue, then `pin_map` neighbourhood. Primary read: MCP `pin_map` / CLI `query pin-map`; leftover `query_warm` / `query warm` are leftover aliases. Aims (MN-REQ-00): save wall-clock time and tokens while keeping factual accuracy. Aids **system**, **programme**, **software**, **firmware**, **hardware**, and **documentation**. Transport: **in-process first** (single-agent; TCP fallback). **Multitask** requires TCP serve or streamable-http MCP — see Multitask policy below. This repo is **engine + generic memnet-mcp** only — novel-writer dropped. Repo product **0.19.22** (Hatch SSOT; last published PyPI **`memnet-llm==0.19.21`** until this cut is uploaded; 0.19.22 honesty `c`: safe serve upgrade — admin `upgrade-prepare` drain, lossless snapshot with manifest, startup restore under the same ids, automatic retire (#208 trimmed by #210, MN-REQ-06.14). Earlier `c` cuts include 0.19.21 serve per-request output isolation, LF-only statement split, undeclared-property snapshot save, label case-fold, and housekeep GQL endpoint orphans with `prune_referenced` refusal (#206), 0.19.20 memnet-mcp product gateway `--transport gateway` (#203, MN-REQ-06.12), 0.19.19 doc-gate readiness (#201) and snapshot / value cap / WHERE / ACL who / expire-save fixes (#202), 0.19.18 snap_model bounded session grain (#199), 0.19.17 product-gate cap contract (#196) and admin-only serve usage report (#197), 0.19.16 shared Path-B ingest defaults 2000 nodes / 2000 edges, 0.19.15 storage role labels (#186), Path-B SysML ingest kinds (#188), Neo4j retired (#189), 0.19.14 TTL expire restore by known sid, and 0.19.11 catalog cross-session satisfy locators + CousinSysMLEdge mustNotInventUploadBind). **1.0** = 0.5–0.8 claimed (unclaimed). See `README.md`, [`docs/SHAPE.md`](docs/SHAPE.md), and `docs/grammar/`. ## Where to look diff --git a/CHANGELOG.md b/CHANGELOG.md index 1940916..bc2841d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,12 +14,11 @@ This project uses Semantic Versioning as **interpreted for MemNet**: package `a. ## [0.19.22] - 2026-10-09 ### Added -- **Safe serve upgrade (MN-REQ-06.14, #208)** — `memnet admin upgrade-prepare --state-dir "$MEMNET_STATE_DIR"` (admin credential `MEMNET_ADMIN_TOKEN`; not an agent MCP tool) drains a running serve: new `session_open` gets the retryable refusal `@ERR: serve_draining|retry_after_s=`, in-flight commands finish, then every loaded session is written with the lossless snapshot writer. Ready only on `@STAT: upgrade_prepare|ready|`. If any session cannot be saved (`snapshot_unsaveable` or another save error), it is named in `upgrade-blocked.json`, no ready manifest is written, and the serve keeps working; `--allow-unsaved` is an explicit override. -- **Upgrade manifest** — `upgrade-manifest.json` and `upgrade-snapshots/` under `MEMNET_STATE_DIR` record session ids, row and edge counts, sha256 checksums, the serve version, and snapshot format `1`. An ACL and clock passport rides as a `# upgrade-passport` line that older v1 loaders skip, so a rollback can still read the graph. -- **Startup restore** — a new serve reads the manifest, reloads each session under the same id (ACL bindings, expiry instant, and house kept), checks counts and checksums, and prints `@STAT: upgrade_restore|ok||failed|`. An unsupported format or checksum/parse failure exits `3` without touching the files. `memnet admin upgrade-retire` removes the snapshots after a clean restore. -- **Client retry** — memnet-mcp and the product gateway retry `serve_draining` and a brief connection refusal for `MEMNET_UPGRADE_RETRY_S` (default 30 s; `0` disables). A command the serve already accepted is not retried on timeout. -- **`memnet-upgrade` helper** — side-by-side venv swap: refuses without `--clients-ready`, preflights the new interpreter's snapshot formats, runs the drain, swaps `ExecStart=` (with `.bak`), optionally moves a gateway product's `pinned_version` after the drain and before the new process listens, restarts, and on any restore failure automatically puts back the previous unit and pin and restarts again. -- **Operations doc** — [`docs/operations/safe-upgrade.md`](docs/operations/safe-upgrade.md) (order: clients first, then serve; rpi5-syson, Endleaf engine, droplet gateway; rollback). +- **Safe serve upgrade — admin drain (MN-REQ-06.14, #208 trimmed by #210)** — `memnet admin upgrade-prepare --state-dir "$MEMNET_STATE_DIR"` (admin credential `MEMNET_ADMIN_TOKEN`; unset is `admin_unconfigured`, a mismatch is `admin_denied`; not an agent MCP tool). New `session_open` is refused with `@ERR: serve_draining|retry_after_s=`, and the drain waits for in-flight commands to finish before snapshotting. +- **Lossless snapshot of every session with a manifest** — every loaded session is written with the lossless snapshot writer, and `upgrade-manifest.json` under `MEMNET_STATE_DIR` records session ids, row and edge counts, sha256 checksums, the serve version, and snapshot format `1`. Ready-to-stop is `@STAT: upgrade_prepare|ready|`. A session that cannot be snapshotted (`snapshot_unsaveable` or another save error) is named in `upgrade-blocked.json`, the command exits non-zero, and no ready manifest is written; `--allow-unsaved` is the explicit override when those named sessions may be dropped. +- **Startup restore** — a new serve on the same `MEMNET_STATE_DIR` reads the manifest, reloads each session under the same id with its ACL bindings, TTL expiry, and house, checks counts and checksums, and prints `@STAT: upgrade_restore|ok||failed|`. A checksum or parse failure, or an unsupported snapshot format, exits `3` and leaves the files untouched. +- **Automatic retire** — a clean restore retires the manifest in that same startup, so a later restart does not replay the snapshots. Snapshot files stay on disk. A failed restore does not retire. +- **Operations doc** — short procedure in [`docs/operations/safe-upgrade.md`](docs/operations/safe-upgrade.md) (side-by-side venv, drain, restart, restore stat, rollback). ### Changed - **Upgrading from 0.19.21 still uses a manual save and reload** — 0.19.21 has no drain command, so the move from 0.19.21 to 0.19.22 must save every session, swap the venv, and reload by hand as before. The built-in `upgrade-prepare` → restore path applies to upgrades from 0.19.22 onward. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 53a7c0a..8035dfc 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -4,7 +4,7 @@ **Audience:** product developers. Dialect teach = **GQL** ([`grammar/gql-wire-profile.md`](grammar/gql-wire-profile.md)). Product shape: [`SHAPE.md`](SHAPE.md). British English. -**Package now:** Hatch **0.19.22** (`memnet.__version__`). Last published PyPI wheel is **`memnet-llm==0.19.21`** until this cut is uploaded. Numbered extras **0.10–0.19** are in this package (unchanged). **0.19.22** honesty `c` is the safe serve upgrade path (#208, MN-REQ-06.14): admin `upgrade-prepare` drain, upgrade manifest, startup restore, client retry, and `memnet-upgrade` with automatic rollback. **0.19.21** honesty `c` was serve per-request output isolation, LF-only statement split, undeclared-property snapshot save, label case-fold, and housekeep GQL endpoint orphans with `prune_referenced` refusal (#206). **0.19.20** honesty `c` was the memnet-mcp product gateway (`--transport gateway`, #203, MN-REQ-06.12). **0.19.19** honesty `c` was the one-session-per-document readiness probe (#201) plus lossless snapshot round-trip, one decoded value cap, honoured-or-refused WHERE, ACL who on save/load/close, and expire-save failures kept live (#202). **0.19.18** honesty `c` was snap_model bounded session grain (#199). **0.19.17** honesty `c` was the product-gate cap contract (#196) and admin-only serve usage report (#197). **0.19.16** honesty `c` was shared Path-B ingest defaults (2000 nodes / 2000 edges). **0.19.15** honesty `c` was storage role labels (#186), Path-B SysML ingest kinds (#188), and Neo4j retired (#189). **0.19.14** honesty `c` was TTL expire restore by known sid. **0.19.11** honesty `c` was the tip roll for [#168](https://github.com/chouswei/MemNet/pull/168) (catalog cross-session satisfy locators + `CousinSysMLEdge` `mustNotInventUploadBind`; do not claim tip=face). **0.19.10** honesty `c` keeps CueConflict for MATCH_L `|Q|>1` only; codebook miss emits CueMiss / Peak_L. **0.19.9** honesty `c` maps Path-B SysML `connection` / `link` to `:CON` (teach already said CON; ingest had mapped defs to PRT). **0.19.8** honesty `c` mints leftover nicknames on **all** snapshot records (not only catalog PKG) and accepts camelCase product relations (`inFile`) so `session_save` → `session_load` round-trips mission graphs. GraphGlot parse front is on master (#109 @ 73a63c9b). Neo4j cabinet is retired (#187); **0.7** is adapter + operator round trip (no runtime caller). **1.0** is still unclaimed (0.5–0.8). +**Package now:** Hatch **0.19.22** (`memnet.__version__`). Last published PyPI wheel is **`memnet-llm==0.19.21`** until this cut is uploaded. Numbered extras **0.10–0.19** are in this package (unchanged). **0.19.22** honesty `c` is the safe serve upgrade path (#208 trimmed by #210, MN-REQ-06.14): admin `upgrade-prepare` drain, lossless snapshot with manifest, startup restore under the same ids, and automatic retire after a clean restore. **0.19.21** honesty `c` was serve per-request output isolation, LF-only statement split, undeclared-property snapshot save, label case-fold, and housekeep GQL endpoint orphans with `prune_referenced` refusal (#206). **0.19.20** honesty `c` was the memnet-mcp product gateway (`--transport gateway`, #203, MN-REQ-06.12). **0.19.19** honesty `c` was the one-session-per-document readiness probe (#201) plus lossless snapshot round-trip, one decoded value cap, honoured-or-refused WHERE, ACL who on save/load/close, and expire-save failures kept live (#202). **0.19.18** honesty `c` was snap_model bounded session grain (#199). **0.19.17** honesty `c` was the product-gate cap contract (#196) and admin-only serve usage report (#197). **0.19.16** honesty `c` was shared Path-B ingest defaults (2000 nodes / 2000 edges). **0.19.15** honesty `c` was storage role labels (#186), Path-B SysML ingest kinds (#188), and Neo4j retired (#189). **0.19.14** honesty `c` was TTL expire restore by known sid. **0.19.11** honesty `c` was the tip roll for [#168](https://github.com/chouswei/MemNet/pull/168) (catalog cross-session satisfy locators + `CousinSysMLEdge` `mustNotInventUploadBind`; do not claim tip=face). **0.19.10** honesty `c` keeps CueConflict for MATCH_L `|Q|>1` only; codebook miss emits CueMiss / Peak_L. **0.19.9** honesty `c` maps Path-B SysML `connection` / `link` to `:CON` (teach already said CON; ingest had mapped defs to PRT). **0.19.8** honesty `c` mints leftover nicknames on **all** snapshot records (not only catalog PKG) and accepts camelCase product relations (`inFile`) so `session_save` → `session_load` round-trips mission graphs. GraphGlot parse front is on master (#109 @ 73a63c9b). Neo4j cabinet is retired (#187); **0.7** is adapter + operator round trip (no runtime caller). **1.0** is still unclaimed (0.5–0.8). **Last updated:** 2026-10-09 (`a.b.c` law locked; package **0.19.22** honesty `c` — safe serve upgrade (#208); extras **0.10–0.19** unchanged; Hatch **0.19.22**; last published PyPI **`memnet-llm==0.19.21`**; do not claim **1.0**; do not invent a **0.20** extra; do not claim tip=face).