archgraph-argo 0.18.1 → 0.19.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.
- package/argo/rules/archgraph.instructions.md +6 -6
- package/argo/scripts/argo-mcp-server.js +3 -3
- package/argo/scripts/ea-qea-sync-lib.js +2 -0
- package/argo/scripts/graph-rag/defaultSemanticRetrieval.js +1 -1
- package/argo/scripts/graph-semantics.js +7 -0
- package/argo/scripts/systemarchitecture-mcp-server.js +86 -40
- package/package.json +1 -1
|
@@ -22,7 +22,7 @@ Non-negotiable red lines (MUST). Never skip, simplify, or silently violate them;
|
|
|
22
22
|
6. Continuously comply with these red lines throughout the session.
|
|
23
23
|
7. Retrieve KG-first and semantic-first. See `<QueryPriorityGuideline>`.
|
|
24
24
|
8. Store content KG-first. See `<ContentStoragePolicy>`.
|
|
25
|
-
9. Never duplicate: reuse
|
|
25
|
+
9. Never duplicate: reuse is the default; a create blocked as an exact or semantic duplicate must be reused, or explicitly overridden with `onConflict: "allowDuplicate"` + justification. See `<GraphDeduplication>`.
|
|
26
26
|
</CoreRules>
|
|
27
27
|
|
|
28
28
|
<Ontology>
|
|
@@ -54,11 +54,11 @@ Your architecture is ArchiMate 3.2 plus ARGO extensions. Reference files live un
|
|
|
54
54
|
</ContentStoragePolicy>
|
|
55
55
|
|
|
56
56
|
<GraphDeduplication>
|
|
57
|
-
Never add what the graph already has. Reuse
|
|
58
|
-
1.
|
|
59
|
-
2.
|
|
60
|
-
3.
|
|
61
|
-
4.
|
|
57
|
+
Never add what the graph already has. Reuse is the default; there is no reject mode.
|
|
58
|
+
1. `addElement` / `addRelationship` / `addView` default to `onConflict: "reuse"` (find-or-create). An exact match (element: same type + name; relationship: same source + type + target + name; view: same parent + name) is reused — the existing object is attached to the requested view(s) and nothing new is created.
|
|
59
|
+
2. A new ELEMENT is also blocked when it is semantically near an existing element of the same type anywhere in the graph: the call writes nothing and returns `semanticConflicts` with the candidates.
|
|
60
|
+
3. To create despite an exact or semantic duplicate you MUST pass `onConflict: "allowDuplicate"` with a real `justification`. Nothing else creates a duplicate.
|
|
61
|
+
4. On a block, reuse the returned existing element: re-call `addElement` with that element's id (it is then attached to your view), or update it with `updateArchitectureElement`.
|
|
62
62
|
5. Updates are never gated. Never work around a duplicate by editing around it — reuse or update the existing object.
|
|
63
63
|
</GraphDeduplication>
|
|
64
64
|
|
|
@@ -149,7 +149,7 @@ const TOOLS = [
|
|
|
149
149
|
properties: {
|
|
150
150
|
element: { type: 'object' },
|
|
151
151
|
view_ids: { type: 'array', minItems: 1, items: { type: 'string' } },
|
|
152
|
-
onConflict: { type: 'string', enum: ['
|
|
152
|
+
onConflict: { type: 'string', enum: ['reuse', 'allowDuplicate'], description: 'Dedup policy (default reuse). reuse: find-or-create — attach an existing exact (type, name) match; a same-type semantic near-duplicate also blocks creation. allowDuplicate: create anyway (even if a duplicate exists), requires a justification.' },
|
|
153
153
|
justification: { type: 'string', description: 'Required when onConflict is allowDuplicate.' },
|
|
154
154
|
architecturePath: { type: 'string', description: 'Default: design/KG/SystemArchitecture.json' },
|
|
155
155
|
},
|
|
@@ -193,7 +193,7 @@ const TOOLS = [
|
|
|
193
193
|
properties: {
|
|
194
194
|
relationship: { type: 'object' },
|
|
195
195
|
view_ids: { type: 'array', minItems: 1, items: { type: 'string' } },
|
|
196
|
-
onConflict: { type: 'string', enum: ['
|
|
196
|
+
onConflict: { type: 'string', enum: ['reuse', 'allowDuplicate'], description: 'Dedup policy (default reuse). reuse: find-or-create — attach an existing exact (source, type, target, name) match. allowDuplicate: create anyway, requires a justification.' },
|
|
197
197
|
justification: { type: 'string', description: 'Required when onConflict is allowDuplicate.' },
|
|
198
198
|
architecturePath: { type: 'string', description: 'Default: design/KG/SystemArchitecture.json' },
|
|
199
199
|
},
|
|
@@ -236,7 +236,7 @@ const TOOLS = [
|
|
|
236
236
|
required: ['view'],
|
|
237
237
|
properties: {
|
|
238
238
|
view: { type: 'object' },
|
|
239
|
-
onConflict: { type: 'string', enum: ['
|
|
239
|
+
onConflict: { type: 'string', enum: ['reuse', 'allowDuplicate'], description: 'Dedup policy (default reuse). reuse: attach an existing exact (parent, view name) match. allowDuplicate: create anyway, requires a justification.' },
|
|
240
240
|
justification: { type: 'string', description: 'Required when onConflict is allowDuplicate.' },
|
|
241
241
|
architecturePath: { type: 'string', description: 'Default: design/KG/SystemArchitecture.json' },
|
|
242
242
|
},
|
|
@@ -698,6 +698,7 @@ function syncGraphToQea(graph, qeaPath, opts) {
|
|
|
698
698
|
RectRight: 40 + col * 260 + 180, RectBottom: 40 + row * 160 + 90,
|
|
699
699
|
Sequence: seq,
|
|
700
700
|
});
|
|
701
|
+
placedObjs.add(Number(oid));
|
|
701
702
|
seq++;
|
|
702
703
|
}
|
|
703
704
|
if (newObjs.length > 0 && !o.dryRun) {
|
|
@@ -731,6 +732,7 @@ function syncGraphToQea(graph, qeaPath, opts) {
|
|
|
731
732
|
if (cid === undefined || cid < 0) { continue; }
|
|
732
733
|
if (placedLinks.has(Number(cid))) { continue; }
|
|
733
734
|
newLinks.push({ DiagramID: diagramId, ConnectorID: Number(cid), Style: '', Geometry: '' });
|
|
735
|
+
placedLinks.add(Number(cid));
|
|
734
736
|
}
|
|
735
737
|
if (newLinks.length > 0 && !o.dryRun) {
|
|
736
738
|
insertMany(db, 't_diagramlinks', ['DiagramID', 'ConnectorID', 'Style', 'Geometry'], newLinks);
|
|
@@ -205,7 +205,7 @@ async function executeWpP2Retrieval({
|
|
|
205
205
|
requireQualifiedVector(vector);
|
|
206
206
|
const purpose = request && typeof request.purpose === 'string' ? request.purpose : '';
|
|
207
207
|
const strict = AUDIT_PURPOSES.has(purpose);
|
|
208
|
-
const topK = resolveTopK();
|
|
208
|
+
const topK = Number.isInteger(request.topK) && request.topK > 0 ? request.topK : resolveTopK();
|
|
209
209
|
const seedsByType = {};
|
|
210
210
|
for (const channel of CHANNELS) {
|
|
211
211
|
seedsByType[channel.key] = await exhaustChannel({
|
|
@@ -116,12 +116,19 @@ function validateGraphSemantics(document, errors) {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
const includedElementIds = new Set(view.included_elements || []);
|
|
119
|
+
if (includedElementIds.size !== (view.included_elements || []).length) {
|
|
120
|
+
errors.push(`views '${view.view_id}' must not contain duplicate included_elements`);
|
|
121
|
+
}
|
|
119
122
|
for (const elementId of view.included_elements || []) {
|
|
120
123
|
elementIdsIncludedInViews.add(elementId);
|
|
121
124
|
if (!elementById.has(elementId)) {
|
|
122
125
|
errors.push(`views '${view.view_id}' references missing included element '${elementId}'`);
|
|
123
126
|
}
|
|
124
127
|
}
|
|
128
|
+
const includedRelationshipIds = new Set(view.included_relationships || []);
|
|
129
|
+
if (includedRelationshipIds.size !== (view.included_relationships || []).length) {
|
|
130
|
+
errors.push(`views '${view.view_id}' must not contain duplicate included_relationships`);
|
|
131
|
+
}
|
|
125
132
|
for (const relationshipId of view.included_relationships || []) {
|
|
126
133
|
relationshipIdsIncludedInViews.add(relationshipId);
|
|
127
134
|
const rel = relationshipById.get(relationshipId);
|
|
@@ -245,7 +245,7 @@ const TOOLS = [
|
|
|
245
245
|
properties: {
|
|
246
246
|
element: { type: 'object' },
|
|
247
247
|
view_ids: { type: 'array', minItems: 1, items: { type: 'string' } },
|
|
248
|
-
onConflict: { type: 'string', enum: ['
|
|
248
|
+
onConflict: { type: 'string', enum: ['reuse', 'allowDuplicate'], description: 'Dedup policy (default reuse). reuse: find-or-create — attach an existing exact (type, name) match; a same-type semantic near-duplicate also blocks creation. allowDuplicate: create anyway (even if a duplicate exists), requires a justification.' },
|
|
249
249
|
justification: { type: 'string', description: 'Required when onConflict is allowDuplicate.' },
|
|
250
250
|
dryRun: { type: 'boolean', description: 'When true, validates and returns the result without writing to the graph. Default: false.' },
|
|
251
251
|
architecturePath: { type: 'string', description: `Default: ${DEFAULT_GRAPH_PATH}` },
|
|
@@ -292,7 +292,7 @@ const TOOLS = [
|
|
|
292
292
|
properties: {
|
|
293
293
|
relationship: { type: 'object' },
|
|
294
294
|
view_ids: { type: 'array', minItems: 1, items: { type: 'string' } },
|
|
295
|
-
onConflict: { type: 'string', enum: ['
|
|
295
|
+
onConflict: { type: 'string', enum: ['reuse', 'allowDuplicate'], description: 'Dedup policy (default reuse). reuse: find-or-create — attach an existing exact (source, type, target, name) match. allowDuplicate: create anyway, requires a justification.' },
|
|
296
296
|
justification: { type: 'string', description: 'Required when onConflict is allowDuplicate.' },
|
|
297
297
|
dryRun: { type: 'boolean', description: 'When true, validates and returns the result without writing to the graph. Default: false.' },
|
|
298
298
|
architecturePath: { type: 'string', description: `Default: ${DEFAULT_GRAPH_PATH}` },
|
|
@@ -338,7 +338,7 @@ const TOOLS = [
|
|
|
338
338
|
required: ['view'],
|
|
339
339
|
properties: {
|
|
340
340
|
view: { type: 'object' },
|
|
341
|
-
onConflict: { type: 'string', enum: ['
|
|
341
|
+
onConflict: { type: 'string', enum: ['reuse', 'allowDuplicate'], description: 'Dedup policy (default reuse). reuse: attach an existing exact (parent, view name) match. allowDuplicate: create anyway, requires a justification.' },
|
|
342
342
|
justification: { type: 'string', description: 'Required when onConflict is allowDuplicate.' },
|
|
343
343
|
dryRun: { type: 'boolean', description: 'When true, validates and returns the result without writing to the graph. Default: false.' },
|
|
344
344
|
architecturePath: { type: 'string', description: `Default: ${DEFAULT_GRAPH_PATH}` },
|
|
@@ -480,7 +480,7 @@ function mutationInputSchema() {
|
|
|
480
480
|
view_ids: { type: 'array', minItems: 1, items: { type: 'string' } },
|
|
481
481
|
element_ids: { type: 'array', items: { type: 'string' } },
|
|
482
482
|
relationship_ids: { type: 'array', items: { type: 'string' } },
|
|
483
|
-
onConflict: { type: 'string', enum: ['
|
|
483
|
+
onConflict: { type: 'string', enum: ['reuse', 'allowDuplicate'], description: 'Dedup policy for add* mutations. reuse (default): find-or-create — attach an existing exact-natural-key match instead of creating a duplicate; a same-type semantic near-duplicate also blocks creation unless allowDuplicate is set. allowDuplicate: create a new object even if a duplicate exists, requires a non-empty justification.' },
|
|
484
484
|
justification: { type: 'string', description: 'Required when onConflict is allowDuplicate; recorded as the reason a semantically-equal duplicate is intentionally created.' },
|
|
485
485
|
},
|
|
486
486
|
additionalProperties: false,
|
|
@@ -1169,7 +1169,7 @@ function mergeAttributesPatch(existing, patchEntries) {
|
|
|
1169
1169
|
return result;
|
|
1170
1170
|
}
|
|
1171
1171
|
|
|
1172
|
-
const DUPLICATE_CONFLICT_POLICIES = new Set(['
|
|
1172
|
+
const DUPLICATE_CONFLICT_POLICIES = new Set(['reuse', 'allowDuplicate']);
|
|
1173
1173
|
|
|
1174
1174
|
// L0 dedup gate: normalize a name to a stable natural-key component. NFKC folds
|
|
1175
1175
|
// full-width forms, whitespace is collapsed, and case is folded, so " Widget ",
|
|
@@ -1221,27 +1221,19 @@ function findDuplicateViews(views, view) {
|
|
|
1221
1221
|
}
|
|
1222
1222
|
|
|
1223
1223
|
// Resolve an add against its exact natural-key candidates under the caller's
|
|
1224
|
-
// onConflict policy:
|
|
1225
|
-
//
|
|
1226
|
-
//
|
|
1224
|
+
// onConflict policy: reuse (default) is find-or-create (attach the existing
|
|
1225
|
+
// object); allowDuplicate creates only with a non-empty justification. Semantic
|
|
1226
|
+
// similarity is handled separately (the semantic dedup gate), never here.
|
|
1227
1227
|
function resolveDuplicateConflict(options, candidates) {
|
|
1228
1228
|
const onConflict = options.onConflict === undefined || options.onConflict === null
|
|
1229
|
-
? '
|
|
1229
|
+
? 'reuse'
|
|
1230
1230
|
: options.onConflict;
|
|
1231
1231
|
if (!DUPLICATE_CONFLICT_POLICIES.has(onConflict)) {
|
|
1232
|
-
throw new Error(`onConflict must be one of
|
|
1232
|
+
throw new Error(`onConflict must be one of reuse, allowDuplicate (got '${onConflict}')`);
|
|
1233
1233
|
}
|
|
1234
1234
|
if (candidates.length === 0) {
|
|
1235
1235
|
return { action: 'create' };
|
|
1236
1236
|
}
|
|
1237
|
-
if (onConflict === 'fail') {
|
|
1238
|
-
const error = new Error(
|
|
1239
|
-
`Duplicate ${options.label} already exists (${candidates.map(candidate => candidate.id).join(', ')}). ` +
|
|
1240
|
-
'Reuse it with onConflict:"reuse", or pass onConflict:"allowDuplicate" with a non-empty justification to create a new one.',
|
|
1241
|
-
);
|
|
1242
|
-
error.duplicateConflicts = candidates;
|
|
1243
|
-
throw error;
|
|
1244
|
-
}
|
|
1245
1237
|
if (onConflict === 'reuse') {
|
|
1246
1238
|
return { action: 'reuse', existing: candidates[0] };
|
|
1247
1239
|
}
|
|
@@ -1527,11 +1519,18 @@ function applyMutations(document, mutations) {
|
|
|
1527
1519
|
requestedId: mutation.view.view_id,
|
|
1528
1520
|
});
|
|
1529
1521
|
} else {
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1522
|
+
const newView = clone(mutation.view);
|
|
1523
|
+
if (Array.isArray(newView.included_elements)) {
|
|
1524
|
+
newView.included_elements = addUnique([], newView.included_elements);
|
|
1525
|
+
}
|
|
1526
|
+
if (Array.isArray(newView.included_relationships)) {
|
|
1527
|
+
newView.included_relationships = addUnique([], newView.included_relationships);
|
|
1528
|
+
}
|
|
1529
|
+
nextDocument.views.push(newView);
|
|
1530
|
+
upsertSubdiagramViewIntoElement(nextDocument, newView.parent_element_id, newView);
|
|
1531
|
+
touchedViewIds.add(newView.view_id);
|
|
1532
|
+
viewLimitCheckIds.add(newView.view_id);
|
|
1533
|
+
mutationSummaries.push({ type: mutation.type, id: newView.view_id });
|
|
1535
1534
|
}
|
|
1536
1535
|
continue;
|
|
1537
1536
|
}
|
|
@@ -1546,7 +1545,14 @@ function applyMutations(document, mutations) {
|
|
|
1546
1545
|
}
|
|
1547
1546
|
const oldParentId = view.parent_element_id;
|
|
1548
1547
|
const oldViewId = view.view_id;
|
|
1549
|
-
|
|
1548
|
+
const patch = clone(mutation.patch);
|
|
1549
|
+
if (Array.isArray(patch.included_elements)) {
|
|
1550
|
+
patch.included_elements = addUnique([], patch.included_elements);
|
|
1551
|
+
}
|
|
1552
|
+
if (Array.isArray(patch.included_relationships)) {
|
|
1553
|
+
patch.included_relationships = addUnique([], patch.included_relationships);
|
|
1554
|
+
}
|
|
1555
|
+
Object.assign(view, patch);
|
|
1550
1556
|
if (oldParentId !== view.parent_element_id) {
|
|
1551
1557
|
removeSubdiagramViewFromElement(nextDocument, oldParentId, oldViewId);
|
|
1552
1558
|
}
|
|
@@ -1812,20 +1818,33 @@ async function buildMutationResult(context, mutations, write, dependencies) {
|
|
|
1812
1818
|
result.guidance = buildFailureGuidance(errors);
|
|
1813
1819
|
}
|
|
1814
1820
|
|
|
1815
|
-
//
|
|
1816
|
-
//
|
|
1817
|
-
//
|
|
1818
|
-
//
|
|
1819
|
-
|
|
1821
|
+
// Semantic dedup gate: a would-be element create is blocked when a same-type
|
|
1822
|
+
// semantic near-duplicate exists, unless the caller explicitly passed
|
|
1823
|
+
// onConflict:'allowDuplicate' (with justification). Never blocks on an
|
|
1824
|
+
// unavailable semantic backend. Both preview and apply surface the outcome.
|
|
1825
|
+
let semanticBlocked = false;
|
|
1820
1826
|
if (errors.length === 0) {
|
|
1821
1827
|
const createdAdds = selectCreatedElementAdds(mutations, mutationResult.mutationSummaries);
|
|
1822
1828
|
const semanticDedup = await buildSemanticDedupAdvisory(context, createdAdds, dependencies);
|
|
1823
1829
|
if (semanticDedup) {
|
|
1824
1830
|
result.semanticDedup = semanticDedup;
|
|
1831
|
+
const gate = evaluateSemanticDedupGate(semanticDedup, createdAdds);
|
|
1832
|
+
if (gate.blocked) {
|
|
1833
|
+
semanticBlocked = true;
|
|
1834
|
+
result.status = 'failed';
|
|
1835
|
+
result.after = beforeSummary;
|
|
1836
|
+
result.semanticConflicts = gate.conflicts;
|
|
1837
|
+
result.errors = gate.conflicts.map(entry => (
|
|
1838
|
+
`Semantic duplicate for element (type '${entry.requested.type}', name '${entry.requested.name}'): `
|
|
1839
|
+
+ `${entry.matches.map(match => `${match.id} (score ${match.score})`).join(', ')}. `
|
|
1840
|
+
+ 'Reuse the existing element (re-call with that element id), or pass onConflict:"allowDuplicate" with a justification to create a new one.'
|
|
1841
|
+
));
|
|
1842
|
+
result.guidance = addUnique(result.guidance || [], result.errors);
|
|
1843
|
+
}
|
|
1825
1844
|
}
|
|
1826
1845
|
}
|
|
1827
1846
|
|
|
1828
|
-
if (errors.length > 0 || !write) {
|
|
1847
|
+
if (errors.length > 0 || semanticBlocked || !write) {
|
|
1829
1848
|
return result;
|
|
1830
1849
|
}
|
|
1831
1850
|
|
|
@@ -2453,6 +2472,9 @@ function compactMutationResponse(payload) {
|
|
|
2453
2472
|
if (Array.isArray(payload.guidance)) {
|
|
2454
2473
|
compact.guidance = payload.guidance;
|
|
2455
2474
|
}
|
|
2475
|
+
if (Array.isArray(payload.semanticConflicts)) {
|
|
2476
|
+
compact.semanticConflicts = payload.semanticConflicts;
|
|
2477
|
+
}
|
|
2456
2478
|
}
|
|
2457
2479
|
return compact;
|
|
2458
2480
|
}
|
|
@@ -2665,15 +2687,16 @@ function memoryHitCard(element, maxDescLen) {
|
|
|
2665
2687
|
return card;
|
|
2666
2688
|
}
|
|
2667
2689
|
|
|
2668
|
-
//
|
|
2669
|
-
//
|
|
2670
|
-
//
|
|
2671
|
-
//
|
|
2672
|
-
//
|
|
2673
|
-
// degrades to an explicit status, never an error.
|
|
2690
|
+
// Semantic dedup gate (GraphDeduplication): for each element that would be
|
|
2691
|
+
// CREATED, search the WHOLE graph for same-type semantically near elements (a
|
|
2692
|
+
// large candidate window, strict threshold). A hit blocks the default create —
|
|
2693
|
+
// the caller must reuse an existing candidate or explicitly pass
|
|
2694
|
+
// onConflict:'allowDuplicate' with a justification. A missing/unavailable
|
|
2695
|
+
// semantic backend degrades to an explicit status, never an error.
|
|
2674
2696
|
const DEFAULT_SEMANTIC_DEDUP_THRESHOLD = 0.85;
|
|
2675
2697
|
const SEMANTIC_DEDUP_MAX_QUERIES = 3;
|
|
2676
|
-
const SEMANTIC_DEDUP_MAX_MATCHES =
|
|
2698
|
+
const SEMANTIC_DEDUP_MAX_MATCHES = 10;
|
|
2699
|
+
const SEMANTIC_DEDUP_TOP_K = 25;
|
|
2677
2700
|
|
|
2678
2701
|
function semanticDedupThreshold() {
|
|
2679
2702
|
const raw = process.env.ARGO_SEMANTIC_DEDUP_THRESHOLD
|
|
@@ -2717,6 +2740,29 @@ function selectCreatedElementAdds(mutations, mutationSummaries) {
|
|
|
2717
2740
|
));
|
|
2718
2741
|
}
|
|
2719
2742
|
|
|
2743
|
+
// Decide whether the semantic candidates block the create. A create is blocked
|
|
2744
|
+
// unless the caller explicitly opted into allowDuplicate (with justification).
|
|
2745
|
+
function evaluateSemanticDedupGate(advisory, mutations) {
|
|
2746
|
+
if (!advisory || advisory.status !== 'passed' || !Array.isArray(advisory.candidates)) {
|
|
2747
|
+
return { blocked: false, conflicts: [] };
|
|
2748
|
+
}
|
|
2749
|
+
const byId = new Map((Array.isArray(mutations) ? mutations : [])
|
|
2750
|
+
.filter(mutation => mutation && mutation.type === 'addElement' && mutation.element)
|
|
2751
|
+
.map(mutation => [mutation.element.id, mutation]));
|
|
2752
|
+
const conflicts = [];
|
|
2753
|
+
for (const entry of advisory.candidates) {
|
|
2754
|
+
if (!entry || !Array.isArray(entry.matches) || entry.matches.length === 0) {
|
|
2755
|
+
continue;
|
|
2756
|
+
}
|
|
2757
|
+
const mutation = byId.get(entry.requested && entry.requested.id);
|
|
2758
|
+
const overridden = mutation && mutation.onConflict === 'allowDuplicate';
|
|
2759
|
+
if (!overridden) {
|
|
2760
|
+
conflicts.push(entry);
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
return { blocked: conflicts.length > 0, conflicts };
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2720
2766
|
async function buildSemanticDedupAdvisory(context, mutations, dependencies) {
|
|
2721
2767
|
if (process.env.ARGO_MCP_SEMANTIC_DEDUP === '0') {
|
|
2722
2768
|
return undefined;
|
|
@@ -2755,7 +2801,7 @@ async function buildSemanticDedupAdvisory(context, mutations, dependencies) {
|
|
|
2755
2801
|
const intent = [element.type, element.name, element.description]
|
|
2756
2802
|
.filter(part => typeof part === 'string' && part.trim() !== '')
|
|
2757
2803
|
.join(' ');
|
|
2758
|
-
const retrieved = await journey.query({ purpose: 'general', intent });
|
|
2804
|
+
const retrieved = await journey.query({ purpose: 'general', intent, topK: SEMANTIC_DEDUP_TOP_K });
|
|
2759
2805
|
const source = retrieved && (retrieved.result || retrieved.document) || retrieved;
|
|
2760
2806
|
const subset = buildCanonicalSemanticDocumentSubset(source, context.document);
|
|
2761
2807
|
const elements = subset && subset.status === 'passed' && subset.document
|
|
@@ -2765,7 +2811,6 @@ async function buildSemanticDedupAdvisory(context, mutations, dependencies) {
|
|
|
2765
2811
|
.filter(candidate => candidate && candidate.id !== element.id && typeof candidate.semanticScore === 'number')
|
|
2766
2812
|
.filter(candidate => !element.type || candidate.type === element.type)
|
|
2767
2813
|
.filter(candidate => candidate.semanticScore >= threshold)
|
|
2768
|
-
.filter(candidate => targetMemberIds.size === 0 || targetMemberIds.has(candidate.id))
|
|
2769
2814
|
.sort((left, right) => right.semanticScore - left.semanticScore)
|
|
2770
2815
|
.slice(0, SEMANTIC_DEDUP_MAX_MATCHES)
|
|
2771
2816
|
.map(candidate => ({
|
|
@@ -2789,7 +2834,7 @@ async function buildSemanticDedupAdvisory(context, mutations, dependencies) {
|
|
|
2789
2834
|
status: 'passed',
|
|
2790
2835
|
advisoryOnly: true,
|
|
2791
2836
|
threshold,
|
|
2792
|
-
scope: '
|
|
2837
|
+
scope: 'whole graph, same type',
|
|
2793
2838
|
candidates,
|
|
2794
2839
|
has_suggestions: candidates.some(entry => entry.matches.length > 0),
|
|
2795
2840
|
};
|
|
@@ -4128,6 +4173,7 @@ module.exports = {
|
|
|
4128
4173
|
applyMutations,
|
|
4129
4174
|
buildSemanticDedupAdvisory,
|
|
4130
4175
|
selectCreatedElementAdds,
|
|
4176
|
+
evaluateSemanticDedupGate,
|
|
4131
4177
|
callTool,
|
|
4132
4178
|
compactMutationResponse,
|
|
4133
4179
|
createDefaultCanonicalSemanticInitComposition,
|
package/package.json
CHANGED