archgraph-argo 0.17.1 → 0.18.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/scripts/graph-rag/mutationEmbeddingVectorLifecycle.js +6 -2
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticBackfill.js +4 -1
- package/argo/scripts/graph-rag/semanticRecordText.js +75 -0
- package/argo/scripts/systemarchitecture-mcp-server.js +30 -4
- package/package.json +1 -1
|
@@ -15,6 +15,9 @@ const {
|
|
|
15
15
|
const {
|
|
16
16
|
createLiveEmbeddingProviderClient,
|
|
17
17
|
} = require('./liveEmbeddingProviderClient.js');
|
|
18
|
+
const {
|
|
19
|
+
buildSemanticRecordText,
|
|
20
|
+
} = require('./semanticRecordText.js');
|
|
18
21
|
const {
|
|
19
22
|
createProductionSemanticNeo4jAdapter,
|
|
20
23
|
} = require('./semantic-persistence/productionSemanticNeo4jAdapter.js');
|
|
@@ -218,7 +221,7 @@ function createProductionPersistentLifecycleDependencies(options = {}) {
|
|
|
218
221
|
provider: Object.freeze({
|
|
219
222
|
async embed(content) {
|
|
220
223
|
const active = await requireResources();
|
|
221
|
-
return active.provider.embed(JSON.stringify(content));
|
|
224
|
+
return active.provider.embed(typeof content === 'string' ? content : JSON.stringify(content));
|
|
222
225
|
},
|
|
223
226
|
}),
|
|
224
227
|
projectionStore: Object.freeze({
|
|
@@ -513,7 +516,8 @@ function buildPersistentWork(canonicalWrite, configuration, versions) {
|
|
|
513
516
|
const tombstones = [];
|
|
514
517
|
for (const definition of definitions) {
|
|
515
518
|
for (const objectId of definition.ids) {
|
|
516
|
-
const
|
|
519
|
+
const object = definition.entries.find(entry => entry && entry[definition.idField] === objectId);
|
|
520
|
+
const content = object ? buildSemanticRecordText(definition.channel, object) : undefined;
|
|
517
521
|
const base = {
|
|
518
522
|
objectId,
|
|
519
523
|
canonicalIdentity: `${definition.channel}:${objectId}`,
|
|
@@ -2,6 +2,9 @@ const crypto = require('node:crypto');
|
|
|
2
2
|
const {
|
|
3
3
|
evaluateEmbeddingQualification,
|
|
4
4
|
} = require('../embeddingQualificationGate.js');
|
|
5
|
+
const {
|
|
6
|
+
buildSemanticRecordText,
|
|
7
|
+
} = require('../semanticRecordText.js');
|
|
5
8
|
|
|
6
9
|
const CHANNELS = Object.freeze(['Element', 'ArchitectureRelationship', 'View']);
|
|
7
10
|
const CHANNEL_SOURCES = Object.freeze({
|
|
@@ -152,7 +155,7 @@ async function processChannel(options) {
|
|
|
152
155
|
|
|
153
156
|
function buildSemanticRecord(record, vector, options) {
|
|
154
157
|
const contentHash = crypto.createHash('sha256')
|
|
155
|
-
.update(
|
|
158
|
+
.update(buildSemanticRecordText(record.channel, record.canonicalObject))
|
|
156
159
|
.digest('hex');
|
|
157
160
|
return Object.freeze({
|
|
158
161
|
canonicalIdentity: record.canonicalIdentity,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Curated embedding text for a canonical semantic record.
|
|
4
|
+
//
|
|
5
|
+
// The canonical JSON (design/KG/SystemArchitecture.json) remains the structural
|
|
6
|
+
// source of truth. The vector index, however, only encodes the human-meaningful
|
|
7
|
+
// fields of each record — never ids, endpoint ids, view member-id arrays, or
|
|
8
|
+
// JSON syntax — because embedding the raw object measurably dilutes retrieval
|
|
9
|
+
// (most severely for Views, whose member-id arrays dominate the text, and for
|
|
10
|
+
// Relationships, which are mostly structural). Both the full backfill and the
|
|
11
|
+
// incremental mutation lifecycle MUST embed through this single composer so the
|
|
12
|
+
// indexed text is identical across both paths.
|
|
13
|
+
|
|
14
|
+
function meaningful(value) {
|
|
15
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function formatAttributes(attributes) {
|
|
19
|
+
if (!Array.isArray(attributes)) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
const lines = [];
|
|
23
|
+
for (const attribute of attributes) {
|
|
24
|
+
if (!attribute || !meaningful(attribute.name)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const detail = meaningful(attribute.value)
|
|
28
|
+
? attribute.value
|
|
29
|
+
: (meaningful(attribute.description) ? attribute.description : '');
|
|
30
|
+
lines.push(detail ? `${attribute.name.trim()}: ${detail.trim()}` : attribute.name.trim());
|
|
31
|
+
}
|
|
32
|
+
return lines;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildSemanticRecordText(channel, object) {
|
|
36
|
+
const record = object && typeof object === 'object' ? object : {};
|
|
37
|
+
const lines = [];
|
|
38
|
+
const add = value => {
|
|
39
|
+
if (meaningful(value)) {
|
|
40
|
+
lines.push(value.trim());
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
if (channel === 'ArchitectureRelationship' || channel === 'Relationship') {
|
|
45
|
+
add(record.statement);
|
|
46
|
+
add(record.name);
|
|
47
|
+
add(record.description);
|
|
48
|
+
add(record.document);
|
|
49
|
+
lines.push(...formatAttributes(record.attributes));
|
|
50
|
+
return lines.join('\n');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (channel === 'View') {
|
|
54
|
+
add(record.view_name);
|
|
55
|
+
add(record.description);
|
|
56
|
+
return lines.join('\n');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Element (default channel)
|
|
60
|
+
add(record.type);
|
|
61
|
+
add(record.name);
|
|
62
|
+
add(record.alias);
|
|
63
|
+
add(record.description);
|
|
64
|
+
lines.push(...formatAttributes(record.attributes));
|
|
65
|
+
if (Array.isArray(record.testcases)) {
|
|
66
|
+
for (const testcase of record.testcases) {
|
|
67
|
+
if (testcase && meaningful(testcase.description)) {
|
|
68
|
+
lines.push(testcase.description.trim());
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return lines.join('\n');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = { buildSemanticRecordText };
|
|
@@ -9,6 +9,10 @@ const {
|
|
|
9
9
|
resolveCallWorkspaceRoot,
|
|
10
10
|
} = require('./argo-paths.js');
|
|
11
11
|
|
|
12
|
+
const {
|
|
13
|
+
buildSemanticRecordText,
|
|
14
|
+
} = require('./graph-rag/semanticRecordText.js');
|
|
15
|
+
|
|
12
16
|
const DEFAULT_GRAPH_PATH = 'design/KG/SystemArchitecture.json';
|
|
13
17
|
const LEGAL_QUERY_PURPOSES = new Set([
|
|
14
18
|
'general',
|
|
@@ -1809,9 +1813,13 @@ async function buildMutationResult(context, mutations, write, dependencies) {
|
|
|
1809
1813
|
}
|
|
1810
1814
|
|
|
1811
1815
|
// L1 advisory: semantic near-duplicate suggestions for element adds. Never
|
|
1812
|
-
// blocks or fails the write; preview and apply both surface it.
|
|
1816
|
+
// blocks or fails the write; preview and apply both surface it. Only adds the
|
|
1817
|
+
// applied mutation actually CREATED are advised — a reuse that found an exact
|
|
1818
|
+
// natural-key match creates nothing and is skipped, while a reuse that fell
|
|
1819
|
+
// through to creation is advised like any other new element.
|
|
1813
1820
|
if (errors.length === 0) {
|
|
1814
|
-
const
|
|
1821
|
+
const createdAdds = selectCreatedElementAdds(mutations, mutationResult.mutationSummaries);
|
|
1822
|
+
const semanticDedup = await buildSemanticDedupAdvisory(context, createdAdds, dependencies);
|
|
1815
1823
|
if (semanticDedup) {
|
|
1816
1824
|
result.semanticDedup = semanticDedup;
|
|
1817
1825
|
}
|
|
@@ -2691,6 +2699,24 @@ function collectViewMemberElementIds(document, viewIds) {
|
|
|
2691
2699
|
return ids;
|
|
2692
2700
|
}
|
|
2693
2701
|
|
|
2702
|
+
// Only element adds that the applied mutation actually CREATED are subject to
|
|
2703
|
+
// the L1 advisory. A reuse that found an exact natural-key match creates nothing
|
|
2704
|
+
// (created:false) and is skipped; a reuse that fell through to creation
|
|
2705
|
+
// (created:true, id === requested id) is advised like any other new element.
|
|
2706
|
+
function selectCreatedElementAdds(mutations, mutationSummaries) {
|
|
2707
|
+
const createdIds = new Set(
|
|
2708
|
+
(Array.isArray(mutationSummaries) ? mutationSummaries : [])
|
|
2709
|
+
.filter(summary => summary && summary.type === 'addElement' && summary.created === true)
|
|
2710
|
+
.map(summary => summary.id),
|
|
2711
|
+
);
|
|
2712
|
+
return (Array.isArray(mutations) ? mutations : []).filter(mutation => (
|
|
2713
|
+
mutation
|
|
2714
|
+
&& mutation.type === 'addElement'
|
|
2715
|
+
&& mutation.element
|
|
2716
|
+
&& createdIds.has(mutation.element.id)
|
|
2717
|
+
));
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2694
2720
|
async function buildSemanticDedupAdvisory(context, mutations, dependencies) {
|
|
2695
2721
|
if (process.env.ARGO_MCP_SEMANTIC_DEDUP === '0') {
|
|
2696
2722
|
return undefined;
|
|
@@ -2701,7 +2727,6 @@ async function buildSemanticDedupAdvisory(context, mutations, dependencies) {
|
|
|
2701
2727
|
&& mutation.element
|
|
2702
2728
|
&& typeof mutation.element.name === 'string'
|
|
2703
2729
|
&& mutation.element.name.trim() !== ''
|
|
2704
|
-
&& mutation.onConflict !== 'reuse'
|
|
2705
2730
|
));
|
|
2706
2731
|
if (addedElements.length === 0) {
|
|
2707
2732
|
return undefined;
|
|
@@ -3839,7 +3864,7 @@ async function createDefaultProductionSemanticRuntime(options = {}) {
|
|
|
3839
3864
|
try {
|
|
3840
3865
|
vectors.push(Object.freeze({
|
|
3841
3866
|
canonicalIdentity: record.canonicalIdentity,
|
|
3842
|
-
vector: Object.freeze(await providerClient.embed(
|
|
3867
|
+
vector: Object.freeze(await providerClient.embed(buildSemanticRecordText(record.channel, record.canonicalObject))),
|
|
3843
3868
|
}));
|
|
3844
3869
|
} catch (error) {
|
|
3845
3870
|
failures.push(Object.freeze({
|
|
@@ -4102,6 +4127,7 @@ module.exports = {
|
|
|
4102
4127
|
TOOLS,
|
|
4103
4128
|
applyMutations,
|
|
4104
4129
|
buildSemanticDedupAdvisory,
|
|
4130
|
+
selectCreatedElementAdds,
|
|
4105
4131
|
callTool,
|
|
4106
4132
|
compactMutationResponse,
|
|
4107
4133
|
createDefaultCanonicalSemanticInitComposition,
|
package/package.json
CHANGED