diff --git a/AGENTS.md b/AGENTS.md index 5d7d8e0..674387a 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.20** (Hatch SSOT; last published PyPI **`memnet-llm==0.19.19`** until this cut is uploaded; 0.19.20 honesty `c`: memnet-mcp product gateway `--transport gateway` (#203, MN-REQ-06.12). Earlier `c` cuts include 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.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/`. ## Where to look diff --git a/CHANGELOG.md b/CHANGELOG.md index 6609b26..9842909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ This project uses Semantic Versioning as **interpreted for MemNet**: package `a. ## [Unreleased] +### Changed +- **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.21] - 2026-10-09 + ### Fixed - **Serve request isolation (MN-REQ-06.13)** — concurrent `memnet serve` commands no longer share a process-global stdout/stderr swap. Each request captures its own streams on contextvars. A caller cannot receive another session's records, and a failed mutate cannot come back as another call's success. No serve-wide lock. TCP listen backlog is 128 so a burst is queued rather than refused. - **Mutate line split** — leftover pipe stdin and GQL statement breaks split on LF only (optional trailing CR stripped), the same rule as snapshot load. VT, FF, FS, GS, RS, NEL, LS, and PS stay inside the value. @@ -15,8 +21,8 @@ This project uses Semantic Versioning as **interpreted for MemNet**: package `a. - **Housekeep endpoints (MN-REQ-04.12)** — orphan, dangling, and stale counts resolve edge ends by hidden element id and by nickname. A GQL graph is not reported as all orphans. `prune orphans` and `prune stale` with `--apply` recompute and refuse `prune_referenced` when a node is still an endpoint. No automatic `prune --apply`. ### Changed -- **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). +- **Honesty `c` — release summary for product gates (#206)** — (1) Serve per-request output isolation: each `memnet serve` request captures its own stdout/stderr, so concurrent callers cannot receive each other's records and a failed mutate cannot come back as another call's success. (2) Mutate and pipe statements split on LF only (optional trailing CR stripped). (3) Undeclared properties save through the snapshot SCHEMA, including a map without `id`. (4) `MATCH (n:Label {…}) SET` case-folds the label to the SCHEMA tag. (5) Housekeep resolves GQL edge endpoints by hidden element id and nickname, so a GQL-built graph is not counted as all orphans; `prune orphans` / `prune stale --apply` refuse `prune_referenced` while a node is still an endpoint. (6) Product gateway contract doc synced to shipped behaviour. +- **Package identity 0.19.21** — Hatch / `project.toml` / `memnet.__version__` honesty cut on **0.19**. Agent loop (`cue → pin_map → mutate`) unchanged. ## [0.19.20] - 2026-10-08 diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index b050418..1dd18d5 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.20** (`memnet.__version__`). Last published PyPI wheel is **`memnet-llm==0.19.19`** until this cut is uploaded. Numbered extras **0.10–0.19** are in this package (unchanged). **0.19.20** honesty `c` is 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.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). -**Last updated:** 2026-10-08 (`a.b.c` law locked; package **0.19.20** honesty `c` — memnet-mcp product gateway (#203); extras **0.10–0.19** unchanged; Hatch **0.19.20**; last published PyPI **`memnet-llm==0.19.19`**; 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.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). 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.20**. Extras **0.10–0.19** stay the owns table below. **1.0** stays unclaimed. +- Package is **0.19.21**. 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.20**; last published PyPI **`memnet-llm==0.19.19`**; 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.20** 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.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 | | **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.20). 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.21). 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 9ba4792..c47a2cc 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.20**. 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.21**. 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.20`). 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.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. `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.20", + "pinned_version": "0.19.21", "credentials": [ {"id": "endleaf-1", "sha256": "", "revoked": false} ] diff --git a/parts/common/memnet/memnet/__init__.py b/parts/common/memnet/memnet/__init__.py index 42a2a0e..f6b380b 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.20" +__version__ = "0.19.21" diff --git a/project.toml b/project.toml index 26c6874..e31aa93 100644 --- a/project.toml +++ b/project.toml @@ -1,7 +1,7 @@ [project] name = "memnet" repo = "MemNet" -version = "0.19.20" +version = "0.19.21" 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 2cf038e..9588379 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_20(): - assert __version__ == "0.19.20" +def test_version_is_0_19_21(): + assert __version__ == "0.19.21" 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 5ec6ad5..50b5a18 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.20"' in text - assert "0.19.21" not in text + assert 'version = "0.19.21"' in text + assert "0.19.22" not in text diff --git a/tests/test_sysml_lan_mcp_front.py b/tests/test_sysml_lan_mcp_front.py index c03c720..ab153f9 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.20"' in text - assert "0.19.21" not in text + assert 'version = "0.19.21"' in text + assert "0.19.22" not in text diff --git a/tests/test_sysml_product_gateway.py b/tests/test_sysml_product_gateway.py index fd4245c..8cd715f 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.20"' in project - assert "0.19.21" not in project + assert 'version = "0.19.21"' in project + assert "0.19.22" not in project