@synth-deploy/server 1.0.6 → 1.2.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.
Files changed (97) hide show
  1. package/dist/agent/envoy-client.d.ts +65 -15
  2. package/dist/agent/envoy-client.d.ts.map +1 -1
  3. package/dist/agent/envoy-client.js +58 -8
  4. package/dist/agent/envoy-client.js.map +1 -1
  5. package/dist/agent/stale-deployment-detector.js +1 -1
  6. package/dist/agent/stale-deployment-detector.js.map +1 -1
  7. package/dist/agent/synth-agent.d.ts +7 -5
  8. package/dist/agent/synth-agent.d.ts.map +1 -1
  9. package/dist/agent/synth-agent.js +59 -50
  10. package/dist/agent/synth-agent.js.map +1 -1
  11. package/dist/alert-webhooks/alert-parsers.d.ts +21 -0
  12. package/dist/alert-webhooks/alert-parsers.d.ts.map +1 -0
  13. package/dist/alert-webhooks/alert-parsers.js +184 -0
  14. package/dist/alert-webhooks/alert-parsers.js.map +1 -0
  15. package/dist/api/agent.d.ts +0 -6
  16. package/dist/api/agent.d.ts.map +1 -1
  17. package/dist/api/agent.js +6 -459
  18. package/dist/api/agent.js.map +1 -1
  19. package/dist/api/alert-webhooks.d.ts +13 -0
  20. package/dist/api/alert-webhooks.d.ts.map +1 -0
  21. package/dist/api/alert-webhooks.js +279 -0
  22. package/dist/api/alert-webhooks.js.map +1 -0
  23. package/dist/api/envoy-reports.js +2 -2
  24. package/dist/api/envoy-reports.js.map +1 -1
  25. package/dist/api/envoys.js +1 -1
  26. package/dist/api/envoys.js.map +1 -1
  27. package/dist/api/fleet.d.ts.map +1 -1
  28. package/dist/api/fleet.js +14 -15
  29. package/dist/api/fleet.js.map +1 -1
  30. package/dist/api/graph.js +3 -3
  31. package/dist/api/graph.js.map +1 -1
  32. package/dist/api/operations.d.ts +7 -0
  33. package/dist/api/operations.d.ts.map +1 -0
  34. package/dist/api/operations.js +1900 -0
  35. package/dist/api/operations.js.map +1 -0
  36. package/dist/api/partitions.js +1 -1
  37. package/dist/api/partitions.js.map +1 -1
  38. package/dist/api/schemas.d.ts +434 -133
  39. package/dist/api/schemas.d.ts.map +1 -1
  40. package/dist/api/schemas.js +53 -25
  41. package/dist/api/schemas.js.map +1 -1
  42. package/dist/api/system.d.ts.map +1 -1
  43. package/dist/api/system.js +22 -21
  44. package/dist/api/system.js.map +1 -1
  45. package/dist/artifact-analyzer.js +2 -2
  46. package/dist/artifact-analyzer.js.map +1 -1
  47. package/dist/fleet/fleet-executor.js +3 -3
  48. package/dist/fleet/fleet-executor.js.map +1 -1
  49. package/dist/graph/graph-executor.d.ts.map +1 -1
  50. package/dist/graph/graph-executor.js +18 -4
  51. package/dist/graph/graph-executor.js.map +1 -1
  52. package/dist/index.js +89 -61
  53. package/dist/index.js.map +1 -1
  54. package/dist/mcp/resources.js +3 -3
  55. package/dist/mcp/resources.js.map +1 -1
  56. package/dist/mcp/tools.d.ts.map +1 -1
  57. package/dist/mcp/tools.js +2 -9
  58. package/dist/mcp/tools.js.map +1 -1
  59. package/dist/middleware/auth.js +1 -1
  60. package/dist/middleware/auth.js.map +1 -1
  61. package/package.json +1 -1
  62. package/src/agent/envoy-client.ts +111 -19
  63. package/src/agent/stale-deployment-detector.ts +1 -1
  64. package/src/agent/synth-agent.ts +76 -56
  65. package/src/alert-webhooks/alert-parsers.ts +291 -0
  66. package/src/api/agent.ts +9 -528
  67. package/src/api/alert-webhooks.ts +354 -0
  68. package/src/api/envoy-reports.ts +2 -2
  69. package/src/api/envoys.ts +1 -1
  70. package/src/api/fleet.ts +14 -15
  71. package/src/api/graph.ts +3 -3
  72. package/src/api/operations.ts +2260 -0
  73. package/src/api/partitions.ts +1 -1
  74. package/src/api/schemas.ts +59 -27
  75. package/src/api/system.ts +23 -21
  76. package/src/artifact-analyzer.ts +2 -2
  77. package/src/fleet/fleet-executor.ts +3 -3
  78. package/src/graph/graph-executor.ts +18 -4
  79. package/src/index.ts +91 -61
  80. package/src/mcp/resources.ts +3 -3
  81. package/src/mcp/tools.ts +5 -9
  82. package/src/middleware/auth.ts +1 -1
  83. package/tests/agent-mode.test.ts +5 -376
  84. package/tests/api-handlers.test.ts +27 -27
  85. package/tests/composite-operations.test.ts +557 -0
  86. package/tests/decision-diary.test.ts +62 -63
  87. package/tests/diary-reader.test.ts +14 -18
  88. package/tests/mcp-tools.test.ts +1 -1
  89. package/tests/orchestration.test.ts +34 -30
  90. package/tests/partition-isolation.test.ts +4 -9
  91. package/tests/rbac-enforcement.test.ts +8 -8
  92. package/tests/ui-journey.test.ts +9 -9
  93. package/dist/api/deployments.d.ts +0 -11
  94. package/dist/api/deployments.d.ts.map +0 -1
  95. package/dist/api/deployments.js +0 -1098
  96. package/dist/api/deployments.js.map +0 -1
  97. package/src/api/deployments.ts +0 -1347
@@ -2,7 +2,8 @@ import crypto from "node:crypto";
2
2
  import type {
3
3
  Deployment,
4
4
  DeploymentId,
5
- DeploymentTrigger,
5
+ OperationInput,
6
+ OperationTrigger,
6
7
  DebriefWriter,
7
8
  Environment,
8
9
  Partition,
@@ -25,7 +26,7 @@ import { serverLog, serverError } from "../logger.js";
25
26
  // Public interfaces
26
27
  // ---------------------------------------------------------------------------
27
28
 
28
- export interface DeploymentStore {
29
+ export interface OperationStore {
29
30
  save(deployment: Deployment): void;
30
31
  get(id: DeploymentId): Deployment | undefined;
31
32
  getByPartition(partitionId: string): Deployment[];
@@ -36,6 +37,8 @@ export interface DeploymentStore {
36
37
  findRecentByArtifact(artifactId: string, since: Date, status?: string): Deployment[];
37
38
  findLatestByEnvironment(envId: string): Deployment | undefined;
38
39
  }
40
+ /** @deprecated Use OperationStore */
41
+ export type DeploymentStore = OperationStore;
39
42
 
40
43
  export interface AgentOptions {
41
44
  /** Number of health check retries after initial failure. Default: 1 */
@@ -172,7 +175,7 @@ export class SynthAgent {
172
175
 
173
176
  constructor(
174
177
  private debrief: DebriefWriter,
175
- private deployments: DeploymentStore,
178
+ private deployments: OperationStore,
176
179
  private artifactStore: IArtifactStore,
177
180
  private environmentStore: IEnvironmentStore,
178
181
  private partitionStore: IPartitionStore,
@@ -217,14 +220,23 @@ export class SynthAgent {
217
220
  // Main entry point
218
221
  // -----------------------------------------------------------------------
219
222
 
220
- async triggerDeployment(
221
- trigger: DeploymentTrigger,
223
+ async triggerOperation(
224
+ input: OperationInput,
225
+ trigger: OperationTrigger,
222
226
  ): Promise<Deployment> {
227
+ if (input.type !== "deploy") {
228
+ throw new OrchestrationError(
229
+ "resolve-entities",
230
+ `Operation type "${input.type}" is not supported via programmatic triggers`,
231
+ `"query" and "investigate" operations run through the REST API path (web UI), not programmatic triggers. Only "deploy" is supported via triggerOperation().`,
232
+ );
233
+ }
234
+
223
235
  const deploymentId = crypto.randomUUID();
224
236
 
225
237
  // --- Look up entities from stores -----------------------------------------------
226
238
 
227
- serverLog("DEPLOY-TRIGGER", { deploymentId, artifactId: trigger.artifactId, environmentId: trigger.environmentId, partitionId: trigger.partitionId ?? null, triggeredBy: trigger.triggeredBy });
239
+ serverLog("DEPLOY-TRIGGER", { operationId: deploymentId, artifactId: input.artifactId, environmentId: trigger.environmentId, partitionId: trigger.partitionId ?? null, triggeredBy: trigger.triggeredBy });
228
240
 
229
241
  const environment = this.environmentStore.get(trigger.environmentId);
230
242
  if (!environment) {
@@ -249,12 +261,12 @@ export class SynthAgent {
249
261
  }
250
262
  }
251
263
 
252
- const artifact = this.artifactStore.get(trigger.artifactId);
264
+ const artifact = this.artifactStore.get(input.artifactId);
253
265
  if (!artifact) {
254
266
  throw new OrchestrationError(
255
267
  "resolve-entities",
256
- `Artifact not found: ${trigger.artifactId}`,
257
- `The trigger references artifact ID "${trigger.artifactId}" which does not exist in the artifact store. ` +
268
+ `Artifact not found: ${input.artifactId}`,
269
+ `The trigger references artifact ID "${input.artifactId}" which does not exist in the artifact store. ` +
258
270
  `Verify the artifact ID is correct and the artifact has been created.`,
259
271
  );
260
272
  }
@@ -275,7 +287,7 @@ export class SynthAgent {
275
287
 
276
288
  const analysisEntry = this.debrief.record({
277
289
  partitionId: trigger.partitionId ?? null,
278
- deploymentId,
290
+ operationId: deploymentId,
279
291
  agent: "server",
280
292
  decisionType: "artifact-analysis",
281
293
  decision: `Analyzed artifact "${artifact.name}" (${artifact.type}) — confidence ${artifact.analysis.confidence}`,
@@ -299,7 +311,7 @@ export class SynthAgent {
299
311
 
300
312
  // --- Step 1: Plan the pipeline -----------------------------------------
301
313
 
302
- const version = trigger.artifactVersionId ?? "latest";
314
+ const version = input.artifactVersionId ?? "latest";
303
315
  const pipelineSteps = [
304
316
  "resolve-configuration",
305
317
  "preflight-health-check",
@@ -309,7 +321,7 @@ export class SynthAgent {
309
321
 
310
322
  const planEntry = this.debrief.record({
311
323
  partitionId: trigger.partitionId ?? null,
312
- deploymentId,
324
+ operationId: deploymentId,
313
325
  agent: "server",
314
326
  decisionType: "pipeline-plan",
315
327
  decision: `Planned deployment pipeline: ${pipelineSteps.join(" → ")}`,
@@ -333,15 +345,21 @@ export class SynthAgent {
333
345
 
334
346
  // Initial plan — the Envoy generates the real deployment plan during execution
335
347
  const initialPlan = {
336
- steps: [
337
- {
338
- description: `Deploy ${artifact.name} v${version}`,
339
- action: "deploy",
340
- target: environment.name,
341
- reversible: true,
342
- rollbackAction: "rollback to previous version",
343
- },
344
- ],
348
+ scriptedPlan: {
349
+ platform: "bash" as const,
350
+ executionScript: `echo "Placeholder — envoy will generate the real plan"`,
351
+ dryRunScript: null,
352
+ rollbackScript: null,
353
+ reasoning: `Initial plan for "${artifact.name}". ` +
354
+ `Deployment intent: ${artifact.analysis.deploymentIntent ?? "standard deployment"}. ` +
355
+ `The Envoy will generate a detailed execution plan based on artifact analysis and environment state.`,
356
+ stepSummary: [
357
+ {
358
+ description: `Deploy ${artifact.name} v${version}`,
359
+ reversible: true,
360
+ },
361
+ ],
362
+ },
345
363
  reasoning: `Initial plan for "${artifact.name}". ` +
346
364
  `Deployment intent: ${artifact.analysis.deploymentIntent ?? "standard deployment"}. ` +
347
365
  `The Envoy will generate a detailed execution plan based on artifact analysis and environment state.`,
@@ -349,13 +367,13 @@ export class SynthAgent {
349
367
 
350
368
  const planGenEntry = this.debrief.record({
351
369
  partitionId: trigger.partitionId ?? null,
352
- deploymentId,
370
+ operationId: deploymentId,
353
371
  agent: "server",
354
372
  decisionType: "plan-generation",
355
- decision: `Generated deployment plan for "${artifact.name}" v${version} — ${initialPlan.steps.length} step(s)`,
373
+ decision: `Generated deployment plan for "${artifact.name}" v${version} — ${initialPlan.scriptedPlan.stepSummary.length} step(s)`,
356
374
  reasoning: initialPlan.reasoning,
357
375
  context: {
358
- stepCount: initialPlan.steps.length,
376
+ stepCount: initialPlan.scriptedPlan.stepSummary.length,
359
377
  artifactName: artifact.name,
360
378
  version,
361
379
  },
@@ -363,7 +381,7 @@ export class SynthAgent {
363
381
 
364
382
  const approvalEntry = this.debrief.record({
365
383
  partitionId: trigger.partitionId ?? null,
366
- deploymentId,
384
+ operationId: deploymentId,
367
385
  agent: "server",
368
386
  decisionType: "plan-approval",
369
387
  decision: `Auto-approved deployment plan for "${artifact.name}" v${version}`,
@@ -378,8 +396,7 @@ export class SynthAgent {
378
396
 
379
397
  const deployment: Deployment = {
380
398
  id: deploymentId,
381
- artifactId: trigger.artifactId,
382
- artifactVersionId: trigger.artifactVersionId,
399
+ input,
383
400
  environmentId: trigger.environmentId,
384
401
  partitionId: trigger.partitionId,
385
402
  version,
@@ -397,7 +414,7 @@ export class SynthAgent {
397
414
  try {
398
415
  // --- Step 2: Resolve configuration -----------------------------------
399
416
 
400
- serverLog("DEPLOY-CONFIG-RESOLVE", { deploymentId: deployment.id });
417
+ serverLog("DEPLOY-CONFIG-RESOLVE", { operationId: deployment.id });
401
418
  const { variables, hasConflicts } = this.resolveConfiguration(
402
419
  deployment,
403
420
  trigger.variables,
@@ -411,13 +428,13 @@ export class SynthAgent {
411
428
 
412
429
  deployment.status = "running";
413
430
  this.deployments.save(deployment);
414
- serverLog("DEPLOY-HEALTH-CHECK", { deploymentId: deployment.id, environment: environment.name });
431
+ serverLog("DEPLOY-HEALTH-CHECK", { operationId: deployment.id, environment: environment.name });
415
432
 
416
433
  await this.preflightHealthCheck(deployment, partition, environment, artifact);
417
434
 
418
435
  // --- Step 4: Execute deployment ----------------------------------------
419
436
 
420
- serverLog("DEPLOY-EXECUTE", { deploymentId: deployment.id, artifact: artifact.name, version: deployment.version, environment: environment.name });
437
+ serverLog("DEPLOY-EXECUTE", { operationId: deployment.id, artifact: artifact.name, version: deployment.version, environment: environment.name });
421
438
  const delegated = await this.executeDeployment(deployment, partition, environment, artifact);
422
439
 
423
440
  // --- Step 5: Post-deploy verify ----------------------------------------
@@ -433,11 +450,11 @@ export class SynthAgent {
433
450
 
434
451
  deployment.status = "succeeded";
435
452
  deployment.completedAt = new Date();
436
- serverLog("DEPLOY-SUCCEEDED", { deploymentId: deployment.id, artifact: artifact.name, version: deployment.version, environment: environment.name, durationMs: deployment.completedAt.getTime() - deployment.createdAt.getTime() });
453
+ serverLog("DEPLOY-SUCCEEDED", { operationId: deployment.id, artifact: artifact.name, version: deployment.version, environment: environment.name, durationMs: deployment.completedAt.getTime() - deployment.createdAt.getTime() });
437
454
 
438
455
  const completionEntry = this.debrief.record({
439
456
  partitionId: deployment.partitionId ?? null,
440
- deploymentId: deployment.id,
457
+ operationId: deployment.id,
441
458
  agent: "server",
442
459
  decisionType: "deployment-completion",
443
460
  decision: `Marking deployment of ${artifact.name} v${deployment.version} as succeeded on "${environment.name}"`,
@@ -465,11 +482,11 @@ export class SynthAgent {
465
482
  error instanceof OrchestrationError
466
483
  ? error.message
467
484
  : `Unexpected error: ${error instanceof Error ? error.message : String(error)}`;
468
- serverError("DEPLOY-FAILED", { deploymentId: deployment.id, reason: deployment.failureReason, durationMs: deployment.completedAt.getTime() - deployment.createdAt.getTime() });
485
+ serverError("DEPLOY-FAILED", { operationId: deployment.id, reason: deployment.failureReason, durationMs: deployment.completedAt.getTime() - deployment.createdAt.getTime() });
469
486
 
470
487
  const failEntry = this.debrief.record({
471
488
  partitionId: deployment.partitionId ?? null,
472
- deploymentId: deployment.id,
489
+ operationId: deployment.id,
473
490
  agent: "server",
474
491
  decisionType: "deployment-failure",
475
492
  decision: `Deployment failed: ${deployment.failureReason}`,
@@ -532,7 +549,7 @@ export class SynthAgent {
532
549
 
533
550
  this.debrief.record({
534
551
  partitionId,
535
- deploymentId: null,
552
+ operationId: null,
536
553
  agent: "server",
537
554
  decisionType: "diagnostic-investigation",
538
555
  decision: `${tools.length} external tool(s) available from ${connectedServers.length} MCP server(s)`,
@@ -557,7 +574,7 @@ export class SynthAgent {
557
574
  // Never let external checks block the deployment
558
575
  this.debrief.record({
559
576
  partitionId,
560
- deploymentId: null,
577
+ operationId: null,
561
578
  agent: "server",
562
579
  decisionType: "diagnostic-investigation",
563
580
  decision: "External MCP check failed — proceeding without external intelligence",
@@ -648,7 +665,7 @@ export class SynthAgent {
648
665
  const partitionVarCount = partition ? Object.keys(partition.variables).length : 0;
649
666
  const configEntry = this.debrief.record({
650
667
  partitionId: deployment.partitionId ?? null,
651
- deploymentId: deployment.id,
668
+ operationId: deployment.id,
652
669
  agent: "server",
653
670
  decisionType: "configuration-resolved",
654
671
  decision:
@@ -828,7 +845,7 @@ export class SynthAgent {
828
845
  if (crossEnvConn) {
829
846
  const entry = this.debrief.record({
830
847
  partitionId: deployment.partitionId ?? null,
831
- deploymentId: deployment.id,
848
+ operationId: deployment.id,
832
849
  agent: "server",
833
850
  decisionType: "variable-conflict",
834
851
  decision:
@@ -859,7 +876,7 @@ export class SynthAgent {
859
876
  .join("; ");
860
877
  const entry = this.debrief.record({
861
878
  partitionId: deployment.partitionId ?? null,
862
- deploymentId: deployment.id,
879
+ operationId: deployment.id,
863
880
  agent: "server",
864
881
  decisionType: "variable-conflict",
865
882
  decision: `Cross-environment variable pattern in ${crossEnv.length} non-connectivity variable(s)`,
@@ -884,7 +901,7 @@ export class SynthAgent {
884
901
  if (sensitiveDetails) {
885
902
  const entry = this.debrief.record({
886
903
  partitionId: deployment.partitionId ?? null,
887
- deploymentId: deployment.id,
904
+ operationId: deployment.id,
888
905
  agent: "server",
889
906
  decisionType: "variable-conflict",
890
907
  decision: `Security-sensitive variable(s) overridden: ${sensitiveDetails.map((d) => d.conflict.variable).join(", ")}`,
@@ -919,7 +936,7 @@ export class SynthAgent {
919
936
  .join("; ");
920
937
  const entry = this.debrief.record({
921
938
  partitionId: deployment.partitionId ?? null,
922
- deploymentId: deployment.id,
939
+ operationId: deployment.id,
923
940
  agent: "server",
924
941
  decisionType: "variable-conflict",
925
942
  decision: `Resolved ${standardDetails.length} variable conflict(s) via precedence rules`,
@@ -993,7 +1010,7 @@ export class SynthAgent {
993
1010
  if (firstCheck.reachable) {
994
1011
  const entry = this.debrief.record({
995
1012
  partitionId: deployment.partitionId ?? null,
996
- deploymentId: deployment.id,
1013
+ operationId: deployment.id,
997
1014
  agent: "server",
998
1015
  decisionType: "health-check",
999
1016
  decision: `Proceeding with deployment — target environment "${environment.name}" confirmed healthy in ${firstCheck.responseTimeMs}ms`,
@@ -1025,7 +1042,7 @@ export class SynthAgent {
1025
1042
  // Reasoning determined retrying won't help (e.g., DNS failure)
1026
1043
  const abortEntry = this.debrief.record({
1027
1044
  partitionId: deployment.partitionId ?? null,
1028
- deploymentId: deployment.id,
1045
+ operationId: deployment.id,
1029
1046
  agent: "server",
1030
1047
  decisionType: "health-check",
1031
1048
  decision: "Pre-flight health check failed — aborting without retry",
@@ -1050,7 +1067,7 @@ export class SynthAgent {
1050
1067
  // Decision is to retry
1051
1068
  const retryEntry = this.debrief.record({
1052
1069
  partitionId: deployment.partitionId ?? null,
1053
- deploymentId: deployment.id,
1070
+ operationId: deployment.id,
1054
1071
  agent: "server",
1055
1072
  decisionType: "health-check",
1056
1073
  decision: "Pre-flight health check failed — attempting retry",
@@ -1079,7 +1096,7 @@ export class SynthAgent {
1079
1096
  if (retryCheck.reachable) {
1080
1097
  const recoveryEntry = this.debrief.record({
1081
1098
  partitionId: deployment.partitionId ?? null,
1082
- deploymentId: deployment.id,
1099
+ operationId: deployment.id,
1083
1100
  agent: "server",
1084
1101
  decisionType: "health-check",
1085
1102
  decision:
@@ -1297,7 +1314,7 @@ export class SynthAgent {
1297
1314
  // No settingsReader configured — execution skipped (test/offline environment)
1298
1315
  const execEntry = this.debrief.record({
1299
1316
  partitionId: deployment.partitionId ?? null,
1300
- deploymentId: deployment.id,
1317
+ operationId: deployment.id,
1301
1318
  agent: "server",
1302
1319
  decisionType: "deployment-execution",
1303
1320
  decision: `Skipped Envoy delegation for ${artifact.name} v${deployment.version} — no settings reader configured`,
@@ -1355,7 +1372,7 @@ export class SynthAgent {
1355
1372
  // Envoy is healthy — delegate the deployment
1356
1373
  const delegateEntry = this.debrief.record({
1357
1374
  partitionId: deployment.partitionId ?? null,
1358
- deploymentId: deployment.id,
1375
+ operationId: deployment.id,
1359
1376
  agent: "server",
1360
1377
  decisionType: "deployment-execution",
1361
1378
  decision: `Delegating execution of ${artifact.name} v${deployment.version} to Envoy at ${envoyConfig.url}`,
@@ -1382,10 +1399,10 @@ export class SynthAgent {
1382
1399
 
1383
1400
  envoyResult = await client.deploy({
1384
1401
  deploymentId: deployment.id,
1402
+ operationId: deployment.id,
1385
1403
  partitionId: deployment.partitionId ?? "",
1386
1404
  environmentId: deployment.environmentId ?? "",
1387
- operationId: deployment.artifactId, // Envoy still uses operationId in its API
1388
- version: deployment.version,
1405
+ version: deployment.version ?? "",
1389
1406
  variables: deployment.variables,
1390
1407
  environmentName: environment.name,
1391
1408
  partitionName: partition?.name ?? "",
@@ -1407,7 +1424,7 @@ export class SynthAgent {
1407
1424
  for (const entry of envoyResult.debriefEntries) {
1408
1425
  const ingested = this.debrief.record({
1409
1426
  partitionId: entry.partitionId ?? deployment.partitionId ?? null,
1410
- deploymentId: entry.deploymentId ?? deployment.id,
1427
+ operationId: entry.operationId ?? deployment.id,
1411
1428
  agent: entry.agent,
1412
1429
  decisionType: entry.decisionType,
1413
1430
  decision: entry.decision,
@@ -1442,7 +1459,7 @@ export class SynthAgent {
1442
1459
  // Record successful delegation completion
1443
1460
  const completionEntry = this.debrief.record({
1444
1461
  partitionId: deployment.partitionId ?? null,
1445
- deploymentId: deployment.id,
1462
+ operationId: deployment.id,
1446
1463
  agent: "server",
1447
1464
  decisionType: "deployment-execution",
1448
1465
  decision: `Envoy completed deployment successfully in ${envoyResult.executionDurationMs}ms — ${envoyResult.artifacts.length} artifact(s) produced`,
@@ -1475,7 +1492,7 @@ export class SynthAgent {
1475
1492
  ): Promise<void> {
1476
1493
  const entry = this.debrief.record({
1477
1494
  partitionId: deployment.partitionId ?? null,
1478
- deploymentId: deployment.id,
1495
+ operationId: deployment.id,
1479
1496
  agent: "server",
1480
1497
  decisionType: "deployment-verification",
1481
1498
  decision: `Post-deploy verification skipped for ${artifact.name} v${deployment.version} — no Envoy configured`,
@@ -1506,7 +1523,10 @@ export class SynthAgent {
1506
1523
  // In-memory deployment store
1507
1524
  // ---------------------------------------------------------------------------
1508
1525
 
1509
- export class InMemoryDeploymentStore implements DeploymentStore {
1526
+ const getArtifactId = (op: Deployment): string =>
1527
+ op.input?.type === "deploy" ? (op.input as { type: "deploy"; artifactId: string }).artifactId : "";
1528
+
1529
+ export class InMemoryDeploymentStore implements OperationStore {
1510
1530
  private deployments: Map<DeploymentId, Deployment> = new Map();
1511
1531
 
1512
1532
  save(deployment: Deployment): void {
@@ -1525,7 +1545,7 @@ export class InMemoryDeploymentStore implements DeploymentStore {
1525
1545
 
1526
1546
  getByArtifact(artifactId: string): Deployment[] {
1527
1547
  return [...this.deployments.values()].filter(
1528
- (d) => d.artifactId === artifactId,
1548
+ (d) => getArtifactId(d) === artifactId,
1529
1549
  );
1530
1550
  }
1531
1551
 
@@ -1542,7 +1562,7 @@ export class InMemoryDeploymentStore implements DeploymentStore {
1542
1562
  findByArtifactVersion(artifactId: string, version: string, status?: string): Deployment[] {
1543
1563
  return [...this.deployments.values()].filter(
1544
1564
  (d) =>
1545
- d.artifactId === artifactId &&
1565
+ getArtifactId(d) === artifactId &&
1546
1566
  d.version === version &&
1547
1567
  (!status || d.status === status),
1548
1568
  );
@@ -1552,7 +1572,7 @@ export class InMemoryDeploymentStore implements DeploymentStore {
1552
1572
  return [...this.deployments.values()]
1553
1573
  .filter(
1554
1574
  (d) =>
1555
- d.artifactId === artifactId &&
1575
+ getArtifactId(d) === artifactId &&
1556
1576
  new Date(d.createdAt).getTime() >= since.getTime() &&
1557
1577
  (!status || d.status === status),
1558
1578
  )