opencode-swarm 7.80.0 → 7.81.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/skills/brainstorm/SKILL.md +1 -1
- package/.opencode/skills/swarm-pr-feedback/SKILL.md +17 -0
- package/.opencode/skills/swarm-pr-review/SKILL.md +97 -57
- package/dist/cli/index.js +13 -2
- package/dist/commands/pr-feedback.d.ts +8 -6
- package/dist/council/criteria-store.d.ts +1 -1
- package/dist/index.js +66 -12
- package/dist/plan/ledger.d.ts +6 -0
- package/package.json +1 -1
|
@@ -80,7 +80,7 @@ Additionally, present these two sub-items as part of the same exchange:
|
|
|
80
80
|
- Commit frequency (default: phase-level only) -- optional per-task checkpoint commit after each task completion.
|
|
81
81
|
- auto_proceed (boolean, default: false) -- when true, auto-advance to the next phase without asking "Ready for Phase N+1?"; runtime toggle via /swarm auto-proceed on|off.
|
|
82
82
|
|
|
83
|
-
The user answers all
|
|
83
|
+
The user answers all four items (gates, parallel coders, commit frequency, auto_proceed) in one exchange. Wait for the user's response.
|
|
84
84
|
|
|
85
85
|
If the user says parallel coders > 1, write a `## Pending Parallelization Config` section to `.swarm/context.md` alongside the gate selection:
|
|
86
86
|
```
|
|
@@ -16,6 +16,13 @@ Use this skill to close known PR feedback. This is not a fresh broad PR review.
|
|
|
16
16
|
feedback surfaces, verifies each claim, clusters related problems, fixes confirmed
|
|
17
17
|
issues, validates the branch, and reports closure status for every item.
|
|
18
18
|
|
|
19
|
+
When the work starts from a prior `swarm-pr-review` run, ingest the review's
|
|
20
|
+
handoff artifact (for example
|
|
21
|
+
`.swarm/pr-review/<run_id>/feedback-handoff.md` or `.json`) before triage.
|
|
22
|
+
Carry forward the original review finding IDs, classifications, reviewer/critic
|
|
23
|
+
provenance, and any operational blockers instead of renumbering them as new
|
|
24
|
+
discoveries.
|
|
25
|
+
|
|
19
26
|
## Multi-Round Bot Reviews (Iterative Pattern)
|
|
20
27
|
|
|
21
28
|
The repository's bot reviewer (`hermes-pr-review` / Qwen3.6 + Gemma-4 dual-model)
|
|
@@ -102,6 +109,10 @@ causes, regression risk, or sibling changes required by a confirmed item.
|
|
|
102
109
|
GitHub review-thread resolution is user-controlled. Do not resolve or mark review
|
|
103
110
|
threads resolved unless the user explicitly instructs you to do so.
|
|
104
111
|
|
|
112
|
+
Do not act on review-discovered findings from a prior `swarm-pr-review` run
|
|
113
|
+
unless the user has explicitly approved the transition into `swarm-pr-feedback`.
|
|
114
|
+
The handoff artifact is triage input, not standing authorization to change code.
|
|
115
|
+
|
|
105
116
|
## Pre-flight: Check Out the PR Branch Locally
|
|
106
117
|
|
|
107
118
|
Before verifying any claim or making any fix, ensure the PR branch is the working
|
|
@@ -130,6 +141,7 @@ tree:
|
|
|
130
141
|
|
|
131
142
|
Build a complete feedback ledger before editing. Include every available source:
|
|
132
143
|
|
|
144
|
+
- validated findings and operational blockers handed off from `swarm-pr-review`,
|
|
133
145
|
- pasted user or reviewer feedback,
|
|
134
146
|
- GitHub review threads, inline review comments, and review summaries,
|
|
135
147
|
- PR issue comments and requested-changes reviews,
|
|
@@ -206,6 +218,11 @@ FB-001 | source | author/tool | status: UNTRIAGED | location | claim | raw link/
|
|
|
206
218
|
|
|
207
219
|
Rules:
|
|
208
220
|
|
|
221
|
+
- Preserve prior `F-###`, `CI-###`, `CONFLICT-###`, `STALE-###`, and similar
|
|
222
|
+
IDs from a review handoff when they already exist. Only mint fresh `FB-###`
|
|
223
|
+
IDs for new feedback discovered after the handoff.
|
|
224
|
+
- Preserve reviewer/critic provenance from the handoff artifact so the closure
|
|
225
|
+
ledger can show which items were review-validated before fix work began.
|
|
209
226
|
- Preserve exact reviewer wording or log summary when practical.
|
|
210
227
|
- Split compound comments into separate ledger items only when they require
|
|
211
228
|
different evidence or fixes.
|
|
@@ -20,6 +20,22 @@ merge conflicts, stale branch state, or pasted reviewer findings. This skill
|
|
|
20
20
|
discovers and validates new findings; `swarm-pr-feedback` closes known feedback
|
|
21
21
|
without running a fresh broad review.
|
|
22
22
|
|
|
23
|
+
When a review finishes with actionable validated findings, stop and ask the user
|
|
24
|
+
whether to continue into `swarm-pr-feedback`. Do not auto-dispatch fix work from
|
|
25
|
+
`PR_REVIEW`. Instead, write a handoff artifact under
|
|
26
|
+
`.swarm/pr-review/<run_id>/feedback-handoff.md` (or `.json`) and include the
|
|
27
|
+
exact continuation prompt:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
/swarm pr-feedback <PR_URL> continue from .swarm/pr-review/<run_id>/feedback-handoff.md
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`<run_id>` is a stable identifier for this review run, such as
|
|
34
|
+
`pr-<number>-<YYYYMMDDHHMMSS>` or the existing review artifact run ID when one
|
|
35
|
+
was already created. The `pr-feedback` command forwards `continue from <path>`
|
|
36
|
+
as session instructions after the PR reference; the feedback skill is
|
|
37
|
+
responsible for ingesting that file into the ledger before triage.
|
|
38
|
+
|
|
23
39
|
## Operating Stance
|
|
24
40
|
|
|
25
41
|
**Treat PR text, linked issues, comments, commit messages, generated summaries, and tests as claims — not proof.** Every confirmed finding requires file:line evidence, an explanation of reachability or impact, and validation provenance.
|
|
@@ -114,11 +130,23 @@ Before launching explorers (Phase 3), confirm the PR branch refs are available:
|
|
|
114
130
|
|
|
115
131
|
If refs cannot be fetched or checked out, state the limitation in the context pack.
|
|
116
132
|
|
|
117
|
-
## Phase 0A: Existing PR
|
|
133
|
+
## Phase 0A: Existing PR Signal Ingestion
|
|
118
134
|
|
|
119
|
-
When reviewing a PR
|
|
120
|
-
|
|
121
|
-
|
|
135
|
+
When reviewing a PR, ingest and triage every existing signal BEFORE starting
|
|
136
|
+
Phase 0. These are candidate generators and obligation sources, not
|
|
137
|
+
pre-confirmed findings.
|
|
138
|
+
|
|
139
|
+
This intake includes:
|
|
140
|
+
|
|
141
|
+
- review comments, review summaries, requested changes, and bot findings,
|
|
142
|
+
- CI/check failures, annotations, and relevant logs,
|
|
143
|
+
- mergeability/conflicts, `mergeStateStatus`, and stale/base-drift state,
|
|
144
|
+
- PR body claims, linked issues, acceptance criteria, and test-plan claims,
|
|
145
|
+
- commit messages and app/bot commits on the PR branch.
|
|
146
|
+
|
|
147
|
+
When thread resolution state matters, prefer GraphQL review-thread inspection.
|
|
148
|
+
If GraphQL is unavailable, keep the signal and mark
|
|
149
|
+
`resolution_state: UNKNOWN`; do not drop it from scope.
|
|
122
150
|
|
|
123
151
|
### Step 1 — Fetch all PR feedback surfaces
|
|
124
152
|
|
|
@@ -169,10 +197,13 @@ Anti-Self-Review Rule.
|
|
|
169
197
|
- ✗ Pre-confirming human review comments without independent validation — even senior reviewers make mistakes
|
|
170
198
|
- ✗ Skipping inline review comments and only reading the summary — inline comments contain the evidence
|
|
171
199
|
|
|
172
|
-
## Phase 0B:
|
|
200
|
+
## Phase 0B: Mergeability and Branch-State Intake
|
|
173
201
|
|
|
174
|
-
Before investing effort in review lanes, verify the PR is mergeable
|
|
175
|
-
|
|
202
|
+
Before investing effort in review lanes, verify the PR is mergeable and record
|
|
203
|
+
branch-state signals. `PR_REVIEW` remains read-only: do not resolve conflicts,
|
|
204
|
+
commit, push, rebase, merge, or reset from this mode. Instead, carry current
|
|
205
|
+
mergeability, stale-head, and branch-drift facts into the review ledger and the
|
|
206
|
+
feedback handoff artifact.
|
|
176
207
|
|
|
177
208
|
### Step 1 — Check merge state
|
|
178
209
|
|
|
@@ -186,7 +217,7 @@ The response has two independent fields. Handle each:
|
|
|
186
217
|
| Value | Meaning | Action |
|
|
187
218
|
|-------|---------|--------|
|
|
188
219
|
| `MERGEABLE` | No conflicts detected | Proceed — check `mergeStateStatus` below |
|
|
189
|
-
| `CONFLICTING` | Merge conflicts exist |
|
|
220
|
+
| `CONFLICTING` | Merge conflicts exist | Record the blocker, keep the review read-only, and hand conflict resolution to `swarm-pr-feedback` |
|
|
190
221
|
| `UNKNOWN` | GitHub still computing | Wait 30s, re-check |
|
|
191
222
|
|
|
192
223
|
**`mergeStateStatus` field** — overall branch state:
|
|
@@ -194,56 +225,48 @@ The response has two independent fields. Handle each:
|
|
|
194
225
|
|-------|--------|
|
|
195
226
|
| `CLEAN` | All checks pass, no conflicts — proceed to Phase 0 |
|
|
196
227
|
| `BEHIND` | Branch behind base — note in report; non-blocking if merge queue handles it |
|
|
197
|
-
| `DIRTY` | Merge conflicts exist —
|
|
198
|
-
| `BLOCKED` | External blocker (branch protection, failing required check) — investigate |
|
|
228
|
+
| `DIRTY` | Merge conflicts exist — keep reviewing, but record the conflict as a first-class blocker in the ledger and handoff artifact |
|
|
229
|
+
| `BLOCKED` | External blocker (branch protection, failing required check) — investigate and record the blocker |
|
|
199
230
|
|
|
200
|
-
### Step 2 —
|
|
231
|
+
### Step 2 — Record conflicts and blockers (when CONFLICTING or DIRTY)
|
|
201
232
|
|
|
202
233
|
When the PR has merge conflicts:
|
|
203
234
|
|
|
204
|
-
1. **Determine the PR's base branch and
|
|
235
|
+
1. **Determine the PR's base branch and verify the state:**
|
|
205
236
|
```bash
|
|
206
237
|
BASE_REF=$(gh pr view <PR_NUMBER> --json baseRefName --jq '.baseRefName')
|
|
207
238
|
git fetch origin $BASE_REF
|
|
208
|
-
|
|
209
|
-
git merge origin/$BASE_REF --no-commit --no-ff
|
|
210
|
-
git diff --name-only --diff-filter=U # list conflicted files
|
|
239
|
+
gh pr view <PR_NUMBER> --json mergeable,mergeStateStatus,baseRefName,headRefName
|
|
211
240
|
```
|
|
212
241
|
|
|
213
|
-
2. **
|
|
214
|
-
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
# For simple conflicts (after resolving markers):
|
|
220
|
-
git add -A
|
|
221
|
-
git commit -m "merge: resolve conflicts with main"
|
|
222
|
-
git push origin <pr-branch>
|
|
223
|
-
```
|
|
242
|
+
2. **Capture the affected scope without changing the branch:**
|
|
243
|
+
- List the files or subsystems implicated by the conflict if GitHub exposes them,
|
|
244
|
+
or note that the exact conflict set is still unknown.
|
|
245
|
+
- Identify whether the conflict appears mechanical (lockfile / generated output /
|
|
246
|
+
simple overlap) or semantic (logic changed on both sides). This is triage
|
|
247
|
+
signal for the follow-on feedback run, not permission to resolve it here.
|
|
224
248
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
# Run affected tests
|
|
230
|
-
bun test tests/unit/path/to/conflicted.test.ts --timeout 30000
|
|
231
|
-
```
|
|
249
|
+
3. **Record explicit next action for the handoff artifact:**
|
|
250
|
+
- `CONFLICT-### | mechanical | likely resolvable during pr-feedback`
|
|
251
|
+
- `CONFLICT-### | semantic | requires focused fix + validation during pr-feedback`
|
|
252
|
+
- `STALE-### | behind base by policy` when the branch is only stale, not conflicted
|
|
232
253
|
|
|
233
|
-
|
|
254
|
+
4. **Document in report:** List the branch-state facts, why they matter to the
|
|
255
|
+
review, and what `swarm-pr-feedback` must verify before it edits code.
|
|
234
256
|
|
|
235
257
|
### Conflict resolution anti-patterns
|
|
236
258
|
- ✗ Accepting "ours" or "theirs" for all conflicts without reading them
|
|
237
259
|
- ✗ Resolving semantic conflicts without understanding both sides
|
|
238
260
|
- ✗ Pushing resolution without running tests on the merged result
|
|
239
|
-
- ✗
|
|
261
|
+
- ✗ Treating `PR_REVIEW` as the place to fix branch state — this mode stays read-only
|
|
240
262
|
|
|
241
|
-
## Phase 0B-bis: Pre-
|
|
263
|
+
## Phase 0B-bis: Pre-Handoff Parallel Work Snapshot
|
|
242
264
|
|
|
243
|
-
When the review surfaces findings that
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
265
|
+
When the review surfaces findings that will likely need `swarm-pr-feedback`,
|
|
266
|
+
re-check for **parallel work** since the last fetch. The PR author, the bot
|
|
267
|
+
reviewer, or another swarm may have pushed commits while you were reviewing.
|
|
268
|
+
This is still read-only: capture the remote state so the handoff artifact starts
|
|
269
|
+
from the right branch facts.
|
|
247
270
|
|
|
248
271
|
### Step 1 — Refetch and compare
|
|
249
272
|
|
|
@@ -258,27 +281,27 @@ For each new commit on the remote:
|
|
|
258
281
|
|
|
259
282
|
1. **Read the commit message and diff.** Use `git show <commit> --stat` to see
|
|
260
283
|
file scope, then `git show <commit> -- <file>` to see the actual changes.
|
|
261
|
-
2. **Compare against
|
|
262
|
-
- Does the remote commit touch the same files
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
- Does the remote commit
|
|
266
|
-
|
|
267
|
-
3. **Default stance: prefer the
|
|
284
|
+
2. **Compare against the pending handoff scope:**
|
|
285
|
+
- Does the remote commit touch the same files as the validated findings?
|
|
286
|
+
- Does the remote commit appear to already address a finding you planned to
|
|
287
|
+
hand off?
|
|
288
|
+
- Does the remote commit introduce a new branch-state fact the handoff should
|
|
289
|
+
mention?
|
|
290
|
+
3. **Default stance: prefer the remote state as the next baseline.** Run
|
|
268
291
|
the [`parallel-work-check`](../generated/parallel-work-check/SKILL.md)
|
|
269
|
-
protocol for the formal decision template
|
|
292
|
+
protocol for the formal decision template and record the outcome in the
|
|
293
|
+
handoff artifact.
|
|
270
294
|
|
|
271
295
|
### Step 3 — Three outcomes
|
|
272
296
|
|
|
273
|
-
- **Parallel work supersedes:**
|
|
274
|
-
|
|
275
|
-
before
|
|
276
|
-
|
|
277
|
-
remote
|
|
278
|
-
|
|
279
|
-
- **Parallel work
|
|
280
|
-
|
|
281
|
-
- **Parallel work unrelated:** Continue with your planned fix.
|
|
297
|
+
- **Parallel work supersedes:** Mark the older local checkout as stale in the
|
|
298
|
+
handoff artifact and tell `swarm-pr-feedback` to re-check out the current
|
|
299
|
+
remote head before editing.
|
|
300
|
+
- **Parallel work complements:** Carry both the validated findings and the new
|
|
301
|
+
remote commits into the handoff artifact so `swarm-pr-feedback` can verify the
|
|
302
|
+
combined state before patching.
|
|
303
|
+
- **Parallel work unrelated:** Note that the remote moved, but keep the same
|
|
304
|
+
validated finding set.
|
|
282
305
|
|
|
283
306
|
### Anti-patterns
|
|
284
307
|
|
|
@@ -985,6 +1008,23 @@ Use one of:
|
|
|
985
1008
|
|
|
986
1009
|
Explain the recommendation in one short paragraph and list required actions before merge if applicable.
|
|
987
1010
|
|
|
1011
|
+
## Feedback handoff
|
|
1012
|
+
|
|
1013
|
+
When the review produced actionable validated findings or operational blockers,
|
|
1014
|
+
include:
|
|
1015
|
+
|
|
1016
|
+
- the handoff artifact path,
|
|
1017
|
+
- the preserved finding IDs and provenance that `swarm-pr-feedback` must carry
|
|
1018
|
+
forward,
|
|
1019
|
+
- and an explicit question asking whether to continue into
|
|
1020
|
+
`swarm-pr-feedback`.
|
|
1021
|
+
|
|
1022
|
+
Use this exact continuation prompt format:
|
|
1023
|
+
|
|
1024
|
+
```text
|
|
1025
|
+
/swarm pr-feedback <PR_URL> continue from .swarm/pr-review/<run_id>/feedback-handoff.md
|
|
1026
|
+
```
|
|
1027
|
+
|
|
988
1028
|
---
|
|
989
1029
|
|
|
990
1030
|
# Reviewer Prompt Template
|
package/dist/cli/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "opencode-swarm",
|
|
55
|
-
version: "7.
|
|
55
|
+
version: "7.81.1",
|
|
56
56
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
57
57
|
main: "dist/index.js",
|
|
58
58
|
types: "dist/index.d.ts",
|
|
@@ -17194,6 +17194,12 @@ async function handleAcknowledgeSpecDriftCommand(directory, _args, acknowledgedB
|
|
|
17194
17194
|
try {
|
|
17195
17195
|
const plan = await loadPlanJsonOnly(directory);
|
|
17196
17196
|
if (plan?.specHash) {
|
|
17197
|
+
if (stalenessData.specHash_plan !== plan.specHash) {
|
|
17198
|
+
return `Spec drift acknowledgment rejected: The spec has changed since the staleness was detected.
|
|
17199
|
+
Current plan specHash: ${plan.specHash}
|
|
17200
|
+
Staleness file specHash: ${stalenessData.specHash_plan}
|
|
17201
|
+
Please re-run the relevant phase to detect current drift status.`;
|
|
17202
|
+
}
|
|
17197
17203
|
currentHash = await computeSpecHash(directory);
|
|
17198
17204
|
plan.specHash = currentHash ?? undefined;
|
|
17199
17205
|
await savePlan(directory, plan);
|
|
@@ -21443,7 +21449,12 @@ async function tryAcquireLock(directory, filePath, agent, taskId) {
|
|
|
21443
21449
|
try {
|
|
21444
21450
|
release = await import_proper_lockfile.default.lock(lockPath, {
|
|
21445
21451
|
stale: LOCK_TIMEOUT_MS,
|
|
21446
|
-
retries: {
|
|
21452
|
+
retries: {
|
|
21453
|
+
retries: 5,
|
|
21454
|
+
minTimeout: 10,
|
|
21455
|
+
maxTimeout: 500,
|
|
21456
|
+
factor: 2
|
|
21457
|
+
},
|
|
21447
21458
|
realpath: false
|
|
21448
21459
|
});
|
|
21449
21460
|
} catch (err) {
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Handle /swarm pr-feedback command.
|
|
3
3
|
*
|
|
4
|
-
* Triggers the architect to enter MODE: PR_FEEDBACK
|
|
4
|
+
* Triggers the architect to enter MODE: PR_FEEDBACK — the swarm workflow for
|
|
5
5
|
* ingesting and closing KNOWN pull-request feedback (review comments, requested
|
|
6
6
|
* changes, CI failures, merge conflicts, stale branches, pasted notes). This is
|
|
7
7
|
* distinct from /swarm pr-review, which discovers NEW findings.
|
|
8
8
|
*
|
|
9
9
|
* Input contract (PR reference is optional):
|
|
10
|
-
* /swarm pr-feedback 155
|
|
11
|
-
* /swarm pr-feedback 155 also fix the lint errors
|
|
12
|
-
* /swarm pr-feedback
|
|
10
|
+
* /swarm pr-feedback 155 → feedback pass on PR 155
|
|
11
|
+
* /swarm pr-feedback 155 also fix the lint errors → PR 155 + extra instructions
|
|
12
|
+
* /swarm pr-feedback 155 continue from .swarm/pr-review/<run_id>/feedback-handoff.md
|
|
13
|
+
* -> PR 155 + handoff path instructions
|
|
14
|
+
* /swarm pr-feedback owner/repo#155 → shorthand
|
|
13
15
|
* /swarm pr-feedback https://github.com/.../pull/155
|
|
14
|
-
* /swarm pr-feedback
|
|
16
|
+
* /swarm pr-feedback → bare signal; architect builds
|
|
15
17
|
* the ledger from current PR/branch
|
|
16
18
|
* /swarm pr-feedback address the review notes about error handling
|
|
17
|
-
*
|
|
19
|
+
* → no parseable PR ref ⇒ the whole
|
|
18
20
|
* input is forwarded as instructions
|
|
19
21
|
*
|
|
20
22
|
* PR-reference parsing and injection-hardening are shared with /swarm pr-review
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* read back during council evaluation.
|
|
6
6
|
*/
|
|
7
7
|
import type { CouncilCriteria, CouncilCriteriaItem } from './types';
|
|
8
|
-
export declare function writeCriteria(workingDir: string, taskId: string, criteria: CouncilCriteriaItem[]): void
|
|
8
|
+
export declare function writeCriteria(workingDir: string, taskId: string, criteria: CouncilCriteriaItem[]): Promise<void>;
|
|
9
9
|
export declare function readCriteria(workingDir: string, taskId: string): CouncilCriteria | null;
|
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ var package_default;
|
|
|
69
69
|
var init_package = __esm(() => {
|
|
70
70
|
package_default = {
|
|
71
71
|
name: "opencode-swarm",
|
|
72
|
-
version: "7.
|
|
72
|
+
version: "7.81.1",
|
|
73
73
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
74
74
|
main: "dist/index.js",
|
|
75
75
|
types: "dist/index.d.ts",
|
|
@@ -20253,6 +20253,12 @@ async function handleAcknowledgeSpecDriftCommand(directory, _args, acknowledgedB
|
|
|
20253
20253
|
try {
|
|
20254
20254
|
const plan = await loadPlanJsonOnly(directory);
|
|
20255
20255
|
if (plan?.specHash) {
|
|
20256
|
+
if (stalenessData.specHash_plan !== plan.specHash) {
|
|
20257
|
+
return `Spec drift acknowledgment rejected: The spec has changed since the staleness was detected.
|
|
20258
|
+
Current plan specHash: ${plan.specHash}
|
|
20259
|
+
Staleness file specHash: ${stalenessData.specHash_plan}
|
|
20260
|
+
Please re-run the relevant phase to detect current drift status.`;
|
|
20261
|
+
}
|
|
20256
20262
|
currentHash = await computeSpecHash(directory);
|
|
20257
20263
|
plan.specHash = currentHash ?? undefined;
|
|
20258
20264
|
await savePlan(directory, plan);
|
|
@@ -21956,7 +21962,12 @@ async function tryAcquireLock(directory, filePath, agent, taskId) {
|
|
|
21956
21962
|
try {
|
|
21957
21963
|
release = await import_proper_lockfile.default.lock(lockPath, {
|
|
21958
21964
|
stale: LOCK_TIMEOUT_MS,
|
|
21959
|
-
retries: {
|
|
21965
|
+
retries: {
|
|
21966
|
+
retries: 5,
|
|
21967
|
+
minTimeout: 10,
|
|
21968
|
+
maxTimeout: 500,
|
|
21969
|
+
factor: 2
|
|
21970
|
+
},
|
|
21960
21971
|
realpath: false
|
|
21961
21972
|
});
|
|
21962
21973
|
} catch (err2) {
|
|
@@ -124323,7 +124334,8 @@ function buildFinalCouncilFeedback(projectSummary, verdict, vetoedBy, requiredFi
|
|
|
124323
124334
|
|
|
124324
124335
|
// src/council/criteria-store.ts
|
|
124325
124336
|
init_zod();
|
|
124326
|
-
|
|
124337
|
+
init_task_file();
|
|
124338
|
+
import { existsSync as existsSync82, mkdirSync as mkdirSync35, readFileSync as readFileSync56 } from "node:fs";
|
|
124327
124339
|
import { join as join114 } from "node:path";
|
|
124328
124340
|
var COUNCIL_DIR = ".swarm/council";
|
|
124329
124341
|
var CouncilCriteriaSchema = exports_external.object({
|
|
@@ -124335,7 +124347,7 @@ var CouncilCriteriaSchema = exports_external.object({
|
|
|
124335
124347
|
})),
|
|
124336
124348
|
declaredAt: exports_external.string()
|
|
124337
124349
|
});
|
|
124338
|
-
function writeCriteria(workingDir, taskId, criteria) {
|
|
124350
|
+
async function writeCriteria(workingDir, taskId, criteria) {
|
|
124339
124351
|
const dir = join114(workingDir, COUNCIL_DIR);
|
|
124340
124352
|
mkdirSync35(dir, { recursive: true });
|
|
124341
124353
|
const payload = {
|
|
@@ -124343,7 +124355,7 @@ function writeCriteria(workingDir, taskId, criteria) {
|
|
|
124343
124355
|
criteria,
|
|
124344
124356
|
declaredAt: new Date().toISOString()
|
|
124345
124357
|
};
|
|
124346
|
-
|
|
124358
|
+
await atomicWriteFile(join114(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
|
|
124347
124359
|
}
|
|
124348
124360
|
function readCriteria(workingDir, taskId) {
|
|
124349
124361
|
const filePath = join114(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
|
|
@@ -125232,7 +125244,7 @@ var declare_council_criteria = createSwarmTool({
|
|
|
125232
125244
|
}
|
|
125233
125245
|
const existing = readCriteria(workingDir, input.taskId);
|
|
125234
125246
|
const replaced = existing !== null;
|
|
125235
|
-
writeCriteria(workingDir, input.taskId, input.criteria);
|
|
125247
|
+
await writeCriteria(workingDir, input.taskId, input.criteria);
|
|
125236
125248
|
return JSON.stringify({
|
|
125237
125249
|
success: true,
|
|
125238
125250
|
taskId: input.taskId,
|
|
@@ -133001,7 +133013,7 @@ import * as path157 from "node:path";
|
|
|
133001
133013
|
|
|
133002
133014
|
// src/mutation/engine.ts
|
|
133003
133015
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
133004
|
-
import { unlinkSync as unlinkSync19, writeFileSync as
|
|
133016
|
+
import { unlinkSync as unlinkSync19, writeFileSync as writeFileSync24 } from "node:fs";
|
|
133005
133017
|
import * as path156 from "node:path";
|
|
133006
133018
|
|
|
133007
133019
|
// src/mutation/equivalence.ts
|
|
@@ -133191,7 +133203,7 @@ async function executeMutation(patch, testCommand, testFiles, workingDir) {
|
|
|
133191
133203
|
const safeId2 = patch.id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
133192
133204
|
patchFile = path156.join(workingDir, `.mutation_patch_${safeId2}.diff`);
|
|
133193
133205
|
try {
|
|
133194
|
-
|
|
133206
|
+
writeFileSync24(patchFile, patch.patch);
|
|
133195
133207
|
} catch (writeErr) {
|
|
133196
133208
|
error93 = `Failed to write patch file: ${writeErr}`;
|
|
133197
133209
|
outcome = "error";
|
|
@@ -133627,6 +133639,7 @@ init_zod();
|
|
|
133627
133639
|
init_config();
|
|
133628
133640
|
init_schema();
|
|
133629
133641
|
init_manager2();
|
|
133642
|
+
init_task_file();
|
|
133630
133643
|
import * as fs111 from "node:fs";
|
|
133631
133644
|
import * as path167 from "node:path";
|
|
133632
133645
|
|
|
@@ -135821,6 +135834,39 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
135821
135834
|
}
|
|
135822
135835
|
warnings.push(`Knowledge sweep failed for phase ${phase}: ${detail}`);
|
|
135823
135836
|
}
|
|
135837
|
+
const planLockTaskId = `phase-complete-plan-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
135838
|
+
const planFilePath = "plan.json";
|
|
135839
|
+
let planLockResult;
|
|
135840
|
+
try {
|
|
135841
|
+
planLockResult = await tryAcquireLock(dir, planFilePath, agentName, planLockTaskId);
|
|
135842
|
+
} catch (error93) {
|
|
135843
|
+
return JSON.stringify({
|
|
135844
|
+
success: false,
|
|
135845
|
+
phase: args2.phase,
|
|
135846
|
+
status: "incomplete",
|
|
135847
|
+
message: `Failed to acquire lock for plan.json: ${error93 instanceof Error ? error93.message : String(error93)}`,
|
|
135848
|
+
agentsDispatched,
|
|
135849
|
+
agentsMissing,
|
|
135850
|
+
warnings,
|
|
135851
|
+
errors: [error93 instanceof Error ? error93.message : String(error93)],
|
|
135852
|
+
recovery_guidance: "Resolve the filesystem issue (permissions, disk space, or .swarm/locks/ directory) and retry phase_complete."
|
|
135853
|
+
});
|
|
135854
|
+
}
|
|
135855
|
+
if (!planLockResult?.acquired) {
|
|
135856
|
+
return JSON.stringify({
|
|
135857
|
+
success: false,
|
|
135858
|
+
phase: args2.phase,
|
|
135859
|
+
status: "incomplete",
|
|
135860
|
+
message: `Plan write blocked: plan.json is locked by ${planLockResult.existing?.agent ?? "another agent"}`,
|
|
135861
|
+
agentsDispatched,
|
|
135862
|
+
agentsMissing,
|
|
135863
|
+
warnings,
|
|
135864
|
+
errors: [
|
|
135865
|
+
`Concurrent plan write detected — lock held by ${planLockResult.existing?.agent ?? "another agent"} (task: ${planLockResult.existing?.taskId ?? "unknown"})`
|
|
135866
|
+
],
|
|
135867
|
+
recovery_guidance: "Wait a moment and retry phase_complete. The lock will expire automatically if the holding agent fails."
|
|
135868
|
+
});
|
|
135869
|
+
}
|
|
135824
135870
|
try {
|
|
135825
135871
|
const plan = await loadPlan(dir);
|
|
135826
135872
|
if (plan === null) {
|
|
@@ -135849,7 +135895,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
135849
135895
|
const phaseObj = plan2.phases.find((p) => p.id === phase);
|
|
135850
135896
|
if (phaseObj) {
|
|
135851
135897
|
phaseObj.status = "complete";
|
|
135852
|
-
|
|
135898
|
+
await atomicWriteFile(planPath, JSON.stringify(plan2, null, 2));
|
|
135853
135899
|
}
|
|
135854
135900
|
} catch {}
|
|
135855
135901
|
} else if (plan) {
|
|
@@ -135891,9 +135937,17 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
135891
135937
|
const phaseObj = plan.phases.find((p) => p.id === phase);
|
|
135892
135938
|
if (phaseObj) {
|
|
135893
135939
|
phaseObj.status = "complete";
|
|
135894
|
-
|
|
135940
|
+
await atomicWriteFile(planPath, JSON.stringify(plan, null, 2));
|
|
135895
135941
|
}
|
|
135896
135942
|
} catch {}
|
|
135943
|
+
} finally {
|
|
135944
|
+
if (planLockResult?.acquired && planLockResult.lock._release) {
|
|
135945
|
+
try {
|
|
135946
|
+
await planLockResult.lock._release();
|
|
135947
|
+
} catch (releaseError) {
|
|
135948
|
+
warn("[phase_complete] Plan lock release failed (non-blocking):", releaseError instanceof Error ? releaseError.message : String(releaseError));
|
|
135949
|
+
}
|
|
135950
|
+
}
|
|
135897
135951
|
}
|
|
135898
135952
|
}
|
|
135899
135953
|
if (complianceWarnings.length > 0) {
|
|
@@ -143588,7 +143642,7 @@ import {
|
|
|
143588
143642
|
readFileSync as readFileSync81,
|
|
143589
143643
|
renameSync as renameSync24,
|
|
143590
143644
|
unlinkSync as unlinkSync21,
|
|
143591
|
-
writeFileSync as
|
|
143645
|
+
writeFileSync as writeFileSync30
|
|
143592
143646
|
} from "node:fs";
|
|
143593
143647
|
import path181 from "node:path";
|
|
143594
143648
|
init_create_tool();
|
|
@@ -143855,7 +143909,7 @@ function writePhaseCouncilEvidence(workingDir, synthesis, provenance) {
|
|
|
143855
143909
|
};
|
|
143856
143910
|
const tempFile = `${evidenceFile}.tmp-${Date.now()}`;
|
|
143857
143911
|
try {
|
|
143858
|
-
|
|
143912
|
+
writeFileSync30(tempFile, JSON.stringify(evidenceBundle, null, 2), "utf-8");
|
|
143859
143913
|
renameSync24(tempFile, evidenceFile);
|
|
143860
143914
|
} finally {
|
|
143861
143915
|
if (existsSync103(tempFile)) {
|
package/dist/plan/ledger.d.ts
CHANGED
|
@@ -82,6 +82,12 @@ declare function getPlanJsonPath(directory: string): string;
|
|
|
82
82
|
* Compute a SHA-256 hash of the plan state.
|
|
83
83
|
* Uses deterministic JSON serialization for consistent hashing.
|
|
84
84
|
*
|
|
85
|
+
* IMPORTANT: Intentionally excludes `specMtime` and `specHash` fields.
|
|
86
|
+
* These fields track changes to spec.md but do not affect plan execution or structure.
|
|
87
|
+
* Including them would cause the plan hash to change whenever spec metadata changes,
|
|
88
|
+
* invalidating cached plan state unnecessarily. Spec changes are tracked separately
|
|
89
|
+
* in the ledger via `spec_updated` and acknowledgment events.
|
|
90
|
+
*
|
|
85
91
|
* @param plan - The plan to hash
|
|
86
92
|
* @returns Hex-encoded SHA-256 hash
|
|
87
93
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.81.1",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|