@chllming/wave-orchestration 0.5.4 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/CHANGELOG.md +52 -3
  2. package/README.md +33 -5
  3. package/docs/README.md +18 -4
  4. package/docs/agents/wave-cont-eval-role.md +36 -0
  5. package/docs/agents/{wave-evaluator-role.md → wave-cont-qa-role.md} +14 -11
  6. package/docs/agents/wave-documentation-role.md +1 -1
  7. package/docs/agents/wave-infra-role.md +1 -1
  8. package/docs/agents/wave-integration-role.md +3 -3
  9. package/docs/agents/wave-launcher-role.md +4 -3
  10. package/docs/agents/wave-security-role.md +40 -0
  11. package/docs/concepts/context7-vs-skills.md +1 -1
  12. package/docs/concepts/what-is-a-wave.md +56 -6
  13. package/docs/evals/README.md +166 -0
  14. package/docs/evals/benchmark-catalog.json +663 -0
  15. package/docs/guides/author-and-run-waves.md +135 -0
  16. package/docs/guides/planner.md +5 -0
  17. package/docs/guides/terminal-surfaces.md +2 -0
  18. package/docs/plans/component-cutover-matrix.json +1 -1
  19. package/docs/plans/component-cutover-matrix.md +1 -1
  20. package/docs/plans/current-state.md +19 -1
  21. package/docs/plans/examples/wave-example-live-proof.md +435 -0
  22. package/docs/plans/migration.md +42 -0
  23. package/docs/plans/wave-orchestrator.md +46 -7
  24. package/docs/plans/waves/wave-0.md +4 -4
  25. package/docs/reference/live-proof-waves.md +177 -0
  26. package/docs/reference/migration-0.2-to-0.5.md +26 -19
  27. package/docs/reference/npmjs-trusted-publishing.md +6 -5
  28. package/docs/reference/runtime-config/README.md +14 -4
  29. package/docs/reference/sample-waves.md +87 -0
  30. package/docs/reference/skills.md +110 -42
  31. package/docs/research/agent-context-sources.md +130 -11
  32. package/docs/research/coordination-failure-review.md +266 -0
  33. package/docs/roadmap.md +6 -2
  34. package/package.json +2 -2
  35. package/releases/manifest.json +35 -2
  36. package/scripts/research/agent-context-archive.mjs +83 -1
  37. package/scripts/research/manifests/agent-context-expanded-2026-03-22.mjs +811 -0
  38. package/scripts/wave-orchestrator/adhoc.mjs +1331 -0
  39. package/scripts/wave-orchestrator/agent-state.mjs +358 -6
  40. package/scripts/wave-orchestrator/artifact-schemas.mjs +173 -0
  41. package/scripts/wave-orchestrator/clarification-triage.mjs +10 -3
  42. package/scripts/wave-orchestrator/config.mjs +48 -12
  43. package/scripts/wave-orchestrator/context7.mjs +2 -0
  44. package/scripts/wave-orchestrator/coord-cli.mjs +51 -19
  45. package/scripts/wave-orchestrator/coordination-store.mjs +26 -4
  46. package/scripts/wave-orchestrator/coordination.mjs +83 -9
  47. package/scripts/wave-orchestrator/dashboard-state.mjs +20 -8
  48. package/scripts/wave-orchestrator/dep-cli.mjs +5 -2
  49. package/scripts/wave-orchestrator/docs-queue.mjs +8 -2
  50. package/scripts/wave-orchestrator/evals.mjs +451 -0
  51. package/scripts/wave-orchestrator/feedback.mjs +15 -1
  52. package/scripts/wave-orchestrator/install.mjs +32 -9
  53. package/scripts/wave-orchestrator/launcher-closure.mjs +281 -0
  54. package/scripts/wave-orchestrator/launcher-runtime.mjs +334 -0
  55. package/scripts/wave-orchestrator/launcher.mjs +709 -601
  56. package/scripts/wave-orchestrator/ledger.mjs +123 -20
  57. package/scripts/wave-orchestrator/local-executor.mjs +99 -12
  58. package/scripts/wave-orchestrator/planner.mjs +177 -42
  59. package/scripts/wave-orchestrator/replay.mjs +6 -3
  60. package/scripts/wave-orchestrator/role-helpers.mjs +84 -0
  61. package/scripts/wave-orchestrator/shared.mjs +75 -11
  62. package/scripts/wave-orchestrator/skills.mjs +637 -106
  63. package/scripts/wave-orchestrator/traces.mjs +71 -48
  64. package/scripts/wave-orchestrator/wave-files.mjs +947 -101
  65. package/scripts/wave.mjs +9 -0
  66. package/skills/README.md +202 -0
  67. package/skills/provider-aws/SKILL.md +111 -0
  68. package/skills/provider-aws/adapters/claude.md +1 -0
  69. package/skills/provider-aws/adapters/codex.md +1 -0
  70. package/skills/provider-aws/references/service-verification.md +39 -0
  71. package/skills/provider-aws/skill.json +50 -1
  72. package/skills/provider-custom-deploy/SKILL.md +59 -0
  73. package/skills/provider-custom-deploy/skill.json +46 -1
  74. package/skills/provider-docker-compose/SKILL.md +90 -0
  75. package/skills/provider-docker-compose/adapters/local.md +1 -0
  76. package/skills/provider-docker-compose/skill.json +49 -1
  77. package/skills/provider-github-release/SKILL.md +116 -1
  78. package/skills/provider-github-release/adapters/claude.md +1 -0
  79. package/skills/provider-github-release/adapters/codex.md +1 -0
  80. package/skills/provider-github-release/skill.json +51 -1
  81. package/skills/provider-kubernetes/SKILL.md +137 -0
  82. package/skills/provider-kubernetes/adapters/claude.md +1 -0
  83. package/skills/provider-kubernetes/adapters/codex.md +1 -0
  84. package/skills/provider-kubernetes/references/kubectl-patterns.md +58 -0
  85. package/skills/provider-kubernetes/skill.json +48 -1
  86. package/skills/provider-railway/SKILL.md +118 -1
  87. package/skills/provider-railway/references/verification-commands.md +39 -0
  88. package/skills/provider-railway/skill.json +67 -1
  89. package/skills/provider-ssh-manual/SKILL.md +91 -0
  90. package/skills/provider-ssh-manual/skill.json +50 -1
  91. package/skills/repo-coding-rules/SKILL.md +84 -0
  92. package/skills/repo-coding-rules/skill.json +30 -1
  93. package/skills/role-cont-eval/SKILL.md +90 -0
  94. package/skills/role-cont-eval/adapters/codex.md +1 -0
  95. package/skills/role-cont-eval/skill.json +36 -0
  96. package/skills/role-cont-qa/SKILL.md +93 -0
  97. package/skills/role-cont-qa/adapters/claude.md +1 -0
  98. package/skills/role-cont-qa/skill.json +36 -0
  99. package/skills/role-deploy/SKILL.md +90 -0
  100. package/skills/role-deploy/skill.json +32 -1
  101. package/skills/role-documentation/SKILL.md +66 -0
  102. package/skills/role-documentation/skill.json +32 -1
  103. package/skills/role-implementation/SKILL.md +62 -0
  104. package/skills/role-implementation/skill.json +32 -1
  105. package/skills/role-infra/SKILL.md +74 -0
  106. package/skills/role-infra/skill.json +32 -1
  107. package/skills/role-integration/SKILL.md +79 -1
  108. package/skills/role-integration/skill.json +32 -1
  109. package/skills/role-research/SKILL.md +58 -0
  110. package/skills/role-research/skill.json +32 -1
  111. package/skills/role-security/SKILL.md +60 -0
  112. package/skills/role-security/skill.json +36 -0
  113. package/skills/runtime-claude/SKILL.md +60 -1
  114. package/skills/runtime-claude/skill.json +32 -1
  115. package/skills/runtime-codex/SKILL.md +52 -1
  116. package/skills/runtime-codex/skill.json +32 -1
  117. package/skills/runtime-local/SKILL.md +39 -0
  118. package/skills/runtime-local/skill.json +32 -1
  119. package/skills/runtime-opencode/SKILL.md +51 -0
  120. package/skills/runtime-opencode/skill.json +32 -1
  121. package/skills/wave-core/SKILL.md +107 -0
  122. package/skills/wave-core/references/marker-syntax.md +62 -0
  123. package/skills/wave-core/skill.json +31 -1
  124. package/wave.config.json +35 -6
  125. package/skills/role-evaluator/SKILL.md +0 -6
  126. package/skills/role-evaluator/skill.json +0 -5
@@ -11,19 +11,23 @@ const REPO_ROOT = WORKSPACE_ROOT;
11
11
 
12
12
  export const DEFAULT_WAVE_CONFIG_PATH = path.join(REPO_ROOT, "wave.config.json");
13
13
  export const DEFAULT_WAVE_LANE = "main";
14
- export const DEFAULT_EVALUATOR_AGENT_ID = "A0";
14
+ export const DEFAULT_CONT_QA_AGENT_ID = "A0";
15
+ export const DEFAULT_CONT_EVAL_AGENT_ID = "E0";
15
16
  export const DEFAULT_INTEGRATION_AGENT_ID = "A8";
16
17
  export const DEFAULT_DOCUMENTATION_AGENT_ID = "A9";
17
18
  export const DEFAULT_ROLE_PROMPT_DIR = "docs/agents";
18
- export const DEFAULT_EVALUATOR_ROLE_PROMPT_PATH = "docs/agents/wave-evaluator-role.md";
19
+ export const DEFAULT_CONT_QA_ROLE_PROMPT_PATH = "docs/agents/wave-cont-qa-role.md";
20
+ export const DEFAULT_CONT_EVAL_ROLE_PROMPT_PATH = "docs/agents/wave-cont-eval-role.md";
19
21
  export const DEFAULT_INTEGRATION_ROLE_PROMPT_PATH = "docs/agents/wave-integration-role.md";
20
22
  export const DEFAULT_DOCUMENTATION_ROLE_PROMPT_PATH =
21
23
  "docs/agents/wave-documentation-role.md";
24
+ export const DEFAULT_SECURITY_ROLE_PROMPT_PATH = "docs/agents/wave-security-role.md";
22
25
  export const DEFAULT_TERMINALS_PATH = ".vscode/terminals.json";
23
26
  export const DEFAULT_DOCS_DIR = "docs";
24
27
  export const DEFAULT_STATE_ROOT = ".tmp";
25
28
  export const DEFAULT_ORCHESTRATOR_STATE_DIR = ".tmp/wave-orchestrator";
26
29
  export const DEFAULT_CONTEXT7_BUNDLE_INDEX_PATH = "docs/context7/bundles.json";
30
+ export const DEFAULT_BENCHMARK_CATALOG_PATH = "docs/evals/benchmark-catalog.json";
27
31
  export const DEFAULT_COMPONENT_CUTOVER_MATRIX_DOC_PATH = "docs/plans/component-cutover-matrix.md";
28
32
  export const DEFAULT_COMPONENT_CUTOVER_MATRIX_JSON_PATH = "docs/plans/component-cutover-matrix.json";
29
33
  export const DEFAULT_REQUIRED_PROMPT_REFERENCES = [
@@ -37,6 +41,10 @@ export const DEFAULT_CODEX_SANDBOX_MODE = "danger-full-access";
37
41
  export const CODEX_SANDBOX_MODES = ["read-only", "workspace-write", "danger-full-access"];
38
42
  export const DEFAULT_CLAUDE_COMMAND = "claude";
39
43
  export const DEFAULT_OPENCODE_COMMAND = "opencode";
44
+ const LEGACY_EVALUATOR_ROLE_KEYS = new Map([
45
+ ["evaluatorAgentId", "contQaAgentId"],
46
+ ["evaluatorRolePromptPath", "contQaRolePromptPath"],
47
+ ]);
40
48
 
41
49
  export function normalizeExecutorMode(value, label = "executor") {
42
50
  const normalized = String(value || "")
@@ -138,7 +146,7 @@ function normalizeOptionalPositiveInt(value, label, fallback = null) {
138
146
  }
139
147
 
140
148
  function normalizeOptionalBoolean(value, fallback = false) {
141
- if (value === undefined) {
149
+ if (value === undefined || value === null || value === "") {
142
150
  return fallback;
143
151
  }
144
152
  if (typeof value === "boolean") {
@@ -253,20 +261,30 @@ function defaultSharedPlanDocs(plansDir) {
253
261
  }
254
262
 
255
263
  function normalizeRoles(rawRoles = {}) {
264
+ for (const [legacyKey, replacementKey] of LEGACY_EVALUATOR_ROLE_KEYS.entries()) {
265
+ if (Object.prototype.hasOwnProperty.call(rawRoles, legacyKey)) {
266
+ throw new Error(`roles.${legacyKey} was renamed to roles.${replacementKey}`);
267
+ }
268
+ }
256
269
  const rolePromptDir = normalizeRepoRelativePath(
257
270
  rawRoles.rolePromptDir || DEFAULT_ROLE_PROMPT_DIR,
258
271
  "roles.rolePromptDir",
259
272
  );
260
273
  return {
261
274
  rolePromptDir,
262
- evaluatorAgentId: String(rawRoles.evaluatorAgentId || DEFAULT_EVALUATOR_AGENT_ID).trim(),
275
+ contQaAgentId: String(rawRoles.contQaAgentId || DEFAULT_CONT_QA_AGENT_ID).trim(),
276
+ contEvalAgentId: String(rawRoles.contEvalAgentId || DEFAULT_CONT_EVAL_AGENT_ID).trim(),
263
277
  integrationAgentId: String(rawRoles.integrationAgentId || DEFAULT_INTEGRATION_AGENT_ID).trim(),
264
278
  documentationAgentId: String(
265
279
  rawRoles.documentationAgentId || DEFAULT_DOCUMENTATION_AGENT_ID,
266
280
  ).trim(),
267
- evaluatorRolePromptPath: normalizeRepoRelativePath(
268
- rawRoles.evaluatorRolePromptPath || DEFAULT_EVALUATOR_ROLE_PROMPT_PATH,
269
- "roles.evaluatorRolePromptPath",
281
+ contQaRolePromptPath: normalizeRepoRelativePath(
282
+ rawRoles.contQaRolePromptPath || DEFAULT_CONT_QA_ROLE_PROMPT_PATH,
283
+ "roles.contQaRolePromptPath",
284
+ ),
285
+ contEvalRolePromptPath: normalizeRepoRelativePath(
286
+ rawRoles.contEvalRolePromptPath || DEFAULT_CONT_EVAL_ROLE_PROMPT_PATH,
287
+ "roles.contEvalRolePromptPath",
270
288
  ),
271
289
  integrationRolePromptPath: normalizeRepoRelativePath(
272
290
  rawRoles.integrationRolePromptPath || DEFAULT_INTEGRATION_ROLE_PROMPT_PATH,
@@ -276,6 +294,10 @@ function normalizeRoles(rawRoles = {}) {
276
294
  rawRoles.documentationRolePromptPath || DEFAULT_DOCUMENTATION_ROLE_PROMPT_PATH,
277
295
  "roles.documentationRolePromptPath",
278
296
  ),
297
+ securityRolePromptPath: normalizeRepoRelativePath(
298
+ rawRoles.securityRolePromptPath || DEFAULT_SECURITY_ROLE_PROMPT_PATH,
299
+ "roles.securityRolePromptPath",
300
+ ),
279
301
  };
280
302
  }
281
303
 
@@ -367,12 +389,18 @@ function normalizeDefaultExecutorByRole(rawDefaultExecutorByRole = {}) {
367
389
  return {};
368
390
  }
369
391
  return Object.fromEntries(
370
- Object.entries(rawDefaultExecutorByRole).map(([role, executorId]) => [
371
- String(role || "")
392
+ Object.entries(rawDefaultExecutorByRole).map(([role, executorId]) => {
393
+ const normalizedRole = String(role || "")
372
394
  .trim()
373
- .toLowerCase(),
374
- normalizeExecutorMode(executorId, `defaultExecutorByRole.${role}`),
375
- ]),
395
+ .toLowerCase();
396
+ if (normalizedRole === "evaluator") {
397
+ throw new Error("defaultExecutorByRole.evaluator was renamed to defaultExecutorByRole.cont-qa");
398
+ }
399
+ return [
400
+ normalizedRole,
401
+ normalizeExecutorMode(executorId, `defaultExecutorByRole.${role}`),
402
+ ];
403
+ }),
376
404
  );
377
405
  }
378
406
 
@@ -732,6 +760,10 @@ export function loadWaveConfig(configPath = DEFAULT_WAVE_CONFIG_PATH) {
732
760
  rawConfig.paths?.context7BundleIndexPath || DEFAULT_CONTEXT7_BUNDLE_INDEX_PATH,
733
761
  "paths.context7BundleIndexPath",
734
762
  ),
763
+ benchmarkCatalogPath: normalizeRepoRelativePath(
764
+ rawConfig.paths?.benchmarkCatalogPath || DEFAULT_BENCHMARK_CATALOG_PATH,
765
+ "paths.benchmarkCatalogPath",
766
+ ),
735
767
  componentCutoverMatrixDocPath: normalizeRepoRelativePath(
736
768
  rawConfig.paths?.componentCutoverMatrixDocPath ||
737
769
  defaultComponentCutoverMatrixDocPath(defaultPlansDir(rawConfig.paths?.docsDir || DEFAULT_DOCS_DIR)),
@@ -848,6 +880,10 @@ export function resolveLaneProfile(config, laneInput = config.defaultLane) {
848
880
  laneConfig.context7BundleIndexPath || config.paths.context7BundleIndexPath,
849
881
  `${lane}.context7BundleIndexPath`,
850
882
  ),
883
+ benchmarkCatalogPath: normalizeRepoRelativePath(
884
+ laneConfig.benchmarkCatalogPath || config.paths.benchmarkCatalogPath,
885
+ `${lane}.benchmarkCatalogPath`,
886
+ ),
851
887
  componentCutoverMatrixDocPath: normalizeRepoRelativePath(
852
888
  laneConfig.componentCutoverMatrixDocPath ||
853
889
  config.paths.componentCutoverMatrixDocPath ||
@@ -218,6 +218,8 @@ export function buildAgentPromptFingerprintSource(agent) {
218
218
  prompt: String(agent?.prompt || ""),
219
219
  context7SelectionHash: String(agent?.context7Resolved?.selectionHash || ""),
220
220
  exitContract: agent?.exitContract || null,
221
+ deliverables: agent?.deliverables || [],
222
+ proofArtifacts: agent?.proofArtifacts || [],
221
223
  components: agent?.components || [],
222
224
  componentTargets: agent?.componentTargets || null,
223
225
  executorResolved: agent?.executorResolved || null,
@@ -18,7 +18,15 @@ import {
18
18
  writeCoordinationBoardProjection,
19
19
  writeJsonArtifact,
20
20
  } from "./coordination-store.mjs";
21
- import { buildLanePaths, ensureDirectory, parseNonNegativeInt } from "./shared.mjs";
21
+ import { writeAssignmentSnapshot, writeDependencySnapshot } from "./artifact-schemas.mjs";
22
+ import {
23
+ buildLanePaths,
24
+ ensureDirectory,
25
+ parseNonNegativeInt,
26
+ readJsonOrNull,
27
+ REPO_ROOT,
28
+ sanitizeAdhocRunId,
29
+ } from "./shared.mjs";
22
30
  import { parseWaveFiles } from "./wave-files.mjs";
23
31
 
24
32
  function printUsage() {
@@ -27,6 +35,7 @@ function printUsage() {
27
35
  wave coord show --lane <lane> --wave <n> [--dry-run] [--json]
28
36
  wave coord render --lane <lane> --wave <n> [--dry-run]
29
37
  wave coord inbox --lane <lane> --wave <n> --agent <id> [--dry-run]
38
+ wave coord <subcommand> --run <id> [--wave 0] ...
30
39
  `);
31
40
  }
32
41
 
@@ -36,6 +45,7 @@ function parseArgs(argv) {
36
45
  const options = {
37
46
  lane: "main",
38
47
  wave: null,
48
+ runId: "",
39
49
  dryRun: false,
40
50
  agent: "",
41
51
  kind: "",
@@ -52,6 +62,8 @@ function parseArgs(argv) {
52
62
  const arg = args[i];
53
63
  if (arg === "--lane") {
54
64
  options.lane = String(args[++i] || "").trim();
65
+ } else if (arg === "--run") {
66
+ options.runId = sanitizeAdhocRunId(args[++i]);
55
67
  } else if (arg === "--wave") {
56
68
  options.wave = parseNonNegativeInt(args[++i], "--wave");
57
69
  } else if (arg === "--agent") {
@@ -86,6 +98,11 @@ function parseArgs(argv) {
86
98
  return { subcommand, options };
87
99
  }
88
100
 
101
+ function resolveLaneForRun(runId, fallbackLane) {
102
+ const resultPath = path.join(REPO_ROOT, ".wave", "adhoc", "runs", runId, "result.json");
103
+ return readJsonOrNull(resultPath)?.lane || fallbackLane;
104
+ }
105
+
89
106
  function loadWave(lanePaths, waveNumber) {
90
107
  const waves = parseWaveFiles(lanePaths.wavesDir, { laneProfile: lanePaths.laneProfile });
91
108
  const wave = waves.find((item) => item.wave === waveNumber);
@@ -133,9 +150,32 @@ export async function runCoordinationCli(argv) {
133
150
  return;
134
151
  }
135
152
  const { subcommand, options } = parseArgs(argv);
153
+ if (options.runId) {
154
+ options.lane = resolveLaneForRun(options.runId, options.lane);
155
+ }
136
156
  const lanePaths = buildLanePaths(options.lane, {
137
157
  runVariant: options.dryRun ? "dry-run" : undefined,
158
+ adhocRunId: options.runId || null,
138
159
  });
160
+ if (options.wave === null && options.runId) {
161
+ options.wave = 0;
162
+ }
163
+ if (options.wave === null) {
164
+ throw new Error("--wave is required");
165
+ }
166
+ const wave = loadWave(lanePaths, options.wave);
167
+ const logPath = coordinationLogPath(lanePaths, wave.wave);
168
+ if (subcommand === "show") {
169
+ const state = readMaterializedCoordinationState(logPath);
170
+ if (options.json) {
171
+ console.log(JSON.stringify(serializeCoordinationState(state), null, 2));
172
+ } else {
173
+ for (const record of state.latestRecords) {
174
+ console.log(`${record.updatedAt} ${record.agentId} ${record.kind}/${record.status} ${record.summary}`);
175
+ }
176
+ }
177
+ return;
178
+ }
139
179
  ensureDirectory(lanePaths.coordinationDir);
140
180
  ensureDirectory(lanePaths.assignmentsDir);
141
181
  ensureDirectory(lanePaths.inboxesDir);
@@ -144,18 +184,14 @@ export async function runCoordinationCli(argv) {
144
184
  ensureDirectory(lanePaths.ledgerDir);
145
185
  ensureDirectory(lanePaths.integrationDir);
146
186
  ensureDirectory(lanePaths.dependencySnapshotsDir);
147
- if (options.wave === null) {
148
- throw new Error("--wave is required");
149
- }
150
- const wave = loadWave(lanePaths, options.wave);
151
- const logPath = coordinationLogPath(lanePaths, wave.wave);
152
187
  updateSeedRecords(logPath, {
153
188
  lane: lanePaths.lane,
154
189
  wave: wave.wave,
155
190
  agents: wave.agents,
156
191
  componentPromotions: wave.componentPromotions,
157
192
  sharedPlanDocs: lanePaths.sharedPlanDocs,
158
- evaluatorAgentId: lanePaths.evaluatorAgentId,
193
+ contQaAgentId: lanePaths.contQaAgentId,
194
+ contEvalAgentId: lanePaths.contEvalAgentId,
159
195
  integrationAgentId: lanePaths.integrationAgentId,
160
196
  documentationAgentId: lanePaths.documentationAgentId,
161
197
  feedbackRequests: [],
@@ -214,22 +250,18 @@ export async function runCoordinationCli(argv) {
214
250
  ledger,
215
251
  capabilityRouting: lanePaths.capabilityRouting,
216
252
  });
217
- writeJsonArtifact(assignmentsPath(lanePaths, wave.wave), capabilityAssignments);
218
- writeJsonArtifact(dependencySnapshotPath(lanePaths, wave.wave), dependencySnapshot);
253
+ writeAssignmentSnapshot(assignmentsPath(lanePaths, wave.wave), capabilityAssignments, {
254
+ lane: lanePaths.lane,
255
+ wave: wave.wave,
256
+ });
257
+ writeDependencySnapshot(dependencySnapshotPath(lanePaths, wave.wave), dependencySnapshot, {
258
+ lane: lanePaths.lane,
259
+ wave: wave.wave,
260
+ });
219
261
  writeDependencySnapshotMarkdown(
220
262
  dependencySnapshotMarkdownPath(lanePaths, wave.wave),
221
263
  dependencySnapshot,
222
264
  );
223
- if (subcommand === "show") {
224
- if (options.json) {
225
- console.log(JSON.stringify(serializeCoordinationState(state), null, 2));
226
- } else {
227
- for (const record of state.latestRecords) {
228
- console.log(`${record.updatedAt} ${record.agentId} ${record.kind}/${record.status} ${record.summary}`);
229
- }
230
- }
231
- return;
232
- }
233
265
  if (subcommand === "render") {
234
266
  const boardPath = messageBoardPath(lanePaths, wave.wave);
235
267
  writeCoordinationBoardProjection(boardPath, {
@@ -489,6 +489,9 @@ export function compileSharedSummary({
489
489
  `- Integration proof gaps: ${(integrationSummary.proofGaps || []).length}`,
490
490
  `- Integration deploy risks: ${(integrationSummary.deployRisks || []).length}`,
491
491
  `- Integration doc gaps: ${(integrationSummary.docGaps || []).length}`,
492
+ `- Security review: ${integrationSummary.securityState || "not-applicable"}`,
493
+ `- Security findings: ${(integrationSummary.securityFindings || []).length}`,
494
+ `- Security approvals: ${(integrationSummary.securityApprovals || []).length}`,
492
495
  ]
493
496
  : []),
494
497
  ...(ledger ? [`- Ledger phase: ${ledger.phase || "n/a"}`] : []),
@@ -545,6 +548,10 @@ export function compileSharedSummary({
545
548
  "",
546
549
  ...renderIntegrationItems("## Deploy risks", integrationSummary.deployRisks),
547
550
  "",
551
+ ...renderIntegrationItems("## Security findings", integrationSummary.securityFindings),
552
+ "",
553
+ ...renderIntegrationItems("## Security approvals", integrationSummary.securityApprovals),
554
+ "",
548
555
  ...renderIntegrationItems("## Documentation gaps", integrationSummary.docGaps),
549
556
  ]
550
557
  : []),
@@ -695,6 +702,9 @@ export function compileAgentInbox({
695
702
  `- Proof gaps: ${(integrationSummary.proofGaps || []).length}`,
696
703
  `- Deploy risks: ${(integrationSummary.deployRisks || []).length}`,
697
704
  `- Documentation gaps: ${(integrationSummary.docGaps || []).length}`,
705
+ `- Security review: ${integrationSummary.securityState || "not-applicable"}`,
706
+ `- Security findings: ${(integrationSummary.securityFindings || []).length}`,
707
+ `- Security approvals: ${(integrationSummary.securityApprovals || []).length}`,
698
708
  ...renderIntegrationItems(
699
709
  "- Changed interfaces",
700
710
  integrationSummary.changedInterfaces,
@@ -711,6 +721,12 @@ export function compileAgentInbox({
711
721
  ...renderIntegrationItems("- Deploy risks", integrationSummary.deployRisks, {
712
722
  maxItems: 3,
713
723
  }),
724
+ ...renderIntegrationItems("- Security findings", integrationSummary.securityFindings, {
725
+ maxItems: 3,
726
+ }),
727
+ ...renderIntegrationItems("- Security approvals", integrationSummary.securityApprovals, {
728
+ maxItems: 3,
729
+ }),
714
730
  ...renderIntegrationItems("- Documentation gaps", integrationSummary.docGaps, {
715
731
  maxItems: 3,
716
732
  }),
@@ -782,7 +798,8 @@ export function buildSeedCoordinationRecords({
782
798
  agents,
783
799
  componentPromotions = [],
784
800
  sharedPlanDocs = [],
785
- evaluatorAgentId = "A0",
801
+ contQaAgentId = "A0",
802
+ contEvalAgentId = "E0",
786
803
  integrationAgentId = "A8",
787
804
  documentationAgentId = "A9",
788
805
  feedbackRequests = [],
@@ -790,7 +807,10 @@ export function buildSeedCoordinationRecords({
790
807
  const records = [];
791
808
  for (const agent of agents) {
792
809
  const targets =
793
- agent.agentId === evaluatorAgentId || agent.agentId === documentationAgentId || agent.agentId === integrationAgentId
810
+ agent.agentId === contQaAgentId ||
811
+ agent.agentId === contEvalAgentId ||
812
+ agent.agentId === documentationAgentId ||
813
+ agent.agentId === integrationAgentId
794
814
  ? []
795
815
  : [`agent:${agent.agentId}`];
796
816
  records.push(
@@ -802,7 +822,9 @@ export function buildSeedCoordinationRecords({
802
822
  agentId: "launcher",
803
823
  targets,
804
824
  priority:
805
- agent.agentId === evaluatorAgentId || agent.agentId === documentationAgentId
825
+ agent.agentId === contQaAgentId ||
826
+ agent.agentId === contEvalAgentId ||
827
+ agent.agentId === documentationAgentId
806
828
  ? "high"
807
829
  : "normal",
808
830
  summary: `Wave ${wave} assigned to ${agent.agentId}: ${agent.title}`,
@@ -853,7 +875,7 @@ export function buildSeedCoordinationRecords({
853
875
  agentId: "launcher",
854
876
  targets: [`agent:${integrationAgentId}`],
855
877
  priority: "high",
856
- summary: `Synthesize wave ${wave} before documentation and evaluator closure`,
878
+ summary: `Synthesize wave ${wave} before documentation and cont-QA closure`,
857
879
  detail: "Integration steward must reconcile open claims, blockers, interfaces, and release risk.",
858
880
  }),
859
881
  );
@@ -14,6 +14,11 @@ import {
14
14
  sleepSync,
15
15
  toIsoTimestamp,
16
16
  } from "./shared.mjs";
17
+ import { resolveEvalTargetsAgainstCatalog } from "./evals.mjs";
18
+ import {
19
+ isContEvalImplementationOwningAgent,
20
+ isSecurityReviewAgent,
21
+ } from "./role-helpers.mjs";
17
22
 
18
23
  export const ENTRY_HEADER_REGEX = /^##\s+(.+?)\s+\|\s+Agent\s+([A-Za-z0-9.]+)\s*$/;
19
24
  export const PLACEHOLDER_TIMESTAMP_REGEX = /\$\{(?:ts|TS)\}/;
@@ -191,8 +196,11 @@ export function buildExecutionPrompt({
191
196
  inboxText = "",
192
197
  context7 = null,
193
198
  componentPromotions = null,
199
+ evalTargets = null,
200
+ benchmarkCatalogPath = null,
194
201
  sharedPlanDocs = null,
195
- evaluatorAgentId = "A0",
202
+ contQaAgentId = "A0",
203
+ contEvalAgentId = "E0",
196
204
  integrationAgentId = "A8",
197
205
  documentationAgentId = "A9",
198
206
  }) {
@@ -211,16 +219,39 @@ export function buildExecutionPrompt({
211
219
  `${lanePlansDir}/migration.md`,
212
220
  ];
213
221
  const sharedPlanDocList = resolvedSharedPlanDocs.map((docPath) => `\`${docPath}\``).join(", ");
214
- const evaluatorRequirements =
215
- agent.agentId === evaluatorAgentId
222
+ const contEvalImplementationOwning = isContEvalImplementationOwningAgent(agent, {
223
+ contEvalAgentId,
224
+ });
225
+ const resolvedEvalTargets = (() => {
226
+ try {
227
+ return resolveEvalTargetsAgainstCatalog(evalTargets, { benchmarkCatalogPath }).targets;
228
+ } catch {
229
+ return Array.isArray(evalTargets) ? evalTargets : [];
230
+ }
231
+ })();
232
+ const contQaRequirements =
233
+ agent.agentId === contQaAgentId
216
234
  ? [
217
- `- Because you are Agent ${evaluatorAgentId}, your evaluator report must end with exactly one standalone line in the form \`Verdict: PASS\`, \`Verdict: CONCERNS\`, or \`Verdict: BLOCKED\`.`,
235
+ `- Because you are Agent ${contQaAgentId}, your cont-QA report must end with exactly one standalone line in the form \`Verdict: PASS\`, \`Verdict: CONCERNS\`, or \`Verdict: BLOCKED\`.`,
218
236
  "- Also emit one matching structured marker in your terminal output: `[wave-verdict] pass`, `[wave-verdict] concerns`, or `[wave-verdict] blocked`.",
219
237
  "- Emit one final structured gate marker: `[wave-gate] architecture=<pass|concerns|blocked> integration=<pass|concerns|blocked> durability=<pass|concerns|blocked> live=<pass|concerns|blocked> docs=<pass|concerns|blocked> detail=<short-note>`.",
220
238
  "- Only use `Verdict: PASS` when the wave is coherent enough to unblock the next wave.",
221
239
  `- Do not declare PASS until the documentation gate is closed: impacted implementation-owned docs must exist, ${sharedPlanDocList} must reflect plan-affecting outcomes, and no unresolved architecture-versus-plans drift remains.`,
222
240
  "- If shared-plan reconciliation is still active inside the wave, require the exact remaining doc delta and an explicit `closed` or `no-change` note from the documentation steward or named owner before finalizing. Do not treat ownership handoff alone as the blocker.",
223
- "- Treat the last evaluator section and last structured gate marker as authoritative. Earlier concerns may remain in the append-only report history but do not control final completion if the closure sweep resolves them.",
241
+ "- Treat the last cont-QA section and last structured gate marker as authoritative. Earlier concerns may remain in the append-only report history but do not control final completion if the closure sweep resolves them.",
242
+ ]
243
+ : [];
244
+ const contEvalRequirements =
245
+ agent.agentId === contEvalAgentId
246
+ ? [
247
+ `- Because you are Agent ${contEvalAgentId}, you own the wave's iterative eval tuning loop.`,
248
+ contEvalImplementationOwning
249
+ ? "- You also own explicit non-report files in this wave. For those files, satisfy the same proof, doc-delta, and component-marker obligations as an implementation owner."
250
+ : "- You are report-only in this wave unless the prompt explicitly assigns additional non-report files. Do not edit product code outside explicit ownership; route exact follow-up work to the owning role.",
251
+ "- Read the wave's declared eval targets before selecting benchmarks or making tuning changes.",
252
+ "- Leave an append-only cont-EVAL report that records the selected benchmarks, commands run, observed gaps, regressions, and final disposition.",
253
+ "- Emit one final structured eval marker: `[wave-eval] state=<satisfied|needs-more-work|blocked> targets=<n> benchmarks=<n> regressions=<n> target_ids=<csv> benchmark_ids=<csv> detail=<short-note>`.",
254
+ "- Use `satisfied` only when observed outputs or benchmark results meet the declared target, not when the code merely looks better.",
224
255
  ]
225
256
  : [];
226
257
  const docStewardRequirements =
@@ -230,8 +261,18 @@ export function buildExecutionPrompt({
230
261
  "- If implementation work is still landing, any early closure note is provisional. Your final closure marker must reflect the post-implementation state seen during the closure sweep.",
231
262
  ]
232
263
  : [];
264
+ const securityRequirements = isSecurityReviewAgent(agent)
265
+ ? [
266
+ "- You are the wave's security reviewer. Default to report-only work and route fixes to the owning agent instead of editing product code.",
267
+ "- Leave a security review report with these sections in order: `Threat Model`, `Risky Surfaces`, `Findings`, `Required Approvals`, `Requested Fixes`, and `Final Disposition`.",
268
+ "- Emit one final structured security marker: `[wave-security] state=<clear|concerns|blocked> findings=<n> approvals=<n> detail=<short-note>`.",
269
+ "- Use `clear` only when no unresolved findings or approvals remain. Use `blocked` only when the wave must stop before integration.",
270
+ ]
271
+ : [];
233
272
  const implementationRequirements =
234
- ![evaluatorAgentId, documentationAgentId].includes(agent.agentId)
273
+ ![contQaAgentId, documentationAgentId].includes(agent.agentId) &&
274
+ !isSecurityReviewAgent(agent) &&
275
+ (agent.agentId !== contEvalAgentId || contEvalImplementationOwning)
235
276
  ? [
236
277
  "- Emit one final structured proof marker: `[wave-proof] completion=<contract|integrated|authoritative|live> durability=<none|ephemeral|durable> proof=<unit|integration|live> state=<met|gap> detail=<short-note>`.",
237
278
  "- Emit one final structured documentation marker: `[wave-doc-delta] state=<none|owned|shared-plan> paths=<comma-separated-paths> detail=<short-note>`.",
@@ -328,8 +369,22 @@ export function buildExecutionPrompt({
328
369
  "",
329
370
  ]
330
371
  : [];
372
+ const evalTargetLines =
373
+ resolvedEvalTargets.length > 0
374
+ ? [
375
+ "Eval targets for this wave:",
376
+ ...(benchmarkCatalogPath ? [`- Benchmark catalog: ${benchmarkCatalogPath}`] : []),
377
+ ...resolvedEvalTargets.map((target) =>
378
+ target.selection === "delegated"
379
+ ? `- ${target.id}: delegated family=${target.benchmarkFamily} allowed-benchmarks=${(target.allowedBenchmarks || []).join(", ")} objective=${target.objective} threshold=${target.threshold}`
380
+ : `- ${target.id}: pinned benchmarks=${(target.benchmarks || []).join(", ")} objective=${target.objective} threshold=${target.threshold}`,
381
+ ),
382
+ "",
383
+ ]
384
+ : [];
331
385
  const ownedComponentLines =
332
- ![evaluatorAgentId, documentationAgentId].includes(agent.agentId) &&
386
+ ![contQaAgentId, documentationAgentId].includes(agent.agentId) &&
387
+ (agent.agentId !== contEvalAgentId || contEvalImplementationOwning) &&
333
388
  Array.isArray(agent.components) &&
334
389
  agent.components.length > 0
335
390
  ? [
@@ -349,6 +404,20 @@ export function buildExecutionPrompt({
349
404
  "",
350
405
  ]
351
406
  : [];
407
+ const proofArtifactLines =
408
+ Array.isArray(agent.proofArtifacts) && agent.proofArtifacts.length > 0
409
+ ? [
410
+ "Proof artifacts required for this agent:",
411
+ ...agent.proofArtifacts.map((artifact) => {
412
+ const requiredFor =
413
+ Array.isArray(artifact.requiredFor) && artifact.requiredFor.length > 0
414
+ ? ` required-for=${artifact.requiredFor.join(",")}`
415
+ : "";
416
+ return `- ${artifact.path}${artifact.kind ? ` kind=${artifact.kind}` : ""}${requiredFor}`;
417
+ }),
418
+ "",
419
+ ]
420
+ : [];
352
421
  const skillLines =
353
422
  Array.isArray(agent.skillsResolved?.ids) && agent.skillsResolved.ids.length > 0
354
423
  ? [
@@ -371,7 +440,8 @@ export function buildExecutionPrompt({
371
440
  "Role model for this run:",
372
441
  "- Wave Orchestrator role: create wave files, initiate wave runs, and manage execution end-to-end.",
373
442
  "- WAVE Executor role (you): deliver the assigned outcome end-to-end within your scope and coordinate through the Wave coordination log every turn.",
374
- `- Evaluator agent id: ${evaluatorAgentId}`,
443
+ `- cont-QA agent id: ${contQaAgentId}`,
444
+ `- cont-EVAL agent id: ${contEvalAgentId}`,
375
445
  `- Integration steward agent id: ${integrationAgentId}`,
376
446
  `- Documentation steward agent id: ${documentationAgentId}`,
377
447
  `- Resolved executor: ${executorId}`,
@@ -397,8 +467,10 @@ export function buildExecutionPrompt({
397
467
  "- Emit explicit progress markers in your output: `[wave-phase] coding`, `[wave-phase] validating`, `[wave-phase] deploying`, `[wave-phase] finalizing`.",
398
468
  "- During deployment checks, emit structured deployment markers: `[deploy-status] service=<service-name> state=<deploying|healthy|failed|rolledover> detail=<short-note>`.",
399
469
  "- If your task touches machine validation, workload identity, node admission, deployment bootstrap, or approved machine actions, emit structured infra markers: `[infra-status] kind=<conformance|role-drift|dependency|identity|admission|action> target=<machine-or-surface> state=<checking|setup-required|setup-in-progress|conformant|drift|blocked|failed|action-required|action-approved|action-complete> detail=<short-note>`.",
400
- ...evaluatorRequirements,
470
+ ...contQaRequirements,
471
+ ...contEvalRequirements,
401
472
  ...docStewardRequirements,
473
+ ...securityRequirements,
402
474
  ...implementationRequirements,
403
475
  `- Update docs impacted by your implementation. If your work changes status, sequencing, ownership, or explicit proof expectations, update the relevant docs. If shared plan docs need changes outside your owned files, post the exact doc paths and exact delta needed for ${sharedPlanDocList} as a coordination record instead of leaving documentation drift for later cleanup.`,
404
476
  "- If the wave defines a documentation steward or other explicit owner for shared plan docs, coordinate those updates through that owner, notify them as soon as the delta is known, and stay engaged until they confirm `closed` or `no-change`. Do not treat the ownership boundary as the definition of done.",
@@ -426,8 +498,10 @@ export function buildExecutionPrompt({
426
498
  : []),
427
499
  ...exitContractLines,
428
500
  ...promotedComponentLines,
501
+ ...evalTargetLines,
429
502
  ...ownedComponentLines,
430
503
  ...deliverableLines,
504
+ ...proofArtifactLines,
431
505
  ...skillLines,
432
506
  ...context7PromptLines,
433
507
  "Assigned implementation prompt:",
@@ -17,6 +17,10 @@ import {
17
17
  truncate,
18
18
  writeJsonAtomic,
19
19
  } from "./shared.mjs";
20
+ import {
21
+ normalizeGlobalDashboardState,
22
+ normalizeWaveDashboardState,
23
+ } from "./artifact-schemas.mjs";
20
24
 
21
25
  export function readStatusCodeIfPresent(statusPath) {
22
26
  return readStatusRecordIfPresent(statusPath)?.code ?? null;
@@ -130,7 +134,7 @@ export function buildWaveDashboardState({
130
134
  agentRuns,
131
135
  }) {
132
136
  const now = toIsoTimestamp();
133
- return {
137
+ return normalizeWaveDashboardState({
134
138
  lane,
135
139
  wave,
136
140
  waveFile,
@@ -169,7 +173,7 @@ export function buildWaveDashboardState({
169
173
  detail: "",
170
174
  })),
171
175
  events: [],
172
- };
176
+ });
173
177
  }
174
178
 
175
179
  export function buildGlobalDashboardState({
@@ -181,7 +185,7 @@ export function buildGlobalDashboardState({
181
185
  feedbackRequestsDir,
182
186
  }) {
183
187
  const now = toIsoTimestamp();
184
- return {
188
+ return normalizeGlobalDashboardState({
185
189
  lane,
186
190
  runId: Math.random().toString(16).slice(2, 14),
187
191
  status: "running",
@@ -234,19 +238,27 @@ export function buildGlobalDashboardState({
234
238
  infraFindings: [],
235
239
  })),
236
240
  events: [],
237
- };
241
+ });
238
242
  }
239
243
 
240
244
  export function writeWaveDashboard(dashboardPath, state) {
241
245
  ensureDirectory(path.dirname(dashboardPath));
242
- state.updatedAt = toIsoTimestamp();
243
- writeJsonAtomic(dashboardPath, state);
246
+ const normalized = normalizeWaveDashboardState({
247
+ ...state,
248
+ updatedAt: toIsoTimestamp(),
249
+ });
250
+ Object.assign(state, normalized);
251
+ writeJsonAtomic(dashboardPath, normalized);
244
252
  }
245
253
 
246
254
  export function writeGlobalDashboard(globalDashboardPath, state) {
247
255
  ensureDirectory(path.dirname(globalDashboardPath));
248
- state.updatedAt = toIsoTimestamp();
249
- writeJsonAtomic(globalDashboardPath, state);
256
+ const normalized = normalizeGlobalDashboardState({
257
+ ...state,
258
+ updatedAt: toIsoTimestamp(),
259
+ });
260
+ Object.assign(state, normalized);
261
+ writeJsonAtomic(globalDashboardPath, normalized);
250
262
  }
251
263
 
252
264
  export function recordWaveDashboardEvent(state, { level = "info", agentId = null, message }) {
@@ -3,8 +3,8 @@ import {
3
3
  appendDependencyTicket,
4
4
  materializeCoordinationState,
5
5
  readDependencyTickets,
6
- writeJsonArtifact,
7
6
  } from "./coordination-store.mjs";
7
+ import { writeDependencySnapshot } from "./artifact-schemas.mjs";
8
8
  import {
9
9
  buildDependencySnapshot,
10
10
  readAllDependencyTickets,
@@ -209,7 +209,10 @@ export async function runDependencyCli(argv) {
209
209
  capabilityRouting: lanePaths.capabilityRouting,
210
210
  });
211
211
  const markdownPath = dependencyMarkdownPath(lanePaths, lane);
212
- writeJsonArtifact(path.join(lanePaths.crossLaneDependenciesDir, `${lane}.json`), snapshot);
212
+ writeDependencySnapshot(path.join(lanePaths.crossLaneDependenciesDir, `${lane}.json`), snapshot, {
213
+ lane,
214
+ wave: options.wave ?? 0,
215
+ });
213
216
  writeTextAtomic(markdownPath, `${renderDependencySnapshotMarkdown(snapshot)}\n`);
214
217
  console.log(JSON.stringify({ markdownPath, jsonPath: path.join(lanePaths.crossLaneDependenciesDir, `${lane}.json`) }, null, 2));
215
218
  return;
@@ -8,6 +8,8 @@ export function buildDocsQueue({
8
8
  componentPromotions = [],
9
9
  runtimeAssignments = [],
10
10
  }) {
11
+ const waveNumber =
12
+ typeof wave === "object" && wave !== null && Object.hasOwn(wave, "wave") ? wave.wave : wave;
11
13
  const items = [];
12
14
  for (const [agentId, summary] of Object.entries(summariesByAgentId || {})) {
13
15
  if (!summary?.docDelta) {
@@ -28,7 +30,11 @@ export function buildDocsQueue({
28
30
  }
29
31
  }
30
32
  if (summary.docDelta.state === "shared-plan") {
31
- for (const docPath of summary.docDelta.paths || sharedPlanDocs) {
33
+ const sharedPlanPaths =
34
+ Array.isArray(summary.docDelta.paths) && summary.docDelta.paths.length > 0
35
+ ? summary.docDelta.paths
36
+ : sharedPlanDocs;
37
+ for (const docPath of sharedPlanPaths) {
32
38
  items.push({
33
39
  id: `${agentId}:shared:${docPath}`,
34
40
  kind: "shared-plan",
@@ -57,7 +63,7 @@ export function buildDocsQueue({
57
63
  const releaseNotesRequired = items.some((item) => item.kind === "shared-plan");
58
64
  return {
59
65
  lane,
60
- wave: wave.wave || wave,
66
+ wave: waveNumber,
61
67
  createdAt: toIsoTimestamp(),
62
68
  updatedAt: toIsoTimestamp(),
63
69
  releaseNotesRequired,