opencode-swarm 7.89.0 → 7.90.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.
Files changed (30) hide show
  1. package/.opencode/skills/commit-pr/SKILL.md +548 -0
  2. package/.opencode/skills/engineering-conventions/SKILL.md +57 -0
  3. package/.opencode/skills/phase-wrap/SKILL.md +1 -1
  4. package/.opencode/skills/running-tests/SKILL.md +282 -0
  5. package/.opencode/skills/writing-tests/SKILL.md +794 -0
  6. package/dist/cli/{evidence-summary-service-5me91eq8.js → evidence-summary-service-mr9sns2d.js} +5 -5
  7. package/dist/cli/{gate-evidence-y8zn7fe2.js → gate-evidence-nphg8hay.js} +4 -4
  8. package/dist/cli/{guardrail-explain-hy0zz0p6.js → guardrail-explain-w29j6dmx.js} +10 -10
  9. package/dist/cli/{index-9w07ye9b.js → index-4gm78w6c.js} +23 -14
  10. package/dist/cli/{index-1ccnwh54.js → index-5hrexm02.js} +3 -3
  11. package/dist/cli/{index-bcp79s17.js → index-91qtsbce.js} +1 -1
  12. package/dist/cli/{index-dprk5c5f.js → index-c5d6tgbs.js} +10 -10
  13. package/dist/cli/{index-6k31ysgd.js → index-j49ge0mg.js} +1 -1
  14. package/dist/cli/{index-fjwwrwr5.js → index-kv4dd5c5.js} +1 -1
  15. package/dist/cli/{index-e7h9bb6v.js → index-mh1ej70w.js} +2 -2
  16. package/dist/cli/{index-vqyfscxd.js → index-sf08zj91.js} +1 -1
  17. package/dist/cli/{index-axwxkbdd.js → index-w7gkpmq8.js} +2 -2
  18. package/dist/cli/{index-p0ye10nd.js → index-xchgryg4.js} +10 -2
  19. package/dist/cli/{index-8y7qetpg.js → index-y1z6yaq4.js} +3 -3
  20. package/dist/cli/index.js +9 -9
  21. package/dist/cli/{knowledge-store-gsy6p46z.js → knowledge-store-eqans52j.js} +4 -4
  22. package/dist/cli/{pending-delegations-35fvcj7z.js → pending-delegations-shqbvfjc.js} +2 -2
  23. package/dist/cli/{pr-subscriptions-b18n1yd8.js → pr-subscriptions-2565fpsc.js} +3 -3
  24. package/dist/cli/{skill-generator-1hzfyhth.js → skill-generator-d0jzw6n2.js} +5 -5
  25. package/dist/cli/{telemetry-9bbyxrvn.js → telemetry-aa1ma1dr.js} +4 -2
  26. package/dist/config/bundled-skills.d.ts +1 -1
  27. package/dist/config/skill-mirrors.d.ts +87 -0
  28. package/dist/index.js +21 -5
  29. package/dist/telemetry.d.ts +7 -0
  30. package/package.json +6 -1
@@ -0,0 +1,548 @@
1
+ ---
2
+ name: commit-pr
3
+ description: >
4
+ Apply when committing, pushing, opening or updating a PR, writing a pull request,
5
+ creating release notes, or closing out remote CI. Enforces the opencode-swarm
6
+ invariant audit, release-note fragment workflow, full validation suite, issue
7
+ comment requirement, and post-PR lifecycle rules.
8
+ effort: medium
9
+ ---
10
+
11
+ # Commit & PR Protocol
12
+
13
+ Follow every step in order. Do not skip steps.
14
+
15
+ ## Step -1 - Mandatory invariant audit
16
+
17
+ Before any build, test, push, or PR action, read:
18
+
19
+ 1. [`../../../AGENTS.md`](../../../AGENTS.md)
20
+ 2. [`../../../docs/engineering-invariants.md`](../../../docs/engineering-invariants.md)
21
+
22
+ For every touched invariant, prepare concrete evidence for the PR body. The PR body must include:
23
+
24
+ ```md
25
+ ## Invariant audit
26
+ - 1 (plugin init): touched / not touched - <evidence>
27
+ - 2 (runtime portability): touched / not touched - <evidence>
28
+ - 3 (subprocesses): touched / not touched - <evidence>
29
+ - 4 (.swarm containment): touched / not touched - <evidence>
30
+ - 5 (plan durability): touched / not touched - <evidence>
31
+ - 6 (test_runner safety): touched / not touched - <evidence>
32
+ - 7 (test writing): touched / not touched - <evidence>
33
+ - 8 (session state): touched / not touched - <evidence>
34
+ - 9 (guardrails/retry): touched / not touched - <evidence>
35
+ - 10 (chat/system msg): touched / not touched - <evidence>
36
+ - 11 (tool registration): touched / not touched - <evidence>
37
+ - 12 (release/cache): touched / not touched - <evidence>
38
+ ```
39
+
40
+ If a touched invariant cannot be proven from source and test output, do not push.
41
+
42
+ ### Required validations for touched invariants
43
+
44
+ If invariants 1, 2, or 3 are touched, run all three:
45
+
46
+ ```bash
47
+ bun run build
48
+ node scripts/repro-704.mjs
49
+ node --input-type=module -e "await import('./dist/index.js'); console.log('dist import OK')"
50
+ ```
51
+
52
+ If invariant 3 is touched, audit changed source files for subprocess use:
53
+
54
+ ```bash
55
+ git diff --name-only origin/main..HEAD | xargs -r grep -nE "bunSpawn\(|spawn\(|spawnSync\(" || true
56
+ ```
57
+
58
+ If invariant 11 is touched, run:
59
+
60
+ ```bash
61
+ bun --smol test tests/unit/config --timeout 60000
62
+ for f in tests/unit/tools/*.test.ts; do bun --smol test "$f" --timeout 30000; done
63
+ ```
64
+
65
+ If invariant 7 is touched, confirm the writing-tests skill was loaded and that new test seams avoid leaking `mock.module`.
66
+
67
+ ## Step 0 - Session start hygiene
68
+
69
+ Run before publication work:
70
+
71
+ ```bash
72
+ git fetch origin main
73
+ rm -f .swarm/evidence/*.json
74
+ git status --short
75
+ ```
76
+
77
+ On Windows, prefer temporary save branches over `git stash`. If you must stash, use `git stash push --include-untracked` and verify the stash contents.
78
+
79
+ ## Step 1 - Commit and PR titles
80
+
81
+ Use `<type>(<scope>): <description>` exactly.
82
+
83
+ - description is lowercase and does not end with a period
84
+ - allowed types: `feat`, `fix`, `perf`, `revert`, `docs`, `chore`, `refactor`, `test`, `ci`, `build`
85
+
86
+ Choose the PR title type by the main change:
87
+
88
+ - new capability -> `feat`
89
+ - bug fix only -> `fix`
90
+ - docs or chore only -> non-bump types
91
+
92
+ The squash merge commit message must match the PR title exactly.
93
+
94
+ > **Note:** The PR title MUST follow `<type>(<scope>): <description>` exactly — CI runs `action-semantic-pull-request` which will fail the `check-title` job if the format is wrong. Do not deviate from this format.
95
+
96
+ ## Step 2 - Release note fragment
97
+
98
+ Create a pending release fragment and do not calculate a version manually.
99
+
100
+ Required file shape:
101
+
102
+ ```text
103
+ docs/releases/pending/<unique-slug>.md
104
+ ```
105
+
106
+ The fragment should cover:
107
+
108
+ - what changed
109
+ - why
110
+ - migration steps, if any
111
+ - breaking changes, if any
112
+ - known caveats
113
+
114
+ Do not manually edit:
115
+
116
+ - `package.json` version
117
+ - `CHANGELOG.md`
118
+ - `.release-please-manifest.json` — exception: reconciliation when the manifest desyncs from actual releases (see below)
119
+
120
+ ### Release-please manifest desync
121
+
122
+ `.release-please-manifest.json` is the version source of truth for release-please. If it desyncs from the actual published release (e.g., `7.26.0` in manifest but `v7.27.1` on GitHub), release-please will propose a version that goes backwards.
123
+
124
+ **Common cause:** An older release PR (e.g., `chore(main): release 7.26.0`) merges after a newer one (`chore(main): release 7.27.1`). Both PRs modify the manifest, so the later one to merge wins — regardless of which version is higher.
125
+
126
+ **Detection:** If a release-please PR proposes a version that seems too low, check:
127
+ 1. `gh release list --limit 5` — what's the latest published release?
128
+ 2. `git show origin/main:.release-please-manifest.json` — what does the manifest say?
129
+ 3. If different, the manifest is desynced.
130
+
131
+ **Fix:** Open a PR that updates `.release-please-manifest.json` to match the actual latest release (e.g., `"7.27.1"`). Close the incorrect release PR with explanation. After the manifest fix merges, release-please will auto-create a correct release PR.
132
+
133
+ ## Step 3 - Mandatory validation suite
134
+
135
+ Run the full validation stack before pushing. The exact commands may be narrowed only when the repo contract or current task explicitly justifies it in evidence, not by intuition.
136
+
137
+ ### Pre-flight
138
+
139
+ `dist/` is generated output and is **not** committed (#1047). Confirm the build still
140
+ succeeds and the bundle loads — do not stage `dist/`:
141
+
142
+ ```bash
143
+ bun run build
144
+ node --input-type=module -e "await import('./dist/index.js'); console.log('dist import OK')"
145
+ ```
146
+
147
+ ### Tier 1 - quality
148
+
149
+ Run both linter AND formatter — e.g., `bunx biome check --write .` or equivalent — because CI quality gates reject code that passes tests but fails style validation.
150
+
151
+ ```bash
152
+ bun run typecheck
153
+ bunx biome ci .
154
+ ```
155
+
156
+ ### Tier 2 - unit tests
157
+
158
+ ```bash
159
+ for f in tests/unit/tools/*.test.ts; do bun --smol test "$f" --timeout 30000; done
160
+ for f in tests/unit/services/*.test.ts; do bun --smol test "$f" --timeout 30000; done
161
+ for f in tests/unit/agents/*.test.ts; do bun --smol test "$f" --timeout 30000; done
162
+ for f in tests/unit/hooks/*.test.ts; do bun --smol test "$f" --timeout 30000; done
163
+ bun --smol test tests/unit/cli tests/unit/commands tests/unit/config --timeout 120000
164
+ ```
165
+
166
+ If agent prompt text changed, grep for the changed text in tests and rerun every matching file individually.
167
+
168
+ ### Tier 3 - integration
169
+
170
+ ```bash
171
+ bun test tests/integration ./test --timeout 120000
172
+ ```
173
+
174
+ ### Tier 4 - security and adversarial
175
+
176
+ ```bash
177
+ bun test tests/security --timeout 120000
178
+ bun test tests/adversarial --timeout 120000
179
+ ```
180
+
181
+ ### Tier 5 - smoke
182
+
183
+ ```bash
184
+ bun test tests/smoke --timeout 120000
185
+ ```
186
+
187
+ ### Pre-existing failure handling
188
+
189
+ If a failure looks unrelated, prove it on clean `origin/main` before carrying it into the PR body:
190
+
191
+ ```bash
192
+ git worktree add /tmp/repro-check origin/main
193
+ bun --smol test /tmp/repro-check/<path-to-failing-test> --timeout 30000
194
+ git worktree remove /tmp/repro-check
195
+ ```
196
+
197
+ If the failure reproduces on `main`, document it under `## Pre-existing failures`. Do not silently inherit it.
198
+
199
+ ### dist/ is generated, not committed
200
+
201
+ `dist/` is build output and is git-ignored (#1047); do **not** stage or commit it, and
202
+ there is no `dist-check` drift gate. The authoritative artifact check is `package-check`,
203
+ which runs `npm pack` and verifies the packed tarball is complete (type declarations,
204
+ grammar assets), installs it in a temp project, imports it under Node, and runs the CLI.
205
+
206
+ A `package-check` failure is a source / build / `package.json#files` problem — fix the
207
+ source or manifest and rebuild; never "commit dist to make CI green." CI builds `dist/`
208
+ itself (the `unit`, `package-check`, and `smoke` jobs run `bun run build`), and
209
+ release/publish builds from source.
210
+
211
+ ## Step 4 - Workflow changes
212
+
213
+ If any `.github/workflows/*.yml` file changed, every third-party `uses:` must be pinned to a full 40-character SHA.
214
+
215
+ ## Step 5 - History shape
216
+
217
+ Before opening a PR, verify no local-only files are staged:
218
+
219
+ ```bash
220
+ git diff --name-only HEAD origin/main | grep -E '\.(local\.json|vscode|idea)' || true
221
+ ```
222
+
223
+ Prefer a single clean commit for the branch before initial PR publication:
224
+
225
+ ```bash
226
+ git fetch origin main
227
+ git log --oneline origin/main..HEAD
228
+ git reset --soft origin/main
229
+ git commit -m "type(scope): description"
230
+ git push --force-with-lease -u origin <branch-name>
231
+ ```
232
+
233
+ If a review cycle is already active and inline comments depend on current SHAs, avoid resquashing until threads are resolved.
234
+
235
+ If pushing to a PR branch owned by another agent or bot, push to the PR's actual head branch:
236
+
237
+ ```powershell
238
+ $prBranch = gh pr view <number> --json headRefName --jq '.headRefName'
239
+ git fetch origin $prBranch
240
+ git push origin "<your-local-branch>:$prBranch" --force-with-lease
241
+ ```
242
+
243
+ ### Pre-push: Push Protection and Canonical Remote
244
+
245
+ Before `git push`, run both checks:
246
+
247
+ #### Push protection scan
248
+
249
+ GitHub push protection blocks commits containing literal secret patterns. This bit the
250
+ first commit of PR #1472 — a test file with a literal `sk_live_*` Stripe fixture
251
+ pattern was pushed before the string-concatenation workaround was applied.
252
+
253
+ **The primary check (pre-push, after commit exists):**
254
+
255
+ ```bash
256
+ git log origin/main..HEAD -p | grep -E "$(printf '%s' "${PREFIX:-sk_live}|ghp_|xox[abprs]-|AKIA|eyJ|AIza")" || true
257
+ ```
258
+
259
+ **The optional pre-commit add-on (staged changes only):**
260
+
261
+ ```bash
262
+ git diff --cached | grep -E "$(printf '%s' "${PREFIX:-sk_live}|ghp_|xox[abprs]-|AKIA|eyJ|AIza")" || true
263
+ ```
264
+
265
+ Forbidden patterns: Stripe (`sk_live_*`), GitHub (`ghp_*`), Slack (`xox[abprs]-*`),
266
+ AWS (`AKIA*`), JWT (`eyJ*`), Google API (`AIza*`).
267
+
268
+ **The fix:** Construct test fixtures via string concatenation rather than literal
269
+ patterns. For example:
270
+
271
+ ```typescript
272
+ // Wrong — triggers push protection:
273
+ const stripeKey = 'sk_live_' + '1234567890abcdefghijklmn'
274
+
275
+ // Right — split the literal so it never appears verbatim in source:
276
+ const stripeKey = 'sk_' + 'live_' + '1234567890abcdefghijklmn'
277
+ ```
278
+
279
+ > **Note:** This scan is a best-effort heuristic. It will not catch deliberately obfuscated patterns (e.g., base64 or hex encoding, runtime string assembly). For genuinely sensitive keys, use environment variables or a secret store — never commit credentials to source.
280
+
281
+ #### Canonical remote resolution
282
+
283
+ When a repo has multiple remotes (e.g. `zaxbysauce/opencode-swarm` and
284
+ `ZaxbyHub/opencode-swarm`), pushing to the wrong remote causes `gh pr create` to
285
+ fail with "No commits between <canonical>:main and <mirror>:<branch>". This happened
286
+ on PR #1472.
287
+
288
+ **The check:** `git remote -v` before push. Identify the canonical-org remote.
289
+
290
+ **The rule:** Push to the canonical-org remote explicitly:
291
+
292
+ ```bash
293
+ git push -u <canonical-remote> <branch>
294
+ ```
295
+
296
+ Create the PR against the canonical repo:
297
+
298
+ ```bash
299
+ gh pr create --repo <canonical-org>/<repo>
300
+ ```
301
+
302
+ **Heuristic for identifying the canonical remote:** the canonical remote is the one whose URL points to the owning organization (e.g. `github.com/<org>/<repo>.git`), not a personal fork or mirror. When the owning org differs from the local fork's owner, the org-owned remote is canonical. Example: `github.com/ZaxbyHub/opencode-swarm.git` is canonical; `github.com/zaxbysauce/opencode-swarm.git` is a personal fork.
303
+
304
+ ## Step 6 - PR creation
305
+
306
+ PR body requirements:
307
+
308
+ - `Closes #<issue-number>` as the first line when the PR resolves an issue
309
+ - `## Summary`
310
+ - `## Invariant audit`
311
+ - `## Test plan`
312
+
313
+ ### Publication-gate evidence
314
+
315
+ A repository publication gate (`.github/hooks/pr-publication-gate.json` ->
316
+ `scripts/copilot-pr-publication-gate.sh`) may block `gh pr create`, `gh pr edit`,
317
+ and `gh pr ready` until publication evidence exists. Before publishing, write:
318
+
319
+ - `.swarm/evidence/pr_body.md` — the exact PR body you will publish (must contain
320
+ `## Summary`, `## Invariant audit`, and `## Test plan`).
321
+ - `.swarm/evidence/commit-pr-validation.md` — the validation commands you ran and
322
+ their results.
323
+
324
+ These files live under `.swarm/` (runtime state, never committed) and double as the
325
+ evidence the gate checks. Keep them current if you edit the PR body or rerun
326
+ validation. The CI `pr-standards` check enforces the same body contract server-side.
327
+
328
+ PowerShell-safe pattern:
329
+
330
+ ```powershell
331
+ $body = @"
332
+ Closes #<issue-number>
333
+
334
+ ## Summary
335
+ - <bullet 1>
336
+ - <bullet 2>
337
+
338
+ ## Invariant audit
339
+ - 1 (plugin init): not touched - <evidence>
340
+
341
+ ## Test plan
342
+ - [ ] <validation item>
343
+ "@
344
+ $utf8NoBom = New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false
345
+ $prBodyPath = Join-Path ([System.IO.Path]::GetTempPath()) "pr_body.txt"
346
+ [System.IO.File]::WriteAllText($prBodyPath, $body, $utf8NoBom)
347
+ gh pr create --title "<type>(<scope>): <description>" --body-file $prBodyPath --base main
348
+ ```
349
+
350
+ ## Step 6a - PR auto-subscribe reminder
351
+
352
+ After PR creation, if the project uses PR monitoring (`pr_monitor.enabled: true`
353
+ in resolved opencode-swarm config), the publisher should subscribe to the new PR
354
+ for background monitoring via `/swarm pr subscribe <pr-url>`.
355
+
356
+ This step is advisory — it reminds the publisher to subscribe but does not
357
+ auto-subscribe. The actual subscription requires the `/swarm pr subscribe` command
358
+ which triggers the subscription store and lazy-starts the polling worker.
359
+
360
+ ## Step 6.5 - Issue comment
361
+
362
+ If the PR closes an issue, post a comment on the issue. This is mandatory.
363
+
364
+ The issue comment must include:
365
+
366
+ 1. the PR link
367
+ 2. what changed
368
+ 3. how to use it
369
+ 4. migration steps or "No migration required"
370
+
371
+ PowerShell-safe pattern:
372
+
373
+ ````powershell
374
+ $comment = @"
375
+ Fixed in PR #<pr-number>.
376
+
377
+ ## What changed
378
+ - <bullet 1>
379
+ - <bullet 2>
380
+
381
+ ## How to use
382
+ ```json
383
+ { "config": "example" }
384
+ ```
385
+
386
+ ## Migration
387
+ No migration required.
388
+ "@
389
+ $utf8NoBom = New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false
390
+ $issueCommentPath = Join-Path ([System.IO.Path]::GetTempPath()) "issue-comment.txt"
391
+ [System.IO.File]::WriteAllText($issueCommentPath, $comment, $utf8NoBom)
392
+ gh issue comment <issue-number> --body-file $issueCommentPath
393
+ ````
394
+
395
+ If the PR merged before this was done, post the missing issue comment immediately.
396
+
397
+ ## Step 7 - Existing PR follow-up and closeout
398
+
399
+ If a PR already exists for the branch:
400
+
401
+ 1. do not open a second PR
402
+ 2. inspect unresolved PR feedback surfaces before updating or readying the PR: review threads/comments, requested-changes reviews, CI/check failures, mergeability/conflicts, and whether check data belongs to the current head SHA
403
+ 3. use `../swarm-pr-feedback/SKILL.md` when feedback needs fixes before closeout
404
+ 4. update the existing PR body when summary, invariant evidence, test counts, caveats, or pre-existing failure notes changed
405
+ 5. keep the PR draft while follow-up edits are still expected or required checks are still pending
406
+ 6. mark the PR ready only after the body is current and required remote checks are green, unless the user explicitly wants it ready earlier
407
+ 7. after any follow-up push or force-push, verify the PR head matches the expected commit and that reported checks belong to the current `headRefOid`:
408
+
409
+ ```powershell
410
+ gh pr view <number> --json headRefOid,body,isDraft,state,mergeable,mergeStateStatus,statusCheckRollup,url
411
+ ```
412
+
413
+ Useful commands:
414
+
415
+ ```powershell
416
+ gh pr edit <number> --body-file "$env:TEMP\pr_body.txt"
417
+ gh pr ready <number>
418
+ gh pr checks <number> --watch --fail-fast
419
+ ```
420
+
421
+ ### Conflict closeout
422
+
423
+ After resolving merge conflicts or syncing a stale branch:
424
+
425
+ 1. verify there are no local unmerged paths or conflict markers,
426
+ 2. push the conflict-resolution commit,
427
+ 3. verify GitHub reports both `mergeable: MERGEABLE` and
428
+ `mergeStateStatus: CLEAN`, not merely that local markers are gone, and
429
+ 4. keep a conflict/branch-drift item in the PR closure ledger when it affected
430
+ the PR.
431
+
432
+ If GitHub still reports `DIRTY`, `BLOCKED`, or stale checks after local conflict
433
+ resolution, fetch current `origin/main` again and re-evaluate before claiming the
434
+ conflict is resolved.
435
+
436
+ ### GitHub auto-merge race condition
437
+
438
+ With a merge queue enabled, prefer queuing over manual freshness rebases, which
439
+ avoids this race entirely. It can still occur if you rebase manually: when `main`
440
+ advances while your PR is open, GitHub's PR sync machinery may **automatically push a
441
+ merge commit to your branch** in the window between when you fetch and when you push.
442
+ This is distinct from a conflict — it is GitHub creating a merge commit on your behalf
443
+ without rebuilding generated outputs (lockfiles, etc.).
444
+
445
+ Symptoms:
446
+ - `git push` is rejected with "fetch first" even though you just fetched
447
+ - `git log HEAD..origin/<branch>` shows a commit authored by GitHub/the repo owner with message `Merge branch 'main' into <branch>`
448
+ - generated outputs (e.g. lockfiles) on that auto-merge commit are stale because it was not rebuilt
449
+
450
+ Recovery:
451
+ ```bash
452
+ git fetch origin <branch>
453
+ git log HEAD..origin/<branch> # confirm it's only the GitHub auto-merge
454
+ # Your local commit is correct. Force-push it:
455
+ git push origin <branch> --force-with-lease
456
+ ```
457
+
458
+ After force-pushing, verify the PR head SHA updated and cancel any CI run
459
+ targeting the superseded auto-merge SHA to unblock concurrency:
460
+
461
+ ```powershell
462
+ gh run list --branch <branch> --limit 5 --json databaseId,headSha,status,workflowName
463
+ gh run cancel <stale-run-id>
464
+ ```
465
+
466
+ ### Check closeout
467
+
468
+ `gh pr checks --watch --fail-fast` is useful but can lag or flatten matrix and
469
+ downstream jobs. When the PR checks view looks stale, missing, or inconsistent,
470
+ use the workflow run as the authoritative detail:
471
+
472
+ > **MCP environments:** When using GitHub MCP tools instead of `gh`, prefer
473
+ > `get_check_runs` over `get_status`. The `get_status` method uses GitHub's
474
+ > legacy commit status API: it returns `state: "pending"` even when all GitHub
475
+ > Actions jobs are green, because Actions creates check-runs (not legacy
476
+ > statuses). `get_check_runs` returns the actual job results.
477
+
478
+ ```powershell
479
+ gh run view <run-id> --json headSha,status,conclusion,jobs,url
480
+ ```
481
+
482
+ Keep watching after unit jobs pass; this repository may enqueue integration and
483
+ smoke jobs later in the same CI run. Do not call the PR green until the current
484
+ `headRefOid` has all required jobs completed successfully.
485
+
486
+ If a previous run from an older PR head is still in progress or already failed
487
+ and is blocking the current head's workflow through concurrency, inspect it with
488
+ `gh run view <run-id> --json headSha,status,conclusion,jobs,url`. Cancel only
489
+ obsolete older-head runs that are no longer relevant to the PR head you are
490
+ validating, then wait for the current-head checks to complete.
491
+
492
+ If you edit the PR body after checks are green, expect PR Standards / title
493
+ checks to rerun. Re-check before claiming final green or merge-readiness.
494
+
495
+ ### Merge queue (current-base validation)
496
+
497
+ When `main` has a GitHub **merge queue** enabled, do not rebase or force-push a PR
498
+ *solely because `main` advanced*. Once required checks and review are green, add the
499
+ PR to the merge queue; GitHub re-runs the required workflows against the queued
500
+ change on top of the latest `main` (and any earlier queued PRs) before merging, so
501
+ manual "freshness" rebases are unnecessary.
502
+
503
+ Still rebase/force-push when there is a **real** reason: a genuine merge conflict,
504
+ a stale review thread that depends on current SHAs, or a correctness issue that only
505
+ appears against current `main`. The queue handles up-to-date validation; it does not
506
+ resolve conflicts for you.
507
+
508
+ Required workflows trigger on both `pull_request` and `merge_group`. PR-only checks
509
+ (title/body validation) no-op to success on `merge_group` because the PR already
510
+ satisfied them before being queued.
511
+
512
+ ## Step 8 - Cancelled jobs and skipped dependents
513
+
514
+ If a required GitHub Actions job is `cancelled` and downstream jobs are `skipped`:
515
+
516
+ 1. inspect the run:
517
+
518
+ ```powershell
519
+ gh run view <run-id> --json status,conclusion,jobs,url
520
+ ```
521
+
522
+ 2. if the cancellation looks like orchestration or infrastructure rather than a code failure, rerun the failed or cancelled jobs:
523
+
524
+ ```powershell
525
+ gh run rerun <run-id> --failed
526
+ ```
527
+
528
+ 3. re-check the PR until required jobs are green:
529
+
530
+ ```powershell
531
+ gh pr checks <number> --watch --fail-fast
532
+ ```
533
+
534
+ Do not call the PR green or merge-ready while a required job is `cancelled`, `skipped`, `in_progress`, or otherwise non-green unless the user explicitly accepts that state.
535
+
536
+ ## Step 9 - Pre-merge checklist
537
+
538
+ - [ ] invariant audit is complete and current
539
+ - [ ] required build and validation commands ran for touched invariants
540
+ - [ ] `test_runner` was not used with broad repo-validation scopes
541
+ - [ ] release fragment exists and version files are untouched
542
+ - [ ] `dist/` was NOT staged (it is generated output, not committed — #1047)
543
+ - [ ] PR body has `Closes`, `## Summary`, `## Invariant audit`, and `## Test plan`
544
+ - [ ] if this was review follow-up, the PR body was refreshed to match current evidence
545
+ - [ ] if the PR resolves an issue, the issue comment was posted with PR link, what changed, how to use it, and migration notes
546
+ - [ ] if any required job was cancelled and dependent jobs skipped, the run was rerun or the non-green state was explicitly accepted by the user
547
+ - [ ] for high-risk work (security, isolation, IPC, auth, payments, migrations), an independent adversarial review subagent ran before the final substantive push and all confirmed findings were addressed — if this was not done before pushing, run the review now and force-push a corrected commit before marking the PR ready
548
+ - [ ] all required CI checks are green before calling the PR merge-ready
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: engineering-conventions
3
+ description: >
4
+ Guidelines and non-negotiable engineering invariants for modifying opencode-swarm.
5
+ Load before architecture, plugin initialization, subprocess, tool registration, plan
6
+ durability, .swarm storage, runtime portability, session/global state, guardrails/retry,
7
+ chat/system message hooks, or release/cache changes. Authoritative source: AGENTS.md
8
+ at the repo root and docs/engineering-invariants.md.
9
+ ---
10
+
11
+ # Engineering Conventions for opencode-swarm
12
+
13
+ **Authoritative source:** [`AGENTS.md`](../../../AGENTS.md) at the repo root and [`docs/engineering-invariants.md`](../../../docs/engineering-invariants.md). This skill is a pointer + summary so the OpenCode agent loads the right invariants before touching dangerous areas. **Read `AGENTS.md` first.** When this skill conflicts with `AGENTS.md`, `AGENTS.md` wins.
14
+
15
+ ## When to load this skill
16
+
17
+ Load this skill **before** beginning implementation work that touches any of:
18
+
19
+ - `src/index.ts` (plugin entry / `initializeOpenCodeSwarm`)
20
+ - `src/hooks/*` (any hook that may run during init or QA review)
21
+ - `src/tools/*` (tool registration, working-directory anchoring, test_runner)
22
+ - `src/utils/bun-compat.ts` (subprocess shim — every spawn in the repo eventually flows through here)
23
+ - `src/utils/timeout.ts` (the `withTimeout` primitive used by every bounded init step)
24
+ - `src/utils/gitignore-warning.ts` (Git hygiene; runs on plugin init path)
25
+ - `package.json`, build configuration, `dist/`, plugin export shape
26
+ - Plan ledger / projection / checkpoint code (`src/plan/*`, `.swarm/plan-*`)
27
+ - Session / guardrails / runtime state (`src/state.ts`, `src/hooks/guardrails.ts`)
28
+ - Tests involving subprocesses, plugin startup, `mock.module`, or temp directories
29
+
30
+ If you are not sure whether you are touching one of these, you are touching one of these.
31
+
32
+ ## Highest-risk invariants (the ones that have already shipped regressions)
33
+
34
+ The full list of 12 invariants is in `AGENTS.md`. The four that have caused the most recent production regressions:
35
+
36
+ 1. **Plugin initialization is bounded and fail-open.** Every awaited operation on the plugin-init path must be wrapped in `withTimeout(...)` and degrade non-fatally on timeout. Issue #704 (v7.0.3) and the v7.3.3 git-hygiene regression both stem from violating this. The OpenCode plugin host silently drops a plugin whose entry never resolves; users see "no agents in TUI / GUI" with no error.
37
+ 2. **Subprocesses are bounded, non-interactive, and killable.** Every `bunSpawn(['<bin>', ...])` call must pass `cwd`, `stdin: 'ignore'` (unless intentionally interactive), `timeout: <ms>`, bounded stdio, and call `proc.kill()` in a `finally`. An outer `withTimeout` is not enough — it lets the awaiter proceed but does not abort the child.
38
+ 3. **Runtime portability — Node-ESM-loadable + v1 plugin shape.** No top-level `bun:` imports in `dist/index.js`. Default export is `{ id, server }`. All `Bun.*` calls go through `src/utils/bun-compat.ts`. v6.86.8 / v6.86.9 are the cautionary tales.
39
+ 4. **Test mock isolation.** `mock.module(...)` leaks across files in Bun's shared test-runner process. Prefer, in order: (a) `_test_exports` for pure function testing with zero mocks, (b) `_internals` dependency-injection seam for within-module mocking (see `src/utils/gitignore-warning.ts:_internals` and `src/hooks/diff-scope.ts:_internals`), (c) `mock.module` only when unavoidable. Restore in `afterEach`. The writing-tests skill covers all three tiers in detail; load it before modifying tests.
40
+
41
+ ## Cross-link: writing tests
42
+
43
+ For test changes, also load [`.opencode/skills/writing-tests/SKILL.md`](../writing-tests/SKILL.md). It covers `bun:test` API, mock isolation rules, CI per-file isolation, and cross-platform anti-patterns.
44
+
45
+ ## Hard warning: do NOT use broad `test_runner` for repo validation
46
+
47
+ The OpenCode `test_runner` tool is for **targeted agent validation** with explicit `files: [...]` or small targeted scopes. It is not the way to validate the full repo from inside an OpenCode session. In this repo:
48
+
49
+ - `MAX_SAFE_TEST_FILES = 50` (`src/tools/test-runner.ts`). Resolutions exceeding this return `outcome: 'scope_exceeded'` with a SKIP. Do not lean on this — broad scopes can stall or kill OpenCode before that guard fires.
50
+ - For repo validation, run the shell commands in `contributing.md` / `TESTING.md` directly (per-file isolation loops + tier orchestration).
51
+ - `scope: 'all'` requires `allow_full_suite: true` and is intended for opt-in CI mirrors only. Default to `files: [...]` instead.
52
+
53
+ ## The invariant-audit gate (PR-time)
54
+
55
+ Every PR that touches a relevant area must include an `## Invariant audit` section in its description. The format is in `AGENTS.md` ("Invariant audit required in PRs"). The `commit-pr` skill enforces this gate before push/PR — load it before committing.
56
+
57
+ If you cannot prove a touched invariant from source and test output, **do not push**.
@@ -132,7 +132,7 @@ The tool will automatically write the retrospective to \`.swarm/evidence/retro-{
132
132
  |---|---|---|
133
133
  | `coder` | Always | Task implementation (coder) |
134
134
  | `reviewer` | Always | Task review (reviewer) |
135
- | `test_engineer` | Always | Test verification (test_engineer) |
135
+ | `test_engineer` | When phase modifies source code/tests (unless explicitly waived) | Test verification (test_engineer) |
136
136
  | `docs` | When `require_docs: true` in QA gate profile | Documentation updates |
137
137
 
138
138
  If any required agent is missing, `phase_complete` returns `{ success: false, status: 'incomplete', message: 'Phase N incomplete: missing required agents: <list>', agentsMissing: [...] }` and the phase is not closed. Dispatch each agent during normal task execution (not only inside optional Phase/Final Councils in steps 5.65/5.7) so the closeout gate is satisfied.