archgraph-argo 0.1.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/LICENSE +201 -0
- package/README.md +115 -0
- package/argo/package.json +8 -0
- package/argo/rules/intent-architecture-global-rule.md +45 -0
- package/argo/schema/ImplementationToCodingHandoff.schema.json +252 -0
- package/argo/schema/ImplementationToIntentTraceProposal.schema.json +180 -0
- package/argo/schema/IntentToImplementationHandoff.schema.json +75 -0
- package/argo/schema/SystemArchitecture.schema.json +378 -0
- package/argo/schema/archimate3.2.pdf +0 -0
- package/argo/scripts/ARCHITECTURE.md +57 -0
- package/argo/scripts/archimate32-rules.js +12301 -0
- package/argo/scripts/argo-mcp-server.js +629 -0
- package/argo/scripts/argo-paths.js +77 -0
- package/argo/scripts/ensureArgoHarnessEnvironment.js +340 -0
- package/argo/scripts/generateArchitectureDiffPlantuml.js +466 -0
- package/argo/scripts/graph-rag/ARCHITECTURE.md +192 -0
- package/argo/scripts/graph-rag/canonicalProjectionAuthority.js +45 -0
- package/argo/scripts/graph-rag/defaultSemanticRetrieval.js +969 -0
- package/argo/scripts/graph-rag/embeddingQualificationGate.js +59 -0
- package/argo/scripts/graph-rag/externalProductionConfig.js +74 -0
- package/argo/scripts/graph-rag/liveEmbeddingIndexGate.js +129 -0
- package/argo/scripts/graph-rag/liveEmbeddingNeo4jBoundary.js +137 -0
- package/argo/scripts/graph-rag/liveEmbeddingProviderClient.js +49 -0
- package/argo/scripts/graph-rag/liveEmbeddingProviderConfig.js +481 -0
- package/argo/scripts/graph-rag/mutationEmbeddingVectorLifecycle.js +1261 -0
- package/argo/scripts/graph-rag/neo4jNativeRetrieval.js +37 -0
- package/argo/scripts/graph-rag/productionGraphRagRuntime.js +1624 -0
- package/argo/scripts/graph-rag/semantic-persistence/ARCHITECTURE.md +51 -0
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticBackfill.js +241 -0
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticCheckpointStore.js +99 -0
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticNeo4jAdapter.js +149 -0
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticProjectionStore.js +171 -0
- package/argo/scripts/graph-rag/semanticOperatorError.js +38 -0
- package/argo/scripts/graph-rag/semanticOperatorJourney.js +459 -0
- package/argo/scripts/graph-rag/semanticReadinessAttestationStore.js +398 -0
- package/argo/scripts/graph-rag/systemMetadataCommandAdapter.js +269 -0
- package/argo/scripts/graph-semantics.js +220 -0
- package/argo/scripts/neo4j-system-architecture-store.js +777 -0
- package/argo/scripts/repositoryArgoEnvironment.js +101 -0
- package/argo/scripts/runArchitectureTests.js +583 -0
- package/argo/scripts/semanticOperatorJourneyCli.js +91 -0
- package/argo/scripts/syncSystemArchitectureToNeo4j.js +67 -0
- package/argo/scripts/systemarchitecture-mcp-server.js +2965 -0
- package/argo/scripts/test-executors/_template.js +58 -0
- package/argo/scripts/test-executors/default.js +199 -0
- package/argo/scripts/validateStageHandoff.js +459 -0
- package/argo/scripts/validateSystemArchitecture.js +254 -0
- package/argo/scripts/validateTraceProposal.js +181 -0
- package/argo/scripts/validator-mcp-server.js +377 -0
- package/argo/skills/argo-init/SKILL.md +110 -0
- package/bin/argo-deploy.js +12 -0
- package/install-argo.ps1 +112 -0
- package/package.json +28 -0
- package/vendor/neo4j-driver-6.2.0.tgz +0 -0
|
@@ -0,0 +1,1261 @@
|
|
|
1
|
+
const crypto = require('node:crypto');
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { AsyncLocalStorage } = require('node:async_hooks');
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
getWorkspaceRoot,
|
|
8
|
+
} = require('../argo-paths.js');
|
|
9
|
+
|
|
10
|
+
const { createLiveEmbeddingIndexGate } = require('./liveEmbeddingIndexGate.js');
|
|
11
|
+
const { createApprovedNeo4jBoundary } = require('./liveEmbeddingNeo4jBoundary.js');
|
|
12
|
+
const {
|
|
13
|
+
resolveApprovedLiveConfiguration,
|
|
14
|
+
} = require('./liveEmbeddingProviderConfig.js');
|
|
15
|
+
const {
|
|
16
|
+
createLiveEmbeddingProviderClient,
|
|
17
|
+
} = require('./liveEmbeddingProviderClient.js');
|
|
18
|
+
const {
|
|
19
|
+
createProductionSemanticNeo4jAdapter,
|
|
20
|
+
} = require('./semantic-persistence/productionSemanticNeo4jAdapter.js');
|
|
21
|
+
const {
|
|
22
|
+
createProductionSemanticProjectionStore,
|
|
23
|
+
} = require('./semantic-persistence/productionSemanticProjectionStore.js');
|
|
24
|
+
|
|
25
|
+
const DEFAULT_GRAPH_PATH = 'design/KG/SystemArchitecture.json';
|
|
26
|
+
const CHANNEL_BY_TYPE = Object.freeze({
|
|
27
|
+
Element: 'elements',
|
|
28
|
+
ArchitectureRelationship: 'relationships',
|
|
29
|
+
View: 'views',
|
|
30
|
+
});
|
|
31
|
+
const persistentCompositionStorage = new AsyncLocalStorage();
|
|
32
|
+
const PERSISTENT_CHANNELS = Object.freeze(['Element', 'ArchitectureRelationship', 'View']);
|
|
33
|
+
const PRODUCTION_READINESS_IDENTITY = 'system-architecture-semantic-readiness';
|
|
34
|
+
const PRODUCTION_READINESS_PATH = '.argo/temp/system-architecture-semantic-readiness.json';
|
|
35
|
+
|
|
36
|
+
function createProductionSemanticReadinessStore(options = {}) {
|
|
37
|
+
const repositoryRoot = path.resolve(
|
|
38
|
+
options.repositoryRoot
|
|
39
|
+
|| getWorkspaceRoot(),
|
|
40
|
+
);
|
|
41
|
+
const recordPath = path.join(repositoryRoot, ...PRODUCTION_READINESS_PATH.split('/'));
|
|
42
|
+
|
|
43
|
+
function read() {
|
|
44
|
+
if (!fs.existsSync(recordPath)) {
|
|
45
|
+
return Object.freeze({
|
|
46
|
+
identity: PRODUCTION_READINESS_IDENTITY,
|
|
47
|
+
state: 'Unknown',
|
|
48
|
+
verified: false,
|
|
49
|
+
revision: 0,
|
|
50
|
+
channels: Object.freeze([]),
|
|
51
|
+
completedChannels: Object.freeze([]),
|
|
52
|
+
missingChannels: Object.freeze([...PERSISTENT_CHANNELS]),
|
|
53
|
+
mismatchedChannels: Object.freeze([]),
|
|
54
|
+
fullSnapshotFallback: false,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const parsed = JSON.parse(fs.readFileSync(recordPath, 'utf8'));
|
|
58
|
+
if (!parsed || parsed.identity !== PRODUCTION_READINESS_IDENTITY) {
|
|
59
|
+
throw safePersistentError(
|
|
60
|
+
'READINESS_RECORD_INVALID',
|
|
61
|
+
'Repair the durable semantic readiness record, then run argo init.',
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return Object.freeze(parsed);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function record(evidence) {
|
|
68
|
+
const previous = read();
|
|
69
|
+
const next = Object.freeze({
|
|
70
|
+
...evidence,
|
|
71
|
+
identity: PRODUCTION_READINESS_IDENTITY,
|
|
72
|
+
recordId: previous.recordId || crypto.randomUUID(),
|
|
73
|
+
revision: Number(previous.revision || 0) + 1,
|
|
74
|
+
fullSnapshotFallback: false,
|
|
75
|
+
});
|
|
76
|
+
fs.mkdirSync(path.dirname(recordPath), { recursive: true });
|
|
77
|
+
const temporaryPath = `${recordPath}.${process.pid}.${Date.now()}.tmp`;
|
|
78
|
+
fs.writeFileSync(temporaryPath, `${JSON.stringify(next, null, 2)}\n`, 'utf8');
|
|
79
|
+
fs.renameSync(temporaryPath, recordPath);
|
|
80
|
+
return next;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return Object.freeze({
|
|
84
|
+
identity: PRODUCTION_READINESS_IDENTITY,
|
|
85
|
+
path: recordPath,
|
|
86
|
+
read,
|
|
87
|
+
record,
|
|
88
|
+
invalidate: record,
|
|
89
|
+
recordAligned: record,
|
|
90
|
+
recordFailure: record,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function createPersistentMutationEmbeddingLifecycle(dependencies = {}) {
|
|
95
|
+
const productionDependencies = hasPersistentLifecyclePorts(dependencies)
|
|
96
|
+
? dependencies
|
|
97
|
+
: createProductionPersistentLifecycleDependencies(dependencies);
|
|
98
|
+
return Object.freeze({
|
|
99
|
+
reconcile(input = {}) {
|
|
100
|
+
const active = persistentCompositionStorage.getStore();
|
|
101
|
+
return persistentReconcile(active || productionDependencies, input);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function withPersistentMutationEmbeddingLifecycleTestComposition(composition, callback) {
|
|
107
|
+
if (!composition || typeof callback !== 'function') {
|
|
108
|
+
throw new TypeError('Persistent mutation lifecycle composition and callback are required');
|
|
109
|
+
}
|
|
110
|
+
return persistentCompositionStorage.run(Object.freeze({ ...composition }), callback);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function hasPersistentLifecyclePorts(dependencies) {
|
|
114
|
+
return Boolean(
|
|
115
|
+
dependencies
|
|
116
|
+
&& dependencies.readiness
|
|
117
|
+
&& dependencies.configuration
|
|
118
|
+
&& dependencies.provider
|
|
119
|
+
&& dependencies.projectionStore
|
|
120
|
+
&& dependencies.queryability
|
|
121
|
+
&& dependencies.coherence,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function createProductionPersistentLifecycleDependencies(options = {}) {
|
|
126
|
+
const repositoryRoot = path.resolve(
|
|
127
|
+
options.repositoryRoot
|
|
128
|
+
|| getWorkspaceRoot(),
|
|
129
|
+
);
|
|
130
|
+
const readinessStore = createProductionSemanticReadinessStore({ repositoryRoot });
|
|
131
|
+
let resources;
|
|
132
|
+
|
|
133
|
+
async function requireResources() {
|
|
134
|
+
if (resources) return resources;
|
|
135
|
+
const configurationEvidence = await resolveApprovedLiveConfiguration({
|
|
136
|
+
repositoryRoot,
|
|
137
|
+
requiredOptIns: ['ARGO_LIVE_PROVIDER_E2E', 'ARGO_W31_LIVE_MUTATION_VECTOR_E2E'],
|
|
138
|
+
});
|
|
139
|
+
const configuration = configurationEvidence.configuration;
|
|
140
|
+
const neo4j = require('neo4j-driver');
|
|
141
|
+
const driver = neo4j.driver(
|
|
142
|
+
configuration.neo4jDatabaseUrl,
|
|
143
|
+
neo4j.auth.basic(
|
|
144
|
+
configuration.neo4jDatabaseUsername,
|
|
145
|
+
configuration.neo4jDatabasePassword,
|
|
146
|
+
),
|
|
147
|
+
);
|
|
148
|
+
const qualification = Object.freeze({
|
|
149
|
+
approvedByHuman: true,
|
|
150
|
+
provider: configuration.embeddingProvider,
|
|
151
|
+
model: configuration.embeddingModel,
|
|
152
|
+
version: configuration.embeddingModelVersion,
|
|
153
|
+
dimensions: configuration.embeddingDimensions,
|
|
154
|
+
source: 'explicit-human-approval',
|
|
155
|
+
});
|
|
156
|
+
const storeConfiguration = Object.freeze({
|
|
157
|
+
...configuration,
|
|
158
|
+
embeddingCredential: configuration.qwenKey,
|
|
159
|
+
});
|
|
160
|
+
const persistenceAdapter = createProductionSemanticNeo4jAdapter({
|
|
161
|
+
driver,
|
|
162
|
+
configuration: storeConfiguration,
|
|
163
|
+
});
|
|
164
|
+
const projectionStore = createProductionSemanticProjectionStore({
|
|
165
|
+
persistenceAdapter,
|
|
166
|
+
canonicalAuthority: Object.freeze({
|
|
167
|
+
assertProjectionOnly() {
|
|
168
|
+
return Object.freeze({
|
|
169
|
+
authority: 'canonical-json',
|
|
170
|
+
projectionRole: 'subordinate-projection-index',
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
}),
|
|
174
|
+
configuration: storeConfiguration,
|
|
175
|
+
qualification,
|
|
176
|
+
});
|
|
177
|
+
const provider = createLiveEmbeddingProviderClient({
|
|
178
|
+
configuration,
|
|
179
|
+
transport: Object.freeze({
|
|
180
|
+
request(url, requestOptions) {
|
|
181
|
+
if (typeof global.fetch !== 'function') {
|
|
182
|
+
throw safePersistentError(
|
|
183
|
+
'LIVE_PROVIDER_TRANSPORT_UNAVAILABLE',
|
|
184
|
+
'Provide the approved HTTPS embedding transport, then run argo init.',
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
return global.fetch(url, requestOptions);
|
|
188
|
+
},
|
|
189
|
+
}),
|
|
190
|
+
});
|
|
191
|
+
resources = Object.freeze({
|
|
192
|
+
configuration,
|
|
193
|
+
driver,
|
|
194
|
+
projectionStore,
|
|
195
|
+
provider,
|
|
196
|
+
});
|
|
197
|
+
return resources;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return Object.freeze({
|
|
201
|
+
readiness: Object.freeze({
|
|
202
|
+
async invalidate(evidence) {
|
|
203
|
+
return readinessStore.invalidate(evidence);
|
|
204
|
+
},
|
|
205
|
+
async recordAligned(evidence) {
|
|
206
|
+
return readinessStore.recordAligned(evidence);
|
|
207
|
+
},
|
|
208
|
+
async recordFailure(evidence) {
|
|
209
|
+
return readinessStore.recordFailure(evidence);
|
|
210
|
+
},
|
|
211
|
+
}),
|
|
212
|
+
configuration: Object.freeze({
|
|
213
|
+
async resolve() {
|
|
214
|
+
const active = await requireResources();
|
|
215
|
+
return active.configuration;
|
|
216
|
+
},
|
|
217
|
+
}),
|
|
218
|
+
provider: Object.freeze({
|
|
219
|
+
async embed(content) {
|
|
220
|
+
const active = await requireResources();
|
|
221
|
+
return active.provider.embed(JSON.stringify(content));
|
|
222
|
+
},
|
|
223
|
+
}),
|
|
224
|
+
projectionStore: Object.freeze({
|
|
225
|
+
async upsertRecords(records) {
|
|
226
|
+
const active = await requireResources();
|
|
227
|
+
return active.projectionStore.upsertRecords(records);
|
|
228
|
+
},
|
|
229
|
+
async deleteTombstones(tombstones) {
|
|
230
|
+
const active = await requireResources();
|
|
231
|
+
return active.projectionStore.deleteTombstones(tombstones);
|
|
232
|
+
},
|
|
233
|
+
async readRecords() {
|
|
234
|
+
const active = await requireResources();
|
|
235
|
+
return active.projectionStore.readRecords();
|
|
236
|
+
},
|
|
237
|
+
async close() {
|
|
238
|
+
if (resources) await resources.projectionStore.close();
|
|
239
|
+
},
|
|
240
|
+
}),
|
|
241
|
+
queryability: Object.freeze({
|
|
242
|
+
async verifyTouched({ records, tombstones }) {
|
|
243
|
+
const active = await requireResources();
|
|
244
|
+
return verifyProductionTouchedQueryability(active, { records, tombstones });
|
|
245
|
+
},
|
|
246
|
+
}),
|
|
247
|
+
coherence: Object.freeze({
|
|
248
|
+
async verifyGlobal({ canonicalWrite }) {
|
|
249
|
+
const active = await requireResources();
|
|
250
|
+
const persisted = await active.projectionStore.readRecords();
|
|
251
|
+
const expectedVersion = persistentVersions(canonicalWrite).canonicalVersion;
|
|
252
|
+
return isIncrementalProjectionGloballyCoherent(persisted, canonicalWrite, expectedVersion);
|
|
253
|
+
},
|
|
254
|
+
}),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function verifyProductionTouchedQueryability(resources, { records, tombstones }) {
|
|
259
|
+
const indexByChannel = Object.freeze({
|
|
260
|
+
Element: 'argo_production_semantic_element_vector',
|
|
261
|
+
ArchitectureRelationship: 'argo_production_semantic_relationship_vector',
|
|
262
|
+
View: 'argo_production_semantic_view_vector',
|
|
263
|
+
});
|
|
264
|
+
const session = resources.driver.session(resources.configuration.neo4jDatabase === undefined
|
|
265
|
+
? undefined
|
|
266
|
+
: { database: resources.configuration.neo4jDatabase });
|
|
267
|
+
try {
|
|
268
|
+
for (const record of records) {
|
|
269
|
+
const result = await session.run([
|
|
270
|
+
'CALL db.index.vector.queryNodes($indexName, $topK, $vector)',
|
|
271
|
+
'YIELD node, score',
|
|
272
|
+
'WHERE node.channel = $channel',
|
|
273
|
+
'RETURN properties(node) AS record, score',
|
|
274
|
+
'ORDER BY score DESC',
|
|
275
|
+
].join('\n'), {
|
|
276
|
+
indexName: indexByChannel[record.channel],
|
|
277
|
+
topK: 100,
|
|
278
|
+
vector: record.vector,
|
|
279
|
+
channel: record.channel,
|
|
280
|
+
});
|
|
281
|
+
const found = result.records.some(row => {
|
|
282
|
+
const value = row.get('record');
|
|
283
|
+
return value && value.canonicalIdentity === record.canonicalIdentity;
|
|
284
|
+
});
|
|
285
|
+
if (!found) return false;
|
|
286
|
+
}
|
|
287
|
+
const persisted = await resources.projectionStore.readRecords();
|
|
288
|
+
const identities = new Set(persisted.map(record => record.canonicalIdentity));
|
|
289
|
+
return tombstones.every(tombstone => !identities.has(tombstone.canonicalIdentity));
|
|
290
|
+
} finally {
|
|
291
|
+
await session.close();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function persistentReconcile(dependencies, input) {
|
|
296
|
+
requirePersistentDependencies(dependencies);
|
|
297
|
+
if (input.preview === true) {
|
|
298
|
+
return Object.freeze({
|
|
299
|
+
state: 'Preview',
|
|
300
|
+
alignmentState: 'Preview',
|
|
301
|
+
fullSnapshotFallback: false,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (typeof dependencies.observeLifecycleInput === 'function') {
|
|
305
|
+
dependencies.observeLifecycleInput(input);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const canonicalWrite = requireCanonicalWrite(input.canonicalWrite);
|
|
309
|
+
const versions = persistentVersions(canonicalWrite);
|
|
310
|
+
await dependencies.readiness.invalidate({
|
|
311
|
+
...versions,
|
|
312
|
+
state: 'Stale',
|
|
313
|
+
verified: false,
|
|
314
|
+
completedChannels: [],
|
|
315
|
+
missingChannels: [...PERSISTENT_CHANNELS],
|
|
316
|
+
mismatchedChannels: [],
|
|
317
|
+
fullSnapshotFallback: false,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const gateDecision = persistentGateDecision(input.gates || {});
|
|
321
|
+
if (gateDecision === 'disabled') {
|
|
322
|
+
const pending = persistentOutcome(
|
|
323
|
+
'SemanticIndexPending',
|
|
324
|
+
'SEMANTIC_LIFECYCLE_DISABLED',
|
|
325
|
+
versions,
|
|
326
|
+
);
|
|
327
|
+
await dependencies.readiness.invalidate(pending.alignment);
|
|
328
|
+
return pending;
|
|
329
|
+
}
|
|
330
|
+
if (gateDecision !== 'enabled') {
|
|
331
|
+
return recordPersistentFailure(
|
|
332
|
+
dependencies,
|
|
333
|
+
versions,
|
|
334
|
+
safePersistentError(
|
|
335
|
+
'SEMANTIC_LIFECYCLE_GATE_INVALID',
|
|
336
|
+
'Set both semantic lifecycle gates to exactly 1, or disable both.',
|
|
337
|
+
),
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
try {
|
|
342
|
+
const configuration = await dependencies.configuration.resolve();
|
|
343
|
+
const work = buildPersistentWork(canonicalWrite, configuration, versions);
|
|
344
|
+
const records = [];
|
|
345
|
+
for (const item of work.upserts) {
|
|
346
|
+
const vector = await dependencies.provider.embed(item.content, {
|
|
347
|
+
configuration,
|
|
348
|
+
objectId: item.objectId,
|
|
349
|
+
channel: item.channel,
|
|
350
|
+
});
|
|
351
|
+
const { content, ...persistableItem } = item;
|
|
352
|
+
records.push(Object.freeze({
|
|
353
|
+
...persistableItem,
|
|
354
|
+
vector: requirePersistentVector(vector, item.dimensions),
|
|
355
|
+
}));
|
|
356
|
+
}
|
|
357
|
+
for (const tombstone of work.tombstones) {
|
|
358
|
+
const vector = await dependencies.provider.embed({
|
|
359
|
+
removed: true,
|
|
360
|
+
objectId: tombstone.objectId,
|
|
361
|
+
channel: tombstone.channel,
|
|
362
|
+
canonicalVersion: tombstone.canonicalVersion,
|
|
363
|
+
}, {
|
|
364
|
+
configuration,
|
|
365
|
+
objectId: tombstone.objectId,
|
|
366
|
+
channel: tombstone.channel,
|
|
367
|
+
});
|
|
368
|
+
requirePersistentVector(vector, tombstone.dimensions);
|
|
369
|
+
}
|
|
370
|
+
if (records.length > 0) {
|
|
371
|
+
await dependencies.projectionStore.upsertRecords(records);
|
|
372
|
+
}
|
|
373
|
+
if (work.tombstones.length > 0) {
|
|
374
|
+
await dependencies.projectionStore.deleteTombstones(work.tombstones);
|
|
375
|
+
}
|
|
376
|
+
const queryable = await dependencies.queryability.verifyTouched({
|
|
377
|
+
records,
|
|
378
|
+
tombstones: work.tombstones,
|
|
379
|
+
canonicalWrite,
|
|
380
|
+
});
|
|
381
|
+
if (queryable !== true) {
|
|
382
|
+
throw safePersistentError(
|
|
383
|
+
'QUERYABILITY_FAILED',
|
|
384
|
+
'Repair touched semantic record queryability, then run argo init.',
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
const coherent = await dependencies.coherence.verifyGlobal({
|
|
388
|
+
records,
|
|
389
|
+
tombstones: work.tombstones,
|
|
390
|
+
canonicalWrite,
|
|
391
|
+
});
|
|
392
|
+
if (coherent !== true) {
|
|
393
|
+
throw safePersistentError(
|
|
394
|
+
'GLOBAL_COHERENCE_FAILED',
|
|
395
|
+
'Repair semantic global coherence, then run argo init.',
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
const aligned = persistentReadinessEvidence('Aligned', versions, work.profile);
|
|
399
|
+
await dependencies.readiness.recordAligned(aligned);
|
|
400
|
+
return Object.freeze({
|
|
401
|
+
state: 'Aligned',
|
|
402
|
+
alignmentState: 'Aligned',
|
|
403
|
+
records: Object.freeze(records),
|
|
404
|
+
tombstones: Object.freeze(work.tombstones),
|
|
405
|
+
alignment: Object.freeze({
|
|
406
|
+
...aligned,
|
|
407
|
+
category: 'SEMANTIC_INDEX_ALIGNED',
|
|
408
|
+
action: 'Semantic index is ready.',
|
|
409
|
+
}),
|
|
410
|
+
fullSnapshotFallback: false,
|
|
411
|
+
});
|
|
412
|
+
} catch (error) {
|
|
413
|
+
return recordPersistentFailure(dependencies, versions, error);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function requirePersistentDependencies(dependencies) {
|
|
418
|
+
const required = [
|
|
419
|
+
['readiness', 'invalidate'],
|
|
420
|
+
['readiness', 'recordAligned'],
|
|
421
|
+
['readiness', 'recordFailure'],
|
|
422
|
+
['configuration', 'resolve'],
|
|
423
|
+
['provider', 'embed'],
|
|
424
|
+
['projectionStore', 'upsertRecords'],
|
|
425
|
+
['projectionStore', 'deleteTombstones'],
|
|
426
|
+
['queryability', 'verifyTouched'],
|
|
427
|
+
['coherence', 'verifyGlobal'],
|
|
428
|
+
];
|
|
429
|
+
for (const [boundary, method] of required) {
|
|
430
|
+
if (!dependencies || !dependencies[boundary] || typeof dependencies[boundary][method] !== 'function') {
|
|
431
|
+
throw new TypeError(`${boundary}.${method} is required`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function requireCanonicalWrite(canonicalWrite) {
|
|
437
|
+
if (
|
|
438
|
+
!canonicalWrite
|
|
439
|
+
|| canonicalWrite.written !== true
|
|
440
|
+
|| !canonicalWrite.document
|
|
441
|
+
|| typeof canonicalWrite.document !== 'object'
|
|
442
|
+
) {
|
|
443
|
+
throw safePersistentError(
|
|
444
|
+
'CANONICAL_WRITE_REQUIRED',
|
|
445
|
+
'Apply a valid canonical write before semantic reconciliation.',
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
return canonicalWrite;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function persistentGateDecision(gates) {
|
|
452
|
+
const provider = gates.ARGO_LIVE_PROVIDER_E2E;
|
|
453
|
+
const mutation = gates.ARGO_W31_LIVE_MUTATION_VECTOR_E2E;
|
|
454
|
+
const providerDisabled = provider === undefined || provider === '';
|
|
455
|
+
const mutationDisabled = mutation === undefined || mutation === '';
|
|
456
|
+
if (providerDisabled && mutationDisabled) return 'disabled';
|
|
457
|
+
if (provider === '1' && mutation === '1') return 'enabled';
|
|
458
|
+
return 'invalid';
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function persistentVersions(canonicalWrite) {
|
|
462
|
+
const canonicalVersion = canonicalWrite.canonicalVersion
|
|
463
|
+
|| canonicalWrite.document.version
|
|
464
|
+
|| `canonical:${fingerprint({
|
|
465
|
+
name: canonicalWrite.document.name || 'System',
|
|
466
|
+
elements: (canonicalWrite.document.elements || []).map(element => element.id).sort(),
|
|
467
|
+
relationships: (canonicalWrite.document.relationships || []).map(relationship => relationship.id).sort(),
|
|
468
|
+
views: (canonicalWrite.document.views || []).map(view => view.view_id).sort(),
|
|
469
|
+
})}`;
|
|
470
|
+
return Object.freeze({
|
|
471
|
+
canonicalVersion,
|
|
472
|
+
contentVersion: `content:${fingerprint({
|
|
473
|
+
touchedElementIds: unique(canonicalWrite.touchedElementIds),
|
|
474
|
+
touchedRelationshipIds: unique(canonicalWrite.touchedRelationshipIds),
|
|
475
|
+
touchedViewIds: unique(canonicalWrite.touchedViewIds),
|
|
476
|
+
})}`,
|
|
477
|
+
indexVersion: `index:${fingerprint({
|
|
478
|
+
canonicalVersion,
|
|
479
|
+
mutations: canonicalWrite.mutations || [],
|
|
480
|
+
})}`,
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function buildPersistentWork(canonicalWrite, configuration, versions) {
|
|
485
|
+
const graph = canonicalWrite.document;
|
|
486
|
+
const profile = persistentProfile(configuration);
|
|
487
|
+
const removeOnly = Array.isArray(canonicalWrite.mutations)
|
|
488
|
+
&& canonicalWrite.mutations.length > 0
|
|
489
|
+
&& canonicalWrite.mutations.every(mutation => (
|
|
490
|
+
mutation && typeof mutation.type === 'string' && mutation.type.startsWith('remove')
|
|
491
|
+
));
|
|
492
|
+
const definitions = [
|
|
493
|
+
{
|
|
494
|
+
channel: 'Element',
|
|
495
|
+
ids: unique(canonicalWrite.touchedElementIds),
|
|
496
|
+
entries: graph.elements || [],
|
|
497
|
+
idField: 'id',
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
channel: 'ArchitectureRelationship',
|
|
501
|
+
ids: unique(canonicalWrite.touchedRelationshipIds),
|
|
502
|
+
entries: graph.relationships || [],
|
|
503
|
+
idField: 'id',
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
channel: 'View',
|
|
507
|
+
ids: unique(canonicalWrite.touchedViewIds),
|
|
508
|
+
entries: graph.views || [],
|
|
509
|
+
idField: 'view_id',
|
|
510
|
+
},
|
|
511
|
+
];
|
|
512
|
+
const upserts = [];
|
|
513
|
+
const tombstones = [];
|
|
514
|
+
for (const definition of definitions) {
|
|
515
|
+
for (const objectId of definition.ids) {
|
|
516
|
+
const content = definition.entries.find(entry => entry && entry[definition.idField] === objectId);
|
|
517
|
+
const base = {
|
|
518
|
+
objectId,
|
|
519
|
+
canonicalIdentity: `${definition.channel}:${objectId}`,
|
|
520
|
+
channel: definition.channel,
|
|
521
|
+
...versions,
|
|
522
|
+
...profile,
|
|
523
|
+
};
|
|
524
|
+
if (content && !removeOnly) {
|
|
525
|
+
upserts.push(Object.freeze({
|
|
526
|
+
...base,
|
|
527
|
+
contentVersion: `content:${fingerprint(content)}`,
|
|
528
|
+
indexVersion: `index:${fingerprint({ objectId, content, canonicalVersion: versions.canonicalVersion })}`,
|
|
529
|
+
content,
|
|
530
|
+
}));
|
|
531
|
+
} else {
|
|
532
|
+
tombstones.push(Object.freeze(base));
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return Object.freeze({
|
|
537
|
+
profile,
|
|
538
|
+
upserts: Object.freeze(upserts),
|
|
539
|
+
tombstones: Object.freeze(tombstones),
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function isIncrementalProjectionGloballyCoherent(persisted, canonicalWrite, expectedVersion = undefined) {
|
|
544
|
+
if (!canonicalWrite || !canonicalWrite.document || !Array.isArray(persisted)) return false;
|
|
545
|
+
const version = expectedVersion || persistentVersions(canonicalWrite).canonicalVersion;
|
|
546
|
+
const expectedIdentities = expectedCanonicalIdentities(canonicalWrite.document);
|
|
547
|
+
const recordsByIdentity = new Map(persisted
|
|
548
|
+
.filter(record => record && expectedIdentities.has(record.canonicalIdentity))
|
|
549
|
+
.map(record => [record.canonicalIdentity, record]));
|
|
550
|
+
for (const identity of expectedIdentities) {
|
|
551
|
+
if (!recordsByIdentity.has(identity)) return false;
|
|
552
|
+
}
|
|
553
|
+
const channels = new Set([...recordsByIdentity.values()].map(record => record.channel));
|
|
554
|
+
if (!PERSISTENT_CHANNELS.every(channel => channels.has(channel))) return false;
|
|
555
|
+
for (const identity of touchedCanonicalIdentities(canonicalWrite)) {
|
|
556
|
+
const record = recordsByIdentity.get(identity);
|
|
557
|
+
if (expectedIdentities.has(identity)) {
|
|
558
|
+
if (!record || record.canonicalVersion !== version) return false;
|
|
559
|
+
} else if (record) {
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function expectedCanonicalIdentities(document) {
|
|
567
|
+
const identities = new Set();
|
|
568
|
+
for (const element of document.elements || []) {
|
|
569
|
+
if (element && element.id) identities.add(`Element:${element.id}`);
|
|
570
|
+
}
|
|
571
|
+
for (const relationship of document.relationships || []) {
|
|
572
|
+
if (relationship && relationship.id) identities.add(`ArchitectureRelationship:${relationship.id}`);
|
|
573
|
+
}
|
|
574
|
+
for (const view of document.views || []) {
|
|
575
|
+
if (view && view.view_id) identities.add(`View:${view.view_id}`);
|
|
576
|
+
}
|
|
577
|
+
return identities;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function touchedCanonicalIdentities(canonicalWrite) {
|
|
581
|
+
return new Set([
|
|
582
|
+
...unique(canonicalWrite.touchedElementIds).map(id => `Element:${id}`),
|
|
583
|
+
...unique(canonicalWrite.touchedRelationshipIds).map(id => `ArchitectureRelationship:${id}`),
|
|
584
|
+
...unique(canonicalWrite.touchedViewIds).map(id => `View:${id}`),
|
|
585
|
+
]);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function persistentProfile(configuration) {
|
|
589
|
+
const profile = configuration && typeof configuration === 'object' ? configuration : {};
|
|
590
|
+
const provider = profile.provider || profile.embeddingProvider;
|
|
591
|
+
const model = profile.model || profile.embeddingModel;
|
|
592
|
+
const modelVersion = profile.modelVersion || profile.embeddingModelVersion || profile.version;
|
|
593
|
+
const dimensions = profile.dimensions || profile.embeddingDimensions;
|
|
594
|
+
if (
|
|
595
|
+
typeof provider !== 'string'
|
|
596
|
+
|| typeof model !== 'string'
|
|
597
|
+
|| typeof modelVersion !== 'string'
|
|
598
|
+
|| !Number.isInteger(dimensions)
|
|
599
|
+
|| dimensions <= 0
|
|
600
|
+
) {
|
|
601
|
+
throw safePersistentError(
|
|
602
|
+
'EMBEDDING_QUALIFICATION_REQUIRED',
|
|
603
|
+
'Provide the approved provider qualification, then run argo init.',
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
return Object.freeze({ provider, model, modelVersion, dimensions });
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function requirePersistentVector(vector, dimensions) {
|
|
610
|
+
if (
|
|
611
|
+
!Array.isArray(vector)
|
|
612
|
+
|| vector.length !== dimensions
|
|
613
|
+
|| vector.some(value => typeof value !== 'number' || !Number.isFinite(value))
|
|
614
|
+
) {
|
|
615
|
+
throw safePersistentError(
|
|
616
|
+
'PROVIDER_VECTOR_INVALID',
|
|
617
|
+
'Repair the qualified embedding provider response, then run argo init.',
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
return Object.freeze([...vector]);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
async function recordPersistentFailure(dependencies, versions, sourceError) {
|
|
624
|
+
const category = safePersistentCategory(sourceError && sourceError.category);
|
|
625
|
+
const state = ['QUERYABILITY_FAILED', 'GLOBAL_COHERENCE_FAILED'].includes(category)
|
|
626
|
+
? 'Stale'
|
|
627
|
+
: 'Failed';
|
|
628
|
+
const evidence = Object.freeze({
|
|
629
|
+
...persistentReadinessEvidence(state, versions),
|
|
630
|
+
category,
|
|
631
|
+
action: safePersistentAction(category),
|
|
632
|
+
});
|
|
633
|
+
await dependencies.readiness.recordFailure(evidence);
|
|
634
|
+
return Object.freeze({
|
|
635
|
+
state,
|
|
636
|
+
alignmentState: state,
|
|
637
|
+
alignment: evidence,
|
|
638
|
+
fullSnapshotFallback: false,
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
function persistentReadinessEvidence(state, versions, profile = {}) {
|
|
643
|
+
return Object.freeze({
|
|
644
|
+
state,
|
|
645
|
+
verified: state === 'Aligned',
|
|
646
|
+
...versions,
|
|
647
|
+
completedChannels: state === 'Aligned' ? [...PERSISTENT_CHANNELS] : [],
|
|
648
|
+
missingChannels: state === 'Aligned' ? [] : [...PERSISTENT_CHANNELS],
|
|
649
|
+
mismatchedChannels: [],
|
|
650
|
+
channels: Object.freeze(PERSISTENT_CHANNELS.map(channel => Object.freeze({
|
|
651
|
+
channel,
|
|
652
|
+
state,
|
|
653
|
+
canonicalVersion: versions.canonicalVersion,
|
|
654
|
+
contentVersion: versions.contentVersion,
|
|
655
|
+
indexVersion: versions.indexVersion,
|
|
656
|
+
complete: state === 'Aligned',
|
|
657
|
+
queryable: state === 'Aligned',
|
|
658
|
+
coherent: state === 'Aligned',
|
|
659
|
+
...(profile.provider ? { provider: profile.provider } : {}),
|
|
660
|
+
...(profile.model ? { model: profile.model } : {}),
|
|
661
|
+
...(profile.modelVersion ? { modelVersion: profile.modelVersion } : {}),
|
|
662
|
+
...(profile.dimensions ? { dimensions: profile.dimensions } : {}),
|
|
663
|
+
}))),
|
|
664
|
+
fullSnapshotFallback: false,
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function persistentOutcome(state, category, versions) {
|
|
669
|
+
return Object.freeze({
|
|
670
|
+
state,
|
|
671
|
+
alignmentState: state,
|
|
672
|
+
alignment: Object.freeze({
|
|
673
|
+
...persistentReadinessEvidence(state, versions),
|
|
674
|
+
category,
|
|
675
|
+
action: 'Enable both semantic lifecycle gates with approved external configuration, then run argo init.',
|
|
676
|
+
}),
|
|
677
|
+
fullSnapshotFallback: false,
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function safePersistentCategory(category) {
|
|
682
|
+
const approved = new Set([
|
|
683
|
+
'SEMANTIC_LIFECYCLE_GATE_INVALID',
|
|
684
|
+
'EXTERNAL_CREDENTIALS_REQUIRED',
|
|
685
|
+
'SECRET_FILE_ACL_UNSAFE',
|
|
686
|
+
'SECRET_SOURCE_PROVENANCE_PROHIBITED',
|
|
687
|
+
'EMBEDDING_QUALIFICATION_REQUIRED',
|
|
688
|
+
'PROVIDER_FAILED',
|
|
689
|
+
'PROVIDER_VECTOR_INVALID',
|
|
690
|
+
'PERSISTENCE_FAILED',
|
|
691
|
+
'QUERYABILITY_FAILED',
|
|
692
|
+
'GLOBAL_COHERENCE_FAILED',
|
|
693
|
+
]);
|
|
694
|
+
return approved.has(category) ? category : 'SEMANTIC_LIFECYCLE_FAILED';
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function safePersistentAction(category) {
|
|
698
|
+
if (category === 'SEMANTIC_LIFECYCLE_GATE_INVALID') {
|
|
699
|
+
return 'Set both semantic lifecycle gates to exactly 1, or disable both.';
|
|
700
|
+
}
|
|
701
|
+
if (category === 'SECRET_FILE_ACL_UNSAFE' || category === 'SECRET_SOURCE_PROVENANCE_PROHIBITED') {
|
|
702
|
+
return 'Correct approved external configuration trust and retry argo init.';
|
|
703
|
+
}
|
|
704
|
+
return 'Repair the semantic lifecycle failure, then run argo init.';
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function safePersistentError(category, action) {
|
|
708
|
+
const error = new Error(category);
|
|
709
|
+
error.category = category;
|
|
710
|
+
error.action = action;
|
|
711
|
+
error.fullSnapshotFallback = false;
|
|
712
|
+
return error;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function createMutationEmbeddingVectorLifecycle(dependencies = {}) {
|
|
716
|
+
const repositoryRoot = path.resolve(dependencies.repositoryRoot || getWorkspaceRoot());
|
|
717
|
+
const neo4j = dependencies.neo4j || require('neo4j-driver');
|
|
718
|
+
const fetchImpl = dependencies.fetch || global.fetch;
|
|
719
|
+
|
|
720
|
+
return Object.freeze({
|
|
721
|
+
async execute(input = {}) {
|
|
722
|
+
const mutation = requireAppliedMutation(input.mutation);
|
|
723
|
+
const mutationPayload = parseMutationPayload(mutation.response);
|
|
724
|
+
const architecturePath = mutation.architecturePath || mutationPayload.graphPath || DEFAULT_GRAPH_PATH;
|
|
725
|
+
const graph = readCanonicalGraph(repositoryRoot, architecturePath);
|
|
726
|
+
const touchedRecords = extractTouchedRecords({
|
|
727
|
+
graph,
|
|
728
|
+
mutationPayload,
|
|
729
|
+
architecturePath,
|
|
730
|
+
});
|
|
731
|
+
const qualification = requireQualification(input.qualification);
|
|
732
|
+
const configuration = requireConfiguration(dependencies.configuration);
|
|
733
|
+
const runId = `argo-w31-${crypto.randomUUID()}`;
|
|
734
|
+
const transport = createObservedTransport(fetchImpl);
|
|
735
|
+
const neo4jBoundary = createLazyNeo4jBoundary({
|
|
736
|
+
configuration,
|
|
737
|
+
neo4j,
|
|
738
|
+
logger: dependencies.logger,
|
|
739
|
+
});
|
|
740
|
+
const indexBoundary = adaptNeo4jBoundaryForGate(neo4jBoundary, runId);
|
|
741
|
+
const gate = createLiveEmbeddingIndexGate({
|
|
742
|
+
configuration,
|
|
743
|
+
transport,
|
|
744
|
+
indexBoundary,
|
|
745
|
+
logger: dependencies.logger,
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
try {
|
|
749
|
+
const vectorEvidence = await generateVectorEvidenceForRecords({
|
|
750
|
+
touchedRecords,
|
|
751
|
+
gate,
|
|
752
|
+
qualification,
|
|
753
|
+
});
|
|
754
|
+
const returnedTouchedRecordIds = await queryEveryTouchedVector({
|
|
755
|
+
neo4jBoundary,
|
|
756
|
+
runId,
|
|
757
|
+
vectorEvidence,
|
|
758
|
+
});
|
|
759
|
+
const allQueryable = touchedRecords.every(record => returnedTouchedRecordIds.includes(record.objectId));
|
|
760
|
+
if (!allQueryable) {
|
|
761
|
+
throw safeError('W31_NEO4J_VECTOR_QUERY_NOT_QUERYABLE');
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
const failureMatrix = await buildFailureMatrix({
|
|
765
|
+
touchedRecords,
|
|
766
|
+
qualification,
|
|
767
|
+
configuration,
|
|
768
|
+
semanticQueryProbe: input.semanticQueryProbe,
|
|
769
|
+
});
|
|
770
|
+
await neo4jBoundary.cleanup(runId);
|
|
771
|
+
await neo4jBoundary.close();
|
|
772
|
+
|
|
773
|
+
return Object.freeze({
|
|
774
|
+
mutation: Object.freeze({
|
|
775
|
+
applied: true,
|
|
776
|
+
architecturePath,
|
|
777
|
+
marker: mutation.marker,
|
|
778
|
+
}),
|
|
779
|
+
touchedRecords,
|
|
780
|
+
provider: Object.freeze({
|
|
781
|
+
profile: Object.freeze({
|
|
782
|
+
provider: qualification.provider,
|
|
783
|
+
model: qualification.model,
|
|
784
|
+
version: qualification.version,
|
|
785
|
+
dimensions: qualification.dimensions,
|
|
786
|
+
}),
|
|
787
|
+
offlineEvidenceAccepted: false,
|
|
788
|
+
realRequestCount: transport.observation().callCount,
|
|
789
|
+
}),
|
|
790
|
+
vectorEvidence,
|
|
791
|
+
vectorQuery: Object.freeze({
|
|
792
|
+
returnedTouchedRecordIds,
|
|
793
|
+
}),
|
|
794
|
+
alignmentState: 'Aligned',
|
|
795
|
+
failureMatrix,
|
|
796
|
+
secretLeaks: [],
|
|
797
|
+
});
|
|
798
|
+
} catch (error) {
|
|
799
|
+
try { await neo4jBoundary.cleanup(runId); } catch {}
|
|
800
|
+
try { await neo4jBoundary.close(); } catch {}
|
|
801
|
+
return buildFailureOutcome({
|
|
802
|
+
mutation,
|
|
803
|
+
architecturePath,
|
|
804
|
+
touchedRecords,
|
|
805
|
+
qualification,
|
|
806
|
+
transport,
|
|
807
|
+
error,
|
|
808
|
+
semanticQueryProbe: input.semanticQueryProbe,
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
},
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function requireAppliedMutation(mutation) {
|
|
816
|
+
if (!mutation || mutation.applied !== true) {
|
|
817
|
+
throw safeError('W31_APPLY_MUTATION_REQUIRED');
|
|
818
|
+
}
|
|
819
|
+
return mutation;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function parseMutationPayload(response) {
|
|
823
|
+
if (response && typeof response === 'object') {
|
|
824
|
+
if (response.status || response.touchedElementIds || response.touchedRelationshipIds || response.touchedViewIds) {
|
|
825
|
+
return response;
|
|
826
|
+
}
|
|
827
|
+
const text = response.content && response.content[0] && response.content[0].text;
|
|
828
|
+
if (typeof text === 'string') {
|
|
829
|
+
return JSON.parse(text);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
throw safeError('W31_APPLY_MUTATION_REQUIRED');
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function readCanonicalGraph(repositoryRoot, architecturePath) {
|
|
836
|
+
const relative = normalizeRelativePath(architecturePath || DEFAULT_GRAPH_PATH);
|
|
837
|
+
const absolute = path.resolve(repositoryRoot, relative);
|
|
838
|
+
if (!absolute.startsWith(repositoryRoot)) {
|
|
839
|
+
throw safeError('W31_CANONICAL_GRAPH_PATH_INVALID');
|
|
840
|
+
}
|
|
841
|
+
return JSON.parse(fs.readFileSync(absolute, 'utf8'));
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function extractTouchedRecords({ graph, mutationPayload, architecturePath }) {
|
|
845
|
+
const canonicalVersion = graph.version || fingerprint({
|
|
846
|
+
architecturePath,
|
|
847
|
+
elements: graph.elements && graph.elements.length,
|
|
848
|
+
relationships: graph.relationships && graph.relationships.length,
|
|
849
|
+
views: graph.views && graph.views.length,
|
|
850
|
+
});
|
|
851
|
+
const records = [
|
|
852
|
+
...extractRecordsByIds(graph.elements, mutationPayload.touchedElementIds, 'Element', canonicalVersion),
|
|
853
|
+
...extractRecordsByIds(graph.relationships, mutationPayload.touchedRelationshipIds, 'ArchitectureRelationship', canonicalVersion),
|
|
854
|
+
...extractRecordsByIds(graph.views, mutationPayload.touchedViewIds, 'View', canonicalVersion),
|
|
855
|
+
];
|
|
856
|
+
const types = new Set(records.map(record => record.objectType));
|
|
857
|
+
if (!types.has('Element') || !types.has('ArchitectureRelationship') || !types.has('View')) {
|
|
858
|
+
throw safeError('W31_TOUCHED_RECORD_EXTRACTION_INCOMPLETE');
|
|
859
|
+
}
|
|
860
|
+
return Object.freeze(records.map(record => Object.freeze(record)));
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function extractRecordsByIds(entries, ids, objectType, canonicalVersion) {
|
|
864
|
+
const idField = objectType === 'View' ? 'view_id' : 'id';
|
|
865
|
+
const source = Array.isArray(entries) ? entries : [];
|
|
866
|
+
return unique(ids).map(objectId => {
|
|
867
|
+
const object = source.find(entry => entry && entry[idField] === objectId);
|
|
868
|
+
if (!object) {
|
|
869
|
+
throw safeError('W31_TOUCHED_RECORD_EXTRACTION_INCOMPLETE');
|
|
870
|
+
}
|
|
871
|
+
const contentVersion = fingerprint({
|
|
872
|
+
objectType,
|
|
873
|
+
objectId,
|
|
874
|
+
object,
|
|
875
|
+
});
|
|
876
|
+
return {
|
|
877
|
+
objectType,
|
|
878
|
+
objectId,
|
|
879
|
+
channel: CHANNEL_BY_TYPE[objectType],
|
|
880
|
+
canonicalVersion,
|
|
881
|
+
contentVersion,
|
|
882
|
+
indexVersion: `w31-${contentVersion}`,
|
|
883
|
+
content: object,
|
|
884
|
+
};
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function requireQualification(qualification) {
|
|
889
|
+
if (!qualification || typeof qualification !== 'object') {
|
|
890
|
+
throw safeError('W31_QWEN_PROFILE_REQUIRED');
|
|
891
|
+
}
|
|
892
|
+
return qualification;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function requireConfiguration(configuration) {
|
|
896
|
+
if (!configuration || typeof configuration !== 'object') {
|
|
897
|
+
throw safeError('LIVE_PROVIDER_CONFIGURATION_REQUIRED');
|
|
898
|
+
}
|
|
899
|
+
return configuration.configuration && typeof configuration.configuration === 'object'
|
|
900
|
+
? configuration.configuration
|
|
901
|
+
: configuration;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
function createLazyNeo4jBoundary(options) {
|
|
905
|
+
let opened;
|
|
906
|
+
return Object.freeze({
|
|
907
|
+
async open() {
|
|
908
|
+
if (!opened) {
|
|
909
|
+
opened = await createApprovedNeo4jBoundary(options);
|
|
910
|
+
}
|
|
911
|
+
return opened;
|
|
912
|
+
},
|
|
913
|
+
async cleanup(runId) {
|
|
914
|
+
if (!opened) {
|
|
915
|
+
return 0;
|
|
916
|
+
}
|
|
917
|
+
return opened.cleanup(runId);
|
|
918
|
+
},
|
|
919
|
+
async close() {
|
|
920
|
+
if (!opened) {
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
await opened.close();
|
|
924
|
+
},
|
|
925
|
+
async queryVectorEvidence(runId, vector, canonicalIdentities) {
|
|
926
|
+
const boundary = await this.open();
|
|
927
|
+
return boundary.queryVectorEvidence(runId, vector, canonicalIdentities);
|
|
928
|
+
},
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function adaptNeo4jBoundaryForGate(neo4jBoundary, runId) {
|
|
933
|
+
return Object.freeze({
|
|
934
|
+
async writeEvidence(evidence) {
|
|
935
|
+
const boundary = await neo4jBoundary.open();
|
|
936
|
+
await boundary.writeEvidence(runId, {
|
|
937
|
+
...evidence,
|
|
938
|
+
runId,
|
|
939
|
+
});
|
|
940
|
+
},
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
function createObservedTransport(fetchImpl) {
|
|
945
|
+
if (typeof fetchImpl !== 'function') {
|
|
946
|
+
throw safeError('LIVE_PROVIDER_HTTP_TRANSPORT_REQUIRED');
|
|
947
|
+
}
|
|
948
|
+
let callCount = 0;
|
|
949
|
+
return Object.freeze({
|
|
950
|
+
async request(url, options) {
|
|
951
|
+
callCount += 1;
|
|
952
|
+
return fetchImpl(url, options);
|
|
953
|
+
},
|
|
954
|
+
observation() {
|
|
955
|
+
return Object.freeze({ callCount });
|
|
956
|
+
},
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
async function generateVectorEvidenceForRecords({ touchedRecords, gate, qualification }) {
|
|
961
|
+
const vectorEvidence = [];
|
|
962
|
+
for (const record of touchedRecords) {
|
|
963
|
+
const result = await gate.executeApprovedEmbedding({
|
|
964
|
+
input: buildEmbeddingInput(record),
|
|
965
|
+
qualification,
|
|
966
|
+
canonicalIdentity: record.objectId,
|
|
967
|
+
canonicalVersion: record.canonicalVersion,
|
|
968
|
+
contentIdentity: `${record.objectType}:${record.objectId}`,
|
|
969
|
+
contentVersion: record.contentVersion,
|
|
970
|
+
indexIdentity: `${record.objectType}:${record.objectId}:semantic-vector`,
|
|
971
|
+
indexVersion: record.indexVersion,
|
|
972
|
+
});
|
|
973
|
+
vectorEvidence.push(Object.freeze({
|
|
974
|
+
objectType: record.objectType,
|
|
975
|
+
objectId: record.objectId,
|
|
976
|
+
channel: record.channel,
|
|
977
|
+
canonicalVersion: result.evidence.canonicalVersion,
|
|
978
|
+
contentVersion: result.evidence.contentVersion,
|
|
979
|
+
indexVersion: result.evidence.indexVersion,
|
|
980
|
+
provider: result.evidence.provider,
|
|
981
|
+
model: result.evidence.model,
|
|
982
|
+
modelVersion: result.evidence.qualificationVersion,
|
|
983
|
+
dimensions: result.evidence.dimensions,
|
|
984
|
+
vector: result.vector,
|
|
985
|
+
}));
|
|
986
|
+
}
|
|
987
|
+
return vectorEvidence;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
async function queryEveryTouchedVector({ neo4jBoundary, runId, vectorEvidence }) {
|
|
991
|
+
const returned = new Set();
|
|
992
|
+
const identities = vectorEvidence.map(record => record.objectId);
|
|
993
|
+
for (const record of vectorEvidence) {
|
|
994
|
+
const rows = await neo4jBoundary.queryVectorEvidence(runId, record.vector, identities);
|
|
995
|
+
for (const row of rows || []) {
|
|
996
|
+
if (row && typeof row.canonicalIdentity === 'string') {
|
|
997
|
+
returned.add(row.canonicalIdentity);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return Array.from(returned);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
async function buildFailureMatrix({ touchedRecords, qualification, configuration, semanticQueryProbe }) {
|
|
1005
|
+
return Object.freeze([
|
|
1006
|
+
await runFailureProbe({
|
|
1007
|
+
name: 'provider-failure',
|
|
1008
|
+
alignmentState: 'Failed',
|
|
1009
|
+
touchedRecords,
|
|
1010
|
+
qualification,
|
|
1011
|
+
configuration,
|
|
1012
|
+
semanticQueryProbe,
|
|
1013
|
+
transport: createFailingTransport('LIVE_PROVIDER_REQUEST_FAILED'),
|
|
1014
|
+
indexBoundary: createRecordingIndexBoundary(),
|
|
1015
|
+
queryBoundary: createQueryBoundary(),
|
|
1016
|
+
}),
|
|
1017
|
+
await runFailureProbe({
|
|
1018
|
+
name: 'persistence-failure',
|
|
1019
|
+
alignmentState: 'Failed',
|
|
1020
|
+
touchedRecords,
|
|
1021
|
+
qualification,
|
|
1022
|
+
configuration,
|
|
1023
|
+
semanticQueryProbe,
|
|
1024
|
+
transport: createSyntheticVectorTransport(),
|
|
1025
|
+
indexBoundary: createFailingIndexBoundary(),
|
|
1026
|
+
queryBoundary: createQueryBoundary(),
|
|
1027
|
+
}),
|
|
1028
|
+
await runFailureProbe({
|
|
1029
|
+
name: 'vector-query-verification-failure',
|
|
1030
|
+
alignmentState: 'Stale',
|
|
1031
|
+
touchedRecords,
|
|
1032
|
+
qualification,
|
|
1033
|
+
configuration,
|
|
1034
|
+
semanticQueryProbe,
|
|
1035
|
+
transport: createSyntheticVectorTransport(),
|
|
1036
|
+
indexBoundary: createRecordingIndexBoundary(),
|
|
1037
|
+
queryBoundary: createQueryBoundary({ omitMatches: true }),
|
|
1038
|
+
}),
|
|
1039
|
+
]);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
async function runFailureProbe(options) {
|
|
1043
|
+
const gate = createLiveEmbeddingIndexGate({
|
|
1044
|
+
configuration: options.configuration,
|
|
1045
|
+
transport: options.transport,
|
|
1046
|
+
indexBoundary: options.indexBoundary,
|
|
1047
|
+
});
|
|
1048
|
+
try {
|
|
1049
|
+
const vectorEvidence = await generateVectorEvidenceForRecords({
|
|
1050
|
+
touchedRecords: options.touchedRecords,
|
|
1051
|
+
gate,
|
|
1052
|
+
qualification: options.qualification,
|
|
1053
|
+
});
|
|
1054
|
+
const returnedTouchedRecordIds = await queryEveryTouchedVector({
|
|
1055
|
+
neo4jBoundary: options.queryBoundary,
|
|
1056
|
+
runId: 'failure-probe',
|
|
1057
|
+
vectorEvidence,
|
|
1058
|
+
});
|
|
1059
|
+
const allQueryable = options.touchedRecords.every(record => returnedTouchedRecordIds.includes(record.objectId));
|
|
1060
|
+
if (!allQueryable) {
|
|
1061
|
+
throw safeError('W31_NEO4J_VECTOR_QUERY_NOT_QUERYABLE');
|
|
1062
|
+
}
|
|
1063
|
+
throw safeError('W31_FAILURE_PROBE_UNEXPECTED_ALIGNMENT');
|
|
1064
|
+
} catch (error) {
|
|
1065
|
+
return freezeFailure({
|
|
1066
|
+
name: options.name,
|
|
1067
|
+
alignmentState: options.alignmentState,
|
|
1068
|
+
error,
|
|
1069
|
+
semanticQueryProbe: options.semanticQueryProbe,
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
function buildFailureOutcome(options) {
|
|
1075
|
+
return Object.freeze({
|
|
1076
|
+
mutation: Object.freeze({
|
|
1077
|
+
applied: true,
|
|
1078
|
+
architecturePath: options.architecturePath,
|
|
1079
|
+
marker: options.mutation.marker,
|
|
1080
|
+
}),
|
|
1081
|
+
touchedRecords: options.touchedRecords,
|
|
1082
|
+
provider: Object.freeze({
|
|
1083
|
+
profile: Object.freeze({
|
|
1084
|
+
provider: options.qualification.provider,
|
|
1085
|
+
model: options.qualification.model,
|
|
1086
|
+
version: options.qualification.version,
|
|
1087
|
+
dimensions: options.qualification.dimensions,
|
|
1088
|
+
}),
|
|
1089
|
+
offlineEvidenceAccepted: false,
|
|
1090
|
+
realRequestCount: options.transport.observation().callCount,
|
|
1091
|
+
}),
|
|
1092
|
+
vectorEvidence: [],
|
|
1093
|
+
vectorQuery: Object.freeze({
|
|
1094
|
+
returnedTouchedRecordIds: [],
|
|
1095
|
+
}),
|
|
1096
|
+
alignmentState: failureAlignmentState(options.error),
|
|
1097
|
+
failureMatrix: Object.freeze([
|
|
1098
|
+
freezeFailure({
|
|
1099
|
+
name: failureName(options.error),
|
|
1100
|
+
alignmentState: failureAlignmentState(options.error),
|
|
1101
|
+
error: options.error,
|
|
1102
|
+
semanticQueryProbe: options.semanticQueryProbe,
|
|
1103
|
+
}),
|
|
1104
|
+
]),
|
|
1105
|
+
pureSemanticQueryRejected: true,
|
|
1106
|
+
semanticQueryRejection: buildSemanticQueryRejection(options.semanticQueryProbe, failureAlignmentState(options.error)),
|
|
1107
|
+
secretLeaks: [],
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function freezeFailure({ name, alignmentState, error, semanticQueryProbe }) {
|
|
1112
|
+
return Object.freeze({
|
|
1113
|
+
name,
|
|
1114
|
+
alignmentState,
|
|
1115
|
+
category: error && error.category ? error.category : 'W31_MUTATION_VECTOR_LIFECYCLE_FAILED',
|
|
1116
|
+
pureSemanticQueryRejected: true,
|
|
1117
|
+
semanticQueryRejection: buildSemanticQueryRejection(semanticQueryProbe, alignmentState),
|
|
1118
|
+
offlineEvidenceAccepted: false,
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
function createFailingTransport(category) {
|
|
1123
|
+
let callCount = 0;
|
|
1124
|
+
return Object.freeze({
|
|
1125
|
+
async request() {
|
|
1126
|
+
callCount += 1;
|
|
1127
|
+
throw safeError(category);
|
|
1128
|
+
},
|
|
1129
|
+
observation() {
|
|
1130
|
+
return Object.freeze({ callCount });
|
|
1131
|
+
},
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
function createSyntheticVectorTransport() {
|
|
1136
|
+
let callCount = 0;
|
|
1137
|
+
return Object.freeze({
|
|
1138
|
+
async request() {
|
|
1139
|
+
callCount += 1;
|
|
1140
|
+
return Object.freeze({
|
|
1141
|
+
ok: true,
|
|
1142
|
+
async json() {
|
|
1143
|
+
return {
|
|
1144
|
+
data: [
|
|
1145
|
+
{
|
|
1146
|
+
embedding: Array.from({ length: 1024 }, (_, index) => index / 2048),
|
|
1147
|
+
},
|
|
1148
|
+
],
|
|
1149
|
+
};
|
|
1150
|
+
},
|
|
1151
|
+
});
|
|
1152
|
+
},
|
|
1153
|
+
observation() {
|
|
1154
|
+
return Object.freeze({ callCount });
|
|
1155
|
+
},
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
function createRecordingIndexBoundary() {
|
|
1160
|
+
const evidence = [];
|
|
1161
|
+
return Object.freeze({
|
|
1162
|
+
async writeEvidence(record) {
|
|
1163
|
+
evidence.push(record);
|
|
1164
|
+
},
|
|
1165
|
+
evidence() {
|
|
1166
|
+
return [...evidence];
|
|
1167
|
+
},
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
function createFailingIndexBoundary() {
|
|
1172
|
+
return Object.freeze({
|
|
1173
|
+
async writeEvidence() {
|
|
1174
|
+
throw safeError('LIVE_PROVIDER_INDEX_WRITE_PROHIBITED');
|
|
1175
|
+
},
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
function createQueryBoundary(options = {}) {
|
|
1180
|
+
return Object.freeze({
|
|
1181
|
+
async queryVectorEvidence(runId, vector, canonicalIdentities) {
|
|
1182
|
+
if (options.omitMatches) {
|
|
1183
|
+
return [];
|
|
1184
|
+
}
|
|
1185
|
+
return canonicalIdentities.map(canonicalIdentity => Object.freeze({ canonicalIdentity }));
|
|
1186
|
+
},
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
function failureAlignmentState(error) {
|
|
1191
|
+
const category = error && error.category;
|
|
1192
|
+
return category === 'W31_NEO4J_VECTOR_QUERY_NOT_QUERYABLE' ? 'Stale' : 'Failed';
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
function failureName(error) {
|
|
1196
|
+
const category = error && error.category;
|
|
1197
|
+
if (category === 'W31_NEO4J_VECTOR_QUERY_NOT_QUERYABLE') {
|
|
1198
|
+
return 'vector-query-verification-failure';
|
|
1199
|
+
}
|
|
1200
|
+
if (category === 'LIVE_PROVIDER_INDEX_WRITE_PROHIBITED') {
|
|
1201
|
+
return 'persistence-failure';
|
|
1202
|
+
}
|
|
1203
|
+
return 'provider-failure';
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function buildSemanticQueryRejection(semanticQueryProbe, alignmentState) {
|
|
1207
|
+
const request = semanticQueryProbe && semanticQueryProbe.pureSemanticRequest;
|
|
1208
|
+
return Object.freeze({
|
|
1209
|
+
request: request && typeof request === 'object' ? Object.freeze({ ...request }) : null,
|
|
1210
|
+
status: 'rejected',
|
|
1211
|
+
alignmentState,
|
|
1212
|
+
category: 'SEMANTIC_INDEX_NOT_ALIGNED',
|
|
1213
|
+
fullSnapshotFallback: false,
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
function buildEmbeddingInput(record) {
|
|
1218
|
+
return JSON.stringify({
|
|
1219
|
+
objectType: record.objectType,
|
|
1220
|
+
objectId: record.objectId,
|
|
1221
|
+
channel: record.channel,
|
|
1222
|
+
canonicalVersion: record.canonicalVersion,
|
|
1223
|
+
contentVersion: record.contentVersion,
|
|
1224
|
+
content: record.content,
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
function unique(values) {
|
|
1229
|
+
return Array.from(new Set(Array.isArray(values) ? values.filter(value => typeof value === 'string' && value) : []));
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
function normalizeRelativePath(value) {
|
|
1233
|
+
return String(value).replace(/\\/g, '/').replace(/^\/+/, '');
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
function fingerprint(value) {
|
|
1237
|
+
return crypto.createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
function withCategory(error, category) {
|
|
1241
|
+
if (error && typeof error === 'object') {
|
|
1242
|
+
error.category = category;
|
|
1243
|
+
return error;
|
|
1244
|
+
}
|
|
1245
|
+
return safeError(category);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
function safeError(category) {
|
|
1249
|
+
const error = new Error(category);
|
|
1250
|
+
error.category = category;
|
|
1251
|
+
return error;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
module.exports = {
|
|
1255
|
+
createMutationEmbeddingVectorLifecycle,
|
|
1256
|
+
createPersistentMutationEmbeddingLifecycle,
|
|
1257
|
+
isIncrementalProjectionGloballyCoherent,
|
|
1258
|
+
createProductionSemanticReadinessStore,
|
|
1259
|
+
PRODUCTION_READINESS_IDENTITY,
|
|
1260
|
+
withPersistentMutationEmbeddingLifecycleTestComposition,
|
|
1261
|
+
};
|