Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ This project uses Semantic Versioning as **interpreted for MemNet**: package `a.

## [Unreleased]

### Fixed
- **Honesty `c` — RSV as-is in behaviour/verify (#67)** — `behaviour.sysml` (`ParentTaskLifecycle`, `EvOverlapScopeForbidden`, `WorkerScopedTurn`), `AsyncTaskDispatch` / `WorkerPool` / `SessionHandoffEmit`, and MN-VER-12-S13 no longer deny shipped NeighbourhoodReserve. Engine unchanged (`memnet.neighbourhood_reserve`; CLI/MCP `reserve` / `extend` / `release`). Same goldfish loop. No SemVer bump.
- **Honesty `c` — Neo4j live-claim notes** — `system-design-notes.md` and companion studies no longer teach extra 0.14 `liveNeo4jClaimed=true`. Match `MemNetArchive` (#187). Same goldfish loop. No SemVer bump.

### 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).
Expand Down
19 changes: 10 additions & 9 deletions sysml-models/models/behaviour.sysml
Original file line number Diff line number Diff line change
Expand Up @@ -287,12 +287,13 @@ package MemNetBehaviour {
attribute def EvOverlapScopeForbidden {
doc /*
Two workers would mutate overlapping WorkerWriteScope anchors on one
shared session without serialisation (MN-REQ-12.5 / 12.12). MUST NOT
dual-write in parallel; prefer one worker, disjoint anchors, separate
session ids, or serial dispatch. The shipped CapsPolicy ACL
hard-rejects an individual out-of-scope mutate when session ACL is
enabled; overlapping writers still require doctrine-level
serialisation because no neighbourhood reserve is shipped.
shared session without serialisation or an RSV lease (MN-REQ-12.5 /
12.12 / 12.13). MUST NOT dual-write in parallel; prefer one worker,
disjoint anchors, separate session ids, serial dispatch, or a
NeighbourhoodReserve lease. The shipped CapsPolicy ACL hard-rejects
an individual out-of-scope mutate when session ACL is enabled.
Overlapping writers on one session MAY take RSV (llm_id + TTL);
without a lease, last-write-wins unless the host serialises.
*/
}
attribute def EvWorkerReturn {
Expand Down Expand Up @@ -766,7 +767,7 @@ package MemNetBehaviour {
MultitaskCoordinator. As-is: CapsPolicy ACL who / pin_map-vs-mutate /
WorkerWriteScope hard reject / optional SessionBind is shipped when
session ACL is enabled (CapsPolicy.engineAclShipped=true; MN-REQ-12.7
ACL cut). Neighbourhood reserve remains not shipped.
ACL cut). NeighbourhoodReserve (MN-REQ-12.13) is shipped.
*/
entry;
then taskAbsent;
Expand Down Expand Up @@ -912,8 +913,8 @@ package MemNetBehaviour {
Mutate under WorkerWriteScope only. MUST NOT invent parent-minted
ids; MUST NOT settle parent TSK_* / USR_* unless delegated.
CapsPolicy hard-rejects out-of-scope mutate when session ACL is
enabled. Reserve/overlap coordination remains host doctrine
(MN-REQ-12.7 — reserve is not shipped).
enabled. Overlap on the same ego MAY take NeighbourhoodReserve
(MN-REQ-12.13); without a lease, host doctrine serialises.
*/
}
transition worker_mutate_ok
Expand Down
4 changes: 3 additions & 1 deletion sysml-models/models/connections.sysml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ package MemNetConnections {
import_slice. MissionDock / EvidenceCentre are application patterns —
MUST NOT nest under MemNetSystem. GQL only.
Required: sessionId, caller (CallerId). Optional: missionId, lease
(reserve design; WAIT / not this ACL cut), anchors,
(SessionBind lease; shipped when configured; in-process MAY skip
bind — distinct from NeighbourhoodReserve RSV under Commit; full
session_token modes WAIT), anchors,
writeScope (Multitask WorkerWriteScope). MUST NOT carry a full
graph dump. Fields already on this item — do not duplicate.
*/
Expand Down
18 changes: 12 additions & 6 deletions sysml-models/models/deploy.sysml
Original file line number Diff line number Diff line change
Expand Up @@ -2763,7 +2763,9 @@ package MemNet {
queue, MissionDock, or HTTP body (EvDumpGraphInChat forbidden).
As-is NOW: the CapsPolicy ACL cut is shipped for the downstream
pin_map/mutate and WorkerWriteScope hard-reject gates when session ACL
is enabled; reserve/lease remains doctrine. engineAclShipped=true.
is enabled. NeighbourhoodReserve (RSV) is shipped (optional use).
SessionBind lease is shipped when configured. Full session_token
modes remain deferred. engineAclShipped=true.
*/
attribute doctrineAsIs : Boolean = true;
port handoffOut : SessionHandoffOutPort;
Expand All @@ -2784,9 +2786,11 @@ package MemNet {
ids (MN-REQ-12.5 / 12.12). Overlapping shared-session scopes →
serial / single writer (MUST NOT dual-write).
As-is NOW: CapsPolicy.aclWorkerWriteScopeHardReject is shipped and
hard-rejects out-of-scope mutate when session ACL is enabled. The
neighbourhood reserve and overlap-coordination doctrine remain
host-controlled; no RSV engine is claimed.
hard-rejects out-of-scope mutate when session ACL is enabled.
NeighbourhoodReserve (RSV, MN-REQ-12.13) is shipped: overlapping
shared-session writers MAY lease ego scopes; overlap with another
holder fails reserve_conflict. Parallel without RSV still requires
disjoint anchors OR serial / single writer (MUST NOT dual-write).
EvWorkerReturn is host-driven async completion on a later turn.
*/
attribute doctrineAsIs : Boolean = true;
Expand Down Expand Up @@ -2881,8 +2885,10 @@ package MemNet {
single writer (MN-REQ-12.5 / 12.12). Fan-in WorkingMemorySlice for
path B lead import.
CapsPolicy WorkerWriteScope hard reject is shipped when session ACL is
enabled. Pool coordination for reserve/overlap remains doctrineAsIs;
no neighbourhood reserve or Path-B PinMapIngest is claimed shipped.
enabled. NeighbourhoodReserve (RSV) is shipped: overlapping writers
MAY take an RSV lease (reserve_conflict rather than last-write-wins).
Path-B PinMapIngest_* domains are shipped. Full session_token modes
are not claimed.
*/
attribute doctrineAsIs : Boolean = true;
port handoffIn : SessionHandoffInPort;
Expand Down
6 changes: 4 additions & 2 deletions sysml-models/models/verify.sysml
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,10 @@ package MemNetVerification {
SessionImportReceive. Anti: overlapping anchors on one session —
EvOverlapScopeForbidden / MUST NOT dual-write. The shipped CapsPolicy
ACL hard-rejects an individual out-of-scope mutate when session ACL is
enabled; no neighbourhood reserve coordinates overlapping writers.
Subjects: AsyncTaskDispatch, WorkerPool. See
enabled. NeighbourhoodReserve (RSV) is shipped: overlapping writers
MAY lease ego scopes (reserve_conflict vs last-write-wins). Without
a lease, overlapping writers still require doctrine-level
serialisation. Subjects: AsyncTaskDispatch, WorkerPool. See
outputs/async-parallel-conflict-case-study.md.
*/
attribute verificationId : String = "MN-VER-12-S13";
Expand Down
4 changes: 2 additions & 2 deletions sysml-models/outputs/async-parallel-conflict-case-study.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Doctrine: `docs/operations/multi-agent-sessions.md`.
| Nest | `MultitaskOperatingModel` -> `MultitaskCoordinator` (`SessionHandoffEmit`, `AsyncTaskDispatch`, `SessionImportReceive`) + `WorkerPool` / `MultitaskWorker[1..*]` |
| Behaviour | `MultitaskMissionCycle`, `WorkerScopedTurn`, `EvDelegateWorker`, `EvEndCoordinatorTurn`, `EvWorkerReturn`, `EvOverlapScopeForbidden` |
| Items | `MissionTaskPin` (`TSK_*`), `WorkerWriteScope`, `SessionHandoff` |
| Requirements | MN-REQ-12.5, 12.6, 12.12 (also 12.3 / 12.4) |
| Requirements | MN-REQ-12.5, 12.6, 12.12, 12.13 (also 12.3 / 12.4) |
| Verify | MN-VER-12-S13 (happy + anti); S05 / S06 / S07 |

**doctrineAsIs:** CapsPolicy **hard-rejects** out-of-scope mutate when session ACL is enabled. ACL is off by default. Overlap without ACL/RSV is last-write-wins.
Expand Down Expand Up @@ -101,7 +101,7 @@ sequenceDiagram
1. One mission session (or **explicit** separate sessions).
2. Mint `TSK_*`; assign `WorkerWriteScope`; emit `SessionHandoff`.
3. Spawn via `AsyncTaskDispatch`; **end the turn**.
4. Parallel only if disjoint anchors **or** separate session ids.
4. Parallel only if disjoint anchors **or** separate session ids; overlapping writers on one session MAY take an **RSV** lease (MN-REQ-12.13).
5. Next lead turn: `pin_map` first; optional import + ImportGuard; settle from pins not chat.

## 5. Related studies
Expand Down
2 changes: 1 addition & 1 deletion sysml-models/outputs/durable-hydrate-flush-case-study.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Evidence walk against SysML under `sysml-models/models/`.
Companions: [company-memory-case-study.md](company-memory-case-study.md), [snapshot-passport-case-study.md](snapshot-passport-case-study.md).

**Wire:** GQL / shaped `pin_map` only. **Status:** M2.5 **0.7** Agens live hydrate/flush proven; extra **0.14** Neo4j live claimed (`liveNeo4jClaimed=true`; live round-trip yes; hid flush; leftover-nickname hydrate after hid miss); extra **0.16** optional library database on the same process (locators only; not hydrate/flush); cabinets external / not vendored.
**Wire:** GQL / shaped `pin_map` only. **Status:** M2.5 **0.7** Agens live hydrate/flush proven. Neo4jAdapter / Neo4jLibraryPort / `liveNeo4jClaimed` are retired on `MemNetArchive` (#187). Cabinets external / not vendored.

## 1. Purpose

Expand Down
2 changes: 1 addition & 1 deletion sysml-models/outputs/session-outline-case-study.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Agent cues `TSK` / `status=open` (a seen pattern) and `pin_map`s. If two exempla
| RAG / HostSearch | Outside `MemNetSystem` |
| SameThingAbsorb this cut | 0.12; `implemented=false` |
| Layer | Do not revive |
| Live Neo4j claim | Extra **0.14** (`liveNeo4jClaimed=true`); not this outline cut |
| Live Neo4j claim | Retired on `MemNetArchive` (#187); `liveNeo4jClaimed=false`; not this outline cut |

## 5. Honesty

Expand Down
2 changes: 1 addition & 1 deletion sysml-models/outputs/sysml-session-nest-cuts-case-study.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ If the parent is still inventing those usages, **do not** spawn: write the shell
| Ingest `max_nodes=2000` / `max_edges=2000` | covers this house and `deploy.sysml` (504); a ~10k file still hits `ingest_budget` before a recurse cut |
| Complete Shape or refuse | TARGET; as-is `context_pack` still `[:max_rows]` |
| VehicleUsages `interface` / `subsets` / `redefines` / `flow` | not in `_DEF_HEAD`; `.sysml` SSOT |
| `liveNeo4jClaimed` | extra **0.14**; not this Snap’s fence |
| `liveNeo4jClaimed` | retired on `MemNetArchive` (#187); not this Snap’s fence |
| Operator count | stays 2 |

## 6. Related
Expand Down
6 changes: 3 additions & 3 deletions sysml-models/outputs/system-design-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Novel-writer is out of scope.

1. **MemNet = shared LLM memory** (`SharedLlmMemory`).
2. **Session as SSOT handle** — `SessionHandoff` / `SessionHandoffById`; module A→B pipe; chat / MissionDock / HTTP never carry the graph. **sessionId = SessionCapability** (secret; MUST NOT dump in chat/queue).
3. **Durable GQL store behind MemNet** — M2.5 / **0.7** Agens live hydrate/flush (`DurableStoreAdapter` / Fake / optional AgensGraph client; optional Neo4j extra **0.14** `liveNeo4jClaimed=true`; extra **0.16** optional library database name on the same Neo4j process, locators only, name MUST differ from the cabinet (`rejectSameNameAsCabinet`); one sync owner). Extra **0.17** `RagHostHook.implemented=true` **outside** `MemNetSystem` (skip valid). Cabinets external / not vendored.
3. **Durable GQL store behind MemNet** — M2.5 / **0.7** Agens live hydrate/flush (`DurableStoreAdapter` / Fake / optional AgensGraph client). Neo4jAdapter / Neo4jLibraryPort / `liveNeo4jClaimed` are retired on `MemNetArchive` (#187). Extra **0.17** `RagHostHook.implemented=true` **outside** `MemNetSystem` (skip valid). Cabinets external / not vendored.
4. **Lead imports member WM** — **path A** shared mission `sessionId` → `pin_map` only (import nest skipped); **path B** `WorkingMemorySliceExport` → optional nested `ImportGuard` (`ImportGuardHook` shipped; `CheapLlmImportGuard` shipped #63) → `ImportAbsorb` (engine SHALL hard; `id_policy` keep|reject|remint). Product verb = **import** (`SessionImport*` only). TARGET `keep` = MERGE of labels+props / type+ends (GraphElement). leftover_MERGE_by_id is 0.9 leftover, not keep. Micro `merge=true` ≠ this. Module: `memnet.import_absorb`.
5. **CapsPolicy ACL cut (as-is shipped)** — beyond size: who /
pin_map-vs-mutate / WorkerWriteScope HARD reject / optional SessionBind.
Expand Down Expand Up @@ -157,7 +157,7 @@ Formal allocate: `sysml-models/models/implementation.sysml` (`SoftwareAllocate`;
| MutateGate | `mutate_gate.py` + `acl.py` | Commit gate (GQL); leftover_NEW_mint as-is; TARGET GraphElement create; Layer/Tier A rejected |
| CapsPolicy | `config.Caps` + `acl.py` | Size caps and ACL who/read-vs-mutate/scope/bind shipped; `engineAclShipped=true` |
| AgensGraphAdapter | `memnet.durable` (Fake + optional AgensGraph client) | **0.7** live hydrate/flush; cabinet external / not vendored |
| Neo4jAdapter | `memnet.durable` (optional Neo4j client) | Extra **0.14** live claimed; `liveNeo4jClaimed=true` |
| Neo4jAdapter | `MemNetArchive` (`models/archive.sysml`) | Retired (#187); `liveNeo4jClaimed=false`; `MEMNET_NEO4J_*` ignored |

## Satisfy (MN-REQ-12 import + async + ACL honesty)

Expand All @@ -173,7 +173,7 @@ Formal allocate: `sysml-models/models/implementation.sysml` (`SoftwareAllocate`;

- **M1:** GQL wire profile — **done**
- **M2:** Engine/MCP GQL accept + shaped pin_map emit; Layer/Tier A retired — **done**
- **M2.5:** Client + **0.7** Agens live hydrate/flush + extra **0.14** Neo4j live claim; cabinets external / not vendored ([durable-hydrate-flush-case-study.md](durable-hydrate-flush-case-study.md))
- **M2.5:** Client + **0.7** Agens live hydrate/flush; Neo4j retired to `MemNetArchive` (#187); cabinets external / not vendored ([durable-hydrate-flush-case-study.md](durable-hydrate-flush-case-study.md))
- **M3:** In-repo playbook / app-note GQL rewrite — **done** (0.8)
- ImportGuardHook — host plug-in (`set_import_guard` / `--no-guard` / GuardPassthrough); **shipped** (`implemented=true`; #49)
- CheapLlmImportGuard — optional default LLM adapter (MN-REQ-12.11); **shipped** (`implemented=true`; **#63**; env-gated)
Expand Down
92 changes: 92 additions & 0 deletions tests/test_sysml_rsv_honesty.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Honesty-c: MN-REQ-12.13 RSV is as-is shipped; behaviour must not deny it (#67)."""

from __future__ import annotations

from pathlib import Path

from memnet.neighbourhood_reserve import IMPLEMENTED as RSV_IMPLEMENTED

ROOT = Path(__file__).resolve().parents[1]
MODELS = ROOT / "sysml-models" / "models"
BEHAVIOUR = MODELS / "behaviour.sysml"
DEPLOY = MODELS / "deploy.sysml"
REQUIREMENTS = MODELS / "requirements.sysml"
VERIFY = MODELS / "verify.sysml"
IMPLEMENTATION = MODELS / "implementation.sysml"
NOTES = ROOT / "sysml-models" / "outputs" / "system-design-notes.md"
ASYNC_STUDY = ROOT / "sysml-models" / "outputs" / "async-parallel-conflict-case-study.md"
MAP = ROOT / "sysml-models" / "outputs" / "ssot-to-code-allocate-map.md"

_RSV_DENIALS = (
"no neighbourhood reserve is shipped",
"Neighbourhood reserve remains not shipped",
"reserve is not shipped",
"no RSV engine is claimed",
"no neighbourhood reserve or Path-B PinMapIngest is claimed shipped",
"no neighbourhood reserve coordinates overlapping writers",
"reserve/lease remains doctrine",
)


def test_engine_rsv_is_shipped():
assert RSV_IMPLEMENTED is True
path = ROOT / "parts" / "common" / "memnet" / "memnet" / "neighbourhood_reserve.py"
assert path.is_file()


def test_deploy_neighbourhood_reserve_implemented():
text = DEPLOY.read_text(encoding="utf-8")
block = text.split("part def NeighbourhoodReserve", 1)[1].split("part def ", 1)[0]
assert "attribute implemented : Boolean = true;" in block
assert 'attribute displayKind : String = "RSV";' in block
assert "memnet.neighbourhood_reserve" in block
dispatch = text.split("part def AsyncTaskDispatch", 1)[1].split("part def ", 1)[0]
assert "no RSV engine is claimed" not in dispatch
assert "NeighbourhoodReserve (RSV, MN-REQ-12.13) is shipped" in dispatch
pool = text.split("part def WorkerPool", 1)[1].split("part def ", 1)[0]
assert "no neighbourhood reserve or Path-B PinMapIngest is claimed shipped" not in pool
assert "NeighbourhoodReserve (RSV) is shipped" in pool


def test_behaviour_and_verify_do_not_deny_shipped_rsv():
blobs = {
"behaviour.sysml": BEHAVIOUR.read_text(encoding="utf-8"),
"deploy.sysml": DEPLOY.read_text(encoding="utf-8"),
"verify.sysml": VERIFY.read_text(encoding="utf-8"),
"connections.sysml": (MODELS / "connections.sysml").read_text(encoding="utf-8"),
"async-parallel-conflict-case-study.md": ASYNC_STUDY.read_text(encoding="utf-8"),
}
found = []
for name, text in blobs.items():
for needle in _RSV_DENIALS:
if needle in text:
found.append(f"{name}: {needle}")
assert found == [], f"RSV denial leftover: {found}"
behaviour = blobs["behaviour.sysml"]
assert "NeighbourhoodReserve (MN-REQ-12.13) is shipped" in behaviour
assert "Overlapping writers on one session MAY take RSV" in behaviour
ver = blobs["verify.sysml"]
assert "NeighbourhoodReserve (RSV) is shipped" in ver
assert "verification def MN_VER_12_S13_AsyncParallelDisjoint" in ver


def test_requirement_and_allocate_still_claim_rsv():
req = REQUIREMENTS.read_text(encoding="utf-8")
assert "requirement def MN_REQ_12_13_NeighbourhoodReserve" in req
assert 'attribute requirementId : String = "MN-REQ-12.13"' in req
assert "reserve_conflict" in req
impl = IMPLEMENTATION.read_text(encoding="utf-8")
assert "part def NeighbourhoodReserveMod" in impl
assert "allocation rsvToMod" in impl
assert "memnet.neighbourhood_reserve" in impl
ledger = MAP.read_text(encoding="utf-8")
assert "| rsvToMod |" in ledger
study = ASYNC_STUDY.read_text(encoding="utf-8")
assert "12.13" in study


def test_design_notes_retire_neo4j_live_claim():
notes = NOTES.read_text(encoding="utf-8")
assert "liveNeo4jClaimed=true" not in notes
assert "MemNetArchive" in notes
assert "Retired (#187)" in notes
Loading