@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.
Files changed (44) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +5 -5
  3. package/docs/README.md +3 -1
  4. package/docs/guides/author-and-run-waves.md +1 -1
  5. package/docs/guides/planner.md +1 -1
  6. package/docs/guides/recommendations-0.8.8.md +133 -0
  7. package/docs/guides/terminal-surfaces.md +2 -0
  8. package/docs/plans/current-state.md +2 -1
  9. package/docs/plans/end-state-architecture.md +1 -1
  10. package/docs/plans/examples/wave-example-design-handoff.md +1 -1
  11. package/docs/plans/examples/wave-example-live-proof.md +1 -1
  12. package/docs/plans/migration.md +25 -8
  13. package/docs/plans/wave-orchestrator.md +8 -5
  14. package/docs/reference/cli-reference.md +11 -3
  15. package/docs/reference/coordination-and-closure.md +28 -5
  16. package/docs/reference/live-proof-waves.md +9 -0
  17. package/docs/reference/npmjs-trusted-publishing.md +2 -2
  18. package/docs/reference/runtime-config/README.md +10 -3
  19. package/docs/reference/sample-waves.md +5 -5
  20. package/docs/reference/skills.md +1 -1
  21. package/docs/reference/wave-control.md +16 -0
  22. package/docs/reference/wave-planning-lessons.md +7 -1
  23. package/docs/research/coordination-failure-review.md +6 -6
  24. package/package.json +1 -1
  25. package/releases/manifest.json +36 -0
  26. package/scripts/wave-orchestrator/agent-state.mjs +42 -0
  27. package/scripts/wave-orchestrator/autonomous.mjs +42 -6
  28. package/scripts/wave-orchestrator/clarification-triage.mjs +4 -3
  29. package/scripts/wave-orchestrator/control-cli.mjs +126 -11
  30. package/scripts/wave-orchestrator/control-plane.mjs +12 -1
  31. package/scripts/wave-orchestrator/coordination-store.mjs +124 -4
  32. package/scripts/wave-orchestrator/executors.mjs +11 -6
  33. package/scripts/wave-orchestrator/gate-engine.mjs +5 -5
  34. package/scripts/wave-orchestrator/launcher-runtime.mjs +1 -1
  35. package/scripts/wave-orchestrator/launcher.mjs +216 -0
  36. package/scripts/wave-orchestrator/ledger.mjs +14 -12
  37. package/scripts/wave-orchestrator/reducer-snapshot.mjs +8 -6
  38. package/scripts/wave-orchestrator/retry-engine.mjs +19 -11
  39. package/scripts/wave-orchestrator/routing-state.mjs +50 -3
  40. package/scripts/wave-orchestrator/session-supervisor.mjs +6 -10
  41. package/scripts/wave-orchestrator/task-entity.mjs +4 -4
  42. package/scripts/wave-orchestrator/terminals.mjs +14 -14
  43. package/scripts/wave-orchestrator/wave-files.mjs +15 -21
  44. 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.6 authored surface, including the optional design-role path."
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.6` authored Wave surface.
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.6` 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.
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.6`:
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.6` surface.
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.
@@ -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.6` 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`.
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
- It does not yet weight:
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, but it is not yet a richer semantic evidence-integration layer. Subtle contradictions or latent information asymmetries may still be missed.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chllming/wave-orchestration",
3
- "version": "0.8.6",
3
+ "version": "0.8.8",
4
4
  "license": "MIT",
5
5
  "description": "Generic wave-based multi-agent orchestration for repository work.",
6
6
  "repository": {
@@ -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 { readDependencyTickets } from "./coordination-store.mjs";
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 pendingHumanItemsForWave(lanePaths, wave) {
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
- isOpenCoordinationStatus(record.status),
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
- isOpenCoordinationStatus(entry.status),
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
- isOpenCoordinationStatus(entry.status),
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 { appendCoordinationRecord, clarificationClosureCondition, clarificationLinkedRequests, isOpenCoordinationStatus, readMaterializedCoordinationState, updateSeedRecords } from "./coordination-store.mjs";
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 BLOCKING_TASK_TYPES.has(String(task?.taskType || "").trim().toLowerCase());
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((task) => task.state === "input-required");
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) => task.taskType === "escalation" && ["open", "working"].includes(task.state),
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) => task.taskType === "clarification" && ["open", "working"].includes(task.state),
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) => task.taskType === "blocker" && ["open", "working"].includes(task.state),
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) => task.taskType === "request" && ["open", "working"].includes(task.state),
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, nextStatus, {
1076
- detail: `${action === "resolve" ? "Resolved" : "Cancelled"} via clarification ${record.id}.`,
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
  }