archgraph-argo 0.17.0 → 0.18.0
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 +5 -1
- package/install-argo.ps1 +32 -6
- 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',
|
|
@@ -3839,7 +3843,7 @@ async function createDefaultProductionSemanticRuntime(options = {}) {
|
|
|
3839
3843
|
try {
|
|
3840
3844
|
vectors.push(Object.freeze({
|
|
3841
3845
|
canonicalIdentity: record.canonicalIdentity,
|
|
3842
|
-
vector: Object.freeze(await providerClient.embed(
|
|
3846
|
+
vector: Object.freeze(await providerClient.embed(buildSemanticRecordText(record.channel, record.canonicalObject))),
|
|
3843
3847
|
}));
|
|
3844
3848
|
} catch (error) {
|
|
3845
3849
|
failures.push(Object.freeze({
|
package/install-argo.ps1
CHANGED
|
@@ -215,6 +215,30 @@ function Register-OpenCodePlugin {
|
|
|
215
215
|
[System.IO.File]::WriteAllText($ConfigPath, $json, (New-Object System.Text.UTF8Encoding $false))
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
function Get-ArchGraphRuleBlockEndIndex {
|
|
219
|
+
# Locate the end of a previously merged ArchGraph rule block. The rule's
|
|
220
|
+
# final section tag changed over versions: earlier rules ended with
|
|
221
|
+
# </ToolsGuideline>, the current layout ends with </Attention> and places
|
|
222
|
+
# <ToolsGuideline> earlier. Pick whichever final tag occurs LAST at/after
|
|
223
|
+
# the block marker, so the merge stays correct across rule reorderings and
|
|
224
|
+
# also repairs a previously duplicated tail.
|
|
225
|
+
param([string]$Text, [int]$From)
|
|
226
|
+
$idx = -1
|
|
227
|
+
foreach ($tag in @('</ToolsGuideline>', '</Attention>')) {
|
|
228
|
+
$i = $Text.LastIndexOf($tag)
|
|
229
|
+
if ($i -ge $From -and $i -gt $idx) { $idx = $i }
|
|
230
|
+
}
|
|
231
|
+
return $idx
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function Get-ArchGraphRuleBlockEndTagLength {
|
|
235
|
+
param([string]$Text, [int]$Index)
|
|
236
|
+
if ($Index -lt 0) { return 0 }
|
|
237
|
+
if ($Text.Substring($Index).StartsWith('</ToolsGuideline>')) { return '</ToolsGuideline>'.Length }
|
|
238
|
+
if ($Text.Substring($Index).StartsWith('</Attention>')) { return '</Attention>'.Length }
|
|
239
|
+
return 0
|
|
240
|
+
}
|
|
241
|
+
|
|
218
242
|
function Add-AgentsRule {
|
|
219
243
|
param(
|
|
220
244
|
[string]$AgentsPath,
|
|
@@ -231,18 +255,18 @@ function Add-AgentsRule {
|
|
|
231
255
|
# An existing ArchGraph rules block is present. Replace it with the
|
|
232
256
|
# current rule content while preserving any unrelated content that
|
|
233
257
|
# surrounds it (e.g. user-authored OpenCode instructions).
|
|
234
|
-
$endTag = '</ToolsGuideline>'
|
|
235
258
|
$markerIdx = $existing.IndexOf($marker)
|
|
236
259
|
if ($markerIdx -lt 0) { $markerIdx = 0 }
|
|
237
260
|
$startIdx = $existing.LastIndexOf('---', $markerIdx)
|
|
238
261
|
if ($startIdx -lt 0) { $startIdx = 0 }
|
|
239
|
-
$endIdx = $existing
|
|
262
|
+
$endIdx = Get-ArchGraphRuleBlockEndIndex -Text $existing -From $markerIdx
|
|
240
263
|
|
|
241
264
|
$before = $existing.Substring(0, $startIdx).TrimEnd()
|
|
242
265
|
if ($endIdx -lt 0) {
|
|
243
266
|
$combined = $ruleContent
|
|
244
267
|
} else {
|
|
245
|
-
$
|
|
268
|
+
$endLen = Get-ArchGraphRuleBlockEndTagLength -Text $existing -Index $endIdx
|
|
269
|
+
$after = $existing.Substring($endIdx + $endLen)
|
|
246
270
|
$combined = $before
|
|
247
271
|
if ($combined.Length -gt 0) { $combined += "`n`n" }
|
|
248
272
|
$combined += $ruleContent
|
|
@@ -301,13 +325,15 @@ function Write-ArchGraphRuleBlock {
|
|
|
301
325
|
if (Test-Path $DestPath) {
|
|
302
326
|
$existing = Get-Content $DestPath -Raw -Encoding UTF8
|
|
303
327
|
if ($existing -like "*$marker*") {
|
|
304
|
-
$endTag = '</ToolsGuideline>'
|
|
305
328
|
$startIdx = $existing.IndexOf($marker)
|
|
306
329
|
if ($startIdx -lt 0) { $startIdx = 0 }
|
|
307
|
-
$endIdx = $existing
|
|
330
|
+
$endIdx = Get-ArchGraphRuleBlockEndIndex -Text $existing -From $startIdx
|
|
308
331
|
$before = $existing.Substring(0, $startIdx).TrimEnd()
|
|
309
332
|
$after = ''
|
|
310
|
-
if ($endIdx -ge 0) {
|
|
333
|
+
if ($endIdx -ge 0) {
|
|
334
|
+
$endLen = Get-ArchGraphRuleBlockEndTagLength -Text $existing -Index $endIdx
|
|
335
|
+
$after = $existing.Substring($endIdx + $endLen).TrimStart()
|
|
336
|
+
}
|
|
311
337
|
$combined = $before
|
|
312
338
|
if ($combined.Length -gt 0) { $combined += "`n`n" }
|
|
313
339
|
$combined += $ruleContent
|
package/package.json
CHANGED