archgraph-argo 0.26.1 → 0.27.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/README.md +13 -4
- package/argo/.env.example +170 -163
- package/argo/plugins/argo-wakeup.js +29 -27
- package/argo/rules/archgraph.instructions.md +189 -187
- package/argo/schema/argob-rules.json +12229 -0
- package/argo/schema/argob.config.json +20 -0
- package/argo/scripts/argo-mcp-server.js +67 -1
- package/argo/scripts/argob-schema.js +574 -0
- package/argo/scripts/ensureArgoHarnessEnvironment.js +14 -0
- package/argo/scripts/external-graph-query.js +162 -0
- package/argo/scripts/graph-rag/defaultSemanticRetrieval.js +0 -13
- package/argo/scripts/graph-rag/liveEmbeddingProviderConfig.js +6 -61
- package/argo/scripts/graph-semantics.js +266 -227
- package/argo/scripts/runArchitectureTests.js +14 -6
- package/argo/scripts/systemarchitecture-mcp-server.js +107 -48
- package/argo/scripts/validateSystemArchitecture.js +260 -253
- package/argo/scripts/validator-mcp-server.js +1 -1
- package/argo/skills/argo-init/SKILL.md +7 -9
- package/argo/skills/ea-human-reconcile/SKILL.md +2 -1
- package/dsh-argo-wakeup/index.js +2 -2
- package/dsh-argo-workspace/index.js +3 -2
- package/install-argo.ps1 +215 -186
- package/package.json +5 -2
|
@@ -6,7 +6,6 @@ const readline = require('node:readline');
|
|
|
6
6
|
const crypto = require('node:crypto');
|
|
7
7
|
|
|
8
8
|
const {
|
|
9
|
-
getArgoRoot,
|
|
10
9
|
resolveCallWorkspaceRoot,
|
|
11
10
|
} = require('./argo-paths.js');
|
|
12
11
|
|
|
@@ -132,6 +131,11 @@ const {
|
|
|
132
131
|
validateArchiMateEndpointMatrix,
|
|
133
132
|
validateViewElementLimits,
|
|
134
133
|
} = require('./graph-semantics.js');
|
|
134
|
+
const {
|
|
135
|
+
loadSchemaBundleAndOntology,
|
|
136
|
+
resolveSchemaBundle,
|
|
137
|
+
resolveTypeEnums,
|
|
138
|
+
} = require('./argob-schema.js');
|
|
135
139
|
const {
|
|
136
140
|
createProductionGraphRagRuntime,
|
|
137
141
|
} = require('./graph-rag/productionGraphRagRuntime.js');
|
|
@@ -168,6 +172,7 @@ const {
|
|
|
168
172
|
} = require('./neo4j-system-architecture-store.js');
|
|
169
173
|
|
|
170
174
|
const losslessWriteGate = require('./lossless-write-gate.js');
|
|
175
|
+
const { EXTERNAL_READ_TOOLS } = require('./external-graph-query.js');
|
|
171
176
|
|
|
172
177
|
const HANDLED_MUTATION_TYPES = new Set([
|
|
173
178
|
'addElement',
|
|
@@ -231,7 +236,7 @@ const TOOLS = [
|
|
|
231
236
|
},
|
|
232
237
|
{
|
|
233
238
|
name: 'previewSystemArchitectureMutation',
|
|
234
|
-
description: 'Use before apply for complex or risky changes. Performs a dry-run of one or more mutations, runs schema, graph, view, and
|
|
239
|
+
description: 'Use before apply for complex or risky changes. Performs a dry-run of one or more mutations, runs schema, graph, view, and modeling-language validation, and does not write the graph.',
|
|
235
240
|
inputSchema: mutationInputSchema(),
|
|
236
241
|
},
|
|
237
242
|
{
|
|
@@ -380,7 +385,7 @@ const TOOLS = [
|
|
|
380
385
|
},
|
|
381
386
|
{
|
|
382
387
|
name: 'queryNeo4jGraph',
|
|
383
|
-
description: 'Run a read-only Cypher query against the Neo4j structural projection of the intent architecture, or request the projection schema so an agent can construct its own Cypher. Pass {schema: true} to return node labels, relationship types, property keys, and the legal
|
|
388
|
+
description: 'Run a read-only Cypher query against the Neo4j structural projection of the intent architecture, or request the projection schema so an agent can construct its own Cypher. Pass {schema: true} to return node labels, relationship types, property keys, and the legal element/relationship type enums of the workspace-resolved schema bundle. Pass {cypher: "..."} to execute a read-only query; scope it with {graphKey: $graphKey}. Write clauses (CREATE/MERGE/DELETE/SET/REMOVE/DROP/LOAD CSV/FOREACH/IN TRANSACTIONS) are rejected.',
|
|
384
389
|
inputSchema: {
|
|
385
390
|
type: 'object',
|
|
386
391
|
properties: {
|
|
@@ -415,6 +420,13 @@ const WORKSPACE_ROOT_PARAM = Object.freeze({
|
|
|
415
420
|
description:
|
|
416
421
|
'Optional absolute workspace root for this call. When provided it is used as-is; otherwise the server launch directory is used.',
|
|
417
422
|
});
|
|
423
|
+
// Cross-project graph query: the 5 read tools accept an optional `projectId`;
|
|
424
|
+
// provided => routed to the federation center, omitted => the local workspace.
|
|
425
|
+
const PROJECT_ID_PARAM = Object.freeze({
|
|
426
|
+
type: 'string',
|
|
427
|
+
description:
|
|
428
|
+
'Optional external project id. When provided, the query is routed to the federation center to read that project\'s graph (this project must be registered in .argo/federation.json); omitted = the local workspace (unchanged).',
|
|
429
|
+
});
|
|
418
430
|
// Lossless write gate: every update/remove helper accepts an explicit loss
|
|
419
431
|
// acknowledgement (see lossless-write-gate.js). add* is purely additive and is
|
|
420
432
|
// left alone. Without the acknowledgement a shrinking text edit or a destructive
|
|
@@ -436,6 +448,9 @@ for (const tool of TOOLS) {
|
|
|
436
448
|
if (!Object.prototype.hasOwnProperty.call(inputSchema.properties, 'workspaceRoot')) {
|
|
437
449
|
inputSchema.properties.workspaceRoot = WORKSPACE_ROOT_PARAM;
|
|
438
450
|
}
|
|
451
|
+
if (EXTERNAL_READ_TOOLS.has(tool.name) && !Object.prototype.hasOwnProperty.call(inputSchema.properties, 'projectId')) {
|
|
452
|
+
inputSchema.properties.projectId = PROJECT_ID_PARAM;
|
|
453
|
+
}
|
|
439
454
|
if (tool.name && /^(update|remove)Architecture/.test(tool.name)) {
|
|
440
455
|
for (const [key, value] of Object.entries(LOSS_ACK_INPUT_PROPS)) {
|
|
441
456
|
if (!Object.prototype.hasOwnProperty.call(inputSchema.properties, key)) {
|
|
@@ -559,22 +574,7 @@ function normalizeRelativePath(value) {
|
|
|
559
574
|
}
|
|
560
575
|
|
|
561
576
|
function resolveSchemaPath(workspaceRoot) {
|
|
562
|
-
|
|
563
|
-
if (fs.existsSync(bundledSchemaPath)) {
|
|
564
|
-
return { absolutePath: bundledSchemaPath, relativePath: SCHEMA_PATH_CANDIDATES[0] };
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
for (const candidate of SCHEMA_PATH_CANDIDATES) {
|
|
568
|
-
const absolutePath = path.join(workspaceRoot, candidate);
|
|
569
|
-
if (fs.existsSync(absolutePath)) {
|
|
570
|
-
return { absolutePath, relativePath: candidate };
|
|
571
|
-
}
|
|
572
|
-
const bundledPath = path.resolve(__dirname, '..', '..', candidate);
|
|
573
|
-
if (fs.existsSync(bundledPath)) {
|
|
574
|
-
return { absolutePath: bundledPath, relativePath: candidate };
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
throw new Error(`Unable to locate SystemArchitecture schema. Checked: ${SCHEMA_PATH_CANDIDATES.join(', ')}`);
|
|
577
|
+
return resolveSchemaBundle(workspaceRoot).schema;
|
|
578
578
|
}
|
|
579
579
|
|
|
580
580
|
function readJson(filePath, label) {
|
|
@@ -588,13 +588,17 @@ function readJson(filePath, label) {
|
|
|
588
588
|
async function loadContext(args = {}) {
|
|
589
589
|
const workspaceRoot = resolveWorkspaceRoot(args);
|
|
590
590
|
const graphPath = resolveWorkspacePath(workspaceRoot, args.architecturePath || DEFAULT_GRAPH_PATH);
|
|
591
|
-
|
|
591
|
+
// Resolve the modeling language for THIS workspace: a repository that ships
|
|
592
|
+
// its own bundle under .argo/schema is validated against that schema.
|
|
593
|
+
const { bundle, ontology } = loadSchemaBundleAndOntology(workspaceRoot);
|
|
592
594
|
const context = {
|
|
593
595
|
workspaceRoot,
|
|
594
596
|
graphPath,
|
|
595
|
-
schemaPath,
|
|
597
|
+
schemaPath: bundle.schema,
|
|
598
|
+
schemaBundle: bundle,
|
|
599
|
+
ontology,
|
|
596
600
|
document: readJson(graphPath.absolutePath, graphPath.relativePath),
|
|
597
|
-
schema:
|
|
601
|
+
schema: bundle.schemaDocument,
|
|
598
602
|
};
|
|
599
603
|
context.neo4jSyncRecovery = await recoverNeo4jSyncIfNeeded({
|
|
600
604
|
architecturePath: graphPath.relativePath,
|
|
@@ -605,13 +609,19 @@ async function loadContext(args = {}) {
|
|
|
605
609
|
}
|
|
606
610
|
|
|
607
611
|
function validateDocument(document, schema, options = {}) {
|
|
612
|
+
const ontology = options.ontology;
|
|
608
613
|
const errors = [];
|
|
614
|
+
if (ontology && ontology.bundleValidation && ontology.bundleValidation.status === 'failed') {
|
|
615
|
+
errors.push(...ontology.bundleValidation.errors.map(error => `schema bundle: ${error}`));
|
|
616
|
+
}
|
|
609
617
|
validateAgainstSchema(document, schema, '#', errors, schema);
|
|
610
|
-
validateGraphSemantics(document, errors);
|
|
618
|
+
validateGraphSemantics(document, errors, ontology);
|
|
611
619
|
validateArchiMateEndpointMatrix(document, errors, {
|
|
620
|
+
ontology,
|
|
612
621
|
touchedRelationshipIds: options.touchedRelationshipIds,
|
|
613
622
|
});
|
|
614
623
|
validateViewElementLimits(document, errors, {
|
|
624
|
+
ontology,
|
|
615
625
|
touchedViewIds: options.validateAllViewElementLimits
|
|
616
626
|
? (document.views || []).map(view => view && view.view_id)
|
|
617
627
|
: options.touchedViewIds,
|
|
@@ -629,29 +639,42 @@ function validateDocument(document, schema, options = {}) {
|
|
|
629
639
|
// also NOT the semantic match surface — semantic retrieval embeds attributes and
|
|
630
640
|
// testcase descriptions (semanticRecordText.js), so semantic results keep them;
|
|
631
641
|
// and the focus element of an intent-element read always keeps its own.
|
|
642
|
+
function byteLenLocal(value) {
|
|
643
|
+
try { return Buffer.byteLength(JSON.stringify(value)); } catch (_) { return 0; }
|
|
644
|
+
}
|
|
645
|
+
|
|
632
646
|
function projectAgentFields(record, opts = {}) {
|
|
633
647
|
const value = clone(record);
|
|
634
648
|
let omittedAttributes = 0;
|
|
635
649
|
let omittedTestcases = 0;
|
|
650
|
+
let omittedBytes = 0;
|
|
636
651
|
if (!opts.includeAttributes && Array.isArray(value.attributes) && value.attributes.length) {
|
|
637
652
|
omittedAttributes = value.attributes.length;
|
|
653
|
+
omittedBytes += byteLenLocal(value.attributes);
|
|
638
654
|
delete value.attributes;
|
|
639
655
|
}
|
|
640
656
|
if (!opts.includeTestcases && Array.isArray(value.testcases) && value.testcases.length) {
|
|
641
657
|
omittedTestcases = value.testcases.length;
|
|
658
|
+
omittedBytes += byteLenLocal(value.testcases);
|
|
642
659
|
delete value.testcases;
|
|
643
660
|
}
|
|
644
|
-
return { value, omittedAttributes, omittedTestcases };
|
|
661
|
+
return { value, omittedAttributes, omittedTestcases, omittedBytes };
|
|
645
662
|
}
|
|
646
663
|
|
|
647
664
|
function buildAgentProjection(omitted) {
|
|
648
665
|
if (!omitted || (omitted.attributes === 0 && omitted.testcases === 0)) {
|
|
649
666
|
return null;
|
|
650
667
|
}
|
|
668
|
+
// Only attach the explanation when the omission actually saves more than the
|
|
669
|
+
// note costs, so trimming a read can NEVER make it larger (cross-scenario
|
|
670
|
+
// no-degradation). The opt-in flags are documented in the tool description.
|
|
671
|
+
if ((omitted.bytes || 0) < 160) {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
651
674
|
return {
|
|
652
675
|
attributesOmitted: omitted.attributes,
|
|
653
676
|
testcasesOmitted: omitted.testcases,
|
|
654
|
-
note: '
|
|
677
|
+
note: 'includeAttributes/includeTestcases=true to include (omitted by default; semantic hits keep them).',
|
|
655
678
|
};
|
|
656
679
|
}
|
|
657
680
|
|
|
@@ -666,7 +689,7 @@ function buildIntentElementContext(context, args = {}) {
|
|
|
666
689
|
const dependentDepth = normalizeDepth(args.dependentDepth, 1);
|
|
667
690
|
const associationDepth = Math.max(1, normalizeDepth(args.associationDepth, 1));
|
|
668
691
|
const associationNeighborDependencyDepth = normalizeDepth(args.associationNeighborDependencyDepth, 0);
|
|
669
|
-
const graphIndex = buildGraphIndex(context.document);
|
|
692
|
+
const graphIndex = buildGraphIndex(context.document, context.ontology);
|
|
670
693
|
const focusElement = focusResult.element;
|
|
671
694
|
const includedElementIds = new Set([focusElement.id]);
|
|
672
695
|
const includedRelationshipIds = new Set();
|
|
@@ -740,7 +763,7 @@ function buildIntentElementContext(context, args = {}) {
|
|
|
740
763
|
const subgraph = buildNativeSubgraph(context.document, includedElementIds, includedRelationshipIds);
|
|
741
764
|
const includeAttributes = args.includeAttributes === true;
|
|
742
765
|
const includeTestcases = args.includeTestcases === true;
|
|
743
|
-
const omitted = { attributes: 0, testcases: 0 };
|
|
766
|
+
const omitted = { attributes: 0, testcases: 0, bytes: 0 };
|
|
744
767
|
if (!(includeAttributes && includeTestcases)) {
|
|
745
768
|
subgraph.elements = subgraph.elements.map((element) => {
|
|
746
769
|
if (element.id === focusElement.id) {
|
|
@@ -749,11 +772,13 @@ function buildIntentElementContext(context, args = {}) {
|
|
|
749
772
|
const projected = projectAgentFields(element, { includeAttributes, includeTestcases });
|
|
750
773
|
omitted.attributes += projected.omittedAttributes;
|
|
751
774
|
omitted.testcases += projected.omittedTestcases;
|
|
775
|
+
omitted.bytes += projected.omittedBytes;
|
|
752
776
|
return projected.value;
|
|
753
777
|
});
|
|
754
778
|
subgraph.relationships = subgraph.relationships.map((relationship) => {
|
|
755
779
|
const projected = projectAgentFields(relationship, { includeAttributes });
|
|
756
780
|
omitted.attributes += projected.omittedAttributes;
|
|
781
|
+
omitted.bytes += projected.omittedBytes;
|
|
757
782
|
return projected.value;
|
|
758
783
|
});
|
|
759
784
|
}
|
|
@@ -851,11 +876,12 @@ function buildViewContext(context, args = {}) {
|
|
|
851
876
|
|
|
852
877
|
const includeAttributes = args.includeAttributes === true;
|
|
853
878
|
const includeTestcases = args.includeTestcases === true;
|
|
854
|
-
const omitted = { attributes: 0, testcases: 0 };
|
|
879
|
+
const omitted = { attributes: 0, testcases: 0, bytes: 0 };
|
|
855
880
|
const project = (record) => {
|
|
856
881
|
const projected = projectAgentFields(record, { includeAttributes, includeTestcases });
|
|
857
882
|
omitted.attributes += projected.omittedAttributes;
|
|
858
883
|
omitted.testcases += projected.omittedTestcases;
|
|
884
|
+
omitted.bytes += projected.omittedBytes;
|
|
859
885
|
return projected.value;
|
|
860
886
|
};
|
|
861
887
|
|
|
@@ -975,7 +1001,7 @@ function normalizeDepth(value, defaultValue) {
|
|
|
975
1001
|
return Math.floor(numericValue);
|
|
976
1002
|
}
|
|
977
1003
|
|
|
978
|
-
function buildGraphIndex(document) {
|
|
1004
|
+
function buildGraphIndex(document, ontology) {
|
|
979
1005
|
const relationshipById = new Map();
|
|
980
1006
|
const elementById = new Map((document.elements || []).map(element => [element.id, element]));
|
|
981
1007
|
const relationshipsByElementId = new Map();
|
|
@@ -984,9 +1010,19 @@ function buildGraphIndex(document) {
|
|
|
984
1010
|
addIndexedRelationship(relationshipsByElementId, relationship.source_id, relationship);
|
|
985
1011
|
addIndexedRelationship(relationshipsByElementId, relationship.target_id, relationship);
|
|
986
1012
|
}
|
|
987
|
-
|
|
1013
|
+
const deliveryDependencies = ontology && ontology.deliveryDependencies
|
|
1014
|
+
? ontology.deliveryDependencies
|
|
1015
|
+
: DEFAULT_DELIVERY_DEPENDENCIES;
|
|
1016
|
+
return { elementById, relationshipById, relationshipsByElementId, deliveryDependencies };
|
|
988
1017
|
}
|
|
989
1018
|
|
|
1019
|
+
// Dependency direction for the semantic-edge walk comes from the active schema
|
|
1020
|
+
// bundle's deliveryDependencies; this is the ArgoBument fallback by default.
|
|
1021
|
+
const DEFAULT_DELIVERY_DEPENDENCIES = Object.freeze({
|
|
1022
|
+
sourceDependsOnTarget: Object.freeze(['Access', 'Assignment', 'Specialization', 'Composition', 'Aggregation']),
|
|
1023
|
+
targetDependsOnSource: Object.freeze(['Serving', 'Realization', 'Flow', 'Triggering', 'Influence']),
|
|
1024
|
+
});
|
|
1025
|
+
|
|
990
1026
|
function addIndexedRelationship(index, elementId, relationship) {
|
|
991
1027
|
if (!index.has(elementId)) {
|
|
992
1028
|
index.set(elementId, []);
|
|
@@ -1041,8 +1077,9 @@ function resolveSemanticEdges(elementId, graphIndex) {
|
|
|
1041
1077
|
continue;
|
|
1042
1078
|
}
|
|
1043
1079
|
|
|
1044
|
-
const
|
|
1045
|
-
const
|
|
1080
|
+
const deliveryDependencies = graphIndex.deliveryDependencies || DEFAULT_DELIVERY_DEPENDENCIES;
|
|
1081
|
+
const sourceDependsOnTarget = deliveryDependencies.sourceDependsOnTarget.includes(relationshipType);
|
|
1082
|
+
const targetDependsOnSource = deliveryDependencies.targetDependsOnSource.includes(relationshipType);
|
|
1046
1083
|
if (sourceDependsOnTarget) {
|
|
1047
1084
|
edges.push({ kind: isSource ? 'dependency' : 'dependent', neighborId, relationship });
|
|
1048
1085
|
continue;
|
|
@@ -1914,7 +1951,7 @@ async function buildMutationResult(context, mutations, write, dependencies, loss
|
|
|
1914
1951
|
before: beforeSummary,
|
|
1915
1952
|
after: beforeSummary,
|
|
1916
1953
|
errors,
|
|
1917
|
-
guidance: buildFailureGuidance(errors),
|
|
1954
|
+
guidance: buildFailureGuidance(errors, context.ontology),
|
|
1918
1955
|
};
|
|
1919
1956
|
if (error && Array.isArray(error.duplicateConflicts) && error.duplicateConflicts.length > 0) {
|
|
1920
1957
|
failed.duplicateConflicts = error.duplicateConflicts;
|
|
@@ -1925,6 +1962,7 @@ async function buildMutationResult(context, mutations, write, dependencies, loss
|
|
|
1925
1962
|
return failed;
|
|
1926
1963
|
}
|
|
1927
1964
|
const errors = validateDocument(mutationResult.document, context.schema, {
|
|
1965
|
+
ontology: context.ontology,
|
|
1928
1966
|
touchedRelationshipIds: mutationResult.touchedRelationshipIds,
|
|
1929
1967
|
touchedViewIds: mutationResult.viewLimitCheckIds,
|
|
1930
1968
|
});
|
|
@@ -1944,7 +1982,7 @@ async function buildMutationResult(context, mutations, write, dependencies, loss
|
|
|
1944
1982
|
errors,
|
|
1945
1983
|
};
|
|
1946
1984
|
if (errors.length > 0) {
|
|
1947
|
-
result.guidance = buildFailureGuidance(errors);
|
|
1985
|
+
result.guidance = buildFailureGuidance(errors, context.ontology);
|
|
1948
1986
|
}
|
|
1949
1987
|
|
|
1950
1988
|
// Semantic dedup gate: a would-be element create is blocked when a same-type
|
|
@@ -2199,10 +2237,10 @@ function buildMutationEmbeddingLifecycleFailure(error, result) {
|
|
|
2199
2237
|
});
|
|
2200
2238
|
}
|
|
2201
2239
|
|
|
2202
|
-
function buildFailureGuidance(errors) {
|
|
2240
|
+
function buildFailureGuidance(errors, ontology) {
|
|
2203
2241
|
const guidance = [];
|
|
2204
2242
|
for (const error of errors || []) {
|
|
2205
|
-
addGuidanceForError(guidance, String(error));
|
|
2243
|
+
addGuidanceForError(guidance, String(error), ontology);
|
|
2206
2244
|
}
|
|
2207
2245
|
if (guidance.length === 0 && Array.isArray(errors) && errors.length > 0) {
|
|
2208
2246
|
guidance.push('Inspect the error text, call getSystemArchitecture with an explicit semantic query to refresh relevant ids, use getIntentElementContext for focused dependency context when needed, then retry with previewSystemArchitectureMutation before writing. Use an omitted-query full snapshot only when exact complete view membership is required.');
|
|
@@ -2210,15 +2248,17 @@ function buildFailureGuidance(errors) {
|
|
|
2210
2248
|
return guidance;
|
|
2211
2249
|
}
|
|
2212
2250
|
|
|
2213
|
-
function addGuidanceForError(guidance, error) {
|
|
2251
|
+
function addGuidanceForError(guidance, error, ontology) {
|
|
2252
|
+
const language = ontology && ontology.language ? ontology.language : 'ArchiMate 3.2';
|
|
2253
|
+
const matrixLabel = ontology && ontology.matrixErrorLabel ? ontology.matrixErrorLabel : 'ArchiMate 3.2 relationship matrix';
|
|
2214
2254
|
if (error.includes('mutation.view_ids must contain at least one view id')) {
|
|
2215
2255
|
pushUnique(guidance, 'Select the target view_ids explicitly. Prefer getSystemArchitecture with an explicit semantic query to find relevant views, then use getIntentElementContext for focused element dependencies when needed. Use a full snapshot only if exact complete view membership is required.');
|
|
2216
2256
|
}
|
|
2217
|
-
if (error.includes(
|
|
2218
|
-
pushUnique(guidance,
|
|
2257
|
+
if (error.includes(`violates ${matrixLabel}`)) {
|
|
2258
|
+
pushUnique(guidance, `Check relationship.type and the source and target element types against ${language}. If the intended meaning is still valid, choose a compliant relationship type or change the endpoint element types by remove-and-add.`);
|
|
2219
2259
|
}
|
|
2220
|
-
if (error.includes('uses unsupported
|
|
2221
|
-
pushUnique(guidance,
|
|
2260
|
+
if (error.includes('uses unsupported') && error.includes('relationship type')) {
|
|
2261
|
+
pushUnique(guidance, `Use relationship.type for the relationship type and choose one of the schema-supported ${language} relationship types.`);
|
|
2222
2262
|
}
|
|
2223
2263
|
if (error.includes('id cannot be updated') || error.includes('type cannot be updated')) {
|
|
2224
2264
|
pushUnique(guidance, 'Do not patch immutable identity or type fields. To change an id or type, remove the existing element or relationship, then add the replacement with the desired id or type.');
|
|
@@ -2227,10 +2267,16 @@ function addGuidanceForError(guidance, error) {
|
|
|
2227
2267
|
pushUnique(guidance, 'Every element and relationship must belong to at least one view. Add it with view_ids, or add the existing object to an appropriate view before validating again.');
|
|
2228
2268
|
}
|
|
2229
2269
|
if (error.includes('must declare parent_element_id') || error.includes('top-level view')) {
|
|
2230
|
-
|
|
2270
|
+
const rootViewName = ontology && ontology.invariants && ontology.invariants.rootViewName
|
|
2271
|
+
? ontology.invariants.rootViewName
|
|
2272
|
+
: 'SystemArchitecture';
|
|
2273
|
+
pushUnique(guidance, `Keep exactly one top-level view${rootViewName ? ` named ${rootViewName}` : ''}. For any sub-view, set parent_element_id to an existing element and keep parent_element_name aligned with that element name.`);
|
|
2231
2274
|
}
|
|
2232
|
-
if (error.includes('must contain at most
|
|
2233
|
-
|
|
2275
|
+
if (error.includes('must contain at most') && error.includes('elements')) {
|
|
2276
|
+
const maxElements = ontology && ontology.invariants && ontology.invariants.maxElementsPerView !== undefined && ontology.invariants.maxElementsPerView !== null
|
|
2277
|
+
? ontology.invariants.maxElementsPerView
|
|
2278
|
+
: 15;
|
|
2279
|
+
pushUnique(guidance, `Do not force more than ${maxElements} included_elements into one view. Pause and think about layered architecture: split the view into layered sub-views, attach each sub-view with parent_element_id, and move lower-level elements into the appropriate child view before retrying.`);
|
|
2234
2280
|
}
|
|
2235
2281
|
if (error.includes('does not exist') || error.includes('references missing')) {
|
|
2236
2282
|
pushUnique(guidance, 'Refresh current ids with getSystemArchitecture semantic query first, then call getIntentElementContext for any returned element that needs dependency context. Do not guess ids; use existing element, relationship, and view ids or create missing objects first.');
|
|
@@ -3086,13 +3132,25 @@ async function queryNeo4jGraphTool(args = {}) {
|
|
|
3086
3132
|
function queryNeo4jGraphSchemaResult(architecturePath, workspaceRoot) {
|
|
3087
3133
|
const schema = buildNeo4jGraphSchema(architecturePath);
|
|
3088
3134
|
let typeEnums = {};
|
|
3135
|
+
let schemaBundleInfo = {};
|
|
3089
3136
|
try {
|
|
3090
3137
|
const resolvedRoot = workspaceRoot || resolveWorkspaceRoot({ architecturePath });
|
|
3091
|
-
const
|
|
3092
|
-
const
|
|
3138
|
+
const { bundle, ontology } = loadSchemaBundleAndOntology(resolvedRoot);
|
|
3139
|
+
const enums = resolveTypeEnums(bundle);
|
|
3093
3140
|
typeEnums = {
|
|
3094
|
-
archimateElementTypes:
|
|
3095
|
-
archimateRelationshipTypes:
|
|
3141
|
+
archimateElementTypes: enums.elementTypes,
|
|
3142
|
+
archimateRelationshipTypes: enums.relationshipTypes,
|
|
3143
|
+
schemaLanguage: (bundle.config && bundle.config.language) || null,
|
|
3144
|
+
schemaKind: bundle.kind,
|
|
3145
|
+
schemaDialect: ontology.dialect,
|
|
3146
|
+
actorElementType: ontology.actorElementType,
|
|
3147
|
+
bundleValidation: ontology.bundleValidation,
|
|
3148
|
+
};
|
|
3149
|
+
schemaBundleInfo = {
|
|
3150
|
+
schemaPath: bundle.schema.relativePath,
|
|
3151
|
+
schemaKind: bundle.kind,
|
|
3152
|
+
schemaDir: bundle.relativeDir,
|
|
3153
|
+
guidePath: bundle.guidePath ? bundle.guidePath.relativePath : null,
|
|
3096
3154
|
};
|
|
3097
3155
|
} catch (error) {
|
|
3098
3156
|
typeEnums = {
|
|
@@ -3109,6 +3167,7 @@ function queryNeo4jGraphSchemaResult(architecturePath, workspaceRoot) {
|
|
|
3109
3167
|
schema: {
|
|
3110
3168
|
...schema,
|
|
3111
3169
|
...typeEnums,
|
|
3170
|
+
...schemaBundleInfo,
|
|
3112
3171
|
},
|
|
3113
3172
|
usage: {
|
|
3114
3173
|
scopeGraph: 'MATCH (e:Element {graphKey: $graphKey}) ...',
|