@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.
- package/CHANGELOG.md +52 -3
- package/README.md +33 -5
- package/docs/README.md +18 -4
- package/docs/agents/wave-cont-eval-role.md +36 -0
- package/docs/agents/{wave-evaluator-role.md → wave-cont-qa-role.md} +14 -11
- package/docs/agents/wave-documentation-role.md +1 -1
- package/docs/agents/wave-infra-role.md +1 -1
- package/docs/agents/wave-integration-role.md +3 -3
- package/docs/agents/wave-launcher-role.md +4 -3
- package/docs/agents/wave-security-role.md +40 -0
- package/docs/concepts/context7-vs-skills.md +1 -1
- package/docs/concepts/what-is-a-wave.md +56 -6
- package/docs/evals/README.md +166 -0
- package/docs/evals/benchmark-catalog.json +663 -0
- package/docs/guides/author-and-run-waves.md +135 -0
- package/docs/guides/planner.md +5 -0
- package/docs/guides/terminal-surfaces.md +2 -0
- package/docs/plans/component-cutover-matrix.json +1 -1
- package/docs/plans/component-cutover-matrix.md +1 -1
- package/docs/plans/current-state.md +19 -1
- package/docs/plans/examples/wave-example-live-proof.md +435 -0
- package/docs/plans/migration.md +42 -0
- package/docs/plans/wave-orchestrator.md +46 -7
- package/docs/plans/waves/wave-0.md +4 -4
- package/docs/reference/live-proof-waves.md +177 -0
- package/docs/reference/migration-0.2-to-0.5.md +26 -19
- package/docs/reference/npmjs-trusted-publishing.md +6 -5
- package/docs/reference/runtime-config/README.md +14 -4
- package/docs/reference/sample-waves.md +87 -0
- package/docs/reference/skills.md +110 -42
- package/docs/research/agent-context-sources.md +130 -11
- package/docs/research/coordination-failure-review.md +266 -0
- package/docs/roadmap.md +6 -2
- package/package.json +2 -2
- package/releases/manifest.json +35 -2
- package/scripts/research/agent-context-archive.mjs +83 -1
- package/scripts/research/manifests/agent-context-expanded-2026-03-22.mjs +811 -0
- package/scripts/wave-orchestrator/adhoc.mjs +1331 -0
- package/scripts/wave-orchestrator/agent-state.mjs +358 -6
- package/scripts/wave-orchestrator/artifact-schemas.mjs +173 -0
- package/scripts/wave-orchestrator/clarification-triage.mjs +10 -3
- package/scripts/wave-orchestrator/config.mjs +48 -12
- package/scripts/wave-orchestrator/context7.mjs +2 -0
- package/scripts/wave-orchestrator/coord-cli.mjs +51 -19
- package/scripts/wave-orchestrator/coordination-store.mjs +26 -4
- package/scripts/wave-orchestrator/coordination.mjs +83 -9
- package/scripts/wave-orchestrator/dashboard-state.mjs +20 -8
- package/scripts/wave-orchestrator/dep-cli.mjs +5 -2
- package/scripts/wave-orchestrator/docs-queue.mjs +8 -2
- package/scripts/wave-orchestrator/evals.mjs +451 -0
- package/scripts/wave-orchestrator/feedback.mjs +15 -1
- package/scripts/wave-orchestrator/install.mjs +32 -9
- package/scripts/wave-orchestrator/launcher-closure.mjs +281 -0
- package/scripts/wave-orchestrator/launcher-runtime.mjs +334 -0
- package/scripts/wave-orchestrator/launcher.mjs +709 -601
- package/scripts/wave-orchestrator/ledger.mjs +123 -20
- package/scripts/wave-orchestrator/local-executor.mjs +99 -12
- package/scripts/wave-orchestrator/planner.mjs +177 -42
- package/scripts/wave-orchestrator/replay.mjs +6 -3
- package/scripts/wave-orchestrator/role-helpers.mjs +84 -0
- package/scripts/wave-orchestrator/shared.mjs +75 -11
- package/scripts/wave-orchestrator/skills.mjs +637 -106
- package/scripts/wave-orchestrator/traces.mjs +71 -48
- package/scripts/wave-orchestrator/wave-files.mjs +947 -101
- package/scripts/wave.mjs +9 -0
- package/skills/README.md +202 -0
- package/skills/provider-aws/SKILL.md +111 -0
- package/skills/provider-aws/adapters/claude.md +1 -0
- package/skills/provider-aws/adapters/codex.md +1 -0
- package/skills/provider-aws/references/service-verification.md +39 -0
- package/skills/provider-aws/skill.json +50 -1
- package/skills/provider-custom-deploy/SKILL.md +59 -0
- package/skills/provider-custom-deploy/skill.json +46 -1
- package/skills/provider-docker-compose/SKILL.md +90 -0
- package/skills/provider-docker-compose/adapters/local.md +1 -0
- package/skills/provider-docker-compose/skill.json +49 -1
- package/skills/provider-github-release/SKILL.md +116 -1
- package/skills/provider-github-release/adapters/claude.md +1 -0
- package/skills/provider-github-release/adapters/codex.md +1 -0
- package/skills/provider-github-release/skill.json +51 -1
- package/skills/provider-kubernetes/SKILL.md +137 -0
- package/skills/provider-kubernetes/adapters/claude.md +1 -0
- package/skills/provider-kubernetes/adapters/codex.md +1 -0
- package/skills/provider-kubernetes/references/kubectl-patterns.md +58 -0
- package/skills/provider-kubernetes/skill.json +48 -1
- package/skills/provider-railway/SKILL.md +118 -1
- package/skills/provider-railway/references/verification-commands.md +39 -0
- package/skills/provider-railway/skill.json +67 -1
- package/skills/provider-ssh-manual/SKILL.md +91 -0
- package/skills/provider-ssh-manual/skill.json +50 -1
- package/skills/repo-coding-rules/SKILL.md +84 -0
- package/skills/repo-coding-rules/skill.json +30 -1
- package/skills/role-cont-eval/SKILL.md +90 -0
- package/skills/role-cont-eval/adapters/codex.md +1 -0
- package/skills/role-cont-eval/skill.json +36 -0
- package/skills/role-cont-qa/SKILL.md +93 -0
- package/skills/role-cont-qa/adapters/claude.md +1 -0
- package/skills/role-cont-qa/skill.json +36 -0
- package/skills/role-deploy/SKILL.md +90 -0
- package/skills/role-deploy/skill.json +32 -1
- package/skills/role-documentation/SKILL.md +66 -0
- package/skills/role-documentation/skill.json +32 -1
- package/skills/role-implementation/SKILL.md +62 -0
- package/skills/role-implementation/skill.json +32 -1
- package/skills/role-infra/SKILL.md +74 -0
- package/skills/role-infra/skill.json +32 -1
- package/skills/role-integration/SKILL.md +79 -1
- package/skills/role-integration/skill.json +32 -1
- package/skills/role-research/SKILL.md +58 -0
- package/skills/role-research/skill.json +32 -1
- package/skills/role-security/SKILL.md +60 -0
- package/skills/role-security/skill.json +36 -0
- package/skills/runtime-claude/SKILL.md +60 -1
- package/skills/runtime-claude/skill.json +32 -1
- package/skills/runtime-codex/SKILL.md +52 -1
- package/skills/runtime-codex/skill.json +32 -1
- package/skills/runtime-local/SKILL.md +39 -0
- package/skills/runtime-local/skill.json +32 -1
- package/skills/runtime-opencode/SKILL.md +51 -0
- package/skills/runtime-opencode/skill.json +32 -1
- package/skills/wave-core/SKILL.md +107 -0
- package/skills/wave-core/references/marker-syntax.md +62 -0
- package/skills/wave-core/skill.json +31 -1
- package/wave.config.json +35 -6
- package/skills/role-evaluator/SKILL.md +0 -6
- 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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
268
|
-
rawRoles.
|
|
269
|
-
"roles.
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
218
|
-
|
|
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
|
-
|
|
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 ===
|
|
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 ===
|
|
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
|
|
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
|
-
|
|
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
|
|
215
|
-
|
|
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 ${
|
|
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
|
|
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
|
-
![
|
|
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
|
-
![
|
|
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
|
-
`-
|
|
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
|
-
...
|
|
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
|
-
|
|
243
|
-
|
|
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
|
-
|
|
249
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
66
|
+
wave: waveNumber,
|
|
61
67
|
createdAt: toIsoTimestamp(),
|
|
62
68
|
updatedAt: toIsoTimestamp(),
|
|
63
69
|
releaseNotesRequired,
|