@chllming/wave-orchestration 0.8.2 → 0.8.3

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 CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.8.3 - 2026-03-24
6
+
7
+ ### Changed
8
+
9
+ - Updated the shipped package metadata, release manifest, README, migration guide, sample-wave docs, current-state notes, and npm publishing runbook to advertise `0.8.3` as the current release surface.
10
+ - Documented that `wave feedback respond` is a canonical-state repair path, not a feedback-JSON-only update, and that ad-hoc reconciliation must keep the `--run <id>` context.
11
+
12
+ ### Fixed And Hardened
13
+
14
+ - Answered human-feedback requests now reconcile linked clarification, escalation, and helper-assignment state back into the canonical coordination log so reducer state, control surfaces, and launcher gates stop reading the wave as still `clarifying`.
15
+ - `wave feedback respond --run <id>` now carries the ad-hoc run id through the reconciliation helper, so answered human input repairs the isolated ad-hoc lane state instead of the roadmap state root.
16
+ - When a wave is stranded after a human answer arrives and no active attempt is still running, the human-input reconciliation path now writes a safe one-shot continuation request instead of leaving the wave waiting for manual relaunch bookkeeping.
17
+
18
+ ### Testing And Validation
19
+
20
+ - Added regression coverage for direct `wave feedback respond` reconciliation and for the ad-hoc `--run <id>` answer path.
21
+
5
22
  ## 0.8.2 - 2026-03-24
6
23
 
7
24
  ### Changed
@@ -10,12 +27,14 @@
10
27
 
11
28
  ### Fixed And Hardened
12
29
 
13
- - `wave control status` now stops surfacing stale blocking edges after a wave has already reached `phase=completed`.
14
- - Completed waves now suppress stale `nextTimer` deadlines and preserve successful logical-agent states instead of re-blocking agents from historical open request records.
30
+ - `wave control status` now treats `phase=completed` as terminal in the control-status projection layer instead of replaying stale blocking edges from historical open coordination records.
31
+ - Completed waves now return `blockingEdge: null` and `nextTimer: null`, so stale overdue timers or request blockers stop leaking into an already-closed wave view.
32
+ - Successful logical-agent state is now preserved for completed waves, so agents that already finished cleanly stay `closed` or `satisfied` even when old request records remain visible in coordination history.
15
33
 
16
34
  ### Testing And Validation
17
35
 
18
36
  - Added regression coverage for completed-wave control-status projections so historical request records stay visible without reopening blocking state after closure.
37
+ - Revalidated the shipped release surface with the full Vitest suite, `wave doctor --json`, and `wave launch --lane main --dry-run --no-dashboard`.
19
38
 
20
39
  ## 0.8.1 - 2026-03-24
21
40
 
package/README.md CHANGED
@@ -79,18 +79,18 @@ Wave is built to mitigate those failures with a canonical authority set, generat
79
79
 
80
80
  Current release:
81
81
 
82
- - `@chllming/wave-orchestration@0.8.2`
83
- - Release tag: [`v0.8.2`](https://github.com/chllming/agent-wave-orchestrator/releases/tag/v0.8.2)
82
+ - `@chllming/wave-orchestration@0.8.3`
83
+ - Release tag: [`v0.8.3`](https://github.com/chllming/agent-wave-orchestrator/releases/tag/v0.8.3)
84
84
  - Public install path: npmjs
85
85
  - Authenticated fallback: GitHub Packages
86
86
 
87
- Highlights in `0.8.2`:
87
+ Highlights in `0.8.3`:
88
88
 
89
- - `wave control status` now clears stale blocking edges once a wave is already completed instead of replaying historical open request records as live blockers.
90
- - Completed waves now suppress stale `nextTimer` deadlines and preserve successful logical-agent states in the control-status projection.
91
- - The helper-assignment policy-closure fixes from `0.8.1` remain intact.
92
- - The architecture-hardening migration plan, reducer or envelope wiring, and aligned docs or skills from the prior releases remain intact.
93
- - Upgrade and operator docs now cover the full `0.8.2` package surface end to end.
89
+ - Answering a human-feedback request now reconciles linked clarification, escalation, and helper-assignment state back into the canonical coordination log instead of only updating the feedback JSON.
90
+ - `wave feedback respond --run <id>` now applies that same reconciliation and safe continuation flow to ad-hoc runs instead of writing into the roadmap lane state root.
91
+ - When a stranded wave can safely continue after the answer arrives and no attempt is still running, Wave writes a one-shot continuation request automatically.
92
+ - The completed-wave control-status hardening from `0.8.2` remains intact.
93
+ - Upgrade and operator docs now cover the full `0.8.3` package surface end to end.
94
94
 
95
95
  Requirements:
96
96
 
@@ -1,6 +1,6 @@
1
1
  # Current State
2
2
 
3
- - The starter workspace in this source repo reflects the `0.8.2` package release surface.
3
+ - The starter workspace in this source repo reflects the `0.8.3` package release surface.
4
4
  - The staged architecture cutover from launcher-centric decisions to reducer and phase-engine ownership is tracked in `docs/plans/architecture-hardening-migration.md`.
5
5
  - The repository contains the published `@chllming/wave-orchestration` package plus the starter scaffold used by `wave init`.
6
6
  - The runtime is package-first and non-destructive for adopting repos: `wave init --adopt-existing` records existing repo-owned plans, waves, prompts, and config without overwriting them, and `wave upgrade` writes only `.wave/install-state.json` plus `.wave/upgrade-history/`.
@@ -15,6 +15,7 @@
15
15
  - `wave adhoc plan|run|list|show|promote` manage transient operator-driven work
16
16
  - requests, generated specs, rendered markdown, and final results live under `.wave/adhoc/runs/<run-id>/`
17
17
  - runtime state stays isolated under `.tmp/<lane>-wave-launcher/adhoc/<run-id>/`
18
+ - `wave feedback respond --run <run-id>` now reconciles answered human-input state inside that isolated ad-hoc state root and can queue a safe one-shot continuation request without touching roadmap state
18
19
  - ad-hoc runs always keep integration, documentation, and cont-QA closure, while `cont-EVAL` and security review are synthesized only when the request needs them
19
20
  - documentation closure still queues canonical shared-plan docs when a run reports a shared-plan delta, alongside the ad-hoc closure report
20
21
  - `wave adhoc promote` copies the stored ad-hoc spec into numbered roadmap artifacts instead of re-deriving it from the current project profile
@@ -39,6 +40,7 @@
39
40
  - hermetic `traceVersion: 2` per-attempt trace bundles with copied launched-agent summaries, copied component matrices for promoted waves, a hashed `outcome.json` replay baseline, run metadata, and cumulative quality metrics
40
41
  - an internal, read-only replay validator for trace bundles, with legacy `traceVersion: 1` bundles kept in best-effort warning mode
41
42
  - orchestrator-first clarification triage plus human escalation artifacts
43
+ - answered human-feedback responses that reconcile canonical coordination state, helper assignments, and safe continuation intent even when the launcher is no longer active
42
44
  - optional `--resident-orchestrator` support for a long-running, non-owning orchestrator session during live waves
43
45
  - persisted relaunch plans under `.tmp/<lane>-wave-launcher/status/` so targeted retry intent can survive a launcher restart
44
46
  - a canonical control-plane event log under `.tmp/<lane>-wave-launcher/control-plane/` that records operator tasks, rerun requests, proof bundles, attempt lifecycle, and human-input events as append-only JSONL; `wave control` materializes state from this log
@@ -2,7 +2,7 @@
2
2
 
3
3
  This is a showcase-first sample wave.
4
4
 
5
- Use it as the single reference example for the current `0.8.2` Wave surface.
5
+ Use it as the single reference example for the current `0.8.3` Wave surface.
6
6
 
7
7
  It intentionally combines more sections than a normal production wave so one file can demonstrate:
8
8
 
@@ -24,13 +24,13 @@ GitHub Packages remains available as an authenticated fallback path, and maintai
24
24
  - Fresh `wave init` seeds the starter `skills/` library. `wave init --adopt-existing` records existing repo-owned skill bundles when they are already present, but does not replace or rewrite them.
25
25
  - The current runtime expects the post-roadmap model: typed coordination, compiled inboxes, `A8` integration, staged closure, orchestrator-first clarification, and operational runtime policy.
26
26
 
27
- ## Upgrading From 0.6.x To 0.8.2
27
+ ## Upgrading From 0.6.x To 0.8.3
28
28
 
29
29
  Read `CHANGELOG.md` first, then treat this section as the repo-owned migration checklist for adopted `0.6.x` workspaces.
30
30
 
31
31
  `wave upgrade` updates the installed runtime only. It does not copy planner starter files into a repo that already owns its docs, skills, and Context7 bundles.
32
32
 
33
- `0.8.2` carries forward the `0.8.1` helper-assignment fixes and hardens the control-status projection layer: completed waves no longer replay stale blocking edges or overdue timers from historical coordination records, and successful logical-agent state stays preserved once the wave is terminal.
33
+ `0.8.3` carries forward the `0.8.2` completed-wave control-status hardening and fixes the human-answer reconciliation path: answered feedback now closes the linked clarification or escalation chain in canonical coordination, re-syncs helper-assignment projections, and preserves ad-hoc `--run <id>` context when writing safe continuation intent.
34
34
 
35
35
  ### Required Repo Changes
36
36
 
@@ -42,7 +42,7 @@ If the repo adopted Wave before the planner corpus became a tracked required sur
42
42
  - `docs/reference/wave-planning-lessons.md`
43
43
  - the `planner-agentic` bundle entry in `docs/context7/bundles.json`
44
44
 
45
- If the repo copied the shipped starter architecture docs or skills and wants the `0.8.2` authority-model language, also sync:
45
+ If the repo copied the shipped starter architecture docs or skills and wants the `0.8.3` authority-model language, also sync:
46
46
 
47
47
  - `docs/agents/wave-launcher-role.md`
48
48
  - `docs/agents/wave-orchestrator-role.md`
@@ -57,6 +57,7 @@ After syncing those repo-owned files:
57
57
  1. Run `pnpm exec wave doctor`.
58
58
  2. Run `pnpm exec wave launch --lane main --dry-run --no-dashboard`.
59
59
  3. Use `pnpm exec wave dashboard --lane <lane> --attach current` or `--attach global` when you need to reattach to a live tmux-backed dashboard without reverse-engineering the socket or session name.
60
+ 4. If your operators answer human-input tickets through `wave feedback respond`, update any repo-local runbooks so ad-hoc runs always pass `--run <id>` when responding outside the main roadmap lane.
60
61
 
61
62
  ## Upgrading From 0.5.4 To 0.6.1
62
63
 
@@ -297,12 +297,14 @@ wave feedback ask \
297
297
 
298
298
  ```
299
299
  wave feedback respond \
300
- --id <request-id> --response "<text>" \
300
+ [--run <id>] --id <request-id> --response "<text>" \
301
301
  [--operator <name>] [--force]
302
302
  ```
303
303
 
304
304
  `--force` overrides a previously answered request.
305
305
 
306
+ When the answered request belongs to a live wave or ad-hoc run, `wave feedback respond` also reconciles the linked clarification, escalation, and helper-assignment state in canonical coordination. If no attempt is still running and the reducer can safely continue, it writes a one-shot continuation request instead of relaunching directly. Use `--run <id>` when answering an ad-hoc request so reconciliation targets the isolated ad-hoc state root.
307
+
306
308
  **List feedback requests:**
307
309
 
308
310
  ```
@@ -321,6 +323,8 @@ wave feedback watch [--lane <lane>] [--wave <n>] [--agent <id>] [--pending] [--r
321
323
  wave feedback show --id <request-id>
322
324
  ```
323
325
 
326
+ All `wave feedback` subcommands accept `--run <id>` for ad-hoc runs.
327
+
324
328
  ## wave dep
325
329
 
326
330
  Cross-lane dependency management.
@@ -250,6 +250,17 @@ pnpm exec wave control task act resolve --lane main --wave 10 --id escalation-cl
250
250
 
251
251
  That keeps clarification routing, dismissal, escalation, and human-answer handling inside the canonical coordination state instead of forcing ad hoc file edits.
252
252
 
253
+ When the operator answers through the feedback queue directly, the answer path now repairs the same canonical state:
254
+
255
+ ```bash
256
+ pnpm exec wave feedback respond \
257
+ --id 202603240000-main-w6-A3-abc123 \
258
+ --response "Use the 90-day compatibility window documented in docs/plans/migration.md." \
259
+ --operator ops-lead
260
+ ```
261
+
262
+ For ad-hoc runs, include `--run <id>` on that command. The response path will reconcile the linked clarification or escalation chain, re-sync helper-assignment projections, and write a safe one-shot continuation request when the reducer can resume but no active attempt is still running.
263
+
253
264
  ## End-To-End Example: Required Dependency
254
265
 
255
266
  Assume the wave needs another lane to land a required API first.
@@ -2,7 +2,7 @@
2
2
 
3
3
  This repo now includes a dedicated npmjs publish workflow at [publish-npm.yml](../../.github/workflows/publish-npm.yml).
4
4
 
5
- The current `0.8.2` release procedure publishes through a repository Actions secret named `NPM_TOKEN`.
5
+ The current `0.8.3` release procedure publishes through a repository Actions secret named `NPM_TOKEN`.
6
6
 
7
7
  ## What This Repo Already Does
8
8
 
@@ -47,6 +47,6 @@ If this repo later needs private npm dependencies during CI, consider a separate
47
47
  1. Confirm [publish-npm.yml](../../.github/workflows/publish-npm.yml) is on the default branch.
48
48
  2. Confirm `NPM_TOKEN` exists in the GitHub repo secrets.
49
49
  3. Confirm the package version has been bumped and committed.
50
- 4. Push the release commit and release tag, for example `v0.8.2`.
50
+ 4. Push the release commit and release tag, for example `v0.8.3`.
51
51
  5. Verify both `publish-npm.yml` and `publish-package.yml` start from the tag push.
52
52
  6. Verify the npmjs publish completes successfully for the tagged source.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  title: "Sample Waves"
3
- summary: "Showcase-first sample waves that demonstrate the current 0.8.2 Wave surface."
3
+ summary: "Showcase-first sample waves that demonstrate the current 0.8.3 Wave surface."
4
4
  ---
5
5
 
6
6
  # Sample Waves
7
7
 
8
- This guide points to showcase-first sample waves that demonstrate the current `0.8.2` authored Wave surface.
8
+ This guide points to showcase-first sample waves that demonstrate the current `0.8.3` 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.2` 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.3` 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
  ## What These Examples Teach
21
21
 
@@ -38,7 +38,7 @@ The examples are intentionally denser than typical production waves. Their job i
38
38
 
39
39
  ## Feature Coverage Map
40
40
 
41
- Together these samples cover the main surfaces added or hardened for `0.8.2`:
41
+ Together these samples cover the main surfaces added or hardened for `0.8.3`:
42
42
 
43
43
  - repo-landed maturity discipline and anti-overclaim framing
44
44
  - explicit shared-plan closure for future-wave safety
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chllming/wave-orchestration",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "license": "MIT",
5
5
  "description": "Generic wave-based multi-agent orchestration for repository work.",
6
6
  "repository": {
@@ -2,6 +2,25 @@
2
2
  "schemaVersion": 1,
3
3
  "packageName": "@chllming/wave-orchestration",
4
4
  "releases": [
5
+ {
6
+ "version": "0.8.3",
7
+ "date": "2026-03-24",
8
+ "summary": "Human-input reconciliation repair, ad-hoc feedback-run context hardening, and 0.8.3 release-surface alignment.",
9
+ "features": [
10
+ "Answered human-feedback requests now reconcile linked clarification, escalation, and helper-assignment state back into the canonical coordination log instead of only updating the feedback request JSON.",
11
+ "`wave feedback respond --run <id>` now preserves ad-hoc run context while writing reconciliation and safe continuation state, so ad-hoc answers stop mutating the roadmap lane state root.",
12
+ "When no attempt is still running and the reducer can safely continue, human-input reconciliation writes a one-shot continuation request instead of leaving the wave stranded after the operator answer arrives.",
13
+ "Regression coverage now exercises both direct feedback-response reconciliation and the ad-hoc `--run <id>` path.",
14
+ "Shipped package metadata, README, migration guidance, current-state notes, sample-wave docs, and npm publishing instructions now point at the `0.8.3` release surface."
15
+ ],
16
+ "manualSteps": [
17
+ "If your operators answer tickets through `wave feedback respond`, update any repo-local runbooks so ad-hoc runs always pass `--run <id>` when answering outside the main roadmap lane.",
18
+ "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.3` runtime and human-input reconciliation contract.",
19
+ "If an adopted repo fails `wave doctor` after the upgrade, sync the repo-owned planner starter surface (`docs/agents/wave-planner-role.md`, `skills/role-planner/`, `docs/context7/planner-agent/`, `docs/reference/wave-planning-lessons.md`, and the `planner-agentic` bundle entry) before relying on planner-aware validation.",
20
+ "If a repo copied the shipped starter architecture docs or skills and wants the `0.8.3` operator guidance, sync the updated launcher and orchestrator role prompts plus the relevant runtime and closure-role skill bundles."
21
+ ],
22
+ "breaking": false
23
+ },
5
24
  {
6
25
  "version": "0.8.2",
7
26
  "date": "2026-03-24",
@@ -6,6 +6,7 @@ import { readWaveHumanFeedbackRequests } from "./coordination.mjs";
6
6
  import { readWaveLedger } from "./ledger.mjs";
7
7
  import { buildDependencySnapshot, buildRequestAssignments } from "./routing-state.mjs";
8
8
  import { parseWaveFiles } from "./wave-files.mjs";
9
+ import { answerHumanInputAndReconcile } from "./human-input-resolution.mjs";
9
10
  import {
10
11
  buildLanePaths,
11
12
  DEFAULT_COORDINATION_ACK_TIMEOUT_MS,
@@ -1017,6 +1018,13 @@ export async function runControlCli(argv) {
1017
1018
  operator: options.operator,
1018
1019
  force: true,
1019
1020
  });
1021
+ answerHumanInputAndReconcile({
1022
+ lanePaths,
1023
+ wave,
1024
+ requestId: options.id,
1025
+ answeredPayload: answered,
1026
+ operator: options.operator,
1027
+ });
1020
1028
  appendWaveControlEvent(lanePaths, wave.wave, {
1021
1029
  entityType: "human_input",
1022
1030
  entityId: options.id,
@@ -23,6 +23,7 @@ import {
23
23
  writeJsonArtifact,
24
24
  } from "./coordination-store.mjs";
25
25
  import { answerFeedbackRequest } from "./feedback.mjs";
26
+ import { answerHumanInputAndReconcile } from "./human-input-resolution.mjs";
26
27
  import { readWaveHumanFeedbackRequests } from "./coordination.mjs";
27
28
  import { readWaveProofRegistry } from "./proof-registry.mjs";
28
29
  import {
@@ -463,6 +464,13 @@ export async function runCoordinationCli(argv) {
463
464
  force: true,
464
465
  recordTelemetry: true,
465
466
  });
467
+ answerHumanInputAndReconcile({
468
+ lanePaths,
469
+ wave,
470
+ requestId: options.id,
471
+ answeredPayload: answered,
472
+ operator: options.operator,
473
+ });
466
474
  console.log(JSON.stringify(answered, null, 2));
467
475
  return;
468
476
  }
@@ -21,6 +21,7 @@ import {
21
21
  truncate,
22
22
  writeJsonAtomic,
23
23
  } from "./shared.mjs";
24
+ import { answerHumanInputByRequest } from "./human-input-resolution.mjs";
24
25
  import { safeQueueWaveControlEvent } from "./wave-control-client.mjs";
25
26
 
26
27
  function sanitizeToken(value) {
@@ -363,7 +364,7 @@ export async function runFeedbackCli(argv) {
363
364
  if (!options.id || !options.response) {
364
365
  throw new Error("respond requires --id and --response");
365
366
  }
366
- answerFeedbackRequest({
367
+ const answered = answerFeedbackRequest({
367
368
  feedbackStateDir: stateDir,
368
369
  feedbackRequestsDir: requestsDir,
369
370
  requestId: options.id,
@@ -372,6 +373,15 @@ export async function runFeedbackCli(argv) {
372
373
  force: options.force,
373
374
  recordTelemetry: true,
374
375
  });
376
+ if (answered?.lane && Number.isFinite(Number(answered.wave))) {
377
+ answerHumanInputByRequest({
378
+ lane: answered.lane,
379
+ waveNumber: Number(answered.wave),
380
+ requestId: options.id,
381
+ operator: options.operator,
382
+ runId: options.runId || null,
383
+ });
384
+ }
375
385
  console.log(`[wave-human-feedback] answered ${options.id}`);
376
386
  return;
377
387
  }
@@ -0,0 +1,344 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import {
4
+ appendCoordinationRecord,
5
+ clarificationClosureCondition,
6
+ clarificationLinkedRequests,
7
+ isOpenCoordinationStatus,
8
+ readMaterializedCoordinationState,
9
+ } from "./coordination-store.mjs";
10
+ import { readWaveHumanFeedbackRequests } from "./coordination.mjs";
11
+ import {
12
+ readControlPlaneEvents,
13
+ readWaveControlPlaneState,
14
+ syncWaveControlPlaneProjections,
15
+ waveControlPlaneLogPath,
16
+ } from "./control-plane.mjs";
17
+ import { readWaveLedger } from "./ledger.mjs";
18
+ import { readRunExecutionSummary } from "./launcher-gates.mjs";
19
+ import { buildResumePlan, clearWaveRelaunchPlan } from "./launcher-retry.mjs";
20
+ import { readWaveProofRegistry } from "./proof-registry.mjs";
21
+ import { buildDependencySnapshot, buildRequestAssignments, syncAssignmentRecords } from "./routing-state.mjs";
22
+ import { buildLanePaths } from "./shared.mjs";
23
+ import { reduceWaveState } from "./wave-state-reducer.mjs";
24
+ import { parseWaveFiles } from "./wave-files.mjs";
25
+ import { writeWaveRetryOverride } from "./retry-control.mjs";
26
+
27
+ function coordinationLogPath(lanePaths, waveNumber) {
28
+ return path.join(lanePaths.coordinationDir, `wave-${waveNumber}.jsonl`);
29
+ }
30
+
31
+ function ledgerPath(lanePaths, waveNumber) {
32
+ return path.join(lanePaths.ledgerDir, `wave-${waveNumber}.json`);
33
+ }
34
+
35
+ function coordinationTriagePath(lanePaths, waveNumber) {
36
+ return path.join(lanePaths.feedbackTriageDir, `wave-${waveNumber}.jsonl`);
37
+ }
38
+
39
+ function appendCoordinationStatusUpdate(logPath, record, status, options = {}) {
40
+ return appendCoordinationRecord(logPath, {
41
+ ...record,
42
+ status,
43
+ summary: options.summary || record.summary,
44
+ detail: options.detail || record.detail,
45
+ source: options.source || "operator",
46
+ });
47
+ }
48
+
49
+ function appendTriageEscalationUpdateIfPresent(lanePaths, waveNumber, record) {
50
+ const triagePath = coordinationTriagePath(lanePaths, waveNumber);
51
+ if (!fs.existsSync(triagePath) || record?.kind !== "human-escalation") {
52
+ return;
53
+ }
54
+ appendCoordinationRecord(triagePath, record);
55
+ }
56
+
57
+ function loadWave(lanePaths, waveNumber) {
58
+ const waves = parseWaveFiles(lanePaths.wavesDir, { laneProfile: lanePaths.laneProfile });
59
+ const wave = waves.find((entry) => entry.wave === waveNumber);
60
+ if (!wave) {
61
+ throw new Error(`Wave ${waveNumber} not found in ${lanePaths.wavesDir}`);
62
+ }
63
+ return wave;
64
+ }
65
+
66
+ function taskRunInfoForAgent(lanePaths, wave, agent, proofRegistry) {
67
+ const safeName = `wave-${wave.wave}-${agent.slug}`;
68
+ return {
69
+ agent,
70
+ logPath: path.join(lanePaths.logsDir, `${safeName}.log`),
71
+ statusPath: path.join(lanePaths.statusDir, `${safeName}.status`),
72
+ proofRegistry,
73
+ };
74
+ }
75
+
76
+ function feedbackLinkMatchesRecord(record, requestId) {
77
+ const normalizedRequestId = String(requestId || "").trim();
78
+ if (!normalizedRequestId) {
79
+ return false;
80
+ }
81
+ if (String(record?.id || "").trim() === normalizedRequestId) {
82
+ return true;
83
+ }
84
+ if (
85
+ Array.isArray(record?.artifactRefs) &&
86
+ record.artifactRefs.some((ref) => String(ref || "").trim() === normalizedRequestId)
87
+ ) {
88
+ return true;
89
+ }
90
+ if (
91
+ Array.isArray(record?.dependsOn) &&
92
+ record.dependsOn.some((value) => String(value || "").trim() === normalizedRequestId)
93
+ ) {
94
+ return true;
95
+ }
96
+ return false;
97
+ }
98
+
99
+ function linkedClarificationIdsForRecords(records) {
100
+ const ids = new Set();
101
+ for (const record of records || []) {
102
+ if (record?.kind === "clarification-request" && record?.id) {
103
+ ids.add(record.id);
104
+ continue;
105
+ }
106
+ const closureCondition = String(record?.closureCondition || "").trim();
107
+ if (closureCondition.startsWith("clarification:")) {
108
+ ids.add(closureCondition.slice("clarification:".length));
109
+ }
110
+ }
111
+ return [...ids].filter(Boolean);
112
+ }
113
+
114
+ export function resolveFeedbackLinkedCoordination({
115
+ lanePaths,
116
+ wave,
117
+ requestId,
118
+ operator = "human-operator",
119
+ detail = "",
120
+ }) {
121
+ const logPath = coordinationLogPath(lanePaths, wave.wave);
122
+ const state = readMaterializedCoordinationState(logPath);
123
+ const resolvedRecords = [];
124
+
125
+ const directlyLinked = state.latestRecords.filter((record) =>
126
+ isOpenCoordinationStatus(record.status) && feedbackLinkMatchesRecord(record, requestId),
127
+ );
128
+
129
+ for (const record of directlyLinked) {
130
+ const updated = appendCoordinationStatusUpdate(logPath, record, "resolved", {
131
+ detail: detail || `Resolved after answered human input ${requestId}.`,
132
+ summary: record.summary,
133
+ source: operator,
134
+ });
135
+ resolvedRecords.push(updated);
136
+ appendTriageEscalationUpdateIfPresent(lanePaths, wave.wave, updated);
137
+ }
138
+
139
+ const nextState = readMaterializedCoordinationState(logPath);
140
+ const clarificationIds = linkedClarificationIdsForRecords([
141
+ ...directlyLinked,
142
+ ...resolvedRecords,
143
+ ]);
144
+ for (const clarificationId of clarificationIds) {
145
+ const clarification = nextState.byId.get(clarificationId);
146
+ if (!clarification || !isOpenCoordinationStatus(clarification.status)) {
147
+ continue;
148
+ }
149
+ const updatedClarification = appendCoordinationStatusUpdate(logPath, clarification, "resolved", {
150
+ detail: detail || `Resolved after answered human input ${requestId}.`,
151
+ summary: clarification.summary,
152
+ source: operator,
153
+ });
154
+ resolvedRecords.push(updatedClarification);
155
+ }
156
+
157
+ const resolvedState = readMaterializedCoordinationState(logPath);
158
+ for (const clarificationId of clarificationIds) {
159
+ const linkedRequests = clarificationLinkedRequests(resolvedState, clarificationId).filter((entry) =>
160
+ isOpenCoordinationStatus(entry.status),
161
+ );
162
+ for (const linked of linkedRequests) {
163
+ const updatedLinked = appendCoordinationStatusUpdate(logPath, linked, "resolved", {
164
+ detail: `Resolved via clarification ${clarificationId}.`,
165
+ summary: linked.summary,
166
+ source: operator,
167
+ });
168
+ resolvedRecords.push(updatedLinked);
169
+ }
170
+ for (const escalation of (resolvedState.humanEscalations || []).filter(
171
+ (entry) =>
172
+ isOpenCoordinationStatus(entry.status) &&
173
+ entry.closureCondition === clarificationClosureCondition(clarificationId),
174
+ )) {
175
+ const updatedEscalation = appendCoordinationStatusUpdate(logPath, escalation, "resolved", {
176
+ detail: detail || `Resolved via clarification ${clarificationId}.`,
177
+ summary: escalation.summary,
178
+ source: operator,
179
+ });
180
+ resolvedRecords.push(updatedEscalation);
181
+ appendTriageEscalationUpdateIfPresent(lanePaths, wave.wave, updatedEscalation);
182
+ }
183
+ }
184
+
185
+ const assignmentState = readMaterializedCoordinationState(logPath);
186
+ const ledger = readWaveLedger(ledgerPath(lanePaths, wave.wave)) || { phase: "planned" };
187
+ const assignments = buildRequestAssignments({
188
+ coordinationState: assignmentState,
189
+ agents: wave.agents,
190
+ ledger,
191
+ capabilityRouting: lanePaths.capabilityRouting,
192
+ });
193
+ syncAssignmentRecords(logPath, {
194
+ lane: lanePaths.lane,
195
+ wave: wave.wave,
196
+ assignments,
197
+ });
198
+
199
+ return {
200
+ resolvedRecords,
201
+ clarificationIds,
202
+ };
203
+ }
204
+
205
+ export function buildResumePlanFromDisk({ lanePaths, wave }) {
206
+ const proofRegistry = readWaveProofRegistry(lanePaths, wave.wave);
207
+ const agentRuns = wave.agents.map((agent) =>
208
+ taskRunInfoForAgent(lanePaths, wave, agent, proofRegistry),
209
+ );
210
+ const agentResults = Object.fromEntries(
211
+ agentRuns
212
+ .map((runInfo) => [runInfo.agent.agentId, readRunExecutionSummary(runInfo, wave)])
213
+ .filter(([, summary]) => Boolean(summary)),
214
+ );
215
+ const coordinationState = readMaterializedCoordinationState(coordinationLogPath(lanePaths, wave.wave));
216
+ const feedbackRequests = readWaveHumanFeedbackRequests({
217
+ feedbackRequestsDir: lanePaths.feedbackRequestsDir,
218
+ lane: lanePaths.lane,
219
+ waveNumber: wave.wave,
220
+ agentIds: wave.agents.map((agent) => agent.agentId),
221
+ orchestratorId: "",
222
+ });
223
+ const ledger = readWaveLedger(ledgerPath(lanePaths, wave.wave)) || { phase: "planned" };
224
+ const dependencySnapshot = buildDependencySnapshot({
225
+ dirPath: lanePaths.crossLaneDependenciesDir,
226
+ lane: lanePaths.lane,
227
+ waveNumber: wave.wave,
228
+ agents: wave.agents,
229
+ ledger,
230
+ capabilityRouting: lanePaths.capabilityRouting,
231
+ });
232
+ const reducerState = reduceWaveState({
233
+ controlPlaneEvents: readControlPlaneEvents(waveControlPlaneLogPath(lanePaths, wave.wave)),
234
+ coordinationRecords: coordinationState.latestRecords || [],
235
+ agentResults,
236
+ waveDefinition: wave,
237
+ dependencyTickets: dependencySnapshot,
238
+ feedbackRequests,
239
+ laneConfig: {
240
+ lane: lanePaths.lane,
241
+ contQaAgentId: lanePaths.contQaAgentId || "A0",
242
+ contEvalAgentId: lanePaths.contEvalAgentId || "E0",
243
+ integrationAgentId: lanePaths.integrationAgentId || "A8",
244
+ documentationAgentId: lanePaths.documentationAgentId || "A9",
245
+ validationMode: "live",
246
+ evalTargets: wave.evalTargets,
247
+ benchmarkCatalogPath: lanePaths.laneProfile?.paths?.benchmarkCatalogPath,
248
+ laneProfile: lanePaths.laneProfile,
249
+ requireIntegrationStewardFromWave: lanePaths.requireIntegrationStewardFromWave,
250
+ capabilityRouting: lanePaths.capabilityRouting,
251
+ },
252
+ });
253
+ return buildResumePlan(reducerState, {
254
+ waveDefinition: wave,
255
+ lanePaths,
256
+ });
257
+ }
258
+
259
+ export function maybeWriteAutoResumeRequest({
260
+ lanePaths,
261
+ wave,
262
+ requestedBy = "human-operator",
263
+ reason = "",
264
+ }) {
265
+ const controlState = readWaveControlPlaneState(lanePaths, wave.wave);
266
+ if (controlState.activeAttempt || controlState.activeRerunRequest) {
267
+ return null;
268
+ }
269
+ const resumePlan = buildResumePlanFromDisk({ lanePaths, wave });
270
+ if (!resumePlan.canResume || resumePlan.reason === "human-request") {
271
+ return { resumePlan, request: null };
272
+ }
273
+ if (resumePlan.resumeFromPhase === "completed") {
274
+ return { resumePlan, request: null };
275
+ }
276
+ clearWaveRelaunchPlan(lanePaths, wave.wave);
277
+ const payload = {
278
+ requestedBy,
279
+ reason:
280
+ reason ||
281
+ `Auto continuation after answered human input; resume from ${resumePlan.resumeFromPhase}.`,
282
+ preserveReusableAgentIds: resumePlan.reusableAgentIds,
283
+ reuseProofBundleIds: resumePlan.reusableProofBundleIds,
284
+ applyOnce: true,
285
+ };
286
+ if (resumePlan.resumeFromPhase === "implementation") {
287
+ payload.selectedAgentIds = resumePlan.invalidatedAgentIds;
288
+ } else {
289
+ payload.resumePhase = resumePlan.resumeFromPhase;
290
+ }
291
+ const request = writeWaveRetryOverride(lanePaths, wave.wave, payload);
292
+ return { resumePlan, request };
293
+ }
294
+
295
+ export function answerHumanInputAndReconcile({
296
+ lanePaths,
297
+ wave,
298
+ requestId,
299
+ answeredPayload,
300
+ operator = "human-operator",
301
+ }) {
302
+ const detail =
303
+ `Resolved after human input ${requestId} was answered by ${operator}` +
304
+ (answeredPayload?.response?.text ? `: ${answeredPayload.response.text}` : ".");
305
+ const resolution = resolveFeedbackLinkedCoordination({
306
+ lanePaths,
307
+ wave,
308
+ requestId,
309
+ operator,
310
+ detail,
311
+ });
312
+ syncWaveControlPlaneProjections(
313
+ lanePaths,
314
+ wave.wave,
315
+ readWaveControlPlaneState(lanePaths, wave.wave),
316
+ );
317
+ const autoResume = maybeWriteAutoResumeRequest({
318
+ lanePaths,
319
+ wave,
320
+ requestedBy: operator,
321
+ reason: `Auto continuation after answered human input ${requestId}.`,
322
+ });
323
+ return {
324
+ resolution,
325
+ autoResume,
326
+ };
327
+ }
328
+
329
+ export function answerHumanInputByRequest({
330
+ lane,
331
+ waveNumber,
332
+ requestId,
333
+ operator = "human-operator",
334
+ runId = null,
335
+ }) {
336
+ const lanePaths = buildLanePaths(lane, { adhocRunId: runId || null });
337
+ const wave = loadWave(lanePaths, waveNumber);
338
+ return answerHumanInputAndReconcile({
339
+ lanePaths,
340
+ wave,
341
+ requestId,
342
+ operator,
343
+ });
344
+ }