archgraph-argo 0.22.0 → 0.22.1

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.
@@ -76,7 +76,7 @@ Never add what the graph already has. Reuse is the default; there is no reject m
76
76
  <LosslessWrite>
77
77
  Never lose stored content silently. A write must never reduce existing content unless it is lossless (merge/delta) or you explicitly acknowledge the reduction; omission never means deletion.
78
78
  1. Structured fields merge: element testcases merge by `name`; view membership accepts a delta `{ add, remove }`; relationship attributes merge by `name`. Omitting an existing entry preserves it. Delete only with an explicit `op: "remove"` or an explicit delta remove.
79
- 2. Scalar text is guarded: `description`, `statement`, `document`, `name`, `view_name` are full-value fields. A *reworded* line is not a loss; dropping prior lines, or losing structured tokens (ids, numbers, commit hashes, paths), is blocked unless you pass `acknowledgeLoss: true` (and `lossJustification` when the loss is major). Read the current value first and edit as a minimal diff.
79
+ 2. Scalar text is guarded: `description`, `statement`, `document`, `name`, `view_name` are full-value fields. A reworded line is not a loss; dropping prior lines, or genuinely losing structured tokens (commit hashes, ids, versions, dates, paths — separator/space changes do NOT count), is blocked unless you pass `acknowledgeLoss: true` (and `lossJustification` when the loss is major). Read the current value first and edit as a minimal diff.
80
80
  3. Destructive removals (`removeElement` / `removeRelationship` / `removeView`) require `acknowledgeLoss: true` — per mutation, or once for the whole set via the top-level `acknowledgeLoss` on `applySystemArchitectureMutation`; the full removed object is appended to the NDJSON tombstone ledger `design/KG/SystemArchitecture.tombstones.ndjson` (rotated by size) for recovery.
81
81
  4. Always read the `lossless` loss report in the response (preview and apply). It lists removed text lines / testcases / members / objects. If it is blocked, fix the mutation—do not retry blindly and do not disable the gate.
82
82
  </LosslessWrite>
@@ -66,12 +66,38 @@ function normalizeSegments(text) {
66
66
  }
67
67
 
68
68
  function tokenize(segment) {
69
- const matches = String(segment === undefined || segment === null ? '' : segment).match(TOKEN_RE);
69
+ const normalized = String(segment === undefined || segment === null ? '' : segment).normalize('NFKC');
70
+ const matches = normalized.match(TOKEN_RE);
70
71
  return matches ? matches.map(token => token.toLowerCase()) : [];
71
72
  }
72
73
 
74
+ // A "structured identity" token whose silent loss matters: commit hashes, ids,
75
+ // versions, dates, path-like strings. Bare separators ("/") and short plain
76
+ // numbers are NOT structured — treating them so made benign edits false-block.
73
77
  function isStructuredToken(token) {
74
- return /[0-9]/.test(token) || /[/\\]/.test(token) || /^[0-9a-f]{7,40}$/i.test(token);
78
+ if (!token) return false;
79
+ if (/^[0-9a-f]{7,40}$/i.test(token)) return true; // commit hash / long hex
80
+ if (/[/\\]/.test(token) && /[a-z]/i.test(token)) return true; // path-like (has a letter)
81
+ if (/[a-z]/i.test(token) && /\d/.test(token) && /[-_]/.test(token)) return true; // id-like AT-rules-04
82
+ if (/^[a-z]*\d+(?:\.\d+)+$/i.test(token)) return true; // version-like stix2.1 / v2.1 / 1.2.3
83
+ if (/^\d{4}-\d{2}(?:-\d{2})?$/.test(token)) return true; // date-like 2026-09-15
84
+ if (/^\d{5,}$/.test(token)) return true; // long numeric id
85
+ return false;
86
+ }
87
+
88
+ // NFKC + strip all whitespace + lowercase: tolerant haystack for the "is this
89
+ // structured token still present anywhere in the new text?" substring check.
90
+ // This drops false positives from separator joins (769465c:bc7f418) and spacing
91
+ // (STIX2.1 -> STIX 2.1) while still catching a genuinely removed token.
92
+ function normalizeForTokenSearch(value) {
93
+ return String(value === undefined || value === null ? '' : value).normalize('NFKC').replace(/\s+/g, '').toLowerCase();
94
+ }
95
+
96
+ // Separator-insensitive form: also drop the punctuation that joins structured
97
+ // tokens, so "A/B" -> "A、B" or "v2.1" -> "v2 1" is still recognised as the same
98
+ // token (same content, punctuation changed) and not mistaken for a loss.
99
+ function comparableTokenForm(value) {
100
+ return normalizeForTokenSearch(value).replace(/[/\\:.\-_]/g, '');
75
101
  }
76
102
 
77
103
  // Sørensen–Dice coefficient over token sets (deterministic, no network).
@@ -97,7 +123,7 @@ function detectTextLoss(oldText, newText) {
97
123
  }
98
124
  const newSegments = normalizeSegments(newText);
99
125
  const newNormalized = normalizeForCompare(newText);
100
- const newTokenSet = new Set(newSegments.flatMap(tokenize));
126
+ const newHaystack = comparableTokenForm(newText);
101
127
  const newSegmentTokens = newSegments.map(tokenize);
102
128
  const removedSegments = [];
103
129
  const modifiedSegments = [];
@@ -105,7 +131,7 @@ function detectTextLoss(oldText, newText) {
105
131
  for (const segment of oldSegments) {
106
132
  const segmentTokens = tokenize(segment);
107
133
  for (const token of segmentTokens) {
108
- if (isStructuredToken(token) && !newTokenSet.has(token)) structuredTokensRemoved.push(token);
134
+ if (isStructuredToken(token) && !newHaystack.includes(comparableTokenForm(token))) structuredTokensRemoved.push(token);
109
135
  }
110
136
  if (newNormalized.includes(segment)) continue; // kept verbatim
111
137
  const best = newSegmentTokens.reduce((max, tokens) => Math.max(max, diceCoefficient(segmentTokens, tokens)), 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archgraph-argo",
3
- "version": "0.22.0",
3
+ "version": "0.22.1",
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": {