archgraph-argo 0.10.40 → 0.10.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@ applyTo: "**"
7
7
  <WakeupGuideline>
8
8
  STEP 0 — UNCONDITIONAL STARTUP GATE. This applies to the very first message of every session, including greetings, casual chat, and questions; it may never be skipped, deferred, or treated as optional.
9
9
  1. Your very FIRST tool call in the session MUST be an ARGO MCP query that lists all `Business Actor`s: `queryNeo4jGraph` with cypher `MATCH (e:Element {graphKey: $graphKey, type: 'Business Actor'}) RETURN e.id, e.name, e.description ORDER BY e.name`. Identify which `Business Actor` you are. If you are not sure, consult your human partner to confirm your role before doing anything else.
10
- 2. Restore your long-term memory: the SUBVIEW hierarchy mounted under that `Business Actor` element — the Views whose `parent_element_id` points to this Actor, plus the elements, relationships, and further nested sub-views inside them (NOT the View that merely includes the Actor in its `included_elements`) and load it into session memory.
10
+ 2. Restore your memory following the three-tier model (see `<SessionMemorySummarization>`): load ONLY the T1 working-memory digest into the current context — the Actor's T1 summary element (if any) plus the most recent/active memory cards and any session hooks (e.g. unfinished items from the last session). Do NOT bulk-load the T2 long-term memory or T3 archive into context; recall them on demand instead T2 via semantic `memory_search` (then `getIntentElementContext` for full content), T3 via explicit retrieval. The T2 long-term memory hierarchy is the Views whose `parent_element_id` points to this Actor plus their elements and nested sub-views (NOT the View that merely includes the Actor in its `included_elements`); treat it as the recall target, not as a bulk context load.
11
11
  3. If the `agent` attribute of the confirmed Actor differs from your current Agent type, switch to that Agent type, or delegate to an Agent of that type per `<CoperationGuideline>` item 2.
12
12
  4. If the ARGO MCP is unavailable or errors, state that explicitly to the user before doing anything else. Only after completing this gate may you respond to the user or take any other action.
13
13
  </WakeupGuideline>
@@ -103,22 +103,28 @@ Every repository change MUST be classified into exactly one tier BEFORE implemen
103
103
  </CapabilityDelegationGuideline>
104
104
 
105
105
  <SessionMemorySummarization>
106
- Before every session ends (before finishing work), you MUST perform a short-term memory summarization and write the summary into long-term memory — a SUBVIEW hierarchy mounted under the relevant `Business Actor` element, i.e. Views whose `parent_element_id` points to that Actor (create the first sub-view if none exists; you may mount multiple sub-views under the Actor, or expand new sub-views under the elements of an existing sub-view, forming a hierarchical long-term memory system) refreshing long-term memory to prevent cross-session forgetting:
107
- 1. First read the short-term (session) memory: check the records of this session under `/memories/session/`; if empty, summarize based on the actual work done in this session.
108
- 2. Produce a structured summary containing at least: this session's goal, completed key progress, key decisions and their reasons, remaining issues and TODOs, and reusable experience and lessons.
109
- 3. Write the summary into long-term memory:
110
- - If this session's work belongs to a `Business Actor` role, write it into the long-term memory sub-views mounted under that Actor (the Views whose `parent_element_id` points to this Actor, NOT the View that merely contains the Actor itself; see `<CoperationGuideline>` item 3);
111
- 4. The summary must be concise and de-duplicated: prefer updating existing memory files and only create new ones when necessary; do not copy redundant process content verbatim from the session into long-term memory.
106
+ Long-term memory capture follows a three-tier model: T1 working memory (the Actor's session-summary element, idempotently overwritten), T2 long-term memory (the Actor's LTM sub-view hierarchy), and T3 archive (move-only). Two capture paths:
107
+ 1. Milestone immediate writes are the RELIABLE BACKBONE see `<MemoryTriggerTiming>`. Never defer critical content (pitfalls, decisions, commit registrations) to session end.
108
+ 2. The session summary is OPPORTUNISTIC and IDEMPOTENT. You MUST NOT rely on precisely detecting when a session ends the LLM cannot reliably predict the human partner ending the session. Instead, write the session summary when either: (a) the human partner explicitly signals wrap-up (e.g. "done", "summarize", "wrap up"); or (b) you have finished the latest request and the turn is ending naturally. Write it into the Actor's T1 working-memory element by OVERWRITING that single element (never append), so repeated triggers only update it and write amplification stays bounded. If the final summary is missed, the milestone immediate writes already preserve all critical content.
109
+ Produce a structured summary containing at least: this session's goal, completed key progress, key decisions and their reasons, remaining issues and TODOs, and reusable experience and lessons. Write it into the Actor's memory: the session summary goes to the T1 working-memory element; long-term capture goes to the T2 LTM sub-view hierarchy mounted under that Actor (the Views whose `parent_element_id` points to this Actor, NOT the View that merely contains the Actor itself; see `<CoperationGuideline>` item 3). Keep the summary concise and de-duplicated: prefer updating existing memory and only create new entries when necessary; do not copy redundant process content verbatim.
112
110
  </SessionMemorySummarization>
113
111
 
114
112
  <MemoryTriggerTiming>
115
- In addition to "session end", long-term memory writes must be triggered immediately at the following moments and must not be deferred to session end:
113
+ Long-term memory writes are primarily triggered IMMEDIATELY at the following moments — this is the reliable backbone and MUST NOT be deferred to session end:
116
114
  1. Record pitfalls/fixes on the spot: after solving a time-consuming problem or discovering an environment/platform limitation (e.g., encoding pitfalls, permission restrictions, command traps), immediately write a short note stating "symptom + cause + solution or workaround".
117
115
  2. Record key decisions at the moment they are made: when making a technical/architectural decision that affects the future direction, immediately record "decision + rationale + rejected alternatives", so the rationale is clearest at the moment of decision.
118
116
  3. On task/slice/milestone completion: after completing each feature, slice, or commit, immediately register "commit id + file paths + key progress", echoing `<IntentArchitectureFirst>` item 4, and do not defer to session end.
119
- The above immediate records also follow the conciseness and de-duplication requirements of `<SessionMemorySummarization>` item 4.
117
+ The above immediate records follow the conciseness and de-duplication requirements of `<SessionMemorySummarization>`. The session-end consolidated summary is a separate, opportunistic and idempotent write (see `<SessionMemorySummarization>`), NOT the primary capture path.
120
118
  </MemoryTriggerTiming>
121
119
 
120
+ <MemoryRecallGuideline>
121
+ Recall long-term memory in TWO steps — a compact card is a LOCATOR, never the full memory:
122
+ 1. LOCATE: run `memory_search` (or `getSystemArchitecture` with purpose `general`) to find relevant memory by meaning. Recall uses the LOOSE memory threshold (default 0.55, configurable via `ARGO_SEMANTIC_MEMORY_THRESHOLD` / `ARGO_SEMANTIC_MEMORY_THRESHOLD_<CHANNEL>`), so paraphrased-but-relevant memory is not missed.
123
+ 2. RETRIEVE FULL CONTENT: for each hit, call `getIntentElementContext` on the hit id to read the full element (description + attributes). Do NOT answer from the truncated card alone.
124
+ Threshold layering: memory recall is loose; `audit` reads are strict (default 0.8). Reject only when there are ZERO or clearly irrelevant hits — never reject a low-but-relevant score.
125
+ Recall target: an Actor's T2 long-term memory is its `<actor>-ltm-001` view hierarchy; T3 archive (`<actor>-archive-001`) is read by explicit retrieval. Recall on demand — never bulk-load memory into context.
126
+ </MemoryRecallGuideline>
127
+
122
128
  <ToolsGuideline>
123
129
  You MUST read/write the intent architecture through the tools provided by the ARGO MCP server; direct modification of the intent architecture source file is forbidden:
124
130
  1. getSystemArchitecture: semantically read the architecture — MUST supply query.purpose + query.intent (semantic retrieval per <QueryPriorityGuideline>); an omitted-query full read is a last resort, not the default.
@@ -155,7 +155,7 @@ const TOOLS = [
155
155
  },
156
156
  {
157
157
  name: 'updateArchitectureElement',
158
- description: 'Use for one global element metadata patch. Does not change view membership. Element id and type are immutable; remove and re-add to change them. patch.attributes is a targeted merge by attribute name: each entry {name, value?, description?} upserts that attribute, {name, op:"remove"} deletes it, and attributes NOT mentioned are preserved — updating one attribute never wipes out the others.',
158
+ description: 'Use for one global element metadata patch. Does not change view membership. Element id and type are immutable; remove and re-add to change them. patch.attributes is a targeted merge: single-valued attributes upsert by name; multi-valued ledgers (e.g. commit) upsert by name+value — a new value appends a new entry, re-registering the same value updates its description; {name, op:"remove"} (optionally with value) deletes the matching entries; attributes NOT mentioned are preserved — updating one attribute never wipes out the others.',
159
159
  inputSchema: {
160
160
  type: 'object',
161
161
  required: ['id', 'patch'],
@@ -248,7 +248,7 @@ const TOOLS = [
248
248
  },
249
249
  {
250
250
  name: 'updateArchitectureElement',
251
- description: 'Use for one global element metadata patch. Does not change view membership. Element id and type are immutable; remove and re-add to change them. patch.attributes is a targeted merge by attribute name: each entry {name, value?, description?} upserts that attribute, {name, op:"remove"} deletes it, and attributes NOT mentioned are preserved — updating one attribute never wipes out the others. Set dryRun to preview without writing.',
251
+ description: 'Use for one global element metadata patch. Does not change view membership. Element id and type are immutable; remove and re-add to change them. patch.attributes is a targeted merge: single-valued attributes upsert by name; multi-valued ledgers (e.g. commit) upsert by name+value — a new value appends a new entry, re-registering the same value updates its description; {name, op:"remove"} (optionally with value) deletes the matching entries; attributes NOT mentioned are preserved — updating one attribute never wipes out the others. Set dryRun to preview without writing.',
252
252
  inputSchema: {
253
253
  type: 'object',
254
254
  required: ['id', 'patch'],
@@ -1031,17 +1031,33 @@ function buildNativeSubgraph(document, includedElementIds, includedRelationshipI
1031
1031
  return { elements, relationships, views };
1032
1032
  }
1033
1033
 
1034
+ /**
1035
+ * Attribute names that are MULTI-VALUED ledgers (many attributes share the same
1036
+ * `name` but carry distinct `value`s). The canonical example is the `commit`
1037
+ * ledger: an element accumulates one `{ name: 'commit', value: <sha> }` per
1038
+ * commit. For these, a patch entry upserts by (name, value) — a new value
1039
+ * APPENDS a new entry, re-registering the same value updates its description.
1040
+ */
1041
+ const MULTI_VALUE_ATTRIBUTE_NAMES = new Set(['commit']);
1042
+
1034
1043
  /**
1035
1044
  * Merge a targeted attribute patch into the element's existing attributes.
1036
1045
  *
1037
1046
  * Each patch entry is an object with a non-empty `name`:
1038
- * - `{ name, op: 'remove' }` -> remove that attribute;
1039
- * - `{ name, value }` / `{ name, value, description }` -> upsert (add or update in place).
1047
+ * - `{ name, op: 'remove' }` / `{ name, value, op: 'remove' }`
1048
+ * -> remove matching attribute(s); with `value`, only the exact entry.
1049
+ * - `{ name, value }` / `{ name, value, description }` -> upsert.
1050
+ *
1051
+ * Upsert semantics:
1052
+ * - Multi-valued ledgers (e.g. `commit`): match by (name, value). Same value
1053
+ * -> update description in place; new value -> APPEND a new entry.
1054
+ * - Single-valued attributes (e.g. `deliveryStatus`): match by name and
1055
+ * update value/description in place; absent -> append.
1040
1056
  *
1041
1057
  * Attributes NOT mentioned in the patch are preserved. This avoids the
1042
1058
  * full-array replacement footgun where updating one attribute would wipe out the
1043
1059
  * rest (e.g. a stale deliveryStatus being re-written when registering a commit
1044
- * attribute).
1060
+ * attribute), while still letting the commit ledger accumulate entries.
1045
1061
  */
1046
1062
  function mergeAttributesPatch(existing, patchEntries) {
1047
1063
  const result = Array.isArray(existing)
@@ -1055,21 +1071,37 @@ function mergeAttributesPatch(existing, patchEntries) {
1055
1071
  throw new Error('patch.attributes entries must have a non-empty string name');
1056
1072
  }
1057
1073
  if (entry.op === 'remove') {
1058
- const index = result.findIndex(attr => attr.name === entry.name);
1059
- if (index >= 0) {
1060
- result.splice(index, 1);
1074
+ // Remove ALL matching entries; when a value is given, restrict to that
1075
+ // exact (name, value) entry so a single ledger line can be removed.
1076
+ for (let i = result.length - 1; i >= 0; i -= 1) {
1077
+ const attr = result[i];
1078
+ const nameMatches = attr.name === entry.name;
1079
+ const valueMatches = !Object.prototype.hasOwnProperty.call(entry, 'value') || attr.value === entry.value;
1080
+ if (nameMatches && valueMatches) result.splice(i, 1);
1061
1081
  }
1062
1082
  continue;
1063
1083
  }
1064
- const index = result.findIndex(attr => attr.name === entry.name);
1065
- if (index >= 0) {
1066
- if (Object.prototype.hasOwnProperty.call(entry, 'value')) result[index].value = entry.value;
1067
- if (Object.prototype.hasOwnProperty.call(entry, 'description')) result[index].description = entry.description;
1084
+ if (MULTI_VALUE_ATTRIBUTE_NAMES.has(entry.name)) {
1085
+ const index = result.findIndex(attr => attr.name === entry.name && attr.value === entry.value);
1086
+ if (index >= 0) {
1087
+ if (Object.prototype.hasOwnProperty.call(entry, 'description')) result[index].description = entry.description;
1088
+ } else {
1089
+ const newAttribute = { name: entry.name };
1090
+ if (Object.prototype.hasOwnProperty.call(entry, 'value')) newAttribute.value = entry.value;
1091
+ if (Object.prototype.hasOwnProperty.call(entry, 'description')) newAttribute.description = entry.description;
1092
+ result.push(newAttribute);
1093
+ }
1068
1094
  } else {
1069
- const newAttribute = { name: entry.name };
1070
- if (Object.prototype.hasOwnProperty.call(entry, 'value')) newAttribute.value = entry.value;
1071
- if (Object.prototype.hasOwnProperty.call(entry, 'description')) newAttribute.description = entry.description;
1072
- result.push(newAttribute);
1095
+ const index = result.findIndex(attr => attr.name === entry.name);
1096
+ if (index >= 0) {
1097
+ if (Object.prototype.hasOwnProperty.call(entry, 'value')) result[index].value = entry.value;
1098
+ if (Object.prototype.hasOwnProperty.call(entry, 'description')) result[index].description = entry.description;
1099
+ } else {
1100
+ const newAttribute = { name: entry.name };
1101
+ if (Object.prototype.hasOwnProperty.call(entry, 'value')) newAttribute.value = entry.value;
1102
+ if (Object.prototype.hasOwnProperty.call(entry, 'description')) newAttribute.description = entry.description;
1103
+ result.push(newAttribute);
1104
+ }
1073
1105
  }
1074
1106
  }
1075
1107
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archgraph-argo",
3
- "version": "0.10.40",
3
+ "version": "0.10.42",
4
4
  "description": "Deploy the ArchGraph ARGO toolchain, skills, and rules (schema, scripts, argo-init skill, global rule) with one command.",
5
5
  "license": "MIT",
6
6
  "bin": {