@chllming/wave-orchestration 0.9.9 → 0.9.11
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 +19 -0
- package/docs/README.md +4 -2
- package/docs/guides/recommendations-0.9.10.md +137 -0
- package/docs/guides/recommendations-0.9.11.md +44 -0
- package/docs/plans/migration.md +10 -6
- package/package.json +9 -8
- package/releases/manifest.json +13 -0
- package/scripts/context7-api-check.sh +0 -0
- package/scripts/context7-export-env.sh +0 -0
- package/scripts/wave-autonomous.mjs +0 -0
- package/scripts/wave-dashboard.mjs +0 -0
- package/scripts/wave-human-feedback.mjs +0 -0
- package/scripts/wave-launcher.mjs +0 -0
- package/scripts/wave-local-executor.mjs +0 -0
- package/scripts/wave-orchestrator/agent-state.mjs +3 -1
- package/scripts/wave-orchestrator/derived-state-engine.mjs +7 -2
- package/scripts/wave-orchestrator/shared.mjs +9 -9
- package/scripts/wave-orchestrator/wave-files.mjs +39 -1
- package/scripts/wave-status.sh +0 -0
- package/scripts/wave-watch.sh +0 -0
- package/scripts/wave.mjs +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.11 - 2026-04-07
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Verdict parsing: first-match-wins** — `parseVerdictFromText()` now returns the **first** regex match instead of the last. In append-only cont-QA report files, stale `Verdict: BLOCKED` entries from earlier attempts would linger at the bottom of the file while newer `Verdict: PASS` entries were written above them. The last-match-wins behavior caused the cont-QA gate to read the stale blocked verdict, creating an infinite retry loop. This was the root cause of 17 blocked waves in React Word Editor (DOCX W20+).
|
|
7
|
+
- **Log verdict priority over report verdict** — `buildAgentExecutionSummary()` now prefers the log-based `[wave-verdict]` marker over the report file `Verdict:` line. The log is authoritative for the current run, while report files may accumulate conflicting entries across retry attempts.
|
|
8
|
+
- **Integration steward closure is now sticky** — When the integration steward (A8) explicitly reports `state=ready-for-doc-closure` with zero blockers, synthesized proof gaps and doc gaps from implementation agent validation are cleared from the integration summary. Previously, `buildWaveIntegrationSummary()` would re-inject gaps derived from agent summaries on every `refreshDerivedState()` call, overriding the steward's explicit sign-off and causing unnecessary retry cycles (observed as 15+ loops in DOCX W20).
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Test suite for `parseVerdictFromText` covering first-match behavior, null/empty inputs, multi-verdict report files, and `[wave-verdict]` marker parsing.
|
|
12
|
+
|
|
13
|
+
## 0.9.10 - 2026-04-07
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- **Run-state history bloat**: The `run-state.json` history array grew unbounded, reaching 500MB+ in long-running repos. Each reconciliation cycle appended entries with full evidence objects (file hashes, status paths) that were never pruned. Fixed with:
|
|
17
|
+
- History cap: max 200 total entries, max 20 per wave. Older entries are pruned on every write.
|
|
18
|
+
- Evidence stripping: only the most recent history entry per wave retains its full evidence object; older entries have evidence set to null.
|
|
19
|
+
- Improved dedup: the transition dedup check now ignores `completedAt` timestamps in status file evidence, preventing identical reconciliation cycles from creating duplicate entries.
|
|
20
|
+
|
|
21
|
+
|
|
3
22
|
## 0.9.9 - 2026-04-07
|
|
4
23
|
|
|
5
24
|
### Fixed
|
package/docs/README.md
CHANGED
|
@@ -56,9 +56,11 @@ The useful path is journey-first:
|
|
|
56
56
|
- Want the practical `0.9.3` operating stance:
|
|
57
57
|
Read [guides/recommendations-0.9.7
|
|
58
58
|
- [0.9.8 Operating Recommendations](guides/recommendations-0.9.8.md
|
|
59
|
-
- [0.9.9 Recommendations](guides/recommendations-0.9.9.md
|
|
59
|
+
- [0.9.9 Recommendations](guides/recommendations-0.9.9.md
|
|
60
|
+
- [0.9.10 Recommendations](guides/recommendations-0.9.10.md))).md](./guides/recommendations-0.9.7
|
|
60
61
|
- [0.9.8 Operating Recommendations](guides/recommendations-0.9.8.md
|
|
61
|
-
- [0.9.9 Recommendations](guides/recommendations-0.9.9.md
|
|
62
|
+
- [0.9.9 Recommendations](guides/recommendations-0.9.9.md
|
|
63
|
+
- [0.9.10 Recommendations](guides/recommendations-0.9.10.md))).md) for the recommended default around relaxed blocker states, advisory turn budgets, and targeted recovery.
|
|
62
64
|
- Want the concrete runtime module map:
|
|
63
65
|
Read [plans/end-state-architecture.md](./plans/end-state-architecture.md) for the engine-by-engine architecture and artifact ownership model.
|
|
64
66
|
- Want the CLI surface map:
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "0.9.10 Recommendations"
|
|
3
|
+
summary: "How to use 0.9.10's softer blocker states, advisory turn budgets, and targeted recovery without weakening proof and closure."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 0.9.10 Recommendations
|
|
7
|
+
|
|
8
|
+
Use this guide when you are adopting `0.9.10` and want one practical operating stance for the softer blocker states, advisory turn-budget behavior, and targeted recovery flow that the current package line ships.
|
|
9
|
+
|
|
10
|
+
## Recommended Default
|
|
11
|
+
|
|
12
|
+
For most repos, the safest `0.9.10` default is:
|
|
13
|
+
|
|
14
|
+
- bound work with `budget.minutes`
|
|
15
|
+
- leave generic `budget.turns` as advisory metadata
|
|
16
|
+
- author non-proof follow-up as `soft`, `stale`, or `advisory` instead of silently treating every open record as a hard blocker
|
|
17
|
+
- use `resolve-policy` when the answer already exists in repo policy or shipped docs
|
|
18
|
+
- prefer targeted rerun or resume after timeout, max-turn, rate-limit, or missing-status outcomes instead of relaunching the whole wave
|
|
19
|
+
- in short-lived sandboxes, prefer `wave submit`, `wave supervise`, `wave status`, and `wave wait` instead of binding the full run to one client shell
|
|
20
|
+
- when a wave-gate dimension has a documented gap that is not an actionable blocker, use `gap` instead of `pass` or `blocked` — the runtime treats it as a conditional pass
|
|
21
|
+
|
|
22
|
+
That recommendation matches the runtime:
|
|
23
|
+
|
|
24
|
+
- executor launch metadata only emits hard turn-limit flags from `claude.maxTurns` or `opencode.steps`
|
|
25
|
+
- open `stale` and `advisory` coordination records stay visible without reopening the active blocking edge
|
|
26
|
+
- recoverable launcher failures queue targeted retry state instead of immediately escalating to broad terminal wave failure
|
|
27
|
+
|
|
28
|
+
## 1. Budgets
|
|
29
|
+
|
|
30
|
+
Treat the two budget knobs differently:
|
|
31
|
+
|
|
32
|
+
- `budget.minutes` is the primary attempt budget
|
|
33
|
+
- generic `budget.turns` is only a planning hint
|
|
34
|
+
- `claude.maxTurns` or `opencode.steps` are the hard runtime ceilings when you actually want deterministic turn stopping
|
|
35
|
+
|
|
36
|
+
Recommended pattern for synthesis-heavy implementation or closure work:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"executors": {
|
|
41
|
+
"profiles": {
|
|
42
|
+
"implementation-default": {
|
|
43
|
+
"id": "claude",
|
|
44
|
+
"model": "claude-sonnet-4-6",
|
|
45
|
+
"budget": {
|
|
46
|
+
"minutes": 35,
|
|
47
|
+
"turns": 12
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
In that pattern, `35` minutes is real policy. `12` turns is only guidance for planning and preview metadata.
|
|
56
|
+
|
|
57
|
+
Only set a hard runtime ceiling when you deliberately want the runtime itself to stop:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"executors": {
|
|
62
|
+
"profiles": {
|
|
63
|
+
"bounded-closure": {
|
|
64
|
+
"id": "claude",
|
|
65
|
+
"model": "claude-sonnet-4-6",
|
|
66
|
+
"budget": {
|
|
67
|
+
"minutes": 20
|
|
68
|
+
},
|
|
69
|
+
"claude": {
|
|
70
|
+
"maxTurns": 6
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 2. Softer Coordination States
|
|
79
|
+
|
|
80
|
+
`0.9.2` keeps “still visible” separate from “still blocking”.
|
|
81
|
+
|
|
82
|
+
Use these states intentionally:
|
|
83
|
+
|
|
84
|
+
| State | Use it for | What the runtime does |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| `soft` | follow-up that still matters but should not be treated like proof failure | remains visible and may still drive repair or retry targeting |
|
|
87
|
+
| `stale` | outdated clarification or blocker context kept for history | visible in control state, but does not reopen blocking by itself |
|
|
88
|
+
| `advisory` | known issue, note, or human context that should stay visible without blocking closure | visible in control state, but does not own the active blocking edge |
|
|
89
|
+
|
|
90
|
+
Practical command paths:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pnpm exec wave control task act defer --lane main --wave 10 --id blocker-doc-follow-up
|
|
94
|
+
pnpm exec wave control task act mark-stale --lane main --wave 10 --id clarify-a7-rollout
|
|
95
|
+
pnpm exec wave control task act mark-advisory --lane main --wave 10 --id request-clarify-a7-rollout
|
|
96
|
+
pnpm exec wave control task act resolve-policy --lane main --wave 10 --id clarify-a7-rollout --detail "Policy already covered in the rollout guide."
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use them when the repo already knows the answer, the remaining item is informational, or the follow-up should stay visible for the next wave without holding the current wave hostage.
|
|
100
|
+
|
|
101
|
+
## 3. What Should Stay Hard
|
|
102
|
+
|
|
103
|
+
Do not relax everything.
|
|
104
|
+
|
|
105
|
+
Keep these hard or closure-critical unless you are intentionally changing wave policy:
|
|
106
|
+
|
|
107
|
+
- missing proof or required deliverables
|
|
108
|
+
- failed integration, documentation, or cont-QA closure gates
|
|
109
|
+
- real human-feedback or escalation requirements that block safe continuation
|
|
110
|
+
- requests or clarifications that still represent unresolved ownership or policy ambiguity for the current wave
|
|
111
|
+
|
|
112
|
+
Use `gap` in wave-gate markers when a dimension has a documented gap that is not actionable in the current wave. For example, `live=gap` is appropriate when an infrastructure topology constraint prevents full live validation but the constraint is known, documented, and does not represent a regression. Do not use `gap` to hide actual failures or unreviewed work.
|
|
113
|
+
|
|
114
|
+
If the current wave cannot truthfully close without the answer, keep it blocking.
|
|
115
|
+
|
|
116
|
+
## 4. Recovery Recommendation
|
|
117
|
+
|
|
118
|
+
My recommendation after reviewing the current `0.9.10` code path is:
|
|
119
|
+
|
|
120
|
+
- let timeout, max-turn, rate-limit, and missing-status failures go through the built-in targeted recovery path first
|
|
121
|
+
- inspect the queued rerun or resume request before manually relaunching the whole wave
|
|
122
|
+
- preserve reusable proof from successful sibling owners whenever the reducer already identified it as reusable
|
|
123
|
+
|
|
124
|
+
That is the shape the launcher now prefers. It only broadens failure when the remaining blockers are still proof-critical or otherwise non-recoverable.
|
|
125
|
+
|
|
126
|
+
## 5. Suggested Operator Policy
|
|
127
|
+
|
|
128
|
+
For most repo-owned runbooks:
|
|
129
|
+
|
|
130
|
+
- teach authors to use `budget.minutes` first
|
|
131
|
+
- teach operators to downgrade only non-proof follow-up
|
|
132
|
+
- treat `resolve-policy` as the preferred path when the answer already exists in docs or repo policy
|
|
133
|
+
- escalate to a full-wave rerun only after targeted recovery proves insufficient
|
|
134
|
+
|
|
135
|
+
If you want a single sentence policy:
|
|
136
|
+
|
|
137
|
+
> Keep proof and closure strict, keep generic turns advisory, and keep non-proof context visible without letting it accidentally own wave closure.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Recommendations for 0.9.11
|
|
2
|
+
|
|
3
|
+
## Upgrade
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
wave self-update
|
|
7
|
+
# or: npm install -g @chllming/wave-orchestration@0.9.11
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## What changed
|
|
11
|
+
|
|
12
|
+
### Verdict parsing (P0 fix)
|
|
13
|
+
|
|
14
|
+
The `parseVerdictFromText()` function now returns the **first** regex match instead of the last. This fixes a critical bug where append-only cont-QA report files accumulated `Verdict:` lines across retries, and the stale `Verdict: BLOCKED` at the bottom would override newer `Verdict: PASS` entries above it.
|
|
15
|
+
|
|
16
|
+
**If you have waves stuck in cont-QA retry loops**, upgrading to 0.9.11 should unblock them on the next attempt. No manual intervention needed — the runner will re-evaluate the gate with the fixed parser.
|
|
17
|
+
|
|
18
|
+
**For ongoing work**: the log-based `[wave-verdict]` marker is now preferred over the report file `Verdict:` line. Both still work, but if both are present, the log marker wins. This is more reliable because the log is per-run while report files persist across attempts.
|
|
19
|
+
|
|
20
|
+
### Log verdict priority
|
|
21
|
+
|
|
22
|
+
`buildAgentExecutionSummary()` now reads verdicts in this order:
|
|
23
|
+
1. `[wave-verdict]` from the agent's log (authoritative per-run)
|
|
24
|
+
2. `Verdict:` from the cont-QA report file (fallback)
|
|
25
|
+
|
|
26
|
+
Previously the report file took priority. If your cont-QA role prompt instructs the agent to write `Verdict: PASS/BLOCKED` in the report, that still works — it just won't override a contradicting `[wave-verdict]` marker in the same run's log.
|
|
27
|
+
|
|
28
|
+
### Integration steward sticky closure
|
|
29
|
+
|
|
30
|
+
When A8 (integration steward) explicitly reports `state=ready-for-doc-closure` with zero blockers, the orchestrator no longer re-injects synthesized proof/doc gaps on the next `refreshDerivedState()` cycle. This prevents the pattern where:
|
|
31
|
+
|
|
32
|
+
1. A8 closes all gaps → integration summary says "ready-for-doc-closure"
|
|
33
|
+
2. Launcher calls `refreshDerivedState()` on next attempt
|
|
34
|
+
3. `buildIntegrationEvidence()` re-derives gaps from agent summaries
|
|
35
|
+
4. Integration summary regresses to "needs-more-work"
|
|
36
|
+
5. Retry → goto 1
|
|
37
|
+
|
|
38
|
+
**No action required** — the fix is automatic. If your waves were stuck in integration retry loops, they should resolve on the next attempt.
|
|
39
|
+
|
|
40
|
+
## Recommendations
|
|
41
|
+
|
|
42
|
+
- **Stuck cont-QA waves**: Just upgrade and let the runner retry. The fixed verdict parser will read the correct verdict.
|
|
43
|
+
- **Stuck integration loops**: Same — upgrade and let the runner retry.
|
|
44
|
+
- **Report file hygiene**: Consider having your cont-QA role prompt write verdicts with a clear section header (e.g., `## Final Verdict`) to make the file structure unambiguous. The first-match-wins behavior rewards putting the definitive verdict early in the file.
|
package/docs/plans/migration.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Migration
|
|
2
2
|
|
|
3
|
-
This page is the practical repo-upgrade guide for the current `0.9.
|
|
3
|
+
This page is the practical repo-upgrade guide for the current `0.9.10` surface.
|
|
4
4
|
|
|
5
5
|
Use it when you are:
|
|
6
6
|
|
|
@@ -24,7 +24,7 @@ The `0.9.4` surface adds laddered gate modes and fixes the steward threshold enf
|
|
|
24
24
|
|
|
25
25
|
## What `0.9.4` Changes
|
|
26
26
|
|
|
27
|
-
The current `0.9.
|
|
27
|
+
The current `0.9.10` surface keeps everything from `0.9.2` and adds two focused improvements with no breaking changes.
|
|
28
28
|
|
|
29
29
|
The practical changes are:
|
|
30
30
|
|
|
@@ -182,7 +182,7 @@ Use `pnpm exec wave dashboard --lane <lane> --attach current` or `--attach globa
|
|
|
182
182
|
|
|
183
183
|
## `0.9.4` Release Model
|
|
184
184
|
|
|
185
|
-
The current `0.9.
|
|
185
|
+
The current `0.9.10` surface combines these strands:
|
|
186
186
|
|
|
187
187
|
- the gap-value wave-gate fix and first-time setup UX improvements released in `0.9.4`
|
|
188
188
|
- the detached process-runner and sandbox supervisor hardening released in `0.9.2`
|
|
@@ -367,11 +367,15 @@ If your repo copied starter config defaults, also sync the `designRolePromptPath
|
|
|
367
367
|
- hybrid design stewards rejoin implementation when they explicitly own code
|
|
368
368
|
- long-running prompts receive signal-state and ack paths when the repo uses the new waiting model
|
|
369
369
|
|
|
370
|
+
## Upgrading From `0.9.9` To `0.9.10`
|
|
371
|
+
|
|
372
|
+
Run-state history is now capped at 200 entries (20 per wave). Existing bloated run-state files will be automatically pruned on the next write. No config changes needed.
|
|
373
|
+
|
|
370
374
|
## Upgrading From `0.9.8` To `0.9.9`
|
|
371
375
|
|
|
372
376
|
Helper assignment barriers are now advisory in bootstrap gate mode. No config changes needed.
|
|
373
377
|
|
|
374
|
-
## Upgrading From `0.8.3` To `0.9.
|
|
378
|
+
## Upgrading From `0.8.3` To `0.9.10`
|
|
375
379
|
|
|
376
380
|
Treat this as one move to the current `0.9.2` surface.
|
|
377
381
|
|
|
@@ -406,7 +410,7 @@ If your repo copied starter docs or skills, sync:
|
|
|
406
410
|
- dry-run one design-steward wave if the repo wants the new authored surface
|
|
407
411
|
- if the repo uses long-running watcher agents or shell automation, validate `scripts/wave-status.sh` and `scripts/wave-watch.sh` against a live or staged lane
|
|
408
412
|
|
|
409
|
-
## Upgrading From `0.6.x` Or `0.7.x` To `0.9.
|
|
413
|
+
## Upgrading From `0.6.x` Or `0.7.x` To `0.9.10`
|
|
410
414
|
|
|
411
415
|
This is the main migration path for older adopted repos.
|
|
412
416
|
|
|
@@ -557,4 +561,4 @@ For repos that depend on replay parity, replay at least:
|
|
|
557
561
|
|
|
558
562
|
## Summary
|
|
559
563
|
|
|
560
|
-
The current `0.9.
|
|
564
|
+
The current `0.9.10` surface keeps the same authority-set and phase-engine architecture, ships both the design-role starter surface and the signal-driven long-running-agent starter surface, keeps the `0.8.7` policy and routing hardening, and now also packages the practical operator recommendations guide inside the release line. For most repos already on `0.8.x`, the upgrade is package bump plus validation. For older adopted repos, the real work is syncing repo-owned prompts, skills, planner corpus, wrapper scripts, and runbooks so they describe the runtime the package now ships.
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chllming/wave-orchestration",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.11",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Generic wave-based multi-agent orchestration for repository work.",
|
|
6
|
+
"packageManager": "pnpm@10.23.0",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/chllming/agent-wave-orchestrator.git"
|
|
@@ -31,12 +32,6 @@
|
|
|
31
32
|
"wave-dashboard": "scripts/wave-dashboard.mjs",
|
|
32
33
|
"wave-local-executor": "scripts/wave-local-executor.mjs"
|
|
33
34
|
},
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@mozilla/readability": "^0.6.0",
|
|
36
|
-
"jsdom": "^29.0.1",
|
|
37
|
-
"pdfjs-dist": "^5.5.207",
|
|
38
|
-
"vitest": "3.2.4"
|
|
39
|
-
},
|
|
40
35
|
"scripts": {
|
|
41
36
|
"context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
|
|
42
37
|
"research:import-agent-context": "node scripts/research/import-agent-context-archive.mjs scripts/research/manifests/agent-context-expanded-2026-03-22.mjs",
|
|
@@ -50,5 +45,11 @@
|
|
|
50
45
|
"wave:feedback": "node scripts/wave-human-feedback.mjs",
|
|
51
46
|
"wave:launch": "node scripts/wave-launcher.mjs",
|
|
52
47
|
"wave:local": "node scripts/wave-local-executor.mjs"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@mozilla/readability": "^0.6.0",
|
|
51
|
+
"jsdom": "^29.0.1",
|
|
52
|
+
"pdfjs-dist": "^5.5.207",
|
|
53
|
+
"vitest": "3.2.4"
|
|
53
54
|
}
|
|
54
|
-
}
|
|
55
|
+
}
|
package/releases/manifest.json
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "@chllming/wave-orchestration",
|
|
4
4
|
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.9.10",
|
|
7
|
+
"date": "2026-04-07",
|
|
8
|
+
"summary": "Fix run-state history bloat that caused 500MB+ JSON files and V8 string length crashes.",
|
|
9
|
+
"features": [
|
|
10
|
+
"Run-state history capped at 200 entries (20 per wave) with automatic pruning.",
|
|
11
|
+
"Evidence objects stripped from older history entries to reduce size.",
|
|
12
|
+
"Improved transition dedup ignores completedAt timestamps in evidence.",
|
|
13
|
+
"planner-agentic bundle placeholder remains available for adopted repos."
|
|
14
|
+
],
|
|
15
|
+
"manualSteps": [],
|
|
16
|
+
"breaking": false
|
|
17
|
+
},
|
|
5
18
|
{
|
|
6
19
|
"version": "0.9.9",
|
|
7
20
|
"date": "2026-04-07",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -509,7 +509,9 @@ export function buildAgentExecutionSummary({ agent, statusRecord, logPath, repor
|
|
|
509
509
|
: "";
|
|
510
510
|
const reportVerdict = parseVerdictFromText(reportText, REPORT_VERDICT_REGEX);
|
|
511
511
|
const logVerdict = parseVerdictFromText(signalText, WAVE_VERDICT_REGEX);
|
|
512
|
-
|
|
512
|
+
// Prefer log verdict (authoritative for the current run) over report verdict
|
|
513
|
+
// (may accumulate stale entries across retries in append-only report files).
|
|
514
|
+
const verdict = logVerdict.verdict ? logVerdict : reportVerdict;
|
|
513
515
|
const termination = detectTermination(agent, logText, statusRecord);
|
|
514
516
|
return {
|
|
515
517
|
agentId: agent?.agentId || null,
|
|
@@ -573,6 +573,11 @@ export function buildWaveIntegrationSummary({
|
|
|
573
573
|
securitySummary,
|
|
574
574
|
});
|
|
575
575
|
if (explicitIntegration) {
|
|
576
|
+
// When the integration steward explicitly asserts ready-for-doc-closure,
|
|
577
|
+
// clear synthesized proof/doc gaps — the steward has signed off on them.
|
|
578
|
+
const stewardClearedGaps =
|
|
579
|
+
explicitIntegration.state === "ready-for-doc-closure" &&
|
|
580
|
+
(explicitIntegration.blockers || 0) === 0;
|
|
576
581
|
return {
|
|
577
582
|
wave: wave.wave,
|
|
578
583
|
lane: lanePaths.lane,
|
|
@@ -595,8 +600,8 @@ export function buildWaveIntegrationSummary({
|
|
|
595
600
|
),
|
|
596
601
|
changedInterfaces: evidence.changedInterfaces,
|
|
597
602
|
crossComponentImpacts: evidence.crossComponentImpacts,
|
|
598
|
-
proofGaps: evidence.proofGaps,
|
|
599
|
-
docGaps: evidence.docGaps,
|
|
603
|
+
proofGaps: stewardClearedGaps ? [] : evidence.proofGaps,
|
|
604
|
+
docGaps: stewardClearedGaps ? [] : evidence.docGaps,
|
|
600
605
|
deployRisks: evidence.deployRisks,
|
|
601
606
|
securityState: evidence.securityState,
|
|
602
607
|
securityFindings: evidence.securityFindings,
|
|
@@ -443,16 +443,16 @@ export function parseVerdictFromText(text, regex) {
|
|
|
443
443
|
}
|
|
444
444
|
regex.lastIndex = 0;
|
|
445
445
|
let match = regex.exec(text);
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
while (match !== null) {
|
|
449
|
-
verdict = normalizeWaveVerdict(match[1]);
|
|
450
|
-
detail = String(match[2] || "")
|
|
451
|
-
.trim()
|
|
452
|
-
.replace(/^detail=/i, "")
|
|
453
|
-
.trim();
|
|
454
|
-
match = regex.exec(text);
|
|
446
|
+
if (!match) {
|
|
447
|
+
return { verdict: null, detail: "" };
|
|
455
448
|
}
|
|
449
|
+
// Use the first match — in append-only reports the latest verdict is written
|
|
450
|
+
// at the top of the newest section, while stale entries linger at the bottom.
|
|
451
|
+
const verdict = normalizeWaveVerdict(match[1]);
|
|
452
|
+
const detail = String(match[2] || "")
|
|
453
|
+
.trim()
|
|
454
|
+
.replace(/^detail=/i, "")
|
|
455
|
+
.trim();
|
|
456
456
|
return { verdict, detail };
|
|
457
457
|
}
|
|
458
458
|
|
|
@@ -2665,6 +2665,9 @@ export function writeRunState(runStatePath, state) {
|
|
|
2665
2665
|
return payload;
|
|
2666
2666
|
}
|
|
2667
2667
|
|
|
2668
|
+
const RUN_STATE_MAX_HISTORY = 200;
|
|
2669
|
+
const RUN_STATE_MAX_HISTORY_PER_WAVE = 20;
|
|
2670
|
+
|
|
2668
2671
|
function nextRunStateSequence(history) {
|
|
2669
2672
|
return (history || []).reduce((max, entry) => Math.max(max, Number(entry?.seq) || 0), 0) + 1;
|
|
2670
2673
|
}
|
|
@@ -2686,13 +2689,23 @@ function appendRunStateTransition(state, {
|
|
|
2686
2689
|
const effectiveDetail = String(detail || "").trim();
|
|
2687
2690
|
const effectiveEvidence =
|
|
2688
2691
|
evidence && typeof evidence === "object" && !Array.isArray(evidence) ? evidence : null;
|
|
2692
|
+
// Dedup: skip if the transition is identical (ignore timestamps in evidence)
|
|
2693
|
+
const evidenceForCompare = (ev) => {
|
|
2694
|
+
if (!ev || typeof ev !== "object") return null;
|
|
2695
|
+
const { statusFiles, ...rest } = ev;
|
|
2696
|
+
// Strip completedAt from status files for comparison (changes every cycle)
|
|
2697
|
+
const normalizedFiles = Array.isArray(statusFiles)
|
|
2698
|
+
? statusFiles.map(({ completedAt, ...f }) => f)
|
|
2699
|
+
: statusFiles;
|
|
2700
|
+
return JSON.stringify({ ...rest, statusFiles: normalizedFiles });
|
|
2701
|
+
};
|
|
2689
2702
|
if (
|
|
2690
2703
|
previousEntry &&
|
|
2691
2704
|
currentState === toState &&
|
|
2692
2705
|
previousEntry.lastSource === source &&
|
|
2693
2706
|
previousEntry.lastReasonCode === reasonCode &&
|
|
2694
2707
|
previousEntry.lastDetail === effectiveDetail &&
|
|
2695
|
-
|
|
2708
|
+
evidenceForCompare(currentEvidence) === evidenceForCompare(effectiveEvidence)
|
|
2696
2709
|
) {
|
|
2697
2710
|
return nextState;
|
|
2698
2711
|
}
|
|
@@ -2717,6 +2730,31 @@ function appendRunStateTransition(state, {
|
|
|
2717
2730
|
lastEvidence: effectiveEvidence,
|
|
2718
2731
|
};
|
|
2719
2732
|
nextState.history = [...nextState.history, historyEntry];
|
|
2733
|
+
// Cap history to prevent unbounded growth (run-state bloat fix)
|
|
2734
|
+
if (nextState.history.length > RUN_STATE_MAX_HISTORY) {
|
|
2735
|
+
// Keep the last N entries per wave, plus the most recent entries overall
|
|
2736
|
+
const byWave = new Map();
|
|
2737
|
+
for (const entry of nextState.history) {
|
|
2738
|
+
const key = String(entry.wave ?? "");
|
|
2739
|
+
if (!byWave.has(key)) byWave.set(key, []);
|
|
2740
|
+
byWave.get(key).push(entry);
|
|
2741
|
+
}
|
|
2742
|
+
const kept = [];
|
|
2743
|
+
for (const [, entries] of byWave) {
|
|
2744
|
+
kept.push(...entries.slice(-RUN_STATE_MAX_HISTORY_PER_WAVE));
|
|
2745
|
+
}
|
|
2746
|
+
// Strip evidence from all but the last entry per wave to reduce size
|
|
2747
|
+
const lastPerWave = new Set();
|
|
2748
|
+
for (let i = kept.length - 1; i >= 0; i--) {
|
|
2749
|
+
const key = String(kept[i].wave ?? "");
|
|
2750
|
+
if (!lastPerWave.has(key)) {
|
|
2751
|
+
lastPerWave.add(key);
|
|
2752
|
+
} else {
|
|
2753
|
+
kept[i] = { ...kept[i], evidence: null };
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
nextState.history = kept.sort((a, b) => (a.seq || 0) - (b.seq || 0));
|
|
2757
|
+
}
|
|
2720
2758
|
nextState.completedWaves = completedWavesFromStateEntries(nextState.waves);
|
|
2721
2759
|
return nextState;
|
|
2722
2760
|
}
|
package/scripts/wave-status.sh
CHANGED
|
File without changes
|
package/scripts/wave-watch.sh
CHANGED
|
File without changes
|
package/scripts/wave.mjs
CHANGED
|
File without changes
|