From e518e9aae58eaf1661ef9ad946bf3f0744046ae4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 13 Oct 2026 08:53:39 +0000 Subject: [PATCH 1/2] Make MN-REQ-13 Recall/Commit the requirements spine. Agent-facing leaves reparent to nested 13.Recall / 13.Commit via #derivation. SameThingAbsorb nests under Commit. Strict add/update semantics stay as they stand. Mapping table records old parent to new parent. Ops, importers, cousins, and archive are not moved. Co-authored-by: chouswei --- sysml-models/README.md | 15 +- sysml-models/models/cousins.sysml | 2 +- sysml-models/models/deploy.sysml | 4 +- sysml-models/models/requirements.sysml | 509 ++++++++++++++++-- sysml-models/outputs/README.md | 1 + .../outputs/mn-req-13-spine-mapping.md | 202 +++++++ sysml-models/outputs/product-nest-one-page.md | 2 + tests/test_sysml_recall_commit_spine.py | 141 +++++ 8 files changed, 819 insertions(+), 57 deletions(-) create mode 100644 sysml-models/outputs/mn-req-13-spine-mapping.md create mode 100644 tests/test_sysml_recall_commit_spine.py diff --git a/sysml-models/README.md b/sysml-models/README.md index 445fbe11..e17ab9b8 100644 --- a/sysml-models/README.md +++ b/sysml-models/README.md @@ -21,7 +21,7 @@ Design authority: rebuilt requirements + ADR-001 (GQL agent wire) + `docs/gramma | File | Package | Role | |------|---------|------| | `models/connections.sysml` | `MemNetConnections` | SharedLlmMemory, SessionHandoff (+ CallerId / SessionBind / SessionCapability), WorkingMemorySlice, SessionImportRequest, optional ImportGuardDecision; ServeUsageLook / ImportGuardArmedLook / HumanUsagePage (ops look); application `CompanyAnalyticalSsot` / `HostSearchBridge` / `DeviceMemNetFleet` / `TipMemNetAccess` / `MemNetLanMcpCluster` / `MemNetTwoMovesContrast` | -| `models/requirements.sysml` | `MemNetRequirements` | MN-REQ-00…13 (01.7/01.8, 06.4, **06.5** human usage look, **06.6** device services / one droplet MemNet MCP tip/ops, product face sysmledge, **06.7** SSOT → code allocate, **06.8** tip MemNet access portal, **06.9** ClusterRoute LAN MCP front invent #191, **06.10** SliceHandCarry invent #47 cousin, **06.12** product gateway on memnet-mcp, 12.9–12.13, 13.1 Recall/Commit; 02.9 cousin store-key; 04.8 cue \|Q\|>1; 04.9 empty-q outline) | +| `models/requirements.sysml` | `MemNetRequirements` | MN-REQ-00…13. **Spine = MN-REQ-13** Recall(q)/Commit(Δ) (`13.Recall` / `13.Commit`; mapping [outputs/mn-req-13-spine-mapping.md](outputs/mn-req-13-spine-mapping.md)). Agent surface: recall, commit, session open, save, close. 01.7/01.8, 06.4, **06.5** human usage look, **06.6** device services / one droplet MemNet MCP tip/ops, product face sysmledge, **06.7** SSOT → code allocate, **06.8** tip MemNet access portal, **06.9** ClusterRoute LAN MCP front invent #191, **06.10** SliceHandCarry invent #47 cousin, **06.12** product gateway on memnet-mcp, 12.9–12.13, 13.1 two operators; 02.9 cousin store-key; 04.8 cue \|Q\|>1; 04.9 empty-q outline | | `models/cousins.sysml` | `MemNetCousinContrast` | TARGET vs eight cousin pointing/identity designs (not a product switch; SysMLEdge is a distinct pin_map; overlay family `SysMLEdgePrj-*`; this engine repo and `modelbasedPrj-*` are repo-based — MUST NOT use SysMLEdge as model SSOT; `SysMLEdgePrj-*` bound desk is working model SSOT) | | `models/deploy.sysml` | `MemNet` | Nested parts; `RecallCommit` two-operator cut; Multitask spine; `MemNetUsageDashboard` / `MemNetOpsFleet` / `TipMemNetAccessPortal` / `MemNetLanMcpFront` / `MemNetProductGateway` / `MemNetTwoMoves` outside `MemNetSystem` | | `models/implementation.sysml` | `MemNetImplementation` | `SoftwareAllocate` logical → live modules; one Hatch wheel many hosts; sysmledge not in wheel | @@ -32,6 +32,19 @@ Design authority: rebuilt requirements + ADR-001 (GQL agent wire) + `docs/gramma **One-page nest:** [outputs/product-nest-one-page.md](outputs/product-nest-one-page.md). +## Requirements spine (MN-REQ-13) + +Agent-facing graph I/O hangs on **Recall(q)** and **Commit(Δ)**. Organisational groups MN-REQ-01…12 stay nested under MN-REQ-00 this cut (ops, importers, cousins, archive not moved). Mapping: [outputs/mn-req-13-spine-mapping.md](outputs/mn-req-13-spine-mapping.md). + +```text +MN-REQ-00 +├── MN-REQ-13 RecallCommit // spine +│ ├── 13.Recall pin_map // empty q = outline +│ └── 13.Commit mutate // 13.2 SameThingAbsorb nested +├── MN-REQ-01 Session // open / save / close +└── MN-REQ-06 ops // later PR may split +``` + ## Nesting outline (target) ```text diff --git a/sysml-models/models/cousins.sysml b/sysml-models/models/cousins.sysml index b8c67bce..e7b3bc9e 100644 --- a/sysml-models/models/cousins.sysml +++ b/sysml-models/models/cousins.sysml @@ -358,7 +358,7 @@ package MemNetCousinContrast { satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_8_CueMultiRootHonest; satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_9_SessionOutlineEmptyCue; satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_1_TwoOperators; - satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_2_SameThingAbsorb; + satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_Commit::MN_REQ_13_2_SameThingAbsorb; satisfy MN_REQ_00_MissionBridge::MN_REQ_11_SnapshotInterop::MN_REQ_11_17_CatalogModelSnap::MN_REQ_11_17_1_CrossCutSatisfyLocators; } diff --git a/sysml-models/models/deploy.sysml b/sysml-models/models/deploy.sysml index fa5f6e7d..cea3bee7 100644 --- a/sysml-models/models/deploy.sysml +++ b/sysml-models/models/deploy.sysml @@ -885,7 +885,7 @@ package MemNet { satisfy MN_REQ_00_MissionBridge::MN_REQ_03_StrictMutate::MN_REQ_03_3_NoSilentUpsert; satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_8_CueMultiRootHonest; satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_1_TwoOperators; - satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_2_SameThingAbsorb; + satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_Commit::MN_REQ_13_2_SameThingAbsorb; } part def Recall { @@ -1000,7 +1000,7 @@ package MemNet { satisfy MN_REQ_00_MissionBridge::MN_REQ_12_MultitaskMode::MN_REQ_12_9_LeadOwnsSessionImport; satisfy MN_REQ_00_MissionBridge::MN_REQ_12_MultitaskMode::MN_REQ_12_13_NeighbourhoodReserve; satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_1_TwoOperators; - satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_2_SameThingAbsorb; + satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_Commit::MN_REQ_13_2_SameThingAbsorb; } part def RecallCommit { diff --git a/sysml-models/models/requirements.sysml b/sysml-models/models/requirements.sysml index db7a4b4b..60c3b5f8 100644 --- a/sysml-models/models/requirements.sysml +++ b/sysml-models/models/requirements.sysml @@ -8,8 +8,18 @@ MN-REQ-10 LLM assumed properties + limitation leaves MN-REQ-11 Snapshot / interoperability (map of pins) MN-REQ-12 Multitask Mode (parent/worker; shared session as-is) - MN-REQ-13 0.5 Recall/Commit (thin; math skeleton; no engine cut) + MN-REQ-13 0.5 Recall/Commit SPINE (Recall(q) + Commit(Δ)) + MN-REQ-13.Recall spine arm: Recall(q) = pin_map (empty q = outline) + MN-REQ-13.Commit spine arm: Commit(Δ) = mutate (leftover add/update stay façades) MN-REQ-13.2 SameThingAbsorb — in-session Commit rule (not ImportAbsorb) + nested under MN-REQ-13.Commit + + Spine: agent-facing leaves reparent to 13.Recall or 13.Commit via + #derivation (deriveReqt). Organisational groups MN-REQ-01…12 stay + nested under MN-REQ-00 this cut (do not split deploy/ops, do not + move importers, cousins, or archive). Agent surface verbs: + recall, commit, session open, save, close. Other agent-visible + operations are options of those or internal; see agentSurface. MN-REQ-02.8 GraphElement identity; optional nickname; leftover mint/store-key MN-REQ-02.9 MUST NOT copy cousin store-key / unique-name / embedding Recall MN-REQ-04.8 Cue |Q|>1 stays RelativeSeed bounded + honest; CueConflict @@ -49,7 +59,10 @@ Nesting: requirement def inside requirement def only (SysML requirements tree). Deploy software elements satisfy leaves; they are not the - requirement structure. + requirement structure. Organisational groups MN-REQ-01…12 remain nested + under MN-REQ-00. MN-REQ-13 is the product spine: nested arms + MN-REQ-13.Recall and MN-REQ-13.Commit. SameThingAbsorb nests under + Commit. Mapping: sysml-models/outputs/mn-req-13-spine-mapping.md. Formal derivation (SysML v2 idiom for SysML 1.x deriveReqt): import RequirementDerivation::*; then @@ -57,8 +70,10 @@ end #original ::> ; end #derive ::> ; } - Groups derive from MN-REQ-00; leaves derive from their group. - Nesting = structure; #derivation = formal deriveReqt edge. + Groups derive from MN-REQ-00; leaves derive from their organisational + group. Agent-facing leaves also derive from 13.Recall or 13.Commit + (spine reparent). Nesting = organisational structure; #derivation = + formal deriveReqt edge, including the Recall/Commit spine. Breaking rename: MN-R1...MN-R9 retired. Do not map old numbers 1:1. @@ -109,6 +124,7 @@ package MemNetRequirements { requirement def MN_REQ_00_MissionBridge { doc /* + Agent surface: mission root. mission root; not an operator. SHALL provide a Net of Memory (MemNet): an in-memory working-memory network of facts and relations -- not a chat notepad and not the search corpus -- that sits between pipelines of LLM calls and data @@ -123,11 +139,13 @@ package MemNetRequirements { on SysMLEdgePrj-*. */ attribute requirementId : String = "MN-REQ-00"; + attribute agentSurface : String = "mission"; // ----- Session lifecycle ----- requirement def MN_REQ_01_SessionLifecycle { doc /* + Agent surface: session (open / save / close). session open / save / close (agent surface beside Recall and Commit). Group: named session working memory across pipeline turns. The live graph is working memory. The session id is the mission handle, not the model SSOT. Durability is the file snapshot @@ -135,24 +153,30 @@ package MemNetRequirements { cabinet ego slice. */ attribute requirementId : String = "MN-REQ-01"; + attribute agentSurface : String = "session"; requirement def MN_REQ_01_1_NamedSessions { doc /* + Agent surface: session (open / save / close). named sessions; session id is the mission handle. SHALL provide named sessions that retain working-memory state across turns so pipeline facts outlive chat context. */ attribute requirementId : String = "MN-REQ-01.1"; + attribute agentSurface : String = "session"; } requirement def MN_REQ_01_2_SessionLifecycleOps { doc /* + Agent surface: session (open / save / close). open, resume, and close. SHALL support open, resume, and close for each named session. */ attribute requirementId : String = "MN-REQ-01.2"; + attribute agentSurface : String = "session"; } requirement def MN_REQ_01_3_SessionTtlAndCaps { doc /* + Agent surface: internal. TTL and concurrent-session caps; not an agent verb. SHALL apply configurable session TTL and a cap on concurrent sessions. Sliding TTL: live access extends expires_at. After TTL the in-memory session SHALL drop, except when expire-save @@ -167,36 +191,44 @@ package MemNetRequirements { SessionSnapshotBlob. */ attribute requirementId : String = "MN-REQ-01.3"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_01_4_SaveSessionSnapshot { doc /* + Agent surface: session (open / save / close). session_save. SHALL persist a named session to one durable file snapshot so working-memory state can outlive the process (schema, records, relations). Explicit session_save and expire-save write that same blob. The file is not the cabinet and not the model SSOT. */ attribute requirementId : String = "MN-REQ-01.4"; + attribute agentSurface : String = "session"; } requirement def MN_REQ_01_5_LoadSessionSnapshot { doc /* + Agent surface: option of session open / save / close. session_load — option of save. SHALL restore a named session from a durable snapshot into an active working-memory session. */ attribute requirementId : String = "MN-REQ-01.5"; + attribute agentSurface : String = "option:session"; } requirement def MN_REQ_01_6_OptionalKeepSessionId { doc /* + Agent surface: option of session open / save / close. keep_id on load — option of save. On load, SHALL allow the caller to optionally keep the session id from the snapshot (keep_id); otherwise assign a new id. */ attribute requirementId : String = "MN-REQ-01.6"; + attribute agentSurface : String = "option:session"; } requirement def MN_REQ_01_7_SessionAsSsotHandle { doc /* + Agent surface: session (open / save / close). session id is the handoff capability and secret; keep unchanged. A named session MAY be the SSOT for a mission SOMETHING (MissionWorkingSet). When so, an LLM SHALL pass that SOMETHING by delivering the session id only (SessionHandoff; module A → B pipe). @@ -207,10 +239,12 @@ package MemNetRequirements { modelbasedPrj-*; a bound SysMLEdge desk only on SysMLEdgePrj-*). */ attribute requirementId : String = "MN-REQ-01.7"; + attribute agentSurface : String = "session"; } requirement def MN_REQ_01_8_NoGraphDumpHandoff { doc /* + Agent surface: session (open / save / close). no graph dump on handoff; keep unchanged. Session handoff SHALL NOT carry a full graph dump (or mission SSOT prose blob) as the handoff payload. SessionHandoff carries sessionId and caller, plus optional missionId, lease, anchors, and writeScope. @@ -218,10 +252,12 @@ package MemNetRequirements { Mutate still hits CapsPolicy (who / what / bind). */ attribute requirementId : String = "MN-REQ-01.8"; + attribute agentSurface : String = "session"; } requirement def MN_REQ_01_9_SnapshotLosslessRoundTrip { doc /* + Agent surface: internal. snapshot lossless round-trip; save integrity. Anything the write path accepts SHALL session_save and session_load byte-for-byte as the same property values. That includes LF, CR, tab, VT, FF, file/group/record @@ -263,10 +299,12 @@ package MemNetRequirements { backslash escapes only) SHALL still load. */ attribute requirementId : String = "MN-REQ-01.9"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_01_10_SessionLifecycleAclWho { doc /* + Agent surface: internal. ACL who-check on save / load / close. When session ACL is enabled, session_save, session_load into that ACL'd session, and session_close SHALL accept --caller / MEMNET_CALLER and enforce CapsPolicy who-check with the same @@ -275,6 +313,7 @@ package MemNetRequirements { behaviour is unchanged. */ attribute requirementId : String = "MN-REQ-01.10"; + attribute agentSurface : String = "internal"; } } @@ -282,6 +321,7 @@ package MemNetRequirements { requirement def MN_REQ_02_MemoryNetGraph { doc /* + Agent surface: internal. graph identity / grammar; organisational. Group: the Net of Memory is a GQL property graph whose three elements are node (ISO synonym vertex), edge (ISO synonym relationship), and property (ADR-001; gql-wire-profile). GraphElement is the identity @@ -296,9 +336,11 @@ package MemNetRequirements { remains import-once only. */ attribute requirementId : String = "MN-REQ-02"; + attribute agentSurface : String = "internal"; requirement def MN_REQ_02_1_ExactlyNodeAndEdge { doc /* + Agent surface: internal. exactly node and edge. SHALL model the Net of Memory as a GQL property graph with three elements: node (vertex), edge (relationship), and property. Topology is node--edge--node; property values attach to nodes and to edges. @@ -308,27 +350,33 @@ package MemNetRequirements { SHALL NOT invent an application store key in place of the element. */ attribute requirementId : String = "MN-REQ-02.1"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_02_2_NodesHoldAtomisedFacts { doc /* + Agent surface: internal. nodes hold atomised facts. Nodes SHALL hold atomised facts as property values -- one idea per property -- not free-form prose or merged multi-idea blobs. */ attribute requirementId : String = "MN-REQ-02.2"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_02_3_EdgesAreFirstClass { doc /* + Agent surface: internal. edges are first class. Edges (relationships) SHALL be first-class links between nodes, not buried in node property values or narrative. */ attribute requirementId : String = "MN-REQ-02.3"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_02_4_QueryOverNodeEdgeGraph { doc /* + Agent surface: Recall (pin_map). query over the node/edge graph is Recall. Pin map, walk, bounded MATCH find, and query SHALL operate over the node/edge graph (property values included). Recall SHALL seed from a cue or pattern (find); pin_map then walks a shape. An optional @@ -339,10 +387,12 @@ package MemNetRequirements { seeds are one Recall operator (MN-REQ-13). */ attribute requirementId : String = "MN-REQ-02.4"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_02_5_GrammarExpressesNodeAndEdge { doc /* + Agent surface: internal. grammar expresses node and edge. Agent-facing grammar and I/O SHALL express node, edge, and property as openCypher-shaped GQL (labels, edge/relationship types, property maps). Spelling/identity SSOT is the vendored official BNF @@ -352,19 +402,23 @@ package MemNetRequirements { Layer / Tier A SHALL NOT be accepted on the product mutate path. */ attribute requirementId : String = "MN-REQ-02.5"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_02_6_ParserRecoversNodeAndEdge { doc /* + Agent surface: internal. parser recovers node and edge; parser is SSOT. The canonical parser SHALL recover node, edge, and property structure for human inspection. Grammar parse front is GraphGlot (official grammar); MemNet product gate is a separate part after parse. */ attribute requirementId : String = "MN-REQ-02.6"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_02_7_SchemaValidatedIngest { doc /* + Agent surface: option of Commit. schema-validated ingest — option of Commit. SHALL validate ingest against the session schema and reject unknown or reserved surface kinds. Tags MAY realise node labels; GQL elements remain node (vertex), edge (relationship), and property. @@ -372,10 +426,12 @@ package MemNetRequirements { tag_map id_first remains engine as-is, not TARGET. */ attribute requirementId : String = "MN-REQ-02.7"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_02_8_IdNotRequiredOnWire { doc /* + Agent surface: internal. id not required on the wire. Headline: GraphElement is identity. CREATE () is legal. properties() MAY be empty. Later MATCH/MERGE looks up labels + properties; an edge is type + end elements. HiddenStoreHandle @@ -392,10 +448,12 @@ package MemNetRequirements { pin_map require_anchor. */ attribute requirementId : String = "MN-REQ-02.8"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_02_9_MustNotCopyCousinStoreKey { doc /* + Agent surface: internal. MUST NOT copy cousin store-key / embedding Recall. Cousin pointing contrast (not a product switch). SHALL keep GraphElement identity and TARGET cue → RelativeSeed → ShapeWalk → pin_map emit. MAY copy cue-without-store-key and neighbourhood @@ -414,6 +472,7 @@ package MemNetRequirements { TARGET; they SHALL NOT force a store-key back onto the wire. */ attribute requirementId : String = "MN-REQ-02.9"; + attribute agentSurface : String = "internal"; } } @@ -421,12 +480,15 @@ package MemNetRequirements { requirement def MN_REQ_03_StrictMutate { doc /* + Agent surface: Commit (mutate). strict mutate group; spine parent Commit. Group: explicit create vs replace; no silent upsert. */ attribute requirementId : String = "MN-REQ-03"; + attribute agentSurface : String = "Commit"; requirement def MN_REQ_03_1_AddFailsIfExists { doc /* + Agent surface: Commit (mutate). AddFailsIfExists; unchanged; merge-and-report pending. When an optional nickname id is present on create, SHALL fail with an id-exists error if that nickname is already taken. SHALL NOT require id on create. TARGET create is CREATE () / CREATE @@ -435,27 +497,35 @@ package MemNetRequirements { contract. */ attribute requirementId : String = "MN-REQ-03.1"; + attribute agentSurface : String = "Commit"; } requirement def MN_REQ_03_2_UpdateFailsIfAbsent { doc /* + Agent surface: Commit (mutate). UpdateFailsIfAbsent; unchanged. When an optional nickname id is present on replace (update), SHALL fail with a not-found error if that nickname is absent. Ends MATCH by label + properties when no nickname. SHALL NOT treat missing id as not-found of identity. */ attribute requirementId : String = "MN-REQ-03.2"; + attribute agentSurface : String = "Commit"; } requirement def MN_REQ_03_3_NoSilentUpsert { doc /* - Silent upsert SHALL NOT be offered. + Agent surface: Commit (mutate). no silent upsert; unchanged; merge-and-report is owner-pending. + Silent upsert SHALL NOT be offered. Merge-and-report is an + owner decision and is pending; this leaf stays under Commit + as no-silent-upsert. */ attribute requirementId : String = "MN-REQ-03.3"; + attribute agentSurface : String = "Commit"; } requirement def MN_REQ_03_4_HonourWherePredicate { doc /* + Agent surface: Commit (mutate). honour WHERE or refuse. MATCH ... WHERE ... SET and MATCH ... WHERE ... DELETE SHALL NOT ignore the WHERE predicate. If lowering cannot honour it, the whole statement SHALL refuse with unsupported_predicate @@ -467,6 +537,7 @@ package MemNetRequirements { actually filter. */ attribute requirementId : String = "MN-REQ-03.4"; + attribute agentSurface : String = "Commit"; } } @@ -474,6 +545,7 @@ package MemNetRequirements { requirement def MN_REQ_04_SliceEconomy { doc /* + Agent surface: Recall (pin_map). slice economy / pin_map group; spine parent Recall. Group: bounded live pin map for LLM turns (not the whole net); hide recyclable rows; optional relation walk; bounded MATCH find as cue/pattern seed (MN-REQ-04.6; shipped seed-only). Dark session @@ -486,9 +558,11 @@ package MemNetRequirements { pin map (deprecated alias). */ attribute requirementId : String = "MN-REQ-04"; + attribute agentSurface : String = "Recall"; requirement def MN_REQ_04_1_AnchoredPinMap { doc /* + Agent surface: Recall (pin_map). bounded live pin_map. SHALL return a bounded live pin map for each turn -- a shaped neighbourhood digest of pins -- not the full session or a raw search dump. Goldfish seeds from a cue or pattern (find); pin_map @@ -500,41 +574,53 @@ package MemNetRequirements { by_id is engine as-is, not TARGET. */ attribute requirementId : String = "MN-REQ-04.1"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_04_2_RecycleHiddenFromPinMap { doc /* + Agent surface: Recall (pin_map). hide recycled rows from pin_map. SHALL hide rows marked for settle or expire recycle from the live pin map. */ attribute requirementId : String = "MN-REQ-04.2"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_04_3_PinMapDepthAndRowCaps { doc /* - SHALL cap live pin map depth and row count. + Agent surface: Recall (pin_map). hard depth/row caps; truncation flagged on the wire. + SHALL cap live pin map depth and row count. Caps are hard: + when a neighbourhood is clipped the wire SHALL flag Truncation + (truncated=true). SHALL NOT silent-clip. */ attribute requirementId : String = "MN-REQ-04.3"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_04_4_EngineLawPrepend { doc /* + Agent surface: Recall (pin_map). prepend engine-law rows on pin_map. SHALL prepend session engine-law rows to the live pin map when those rows are present in the store. */ attribute requirementId : String = "MN-REQ-04.4"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_04_5_AnchoredWalk { doc /* + Agent surface: option of Recall. query_walk / anchored walk — leftover option of Recall. Anchored walk hop lines over the NODE and EDGE graph SHOULD be available for relation enumeration without loading the whole graph. */ attribute requirementId : String = "MN-REQ-04.5"; + attribute agentSurface : String = "option:Recall"; } requirement def MN_REQ_04_6_BoundedMatchFind { doc /* + Agent surface: option of Recall. find — bounded MATCH seed; option of Recall. When no nickname id is known, or when pin_map caps dropped a needed row, the agent surface SHOULD offer a bounded MATCH find: label and/or property/locator predicates with LIMIT required @@ -548,10 +634,12 @@ package MemNetRequirements { pin_map k-hop, not copy-id then --anchor. */ attribute requirementId : String = "MN-REQ-04.6"; + attribute agentSurface : String = "option:Recall"; } requirement def MN_REQ_04_7_RelativeSeedThenShapeWalk { doc /* + Agent surface: Recall (pin_map). RelativeSeed then ShapeWalk. Goldfish query SHALL be discrete codebook tokens (kind / primary label, properties including locators, keyword; optional nickname id only if already set) — not a store-key id, not elementId, not @@ -573,10 +661,12 @@ package MemNetRequirements { Still ONE Recall operator (MN-REQ-13.1). */ attribute requirementId : String = "MN-REQ-04.7"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_04_8_CueMultiRootHonest { doc /* + Agent surface: Recall (pin_map). CueConflict when |Q|>1; no absorb in Recall. Open hard part without a third operator: a codebook cue MAY hit several relative roots (|Q| > 1). RelativeSeed SHALL stay bounded (hard LIMIT L) and honest — emit Q as walk roots, do @@ -599,10 +689,12 @@ package MemNetRequirements { records this; Recall/Commit operatorCount stays 2. */ attribute requirementId : String = "MN-REQ-04.8"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_04_9_SessionOutlineEmptyCue { doc /* + Agent surface: Recall (pin_map). empty cue returns the session outline; keep unchanged. Teach name = outline. A dark session SHALL join the handed session id and ask the session, not a node. Empty q SHALL be Recall of S: a census of kinds that exist plus a few real @@ -623,10 +715,12 @@ package MemNetRequirements { revive Layer; restore the retired Neo4j cabinet. */ attribute requirementId : String = "MN-REQ-04.9"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_04_10_PeakLLastResort { doc /* + Agent surface: Recall (pin_map). Peak_L last-resort cue. Peak_L is a cue under RelativeSeed, not a third operator. When seed MATCH_L is empty and q is a non-empty codebook token (kind / props / keyword present, no hit), Recall SHALL seed from @@ -642,10 +736,12 @@ package MemNetRequirements { stays off the wire. operatorCount stays 2. */ attribute requirementId : String = "MN-REQ-04.10"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_04_11_PinMapOrderFromObservables { doc /* + Agent surface: Recall (pin_map). pin_map order from Shape observables. Gauge honesty (0.19.c; not a usage-method b, not 0.20, not 1.0). The emitted order of a live pin_map SHALL be a function of the Recall Shape's observables: kind (primary label) plus the @@ -665,10 +761,12 @@ package MemNetRequirements { slug), never leftover id as the rank key. */ attribute requirementId : String = "MN-REQ-04.11"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_04_12_HousekeepEndpointIdentity { doc /* + Agent surface: internal. housekeep endpoint identity; not an agent verb. Housekeep orphan, dangling, and stale counts SHALL resolve each edge endpoint by hidden element id (_elN) and by the optional id nickname. A GQL edge stores _elN. Comparing that @@ -684,6 +782,7 @@ package MemNetRequirements { --apply does. */ attribute requirementId : String = "MN-REQ-04.12"; + attribute agentSurface : String = "internal"; } } @@ -691,28 +790,37 @@ package MemNetRequirements { requirement def MN_REQ_05_HardCaps { doc /* + Agent surface: internal. hard caps group; leaves split across Recall / Commit / internal. Group: bounded resource use under misuse. */ attribute requirementId : String = "MN-REQ-05"; + attribute agentSurface : String = "internal"; requirement def MN_REQ_05_1_StoreResourceCaps { doc /* + Agent surface: internal. store resource caps. SHALL enforce hard caps on rows, law rows, relations, tags, fields, and value/line/batch bytes so the store cannot grow unbounded. */ attribute requirementId : String = "MN-REQ-05.1"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_05_2_QueryFanoutCaps { doc /* + Agent surface: Recall (pin_map). query fan-out caps; truncation flagged on the wire. SHALL enforce hard caps on pin-map/walk depth and fanout so token - cost stays bounded under misuse. + cost stays bounded under misuse. Caps are hard: when a walk is + clipped the wire SHALL flag Truncation (truncated=true). SHALL + NOT silent-clip. */ attribute requirementId : String = "MN-REQ-05.2"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_05_3_ConsistentValueByteCap { doc /* + Agent surface: Commit (mutate). one value-byte cap on mutate and snapshot load. The field-value byte cap SHALL be one hard cap on leftover pipe mutate, GQL mutate, and snapshot load, measured as the UTF-8 byte length of the decoded (raw) property value. @@ -729,6 +837,7 @@ package MemNetRequirements { line_bytes) rather than write a file load will refuse. */ attribute requirementId : String = "MN-REQ-05.3"; + attribute agentSurface : String = "Commit"; } } @@ -736,40 +845,49 @@ package MemNetRequirements { requirement def MN_REQ_06_ProcessBoundary { doc /* + Agent surface: ops (later PR may split deploy/ops). process / transport / fleet; later PR may split deploy/ops. Group: how clients bind to GraphStore and the session registry. Locked preference: in-process primary, local IPC for multi-process share, TCP localhost only as migration / fallback (not the core). */ attribute requirementId : String = "MN-REQ-06"; + attribute agentSurface : String = "ops"; requirement def MN_REQ_06_1_InProcessPrimary { doc /* + Agent surface: ops (later PR may split deploy/ops). in-process primary binding. SHALL provide in-process GraphStore and session APIs as the primary binding for MCP and library users (lowest latency; no network hop). */ attribute requirementId : String = "MN-REQ-06.1"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_2_LocalIpcShare { doc /* + Agent surface: ops (later PR may split deploy/ops). local IPC share. SHALL support a local IPC gateway when CLI and MCP must share one session registry across processes (prefer named pipe on Windows / AF_UNIX on POSIX). */ attribute requirementId : String = "MN-REQ-06.2"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_3_TcpServeMigration { doc /* + Agent surface: ops (later PR may split deploy/ops). TCP serve migration. MAY expose TCP localhost length-prefixed serve as a migration or multi-process fallback; it SHALL NOT be treated as the conceptual core binding. */ attribute requirementId : String = "MN-REQ-06.3"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_4_DurableStoreBehindMemNet { doc /* + Agent surface: ops (later PR may split deploy/ops). durable store behind MemNet. When a durable online GQL store (AgensGraph-class) is present, MemNet SHALL remain the sole agent-facing shared memory. The store SHALL sit behind MemNet (hydrate/flush). LLM ↔ store @@ -797,10 +915,12 @@ package MemNetRequirements { SHALL NOT add rag_query MCP. SHALL NOT read a Neo4j library. */ attribute requirementId : String = "MN-REQ-06.4"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_5_HumanUsageLook { doc /* + Agent surface: ops (later PR may split deploy/ops). human usage look; not agent wire. SHALL provide a read-only human look at live serve usage: serve_status (up / host / port), session census (@STAT sessions|n/max plus listed ids), housekeep_stats @@ -817,10 +937,12 @@ package MemNetRequirements { shipped without teaching agents the page as wire. */ attribute requirementId : String = "MN-REQ-06.5"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_6_DeviceServicesOneDropletMcp { doc /* + Agent surface: ops (later PR may split deploy/ops). device services / one droplet MemNet MCP. A droplet-centred ops fleet SHALL run MemNet engine/serve (MemNetCoreLibrary) on one or more device hosts (Pi, workstation, SBC) as separate services, each with its own @@ -849,10 +971,12 @@ package MemNetRequirements { per MN-REQ-12.2. */ attribute requirementId : String = "MN-REQ-06.6"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_7_SsotToCodeAllocate { doc /* + Agent surface: ops (later PR may split deploy/ops). SSOT → code allocate. SHALL allocate shipped logical SSOT parts (MemNetCoreLibrary, MemNetMcpServer, transport, session, RecallCommit, MCP, durable, pin-map, host-search) to live Python modules in this checkout @@ -876,10 +1000,12 @@ package MemNetRequirements { satisfy this allocate. Hatch stays 0.19.11 honesty c. */ attribute requirementId : String = "MN-REQ-06.7"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_8_TipMemNetAccessPortal { doc /* + Agent surface: ops (later PR may split deploy/ops). tip MemNet access portal. SHALL provide an ops access portal for gated tip MemNet MCP keys. This portal is tip/ops access control (tip≠face). It SHALL NOT be the SysMLEdge product, a sysmledge key page, or @@ -910,10 +1036,12 @@ package MemNetRequirements { session_close from the page. */ attribute requirementId : String = "MN-REQ-06.8"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_9_LanMcpFrontSeveralServes { doc /* + Agent surface: ops (later PR may split deploy/ops). LAN MCP front; inventOnly. OPTIONAL Later invent (#191): named move ClusterRoute — where the session lives. One MemNet MCP front MAY catalogue sessions across several LAN memnet serve @@ -970,10 +1098,12 @@ package MemNetRequirements { false; no SemVer bump. */ attribute requirementId : String = "MN-REQ-06.9"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_10_SliceHandCarryAcrossServes { doc /* + Agent surface: ops (later PR may split deploy/ops). SliceHandCarry; inventOnly. OPTIONAL Later invent (#47 cousin; contrast to #191): named move SliceHandCarry — a bounded WorkingMemorySlice MAY leave one memnet serve and be absorbed into a @@ -1003,10 +1133,12 @@ package MemNetRequirements { no SemVer bump. Public operator text: InkMirage only. */ attribute requirementId : String = "MN-REQ-06.10"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_11_AdminServeUsageReport { doc /* + Agent surface: ops (later PR may split deploy/ops). admin serve usage report. SHALL expose a read-only admin usage report on memnet-serve (TcpServeBridge / LocalIpcGateway argv proxy; optional envelope admin_usage entry). InkMirage admin MCP / face @@ -1062,10 +1194,12 @@ package MemNetRequirements { arg in this cut. No SemVer bump. */ attribute requirementId : String = "MN-REQ-06.11"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_12_ProductGateway { doc /* + Agent surface: ops (later PR may split deploy/ops). product gateway. SHALL provide MemNetProductGateway as a capability of memnet-mcp (MemNetLanMcpFront), not a separate droplet shim. Online products (Endleaf now; Atelier and @@ -1119,10 +1253,12 @@ package MemNetRequirements { (implemented=true). */ attribute requirementId : String = "MN-REQ-06.12"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_13_ServeRequestIsolation { doc /* + Agent surface: ops (later PR may split deploy/ops). serve request isolation. Concurrent memnet serve commands (TCP and the product gateway, which forwards to that serve) SHALL isolate each request's stdout, stderr, and exit_code. One @@ -1136,10 +1272,12 @@ package MemNetRequirements { require a serve-wide lock. */ attribute requirementId : String = "MN-REQ-06.13"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_14_SafeServeUpgrade { doc /* + Agent surface: ops (later PR may split deploy/ops). safe serve upgrade. SHALL upgrade a live memnet-serve without silently dropping a loaded session. The procedure stays inside memnet serve: snapshot every loaded session, restart @@ -1188,10 +1326,12 @@ package MemNetRequirements { the old venv. No real session id in docs or tests. */ attribute requirementId : String = "MN-REQ-06.14"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_06_15_ServeCheckpoint { doc /* + Agent surface: ops (later PR may split deploy/ops). admin checkpoint. SHALL checkpoint every loaded session to disk without draining the serve and without refusing traffic. Patch-level c on 0.19. Same usage method (cue then @@ -1240,6 +1380,7 @@ package MemNetRequirements { tests. */ attribute requirementId : String = "MN-REQ-06.15"; + attribute agentSurface : String = "ops"; } } @@ -1247,65 +1388,83 @@ package MemNetRequirements { requirement def MN_REQ_07_McpAgentBoundary { doc /* + Agent surface: names the five product verbs (recall, commit, session open, save, close). MCP/CLI boundary group; names the agent surface. Group: generic MCP/CLI tool surface for MemNet; TARGET is cue/pattern only (no store-key id); leftover id tools are not product commands; no domain-product tools. */ attribute requirementId : String = "MN-REQ-07"; + attribute agentSurface : String = "surface"; requirement def MN_REQ_07_1_GenericToolSurface { doc /* - SHALL expose a TARGET generic MCP/CLI tool surface (same two - operators: Recall + Commit): session_open / current / save / load / - list / close - (session handle is not a graph store key); find (bounded MATCH by - kind / labels / properties / keyword, hard LIMIT — the cue when - no nickname exists); pin_map as a shaped neighbourhood walk from - a cue/pattern (labels+props; optional nickname id if the agent - set one — SHALL NOT require a store-key anchor); mutate as one - Commit of GQL (CREATE () legal; MERGE/SET/DELETE by - labels+properties; edge = type + two end elements; no NEW; no - AssignedIdMap); Path-B ingest (locators qname/path/refdes/… are - properties, not PK; no client NEW; no allocate_from_locator as - identity); Path-B import (match by labels+properties, not - MERGE-by-id remint). SHALL NOT nest leftover 0.9 read_get(id), - NEW / [NEW], AssignedIdMap, add/update that fail without a - ground store-key, pin_map(anchor=) as the only pin - form, allocate_from_locator, import_slice id_policy + Agent surface: names the five product verbs (recall, commit, session open, save, close). agent surface is exactly recall, commit, session open, save, and close. + SHALL expose a TARGET generic MCP/CLI tool surface whose agent + verbs are exactly five: recall (pin_map), commit (mutate), + session_open, session_save, and session_close. Session handle + is not a graph store key. pin_map is Recall (shaped neighbourhood + from a cue/pattern; empty q = outline; optional nickname id if + the agent set one — SHALL NOT require a store-key anchor). + mutate is one Commit of GQL (CREATE () legal; MERGE/SET/DELETE + by labels+properties; edge = type + two end elements; no NEW; + no AssignedIdMap). + Options of session: session_current, session_list, session_load + (load is an option of save), keep_id. + Options of Recall: find (bounded MATCH seed, hard LIMIT); + leftover query_warm (alias of pin_map); leftover query_walk; + export_pin_map (GQL write-out of a cue pin_map, not SysML reverse). + Options of Commit: leftover add / update (façades; AddFailsIfExists + / UpdateFailsIfAbsent / no-silent-upsert unchanged; merge-and-report + pending); Path-B ingest (locators are properties, not PK); + snap_model; import_slice / Absorb (match by labels+properties, + not MERGE-by-id remint); RSV reserve / extend / release. + Internal: housekeep_stats, serve_status, session_acl_*, + leftover read_list / read_get(id). + SHALL NOT nest leftover 0.9 read_get(id), NEW / [NEW], + AssignedIdMap, add/update that fail without a ground store-key, + pin_map(anchor=) as the only pin form, + allocate_from_locator, import_slice id_policy keep|reject|remint keyed on store-key, or elementId/handle on the wire as product commands (those remain leftover_* engine - honesty). Optional engine/status probe and housekeep stats MAY - remain. In-process by default (MN-REQ-06.1). + honesty). In-process by default (MN-REQ-06.1). */ attribute requirementId : String = "MN-REQ-07.1"; + attribute agentSurface : String = "surface"; } requirement def MN_REQ_07_2_StructuredToolEnvelope { doc /* + Agent surface: internal. structured tool envelope. SHALL return each MCP tool result in a structured envelope carrying exit status, stdout payload, and stderr/error lines. */ attribute requirementId : String = "MN-REQ-07.2"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_07_3_NoDomainProductTools { doc /* + Agent surface: internal. no domain-product tools on generic MCP. Domain-product tools SHALL NOT be embedded in the generic MemNet MCP boundary. */ attribute requirementId : String = "MN-REQ-07.3"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_07_4_McpLawSeedOnOpen { doc /* + Agent surface: option of session open / save / close. law-seed on session_open — option of open. MCP session_open SHALL auto-seed engine-law rows when they are missing from the session. */ attribute requirementId : String = "MN-REQ-07.4"; + attribute agentSurface : String = "option:session"; } requirement def MN_REQ_07_5_NoStoreKeyToolSurface { doc /* + Agent surface: internal. no store-key tool surface. TARGET agent/CLI/MCP I/O SHALL NOT key tools on a store-key id. Optional NicknameIdProperty MAY seed find/pin_map/mutate when the agent set one — it is not identity (MN-REQ-02.8). @@ -1322,6 +1481,7 @@ package MemNetRequirements { mint laws are not TARGET commands. */ attribute requirementId : String = "MN-REQ-07.5"; + attribute agentSurface : String = "internal"; } } @@ -1329,6 +1489,7 @@ package MemNetRequirements { requirement def MN_REQ_08_AgentIoGrammar { doc /* + Agent surface: internal. agent I/O grammar; applies to both operators. Group: LLM-friendly grammar on agent-facing I/O. MemNet agent I/O (stdio pin-map emit; MCP tool args/results that carry memory language) is produced and consumed by an LLM, so presentation SHALL follow @@ -1339,34 +1500,42 @@ package MemNetRequirements { import-once only. */ attribute requirementId : String = "MN-REQ-08"; + attribute agentSurface : String = "internal"; requirement def MN_REQ_08_1_LlmFriendlyStdio { doc /* + Agent surface: internal. LLM-friendly stdio. SHALL present an LLM-friendly grammar on CLI/stdio agent I/O: low-noise, learnable, and accurate when presenting facts. */ attribute requirementId : String = "MN-REQ-08.1"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_08_2_LlmFriendlyMcp { doc /* + Agent surface: internal. LLM-friendly MCP. SHALL present an LLM-friendly grammar on MCP tool payloads that agents read or write. */ attribute requirementId : String = "MN-REQ-08.2"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_08_3_NoHostilePositionalDump { doc /* + Agent surface: internal. no hostile positional dump. Agent-facing I/O SHALL NOT rely on hostile positional dumps that burn tokens or invite routine field-count and escape errors, contradicting MN-REQ-00 token economy and factual accuracy. */ attribute requirementId : String = "MN-REQ-08.3"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_08_4_BoundaryFriendlyIfInternalDiffers { doc /* + Agent surface: internal. boundary-friendly if internal differs. Store-internal representation MAY differ from the agent grammar only if every agent boundary still presents the friendly surface. Internal store records MAY differ; legacy pipe import-once is allowed. Pipe @@ -1374,18 +1543,22 @@ package MemNetRequirements { or mutate/write. */ attribute requirementId : String = "MN-REQ-08.4"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_08_5_IoActorIsLlm { doc /* + Agent surface: internal. I/O actor is the LLM. Agent-facing MemNet I/O SHALL be designed as produced and consumed by an LLM (not primarily by hand-written UI forms). */ attribute requirementId : String = "MN-REQ-08.5"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_08_6_FollowPromptRules { doc /* + Agent surface: internal. follow prompt rules. Agent-facing I/O grammar and presentation SHALL follow prompt rules: low-noise; accurate, precise, consistent, and coherent fact presentation; template-copyable shapes; write equals display. @@ -1393,34 +1566,41 @@ package MemNetRequirements { prompt-rule doctrines; not a novel-writer product dependency. */ attribute requirementId : String = "MN-REQ-08.6"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_08_7_WriteApproxDisplay { doc /* + Agent surface: Recall (pin_map). Write = display = shaped pin_map emit. Agent-facing emit and accept shapes SHALL be the same dialect: what the LLM writes is what it (and humans) read back -- Write = display -- so prompts need not teach a second grammar. */ attribute requirementId : String = "MN-REQ-08.7"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_08_8_TemplateCopyableShapes { doc /* + Agent surface: internal. template-copyable shapes. Agent-facing records SHALL use template-copyable shapes the LLM can imitate from few examples without inventing column order or escape tricks. */ attribute requirementId : String = "MN-REQ-08.8"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_08_9_PinMapEmitSameAsMutate { doc /* + Agent surface: Recall (pin_map). pin_map emit same family as mutate; Commit shares the grammar. Live pin map emit SHALL be a shaped subgraph in the same GQL / property-graph family as accepted mutate input (redefined Write = display). SHALL NOT present raw tabular RETURN as primary read, nor a second peer dialect on read vs GQL on write. */ attribute requirementId : String = "MN-REQ-08.9"; + attribute agentSurface : String = "Recall"; } } @@ -1428,13 +1608,16 @@ package MemNetRequirements { requirement def MN_REQ_09_HumanInspect { doc /* + Agent surface: internal. human inspect; parser is SSOT. Group: humans and tools recover structure from the grammar via one canonical parser (grammar = language; parser = structure recovery). */ attribute requirementId : String = "MN-REQ-09"; + attribute agentSurface : String = "internal"; requirement def MN_REQ_09_1_CanonicalParserSsot { doc /* + Agent surface: internal. canonical parser SSOT; keep unchanged. SHALL provide a single canonical parser (one SSOT parse path) that recovers NODE and EDGE structure (kind, optional nickname id, fields, endpoints) from the agent-facing grammar, including @@ -1445,34 +1628,42 @@ package MemNetRequirements { id_first / NEW. */ attribute requirementId : String = "MN-REQ-09.1"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_09_2_RejectInvalidWire { doc /* + Agent surface: internal. reject invalid wire. The canonical parser SHALL reject invalid wire rather than silently mis-split fields. */ attribute requirementId : String = "MN-REQ-09.2"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_09_3_ParseFaithfulInspect { doc /* + Agent surface: internal. parse-faithful inspect. Humans and tools SHALL be able to inspect what MemNet knows via a parse-faithful render or read path that uses the canonical parser. */ attribute requirementId : String = "MN-REQ-09.3"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_09_4_NoAdHocConsumerSplits { doc /* + Agent surface: internal. no ad-hoc consumer splits. Ad-hoc consumer splits (e.g. naive delimiter split) SHALL NOT be the supported inspection or ingest path. */ attribute requirementId : String = "MN-REQ-09.4"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_09_5_GraphGlotParseThenProductGate { doc /* + Agent surface: internal. GraphGlot parse then product gate. Accept SHALL parse agent GQL with GraphGlot (PyPI graphglot; official grammar front), then run a separate MemNet product gate that rejects Layer / Tier A and free WITH / UNWIND / CALL / unbounded MATCH…RETURN @@ -1482,6 +1673,7 @@ package MemNetRequirements { calls graphglot on the accept path. */ attribute requirementId : String = "MN-REQ-09.5"; + attribute agentSurface : String = "internal"; } } @@ -1489,6 +1681,7 @@ package MemNetRequirements { requirement def MN_REQ_10_LlmPropertiesAndLimits { doc /* + Agent surface: internal. LLM assumed properties and limits. Group: assumed properties of the LLM agents MemNet serves, and the MemNet limitations that follow. Attribute values document assumed properties (true = property is assumed to hold for design). Nested @@ -1496,6 +1689,7 @@ package MemNetRequirements { These underpin MN-REQ-00 token economy and factual accuracy. */ attribute requirementId : String = "MN-REQ-10"; + attribute agentSurface : String = "internal"; // Assumed LLM properties (design assumptions -- not product SHALLs) attribute unreliableCrossTurnMemory : Boolean = true; @@ -1507,59 +1701,73 @@ package MemNetRequirements { requirement def MN_REQ_10_1_NoChatAsDurableStore { doc /* + Agent surface: internal. chat is not durable store. SHALL NOT treat chat context as the durable store of pipeline facts (LLMs have unreliable cross-turn working memory). */ attribute requirementId : String = "MN-REQ-10.1"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_10_2_PinMapMustFitContext { doc /* + Agent surface: Recall (pin_map). pin_map must fit context. Re-injected live pin maps SHALL be sized to fit a finite context window. window and token budget. */ attribute requirementId : String = "MN-REQ-10.2"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_10_3_MinimiseTokenRoundTrips { doc /* + Agent surface: Recall (pin_map). minimise token round-trips. Agent-facing I/O SHALL minimise unnecessary token burn and tool round-trips when re-presenting the same facts. */ attribute requirementId : String = "MN-REQ-10.3"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_10_4_ExternalGroundTruthRequired { doc /* + Agent surface: internal. external ground truth required. Facts that must stay accurate SHALL be grounded in MemNet (or via MemNet from search), not invented ids or claims in chat alone. */ attribute requirementId : String = "MN-REQ-10.4"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_10_5_GrammarMustBeLlmLearnable { doc /* + Agent surface: internal. grammar must be LLM-learnable. Agent-facing grammar SHALL be LLM-learnable: low noise, not a brittle positional or escape-heavy formalism that invites routine errors. */ attribute requirementId : String = "MN-REQ-10.5"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_10_6_PreferTemplateFriendlyPins { doc /* + Agent surface: Recall (pin_map). prefer template-friendly pins. Agent-facing presentation SHOULD favour English or structured low-noise pins with clear templates where the LLM is strong. */ attribute requirementId : String = "MN-REQ-10.6"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_10_7_HumanVerifyViaParser { doc /* + Agent surface: internal. human verify via parser. Humans SHALL be able to verify what MemNet knows via the canonical parser path, compensating for LLM invention without ground truth. */ attribute requirementId : String = "MN-REQ-10.7"; + attribute agentSurface : String = "internal"; } } @@ -1568,6 +1776,7 @@ package MemNetRequirements { requirement def MN_REQ_11_SnapshotInterop { doc /* + Agent surface: option of Commit. snapshot / ingest interop; importers stay here this cut. Group: a map of pins -- short accurate anchors and locators into SysML v2, codebase, PCBA schematics (Atopile .ato), and agent skills/rules, linked as NODE and EDGE so the LLM can navigate without @@ -1576,29 +1785,35 @@ package MemNetRequirements { through MN-REQ-01.6). */ attribute requirementId : String = "MN-REQ-11"; + attribute agentSurface : String = "option:Commit"; requirement def MN_REQ_11_13_MapOfPins { doc /* + Agent surface: option of Recall. map of pins; emit economy shared with Recall. Every MN-REQ-11 snapshot or export SHALL be a map of pins: short accurate anchors and locators linked as NODE and EDGE, sufficient for LLM navigation. SHALL NOT be a complete copy of the source artefact or a bulky corpus dump (same spirit as pin-map economy). */ attribute requirementId : String = "MN-REQ-11.13"; + attribute agentSurface : String = "option:Recall"; } requirement def MN_REQ_11_1_ExportGraphToSysmlV2 { doc /* + Agent surface: option of Recall. export_pin_map is a GQL write-out of a cue pin_map (option of Recall); SysML v2 reverse is not claimed. SHALL support exporting a pin map of in-memory NODE and EDGE facts into a SysML v2 model representation for the in-repo model-based workflow (sysml-models). SHALL NOT require dumping the entire session graph. */ attribute requirementId : String = "MN-REQ-11.1"; + attribute agentSurface : String = "option:Recall"; } requirement def MN_REQ_11_2_NodeMapsToSysmlStructure { doc /* + Agent surface: option of Recall. SysML node mapping of export; live code is GQL pin_map write-out, not .sysml reverse. On SysML export, each MemNet NODE included in the pin map SHALL map to a SysML v2 structural element analogue (e.g. part, item, or attribute usage) -- an isomorphic projection of the pins, not a @@ -1606,20 +1821,24 @@ package MemNetRequirements { a requirement to export every store row. */ attribute requirementId : String = "MN-REQ-11.2"; + attribute agentSurface : String = "option:Recall"; } requirement def MN_REQ_11_3_EdgeMapsToSysmlConnection { doc /* + Agent surface: option of Recall. SysML edge mapping of export; live code is GQL pin_map write-out, not .sysml reverse. On SysML export, each MemNet EDGE included in the pin map SHALL map to a SysML v2 connection or relationship analogue between the mapped structural elements -- projection of the pins only, not literal EDGE metamodel types. */ attribute requirementId : String = "MN-REQ-11.3"; + attribute agentSurface : String = "option:Recall"; } requirement def MN_REQ_11_4_DistinctFromSessionSnapshot { doc /* + Agent surface: internal. pin-map export/ingest distinct from session snapshot. Pin maps (SysML export, codebase, PCBA schematics via Atopile .ato, skills/rules) SHALL be distinct from named-session file save and load (MN-REQ-01.4 through MN-REQ-01.6), which persist @@ -1628,48 +1847,58 @@ package MemNetRequirements { taken as a full-fidelity session backup or corpus dump. */ attribute requirementId : String = "MN-REQ-11.4"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_11_5_SysmlRoundTripShould { doc /* + Agent surface: option of Recall. SysML round-trip SHOULD; not claimed from ingest or export_pin_map. Round-trip of the SysML pin map back into MemNet NODE and EDGE form SHOULD be supported where practical. Full-fidelity recovery of original SysML sources from the pins SHALL NOT be required; lossy cases SHALL be documented. */ attribute requirementId : String = "MN-REQ-11.5"; + attribute agentSurface : String = "option:Recall"; } requirement def MN_REQ_11_6_SnapshotCodebaseIntoGraph { doc /* + Agent surface: option of Commit. ingest_codebase — option of Commit. SHALL support ingesting a pin map of a codebase into the Net of Memory as NODE and EDGE locators so agents can fetch a pin map, walk, and navigate purpose-relevant structure without swallowing the tree. SHALL NOT require indexing every file, symbol, or line. */ attribute requirementId : String = "MN-REQ-11.6"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_7_CodebaseNodesAreModulesSymbols { doc /* + Agent surface: option of Commit. codebase nodes; option of Commit. A codebase pin map SHALL represent selected files or modules and symbols (functions, variables, types, and similar) as NODE pins -- short anchors, not free prose blobs or an exhaustive inventory. */ attribute requirementId : String = "MN-REQ-11.7"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_8_CodebaseEdgesAreRelations { doc /* + Agent surface: option of Commit. codebase edges; option of Commit. A codebase pin map SHALL represent selected relations among those pins (defines, calls, includes, owns, uses, and similar) as first-class EDGEs -- not every possible edge in the corpus. */ attribute requirementId : String = "MN-REQ-11.8"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_9_SnapshotAtoIntoGraph { doc /* + Agent surface: option of Commit. ingest_pcba / .ato — option of Commit. SHALL support ingesting a pin map of PCBA schematics from Atopile (.ato projects or files) into the Net of Memory as NODE and EDGE locators so agents can fetch a pin map, walk, and navigate purpose-relevant @@ -1679,58 +1908,70 @@ package MemNetRequirements { every .ato artefact. */ attribute requirementId : String = "MN-REQ-11.9"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_10_SnapshotSkillsAndRulesIntoGraph { doc /* + Agent surface: option of Commit. ingest_skills — option of Commit. SHALL support ingesting a pin map of agent skills and rules (Cursor/agent SKILL.md packs, .mdc rules, and similar) into the Net of Memory as NODE and EDGE locators. SHALL NOT require copying every skill body or rule file verbatim into the graph. */ attribute requirementId : String = "MN-REQ-11.10"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_11_SkillsAndRulesAreNodes { doc /* + Agent surface: option of Commit. skills/rules nodes; option of Commit. A skills/rules pin map SHALL represent selected skill packs and rules as NODE pins -- short anchors, not free prose blobs or an exhaustive catalogue of every installed pack. */ attribute requirementId : String = "MN-REQ-11.11"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_12_SkillsRulesEdgesAreRelations { doc /* + Agent surface: option of Commit. skills/rules edges; option of Commit. A skills/rules pin map SHALL represent selected relations among those pins (paired_with, triggers, governs, and similar) as first-class EDGEs. */ attribute requirementId : String = "MN-REQ-11.12"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_14_PcbaAtoNodesAreNetsComponentsPins { doc /* + Agent surface: option of Commit. PCBA nodes; option of Commit. A PCBA schematic (Atopile .ato) pin map SHALL represent selected nets, components, and pins as NODE pins -- short anchors into the schematic, not free prose blobs or an exhaustive inventory of every symbol and wire. */ attribute requirementId : String = "MN-REQ-11.14"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_15_PcbaAtoEdgesAreConnectivity { doc /* + Agent surface: option of Commit. PCBA edges; option of Commit. A PCBA schematic (Atopile .ato) pin map SHALL represent selected connectivity among those pins (pin-to-net, component-owns-pin, and similar) as first-class EDGEs -- not every possible connection in the schematic. */ attribute requirementId : String = "MN-REQ-11.15"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_16_PinIngestDeterministicIds { doc /* + Agent surface: option of Commit. Path-B ingest locators; option of Commit. Pin-map ingest (SysML, codebase, PCBA/.ato, skills) SHALL Commit create of GraphElement from the source. Locators (path, refdes, net, pin, qname, skill_id, and similar) SHALL be properties on @@ -1746,10 +1987,12 @@ package MemNetRequirements { SysML v2 parser and has no grammar. */ attribute requirementId : String = "MN-REQ-11.16"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_17_CatalogModelSnap { doc /* + Agent surface: option of Commit. snap_model — option of Commit (locator mint, not a third operator). 0.15 Catalog Snap + session strata + model Snap. SHALL Snap one SysML load tree into a catalog session plus package interiors (optional kind band or child package when an interior exceeds @@ -1766,9 +2009,11 @@ package MemNetRequirements { Cross-cut satisfy: MN-REQ-11.17.1. */ attribute requirementId : String = "MN-REQ-11.17"; + attribute agentSurface : String = "option:Commit"; requirement def MN_REQ_11_17_1_CrossCutSatisfyLocators { doc /* + Agent surface: option of Commit. cross-cut satisfy locators on catalog Snap. When Snap drops a satisfy whose ends live in two interiors of the same model Snap, the catalog SHALL Commit locator stubs (session= + qname= / requirementId=) and a satisfies edge when @@ -1785,10 +2030,12 @@ package MemNetRequirements { generate; join remains Path-B slice. */ attribute requirementId : String = "MN-REQ-11.17.1"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_17_2_SnapSessionGrain { doc /* + Agent surface: option of Commit. Snap session grain. Catalog Snap SHALL mint one catalog session plus a bounded number of interiors for one load tree. Grain SHALL be top-level package first; optional kind band (REQ / PRT / POR) @@ -1807,10 +2054,12 @@ package MemNetRequirements { interior fits 2M remains TARGET, not this leaf. */ attribute requirementId : String = "MN-REQ-11.17.2"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_17_3_SnapBudgetPrecheck { doc /* + Agent surface: option of Commit. Snap budget pre-check. Before Catalog Snap mints any session, it SHALL count the catalog plus planned interiors against remaining session capacity (MEMNET_MAX_SESSIONS unchanged) and SHALL check @@ -1823,10 +2072,12 @@ package MemNetRequirements { Caps defaults SHALL NOT change. */ attribute requirementId : String = "MN-REQ-11.17.3"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_17_4_SnapCallerGoneRollback { doc /* + Agent surface: option of Commit. Snap caller-gone rollback. When the serve or IPC caller is gone before Snap finishes (client wait exceeded, or the peer socket closed), serve SHALL stop minting, roll back sessions this call created, @@ -1838,10 +2089,12 @@ package MemNetRequirements { SHALL NOT return an error while sessions keep appearing. */ attribute requirementId : String = "MN-REQ-11.17.4"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_11_17_5_SnapReplaceByModelIdentity { doc /* + Agent surface: option of Commit. Snap replace by model identity. A second Catalog Snap of the same load-tree identity (resolved root path) SHALL replace the live stack for that identity after the new stack commits, and SHALL NOT leave @@ -1853,6 +2106,7 @@ package MemNetRequirements { first; replace still holds when they do not. */ attribute requirementId : String = "MN-REQ-11.17.5"; + attribute agentSurface : String = "option:Commit"; } } } @@ -1867,6 +2121,7 @@ package MemNetRequirements { requirement def MN_REQ_12_MultitaskMode { doc /* + Agent surface: internal. Multitask protocol; workers use Recall then Commit; not one operator. Group: when Multitask Mode (or Task sub-agents) is active, MemNet SHALL be operated as a shared mission store with a parent coordinator and scoped workers. Chat is never mission SSOT. As-is product @@ -1875,9 +2130,11 @@ package MemNetRequirements { private/shared/open + session_token modes deferred. */ attribute requirementId : String = "MN-REQ-12"; + attribute agentSurface : String = "internal"; requirement def MN_REQ_12_1_SharedSessionMissionSsot { doc /* + Agent surface: session (open / save / close). shared session id is mission SSOT handle. Under Multitask, SHALL use one shared named MemNet session id as the mission source of truth for ids, paths, and task state (session as SSOT handle for the MissionWorkingSet). Handoff SHALL be by session @@ -1886,10 +2143,12 @@ package MemNetRequirements { (extends MN-REQ-10.1 / MN-REQ-01.7 / MN-REQ-01.8). */ attribute requirementId : String = "MN-REQ-12.1"; + attribute agentSurface : String = "session"; } requirement def MN_REQ_12_2_SharedStoreTransport { doc /* + Agent surface: ops (later PR may split deploy/ops). shared store transport (TCP / streamable-http). Under Multitask, parent and workers SHALL bind to the same shared GraphStore/session registry via TCP serve, streamable-http MCP, or local IPC when available. Default isolated in-process MCP (one @@ -1897,10 +2156,12 @@ package MemNetRequirements { missions. Single-agent goldfish MAY still use in-process (MN-REQ-06.1). */ attribute requirementId : String = "MN-REQ-12.2"; + attribute agentSurface : String = "ops"; } requirement def MN_REQ_12_3_ParentOwnsTaskLifecycle { doc /* + Agent surface: internal. parent owns task lifecycle. The parent coordinator SHALL mint and own house task and user- constraint lifecycle rows (TSK_* / USR_*): create, status=active, status=settled, and optional led_to_success edges. Workers SHALL NOT @@ -1908,10 +2169,12 @@ package MemNetRequirements { delegated that settle. */ attribute requirementId : String = "MN-REQ-12.3"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_12_4_WorkerPinMapAndScopedWrite { doc /* + Agent surface: Recall (pin_map). worker pin_map first; scoped write is Commit under the same mission. Each worker turn SHALL begin with a live pin map on the shared session, copy assigned ids from that pin map, and mutate only under the parent-assigned subgraph (anchors and relation scope). Workers @@ -1919,10 +2182,12 @@ package MemNetRequirements { different session id unless the parent assigned a separate mission. */ attribute requirementId : String = "MN-REQ-12.4"; + attribute agentSurface : String = "Recall"; } requirement def MN_REQ_12_5_PreferOneWorkerOrDisjoint { doc /* + Agent surface: internal. prefer one worker or disjoint scopes. Multitask missions SHOULD prefer one worker per coherent workstream. Asynchronous parallel workers SHALL use disjoint anchor subtrees on one shared session OR separate parent-assigned session ids. @@ -1934,10 +2199,12 @@ package MemNetRequirements { last-write-wins. */ attribute requirementId : String = "MN-REQ-12.5"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_12_6_ParentEndTurnNoPoll { doc /* + Agent surface: internal. parent ends turn; no poll. After AsyncTaskDispatch (spawning background workers via EvDelegateWorker), the parent SHALL EvEndCoordinatorTurn (Multitask gate). The parent SHALL NOT poll or await worker @@ -1947,10 +2214,12 @@ package MemNetRequirements { (or SessionImportReceive when sessions were separate). */ attribute requirementId : String = "MN-REQ-12.6"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_12_7_NoAssumeAclReserveIngest { doc /* + Agent surface: internal. MUST NOT assume full ACL / reserve / ingest. As-is NOW: the CapsPolicy ACL cut IS shipped (engineAclShipped=true) when session ACL is enabled. Neighbourhood reserve (RSV, llm_id + TTL) IS shipped as NeighbourhoodReserve / MN-REQ-12.13. Path-B @@ -1962,10 +2231,12 @@ package MemNetRequirements { from ingest alone. */ attribute requirementId : String = "MN-REQ-12.7"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_12_8_AppliesToRelevantTasks { doc /* + Agent surface: internal. applies to relevant tasks. Multitask Mode (MN-REQ-12.1 through 12.7) SHALL apply when mission work is multi-step, coordinator-delegated to workers, or parallelisable across disjoint workers. Trivial one-shot @@ -1973,10 +2244,12 @@ package MemNetRequirements { Multitask shared-store or parent/worker obligations. */ attribute requirementId : String = "MN-REQ-12.8"; + attribute agentSurface : String = "internal"; } requirement def MN_REQ_12_13_NeighbourhoodReserve { doc /* + Agent surface: option of Commit. RSV lease — option of Commit, not a third operator. SHALL provide neighbourhood reserve (RSV) so parallel agents on one shared session_id can hold disjoint pin-map ego scopes: required llm_id holder, required ttl_s lease, scope = anchor + depth matching @@ -1987,10 +2260,12 @@ package MemNetRequirements { extend / release (MCP/CLI) — not + RSV graph mutate. */ attribute requirementId : String = "MN-REQ-12.13"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_12_9_LeadOwnsSessionImport { doc /* + Agent surface: option of Commit. lead-owned import_slice / Absorb — option of Commit. When a team lead imports a member's working memory (SessionImportReceive), the lead / parent SHALL own the import into the mission session. Path A (prefer shared mission sessionId): @@ -2016,10 +2291,12 @@ package MemNetRequirements { Distinct from micro merge=true (in-session re-id). */ attribute requirementId : String = "MN-REQ-12.9"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_12_10_NoChatOrWholeStoreImport { doc /* + Agent surface: option of Commit. no chat or whole-store import. Session import (SessionImportReceive) SHALL NOT use chat or tool transcripts as the import payload (EvImportFromChat forbidden). Engine SHALL require anchors on WorkingMemorySliceExport, enforce @@ -2028,10 +2305,12 @@ package MemNetRequirements { last-write-wins; MN-REQ-12.7). */ attribute requirementId : String = "MN-REQ-12.10"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_12_11_CheapLlmImportGuardSoft { doc /* + Agent surface: option of Commit. optional ImportGuard on Absorb. OPTIONAL soft policy (never mandatory LLM): Path B WorkingMemorySlice import SHOULD pass ImportGuard soft review before ImportAbsorb (allow / trim / reject + short reason; trim @@ -2052,10 +2331,12 @@ package MemNetRequirements { Satisfied by CheapLlmImportGuard part (not the hook alone). */ attribute requirementId : String = "MN-REQ-12.11"; + attribute agentSurface : String = "option:Commit"; } requirement def MN_REQ_12_12_HostDrivenAsyncParallel { doc /* + Agent surface: internal. host-driven async parallel. Asynchronous parallel Multitask: parent opens/loads one mission session (or explicitly separate sessions), mints TSK_*, assigns WorkerWriteScope, emits SessionHandoff, spawns workers via @@ -2069,6 +2350,7 @@ package MemNetRequirements { (MN-REQ-12.13) sits under Commit — not a third operator. */ attribute requirementId : String = "MN-REQ-12.12"; + attribute agentSurface : String = "internal"; } } @@ -2076,15 +2358,27 @@ package MemNetRequirements { requirement def MN_REQ_13_RecallCommit { doc /* - Group (thin 0.5): agent surface is two operators only — Recall(q) + Agent surface: spine (Recall(q) and Commit(Δ)). spine group: Recall(q) and Commit(Δ). + SPINE (0.5): agent-facing graph I/O is two operators only — Recall(q) and Commit(Δ) — over session graph S with rate cap R. Grammar SSOT: - docs/grammar/math-skeleton.md. Does not replace MN-REQ-02.4 / 04.*. - Engine/MCP cut SHALL NOT be claimed from this group alone. + docs/grammar/math-skeleton.md. Agent surface verbs are exactly + recall (pin_map), commit (mutate), session open, save, and close + (session verbs live on MN-REQ-01). Every other agent-visible + operation is an option of Recall, an option of Commit, an option + of session, or internal — named on agentSurface and in the leaf + doc. Organisational groups MN-REQ-01…12 stay nested under + MN-REQ-00 this cut; spine parentage is #derivation to the nested + arms MN-REQ-13.Recall / MN-REQ-13.Commit. Does not replace + MN-REQ-02.4 / 04.*. Engine/MCP cut SHALL NOT be claimed from this + group alone. Mapping table: + sysml-models/outputs/mn-req-13-spine-mapping.md. */ attribute requirementId : String = "MN-REQ-13"; + attribute agentSurface : String = "spine"; requirement def MN_REQ_13_1_TwoOperators { doc /* + Agent surface: spine (Recall(q) and Commit(Δ)). exactly two product operators; RSV is a Commit lease. SHALL model exactly two product operators: Recall(q) (seed then bounded k-hop reconstruct; empty q ⇒ session outline census of S — leftover empty-seed skip is leftover, not TARGET) and Commit(Δ) @@ -2120,31 +2414,67 @@ package MemNetRequirements { LangChain / HiGram as agent wire. */ attribute requirementId : String = "MN-REQ-13.1"; + attribute agentSurface : String = "spine"; } - requirement def MN_REQ_13_2_SameThingAbsorb { - doc /* - Second absorb case under Commit, distinct from ImportAbsorb. - SameThingAbsorb SHALL collapse two live session elements only - when the agent judges they are one world-thing. It is a Commit - rule, not a product verb, not Recall, not RelativeSeed / - ShapeWalk / pin_map. Pattern match is labels + properties (and - relationship type + ends); SHALL NOT leftover MERGE-by-id / - store-key / elementId. Same name (or any shared cue) is a - candidate only. Two same-name nodes that are different - world-things SHALL remain two (two "First National Bank"s stay - two) and SHALL appear on Recall as CueConflict (two roots), not - as one merged node. Collapse SHALL wait until the agent has - seen that conflict on find / pin_map emit. Name is a handle, not the thing: a name-change SHALL NOT - mint a second thing-node; optional aka (or name tokens pointing - at one element) is enough; name-token history is optional. - CHANGED between two thing-nodes only if the world actually split. - ImportAbsorb SHALL remain Path-B WorkingMemorySlice + leftover - id_policy and SHALL NOT do this entity resolution. Goldfish - writeback SHALL still not be called Absorb (MN-REQ-13.1). - operatorCount stays 2. - */ - attribute requirementId : String = "MN-REQ-13.2"; + requirement def MN_REQ_13_Recall { + doc /* + Agent surface: Recall (pin_map). spine arm Recall(q). + Spine arm Recall(q): product read. TARGET verb is pin_map. + Empty q SHALL return the session outline (MN-REQ-04.9). + find is an option of Recall (seed). leftover query_warm is a + leftover alias of pin_map. leftover query_walk / read_list / + --anchor are leftover options or leftover nicknames, not + product verbs. export_pin_map is an option of Recall (GQL + write-out of a cue pin_map; not SysML reverse). Hard caps + stay hard; a clipped neighbourhood SHALL flag Truncation on + the wire. + */ + attribute requirementId : String = "MN-REQ-13.Recall"; + attribute agentSurface : String = "Recall"; + } + + requirement def MN_REQ_13_Commit { + doc /* + Agent surface: Commit (mutate). spine arm Commit(Δ). + Spine arm Commit(Δ): product write. TARGET verb is mutate. + leftover add / update remain leftover façades of Commit + (AddFailsIfExists / UpdateFailsIfAbsent / no-silent-upsert + unchanged; merge-and-report is owner-pending). Path-B ingest + (ingest_sysml / ingest_codebase / ingest_pcba / ingest_skills), + snap_model, import_slice / Absorb, and RSV (reserve / extend / + release) are options of Commit, not third operators. + SameThingAbsorb is a Commit rule nested here. + */ + attribute requirementId : String = "MN-REQ-13.Commit"; + attribute agentSurface : String = "Commit"; + + requirement def MN_REQ_13_2_SameThingAbsorb { + doc /* + Agent surface: Commit (mutate). SameThingAbsorb is a Commit rule, not Recall. + Second absorb case under Commit, distinct from ImportAbsorb. + SameThingAbsorb SHALL collapse two live session elements only + when the agent judges they are one world-thing. It is a Commit + rule, not a product verb, not Recall, not RelativeSeed / + ShapeWalk / pin_map. Pattern match is labels + properties (and + relationship type + ends); SHALL NOT leftover MERGE-by-id / + store-key / elementId. Same name (or any shared cue) is a + candidate only. Two same-name nodes that are different + world-things SHALL remain two (two "First National Bank"s stay + two) and SHALL appear on Recall as CueConflict (two roots), not + as one merged node. Collapse SHALL wait until the agent has + seen that conflict on find / pin_map emit. Name is a handle, not the thing: a name-change SHALL NOT + mint a second thing-node; optional aka (or name tokens pointing + at one element) is enough; name-token history is optional. + CHANGED between two thing-nodes only if the world actually split. + ImportAbsorb SHALL remain Path-B WorkingMemorySlice + leftover + id_policy and SHALL NOT do this entity resolution. Goldfish + writeback SHALL still not be called Absorb (MN-REQ-13.1). + operatorCount stays 2. + */ + attribute requirementId : String = "MN-REQ-13.2"; + attribute agentSurface : String = "Commit"; + } } } } @@ -2422,8 +2752,12 @@ package MemNetRequirements { : MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit; requirement twoOperatorsReq : MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_1_TwoOperators; + requirement recallSpineReq + : MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_Recall; + requirement commitSpineReq + : MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_Commit; requirement sameThingAbsorbReq - : MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_2_SameThingAbsorb; + : MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_Commit::MN_REQ_13_2_SameThingAbsorb; // Groups derive from mission (MN-REQ-00) #derivation connection deriveGroupsFromMission { @@ -2604,9 +2938,78 @@ package MemNetRequirements { #derivation connection deriveRecallCommitLeaves { end #original ::> recallCommitReq; end #derive ::> twoOperatorsReq; + end #derive ::> recallSpineReq; + end #derive ::> commitSpineReq; + } + + #derivation connection deriveSameThingUnderCommit { + end #original ::> commitSpineReq; end #derive ::> sameThingAbsorbReq; } + // Spine reparent: agent-facing leaves derive from Recall or Commit. + // Organisational group→leaf derivation above is unchanged. + #derivation connection deriveRecallSpineLeaves { + end #original ::> recallSpineReq; + end #derive ::> sliceEconomyReq; + end #derive ::> queryOverNodeEdgeGraphReq; + end #derive ::> anchoredPinMapReq; + end #derive ::> recycleHiddenFromPinMapReq; + end #derive ::> pinMapDepthAndRowCapsReq; + end #derive ::> engineLawPrependReq; + end #derive ::> anchoredWalkReq; + end #derive ::> boundedMatchFindReq; + end #derive ::> relativeSeedThenShapeWalkReq; + end #derive ::> cueMultiRootHonestReq; + end #derive ::> sessionOutlineEmptyCueReq; + end #derive ::> peakLLastResortReq; + end #derive ::> pinMapOrderFromObservablesReq; + end #derive ::> queryFanoutCapsReq; + end #derive ::> writeApproxDisplayReq; + end #derive ::> pinMapEmitSameAsMutateReq; + end #derive ::> pinMapMustFitContextReq; + end #derive ::> minimiseTokenRoundTripsReq; + end #derive ::> preferTemplateFriendlyPinsReq; + end #derive ::> mapOfPinsReq; + end #derive ::> exportGraphToSysmlV2Req; + end #derive ::> nodeMapsToSysmlStructureReq; + end #derive ::> edgeMapsToSysmlConnectionReq; + end #derive ::> sysmlRoundTripShouldReq; + end #derive ::> workerPinMapAndScopedWriteReq; + } + + #derivation connection deriveCommitSpineLeaves { + end #original ::> commitSpineReq; + end #derive ::> strictMutateReq; + end #derive ::> addFailsIfExistsReq; + end #derive ::> updateFailsIfAbsentReq; + end #derive ::> noSilentUpsertReq; + end #derive ::> honourWherePredicateReq; + end #derive ::> schemaValidatedIngestReq; + end #derive ::> consistentValueByteCapReq; + end #derive ::> snapshotInteropReq; + end #derive ::> snapshotCodebaseIntoGraphReq; + end #derive ::> codebaseNodesAreModulesSymbolsReq; + end #derive ::> codebaseEdgesAreRelationsReq; + end #derive ::> snapshotAtoIntoGraphReq; + end #derive ::> snapshotSkillsAndRulesIntoGraphReq; + end #derive ::> skillsAndRulesAreNodesReq; + end #derive ::> skillsRulesEdgesAreRelationsReq; + end #derive ::> pcbaAtoNodesAreNetsComponentsPinsReq; + end #derive ::> pcbaAtoEdgesAreConnectivityReq; + end #derive ::> pinIngestDeterministicIdsReq; + end #derive ::> catalogModelSnapReq; + end #derive ::> crossCutSatisfyLocatorsReq; + end #derive ::> snapSessionGrainReq; + end #derive ::> snapBudgetPrecheckReq; + end #derive ::> snapCallerGoneRollbackReq; + end #derive ::> snapReplaceByModelIdentityReq; + end #derive ::> leadOwnsSessionImportReq; + end #derive ::> noChatOrWholeStoreImportReq; + end #derive ::> cheapLlmImportGuardSoftReq; + end #derive ::> neighbourhoodReserveReq; + } + #derivation connection deriveSameThingFromCueHonesty { end #original ::> cueMultiRootHonestReq; end #original ::> twoOperatorsReq; diff --git a/sysml-models/outputs/README.md b/sysml-models/outputs/README.md index 0fc4714e..a2398943 100644 --- a/sysml-models/outputs/README.md +++ b/sysml-models/outputs/README.md @@ -6,6 +6,7 @@ Derived views from `sysml-models/models/`. Model-first; sync here after structur - [system-design-notes.md](system-design-notes.md) - SharedLlmMemory, handoff, happy-path A re-pin, optional ImportGuard (path B), AsyncTaskDispatch / WorkerPool, M2.5, CIP/oC9 - [product-nest-one-page.md](product-nest-one-page.md) - Core session/GQL/RecallCommit + Multitask Path A/B + DurableBuffer; APPLICATION/ARCHIVE/OPS LOOK + IMPLEMENTATION allocate +- [mn-req-13-spine-mapping.md](mn-req-13-spine-mapping.md) - MN-REQ-13 Recall/Commit spine: old organisational parent → new spine parent; agentSurface counts ## Principles (once) diff --git a/sysml-models/outputs/mn-req-13-spine-mapping.md b/sysml-models/outputs/mn-req-13-spine-mapping.md new file mode 100644 index 00000000..e4e62f30 --- /dev/null +++ b/sysml-models/outputs/mn-req-13-spine-mapping.md @@ -0,0 +1,202 @@ +# MN-REQ-13 spine mapping + +Reparent of agent-facing requirements under **Recall(q)** or **Commit(Δ)**. +Requirement ids are unchanged. Organisational nesting of groups +MN-REQ-01…12 under MN-REQ-00 is unchanged this cut (importers, ops, +cousins, and archive stay put). Spine parentage is `#derivation` +to `MN-REQ-13.Recall` or `MN-REQ-13.Commit`. Session open / save / +close stay on MN-REQ-01 as the other agent-surface verbs. + +Source of the table: `agentSurface` on each requirement def in +`models/requirements.sysml`. + +## Later PRs (not this cut) + +- Split deploy/ops out of MN-REQ-06 / `deploy.sysml` ops parts. +- Move Path-B importers (MN-REQ-11.6…11.16) to a dedicated ingest nest. +- Move `cousins.sysml` / `archive.sysml` (or rename leftover coinages). +- Merge-and-report as a Commit rule (owner decision; 03.1–03.3 stay strict). + +## Spine tree + +```text +MN-REQ-00 Mission +├── MN-REQ-13 RecallCommit SPINE +│ ├── MN-REQ-13.1 TwoOperators +│ ├── MN-REQ-13.Recall Recall(q) = pin_map +│ │ └── agent-facing read leaves via #derivation +│ │ (04.* minus housekeep, 02.4, 05.2, 08.7/08.9, +│ │ 10.2/10.3/10.6, find/export options, 12.4 pin_map) +│ └── MN-REQ-13.Commit Commit(Δ) = mutate +│ ├── MN-REQ-13.2 SameThingAbsorb nested +│ └── agent-facing write leaves via #derivation +│ (03.* strict mutate unchanged, ingest/snap options, +│ Absorb / RSV options) +├── MN-REQ-01 SessionLifecycle open / save / close +├── MN-REQ-02, 05, 07–12 organisational groups (importers stay) +└── MN-REQ-06 ProcessBoundary ops (later PR may split) +``` + +Agent surface verbs: **recall**, **commit**, **session open**, **save**, **close**. + +## Counts + +- Requirement defs / usages: **132** (130 at ab0d2db plus spine arms `MN-REQ-13.Recall` and `MN-REQ-13.Commit`). +- Under Recall (`Recall` + `option:Recall`): **26** +- Under Commit (`Commit` + `option:Commit`): **30** +- Session surface (`session` + `option:session`): **10** +- Internal: **44** +- Ops: **17** +- Other (mission / spine / surface listing): **5** +- Named by some `verify`: **52**; still unverified: **80** (the previous 78 plus the two new arms). +- `agentSurface` breakdown: mission 1, session 7, option:session 3, Recall 19, option:Recall 7, Commit 8, option:Commit 22, internal 44, ops 17, surface 2, spine 2. + +## Mapping (old organisational parent → new spine parent) + +| Id | Old parent | New parent | agentSurface | Note | +|----|------------|------------|--------------|------| +| `MN-REQ-00` | `—` | `—` | `mission` | mission root; not an operator | +| `MN-REQ-01` | `MN-REQ-00` | `MN-REQ-01 (session surface; not Recall/Commit)` | `session` | session open / save / close (agent surface beside Recall and Commit) | +| `MN-REQ-01.1` | `MN-REQ-01` | `MN-REQ-01 (session surface; not Recall/Commit)` | `session` | named sessions; session id is the mission handle | +| `MN-REQ-01.2` | `MN-REQ-01` | `MN-REQ-01 (session surface; not Recall/Commit)` | `session` | open, resume, and close | +| `MN-REQ-01.3` | `MN-REQ-01` | `internal (stays under organisational group)` | `internal` | TTL and concurrent-session caps; not an agent verb | +| `MN-REQ-01.4` | `MN-REQ-01` | `MN-REQ-01 (session surface; not Recall/Commit)` | `session` | session_save | +| `MN-REQ-01.5` | `MN-REQ-01` | `MN-REQ-01 (session surface; not Recall/Commit)` | `option:session` | session_load — option of save | +| `MN-REQ-01.6` | `MN-REQ-01` | `MN-REQ-01 (session surface; not Recall/Commit)` | `option:session` | keep_id on load — option of save | +| `MN-REQ-01.7` | `MN-REQ-01` | `MN-REQ-01 (session surface; not Recall/Commit)` | `session` | session id is the handoff capability and secret; keep unchanged | +| `MN-REQ-01.8` | `MN-REQ-01` | `MN-REQ-01 (session surface; not Recall/Commit)` | `session` | no graph dump on handoff; keep unchanged | +| `MN-REQ-01.9` | `MN-REQ-01` | `internal (stays under organisational group)` | `internal` | snapshot lossless round-trip; save integrity | +| `MN-REQ-01.10` | `MN-REQ-01` | `internal (stays under organisational group)` | `internal` | ACL who-check on save / load / close | +| `MN-REQ-02` | `MN-REQ-00` | `internal (stays under organisational group)` | `internal` | graph identity / grammar; organisational | +| `MN-REQ-02.1` | `MN-REQ-02` | `internal (stays under organisational group)` | `internal` | exactly node and edge | +| `MN-REQ-02.2` | `MN-REQ-02` | `internal (stays under organisational group)` | `internal` | nodes hold atomised facts | +| `MN-REQ-02.3` | `MN-REQ-02` | `internal (stays under organisational group)` | `internal` | edges are first class | +| `MN-REQ-02.4` | `MN-REQ-02` | `MN-REQ-13.Recall` | `Recall` | query over the node/edge graph is Recall | +| `MN-REQ-02.5` | `MN-REQ-02` | `internal (stays under organisational group)` | `internal` | grammar expresses node and edge | +| `MN-REQ-02.6` | `MN-REQ-02` | `internal (stays under organisational group)` | `internal` | parser recovers node and edge; parser is SSOT | +| `MN-REQ-02.7` | `MN-REQ-02` | `MN-REQ-13.Commit` | `option:Commit` | schema-validated ingest — option of Commit | +| `MN-REQ-02.8` | `MN-REQ-02` | `internal (stays under organisational group)` | `internal` | id not required on the wire | +| `MN-REQ-02.9` | `MN-REQ-02` | `internal (stays under organisational group)` | `internal` | MUST NOT copy cousin store-key / embedding Recall | +| `MN-REQ-03` | `MN-REQ-00` | `MN-REQ-13.Commit` | `Commit` | strict mutate group; spine parent Commit | +| `MN-REQ-03.1` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | AddFailsIfExists; unchanged; merge-and-report pending | +| `MN-REQ-03.2` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | UpdateFailsIfAbsent; unchanged | +| `MN-REQ-03.3` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | no silent upsert; unchanged; merge-and-report is owner-pending | +| `MN-REQ-03.4` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | honour WHERE or refuse | +| `MN-REQ-04` | `MN-REQ-00` | `MN-REQ-13.Recall` | `Recall` | slice economy / pin_map group; spine parent Recall | +| `MN-REQ-04.1` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | bounded live pin_map | +| `MN-REQ-04.2` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | hide recycled rows from pin_map | +| `MN-REQ-04.3` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | hard depth/row caps; truncation flagged on the wire | +| `MN-REQ-04.4` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | prepend engine-law rows on pin_map | +| `MN-REQ-04.5` | `MN-REQ-04` | `MN-REQ-13.Recall` | `option:Recall` | query_walk / anchored walk — leftover option of Recall | +| `MN-REQ-04.6` | `MN-REQ-04` | `MN-REQ-13.Recall` | `option:Recall` | find — bounded MATCH seed; option of Recall | +| `MN-REQ-04.7` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | RelativeSeed then ShapeWalk | +| `MN-REQ-04.8` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | CueConflict when |Q|>1; no absorb in Recall | +| `MN-REQ-04.9` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | empty cue returns the session outline; keep unchanged | +| `MN-REQ-04.10` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | Peak_L last-resort cue | +| `MN-REQ-04.11` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | pin_map order from Shape observables | +| `MN-REQ-04.12` | `MN-REQ-04` | `internal (stays under organisational group)` | `internal` | housekeep endpoint identity; not an agent verb | +| `MN-REQ-05` | `MN-REQ-00` | `internal (stays under organisational group)` | `internal` | hard caps group; leaves split across Recall / Commit / internal | +| `MN-REQ-05.1` | `MN-REQ-05` | `internal (stays under organisational group)` | `internal` | store resource caps | +| `MN-REQ-05.2` | `MN-REQ-05` | `MN-REQ-13.Recall` | `Recall` | query fan-out caps; truncation flagged on the wire | +| `MN-REQ-05.3` | `MN-REQ-05` | `MN-REQ-13.Commit` | `Commit` | one value-byte cap on mutate and snapshot load | +| `MN-REQ-06` | `MN-REQ-00` | `MN-REQ-06 (ops; later PR may split)` | `ops` | process / transport / fleet; later PR may split deploy/ops | +| `MN-REQ-06.1` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | in-process primary binding | +| `MN-REQ-06.2` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | local IPC share | +| `MN-REQ-06.3` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | TCP serve migration | +| `MN-REQ-06.4` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | durable store behind MemNet | +| `MN-REQ-06.5` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | human usage look; not agent wire | +| `MN-REQ-06.6` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | device services / one droplet MemNet MCP | +| `MN-REQ-06.7` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | SSOT → code allocate | +| `MN-REQ-06.8` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | tip MemNet access portal | +| `MN-REQ-06.9` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | LAN MCP front; inventOnly | +| `MN-REQ-06.10` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | SliceHandCarry; inventOnly | +| `MN-REQ-06.11` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | admin serve usage report | +| `MN-REQ-06.12` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | product gateway | +| `MN-REQ-06.13` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | serve request isolation | +| `MN-REQ-06.14` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | safe serve upgrade | +| `MN-REQ-06.15` | `MN-REQ-06` | `MN-REQ-06 (ops; later PR may split)` | `ops` | admin checkpoint | +| `MN-REQ-07` | `MN-REQ-00` | `MN-REQ-13 (names the five verbs)` | `surface` | MCP/CLI boundary group; names the agent surface | +| `MN-REQ-07.1` | `MN-REQ-07` | `MN-REQ-13 (names the five verbs)` | `surface` | agent surface is exactly recall, commit, session open, save, and close | +| `MN-REQ-07.2` | `MN-REQ-07` | `internal (stays under organisational group)` | `internal` | structured tool envelope | +| `MN-REQ-07.3` | `MN-REQ-07` | `internal (stays under organisational group)` | `internal` | no domain-product tools on generic MCP | +| `MN-REQ-07.4` | `MN-REQ-07` | `MN-REQ-01 (session surface; not Recall/Commit)` | `option:session` | law-seed on session_open — option of open | +| `MN-REQ-07.5` | `MN-REQ-07` | `internal (stays under organisational group)` | `internal` | no store-key tool surface | +| `MN-REQ-08` | `MN-REQ-00` | `internal (stays under organisational group)` | `internal` | agent I/O grammar; applies to both operators | +| `MN-REQ-08.1` | `MN-REQ-08` | `internal (stays under organisational group)` | `internal` | LLM-friendly stdio | +| `MN-REQ-08.2` | `MN-REQ-08` | `internal (stays under organisational group)` | `internal` | LLM-friendly MCP | +| `MN-REQ-08.3` | `MN-REQ-08` | `internal (stays under organisational group)` | `internal` | no hostile positional dump | +| `MN-REQ-08.4` | `MN-REQ-08` | `internal (stays under organisational group)` | `internal` | boundary-friendly if internal differs | +| `MN-REQ-08.5` | `MN-REQ-08` | `internal (stays under organisational group)` | `internal` | I/O actor is the LLM | +| `MN-REQ-08.6` | `MN-REQ-08` | `internal (stays under organisational group)` | `internal` | follow prompt rules | +| `MN-REQ-08.7` | `MN-REQ-08` | `MN-REQ-13.Recall` | `Recall` | Write = display = shaped pin_map emit | +| `MN-REQ-08.8` | `MN-REQ-08` | `internal (stays under organisational group)` | `internal` | template-copyable shapes | +| `MN-REQ-08.9` | `MN-REQ-08` | `MN-REQ-13.Recall` | `Recall` | pin_map emit same family as mutate; Commit shares the grammar | +| `MN-REQ-09` | `MN-REQ-00` | `internal (stays under organisational group)` | `internal` | human inspect; parser is SSOT | +| `MN-REQ-09.1` | `MN-REQ-09` | `internal (stays under organisational group)` | `internal` | canonical parser SSOT; keep unchanged | +| `MN-REQ-09.2` | `MN-REQ-09` | `internal (stays under organisational group)` | `internal` | reject invalid wire | +| `MN-REQ-09.3` | `MN-REQ-09` | `internal (stays under organisational group)` | `internal` | parse-faithful inspect | +| `MN-REQ-09.4` | `MN-REQ-09` | `internal (stays under organisational group)` | `internal` | no ad-hoc consumer splits | +| `MN-REQ-09.5` | `MN-REQ-09` | `internal (stays under organisational group)` | `internal` | GraphGlot parse then product gate | +| `MN-REQ-10` | `MN-REQ-00` | `internal (stays under organisational group)` | `internal` | LLM assumed properties and limits | +| `MN-REQ-10.1` | `MN-REQ-10` | `internal (stays under organisational group)` | `internal` | chat is not durable store | +| `MN-REQ-10.2` | `MN-REQ-10` | `MN-REQ-13.Recall` | `Recall` | pin_map must fit context | +| `MN-REQ-10.3` | `MN-REQ-10` | `MN-REQ-13.Recall` | `Recall` | minimise token round-trips | +| `MN-REQ-10.4` | `MN-REQ-10` | `internal (stays under organisational group)` | `internal` | external ground truth required | +| `MN-REQ-10.5` | `MN-REQ-10` | `internal (stays under organisational group)` | `internal` | grammar must be LLM-learnable | +| `MN-REQ-10.6` | `MN-REQ-10` | `MN-REQ-13.Recall` | `Recall` | prefer template-friendly pins | +| `MN-REQ-10.7` | `MN-REQ-10` | `internal (stays under organisational group)` | `internal` | human verify via parser | +| `MN-REQ-11` | `MN-REQ-00` | `MN-REQ-13.Commit` | `option:Commit` | snapshot / ingest interop; importers stay here this cut | +| `MN-REQ-11.1` | `MN-REQ-11` | `MN-REQ-13.Recall` | `option:Recall` | export_pin_map is a GQL write-out of a cue pin_map (option of Recall); SysML v2 reverse is not claimed | +| `MN-REQ-11.2` | `MN-REQ-11` | `MN-REQ-13.Recall` | `option:Recall` | SysML node mapping of export; live code is GQL pin_map write-out, not .sysml reverse | +| `MN-REQ-11.3` | `MN-REQ-11` | `MN-REQ-13.Recall` | `option:Recall` | SysML edge mapping of export; live code is GQL pin_map write-out, not .sysml reverse | +| `MN-REQ-11.4` | `MN-REQ-11` | `internal (stays under organisational group)` | `internal` | pin-map export/ingest distinct from session snapshot | +| `MN-REQ-11.5` | `MN-REQ-11` | `MN-REQ-13.Recall` | `option:Recall` | SysML round-trip SHOULD; not claimed from ingest or export_pin_map | +| `MN-REQ-11.6` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | ingest_codebase — option of Commit | +| `MN-REQ-11.7` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | codebase nodes; option of Commit | +| `MN-REQ-11.8` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | codebase edges; option of Commit | +| `MN-REQ-11.9` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | ingest_pcba / .ato — option of Commit | +| `MN-REQ-11.10` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | ingest_skills — option of Commit | +| `MN-REQ-11.11` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | skills/rules nodes; option of Commit | +| `MN-REQ-11.12` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | skills/rules edges; option of Commit | +| `MN-REQ-11.13` | `MN-REQ-11` | `MN-REQ-13.Recall` | `option:Recall` | map of pins; emit economy shared with Recall | +| `MN-REQ-11.14` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | PCBA nodes; option of Commit | +| `MN-REQ-11.15` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | PCBA edges; option of Commit | +| `MN-REQ-11.16` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | Path-B ingest locators; option of Commit | +| `MN-REQ-11.17` | `MN-REQ-11` | `MN-REQ-13.Commit` | `option:Commit` | snap_model — option of Commit (locator mint, not a third operator) | +| `MN-REQ-11.17.1` | `MN-REQ-11.17` | `MN-REQ-13.Commit` | `option:Commit` | cross-cut satisfy locators on catalog Snap | +| `MN-REQ-11.17.2` | `MN-REQ-11.17` | `MN-REQ-13.Commit` | `option:Commit` | Snap session grain | +| `MN-REQ-11.17.3` | `MN-REQ-11.17` | `MN-REQ-13.Commit` | `option:Commit` | Snap budget pre-check | +| `MN-REQ-11.17.4` | `MN-REQ-11.17` | `MN-REQ-13.Commit` | `option:Commit` | Snap caller-gone rollback | +| `MN-REQ-11.17.5` | `MN-REQ-11.17` | `MN-REQ-13.Commit` | `option:Commit` | Snap replace by model identity | +| `MN-REQ-12` | `MN-REQ-00` | `internal (stays under organisational group)` | `internal` | Multitask protocol; workers use Recall then Commit; not one operator | +| `MN-REQ-12.1` | `MN-REQ-12` | `MN-REQ-01 (session surface; not Recall/Commit)` | `session` | shared session id is mission SSOT handle | +| `MN-REQ-12.2` | `MN-REQ-12` | `MN-REQ-06 (ops; later PR may split)` | `ops` | shared store transport (TCP / streamable-http) | +| `MN-REQ-12.3` | `MN-REQ-12` | `internal (stays under organisational group)` | `internal` | parent owns task lifecycle | +| `MN-REQ-12.4` | `MN-REQ-12` | `MN-REQ-13.Recall` | `Recall` | worker pin_map first; scoped write is Commit under the same mission | +| `MN-REQ-12.5` | `MN-REQ-12` | `internal (stays under organisational group)` | `internal` | prefer one worker or disjoint scopes | +| `MN-REQ-12.6` | `MN-REQ-12` | `internal (stays under organisational group)` | `internal` | parent ends turn; no poll | +| `MN-REQ-12.7` | `MN-REQ-12` | `internal (stays under organisational group)` | `internal` | MUST NOT assume full ACL / reserve / ingest | +| `MN-REQ-12.8` | `MN-REQ-12` | `internal (stays under organisational group)` | `internal` | applies to relevant tasks | +| `MN-REQ-12.9` | `MN-REQ-12` | `MN-REQ-13.Commit` | `option:Commit` | lead-owned import_slice / Absorb — option of Commit | +| `MN-REQ-12.10` | `MN-REQ-12` | `MN-REQ-13.Commit` | `option:Commit` | no chat or whole-store import | +| `MN-REQ-12.11` | `MN-REQ-12` | `MN-REQ-13.Commit` | `option:Commit` | optional ImportGuard on Absorb | +| `MN-REQ-12.12` | `MN-REQ-12` | `internal (stays under organisational group)` | `internal` | host-driven async parallel | +| `MN-REQ-12.13` | `MN-REQ-12` | `MN-REQ-13.Commit` | `option:Commit` | RSV lease — option of Commit, not a third operator | +| `MN-REQ-13` | `MN-REQ-00` | `MN-REQ-00 (spine)` | `spine` | spine group: Recall(q) and Commit(Δ) | +| `MN-REQ-13.1` | `MN-REQ-13` | `MN-REQ-13` | `spine` | exactly two product operators; RSV is a Commit lease | +| `MN-REQ-13.2` | `MN-REQ-13` | `MN-REQ-13.Commit` | `Commit` | SameThingAbsorb is a Commit rule, not Recall | +| `MN-REQ-13.Recall` | `MN-REQ-13` | `MN-REQ-13` | `Recall` | spine arm Recall(q) | +| `MN-REQ-13.Commit` | `MN-REQ-13` | `MN-REQ-13` | `Commit` | spine arm Commit(Δ) | + +## Did not fit cleanly under Recall or Commit + +- **MN-REQ-01** session open / save / close — agent surface beside the two operators, not inside them. +- **MN-REQ-05** hard caps — 05.1 internal store, 05.2 Recall, 05.3 Commit (and snapshot). +- **MN-REQ-07.1** names all five verbs; not a sub-rule of one operator. +- **MN-REQ-08** I/O grammar applies to both; 08.7 / 08.9 hang on Recall (emit) with Commit sharing the family. +- **MN-REQ-11.1–11.5** SysML v2 export / round-trip text vs live `export_pin_map` GQL write-out (option of Recall); `.sysml` reverse still unclaimed. +- **MN-REQ-11** Path-B importers stay nested here; marked option of Commit (move importers = later PR). +- **MN-REQ-12** Multitask is host protocol: workers Recall then Commit; 12.4 is dual; 12.2 is ops transport. +- **MN-REQ-04.12** housekeep sits in the slice-economy group but is internal, not Recall. +- **MN-REQ-13.1** is the two-operator law itself (spine, not a leaf of one arm). + diff --git a/sysml-models/outputs/product-nest-one-page.md b/sysml-models/outputs/product-nest-one-page.md index cfddf7ac..16dcff19 100644 --- a/sysml-models/outputs/product-nest-one-page.md +++ b/sysml-models/outputs/product-nest-one-page.md @@ -2,6 +2,8 @@ Mission working memory: hand off by **session id**; GQL ask/commit; no graph dump in chat. +Requirements spine is the same two operators: MN-REQ-13 Recall(q) / Commit(Δ). Mapping: [mn-req-13-spine-mapping.md](mn-req-13-spine-mapping.md). Agent surface verbs: recall, commit, session open, save, close. + ```text MemNetSystem // SharedLlmMemory product ├── Core diff --git a/tests/test_sysml_recall_commit_spine.py b/tests/test_sysml_recall_commit_spine.py new file mode 100644 index 00000000..bf4b09f0 --- /dev/null +++ b/tests/test_sysml_recall_commit_spine.py @@ -0,0 +1,141 @@ +"""Honesty: MN-REQ-13 Recall/Commit is the requirements spine.""" + +from __future__ import annotations + +import re +from collections import Counter +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +MODELS = ROOT / "sysml-models" / "models" +REQ = MODELS / "requirements.sysml" +DEPLOY = MODELS / "deploy.sysml" +COUSINS = MODELS / "cousins.sysml" +VERIFY = MODELS / "verify.sysml" +MAPPING = ROOT / "sysml-models" / "outputs" / "mn-req-13-spine-mapping.md" + +CLOSED_SURFACES = { + "Recall", + "Commit", + "option:Recall", + "option:Commit", + "session", + "option:session", + "internal", + "ops", + "surface", + "mission", + "spine", +} + + +def _req() -> str: + return REQ.read_text(encoding="utf-8") + + +def _pairs(text: str) -> list[tuple[str, str]]: + ids = re.findall(r'attribute requirementId : String = "(MN-REQ-[^"]+)"', text) + surfaces = re.findall(r'attribute agentSurface : String = "([^"]+)"', text) + assert len(ids) == len(surfaces), f"id/surface mismatch {len(ids)} vs {len(surfaces)}" + return list(zip(ids, surfaces, strict=True)) + + +def test_every_requirement_has_closed_agent_surface(): + pairs = _pairs(_req()) + assert len(pairs) == 132 + unknown = sorted({s for _, s in pairs if s not in CLOSED_SURFACES}) + assert unknown == [] + by_id = dict(pairs) + assert by_id["MN-REQ-13"] == "spine" + assert by_id["MN-REQ-13.Recall"] == "Recall" + assert by_id["MN-REQ-13.Commit"] == "Commit" + assert by_id["MN-REQ-13.2"] == "Commit" + assert by_id["MN-REQ-03.1"] == "Commit" + assert by_id["MN-REQ-03.2"] == "Commit" + assert by_id["MN-REQ-03.3"] == "Commit" + assert by_id["MN-REQ-04.9"] == "Recall" + assert by_id["MN-REQ-09.1"] == "internal" + assert by_id["MN-REQ-01.7"] == "session" + assert by_id["MN-REQ-01.8"] == "session" + assert by_id["MN-REQ-06.5"] == "ops" + + +def test_spine_nest_and_usages(): + text = _req() + assert "requirement def MN_REQ_13_Recall" in text + assert "requirement def MN_REQ_13_Commit" in text + commit_block = text.split("requirement def MN_REQ_13_Commit {", 1)[1] + commit_block = commit_block.split("requirement recallCommitReq", 1)[0] + assert "requirement def MN_REQ_13_2_SameThingAbsorb" in commit_block + assert ( + "MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_Commit" + "::MN_REQ_13_2_SameThingAbsorb" in text + ) + assert "requirement recallSpineReq" in text + assert "requirement commitSpineReq" in text + assert "deriveRecallSpineLeaves" in text + assert "deriveCommitSpineLeaves" in text + assert "end #original ::> recallSpineReq" in text + assert "end #original ::> commitSpineReq" in text + assert "end #derive ::> addFailsIfExistsReq" in text + assert "end #derive ::> sessionOutlineEmptyCueReq" in text + + +def test_agent_surface_five_verbs_and_kept_laws(): + text = _req() + assert "verbs are exactly five: recall (pin_map), commit (mutate)" in text + assert "session_open, session_save, and session_close" in text + assert "Silent upsert SHALL NOT be offered" in text + assert "Merge-and-report is an" in text + assert "owner decision and is pending" in text + assert "truncated=true" in text + assert "Empty q SHALL be" in text or "empty q SHALL return" in text + assert "canonical parser" in text.lower() or "Canonical parser" in text + seven = text.split("requirement def MN_REQ_07_1_GenericToolSurface", 1)[1] + seven = seven.split("requirement def MN_REQ_07_2_StructuredToolEnvelope", 1)[0] + assert "Options of Recall: find" in seven + assert "Options of Commit: leftover add / update" in seven + assert "Internal: housekeep_stats" in seven + + +def test_satisfy_paths_follow_commit_nest(): + path = ( + "MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit" + "::MN_REQ_13_Commit::MN_REQ_13_2_SameThingAbsorb" + ) + assert path in DEPLOY.read_text(encoding="utf-8") + assert path in COUSINS.read_text(encoding="utf-8") + old = "MN_REQ_13_RecallCommit::MN_REQ_13_2_SameThingAbsorb;" + assert old not in DEPLOY.read_text(encoding="utf-8") + assert old not in COUSINS.read_text(encoding="utf-8") + + +def test_mapping_table_and_verify_coverage(): + md = MAPPING.read_text(encoding="utf-8") + assert "MN-REQ-13.Recall" in md + assert "MN-REQ-13.Commit" in md + assert "old organisational parent" in md.lower() or "Old parent" in md + assert "mn_" not in md + text = _req() + pairs = _pairs(text) + counts = Counter(s for _, s in pairs) + assert counts["Recall"] + counts["option:Recall"] == 26 + assert counts["Commit"] + counts["option:Commit"] == 30 + assert counts["internal"] == 44 + assert counts["ops"] == 17 + usages = re.findall(r"^ requirement (missionReq|\w+Req)\b", text, re.M) + assert "missionReq" in usages + assert "recallSpineReq" in usages + assert "commitSpineReq" in usages + assert len(usages) == 132 + verified = set(re.findall(r"verify (\w+);", VERIFY.read_text(encoding="utf-8"))) + unverified = [u for u in usages if u not in verified] + assert len(verified & set(usages)) == 52 + assert len(unverified) == 80 + assert "recallSpineReq" in unverified + assert "commitSpineReq" in unverified + + +def test_brace_balance_requirements(): + text = _req() + assert text.count("{") == text.count("}") From 96158ddd5e795b2b19b6202048971ac12411500a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 13 Oct 2026 09:07:10 +0000 Subject: [PATCH 2/2] Approve Commit merge-and-report and add playbooks as a Recall option. Default Commit merges by GraphElement identity and reports created, updated, or unchanged per item (MN-REQ-03.3 never silent). Strict AddFailsIfExists / UpdateFailsIfAbsent is an opt-in Commit mode. Playbooks (MN-REQ-04.13) are a Recall option modelled on Endleaf list/get, on paper, with planned deciding tests named in verify. Co-authored-by: chouswei --- sysml-models/README.md | 10 +- sysml-models/models/deploy.sysml | 115 +++++++++++++++- sysml-models/models/requirements.sysml | 89 +++++++++---- sysml-models/models/verify.sysml | 124 ++++++++++++++++++ .../outputs/mn-req-13-spine-mapping.md | 38 ++++-- sysml-models/outputs/product-nest-one-page.md | 4 +- tests/test_sysml_recall_commit_spine.py | 61 ++++++++- 7 files changed, 387 insertions(+), 54 deletions(-) diff --git a/sysml-models/README.md b/sysml-models/README.md index e17ab9b8..deba8fd1 100644 --- a/sysml-models/README.md +++ b/sysml-models/README.md @@ -21,12 +21,12 @@ Design authority: rebuilt requirements + ADR-001 (GQL agent wire) + `docs/gramma | File | Package | Role | |------|---------|------| | `models/connections.sysml` | `MemNetConnections` | SharedLlmMemory, SessionHandoff (+ CallerId / SessionBind / SessionCapability), WorkingMemorySlice, SessionImportRequest, optional ImportGuardDecision; ServeUsageLook / ImportGuardArmedLook / HumanUsagePage (ops look); application `CompanyAnalyticalSsot` / `HostSearchBridge` / `DeviceMemNetFleet` / `TipMemNetAccess` / `MemNetLanMcpCluster` / `MemNetTwoMovesContrast` | -| `models/requirements.sysml` | `MemNetRequirements` | MN-REQ-00…13. **Spine = MN-REQ-13** Recall(q)/Commit(Δ) (`13.Recall` / `13.Commit`; mapping [outputs/mn-req-13-spine-mapping.md](outputs/mn-req-13-spine-mapping.md)). Agent surface: recall, commit, session open, save, close. 01.7/01.8, 06.4, **06.5** human usage look, **06.6** device services / one droplet MemNet MCP tip/ops, product face sysmledge, **06.7** SSOT → code allocate, **06.8** tip MemNet access portal, **06.9** ClusterRoute LAN MCP front invent #191, **06.10** SliceHandCarry invent #47 cousin, **06.12** product gateway on memnet-mcp, 12.9–12.13, 13.1 two operators; 02.9 cousin store-key; 04.8 cue \|Q\|>1; 04.9 empty-q outline | +| `models/requirements.sysml` | `MemNetRequirements` | MN-REQ-00…13. **Spine = MN-REQ-13** Recall(q)/Commit(Δ) (`13.Recall` / `13.Commit`; mapping [outputs/mn-req-13-spine-mapping.md](outputs/mn-req-13-spine-mapping.md)). Agent surface: recall, commit, session open, save, close. 01.7/01.8, 06.4, **06.5** human usage look, **06.6** device services / one droplet MemNet MCP tip/ops, product face sysmledge, **06.7** SSOT → code allocate, **06.8** tip MemNet access portal, **06.9** ClusterRoute LAN MCP front invent #191, **06.10** SliceHandCarry invent #47 cousin, **06.12** product gateway on memnet-mcp, 12.9–12.13, 13.1 two operators; 02.9 cousin store-key; 03.3 merge-and-report (never silent); 04.8 cue \|Q\|>1; 04.9 empty-q outline; **04.13** playbooks Recall option | | `models/cousins.sysml` | `MemNetCousinContrast` | TARGET vs eight cousin pointing/identity designs (not a product switch; SysMLEdge is a distinct pin_map; overlay family `SysMLEdgePrj-*`; this engine repo and `modelbasedPrj-*` are repo-based — MUST NOT use SysMLEdge as model SSOT; `SysMLEdgePrj-*` bound desk is working model SSOT) | | `models/deploy.sysml` | `MemNet` | Nested parts; `RecallCommit` two-operator cut; Multitask spine; `MemNetUsageDashboard` / `MemNetOpsFleet` / `TipMemNetAccessPortal` / `MemNetLanMcpFront` / `MemNetProductGateway` / `MemNetTwoMoves` outside `MemNetSystem` | | `models/implementation.sysml` | `MemNetImplementation` | `SoftwareAllocate` logical → live modules; one Hatch wheel many hosts; sysmledge not in wheel | | `models/behaviour.sysml` | `MemNetBehaviour` | HandoffById, SessionImportReceive, Multitask async, landed-client hydrate/flush | -| `models/verify.sysml` | `MemNetVerification` | MN-VER-12-G00 + S01…S14; MN-VER-04-S01…S05; MN-VER-09-S01; MN-VER-13-S01; MN-VER-06-S01…S10 (S06 storage roles; S07 ClusterRoute LAN MCP front; S08 two named moves; S09 admin usage; S10 product gateway); MN-VER-01-S03 | +| `models/verify.sysml` | `MemNetVerification` | MN-VER-12-G00 + S01…S14; MN-VER-04-S01…S07 (S07 playbooks Recall option); MN-VER-03-S01 / S02 (S02 merge-and-report); MN-VER-09-S01; MN-VER-13-S01; MN-VER-06-S01…S10 (S06 storage roles; S07 ClusterRoute LAN MCP front; S08 two named moves; S09 admin usage; S10 product gateway); MN-VER-01-S03 | | `models/root.sysml` | `ProjectMemNet` | Root imports (load last). MUST NOT import `MemNetArchive` | | `models/archive.sysml` | `MemNetArchive` | ARCHIVE shelf (leftover_* / TierACodec / LegacyPipe* / retired Neo4j). **Off** `config.yaml` load | @@ -39,8 +39,8 @@ Agent-facing graph I/O hangs on **Recall(q)** and **Commit(Δ)**. Organisational ```text MN-REQ-00 ├── MN-REQ-13 RecallCommit // spine -│ ├── 13.Recall pin_map // empty q = outline -│ └── 13.Commit mutate // 13.2 SameThingAbsorb nested +│ ├── 13.Recall pin_map // empty q = outline; playbooks option +│ └── 13.Commit mutate // merge-and-report; 13.2 nested ├── MN-REQ-01 Session // open / save / close └── MN-REQ-06 ops // later PR may split ``` @@ -76,7 +76,7 @@ IMPLEMENTATION MemNetImplementation — SoftwareAllocate SSOT → live modul ## Target subsystems -- **AgentMemory (SharedLlmMemory):** GraphStore, GqlCodec, **RecallCommit** (Recall: SessionOutline empty-q census + AgentShapedRead / PinMapShapedRead + BoundedMatchFind; Commit: MutateGate + RSV lease), SessionLifecycle +- **AgentMemory (SharedLlmMemory):** GraphStore, GqlCodec, **RecallCommit** (Recall: SessionOutline empty-q census + AgentShapedRead / PinMapShapedRead + BoundedMatchFind + PlaybookGuide on paper; Commit: MutateGate merge-and-report + RSV lease), SessionLifecycle - **MCP / CLI:** LLM ↔ MemNet only (not DurableBuffer as primary) - **DurableBuffer:** AgensGraphAdapter **client**; 0.7 claim is adapter + operator round trip (no runtime caller); Neo4j retired to archive (#187); cabinet external / not vendored - **Multitask:** nested lead handoff + AsyncTaskDispatch + WorkerPool + import spine; MN-REQ-12 diff --git a/sysml-models/models/deploy.sysml b/sysml-models/models/deploy.sysml index cea3bee7..df6f370b 100644 --- a/sysml-models/models/deploy.sysml +++ b/sysml-models/models/deploy.sysml @@ -19,11 +19,12 @@ │ │ │ ├── RelativeSeed // MATCH_L → Q walk roots; Peak_L last-resort cue │ │ │ │ └── Peak_L // ρ* residual local max; never default goldfish │ │ │ ├── SessionOutline // empty q = census of S (0.11 TARGET) + │ │ │ ├── PlaybookGuide // Recall option; list/get on paper │ │ │ └── AgentShapedRead │ │ │ ├── RelativeSeed + ShapeWalk // seedToWalk: Q → k-hop │ │ │ ├── PinMapShapedRead // ShapeWalk emit; CueConflict when |Q|>1 │ │ │ └── BoundedMatchFind // RelativeSeed MATCH_L; CueConflict on emit - │ │ └── Commit // ONE gate; TARGET GraphElement identity + │ │ └── Commit // ONE gate; merge-and-report default │ │ ├── MutateGate │ │ ├── IdAllocator // leftover mint honesty flags; leftover_* parts off this nest │ │ ├── NeighbourhoodReserve // RSV lease — not a third API @@ -355,7 +356,9 @@ package MemNet { part def GraphStore { doc /* In-memory Net of Memory: property-graph working set with Node and - Edge indexes, adjacency, properties, labels; strict create/replace. + Edge indexes, adjacency, properties, labels; default Commit is + merge-and-report (created / updated / unchanged); strict + add/update is an opt-in Commit mode. Dual EDGE: bind vs relation as relationship types / endpoint grain. Law-on-node as node properties (+ port structure). Agent wire is GQL via GqlCodec. Realises session-scoped SharedLlmMemory content. @@ -395,6 +398,13 @@ package MemNet { attribute nicknameIdIsRankingKey : Boolean = false; attribute createOrderIsRankingKey : Boolean = false; attribute storageRole : String = "working_memory"; + attribute defaultCommitMergeAndReport : Boolean = true; + attribute reportCreatedUpdatedUnchanged : Boolean = true; + attribute silentUpsert : Boolean = false; + attribute defaultAddFailsIfExists : Boolean = false; + attribute defaultUpdateFailsIfAbsent : Boolean = false; + attribute optInStrictAddFailsIfExists : Boolean = true; + attribute optInStrictUpdateFailsIfAbsent : Boolean = true; satisfy MN_REQ_00_MissionBridge::MN_REQ_02_MemoryNetGraph::MN_REQ_02_1_ExactlyNodeAndEdge; satisfy MN_REQ_00_MissionBridge::MN_REQ_02_MemoryNetGraph::MN_REQ_02_2_NodesHoldAtomisedFacts; @@ -412,7 +422,11 @@ package MemNet { part def MutateGate { doc /* Orchestrates GQL parse -> schema validate -> leftover_NEW_mint -> - strict add/update into GraphStore -> ack. + merge-and-report into GraphStore -> ack. Default Commit merges + by GraphElement identity and reports created / updated / + unchanged per item (never silent). Strict AddFailsIfExists / + UpdateFailsIfAbsent is an opt-in Commit mode (leftover add / + update façades MAY select it). TARGET create: CREATE () is legal; CREATE (:Label {props}) with no required id; properties MAY be empty. MERGE is a per-write lookup of labels + properties, not a primary key. Ends MATCH by label + @@ -441,6 +455,14 @@ package MemNet { attribute whereTrueEdgeDeleteKept : Boolean = true; attribute matchMapEqualityKept : Boolean = true; attribute gqlValueBytesHardCap : Boolean = true; + attribute defaultMode : String = "merge_and_report"; + attribute mergeByIdentity : Boolean = true; + attribute reportCreatedUpdatedUnchanged : Boolean = true; + attribute silentUpsert : Boolean = false; + attribute defaultAddFailsIfExists : Boolean = false; + attribute defaultUpdateFailsIfAbsent : Boolean = false; + attribute optInStrictAddFailsIfExists : Boolean = true; + attribute optInStrictUpdateFailsIfAbsent : Boolean = true; satisfy MN_REQ_00_MissionBridge::MN_REQ_03_StrictMutate::MN_REQ_03_1_AddFailsIfExists; satisfy MN_REQ_00_MissionBridge::MN_REQ_03_StrictMutate::MN_REQ_03_2_UpdateFailsIfAbsent; @@ -791,6 +813,7 @@ package MemNet { part outline : SessionOutline; part pinMap : PinMapShapedRead; part find : BoundedMatchFind; + part playbooks : PlaybookGuide; connection seedToWalk : SeedSetFlow { end port source ::> seed.seedOut; end port sink ::> walk.seedIn; @@ -911,6 +934,8 @@ package MemNet { SHALL NOT absorb / MERGE-by-name / LLM-collapse Q. Same name is a candidate only; agent may later SameThingAbsorb under Commit after CueConflict is visible on the emit. + PlaybookGuide is a Recall option (listPlaybooks / getPlaybook); + not a third operator. */ port cueIn : CueInPort; port shapedOut : ShapedSubgraphOutPort; @@ -933,6 +958,7 @@ package MemNet { attribute codebookMissOnly : Boolean = true; part seed : RelativeSeed; part outline : SessionOutline; + part playbooks : PlaybookGuide; attribute leftoverEmptySkipProductCommand : Boolean = false; attribute leftoverEmptySkipTargetImplemented : Boolean = false; part shapedRead : AgentShapedRead; @@ -949,6 +975,32 @@ package MemNet { satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_8_CueMultiRootHonest; satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_9_SessionOutlineEmptyCue; satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_10_PeakLLastResort; + satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_13_PlaybooksAsRecallOption; + satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_1_TwoOperators; + } + + part def PlaybookGuide { + doc /* + Recall option (MN-REQ-04.13): read-only guides that teach an + agent how to use MemNet's tools. Modelled on Endleaf + listPlaybooks / getPlaybook: omit topic to list; pass topic to + fetch one guide. SHALL NOT mutate S, SHALL NOT bind a desk, + SHALL NOT be a third operator or a sixth agent verb. Product + read remains pin_map. On paper this cut (implemented=false). + */ + attribute listPlaybooks : Boolean = true; + attribute getPlaybook : Boolean = true; + attribute readOnly : Boolean = true; + attribute mutatesSession : Boolean = false; + attribute bindsDesk : Boolean = false; + attribute notAThirdOperator : Boolean = true; + attribute notASixthVerb : Boolean = true; + attribute productCommand : Boolean = false; + attribute implemented : Boolean = false; + attribute teachesMemNetTools : Boolean = true; + attribute endleafLike : Boolean = true; + + satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_13_PlaybooksAsRecallOption; satisfy MN_REQ_00_MissionBridge::MN_REQ_13_RecallCommit::MN_REQ_13_1_TwoOperators; } @@ -967,6 +1019,9 @@ package MemNet { leftover id_policy (absorbPathBOnly — goldfish writeback is still not called Absorb; ImportAbsorb SHALL NOT entity-resolve); SameThingAbsorb is in-session collapse, nested here. Python cut not this PR. + Default Commit merges by identity and reports created / updated + / unchanged per item (never silent; MN-REQ-03.3). Strict + AddFailsIfExists / UpdateFailsIfAbsent is opt-in. Recall/Commit operatorCount stays 2. */ port gqlIn : GqlWireInPort; @@ -989,12 +1044,21 @@ package MemNet { attribute mergeIsPerWriteLookup : Boolean = true; attribute noReplacementStoreKey : Boolean = true; attribute sameThingAbsorbIsCommitRule : Boolean = true; + attribute defaultMode : String = "merge_and_report"; + attribute mergeByIdentity : Boolean = true; + attribute reportCreatedUpdatedUnchanged : Boolean = true; + attribute silentUpsert : Boolean = false; + attribute defaultAddFailsIfExists : Boolean = false; + attribute defaultUpdateFailsIfAbsent : Boolean = false; + attribute optInStrictAddFailsIfExists : Boolean = true; + attribute optInStrictUpdateFailsIfAbsent : Boolean = true; part mutate : MutateGate; part reserve : NeighbourhoodReserve; part sameThing : SameThingAbsorb; satisfy MN_REQ_00_MissionBridge::MN_REQ_02_MemoryNetGraph::MN_REQ_02_8_IdNotRequiredOnWire; satisfy MN_REQ_00_MissionBridge::MN_REQ_03_StrictMutate::MN_REQ_03_1_AddFailsIfExists; + satisfy MN_REQ_00_MissionBridge::MN_REQ_03_StrictMutate::MN_REQ_03_2_UpdateFailsIfAbsent; satisfy MN_REQ_00_MissionBridge::MN_REQ_03_StrictMutate::MN_REQ_03_3_NoSilentUpsert; satisfy MN_REQ_00_MissionBridge::MN_REQ_11_SnapshotInterop::MN_REQ_11_16_PinIngestDeterministicIds; satisfy MN_REQ_00_MissionBridge::MN_REQ_12_MultitaskMode::MN_REQ_12_9_LeadOwnsSessionImport; @@ -1603,6 +1667,37 @@ package MemNet { satisfy MN_REQ_00_MissionBridge::MN_REQ_07_McpAgentBoundary::MN_REQ_07_5_NoStoreKeyToolSurface; } + part def CmdListPlaybooks { + doc /* + Recall option: list playbook topics (Endleaf listPlaybooks). + Omit topic. Read-only. Not a product verb. implemented=false + this cut (on paper). + */ + attribute productCommand : Boolean = false; + attribute implemented : Boolean = false; + attribute readOnly : Boolean = true; + attribute notAThirdOperator : Boolean = true; + part guide : PlaybookGuide; + + satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_13_PlaybooksAsRecallOption; + } + + part def CmdGetPlaybook { + doc /* + Recall option: fetch one playbook by topic (Endleaf getPlaybook). + Read-only. Not a product verb. implemented=false this cut + (on paper). + */ + attribute productCommand : Boolean = false; + attribute implemented : Boolean = false; + attribute readOnly : Boolean = true; + attribute topicRequired : Boolean = true; + attribute notAThirdOperator : Boolean = true; + part guide : PlaybookGuide; + + satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_13_PlaybooksAsRecallOption; + } + part def CmdPinMap { doc /* TARGET command realizing ShapeWalk from seed set Q (not a required @@ -1755,7 +1850,9 @@ package MemNet { leftover_require_anchor, leftover_allocate_from_locator, or leftover id_policy as product commands. MUST NOT nest name_conflict / merge / absorb as product commands (CueConflict is an emit mark; - SameThingAbsorb is a Commit rule). TierA / Layer line codecs + SameThingAbsorb is a Commit rule). list_playbooks / get_playbook + are Recall options (MN-REQ-04.13; productCommand=false; + implemented=false this cut). TierA / Layer line codecs are RETIRED on product path (M2 done). As-is: memnet.cli (+ serve_client). Python rewrite out of scope. */ @@ -1779,6 +1876,8 @@ package MemNet { part exportPinMap : CmdPinMapExport; part importSlice : CmdPathBImport; part catalogSnap : CmdCatalogSnap; + part listPlaybooks : CmdListPlaybooks; + part getPlaybook : CmdGetPlaybook; part adminUsageReport : CmdAdminUsageReport; part upgradePrepare : CmdAdminUpgradePrepare; part checkpoint : CmdAdminCheckpoint; @@ -1821,6 +1920,7 @@ package MemNet { satisfy MN_REQ_00_MissionBridge::MN_REQ_08_AgentIoGrammar::MN_REQ_08_6_FollowPromptRules; satisfy MN_REQ_00_MissionBridge::MN_REQ_08_AgentIoGrammar::MN_REQ_08_7_WriteApproxDisplay; satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_9_SessionOutlineEmptyCue; + satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_13_PlaybooksAsRecallOption; satisfy MN_REQ_00_MissionBridge::MN_REQ_11_SnapshotInterop::MN_REQ_11_17_CatalogModelSnap; satisfy MN_REQ_00_MissionBridge::MN_REQ_11_SnapshotInterop::MN_REQ_11_17_CatalogModelSnap::MN_REQ_11_17_1_CrossCutSatisfyLocators; satisfy MN_REQ_00_MissionBridge::MN_REQ_09_HumanInspect::MN_REQ_09_3_ParseFaithfulInspect; @@ -1859,7 +1959,9 @@ package MemNet { leftover_allocate_from_locator, or leftover id_policy as product commands. MUST NOT nest name_conflict / merge / absorb as product commands (CueConflict is an emit mark; SameThingAbsorb is a Commit - rule). Agent memory payloads: GQL mutate + shaped pin_map. + rule). list_playbooks / get_playbook are Recall options + (MN-REQ-04.13; productCommand=false; implemented=false this cut). + Agent memory payloads: GQL mutate + shaped pin_map. Structured JSON envelopes. No domain-product tools. LLM agents connect here to MemNet — not to DurableBuffer. Python rewrite out of scope. @@ -1881,6 +1983,8 @@ package MemNet { part exportPinMap : CmdPinMapExport; part importSlice : CmdPathBImport; part catalogSnap : CmdCatalogSnap; + part listPlaybooks : CmdListPlaybooks; + part getPlaybook : CmdGetPlaybook; attribute leftoverReadGetNested : Boolean = false; attribute leftoverNewNested : Boolean = false; attribute leftoverAssignedIdMapNested : Boolean = false; @@ -1920,6 +2024,7 @@ package MemNet { satisfy MN_REQ_00_MissionBridge::MN_REQ_09_HumanInspect::MN_REQ_09_3_ParseFaithfulInspect; satisfy MN_REQ_00_MissionBridge::MN_REQ_10_LlmPropertiesAndLimits::MN_REQ_10_5_GrammarMustBeLlmLearnable; satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_9_SessionOutlineEmptyCue; + satisfy MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_13_PlaybooksAsRecallOption; satisfy MN_REQ_00_MissionBridge::MN_REQ_11_SnapshotInterop::MN_REQ_11_17_CatalogModelSnap; satisfy MN_REQ_00_MissionBridge::MN_REQ_11_SnapshotInterop::MN_REQ_11_17_CatalogModelSnap::MN_REQ_11_17_1_CrossCutSatisfyLocators; } diff --git a/sysml-models/models/requirements.sysml b/sysml-models/models/requirements.sysml index 60c3b5f8..7a8eae96 100644 --- a/sysml-models/models/requirements.sysml +++ b/sysml-models/models/requirements.sysml @@ -41,7 +41,11 @@ MN-REQ-01.9 Snapshot save/load lossless property round-trip; fail-closed save; expire-save failure keeps RAM and counts against the session cap MN-REQ-01.10 CapsPolicy who-check on session save / load-into-ACL / close + MN-REQ-03.1 AddFailsIfExists — opt-in Commit mode (default is merge-and-report) + MN-REQ-03.2 UpdateFailsIfAbsent — opt-in Commit mode + MN-REQ-03.3 Merge-and-report is never silent (created / updated / unchanged) MN-REQ-03.4 MATCH WHERE SET/DELETE SHALL honour the predicate or refuse + MN-REQ-04.13 Playbooks — Recall option (listPlaybooks / getPlaybook; not a new verb) MN-REQ-05.3 One decoded value_bytes cap on pipe mutate, GQL mutate, snapshot load MN-REQ-06.11 Admin-only serve usage report (counts/caps; opaque session alias; not agent MCP) MN-REQ-06.12 Product gateway on memnet-mcp: per-product route to owning serve (not mn_tip_) @@ -480,21 +484,25 @@ package MemNetRequirements { requirement def MN_REQ_03_StrictMutate { doc /* - Agent surface: Commit (mutate). strict mutate group; spine parent Commit. - Group: explicit create vs replace; no silent upsert. + Agent surface: Commit (mutate). Commit group; spine parent Commit. + Default Commit merges by GraphElement identity and reports + created, updated, or unchanged for each item. Strict + AddFailsIfExists / UpdateFailsIfAbsent is an opt-in Commit + mode. The merge is never silent (MN-REQ-03.3). */ attribute requirementId : String = "MN-REQ-03"; attribute agentSurface : String = "Commit"; requirement def MN_REQ_03_1_AddFailsIfExists { doc /* - Agent surface: Commit (mutate). AddFailsIfExists; unchanged; merge-and-report pending. - When an optional nickname id is present on create, SHALL fail with - an id-exists error if that nickname is already taken. SHALL NOT + Agent surface: Commit (mutate). AddFailsIfExists is an opt-in + Commit mode, not the default. When that mode is selected and an + optional nickname id is present on create, SHALL fail with an + id-exists error if that nickname is already taken. SHALL NOT require id on create. TARGET create is CREATE () / CREATE (:Label {props}) without id; properties MAY be empty. leftover_NEW_mint / leftover_by_id is 0.9 as-is, not this - contract. + contract. Default Commit is merge-and-report (MN-REQ-03.3). */ attribute requirementId : String = "MN-REQ-03.1"; attribute agentSurface : String = "Commit"; @@ -502,11 +510,13 @@ package MemNetRequirements { requirement def MN_REQ_03_2_UpdateFailsIfAbsent { doc /* - Agent surface: Commit (mutate). UpdateFailsIfAbsent; unchanged. - When an optional nickname id is present on replace (update), SHALL - fail with a not-found error if that nickname is absent. Ends MATCH + Agent surface: Commit (mutate). UpdateFailsIfAbsent is an opt-in + Commit mode, not the default. When that mode is selected and an + optional nickname id is present on replace (update), SHALL fail + with a not-found error if that nickname is absent. Ends MATCH by label + properties when no nickname. SHALL NOT treat missing id - as not-found of identity. + as not-found of identity. Default Commit is merge-and-report + (MN-REQ-03.3). */ attribute requirementId : String = "MN-REQ-03.2"; attribute agentSurface : String = "Commit"; @@ -514,10 +524,15 @@ package MemNetRequirements { requirement def MN_REQ_03_3_NoSilentUpsert { doc /* - Agent surface: Commit (mutate). no silent upsert; unchanged; merge-and-report is owner-pending. - Silent upsert SHALL NOT be offered. Merge-and-report is an - owner decision and is pending; this leaf stays under Commit - as no-silent-upsert. + Agent surface: Commit (mutate). Default Commit SHALL merge by + GraphElement identity (labels + properties; edge = type + ends) + and SHALL report created, updated, or unchanged for each item + in Δ. The merge is never silent: every item carries an outcome + on the wire. Silent upsert SHALL NOT be offered. Opt-in strict + AddFailsIfExists / UpdateFailsIfAbsent (MN-REQ-03.1 / 03.2) + remains available as a Commit mode; leftover add / update + façades MAY select that mode. SameThingAbsorb stays a distinct + in-session collapse rule, not this per-item report. */ attribute requirementId : String = "MN-REQ-03.3"; attribute agentSurface : String = "Commit"; @@ -784,6 +799,23 @@ package MemNetRequirements { attribute requirementId : String = "MN-REQ-04.12"; attribute agentSurface : String = "internal"; } + + requirement def MN_REQ_04_13_PlaybooksAsRecallOption { + doc /* + Agent surface: option of Recall. listPlaybooks / getPlaybook — + read-only guides that teach an agent how to use MemNet's tools. + SHALL expose a Recall option modelled on Endleaf listPlaybooks / + getPlaybook: list the playbook topics (no topic) and fetch one + guide (topic required). Read-only. SHALL NOT mutate the session + graph, SHALL NOT bind a desk, SHALL NOT be a third operator + beside Recall and Commit, and SHALL NOT be a sixth agent verb. + Product read remains pin_map. Playbooks teach the five surface + verbs (recall, commit, session open, save, close) and the + named options. On paper this cut; engine/MCP not claimed. + */ + attribute requirementId : String = "MN-REQ-04.13"; + attribute agentSurface : String = "option:Recall"; + } } // ----- Hard caps ----- @@ -1412,10 +1444,13 @@ package MemNetRequirements { (load is an option of save), keep_id. Options of Recall: find (bounded MATCH seed, hard LIMIT); leftover query_warm (alias of pin_map); leftover query_walk; - export_pin_map (GQL write-out of a cue pin_map, not SysML reverse). - Options of Commit: leftover add / update (façades; AddFailsIfExists - / UpdateFailsIfAbsent / no-silent-upsert unchanged; merge-and-report - pending); Path-B ingest (locators are properties, not PK); + export_pin_map (GQL write-out of a cue pin_map, not SysML reverse); + list_playbooks / get_playbook (read-only guides; MN-REQ-04.13; + not a third verb). + Options of Commit: leftover add / update (façades of the opt-in + strict mode AddFailsIfExists / UpdateFailsIfAbsent); default + mutate is merge-by-identity and reports created / updated / + unchanged (never silent; MN-REQ-03.3); Path-B ingest (locators are properties, not PK); snap_model; import_slice / Absorb (match by labels+properties, not MERGE-by-id remint); RSV reserve / extend / release. Internal: housekeep_stats, serve_status, session_acl_*, @@ -2423,10 +2458,12 @@ package MemNetRequirements { Spine arm Recall(q): product read. TARGET verb is pin_map. Empty q SHALL return the session outline (MN-REQ-04.9). find is an option of Recall (seed). leftover query_warm is a - leftover alias of pin_map. leftover query_walk / read_list / + leftover alias of pin_map. leftover query_walk / read_list / --anchor are leftover options or leftover nicknames, not product verbs. export_pin_map is an option of Recall (GQL - write-out of a cue pin_map; not SysML reverse). Hard caps + write-out of a cue pin_map; not SysML reverse). list_playbooks + / get_playbook are a Recall option (MN-REQ-04.13; not a third + verb). Hard caps stay hard; a clipped neighbourhood SHALL flag Truncation on the wire. */ @@ -2438,9 +2475,10 @@ package MemNetRequirements { doc /* Agent surface: Commit (mutate). spine arm Commit(Δ). Spine arm Commit(Δ): product write. TARGET verb is mutate. - leftover add / update remain leftover façades of Commit - (AddFailsIfExists / UpdateFailsIfAbsent / no-silent-upsert - unchanged; merge-and-report is owner-pending). Path-B ingest + leftover add / update remain leftover façades of the opt-in + strict Commit mode (AddFailsIfExists / UpdateFailsIfAbsent). + Default Commit merges by identity and reports created, updated, + or unchanged (MN-REQ-03.3; never silent). Path-B ingest (ingest_sysml / ingest_codebase / ingest_pcba / ingest_skills), snap_model, import_slice / Absorb, and RSV (reserve / extend / release) are options of Commit, not third operators. @@ -2565,6 +2603,8 @@ package MemNetRequirements { : MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_11_PinMapOrderFromObservables; requirement housekeepEndpointIdentityReq : MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_12_HousekeepEndpointIdentity; + requirement playbooksAsRecallOptionReq + : MN_REQ_00_MissionBridge::MN_REQ_04_SliceEconomy::MN_REQ_04_13_PlaybooksAsRecallOption; requirement hardCapsReq : MN_REQ_00_MissionBridge::MN_REQ_05_HardCaps; @@ -2826,6 +2866,8 @@ package MemNetRequirements { end #derive ::> sessionOutlineEmptyCueReq; end #derive ::> peakLLastResortReq; end #derive ::> pinMapOrderFromObservablesReq; + end #derive ::> housekeepEndpointIdentityReq; + end #derive ::> playbooksAsRecallOptionReq; } #derivation connection deriveHardCapsLeaves { @@ -2964,6 +3006,7 @@ package MemNetRequirements { end #derive ::> sessionOutlineEmptyCueReq; end #derive ::> peakLLastResortReq; end #derive ::> pinMapOrderFromObservablesReq; + end #derive ::> playbooksAsRecallOptionReq; end #derive ::> queryFanoutCapsReq; end #derive ::> writeApproxDisplayReq; end #derive ::> pinMapEmitSameAsMutateReq; diff --git a/sysml-models/models/verify.sysml b/sysml-models/models/verify.sysml index 3f2a7d18..26a798a9 100644 --- a/sysml-models/models/verify.sysml +++ b/sysml-models/models/verify.sysml @@ -74,6 +74,10 @@ fail-closed save; 0.19.18 snapshots still load. Session save/load/close ACL who (MN-VER-01-S05) when session ACL on. Honour WHERE or refuse (MN-VER-03-S01). + Merge-and-report default Commit (MN-VER-03-S02); strict add/update + is opt-in. Deciding test planned, not yet implemented. + Playbooks as Recall option (MN-VER-04-S07); listPlaybooks / + getPlaybook on paper. Deciding test planned, not yet implemented. Consistent decoded value_bytes cap (MN-VER-05-S01). */ package MemNetVerification { @@ -1753,6 +1757,68 @@ package MemNetVerification { } } + verification def MN_VER_04_S07_PlaybooksAsRecallOption { + doc /* + MN-REQ-04.13 — playbooks are a Recall option, not a new verb. + listPlaybooks (omit topic) and getPlaybook (topic required) + teach MemNet tools. Read-only. SHALL NOT mutate S, SHALL NOT + bind a desk, SHALL NOT be a third operator or a sixth agent + verb. productCommand=false. On paper this cut + (implemented=false). Modelled on Endleaf listPlaybooks / + getPlaybook. + Planned deciding test (not yet implemented): + pytest tests/test_memnet_playbooks.py::test_list_playbooks_and_get_playbook + */ + attribute verificationId : String = "MN-VER-04-S07"; + attribute decidingTest : String + = "tests/test_memnet_playbooks.py::test_list_playbooks_and_get_playbook"; + attribute decidingCommand : String + = "pytest tests/test_memnet_playbooks.py::test_list_playbooks_and_get_playbook"; + attribute decidingTestImplemented : Boolean = false; + + subject system : MemNetSystem; + + objective playbooksReadOnlyRecallOption { + verify playbooksAsRecallOptionReq; + require constraint { + system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.listPlaybooks == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.getPlaybook == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.readOnly == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.mutatesSession == false + and system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.bindsDesk == false + and system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.notAThirdOperator == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.notASixthVerb == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.productCommand == false + and system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.implemented == false + and system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.teachesMemNetTools == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .recall.playbooks.endleafLike == true + and system.mcp.tools.listPlaybooks.productCommand == false + and system.mcp.tools.listPlaybooks.implemented == false + and system.mcp.tools.listPlaybooks.readOnly == true + and system.mcp.tools.getPlaybook.productCommand == false + and system.mcp.tools.getPlaybook.implemented == false + and system.mcp.tools.getPlaybook.readOnly == true + and system.mcp.tools.getPlaybook.topicRequired == true + and system.core.cli.listPlaybooks.productCommand == false + and system.core.cli.listPlaybooks.implemented == false + and system.core.cli.getPlaybook.productCommand == false + and system.core.cli.getPlaybook.implemented == false + and system.core.cli.getPlaybook.topicRequired == true + } + } + } + verification def MN_VER_06_S11_ServeRequestIsolation { doc /* MN-REQ-06.13 — each memnet serve command keeps its own @@ -2166,6 +2232,60 @@ package MemNetVerification { } } + verification def MN_VER_03_S02_MergeAndReport { + doc /* + MN-REQ-03.1 / 03.2 / 03.3 — default Commit merges by GraphElement + identity and reports created, updated, or unchanged for each item + in Δ. The merge is never silent. Strict AddFailsIfExists / + UpdateFailsIfAbsent is an opt-in Commit mode. leftover add / + update façades MAY select that mode. + Planned deciding test (not yet implemented): + pytest tests/test_mutate_merge_report.py::test_commit_reports_created_updated_unchanged + */ + attribute verificationId : String = "MN-VER-03-S02"; + attribute decidingTest : String + = "tests/test_mutate_merge_report.py::test_commit_reports_created_updated_unchanged"; + attribute decidingCommand : String + = "pytest tests/test_mutate_merge_report.py::test_commit_reports_created_updated_unchanged"; + attribute decidingTestImplemented : Boolean = false; + + subject system : MemNetSystem; + + objective mergeAndReportNeverSilent { + verify addFailsIfExistsReq; + verify updateFailsIfAbsentReq; + verify noSilentUpsertReq; + require constraint { + system.core.transport.inProcess.memory.sessions.recallCommit + .commit.defaultMode == "merge_and_report" + and system.core.transport.inProcess.memory.sessions.recallCommit + .commit.mergeByIdentity == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .commit.reportCreatedUpdatedUnchanged == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .commit.silentUpsert == false + and system.core.transport.inProcess.memory.sessions.recallCommit + .commit.defaultAddFailsIfExists == false + and system.core.transport.inProcess.memory.sessions.recallCommit + .commit.defaultUpdateFailsIfAbsent == false + and system.core.transport.inProcess.memory.sessions.recallCommit + .commit.optInStrictAddFailsIfExists == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .commit.optInStrictUpdateFailsIfAbsent == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .commit.mutate.defaultMode == "merge_and_report" + and system.core.transport.inProcess.memory.sessions.recallCommit + .commit.mutate.reportCreatedUpdatedUnchanged == true + and system.core.transport.inProcess.memory.sessions.recallCommit + .commit.mutate.silentUpsert == false + and system.core.transport.inProcess.memory.sessions.store + .defaultCommitMergeAndReport == true + and system.core.transport.inProcess.memory.sessions.store + .silentUpsert == false + } + } + } + verification def MN_VER_05_S01_ConsistentValueByteCap { doc /* MN-REQ-05.3 — one decoded value_bytes cap; knob MEMNET_MAX_VALUE_BYTES; @@ -2289,6 +2409,8 @@ package MemNetVerification { : MN_VER_01_S05_SessionLifecycleAclWho; verification honourWherePredicateVerify : MN_VER_03_S01_HonourWherePredicate; + verification mergeAndReportVerify + : MN_VER_03_S02_MergeAndReport; verification consistentValueByteCapVerify : MN_VER_05_S01_ConsistentValueByteCap; verification storageRolesVerify : MN_VER_06_S06_StorageRoles; @@ -2304,4 +2426,6 @@ package MemNetVerification { : MN_VER_06_S13_ServeCheckpoint; verification housekeepEndpointIdentityVerify : MN_VER_04_S06_HousekeepEndpointIdentity; + verification playbooksAsRecallOptionVerify + : MN_VER_04_S07_PlaybooksAsRecallOption; } diff --git a/sysml-models/outputs/mn-req-13-spine-mapping.md b/sysml-models/outputs/mn-req-13-spine-mapping.md index e4e62f30..5749d13f 100644 --- a/sysml-models/outputs/mn-req-13-spine-mapping.md +++ b/sysml-models/outputs/mn-req-13-spine-mapping.md @@ -15,7 +15,17 @@ Source of the table: `agentSurface` on each requirement def in - Split deploy/ops out of MN-REQ-06 / `deploy.sysml` ops parts. - Move Path-B importers (MN-REQ-11.6…11.16) to a dedicated ingest nest. - Move `cousins.sysml` / `archive.sysml` (or rename leftover coinages). -- Merge-and-report as a Commit rule (owner decision; 03.1–03.3 stay strict). +- Engine/MCP for merge-and-report (MN-REQ-03.3) and playbooks (MN-REQ-04.13). + +## Contract change this cut (ids stable) + +- **MN-REQ-03.1 / 03.2 / 03.3** keep the same ids. Default Commit is + merge-by-identity and reports created / updated / unchanged per item + (never silent). AddFailsIfExists / UpdateFailsIfAbsent become an + **opt-in Commit mode**. This replaces the earlier “do not change + strict add/update” instruction. +- **MN-REQ-04.13** is a new leaf (playbooks as a Recall option). Not a + rename of an existing id. ## Spine tree @@ -26,12 +36,12 @@ MN-REQ-00 Mission │ ├── MN-REQ-13.Recall Recall(q) = pin_map │ │ └── agent-facing read leaves via #derivation │ │ (04.* minus housekeep, 02.4, 05.2, 08.7/08.9, -│ │ 10.2/10.3/10.6, find/export options, 12.4 pin_map) +│ │ 10.2/10.3/10.6, find/export/playbooks options, 12.4 pin_map) │ └── MN-REQ-13.Commit Commit(Δ) = mutate │ ├── MN-REQ-13.2 SameThingAbsorb nested │ └── agent-facing write leaves via #derivation -│ (03.* strict mutate unchanged, ingest/snap options, -│ Absorb / RSV options) +│ (03.* merge-and-report default; 03.1/03.2 opt-in strict, +│ ingest/snap options, Absorb / RSV options) ├── MN-REQ-01 SessionLifecycle open / save / close ├── MN-REQ-02, 05, 07–12 organisational groups (importers stay) └── MN-REQ-06 ProcessBoundary ops (later PR may split) @@ -41,15 +51,17 @@ Agent surface verbs: **recall**, **commit**, **session open**, **save**, **close ## Counts -- Requirement defs / usages: **132** (130 at ab0d2db plus spine arms `MN-REQ-13.Recall` and `MN-REQ-13.Commit`). -- Under Recall (`Recall` + `option:Recall`): **26** +- Requirement defs / usages: **133** (132 at e518e9a plus `MN-REQ-04.13`). +- Under Recall (`Recall` + `option:Recall`): **27** - Under Commit (`Commit` + `option:Commit`): **30** - Session surface (`session` + `option:session`): **10** - Internal: **44** - Ops: **17** - Other (mission / spine / surface listing): **5** -- Named by some `verify`: **52**; still unverified: **80** (the previous 78 plus the two new arms). -- `agentSurface` breakdown: mission 1, session 7, option:session 3, Recall 19, option:Recall 7, Commit 8, option:Commit 22, internal 44, ops 17, surface 2, spine 2. +- Named by some `verify`: **56**; still unverified: **77** + (MN-VER-03-S02 binds 03.1–03.3; MN-VER-04-S07 binds 04.13; + deciding tests planned, `decidingTestImplemented=false`). +- `agentSurface` breakdown: mission 1, session 7, option:session 3, Recall 19, option:Recall 8, Commit 8, option:Commit 22, internal 44, ops 17, surface 2, spine 2. ## Mapping (old organisational parent → new spine parent) @@ -77,10 +89,10 @@ Agent surface verbs: **recall**, **commit**, **session open**, **save**, **close | `MN-REQ-02.7` | `MN-REQ-02` | `MN-REQ-13.Commit` | `option:Commit` | schema-validated ingest — option of Commit | | `MN-REQ-02.8` | `MN-REQ-02` | `internal (stays under organisational group)` | `internal` | id not required on the wire | | `MN-REQ-02.9` | `MN-REQ-02` | `internal (stays under organisational group)` | `internal` | MUST NOT copy cousin store-key / embedding Recall | -| `MN-REQ-03` | `MN-REQ-00` | `MN-REQ-13.Commit` | `Commit` | strict mutate group; spine parent Commit | -| `MN-REQ-03.1` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | AddFailsIfExists; unchanged; merge-and-report pending | -| `MN-REQ-03.2` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | UpdateFailsIfAbsent; unchanged | -| `MN-REQ-03.3` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | no silent upsert; unchanged; merge-and-report is owner-pending | +| `MN-REQ-03` | `MN-REQ-00` | `MN-REQ-13.Commit` | `Commit` | Commit group; default merge-and-report | +| `MN-REQ-03.1` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | AddFailsIfExists; **opt-in** Commit mode (id stable; contract changed) | +| `MN-REQ-03.2` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | UpdateFailsIfAbsent; **opt-in** Commit mode (id stable; contract changed) | +| `MN-REQ-03.3` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | merge-and-report is never silent; created / updated / unchanged per item (id stable; contract changed) | | `MN-REQ-03.4` | `MN-REQ-03` | `MN-REQ-13.Commit` | `Commit` | honour WHERE or refuse | | `MN-REQ-04` | `MN-REQ-00` | `MN-REQ-13.Recall` | `Recall` | slice economy / pin_map group; spine parent Recall | | `MN-REQ-04.1` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | bounded live pin_map | @@ -95,6 +107,7 @@ Agent surface verbs: **recall**, **commit**, **session open**, **save**, **close | `MN-REQ-04.10` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | Peak_L last-resort cue | | `MN-REQ-04.11` | `MN-REQ-04` | `MN-REQ-13.Recall` | `Recall` | pin_map order from Shape observables | | `MN-REQ-04.12` | `MN-REQ-04` | `internal (stays under organisational group)` | `internal` | housekeep endpoint identity; not an agent verb | +| `MN-REQ-04.13` | `MN-REQ-04` | `MN-REQ-13.Recall` | `option:Recall` | playbooks — Recall option (listPlaybooks / getPlaybook; not a new verb); on paper | | `MN-REQ-05` | `MN-REQ-00` | `internal (stays under organisational group)` | `internal` | hard caps group; leaves split across Recall / Commit / internal | | `MN-REQ-05.1` | `MN-REQ-05` | `internal (stays under organisational group)` | `internal` | store resource caps | | `MN-REQ-05.2` | `MN-REQ-05` | `MN-REQ-13.Recall` | `Recall` | query fan-out caps; truncation flagged on the wire | @@ -198,5 +211,6 @@ Agent surface verbs: **recall**, **commit**, **session open**, **save**, **close - **MN-REQ-11** Path-B importers stay nested here; marked option of Commit (move importers = later PR). - **MN-REQ-12** Multitask is host protocol: workers Recall then Commit; 12.4 is dual; 12.2 is ops transport. - **MN-REQ-04.12** housekeep sits in the slice-economy group but is internal, not Recall. +- **MN-REQ-04.13** playbooks fit as an option of Recall (Endleaf list/get; not a sixth verb). - **MN-REQ-13.1** is the two-operator law itself (spine, not a leaf of one arm). diff --git a/sysml-models/outputs/product-nest-one-page.md b/sysml-models/outputs/product-nest-one-page.md index 16dcff19..53a66cf9 100644 --- a/sysml-models/outputs/product-nest-one-page.md +++ b/sysml-models/outputs/product-nest-one-page.md @@ -10,8 +10,8 @@ MemNetSystem // SharedLlmMemory product │ ├── Session // session id = SSOT handle │ ├── GQL // GqlCodec + ProductGqlGate (reject Layer) │ └── RecallCommit // TWO operators only -│ ├── Recall // cue → pin_map (outline / Peak_L honesty) -│ └── Commit // mutate (+ RSV lease) +│ ├── Recall // cue → pin_map (outline / Peak_L / playbooks option) +│ └── Commit // mutate (+ RSV; merge-and-report default) ├── Multitask │ ├── Path A // shared session → re-pin (no import nest) │ └── Path B // slice → ImportGuard (optional soft) diff --git a/tests/test_sysml_recall_commit_spine.py b/tests/test_sysml_recall_commit_spine.py index bf4b09f0..3177230e 100644 --- a/tests/test_sysml_recall_commit_spine.py +++ b/tests/test_sysml_recall_commit_spine.py @@ -42,7 +42,7 @@ def _pairs(text: str) -> list[tuple[str, str]]: def test_every_requirement_has_closed_agent_surface(): pairs = _pairs(_req()) - assert len(pairs) == 132 + assert len(pairs) == 133 unknown = sorted({s for _, s in pairs if s not in CLOSED_SURFACES}) assert unknown == [] by_id = dict(pairs) @@ -54,6 +54,7 @@ def test_every_requirement_has_closed_agent_surface(): assert by_id["MN-REQ-03.2"] == "Commit" assert by_id["MN-REQ-03.3"] == "Commit" assert by_id["MN-REQ-04.9"] == "Recall" + assert by_id["MN-REQ-04.13"] == "option:Recall" assert by_id["MN-REQ-09.1"] == "internal" assert by_id["MN-REQ-01.7"] == "session" assert by_id["MN-REQ-01.8"] == "session" @@ -79,6 +80,7 @@ def test_spine_nest_and_usages(): assert "end #original ::> commitSpineReq" in text assert "end #derive ::> addFailsIfExistsReq" in text assert "end #derive ::> sessionOutlineEmptyCueReq" in text + assert "end #derive ::> playbooksAsRecallOptionReq" in text def test_agent_surface_five_verbs_and_kept_laws(): @@ -86,15 +88,23 @@ def test_agent_surface_five_verbs_and_kept_laws(): assert "verbs are exactly five: recall (pin_map), commit (mutate)" in text assert "session_open, session_save, and session_close" in text assert "Silent upsert SHALL NOT be offered" in text - assert "Merge-and-report is an" in text - assert "owner decision and is pending" in text + assert "opt-in Commit mode" in text + assert "created, updated, or unchanged" in text + assert "The merge is never silent" in text + assert "owner decision and is pending" not in text assert "truncated=true" in text assert "Empty q SHALL be" in text or "empty q SHALL return" in text assert "canonical parser" in text.lower() or "Canonical parser" in text + three_three = text.split("requirement def MN_REQ_03_3_NoSilentUpsert", 1)[1] + three_three = three_three.split("requirement def MN_REQ_03_4_HonourWherePredicate", 1)[0] + assert "report created, updated, or unchanged" in three_three + assert "never silent" in three_three seven = text.split("requirement def MN_REQ_07_1_GenericToolSurface", 1)[1] seven = seven.split("requirement def MN_REQ_07_2_StructuredToolEnvelope", 1)[0] assert "Options of Recall: find" in seven + assert "list_playbooks / get_playbook" in seven assert "Options of Commit: leftover add / update" in seven + assert "merge-by-identity" in seven assert "Internal: housekeep_stats" in seven @@ -115,11 +125,16 @@ def test_mapping_table_and_verify_coverage(): assert "MN-REQ-13.Recall" in md assert "MN-REQ-13.Commit" in md assert "old organisational parent" in md.lower() or "Old parent" in md + assert "MN-REQ-04.13" in md + assert "opt-in" in md.lower() + assert "never silent" in md + assert "ids stable" in md assert "mn_" not in md + assert "03.1–03.3 stay strict" not in md text = _req() pairs = _pairs(text) counts = Counter(s for _, s in pairs) - assert counts["Recall"] + counts["option:Recall"] == 26 + assert counts["Recall"] + counts["option:Recall"] == 27 assert counts["Commit"] + counts["option:Commit"] == 30 assert counts["internal"] == 44 assert counts["ops"] == 17 @@ -127,15 +142,47 @@ def test_mapping_table_and_verify_coverage(): assert "missionReq" in usages assert "recallSpineReq" in usages assert "commitSpineReq" in usages - assert len(usages) == 132 + assert "playbooksAsRecallOptionReq" in usages + assert len(usages) == 133 verified = set(re.findall(r"verify (\w+);", VERIFY.read_text(encoding="utf-8"))) unverified = [u for u in usages if u not in verified] - assert len(verified & set(usages)) == 52 - assert len(unverified) == 80 + assert len(verified & set(usages)) == 56 + assert len(unverified) == 77 assert "recallSpineReq" in unverified assert "commitSpineReq" in unverified + assert "playbooksAsRecallOptionReq" in verified + assert "noSilentUpsertReq" in verified + assert "addFailsIfExistsReq" in verified + assert "updateFailsIfAbsentReq" in verified def test_brace_balance_requirements(): text = _req() assert text.count("{") == text.count("}") + + +def test_merge_and_report_and_playbooks_on_paper(): + deploy = DEPLOY.read_text(encoding="utf-8") + verify = VERIFY.read_text(encoding="utf-8") + assert 'attribute defaultMode : String = "merge_and_report"' in deploy + assert "attribute reportCreatedUpdatedUnchanged : Boolean = true" in deploy + assert "attribute silentUpsert : Boolean = false" in deploy + assert "attribute optInStrictAddFailsIfExists : Boolean = true" in deploy + assert "part def PlaybookGuide" in deploy + assert "part listPlaybooks : CmdListPlaybooks" in deploy + assert "part getPlaybook : CmdGetPlaybook" in deploy + assert deploy.count("part listPlaybooks : CmdListPlaybooks") == 2 + assert deploy.count("part getPlaybook : CmdGetPlaybook") == 2 + assert ( + "attribute implemented : Boolean = false" + in deploy.split("part def PlaybookGuide {", 1)[1].split("part def Commit {", 1)[0] + ) + assert "MN-VER-03-S02" in verify + assert "MN-VER-04-S07" in verify + assert ( + "tests/test_mutate_merge_report.py::test_commit_reports_created_updated_unchanged" in verify + ) + assert "tests/test_memnet_playbooks.py::test_list_playbooks_and_get_playbook" in verify + assert verify.count("attribute decidingTestImplemented : Boolean = false") == 2 + assert "playbooksAsRecallOptionReq" in verify + assert "noSilentUpsertReq" in verify