opencode-swarm 7.109.0 → 7.109.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/.opencode/skills/critic-gate/SKILL.md +2 -0
  2. package/.opencode/skills/engineering-conventions/SKILL.md +45 -0
  3. package/.opencode/skills/execute/SKILL.md +1 -0
  4. package/.opencode/skills/swarm-pr-feedback/SKILL.md +47 -0
  5. package/dist/cli/{core-vev13ej2.js → core-mbd2g302.js} +5 -1
  6. package/dist/cli/{curator-3efwdf9x.js → curator-6p9tpprq.js} +3 -3
  7. package/dist/cli/{curator-llm-factory-nttpcx4b.js → curator-llm-factory-wqbm9mwr.js} +3 -3
  8. package/dist/cli/{evidence-summary-service-j7jwf20f.js → evidence-summary-service-8tasw2tt.js} +1 -1
  9. package/dist/cli/{guardrail-explain-e85gbj0r.js → guardrail-explain-ynge4ww3.js} +4 -4
  10. package/dist/cli/{hive-promoter-rc6z2x8f.js → hive-promoter-qh2rh8x2.js} +3 -3
  11. package/dist/cli/{index-1mjzvkzj.js → index-3qwaydq1.js} +4 -4
  12. package/dist/cli/{index-f1qyj61w.js → index-3yk9196e.js} +40 -3
  13. package/dist/cli/{index-3e7jajwe.js → index-8a8b5wjf.js} +1 -1
  14. package/dist/cli/{index-vb5t1kvs.js → index-cqbqynzp.js} +10 -10
  15. package/dist/cli/{index-wfq4r95s.js → index-j2v3w1ds.js} +7 -1
  16. package/dist/cli/index.js +3 -3
  17. package/dist/config/index.d.ts +1 -0
  18. package/dist/config/qa-gate-pipeline.d.ts +67 -0
  19. package/dist/config/worktree-isolation-config.d.ts +10 -0
  20. package/dist/hooks/delegation-gate/worktree-isolation.d.ts +4 -2
  21. package/dist/hooks/delegation-gate.d.ts +1 -0
  22. package/dist/hooks/guardrails/index.d.ts +1 -1
  23. package/dist/hooks/guardrails/tool-before.d.ts +5 -0
  24. package/dist/index.js +39 -35
  25. package/dist/plan/ledger.d.ts +60 -0
  26. package/dist/tools/save-plan.d.ts +24 -0
  27. package/dist/worktree/core.d.ts +58 -7
  28. package/dist/worktree/index.d.ts +1 -1
  29. package/package.json +1 -1
@@ -11,6 +11,7 @@ This protocol is loaded on demand by the architect stub in src/agents/architect.
11
11
  ### MODE: CRITIC-GATE
12
12
  Delegate plan to the active swarm's critic agent for review BEFORE any implementation begins.
13
13
  - Send the full plan.md content and codebase context summary
14
+ - Explicitly reference "plan.md" or "critic-gate" in the dispatch prompt text. This lets the mechanical approval-recording gate reliably detect the review and record the critic's APPROVED verdict, which the EXECUTE-phase coder gate then requires.
14
15
  - **APPROVED** → Proceed to MODE: EXECUTE
15
16
  - **NEEDS_REVISION** → Revise the plan based on critic feedback, then resubmit (max 2 cycles)
16
17
  - **REJECTED** → Inform the user of fundamental issues and ask for guidance before proceeding
@@ -26,6 +27,7 @@ You MUST NOT proceed to MODE: EXECUTE without printing this checklist with fille
26
27
  CRITIC-GATE TRIGGER: Run ONCE when you first write the complete .swarm/plan.md.
27
28
  Do NOT re-run CRITIC-GATE before every project phase.
28
29
  If resuming a project with an existing approved plan, CRITIC-GATE is already satisfied.
30
+ Caveat: this assumption breaks if the plan lacks a `plan_critic_gate`-tagged approval snapshot (e.g. a plan approved before this mechanical gate existed, or one where the recording heuristic didn't fire) — in that case the first coder dispatch will fail with `PLAN_CRITIC_GATE_VIOLATION`. If that happens, do not assume CRITIC-GATE is satisfied; re-run it and get a fresh APPROVED verdict.
29
31
 
30
32
  6j. SPEC-GATE (Execute BEFORE any save_plan call):
31
33
  - An effective spec exists iff `/swarm sdd status` reports a resolved spec (it reflects `readEffectiveSpecSync`, which returns null — NO effective spec — for no sources, multiple competing sources (openspec+specify), multi-feature Spec-Kit without a selected feature, or any unresolvable state). `save_plan` rejects (SPEC_REQUIRED) when `/swarm sdd status` reports no resolved spec. The gate is overridable via `SWARM_SKIP_SPEC_GATE=1`.
@@ -127,3 +127,48 @@ When a sandbox executor (`src/sandbox/{linux,macos,win32}/*.ts`) interpolates en
127
127
  - Scope-materialization for lane-scoped resources.
128
128
 
129
129
  A divergence between primary and fallback that is not exercised by a parity test is a regression. The existing per-OS test files `tests/unit/sandbox/{linux,macos,win32}.test.ts` must continue to cover both the primary and fallback paths after every env-affecting change — extend these tests rather than relying on dedicated sandbox-envoverride test files that may or may not exist in your branch.
130
+
131
+ ## SAST baseline capturing (differential scanning)
132
+
133
+ The `sast_scan` tool supports `capture_baseline: true` with a `phase` parameter
134
+ to snapshot pre-existing findings. Subsequent scans with the same `phase` value
135
+ perform differential checking — they only fail on **new** findings, not
136
+ pre-existing ones.
137
+
138
+ ### When to capture a baseline
139
+
140
+ - **Before Phase 1 code changes.** The baseline must reflect the state of the
141
+ codebase *before* any new work is done. This ensures the differential scan
142
+ catches findings introduced by the current session's changes.
143
+
144
+ ### Critical safety guard
145
+
146
+ **NEVER capture a baseline after code changes have been made in a phase.**
147
+ A baseline captured post-edit silently encodes the very bugs the scan is meant
148
+ to catch as "pre-existing," suppressing them indefinitely. This turns the SAST
149
+ gate into theater.
150
+
151
+ ### How to use it
152
+
153
+ 1. Identify the files to scan. In a phase, use the union of declared task-scope
154
+ files plus files the coder is expected to touch. Derive the list from
155
+ `declare_scope` outputs, `git diff --name-only`, or the phase's task specs.
156
+ 2. Before any coder delegation in Phase 1, capture the baseline:
157
+ ```
158
+ sast_scan(directory, changed_files=[...], capture_baseline=true, phase=1)
159
+ ```
160
+ 3. After coder work, scan the same file set:
161
+ ```
162
+ sast_scan(directory, changed_files=[...], phase=1)
163
+ ```
164
+ This returns only NEW findings (absent from the baseline).
165
+ 4. If a pre-existing finding is legitimately fixed, the baseline can be
166
+ re-captured at the start of the next phase with the updated file list.
167
+
168
+ ### Why this matters
169
+
170
+ During PR #1704 review, SAST flagged `RegExp.prototype.exec()` as
171
+ "command injection via child_process.exec()" — a false positive that blocked
172
+ the gate. With a baseline captured before the phase, this pre-existing false
173
+ positive would have been suppressed, and only genuinely new findings would
174
+ surface.
@@ -55,6 +55,7 @@ All other gates: failure → return to coder. No self-fixes. No workarounds.
55
55
  → Subsequent `pre_check_batch` calls with `phase: <N>` will automatically diff against this baseline — only NEW findings (not in baseline) drive the fail verdict.
56
56
  -> PREFLIGHT CHECKLIST: Before first coder delegation, answer "SAST baseline captured before first coder delegation? yes/no/disabled/error". If the answer is no, do not delegate to coder; run 5b-BASE first. If disabled or error, record the exact tool result.
57
57
  5b. the active swarm's coder agent - Implement (if designer scaffold produced, include it as INPUT).
58
+ → If this dispatch fails with `PLAN_CRITIC_GATE_VIOLATION`: the plan has no current critic-approved snapshot (commonly a plan approved before this mechanical gate existed). Do NOT retry the coder dispatch as-is — re-run MODE: CRITIC-GATE to get a fresh critic `APPROVED` verdict, then retry this step.
58
59
  5b-bis. **CODER OUTPUT VERIFICATION**: After the coder reports completion, do NOT accept the self-report alone. Run `diff` (step 5c) and inspect at least one of the modified files yourself to confirm the change exists. The coder may report DONE without having produced any diff. A 30-second read of the changed file(s) catches this failure mode. This is NOT a separate explorer dispatch — the existing `diff` tool at step 5c is the verification mechanism; the key discipline is checking that `diff` returns actual changes before proceeding, rather than forwarding the coder's self-report to the next gate.
59
60
  5c. Run `diff` tool. If `hasContractChanges` → the active swarm's explorer agent integration analysis. If COMPATIBILITY SIGNALS=INCOMPATIBLE or MIGRATION_SURFACE=yes → coder retry. If COMPATIBILITY SIGNALS=COMPATIBLE and MIGRATION_SURFACE=no → proceed.
60
61
  → REQUIRED: Print "diff: [PASS | CONTRACT CHANGE — details]"
@@ -102,6 +102,53 @@ current branch before editing:
102
102
  - **Cache/state claims:** Test both relevant state orders when the behavior
103
103
  depends on cache priming, singleton state, or prior calls.
104
104
 
105
+ ### Automated Security Finding Verification
106
+
107
+ Automated security bots (e.g., hermes-pr-review, CodeRabbit, Gemini) frequently
108
+ produce findings rated CRITICAL or HIGH that are false positives. In a recent
109
+ PR review cycle, 7/7 bot security findings were false positives upon source
110
+ verification. Before acting on any bot security finding, perform these
111
+ source-level checks:
112
+
113
+ 1. **`child_process.exec` vs `RegExp.exec`**: SAST rules pattern-match on
114
+ `.exec(` and cannot distinguish `child_process.exec(userInput)` (real
115
+ injection risk) from `/^pattern$/.exec(str)` (safe regex test). Read the
116
+ actual line to determine which `.exec` is called.
117
+
118
+ 2. **Schema validation already present**: Bots may flag "missing type
119
+ validation" without checking the Zod schema. Search for the field name in
120
+ `src/config/schema.ts` — `z.number().int()`, `z.string().min()`, etc. are
121
+ runtime validators that run before the code path the bot reviewed.
122
+
123
+ 3. **`Object.assign` mutation claims**: Bots may claim `Object.assign` mutates
124
+ the source object. Check whether the call is `Object.assign(target, source)`
125
+ (mutates target) vs `Object.assign({}, source)` or a manual copy loop into a
126
+ new `{}` (creates a new object, source is safe). Read the actual assignment.
127
+
128
+ 4. **Path containment for system-generated paths**: Bots may flag "path
129
+ traversal" on file paths. Check whether the path is user-controlled (real
130
+ risk) or system-generated from `provisionWorktree`, `mkdtempSync`, or
131
+ similar (no user input reaches the path). Trace the variable's origin.
132
+
133
+ 5. **Value validation vs key validation**: Bots may suggest validating env var
134
+ *values* for shell injection characters. Check whether the value is passed
135
+ through a sandbox executor that escapes arguments (e.g., `wrapCommand`
136
+ which returns a shell-quoted / `psStringEscape`-escaped string for the
137
+ `bunSpawn` array-form argv to consume). Value validation would break
138
+ legitimate env vars (PATH with `;`, URLs with `$`); escaping is the
139
+ sandbox's job — see `engineering-conventions` § "Sandbox env overrides"
140
+ for the full escape contract.
141
+
142
+ 6. **Deduplication for independent resources**: Bots may suggest deduplicating
143
+ cache redirects or env var entries. Check whether the entries map to
144
+ independent keys (different env var names) — independent keys cannot
145
+ "collide" and deduplication is nonsensical.
146
+
147
+ **Rule:** For any bot finding rated CRITICAL or HIGH, read the actual source
148
+ line AND its surrounding context (parent function, schema definition, type
149
+ annotations) before accepting the finding. If the finding is disproved, record
150
+ it in the closure ledger with the specific source evidence that disproves it.
151
+
105
152
  ## Operating Stance
106
153
 
107
154
  Treat every review comment, CI failure, bot summary, PR body claim, and pasted note
@@ -6,13 +6,15 @@ import {
6
6
  checkPathBudget,
7
7
  cleanUntrackedFiles,
8
8
  isCleanWorktree,
9
+ isPathUnderSwarmWorktreeBase,
9
10
  makeWorktreeBranchName,
10
11
  provisionWorktree,
11
12
  removeLaneProfileFromDiskReal,
12
13
  removeWorktree,
14
+ resolveWorktreeBaseDir,
13
15
  shortenWorktreePath,
14
16
  writeLaneProfileToDiskReal
15
- } from "./index-f1qyj61w.js";
17
+ } from "./index-3yk9196e.js";
16
18
  import"./index-5e4e2hvv.js";
17
19
  import"./index-p0arc26j.js";
18
20
  import"./index-zgwm4ryv.js";
@@ -21,10 +23,12 @@ import"./index-a76rekgs.js";
21
23
  export {
22
24
  writeLaneProfileToDiskReal,
23
25
  shortenWorktreePath,
26
+ resolveWorktreeBaseDir,
24
27
  removeWorktree,
25
28
  removeLaneProfileFromDiskReal,
26
29
  provisionWorktree,
27
30
  makeWorktreeBranchName,
31
+ isPathUnderSwarmWorktreeBase,
28
32
  isCleanWorktree,
29
33
  cleanUntrackedFiles,
30
34
  checkPathBudget,
@@ -11,12 +11,12 @@ import {
11
11
  runCuratorInit,
12
12
  runCuratorPhase,
13
13
  writeCuratorSummary
14
- } from "./index-vb5t1kvs.js";
14
+ } from "./index-cqbqynzp.js";
15
15
  import"./index-wj4jeavn.js";
16
16
  import"./index-5gnp8fyw.js";
17
17
  import"./index-c8s9a3zh.js";
18
18
  import"./index-fm7xz1ne.js";
19
- import"./index-wfq4r95s.js";
19
+ import"./index-j2v3w1ds.js";
20
20
  import"./index-scww5b77.js";
21
21
  import"./index-yw8qpf51.js";
22
22
  import"./index-85bacexr.js";
@@ -31,7 +31,7 @@ import"./index-bfwt3abw.js";
31
31
  import"./index-wvrj16f0.js";
32
32
  import"./index-q1exe2b3.js";
33
33
  import"./index-x2q0gnt8.js";
34
- import"./index-f1qyj61w.js";
34
+ import"./index-3yk9196e.js";
35
35
  import"./index-r3j3458j.js";
36
36
  import"./index-jtqkh8jf.js";
37
37
  import"./index-5e4e2hvv.js";
@@ -1,12 +1,12 @@
1
1
  // @bun
2
2
  import {
3
3
  createCuratorLLMDelegate
4
- } from "./index-vb5t1kvs.js";
4
+ } from "./index-cqbqynzp.js";
5
5
  import"./index-wj4jeavn.js";
6
6
  import"./index-5gnp8fyw.js";
7
7
  import"./index-c8s9a3zh.js";
8
8
  import"./index-fm7xz1ne.js";
9
- import"./index-wfq4r95s.js";
9
+ import"./index-j2v3w1ds.js";
10
10
  import"./index-scww5b77.js";
11
11
  import"./index-yw8qpf51.js";
12
12
  import"./index-85bacexr.js";
@@ -21,7 +21,7 @@ import"./index-bfwt3abw.js";
21
21
  import"./index-wvrj16f0.js";
22
22
  import"./index-q1exe2b3.js";
23
23
  import"./index-x2q0gnt8.js";
24
- import"./index-f1qyj61w.js";
24
+ import"./index-3yk9196e.js";
25
25
  import"./index-r3j3458j.js";
26
26
  import"./index-jtqkh8jf.js";
27
27
  import"./index-5e4e2hvv.js";
@@ -6,7 +6,7 @@ import {
6
6
  loadPlanJsonOnly,
7
7
  mergeDurableGateEntriesFromEvidence,
8
8
  readDurableGateEvidence
9
- } from "./index-wfq4r95s.js";
9
+ } from "./index-j2v3w1ds.js";
10
10
  import"./index-scww5b77.js";
11
11
  import"./index-q1exe2b3.js";
12
12
  import"./index-r3j3458j.js";
@@ -1,13 +1,13 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-3e7jajwe.js";
5
- import"./index-vb5t1kvs.js";
4
+ } from "./index-8a8b5wjf.js";
5
+ import"./index-cqbqynzp.js";
6
6
  import"./index-wj4jeavn.js";
7
7
  import"./index-5gnp8fyw.js";
8
8
  import"./index-c8s9a3zh.js";
9
9
  import"./index-fm7xz1ne.js";
10
- import"./index-wfq4r95s.js";
10
+ import"./index-j2v3w1ds.js";
11
11
  import"./index-scww5b77.js";
12
12
  import"./index-yw8qpf51.js";
13
13
  import"./index-85bacexr.js";
@@ -22,7 +22,7 @@ import"./index-bfwt3abw.js";
22
22
  import"./index-wvrj16f0.js";
23
23
  import"./index-q1exe2b3.js";
24
24
  import"./index-x2q0gnt8.js";
25
- import"./index-f1qyj61w.js";
25
+ import"./index-3yk9196e.js";
26
26
  import"./index-r3j3458j.js";
27
27
  import"./index-jtqkh8jf.js";
28
28
  import"./index-5e4e2hvv.js";
@@ -5,12 +5,12 @@ import {
5
5
  isHiveEligible,
6
6
  promoteFromSwarm,
7
7
  promoteToHive
8
- } from "./index-vb5t1kvs.js";
8
+ } from "./index-cqbqynzp.js";
9
9
  import"./index-wj4jeavn.js";
10
10
  import"./index-5gnp8fyw.js";
11
11
  import"./index-c8s9a3zh.js";
12
12
  import"./index-fm7xz1ne.js";
13
- import"./index-wfq4r95s.js";
13
+ import"./index-j2v3w1ds.js";
14
14
  import"./index-scww5b77.js";
15
15
  import"./index-yw8qpf51.js";
16
16
  import"./index-85bacexr.js";
@@ -25,7 +25,7 @@ import"./index-bfwt3abw.js";
25
25
  import"./index-wvrj16f0.js";
26
26
  import"./index-q1exe2b3.js";
27
27
  import"./index-x2q0gnt8.js";
28
- import"./index-f1qyj61w.js";
28
+ import"./index-3yk9196e.js";
29
29
  import"./index-r3j3458j.js";
30
30
  import"./index-jtqkh8jf.js";
31
31
  import"./index-5e4e2hvv.js";
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-3e7jajwe.js";
4
+ } from "./index-8a8b5wjf.js";
5
5
  import {
6
6
  handleGuardrailLog
7
7
  } from "./index-vx76tcxe.js";
@@ -80,12 +80,12 @@ import {
80
80
  handleWriteRetroCommand,
81
81
  normalizeSwarmCommandInput,
82
82
  resolveCommand
83
- } from "./index-vb5t1kvs.js";
83
+ } from "./index-cqbqynzp.js";
84
84
  import"./index-wj4jeavn.js";
85
85
  import"./index-5gnp8fyw.js";
86
86
  import"./index-c8s9a3zh.js";
87
87
  import"./index-fm7xz1ne.js";
88
- import"./index-wfq4r95s.js";
88
+ import"./index-j2v3w1ds.js";
89
89
  import"./index-scww5b77.js";
90
90
  import"./index-yw8qpf51.js";
91
91
  import"./index-85bacexr.js";
@@ -104,7 +104,7 @@ import"./index-bfwt3abw.js";
104
104
  import"./index-wvrj16f0.js";
105
105
  import"./index-q1exe2b3.js";
106
106
  import"./index-x2q0gnt8.js";
107
- import"./index-f1qyj61w.js";
107
+ import"./index-3yk9196e.js";
108
108
  import"./index-r3j3458j.js";
109
109
  import"./index-jtqkh8jf.js";
110
110
  import"./index-5e4e2hvv.js";
@@ -162,9 +162,39 @@ function makeWorktreeBranchName(sessionId, id, options) {
162
162
  }
163
163
  return `swarm/${options.purpose}/${sessionId}/${id}`;
164
164
  }
165
+ function resolveWorktreeBaseDir(directory, worktreeDir) {
166
+ return worktreeDir ? path.resolve(directory, worktreeDir) : path.resolve(path.dirname(directory), ".swarm-worktrees");
167
+ }
168
+ function isPathUnderSwarmWorktreeBase(targetPath, directory, worktreeDirOverrides = []) {
169
+ const caseInsensitive = process.platform === "win32";
170
+ let realTarget;
171
+ try {
172
+ realTarget = fs.realpathSync(path.resolve(directory, targetPath));
173
+ } catch {
174
+ return false;
175
+ }
176
+ const bases = [
177
+ resolveWorktreeBaseDir(directory),
178
+ ...worktreeDirOverrides.map((d) => resolveWorktreeBaseDir(directory, d))
179
+ ];
180
+ const cmpTarget = caseInsensitive ? realTarget.toLowerCase() : realTarget;
181
+ return bases.some((base) => {
182
+ let realBase;
183
+ try {
184
+ realBase = fs.realpathSync(base);
185
+ } catch {
186
+ realBase = path.resolve(base);
187
+ }
188
+ const cmpBase = caseInsensitive ? realBase.toLowerCase() : realBase;
189
+ if (cmpTarget === cmpBase)
190
+ return true;
191
+ const rel = path.relative(cmpBase, cmpTarget);
192
+ return rel.length > 0 && !rel.startsWith("..") && !path.isAbsolute(rel);
193
+ });
194
+ }
165
195
  async function provisionWorktree(directory, id, sessionId, options) {
166
196
  const branchName = makeWorktreeBranchName(sessionId, id, options);
167
- let worktreePath = options.worktreeDir ? path.resolve(directory, options.worktreeDir, sessionId, id) : path.resolve(path.dirname(directory), ".swarm-worktrees", sessionId, id);
197
+ let worktreePath = path.resolve(resolveWorktreeBaseDir(directory, options.worktreeDir), sessionId, id);
168
198
  const budgetResult = await checkPathBudget(worktreePath, directory);
169
199
  if (budgetResult.ok === false) {
170
200
  if (options.worktreeDir) {
@@ -285,7 +315,7 @@ async function provisionWorktree(directory, id, sessionId, options) {
285
315
  sessionId
286
316
  };
287
317
  }
288
- async function removeWorktree(worktreePath, projectRoot) {
318
+ async function removeWorktree(worktreePath, projectRoot, options) {
289
319
  const isWindows = _internals.platform === "win32";
290
320
  const MAX_RETRIES = 4;
291
321
  const RETRY_DELAY_MS = 2000;
@@ -300,6 +330,13 @@ async function removeWorktree(worktreePath, projectRoot) {
300
330
  await _internals.sleep(RETRY_DELAY_MS);
301
331
  continue;
302
332
  }
333
+ if (options?.force && isPathUnderSwarmWorktreeBase(worktreePath, projectRoot, options.worktreeDir ? [options.worktreeDir] : [])) {
334
+ const forced = await runGit(["worktree", "remove", "--force", worktreePath], projectRoot);
335
+ if (forced.exitCode === 0) {
336
+ return { success: true };
337
+ }
338
+ return { error: forced.stderr.trim() || forced.stdout.trim() };
339
+ }
303
340
  return { error: lastError };
304
341
  }
305
342
  return { error: lastError };
@@ -415,4 +452,4 @@ async function assertCleanWorkingTree(directory) {
415
452
  return { clean: true };
416
453
  }
417
454
 
418
- export { _internals, writeLaneProfileToDiskReal, removeLaneProfileFromDiskReal, checkPathBudget, shortenWorktreePath, makeWorktreeBranchName, provisionWorktree, removeWorktree, isCleanWorktree, autoCommitDirty, cleanUntrackedFiles, assertCleanWorkingTree };
455
+ export { _internals, writeLaneProfileToDiskReal, removeLaneProfileFromDiskReal, checkPathBudget, shortenWorktreePath, makeWorktreeBranchName, resolveWorktreeBaseDir, isPathUnderSwarmWorktreeBase, provisionWorktree, removeWorktree, isCleanWorktree, autoCommitDirty, cleanUntrackedFiles, assertCleanWorkingTree };
@@ -12,7 +12,7 @@ import {
12
12
  detectPosixWrites,
13
13
  detectWindowsWrites,
14
14
  resolveWriteTargets
15
- } from "./index-vb5t1kvs.js";
15
+ } from "./index-cqbqynzp.js";
16
16
  import {
17
17
  checkFileAuthority,
18
18
  classifyFile,
@@ -54,7 +54,7 @@ import {
54
54
  savePlan,
55
55
  transientBackoff,
56
56
  validateProjectRoot
57
- } from "./index-wfq4r95s.js";
57
+ } from "./index-j2v3w1ds.js";
58
58
  import {
59
59
  buildOpenSpecProjectionSync,
60
60
  detectSpeckit,
@@ -7203,11 +7203,11 @@ var _internals7 = {
7203
7203
  return KnowledgeConfigSchema2.parse({});
7204
7204
  },
7205
7205
  applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig) => {
7206
- const { applyCuratorKnowledgeUpdates } = await import("./curator-3efwdf9x.js");
7206
+ const { applyCuratorKnowledgeUpdates } = await import("./curator-6p9tpprq.js");
7207
7207
  return applyCuratorKnowledgeUpdates(directory, recommendations, knowledgeConfig);
7208
7208
  },
7209
7209
  checkHivePromotions: async (entries, knowledgeConfig) => {
7210
- const { checkHivePromotions } = await import("./hive-promoter-rc6z2x8f.js");
7210
+ const { checkHivePromotions } = await import("./hive-promoter-qh2rh8x2.js");
7211
7211
  return checkHivePromotions(entries, knowledgeConfig);
7212
7212
  },
7213
7213
  applyProposalTriage: async (directory, triage) => {
@@ -16499,8 +16499,8 @@ var _internals21 = {
16499
16499
  loadCuratorDeps: async () => {
16500
16500
  const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
16501
16501
  import("./schema-x17zmd03.js"),
16502
- import("./curator-3efwdf9x.js"),
16503
- import("./curator-llm-factory-nttpcx4b.js")
16502
+ import("./curator-6p9tpprq.js"),
16503
+ import("./curator-llm-factory-wqbm9mwr.js")
16504
16504
  ]);
16505
16505
  return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
16506
16506
  }
@@ -16996,7 +16996,7 @@ import { fileURLToPath } from "url";
16996
16996
  // package.json
16997
16997
  var package_default = {
16998
16998
  name: "opencode-swarm",
16999
- version: "7.109.0",
16999
+ version: "7.109.2",
17000
17000
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
17001
17001
  main: "dist/index.js",
17002
17002
  types: "dist/index.d.ts",
@@ -19254,7 +19254,7 @@ async function handleEvidenceCommand(directory, args) {
19254
19254
  return formatTaskEvidenceMarkdown(evidenceData);
19255
19255
  }
19256
19256
  async function handleEvidenceSummaryCommand(directory) {
19257
- const { buildEvidenceSummary } = await import("./evidence-summary-service-j7jwf20f.js");
19257
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-8tasw2tt.js");
19258
19258
  const artifact = await buildEvidenceSummary(directory);
19259
19259
  if (!artifact) {
19260
19260
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -31714,7 +31714,7 @@ function buildDetailedHelp(commandName, entry) {
31714
31714
  async function handleHelpCommand(ctx) {
31715
31715
  const targetCommand = ctx.args.join(" ");
31716
31716
  if (!targetCommand) {
31717
- const { buildHelpText } = await import("./index-1mjzvkzj.js");
31717
+ const { buildHelpText } = await import("./index-3qwaydq1.js");
31718
31718
  return buildHelpText();
31719
31719
  }
31720
31720
  const tokens = targetCommand.split(/\s+/);
@@ -31723,7 +31723,7 @@ async function handleHelpCommand(ctx) {
31723
31723
  return _internals46.buildDetailedHelp(resolved.key, resolved.entry);
31724
31724
  }
31725
31725
  const similar = _internals46.findSimilarCommands(targetCommand);
31726
- const { buildHelpText: fullHelp } = await import("./index-1mjzvkzj.js");
31726
+ const { buildHelpText: fullHelp } = await import("./index-3qwaydq1.js");
31727
31727
  if (similar.length > 0) {
31728
31728
  return `Command '/swarm ${targetCommand}' not found.
31729
31729
 
@@ -31856,7 +31856,7 @@ var COMMAND_REGISTRY = {
31856
31856
  },
31857
31857
  "guardrail explain": {
31858
31858
  handler: async (ctx) => {
31859
- const { handleGuardrailExplain } = await import("./guardrail-explain-e85gbj0r.js");
31859
+ const { handleGuardrailExplain } = await import("./guardrail-explain-ynge4ww3.js");
31860
31860
  return handleGuardrailExplain(ctx.directory, ctx.args);
31861
31861
  },
31862
31862
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -2339,7 +2339,7 @@ async function takeSnapshotWithRetry(directory, plan, options) {
2339
2339
  } catch {}
2340
2340
  }
2341
2341
  async function takeSnapshotEvent(directory, plan, options) {
2342
- const payloadHash = computePlanHash(plan);
2342
+ const payloadHash = options?.payloadHashOverride ?? computePlanHash(plan);
2343
2343
  const snapshotPayload = {
2344
2344
  plan,
2345
2345
  payload_hash: payloadHash
@@ -2536,6 +2536,9 @@ async function quarantineLedgerSuffix(directory, badSuffix) {
2536
2536
  }
2537
2537
  async function loadLastApprovedPlan(directory, expectedPlanId) {
2538
2538
  const events = await readLedgerEvents(directory);
2539
+ return findLastApprovedSnapshot(events, expectedPlanId);
2540
+ }
2541
+ function findLastApprovedSnapshot(events, expectedPlanId, extraFilter) {
2539
2542
  if (events.length === 0) {
2540
2543
  return null;
2541
2544
  }
@@ -2558,6 +2561,9 @@ async function loadLastApprovedPlan(directory, expectedPlanId) {
2558
2561
  continue;
2559
2562
  }
2560
2563
  }
2564
+ if (extraFilter && !extraFilter(payload)) {
2565
+ continue;
2566
+ }
2561
2567
  return {
2562
2568
  plan: payload.plan,
2563
2569
  seq: event.seq,
package/dist/cli/index.js CHANGED
@@ -7,12 +7,12 @@ import {
7
7
  getPluginLockFilePaths,
8
8
  package_default,
9
9
  resolveCommand
10
- } from "./index-vb5t1kvs.js";
10
+ } from "./index-cqbqynzp.js";
11
11
  import"./index-wj4jeavn.js";
12
12
  import"./index-5gnp8fyw.js";
13
13
  import"./index-c8s9a3zh.js";
14
14
  import"./index-fm7xz1ne.js";
15
- import"./index-wfq4r95s.js";
15
+ import"./index-j2v3w1ds.js";
16
16
  import"./index-scww5b77.js";
17
17
  import"./index-yw8qpf51.js";
18
18
  import"./index-85bacexr.js";
@@ -29,7 +29,7 @@ import"./index-bfwt3abw.js";
29
29
  import"./index-wvrj16f0.js";
30
30
  import"./index-q1exe2b3.js";
31
31
  import"./index-x2q0gnt8.js";
32
- import"./index-f1qyj61w.js";
32
+ import"./index-3yk9196e.js";
33
33
  import"./index-r3j3458j.js";
34
34
  import"./index-jtqkh8jf.js";
35
35
  import"./index-5e4e2hvv.js";
@@ -22,3 +22,4 @@ export type { AgentOverrideConfig, AutomationCapabilities, AutomationConfig, Aut
22
22
  export { AgentOverrideConfigSchema, AutomationCapabilitiesSchema, AutomationConfigSchema, AutomationModeSchema, LeanTurboConfigSchema, MemoryConfigSchema, ModelPricingConfigSchema, PhaseCompleteConfigSchema, PipelineConfigSchema, PluginConfigSchema, PricingConfigSchema, RepoGraphConfigSchema, SkillPropagationConfigSchema, SwarmConfigSchema, TurboConfigSchema, WorktreeIsolationConfigSchema, } from './schema';
23
23
  export type { DeltaSpec, Obligation, SpecDelta, SpecRequirement, SpecScenario, SpecSection, SwarmSpec, } from './spec-schema';
24
24
  export { DeltaSpecSchema, ObligationSchema, SpecDeltaSchema, SpecRequirementSchema, SpecScenarioSchema, SpecSectionSchema, SwarmSpecSchema, validateSpecContent, } from './spec-schema';
25
+ export { resolveWorktreeIsolationConfig } from './worktree-isolation-config';
@@ -0,0 +1,67 @@
1
+ export interface QaGatePipelineStep {
2
+ readonly id: string;
3
+ readonly docsLabel: string;
4
+ readonly protocolToken: string;
5
+ }
6
+ export declare const QA_GATE_PIPELINE_STEPS: readonly [{
7
+ readonly id: "diff";
8
+ readonly docsLabel: "`diff`";
9
+ readonly protocolToken: "diff";
10
+ }, {
11
+ readonly id: "syntax_check";
12
+ readonly docsLabel: "`syntax_check`";
13
+ readonly protocolToken: "syntax_check";
14
+ }, {
15
+ readonly id: "placeholder_scan";
16
+ readonly docsLabel: "`placeholder_scan`";
17
+ readonly protocolToken: "placeholder_scan";
18
+ }, {
19
+ readonly id: "imports";
20
+ readonly docsLabel: "`imports`";
21
+ readonly protocolToken: "imports";
22
+ }, {
23
+ readonly id: "lint_fix";
24
+ readonly docsLabel: "`lint fix`";
25
+ readonly protocolToken: "lint";
26
+ }, {
27
+ readonly id: "build_check";
28
+ readonly docsLabel: "`build_check`";
29
+ readonly protocolToken: "build_check";
30
+ }, {
31
+ readonly id: "pre_check_batch";
32
+ readonly docsLabel: "`pre_check_batch`";
33
+ readonly protocolToken: "pre_check_batch";
34
+ }, {
35
+ readonly id: "reviewer";
36
+ readonly docsLabel: "`reviewer`";
37
+ readonly protocolToken: "reviewer";
38
+ }, {
39
+ readonly id: "security_reviewer";
40
+ readonly docsLabel: "`security-reviewer`";
41
+ readonly protocolToken: "security-reviewer";
42
+ }, {
43
+ readonly id: "test_engineer_verification";
44
+ readonly docsLabel: "`test_engineer verification`";
45
+ readonly protocolToken: "testengineer-verification";
46
+ }, {
47
+ readonly id: "regression_sweep";
48
+ readonly docsLabel: "`regression-sweep`";
49
+ readonly protocolToken: "regression-sweep";
50
+ }, {
51
+ readonly id: "test_drift";
52
+ readonly docsLabel: "`test-drift`";
53
+ readonly protocolToken: "test-drift";
54
+ }, {
55
+ readonly id: "test_engineer_adversarial";
56
+ readonly docsLabel: "`test_engineer adversarial`";
57
+ readonly protocolToken: "ADVERSARIAL TEST STEP";
58
+ }, {
59
+ readonly id: "coverage_check";
60
+ readonly docsLabel: "`coverage check`";
61
+ readonly protocolToken: "COVERAGE CHECK";
62
+ }, {
63
+ readonly id: "pre_commit_checklist";
64
+ readonly docsLabel: "`pre-commit checklist`";
65
+ readonly protocolToken: "PRE-COMMIT RULE";
66
+ }];
67
+ export declare const QA_GATE_PIPELINE_STEP_COUNT: 15;
@@ -0,0 +1,10 @@
1
+ import type { PluginConfig, WorktreeIsolationConfig } from './schema';
2
+ /**
3
+ * Pure config helper that resolves the worktree-isolation configuration
4
+ * from a PluginConfig. Lives in this leaf module (no heavy imports) so
5
+ * the plugin entry (src/index.ts) can call it during init without
6
+ * pulling in the worktree lifecycle module.
7
+ *
8
+ * @see tests/unit/turbo/lean/init-safety.test.ts (static source check)
9
+ */
10
+ export declare function resolveWorktreeIsolationConfig(config: PluginConfig): WorktreeIsolationConfig;
@@ -58,6 +58,8 @@ export interface StandardWorktreeDispatch {
58
58
  mergeStrategy: 'merge' | 'rebase' | 'cherry-pick';
59
59
  /** FR-201: 0-based lane index for runtime profile derivation. */
60
60
  laneIndex: number;
61
+ /** Configured worktree-dir override, so cleanup trusts the same base. */
62
+ worktree_dir?: string;
61
63
  }
62
64
  export interface AwaitingMergeRecord {
63
65
  callID: string;
@@ -103,7 +105,8 @@ export declare function checkStandardWorktreeSerializationRelease(sessionID: str
103
105
  */
104
106
  declare function rememberStandardWorktreeSerializationSession(sessionID: string): boolean;
105
107
  export declare function sanitizeWorktreeTaskId(raw: string): string;
106
- export declare function resolveWorktreeIsolationConfig(config: PluginConfig): WorktreeIsolationConfig;
108
+ import { resolveWorktreeIsolationConfig } from '../../config/worktree-isolation-config';
109
+ export { resolveWorktreeIsolationConfig };
107
110
  export declare function precreateStandardWorktreeSession(args: {
108
111
  config: PluginConfig;
109
112
  directory: string;
@@ -163,4 +166,3 @@ export declare const _internals: {
163
166
  startupOrphanRecovery: typeof startupOrphanRecovery;
164
167
  cleanupOrphanedBranches: typeof cleanupOrphanedBranches;
165
168
  };
166
- export {};
@@ -110,6 +110,7 @@ export declare const _internals: {
110
110
  buildParallelExecutionGuidance: typeof buildParallelExecutionGuidance;
111
111
  loadPlanJsonOnly: typeof loadPlanJsonOnly;
112
112
  resetStandardWorktreeIsolationState: typeof resetStandardWorktreeIsolationState;
113
+ PLAN_CRITIC_TASK_SIGNALS: readonly ["critic-gate", "plan critic", "review plan", "review the plan", "plan.md", "approve the plan", "plan approval"];
113
114
  provisionWorktree: typeof _wtiInternals.provisionWorktree;
114
115
  removeWorktree: typeof _wtiInternals.removeWorktree;
115
116
  attemptMergeBackFromDirty: typeof _wtiInternals.attemptMergeBackFromDirty;
@@ -47,7 +47,7 @@ export declare function redactShellCommand(cmd: string): string;
47
47
  * @param config Guardrails configuration (optional)
48
48
  * @returns Tool before/after hooks and messages transform hook
49
49
  */
50
- export declare function createGuardrailsHooks(directory: string, directoryOrConfig?: string | GuardrailsConfig, config?: GuardrailsConfig, authorityConfig?: AuthorityConfig): {
50
+ export declare function createGuardrailsHooks(directory: string, directoryOrConfig?: string | GuardrailsConfig, config?: GuardrailsConfig, authorityConfig?: AuthorityConfig, worktreeBaseDirOverrides?: string[]): {
51
51
  toolBefore: (input: {
52
52
  tool: string;
53
53
  sessionID: string;