@chllming/wave-orchestration 0.8.6 → 0.8.8
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 +38 -0
- package/README.md +5 -5
- package/docs/README.md +3 -1
- package/docs/guides/author-and-run-waves.md +1 -1
- package/docs/guides/planner.md +1 -1
- package/docs/guides/recommendations-0.8.8.md +133 -0
- package/docs/guides/terminal-surfaces.md +2 -0
- package/docs/plans/current-state.md +2 -1
- package/docs/plans/end-state-architecture.md +1 -1
- package/docs/plans/examples/wave-example-design-handoff.md +1 -1
- package/docs/plans/examples/wave-example-live-proof.md +1 -1
- package/docs/plans/migration.md +25 -8
- package/docs/plans/wave-orchestrator.md +8 -5
- package/docs/reference/cli-reference.md +11 -3
- package/docs/reference/coordination-and-closure.md +28 -5
- package/docs/reference/live-proof-waves.md +9 -0
- package/docs/reference/npmjs-trusted-publishing.md +2 -2
- package/docs/reference/runtime-config/README.md +10 -3
- package/docs/reference/sample-waves.md +5 -5
- package/docs/reference/skills.md +1 -1
- package/docs/reference/wave-control.md +16 -0
- package/docs/reference/wave-planning-lessons.md +7 -1
- package/docs/research/coordination-failure-review.md +6 -6
- package/package.json +1 -1
- package/releases/manifest.json +36 -0
- package/scripts/wave-orchestrator/agent-state.mjs +42 -0
- package/scripts/wave-orchestrator/autonomous.mjs +42 -6
- package/scripts/wave-orchestrator/clarification-triage.mjs +4 -3
- package/scripts/wave-orchestrator/control-cli.mjs +126 -11
- package/scripts/wave-orchestrator/control-plane.mjs +12 -1
- package/scripts/wave-orchestrator/coordination-store.mjs +124 -4
- package/scripts/wave-orchestrator/executors.mjs +11 -6
- package/scripts/wave-orchestrator/gate-engine.mjs +5 -5
- package/scripts/wave-orchestrator/launcher-runtime.mjs +1 -1
- package/scripts/wave-orchestrator/launcher.mjs +216 -0
- package/scripts/wave-orchestrator/ledger.mjs +14 -12
- package/scripts/wave-orchestrator/reducer-snapshot.mjs +8 -6
- package/scripts/wave-orchestrator/retry-engine.mjs +19 -11
- package/scripts/wave-orchestrator/routing-state.mjs +50 -3
- package/scripts/wave-orchestrator/session-supervisor.mjs +6 -10
- package/scripts/wave-orchestrator/task-entity.mjs +4 -4
- package/scripts/wave-orchestrator/terminals.mjs +14 -14
- package/scripts/wave-orchestrator/wave-files.mjs +15 -21
- package/scripts/wave-orchestrator/wave-state-reducer.mjs +72 -5
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Sample Waves"
|
|
3
|
-
summary: "Showcase-first sample waves that demonstrate the shipped 0.8.
|
|
3
|
+
summary: "Showcase-first sample waves that demonstrate the shipped 0.8.8 authored surface, including the optional design-role path."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Sample Waves
|
|
7
7
|
|
|
8
|
-
This guide points to showcase-first sample waves that demonstrate the shipped `0.8.
|
|
8
|
+
This guide points to showcase-first sample waves that demonstrate the shipped `0.8.8` authored Wave surface.
|
|
9
9
|
|
|
10
10
|
The examples are intentionally denser than typical production waves. Their job is to teach the current authoring and runtime surface quickly, not to be the smallest possible launch-ready files.
|
|
11
11
|
|
|
@@ -15,7 +15,7 @@ The examples are intentionally denser than typical production waves. Their job i
|
|
|
15
15
|
Shows what a good `repo-landed` outcome looks like when one promoted component only closes honestly if desired-state records, reconcile-loop substrate, and cluster-view surfaces land together. It emphasizes maturity discipline, explicit deliverables, and shared-plan closure without drifting into `pilot-live` claims.
|
|
16
16
|
|
|
17
17
|
- [Full modern sample wave](../plans/examples/wave-example-live-proof.md)
|
|
18
|
-
Shows the combined `0.8.
|
|
18
|
+
Shows the combined `0.8.8` authored surface in one file: closure roles, `E0`, optional security review, delegated and pinned benchmark targets, richer executor config, `### Skills`, `### Capabilities`, `### Deliverables`, `### Exit contract`, `### Proof artifacts`, sticky retry, deploy environments, and proof-first live-wave structure.
|
|
19
19
|
|
|
20
20
|
- [Optional design-steward handoff wave](../plans/examples/wave-example-design-handoff.md)
|
|
21
21
|
Shows the shipped design-role surface: one pre-implementation design steward publishes a design packet, downstream implementation owners read that packet before coding, and normal closure roles still decide final completion. For terminal or operator-surface work, pair that shape with explicit `tui-design` in the design steward's `### Skills`. For the hybrid variant, explicitly give that same design agent implementation-owned paths and the normal implementation contract sections.
|
|
@@ -42,7 +42,7 @@ The examples are intentionally denser than typical production waves. Their job i
|
|
|
42
42
|
|
|
43
43
|
## Feature Coverage Map
|
|
44
44
|
|
|
45
|
-
Together these samples cover the main surfaces added or hardened through `0.8.
|
|
45
|
+
Together these samples cover the main surfaces added or hardened through `0.8.8`:
|
|
46
46
|
|
|
47
47
|
- repo-landed maturity discipline and anti-overclaim framing
|
|
48
48
|
- explicit shared-plan closure for future-wave safety
|
|
@@ -89,7 +89,7 @@ Adapt more aggressively when:
|
|
|
89
89
|
## Suggested Reading Order
|
|
90
90
|
|
|
91
91
|
1. Start with [High-fidelity repo-landed rollout wave](../plans/examples/wave-example-rollout-fidelity.md) if you want the clearest example of good closure-ready wave fidelity for a repo-only outcome.
|
|
92
|
-
2. Read [Full modern sample wave](../plans/examples/wave-example-live-proof.md) if you want the denser proof-first and eval-heavy `0.8.
|
|
92
|
+
2. Read [Full modern sample wave](../plans/examples/wave-example-live-proof.md) if you want the denser proof-first and eval-heavy `0.8.8` surface.
|
|
93
93
|
3. Read [Optional design-steward handoff wave](../plans/examples/wave-example-design-handoff.md) if the task needs a design packet before implementation fan-out.
|
|
94
94
|
4. Read [docs/evals/README.md](../evals/README.md) if you want more background on benchmark target selection.
|
|
95
95
|
5. Read [docs/reference/live-proof-waves.md](./live-proof-waves.md) if you want more detail on proof-first `pilot-live` authoring.
|
package/docs/reference/skills.md
CHANGED
|
@@ -124,7 +124,7 @@ Top-level and lane-local skill attachment use the same shape:
|
|
|
124
124
|
|
|
125
125
|
Lane-local `lanes.<lane>.skills` extends the global config instead of replacing it.
|
|
126
126
|
|
|
127
|
-
Optional design workers in the shipped `0.8.
|
|
127
|
+
Optional design workers in the shipped `0.8.8` surface normally attach `role-design`. That bundle is intended for docs/spec-first design packets and explicit implementation handoff work before implementation starts. When the design packet covers terminal UX, dashboards, or other operator surfaces, add `tui-design` explicitly in the wave's `### Skills`.
|
|
128
128
|
|
|
129
129
|
Long-running agents that should stay resident and react only to orchestrator signal changes can add `signal-hygiene` explicitly in `### Skills`. That bundle is not auto-attached and is not meant for normal one-shot implementation agents.
|
|
130
130
|
|
|
@@ -42,6 +42,8 @@ This lets the control plane answer:
|
|
|
42
42
|
- which proof and benchmark artifacts back a claim
|
|
43
43
|
- whether a benchmark result is comparison-valid or only diagnostic
|
|
44
44
|
- which coordination failures blocked closure
|
|
45
|
+
- which blockers were hard, soft, stale, or advisory
|
|
46
|
+
- whether a blocked wave is terminal or recoverable and which targeted rerun request was queued
|
|
45
47
|
|
|
46
48
|
## Run Identity
|
|
47
49
|
|
|
@@ -92,6 +94,20 @@ Signals to preserve:
|
|
|
92
94
|
- benchmark trust:
|
|
93
95
|
every benchmark item should distinguish capability from validity
|
|
94
96
|
|
|
97
|
+
## Blocker And Recovery Metadata
|
|
98
|
+
|
|
99
|
+
Wave Control should preserve the softer runtime policy, not flatten it away.
|
|
100
|
+
|
|
101
|
+
In practice that means `coordination_record`, `task`, `gate`, `wave_run`, and `rerun_request` payloads should keep fields such as:
|
|
102
|
+
|
|
103
|
+
- `blocking`
|
|
104
|
+
- `blockerSeverity`
|
|
105
|
+
- `recoverable`
|
|
106
|
+
- `recoveryReason`
|
|
107
|
+
- queued rerun request ids or resume targets
|
|
108
|
+
|
|
109
|
+
That distinction matters because a wave that is `blocked` by a proof-critical gate is different from a wave that is `blocked` only long enough to surface a targeted recovery after timeout, max-turn, rate-limit, or missing-status failure. The control plane should let operators ask which barriers still stop closure outright and which ones were intentionally downgraded to advisory or stale context.
|
|
110
|
+
|
|
95
111
|
## Artifact Contract
|
|
96
112
|
|
|
97
113
|
Selected artifacts are described with typed descriptors:
|
|
@@ -74,11 +74,15 @@ runtime setup, and the closure artifacts all describe the same truth.
|
|
|
74
74
|
## 7. Runtime setup matters as much as wave prose
|
|
75
75
|
|
|
76
76
|
- Do not use small fixed turn caps for synthesis-heavy or closure-heavy agents.
|
|
77
|
-
Bound them with `budget.minutes`, not `budget.turns`.
|
|
77
|
+
Bound them with `budget.minutes`, not generic `budget.turns`.
|
|
78
|
+
- Treat generic `budget.turns` as advisory unless you intentionally set a
|
|
79
|
+
runtime-specific hard stop such as `claude.max_turns` or `opencode.steps`.
|
|
78
80
|
- Pin exact model and reasoning settings for each runtime. Ambiguous profiles
|
|
79
81
|
create unclear failure modes.
|
|
80
82
|
- Avoid cross-runtime fallback on live-proof or deploy-sensitive slices unless
|
|
81
83
|
there is a very good reason.
|
|
84
|
+
- For non-proof-centric owners, prefer targeted recovery and reuse over broad
|
|
85
|
+
relaunch when a timeout or max-turn event leaves partial artifacts behind.
|
|
82
86
|
- Context7 should be explicit and real; unresolved bundles create noise instead
|
|
83
87
|
of help.
|
|
84
88
|
|
|
@@ -121,6 +125,8 @@ runtime setup, and the closure artifacts all describe the same truth.
|
|
|
121
125
|
- Are A8 and A0 told what would make the wave fail honestly?
|
|
122
126
|
- Are runtime pins, Context7 bundles, and budgets specific enough to avoid
|
|
123
127
|
preventable execution failures?
|
|
128
|
+
- Can any non-proof coordination ask be authored as `soft`, `stale`, or
|
|
129
|
+
`advisory` instead of silently becoming a hard closure blocker?
|
|
124
130
|
- Would a reviewer understand the difference between “code landed” and
|
|
125
131
|
“component promoted” just by reading the wave file?
|
|
126
132
|
|
|
@@ -229,16 +229,16 @@ This is the central failure highlighted by `HiddenBench` and `Silo-Bench`, and t
|
|
|
229
229
|
|
|
230
230
|
### 3. Expertise routing is explicit, but shallow
|
|
231
231
|
|
|
232
|
-
[scripts/wave-orchestrator/routing-state.mjs](../../scripts/wave-orchestrator/routing-state.mjs) is better than unconstrained self-organization, but it still routes mostly by:
|
|
232
|
+
[scripts/wave-orchestrator/routing-state.mjs](../../scripts/wave-orchestrator/routing-state.mjs) is better than unconstrained self-organization, and it now has a light same-wave success preference, but it still routes mostly by:
|
|
233
233
|
|
|
234
234
|
- explicit target
|
|
235
235
|
- configured preferred agents
|
|
236
236
|
- declared capability ownership
|
|
237
|
+
- demonstrated same-wave completions on the capability
|
|
237
238
|
- least-busy fallback
|
|
238
239
|
|
|
239
|
-
|
|
240
|
+
Beyond that light historical-success preference, it still does not weight:
|
|
240
241
|
|
|
241
|
-
- historical success on a capability
|
|
242
242
|
- evidence quality by agent
|
|
243
243
|
- confidence calibration
|
|
244
244
|
- expert-leverage metrics
|
|
@@ -247,14 +247,14 @@ So the repo partially addresses the concern from `Multi-Agent Teams Hold Experts
|
|
|
247
247
|
|
|
248
248
|
### 4. Clarification and contradiction handling are still somewhat heuristic
|
|
249
249
|
|
|
250
|
-
Clarification triage and integration evidence aggregation are real safeguards, but they still lean heavily on:
|
|
250
|
+
Clarification triage, blocker taxonomy, operator downgrade controls, and integration evidence aggregation are real safeguards, but they still lean heavily on:
|
|
251
251
|
|
|
252
252
|
- ownership mappings
|
|
253
253
|
- artifact references
|
|
254
254
|
- structured markers
|
|
255
255
|
- text-level summaries and conflict extraction
|
|
256
256
|
|
|
257
|
-
That is enough to make the runtime operationally safer
|
|
257
|
+
That is enough to make the runtime operationally safer. The newer hard-vs-soft blocker split also removes some unnecessary terminal failures by letting stale or advisory coordination remain visible without owning closure. But it is not yet a richer semantic evidence-integration layer, and subtle contradictions or latent information asymmetries may still be missed.
|
|
258
258
|
|
|
259
259
|
### 5. DPBench-style simultaneous coordination is only indirectly addressed
|
|
260
260
|
|
|
@@ -283,7 +283,7 @@ So the design points in the right direction, but the claim is not yet validated.
|
|
|
283
283
|
|
|
284
284
|
If the standard is "does this repo merely claim multi-agent coordination," the answer is no. It has real machinery for blackboard-like state sharing, evidence-based closure, clarification handling, and coordination diagnostics.
|
|
285
285
|
|
|
286
|
-
If the standard is "has this repo already demonstrated that its design beats the core failure modes isolated by HiddenBench, Silo-Bench, DPBench, and related work," the answer is also no. The design is substantially more credible than most MAS stacks, but the empirical proof is still missing.
|
|
286
|
+
If the standard is "has this repo already demonstrated that its design beats the core failure modes isolated by HiddenBench, Silo-Bench, DPBench, and related work," the answer is also no. The design is substantially more credible than most MAS stacks, and it now also reduces avoidable failure through targeted recovery, blocker severity, and policy-safe downgrade paths, but the empirical proof is still missing.
|
|
287
287
|
|
|
288
288
|
The most accurate claim today is:
|
|
289
289
|
|
package/package.json
CHANGED
package/releases/manifest.json
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "@chllming/wave-orchestration",
|
|
4
4
|
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.8.8",
|
|
7
|
+
"date": "2026-03-27",
|
|
8
|
+
"summary": "0.8.8 release-surface alignment, packaged operating recommendations, and install-state fixture refresh.",
|
|
9
|
+
"features": [
|
|
10
|
+
"The practical operating recommendations guide now ships as `docs/guides/recommendations-0.8.8.md`, and the README, migration, coordination, and runtime-config docs now point at the same current package surface.",
|
|
11
|
+
"The tracked `.wave/install-state.json` fixture and upgrade-history metadata now align with the `0.8.8` package version so `wave doctor --json` no longer reports stale install-state versioning after the follow-up docs release.",
|
|
12
|
+
"Release-surface regression coverage now derives the recommendations-guide path from `package.json` instead of a hard-coded `0.8.7` filename, which hardens future release cuts against the same doc-link drift.",
|
|
13
|
+
"Planner migration guidance and the `planner-agentic` bundle placeholder remain part of the shipped current-surface docs so adopted repos still have one aligned upgrade target."
|
|
14
|
+
],
|
|
15
|
+
"manualSteps": [
|
|
16
|
+
"Run `pnpm exec wave doctor` and `pnpm exec wave launch --lane main --dry-run --no-dashboard` after upgrading so the repo validates against the `0.8.8` release surface.",
|
|
17
|
+
"If your repo copied current-surface docs or runbooks, sync `README.md`, `docs/README.md`, `docs/plans/current-state.md`, `docs/plans/migration.md`, `docs/reference/coordination-and-closure.md`, `docs/reference/runtime-config/README.md`, and `docs/guides/recommendations-0.8.8.md` so local guidance matches the packaged release.",
|
|
18
|
+
"If your repo tracks the install-state fixture in source control, refresh `.wave/install-state.json` and the matching upgrade-history report so repo-owned validation stays aligned with the installed package."
|
|
19
|
+
],
|
|
20
|
+
"breaking": false
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"version": "0.8.7",
|
|
24
|
+
"date": "2026-03-27",
|
|
25
|
+
"summary": "Policy-consistency hardening, capability-specific same-wave routing, stable per-wave tmux session reuse, and 0.8.7 release-surface alignment.",
|
|
26
|
+
"features": [
|
|
27
|
+
"Generic `budget.turns` is now documented and tested consistently as advisory metadata only; hard runtime turn ceilings come only from runtime-specific settings such as `claude.maxTurns` or `opencode.steps`.",
|
|
28
|
+
"Capability-targeted helper routing now prefers demonstrated same-wave success for the requested capability before falling back to the least-busy matching capability owner, and unrelated completed work no longer counts as routing evidence.",
|
|
29
|
+
"Advisory, stale, and other non-blocking clarification or human-input records stay visible in control and reducer projections without reopening hard blocked reducer state by themselves.",
|
|
30
|
+
"Wave-agent, resident-orchestrator, and per-wave dashboard tmux sessions now reuse stable per-wave session names, so stale launcher exits stop accumulating extra sessions for the same wave.",
|
|
31
|
+
"Structured signal extraction now also recognizes markers embedded inside JSON log lines, so wrapped executor transcripts still produce proof, doc-delta, and component evidence."
|
|
32
|
+
],
|
|
33
|
+
"manualSteps": [
|
|
34
|
+
"Run `pnpm exec wave doctor` and `pnpm exec wave launch --lane main --dry-run --no-dashboard` after upgrading so the repo validates against the `0.8.7` routing, blocker-severity, signal-wrapper, and stable-session behavior.",
|
|
35
|
+
"If your repo copied starter scripts or operator docs, sync `scripts/wave-status.sh`, `scripts/wave-watch.sh`, `docs/guides/signal-wrappers.md`, `docs/guides/terminal-surfaces.md`, `docs/reference/cli-reference.md`, and any local tmux/session runbooks that still assume run-tagged session names.",
|
|
36
|
+
"If your repo copied planner or routing guidance, sync `docs/guides/planner.md`, `docs/reference/wave-planning-lessons.md`, `docs/plans/wave-orchestrator.md`, the `planner-agentic` bundle entry in `docs/context7/bundles.json`, and any local helper-assignment policy docs so they describe capability-specific same-wave routing evidence instead of generic prior completion.",
|
|
37
|
+
"If your repo relies on advisory `budget.turns` as if it were a hard ceiling, move that limit to the runtime-specific executor config (`claude.maxTurns` or `opencode.steps`) before you depend on deterministic turn enforcement."
|
|
38
|
+
],
|
|
39
|
+
"breaking": false
|
|
40
|
+
},
|
|
5
41
|
{
|
|
6
42
|
"version": "0.8.6",
|
|
7
43
|
"date": "2026-03-25",
|
|
@@ -160,6 +160,44 @@ function appendParsedStructuredSignalCandidates(lines, candidates, { requireAll
|
|
|
160
160
|
candidates.push(...parsedCandidates);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
function collectEmbeddedStructuredSignalTexts(value, texts) {
|
|
164
|
+
if (!value || typeof value !== "object") {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (Array.isArray(value)) {
|
|
168
|
+
for (const item of value) {
|
|
169
|
+
collectEmbeddedStructuredSignalTexts(item, texts);
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (typeof value.text === "string") {
|
|
174
|
+
texts.push(value.text);
|
|
175
|
+
}
|
|
176
|
+
if (typeof value.aggregated_output === "string") {
|
|
177
|
+
texts.push(value.aggregated_output);
|
|
178
|
+
}
|
|
179
|
+
for (const nestedValue of Object.values(value)) {
|
|
180
|
+
if (nestedValue && typeof nestedValue === "object") {
|
|
181
|
+
collectEmbeddedStructuredSignalTexts(nestedValue, texts);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function extractEmbeddedStructuredSignalTextsFromJsonLine(line) {
|
|
187
|
+
const trimmed = String(line || "").trim();
|
|
188
|
+
if (!trimmed || !/^[{\[]/.test(trimmed)) {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
const payload = JSON.parse(trimmed);
|
|
193
|
+
const texts = [];
|
|
194
|
+
collectEmbeddedStructuredSignalTexts(payload, texts);
|
|
195
|
+
return texts.filter(Boolean);
|
|
196
|
+
} catch {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
163
201
|
function collectStructuredSignalCandidates(text) {
|
|
164
202
|
if (!text) {
|
|
165
203
|
return [];
|
|
@@ -167,6 +205,10 @@ function collectStructuredSignalCandidates(text) {
|
|
|
167
205
|
const candidates = [];
|
|
168
206
|
let fenceLines = null;
|
|
169
207
|
for (const rawLine of String(text || "").split(/\r?\n/)) {
|
|
208
|
+
const embeddedTexts = extractEmbeddedStructuredSignalTextsFromJsonLine(rawLine);
|
|
209
|
+
for (const embeddedText of embeddedTexts) {
|
|
210
|
+
candidates.push(...collectStructuredSignalCandidates(embeddedText));
|
|
211
|
+
}
|
|
170
212
|
const trimmed = rawLine.trim();
|
|
171
213
|
if (/^```/.test(trimmed)) {
|
|
172
214
|
if (fenceLines === null) {
|
|
@@ -27,8 +27,13 @@ import {
|
|
|
27
27
|
maybeAnnouncePackageUpdate,
|
|
28
28
|
WAVE_SUPPRESS_UPDATE_NOTICE_ENV,
|
|
29
29
|
} from "./package-update-notice.mjs";
|
|
30
|
+
import { buildTaskSnapshots } from "./control-plane.mjs";
|
|
31
|
+
import { readWaveHumanFeedbackRequests } from "./coordination.mjs";
|
|
30
32
|
import { readRunState } from "./wave-files.mjs";
|
|
31
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
readDependencyTickets,
|
|
35
|
+
readMaterializedCoordinationState,
|
|
36
|
+
} from "./coordination-store.mjs";
|
|
32
37
|
import { readWaveLedger } from "./ledger.mjs";
|
|
33
38
|
|
|
34
39
|
const AUTONOMOUS_EXECUTOR_MODES = SUPPORTED_EXECUTOR_MODES.filter((mode) => mode !== "local");
|
|
@@ -249,7 +254,38 @@ function requiredInboundDependenciesOpen(lanePaths, lane) {
|
|
|
249
254
|
});
|
|
250
255
|
}
|
|
251
256
|
|
|
252
|
-
function
|
|
257
|
+
function liveBlockingHumanItemsForWave(lanePaths, lane, wave) {
|
|
258
|
+
if (!lanePaths?.coordinationDir || !lanePaths?.feedbackRequestsDir) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const coordinationState = readMaterializedCoordinationState(
|
|
262
|
+
path.join(lanePaths.coordinationDir, `wave-${wave}.jsonl`),
|
|
263
|
+
);
|
|
264
|
+
const feedbackRequests = readWaveHumanFeedbackRequests({
|
|
265
|
+
feedbackRequestsDir: lanePaths.feedbackRequestsDir,
|
|
266
|
+
lane,
|
|
267
|
+
waveNumber: wave,
|
|
268
|
+
agentIds: [],
|
|
269
|
+
orchestratorId: "",
|
|
270
|
+
});
|
|
271
|
+
return buildTaskSnapshots({
|
|
272
|
+
coordinationState,
|
|
273
|
+
feedbackRequests,
|
|
274
|
+
})
|
|
275
|
+
.filter(
|
|
276
|
+
(task) =>
|
|
277
|
+
["human-input", "escalation"].includes(task.taskType) &&
|
|
278
|
+
task.blocking !== false &&
|
|
279
|
+
["open", "working", "input-required"].includes(task.state),
|
|
280
|
+
)
|
|
281
|
+
.map((task) => task.taskId);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function pendingHumanItemsForWave(lanePaths, lane, wave) {
|
|
285
|
+
const liveItems = liveBlockingHumanItemsForWave(lanePaths, lane, wave);
|
|
286
|
+
if (Array.isArray(liveItems)) {
|
|
287
|
+
return liveItems;
|
|
288
|
+
}
|
|
253
289
|
const existingLedger = readWaveLedger(path.join(lanePaths.ledgerDir, `wave-${wave}.json`));
|
|
254
290
|
return [
|
|
255
291
|
...(existingLedger?.humanFeedback || []),
|
|
@@ -257,7 +293,7 @@ function pendingHumanItemsForWave(lanePaths, wave) {
|
|
|
257
293
|
];
|
|
258
294
|
}
|
|
259
295
|
|
|
260
|
-
function pendingHumanItemsForLane(lanePaths) {
|
|
296
|
+
function pendingHumanItemsForLane(lanePaths, lane) {
|
|
261
297
|
if (!fs.existsSync(lanePaths.ledgerDir)) {
|
|
262
298
|
return [];
|
|
263
299
|
}
|
|
@@ -271,7 +307,7 @@ function pendingHumanItemsForLane(lanePaths) {
|
|
|
271
307
|
.filter((item) => Number.isFinite(item.wave))
|
|
272
308
|
.sort((left, right) => left.wave - right.wave)
|
|
273
309
|
.flatMap((item) =>
|
|
274
|
-
pendingHumanItemsForWave(lanePaths, item.wave).map((id) => ({
|
|
310
|
+
pendingHumanItemsForWave(lanePaths, lane, item.wave).map((id) => ({
|
|
275
311
|
wave: item.wave,
|
|
276
312
|
id,
|
|
277
313
|
})),
|
|
@@ -292,7 +328,7 @@ export function readAutonomousBarrier(lanePaths, lane, wave = null) {
|
|
|
292
328
|
};
|
|
293
329
|
}
|
|
294
330
|
if (wave === null) {
|
|
295
|
-
const pendingHumanEntries = pendingHumanItemsForLane(lanePaths);
|
|
331
|
+
const pendingHumanEntries = pendingHumanItemsForLane(lanePaths, lane);
|
|
296
332
|
if (pendingHumanEntries.length > 0) {
|
|
297
333
|
return {
|
|
298
334
|
kind: "human-input",
|
|
@@ -303,7 +339,7 @@ export function readAutonomousBarrier(lanePaths, lane, wave = null) {
|
|
|
303
339
|
}
|
|
304
340
|
return null;
|
|
305
341
|
}
|
|
306
|
-
const pendingHumanItems = pendingHumanItemsForWave(lanePaths, wave);
|
|
342
|
+
const pendingHumanItems = pendingHumanItemsForWave(lanePaths, lane, wave);
|
|
307
343
|
if (pendingHumanItems.length > 0) {
|
|
308
344
|
return {
|
|
309
345
|
kind: "human-input",
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
appendCoordinationRecord,
|
|
5
5
|
clarificationClosureCondition,
|
|
6
6
|
clarificationLinkedRequests,
|
|
7
|
+
coordinationRecordBlocksWave,
|
|
7
8
|
isOpenCoordinationStatus,
|
|
8
9
|
readMaterializedCoordinationState,
|
|
9
10
|
} from "./coordination-store.mjs";
|
|
@@ -467,14 +468,14 @@ export function triageClarificationRequests({
|
|
|
467
468
|
ensureDirectory(lanePaths.feedbackTriageDir);
|
|
468
469
|
const triagePath = triageLogPath(lanePaths, wave.wave);
|
|
469
470
|
const openClarifications = (coordinationState?.clarifications || []).filter((record) =>
|
|
470
|
-
|
|
471
|
+
coordinationRecordBlocksWave(record),
|
|
471
472
|
);
|
|
472
473
|
let changed = false;
|
|
473
474
|
|
|
474
475
|
for (const record of openClarifications) {
|
|
475
476
|
const linkedRequests = clarificationLinkedRequests(coordinationState, record.id);
|
|
476
477
|
const openLinkedRequests = linkedRequests.filter((entry) =>
|
|
477
|
-
|
|
478
|
+
coordinationRecordBlocksWave(entry),
|
|
478
479
|
);
|
|
479
480
|
const openAckPendingLinkedRequests = openLinkedRequests.filter(
|
|
480
481
|
(entry) => entry.status === "open",
|
|
@@ -487,7 +488,7 @@ export function triageClarificationRequests({
|
|
|
487
488
|
const openEscalations = (coordinationState?.humanEscalations || []).filter(
|
|
488
489
|
(entry) =>
|
|
489
490
|
entry.closureCondition === clarificationClosureCondition(record.id) &&
|
|
490
|
-
|
|
491
|
+
coordinationRecordBlocksWave(entry),
|
|
491
492
|
);
|
|
492
493
|
if (resolvedLinkedRequest || resolvedEscalation) {
|
|
493
494
|
if (openEscalations.length > 0) {
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
appendCoordinationRecord,
|
|
5
|
+
clarificationClosureCondition,
|
|
6
|
+
clarificationLinkedRequests,
|
|
7
|
+
isOpenCoordinationStatus,
|
|
8
|
+
readMaterializedCoordinationState,
|
|
9
|
+
updateSeedRecords,
|
|
10
|
+
} from "./coordination-store.mjs";
|
|
4
11
|
import { answerFeedbackRequest, createFeedbackRequest } from "./feedback.mjs";
|
|
5
12
|
import { readWaveHumanFeedbackRequests } from "./coordination.mjs";
|
|
6
13
|
import { readWaveLedger } from "./ledger.mjs";
|
|
@@ -18,6 +25,7 @@ import {
|
|
|
18
25
|
REPO_ROOT,
|
|
19
26
|
sanitizeAdhocRunId,
|
|
20
27
|
sanitizeLaneName,
|
|
28
|
+
toIsoTimestamp,
|
|
21
29
|
} from "./shared.mjs";
|
|
22
30
|
import {
|
|
23
31
|
appendWaveControlEvent,
|
|
@@ -50,7 +58,7 @@ function printUsage() {
|
|
|
50
58
|
wave control task create --lane <lane> --wave <n> --agent <id> --kind <request|blocker|clarification|handoff|evidence|claim|decision|human-input> --summary <text> [options]
|
|
51
59
|
wave control task list --lane <lane> --wave <n> [--agent <id>] [--json]
|
|
52
60
|
wave control task get --lane <lane> --wave <n> --id <task-id> [--json]
|
|
53
|
-
wave control task act <start|resolve|dismiss|cancel|reassign|answer|escalate> --lane <lane> --wave <n> --id <task-id> [options]
|
|
61
|
+
wave control task act <start|resolve|dismiss|cancel|reassign|answer|escalate|defer|mark-advisory|mark-stale|resolve-policy> --lane <lane> --wave <n> --id <task-id> [options]
|
|
54
62
|
|
|
55
63
|
wave control rerun request --lane <lane> --wave <n> [--agent <id> ...] [--resume-cursor <cursor>] [--reuse-attempt <id> ...] [--reuse-proof <id> ...] [--reuse-derived-summaries <true|false>] [--invalidate-component <id> ...] [--clear-reuse <id> ...] [--preserve-reuse <id> ...] [--requested-by <name>] [--reason <text>] [--json]
|
|
56
64
|
wave control rerun get --lane <lane> --wave <n> [--json]
|
|
@@ -94,6 +102,8 @@ function parseArgs(argv) {
|
|
|
94
102
|
detail: "",
|
|
95
103
|
targets: [],
|
|
96
104
|
priority: "normal",
|
|
105
|
+
blocking: null,
|
|
106
|
+
blockerSeverity: "",
|
|
97
107
|
dependsOn: [],
|
|
98
108
|
artifactRefs: [],
|
|
99
109
|
status: "open",
|
|
@@ -155,6 +165,10 @@ function parseArgs(argv) {
|
|
|
155
165
|
options.targets.push(String(args[++i] || "").trim());
|
|
156
166
|
} else if (arg === "--priority") {
|
|
157
167
|
options.priority = String(args[++i] || "").trim();
|
|
168
|
+
} else if (arg === "--blocking") {
|
|
169
|
+
options.blocking = normalizeBooleanish(args[++i], true);
|
|
170
|
+
} else if (arg === "--severity") {
|
|
171
|
+
options.blockerSeverity = String(args[++i] || "").trim();
|
|
158
172
|
} else if (arg === "--depends-on") {
|
|
159
173
|
options.dependsOn.push(String(args[++i] || "").trim());
|
|
160
174
|
} else if (arg === "--artifact") {
|
|
@@ -264,7 +278,10 @@ const BLOCKING_TASK_TYPES = new Set([
|
|
|
264
278
|
]);
|
|
265
279
|
|
|
266
280
|
function taskBlocksAgent(task) {
|
|
267
|
-
return
|
|
281
|
+
return (
|
|
282
|
+
BLOCKING_TASK_TYPES.has(String(task?.taskType || "").trim().toLowerCase()) &&
|
|
283
|
+
task?.blocking !== false
|
|
284
|
+
);
|
|
268
285
|
}
|
|
269
286
|
|
|
270
287
|
function assignmentRelevantToAgent(assignment, agentId = "") {
|
|
@@ -462,7 +479,9 @@ function buildBlockingEdge({
|
|
|
462
479
|
selectionTargetsAgent(task.assigneeAgentId, attemptSelection)
|
|
463
480
|
);
|
|
464
481
|
});
|
|
465
|
-
const pendingHuman = scopedTasks.find(
|
|
482
|
+
const pendingHuman = scopedTasks.find(
|
|
483
|
+
(task) => task.state === "input-required" && task.blocking !== false,
|
|
484
|
+
);
|
|
466
485
|
if (pendingHuman) {
|
|
467
486
|
return {
|
|
468
487
|
kind: "human-input",
|
|
@@ -472,7 +491,10 @@ function buildBlockingEdge({
|
|
|
472
491
|
};
|
|
473
492
|
}
|
|
474
493
|
const escalation = scopedTasks.find(
|
|
475
|
-
(task) =>
|
|
494
|
+
(task) =>
|
|
495
|
+
task.taskType === "escalation" &&
|
|
496
|
+
task.blocking !== false &&
|
|
497
|
+
["open", "working"].includes(task.state),
|
|
476
498
|
);
|
|
477
499
|
if (escalation) {
|
|
478
500
|
return {
|
|
@@ -483,7 +505,10 @@ function buildBlockingEdge({
|
|
|
483
505
|
};
|
|
484
506
|
}
|
|
485
507
|
const clarification = scopedTasks.find(
|
|
486
|
-
(task) =>
|
|
508
|
+
(task) =>
|
|
509
|
+
task.taskType === "clarification" &&
|
|
510
|
+
task.blocking !== false &&
|
|
511
|
+
["open", "working"].includes(task.state),
|
|
487
512
|
);
|
|
488
513
|
if (clarification) {
|
|
489
514
|
return {
|
|
@@ -564,7 +589,10 @@ function buildBlockingEdge({
|
|
|
564
589
|
};
|
|
565
590
|
}
|
|
566
591
|
const blocker = scopedTasks.find(
|
|
567
|
-
(task) =>
|
|
592
|
+
(task) =>
|
|
593
|
+
task.taskType === "blocker" &&
|
|
594
|
+
task.blocking !== false &&
|
|
595
|
+
["open", "working"].includes(task.state),
|
|
568
596
|
);
|
|
569
597
|
if (blocker) {
|
|
570
598
|
return {
|
|
@@ -575,7 +603,10 @@ function buildBlockingEdge({
|
|
|
575
603
|
};
|
|
576
604
|
}
|
|
577
605
|
const request = scopedTasks.find(
|
|
578
|
-
(task) =>
|
|
606
|
+
(task) =>
|
|
607
|
+
task.taskType === "request" &&
|
|
608
|
+
task.blocking !== false &&
|
|
609
|
+
["open", "working"].includes(task.state),
|
|
579
610
|
);
|
|
580
611
|
if (request) {
|
|
581
612
|
return {
|
|
@@ -738,7 +769,9 @@ function printStatus(payload) {
|
|
|
738
769
|
function appendCoordinationStatusUpdate(logPath, record, status, options = {}) {
|
|
739
770
|
return appendCoordinationRecord(logPath, {
|
|
740
771
|
...record,
|
|
772
|
+
...(options.patch || {}),
|
|
741
773
|
status,
|
|
774
|
+
updatedAt: options.updatedAt || toIsoTimestamp(),
|
|
742
775
|
summary: options.summary || record.summary,
|
|
743
776
|
detail: options.detail || record.detail,
|
|
744
777
|
source: options.source || "operator",
|
|
@@ -823,6 +856,73 @@ function appendTaskCoordinationEvent(logPath, lanePaths, wave, record, action, o
|
|
|
823
856
|
source: "operator",
|
|
824
857
|
});
|
|
825
858
|
}
|
|
859
|
+
if (action === "defer") {
|
|
860
|
+
return appendCoordinationStatusUpdate(logPath, record, record.status, {
|
|
861
|
+
detail:
|
|
862
|
+
options.detail ||
|
|
863
|
+
`${record.summary || record.id} deferred by operator; keep visible but do not block wave progression.`,
|
|
864
|
+
patch: {
|
|
865
|
+
blocking: false,
|
|
866
|
+
blockerSeverity: "soft",
|
|
867
|
+
},
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
if (action === "mark-advisory") {
|
|
871
|
+
return appendCoordinationStatusUpdate(logPath, record, record.status, {
|
|
872
|
+
detail:
|
|
873
|
+
options.detail ||
|
|
874
|
+
`${record.summary || record.id} marked advisory by operator; keep visible without blocking closure.`,
|
|
875
|
+
patch: {
|
|
876
|
+
blocking: false,
|
|
877
|
+
blockerSeverity: "advisory",
|
|
878
|
+
},
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
if (action === "mark-stale") {
|
|
882
|
+
return appendCoordinationStatusUpdate(logPath, record, record.status, {
|
|
883
|
+
detail:
|
|
884
|
+
options.detail ||
|
|
885
|
+
`${record.summary || record.id} marked stale by operator; historical context preserved without blocking.`,
|
|
886
|
+
patch: {
|
|
887
|
+
blocking: false,
|
|
888
|
+
blockerSeverity: "stale",
|
|
889
|
+
},
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
if (action === "resolve-policy") {
|
|
893
|
+
const resolvedRecord = appendCoordinationStatusUpdate(logPath, record, "resolved", {
|
|
894
|
+
detail: options.detail || `Resolved by operator policy: ${record.summary || record.id}.`,
|
|
895
|
+
patch: {
|
|
896
|
+
blocking: false,
|
|
897
|
+
blockerSeverity: "advisory",
|
|
898
|
+
},
|
|
899
|
+
});
|
|
900
|
+
const policyRecord = appendCoordinationRecord(logPath, {
|
|
901
|
+
id: `policy-${record.id}`,
|
|
902
|
+
lane: lanePaths.lane,
|
|
903
|
+
wave: wave.wave,
|
|
904
|
+
agentId: options.agent || "operator",
|
|
905
|
+
kind: "resolved-by-policy",
|
|
906
|
+
targets: record.targets,
|
|
907
|
+
priority: record.priority,
|
|
908
|
+
artifactRefs: record.artifactRefs,
|
|
909
|
+
dependsOn: Array.from(new Set([record.id, ...(record.dependsOn || [])])),
|
|
910
|
+
closureCondition:
|
|
911
|
+
record.kind === "clarification-request"
|
|
912
|
+
? clarificationClosureCondition(record.id)
|
|
913
|
+
: record.closureCondition || "",
|
|
914
|
+
summary: record.summary,
|
|
915
|
+
detail: options.detail || `Operator resolved ${record.id} by policy.`,
|
|
916
|
+
status: "resolved",
|
|
917
|
+
source: "operator",
|
|
918
|
+
blocking: false,
|
|
919
|
+
blockerSeverity: "advisory",
|
|
920
|
+
});
|
|
921
|
+
return {
|
|
922
|
+
resolvedRecord,
|
|
923
|
+
policyRecord,
|
|
924
|
+
};
|
|
925
|
+
}
|
|
826
926
|
throw new Error(`Unsupported task action: ${action}`);
|
|
827
927
|
}
|
|
828
928
|
|
|
@@ -975,6 +1075,8 @@ export async function runControlCli(argv) {
|
|
|
975
1075
|
artifactRefs: options.artifactRefs,
|
|
976
1076
|
status: options.status,
|
|
977
1077
|
source: "operator",
|
|
1078
|
+
...(options.blocking !== null ? { blocking: options.blocking } : {}),
|
|
1079
|
+
...(options.blockerSeverity ? { blockerSeverity: options.blockerSeverity } : {}),
|
|
978
1080
|
});
|
|
979
1081
|
console.log(JSON.stringify(record, null, 2));
|
|
980
1082
|
return;
|
|
@@ -1067,14 +1169,27 @@ export async function runControlCli(argv) {
|
|
|
1067
1169
|
throw new Error(`Task not found: ${options.id}`);
|
|
1068
1170
|
}
|
|
1069
1171
|
const updated = appendTaskCoordinationEvent(logPath, lanePaths, wave, record, action, options);
|
|
1070
|
-
if (record.kind === "clarification-request" && ["resolve", "dismiss"].includes(action)) {
|
|
1172
|
+
if (record.kind === "clarification-request" && ["resolve", "dismiss", "resolve-policy"].includes(action)) {
|
|
1071
1173
|
const nextStatus = action === "resolve" ? "resolved" : "cancelled";
|
|
1174
|
+
const linkedStatus = action === "resolve-policy" ? "resolved" : nextStatus;
|
|
1072
1175
|
for (const linked of clarificationLinkedRequests(coordinationState, record.id).filter((entry) =>
|
|
1073
1176
|
isOpenCoordinationStatus(entry.status),
|
|
1074
1177
|
)) {
|
|
1075
|
-
appendCoordinationStatusUpdate(logPath, linked,
|
|
1076
|
-
detail:
|
|
1178
|
+
appendCoordinationStatusUpdate(logPath, linked, linkedStatus, {
|
|
1179
|
+
detail:
|
|
1180
|
+
action === "resolve"
|
|
1181
|
+
? `Resolved via clarification ${record.id}.`
|
|
1182
|
+
: action === "resolve-policy"
|
|
1183
|
+
? `Resolved by policy via clarification ${record.id}.`
|
|
1184
|
+
: `Cancelled via clarification ${record.id}.`,
|
|
1077
1185
|
summary: linked.summary,
|
|
1186
|
+
patch:
|
|
1187
|
+
action === "resolve-policy"
|
|
1188
|
+
? {
|
|
1189
|
+
blocking: false,
|
|
1190
|
+
blockerSeverity: "advisory",
|
|
1191
|
+
}
|
|
1192
|
+
: undefined,
|
|
1078
1193
|
});
|
|
1079
1194
|
}
|
|
1080
1195
|
}
|