opencode-swarm 7.112.2 → 7.113.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 (66) hide show
  1. package/.opencode/skills/brainstorm/SKILL.md +1 -0
  2. package/.opencode/skills/ci-failure-batching/SKILL.md +2 -1
  3. package/.opencode/skills/ci-fix-monitor/SKILL.md +203 -0
  4. package/.opencode/skills/clarify/SKILL.md +1 -0
  5. package/.opencode/skills/clarify-spec/SKILL.md +1 -0
  6. package/.opencode/skills/codebase-review-swarm/SKILL.md +1 -0
  7. package/.opencode/skills/commit-pr/SKILL.md +15 -0
  8. package/.opencode/skills/consult/SKILL.md +1 -0
  9. package/.opencode/skills/council/SKILL.md +1 -0
  10. package/.opencode/skills/critic-gate/SKILL.md +1 -0
  11. package/.opencode/skills/deep-dive/SKILL.md +1 -0
  12. package/.opencode/skills/deep-research/SKILL.md +1 -0
  13. package/.opencode/skills/design-docs/SKILL.md +2 -1
  14. package/.opencode/skills/discover/SKILL.md +1 -0
  15. package/.opencode/skills/engineering-conventions/SKILL.md +2 -1
  16. package/.opencode/skills/execute/SKILL.md +1 -0
  17. package/.opencode/skills/fork-pr-operations/SKILL.md +136 -0
  18. package/.opencode/skills/gate-attribution/SKILL.md +2 -1
  19. package/.opencode/skills/issue-ingest/SKILL.md +1 -0
  20. package/.opencode/skills/loop/SKILL.md +7 -6
  21. package/.opencode/skills/merge-queue-readiness/SKILL.md +2 -1
  22. package/.opencode/skills/parallel-work-check/SKILL.md +133 -0
  23. package/.opencode/skills/phase-wrap/SKILL.md +1 -0
  24. package/.opencode/skills/plan/SKILL.md +1 -0
  25. package/.opencode/skills/pre-phase-briefing/SKILL.md +1 -0
  26. package/.opencode/skills/resume/SKILL.md +1 -0
  27. package/.opencode/skills/running-tests/SKILL.md +1 -0
  28. package/.opencode/skills/skill-edit-validation/SKILL.md +2 -1
  29. package/.opencode/skills/specify/SKILL.md +1 -0
  30. package/.opencode/skills/swarm/SKILL.md +1 -0
  31. package/.opencode/skills/swarm-ci-monitor/SKILL.md +5 -4
  32. package/.opencode/skills/swarm-implement/SKILL.md +4 -4
  33. package/.opencode/skills/swarm-pr-feedback/SKILL.md +26 -5
  34. package/.opencode/skills/swarm-pr-review/SKILL.md +2 -1
  35. package/.opencode/skills/swarm-pr-subscribe/SKILL.md +1 -0
  36. package/.opencode/skills/test-file-split/SKILL.md +102 -0
  37. package/.opencode/skills/worktree-retry-cleanup/SKILL.md +2 -1
  38. package/.opencode/skills/writing-tests/SKILL.md +43 -0
  39. package/dist/cli/{config-doctor-mmw4c50z.js → config-doctor-az11cwf5.js} +2 -2
  40. package/dist/cli/{curator-llm-factory-5bns4xy6.js → curator-llm-factory-nmhzc774.js} +5 -5
  41. package/dist/cli/{curator-snvp9542.js → curator-nm2c9y1p.js} +5 -5
  42. package/dist/cli/{guardrail-explain-4s7djf9p.js → guardrail-explain-q1np1xaw.js} +6 -6
  43. package/dist/cli/{guardrail-log-xjgr00eq.js → guardrail-log-hysxdwg9.js} +3 -3
  44. package/dist/cli/{hive-promoter-zs295trk.js → hive-promoter-z4dezw62.js} +5 -5
  45. package/dist/cli/{index-xehrmy76.js → index-0gf6de8b.js} +1 -1
  46. package/dist/cli/{index-cq2hgvd2.js → index-1ey3dxq7.js} +9 -1
  47. package/dist/cli/{index-npxmfj48.js → index-8f256d7t.js} +1 -1
  48. package/dist/cli/{index-82v0d72f.js → index-h389ed8y.js} +424 -87
  49. package/dist/cli/{index-9fjpecax.js → index-ky8eb4q6.js} +2 -2
  50. package/dist/cli/{index-snmfqhez.js → index-npd9rypp.js} +2009 -958
  51. package/dist/cli/{index-w84stmkx.js → index-r9fjzr3k.js} +1 -1
  52. package/dist/cli/{index-k6wd3chp.js → index-rqpm9cwe.js} +7 -7
  53. package/dist/cli/index.js +5 -5
  54. package/dist/cli/{schema-2p15yrvn.js → schema-qb313fyy.js} +1 -1
  55. package/dist/cli/{skill-generator-fe6q8xgm.js → skill-generator-nnwn4sq0.js} +1 -1
  56. package/dist/commands/loop.d.ts +1 -1
  57. package/dist/commands/registry.d.ts +1 -1
  58. package/dist/config/bundled-skills.d.ts +15 -5
  59. package/dist/config/project-init.d.ts +1 -1
  60. package/dist/config/schema.d.ts +2 -0
  61. package/dist/hooks/skill-propagation-gate.d.ts +45 -5
  62. package/dist/hooks/skill-scoring.d.ts +22 -1
  63. package/dist/index.js +88 -88
  64. package/dist/sast/rules/index.d.ts +0 -9
  65. package/dist/utils/gitignore-warning.d.ts +8 -4
  66. package/package.json +5 -1
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: parallel-work-check
3
+ audience: swarm-plugin
4
+ description: >
5
+ Apply before starting work on an existing branch. Checks for parallel work by
6
+ other agents or developers that may supersede or conflict with your planned
7
+ changes. Prevents wasted effort on stale branches.
8
+ effort: small
9
+ generated_from_knowledge: []
10
+ source_knowledge_ids: ['f07c1f4d-9bb0-4219-9804-26aa8efe8146']
11
+ generated_at: 2026-06-14T16:50:00Z
12
+ confidence: 0.8
13
+ status: active
14
+ version: 4
15
+ skill_origin: generated
16
+ provenance_note: >
17
+ Re-linked to current knowledge entries (version 4). The original source ID
18
+ b8fee776... is no longer present in the active knowledge store. The skill
19
+ body and behavior are unchanged; only source_knowledge_ids metadata was
20
+ updated to point to the current lesson about verifying pre-existing state
21
+ on parent commit, which is directly relevant to the parallel-work-check
22
+ protocol.
23
+ ---
24
+
25
+ # Parallel Work Check Protocol
26
+
27
+ Run this check before starting ANY work on an existing branch (not a fresh branch
28
+ you just created). This applies to PR branches, feature branches, and any branch
29
+ that may have concurrent contributors.
30
+
31
+ ## Step 1 — Check current branch state
32
+
33
+ 1. Determine the current branch name.
34
+ 2. Determine the remote tracking branch (usually `origin/<branch-name>`).
35
+
36
+ ## Step 2 — Fetch remote state
37
+
38
+ Fetch the latest state from the remote for the current branch. Do NOT skip this
39
+ step because "the branch looks recent" or "I just checked."
40
+
41
+ ## Step 3 — Compare local vs remote
42
+
43
+ Compare the local HEAD commit hash with the remote HEAD commit hash:
44
+
45
+ - **Identical**: Remote has not diverged. Proceed with your work.
46
+ - **Remote ahead**: The remote branch has commits you don't have locally.
47
+ - Read the new commit messages with `git log local..remote`.
48
+ - Check if any of those commits touch files you plan to modify.
49
+ - If yes: evaluate whether the parallel work supersedes your planned changes.
50
+ - If the parallel work is superior: reset your local branch to match remote
51
+ and abandon your planned approach. Document the decision.
52
+ - If the parallel work is complementary: integrate it first, then proceed.
53
+ - **Local ahead**: You have local commits not on remote. This is normal if you
54
+ already started work. Proceed, but be aware that pushing may conflict with
55
+ subsequent remote changes.
56
+ - **Diverged**: Both local and remote have unique commits. This requires
57
+ integration. Merge or rebase as appropriate for the team's workflow.
58
+
59
+ ## Step 4 — Check for parallel swarm/agent work
60
+
61
+ If the remote has new commits:
62
+
63
+ 1. Check the commit authors. If commits are from a different swarm/agent
64
+ (different author name/email pattern), treat this as parallel swarm work.
65
+ 2. Parallel swarm work is often superior because:
66
+ - It may have access to different context or tools
67
+ - It may have started earlier or had more iterations
68
+ - It may have taken a fundamentally better approach
69
+ 3. Default stance: **prefer the parallel swarm's work** unless you can clearly
70
+ articulate why your approach is better.
71
+
72
+ ## Step 5 — Decision and documentation
73
+
74
+ Before proceeding, document your decision:
75
+
76
+ ```
77
+ PARALLEL WORK CHECK:
78
+ - Branch: <name>
79
+ - Local HEAD: <hash> <message>
80
+ - Remote HEAD: <hash> <message>
81
+ - Diverged: yes/no
82
+ - New commits on remote: <count>
83
+ - Parallel swarm work detected: yes/no
84
+ - Decision: [proceed / integrate-then-proceed / abandon-use-remote / needs-review]
85
+ - Rationale: <one sentence>
86
+ ```
87
+
88
+ ## Anti-patterns — do NOT do these
89
+
90
+ - Skip the fetch because "I'm sure nothing changed."
91
+ - Ignore remote commits because "my approach is probably better."
92
+ - Start fixing code without checking if the remote already fixed it.
93
+ - Blindly overwrite remote work with local changes without evaluating first.
94
+
95
+ ## Example: parallel swarm superseded local work
96
+
97
+ ```
98
+ PARALLEL WORK CHECK:
99
+ - Branch: codex/issue-956-plan-completion-gate
100
+ - Local HEAD: 5aa34f88 fix(delegation-gate): block next task until completion is persisted
101
+ - Remote HEAD: 2b4e9266 fix: correct contradictory test title/comments
102
+ - Diverged: yes (remote is 8 commits ahead)
103
+ - New commits on remote: 8
104
+ - Parallel swarm work detected: yes (different commit author)
105
+ - Decision: abandon-use-remote
106
+ - Rationale: Parallel swarm restored file from main and re-integrated cleanly,
107
+ producing 486 passing tests vs our incremental patching which left 38 failures.
108
+ ```
109
+
110
+ ## Integration with swarm workflow
111
+
112
+ This check should run:
113
+ - At session start (MODE: RESUME or MODE: EXECUTE)
114
+ - Before creating a new plan for an existing branch
115
+ - Before dispatching the first coder task
116
+ - After any significant pause where parallel work could have occurred
117
+
118
+ ## Integration with other skills
119
+
120
+ The parallel-work-check skill is referenced by other skills that start work on an existing branch:
121
+
122
+ | Skill | Usage |
123
+ |-------|-------|
124
+ | `file:.swarm/bundled-skills/swarm-pr-feedback/SKILL.md` | Checks before starting PR feedback fixes — ensures no parallel work has already addressed the same findings |
125
+ | Legacy `pr-review-fix` alias | Compatibility entry that delegates to the bundled `swarm-pr-feedback` protocol |
126
+ | `file:.swarm/bundled-skills/swarm-implement/SKILL.md` | Checks before implementation Phase 1 — ensures the branch is up-to-date before planning |
127
+ | Any skill that starts work on an existing branch | Run the parallel-work-check protocol before beginning fixes or implementation |
128
+
129
+ When a skill references parallel-work-check, the checking agent must:
130
+ 1. Fetch and compare remote vs local state
131
+ 2. Read any new commits from parallel work
132
+ 3. Evaluate whether the parallel work supersedes, complements, or does not affect the planned work
133
+ 4. Document the decision using the PARALLEL WORK CHECK template
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: phase-wrap
3
+ audience: swarm-plugin
3
4
  description: >
4
5
  Full execution protocol for MODE: PHASE-WRAP -- phase boundary evidence, drift and hallucination gates, retrospectives, phase completion, and final council.
5
6
  ---
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: plan
3
+ audience: swarm-plugin
3
4
  description: >
4
5
  Full execution protocol for MODE: PLAN -- plan creation, external plan ingestion, QA gate persistence, task granularity, and traceability checks.
5
6
  ---
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: pre-phase-briefing
3
+ audience: swarm-plugin
3
4
  description: >
4
5
  Full execution protocol for MODE: PRE-PHASE BRIEFING -- phase-start context assembly, evidence review, and task readiness checks.
5
6
  ---
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: resume
3
+ audience: swarm-plugin
3
4
  description: >
4
5
  Full execution protocol for MODE: RESUME -- continuing an existing approved plan safely from current state.
5
6
  ---
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: running-tests
3
+ audience: swarm-plugin
3
4
  description: >
4
5
  Safe test execution patterns for opencode-swarm. Covers when to use the test_runner
5
6
  tool vs shell bun commands, scope safety rules, per-file isolation loops (bash and
@@ -1,5 +1,6 @@
1
- ---
1
+ ---
2
2
  name: skill-edit-validation
3
+ audience: swarm-plugin
3
4
  description: Content-assertion sweep after editing SKILL.md files. Triggered when a task changes skill or prompt content that tests assert against. Prevents stale-assertion CI failures.
4
5
  ---
5
6
 
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: specify
3
+ audience: swarm-plugin
3
4
  description: >
4
5
  Full execution protocol for MODE: SPECIFY -- spec creation, codebase reality checks, SME input, QA gate persistence, and optional council spec review.
5
6
  ---
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: swarm
3
+ audience: swarm-plugin
3
4
  description: Cross-agent swarm-mode behavior model — a higher-rigor workflow using parallel investigation, independent reviewer validation, and critic challenge, plus the mandatory implementation closeout gate. Runtime adapters (.claude, .agents) add execution-specific notes and command wiring.
4
5
  ---
5
6
 
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: swarm-ci-monitor
3
+ audience: swarm-plugin
3
4
  description: >
4
5
  End-to-end CI monitor that takes an already-human-reviewed PR, exhaustively
5
6
  researches every CI failure, fixes it end-to-end, iterates until all required
@@ -33,7 +34,7 @@ invoking user is the source of truth: only invoke after review is done.
33
34
 
34
35
  Load these skills before doing anything destructive (push / merge):
35
36
 
36
- - `../../../.opencode/skills/generated/ci-fix-monitor/SKILL.md` — for failure
37
+ - `file:.swarm/bundled-skills/ci-fix-monitor/SKILL.md` — for failure
37
38
  classification and the per-type fix recipes (package-check, rebase,
38
39
  format/lint, macOS file I/O, integration, security, smoke). Do not re-derive
39
40
  these recipes here; ci-fix-monitor owns them.
@@ -94,9 +95,9 @@ Only after all three gates pass, enter the loop.
94
95
  Maintain an iteration counter starting at 5 (decremented at the end of each
95
96
  fix-push cycle, in 2g — this is a hard safety gate, not a soft target). At 0,
96
97
  stop (Step 5). This loop can span multiple CI runs and several minutes per
97
- iteration; if the session may compact mid-loop, persist the counter per
98
- `../../../.claude/skills/durable-session-state/SKILL.md` so the 5-cap
99
- survives a resume.
98
+ iteration; if the session may compact mid-loop, record the remaining count in
99
+ the active durable task/plan checkpoint and reload it on resume. Never reset
100
+ the counter to 5 after compaction.
100
101
 
101
102
  ### 2a. Fetch check runs for the PR head SHA
102
103
 
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: swarm-implement
3
+ audience: swarm-plugin
3
4
  description: Execute complex implementation work with a swarm-like workflow: parallel exploration, scoped planning, objective validation, mandatory independent implementation review for changed work, and final critic approval. Use for feature work, bug fixes, refactors, and multi-file changes.
4
5
  disable-model-invocation: true
5
6
  ---
@@ -58,10 +59,9 @@ before coding.
58
59
 
59
60
  ### Phase 0a - Parallel work check
60
61
 
61
- If this project has the OpenCode-generated
62
- [`parallel-work-check` skill](../generated/parallel-work-check/SKILL.md), follow
63
- its full protocol. Otherwise, before starting implementation on an existing
64
- branch:
62
+ Load and follow
63
+ `file:.swarm/bundled-skills/parallel-work-check/SKILL.md`. Then, before
64
+ starting implementation on an existing branch:
65
65
 
66
66
  1. Fetch remote state and compare with local (`git fetch` plus HEAD hashes).
67
67
  2. If parallel swarm work is detected on the target branch, read the new commits,
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: swarm-pr-feedback
3
+ audience: swarm-plugin
3
4
  description: >
4
5
  Ingest and resolve known pull request feedback with skeptical source verification.
5
6
  Use when addressing pasted PR feedback, GitHub review comments or threads,
@@ -183,8 +184,8 @@ tree:
183
184
 
184
185
  - If `head_ref` is a remote branch that is not checked out locally, fetch it
185
186
  (`git fetch origin <head_ref>`).
186
- - **Check for parallel work first.** Before checkout, run the
187
- [`parallel-work-check`](../generated/parallel-work-check/SKILL.md) protocol to
187
+ - **Check for parallel work first.** Before checkout, run
188
+ `file:.swarm/bundled-skills/parallel-work-check/SKILL.md` to
188
189
  detect concurrent pushes from other agents (e.g., `hermes-pr-review` bot
189
190
  following up, maintainer pushing fixes, parallel swarm work). If remote has new
190
191
  commits: read `git log local..remote`, evaluate whether the parallel work
@@ -512,9 +513,29 @@ or compatibility policy, mark the item `NEEDS_USER_DECISION` and ask.
512
513
  - When `main` has a merge queue enabled, do not rebase or force-push a PR only
513
514
  because `main` advanced. Once required checks and review are green, queue the PR
514
515
  and let the merge queue perform final current-base validation. Still resolve real
515
- merge conflicts and SHA-dependent review threads before queuing.
516
-
517
- ## Mandatory Gates
516
+ merge conflicts and SHA-dependent review threads before queuing.
517
+
518
+ ### Operational Gotchas
519
+
520
+ - **Plan identity change:** When switching from a review plan to a feedback-closure
521
+ plan, `save_plan` rejects with `PLAN_IDENTITY_MISMATCH`. Pass
522
+ `confirm_identity_change: true` to acknowledge the intentional overwrite.
523
+ - **Stale gate evidence:** After a plan identity change, `check_gate_status` returns
524
+ timestamps from the *prior* plan. Reset task statuses and re-run Stage A gates
525
+ before trusting gate results. Do not accept cached gate verdicts from before the
526
+ identity change.
527
+ - **PowerShell PR comment posting:** Complex markdown bodies containing backticks,
528
+ dollar signs, or nested quotes fail in PowerShell here-strings. Write the body
529
+ to a temp file and use `gh pr comment <number> --body-file <tempfile>` instead
530
+ of inline `--body "..."`.
531
+ - **Same-file batching:** Multiple findings targeting the same file for the same
532
+ review cycle CAN be fixed in one coder task when the fixes are trivially
533
+ independent (e.g., a one-line guard and a typo fix). When findings require
534
+ different fixes on different code paths, use separate coder tasks even if
535
+ targeting the same file. The "ONE task per coder" rule is about distinct
536
+ objectives, not about N edits to one file.
537
+
538
+ ## Mandatory Gates
518
539
 
519
540
  **Stage A and Stage B gates and the reviewer + critic closeout gate are
520
541
  MANDATORY for any change made as part of the PR-feedback process.** No fix
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: swarm-pr-review
3
+ audience: swarm-plugin
3
4
  description: Run a graph-guided, tool-augmented Swarm PR review using context packing, parallel exploration, triggered plugin micro-lanes, independent reviewer validation, critic challenge, and metrics writeback. Use for deep pull request review with low false-positive tolerance and high recall.
4
5
  disable-model-invocation: true
5
6
  ---
@@ -308,7 +309,7 @@ For each new commit on the remote:
308
309
  - Does the remote commit introduce a new branch-state fact the handoff should
309
310
  mention?
310
311
  3. **Default stance: prefer the remote state as the next baseline.** Run
311
- the [`parallel-work-check`](../generated/parallel-work-check/SKILL.md)
312
+ `file:.swarm/bundled-skills/parallel-work-check/SKILL.md`
312
313
  protocol for the formal decision template and record the outcome in the
313
314
  handoff artifact.
314
315
 
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: swarm-pr-subscribe
3
+ audience: swarm-plugin
3
4
  description: >
4
5
  Monitor a pull request after creation and act autonomously on pushed PR
5
6
  activity. Use when subscribing to a PR after opening it, when asked to watch,
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: test-file-split
3
+ audience: swarm-plugin
4
+ description: Protocol for splitting test files that exceed the FR-006 500-line hard limit. Covers describe-block extraction, shared helper management, pure-function extraction, mock isolation verification, and cascading-split detection. Load when a test file approaches or exceeds 500 lines.
5
+ ---
6
+
7
+ # Test File Split Protocol (FR-006)
8
+
9
+ CI enforces a **hard 500-line limit** per test file (FR-006 / SC-006.1). Files exceeding this limit fail the quality gate and block PR merge. This skill covers the complete splitting protocol.
10
+
11
+ Read first: `.opencode/skills/writing-tests/SKILL.md` (or `.claude/skills/writing-tests/SKILL.md`) for bun:test framework rules, mock isolation patterns, and file placement conventions.
12
+
13
+ ## When to use this skill
14
+
15
+ - A test file exceeds or approaches 500 lines
16
+ - CI fails with an FR-006 file-size violation
17
+ - You are adding tests to a file that is already above 400 lines (proactive split)
18
+
19
+ ## Step 1 — Measure and identify split boundaries
20
+
21
+ ```bash
22
+ # Check the file
23
+ wc -l tests/unit/scripts/my-module.test.ts
24
+
25
+ # Find all test files exceeding 400 lines (early warning)
26
+ find tests/ -name "*.test.ts" -exec wc -l {} \; | sort -rn | awk '$1 > 400'
27
+ ```
28
+
29
+ Identify natural `describe()` block boundaries. Group blocks by functional area:
30
+ - Each `describe()` block should belong to exactly one split file
31
+ - Shared `beforeEach`/`afterEach` hooks determine which blocks must stay together
32
+
33
+ ## Step 2 — Choose a suffix for the new file
34
+
35
+ | Pattern | Example | When to use |
36
+ |---------|---------|-------------|
37
+ | `<module>-<area>.test.ts` | `release-notes-fragments-sha.test.ts` | Split by functional area (SHA resolution, validation, merge logic) |
38
+ | `<module>-<area>.adversarial.test.ts` | `auth-login.adversarial.test.ts` | Split adversarial tests into their own file |
39
+
40
+ ## Step 3 — Manage shared imports and helpers
41
+
42
+ Three options, in order of preference:
43
+
44
+ 1. **Extract to shared utility (preferred for complex shared setup):**
45
+ Create `tests/helpers/<module>-shared.ts` with shared fixtures, mock factories, and setup functions. Import from both split files.
46
+
47
+ 2. **Duplicate simple imports (for small overlap):**
48
+ If only `bun:test` imports and 1-2 source imports are shared, duplicate them in both files. Simpler than a utility module for trivial cases.
49
+
50
+ 3. **Extract pure functions from source (for testability):**
51
+ If the source module has inline validation logic, extract them as exported pure functions (e.g., `isValidPrNumber`, `resolveAllCandidates`) so both test files can target them independently. See the PR #1762 example below. See `.opencode/skills/generated/safe-extraction/SKILL.md` for the source extraction pattern.
52
+
53
+ ## Step 4 — Extract and move describe blocks
54
+
55
+ 1. Cut the selected `describe()` blocks from the original file.
56
+ 2. Paste them into the new file.
57
+ 3. Add all necessary imports to the new file.
58
+ 4. Remove now-unused imports from the original file.
59
+
60
+ ## Step 5 — Verify both files
61
+
62
+ ### Line count check
63
+ ```bash
64
+ wc -l tests/unit/scripts/my-module.test.ts tests/unit/scripts/my-module-sha.test.ts
65
+ # Both must be under 500 lines
66
+ ```
67
+
68
+ ### Isolated run
69
+ ```bash
70
+ bun --smol test tests/unit/scripts/my-module.test.ts --timeout 60000
71
+ bun --smol test tests/unit/scripts/my-module-sha.test.ts --timeout 60000
72
+ ```
73
+
74
+ ### Co-run (mock isolation verification)
75
+ ```bash
76
+ # Critical: Bun shares a single process across test files.
77
+ # mock.module leaks can cause co-run failures even when isolated runs pass.
78
+ bun --smol test tests/unit/scripts/my-module*.test.ts --timeout 60000
79
+ ```
80
+
81
+ If the co-run fails but isolated runs pass, check for `mock.module()` leakage. See `.opencode/skills/writing-tests/SKILL.md` → "Mock Isolation Rules" and the `_internals` DI seam pattern.
82
+
83
+ ## Step 6 — Evaluate `_test_exports` opportunity
84
+
85
+ After splitting, evaluate whether internal utility functions in the source module can be exported via `_test_exports` for zero-mock testing. This is a natural cleanup moment — the split already forces you to review test coverage boundaries.
86
+
87
+ ## Cascading split warning
88
+
89
+ If a previously split file exceeds 500 lines **again**, the test suite is structurally too large for a single module. Do not split a third time — reorganize the tests by source module boundaries instead. Repeated splitting produces fragmented test suites that are hard to navigate and maintain.
90
+
91
+ ## Real-world example (PR #1762 — pending merge)
92
+
93
+ > **Note:** This example references functions and files from PR #1762's branch. These do not exist on `main` until the PR is merged. The pattern is still valid as an illustration of the splitting protocol.
94
+
95
+ `tests/unit/scripts/release-notes-fragments.test.ts` exceeded 500 lines. It was split into:
96
+
97
+ | File | Lines | Content |
98
+ |------|-------|---------|
99
+ | `release-notes-fragments.test.ts` | 379 | Fragment collection, deduplication, output formatting |
100
+ | `release-notes-fragments-sha.test.ts` | 261 | `extractCommitShasFromBody`, `mergeCandidateLists`, `resolveAllCandidates`, `isValidPrNumber`, `stripCustomReleaseNotesBlock` |
101
+
102
+ The split also extracted `isValidPrNumber` and `resolveAllCandidates` as pure exported functions from the source module, enabling independent testing.
@@ -1,5 +1,6 @@
1
- ---
1
+ ---
2
2
  name: worktree-retry-cleanup
3
+ audience: swarm-plugin
3
4
  description: Protocol for cleaning parallel-coder worktree lanes before retry. Triggered before re-dispatching any task that already has a lane (completed, denied, cancelled, or failed).
4
5
  ---
5
6
 
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: writing-tests
3
+ audience: swarm-plugin
3
4
  description: >
4
5
  Guidelines for writing, organizing, and maintaining tests in the opencode-swarm repository.
5
6
  Covers framework rules (bun:test), mock isolation, CI pipeline structure, file placement,
@@ -610,6 +611,48 @@ authority checks:
610
611
  - For matcher caches or other shared state, test both priming orders when the
611
612
  selected behavior depends on mode, platform, or prior calls.
612
613
 
614
+ ## FR-006: Test File Size Limit (500 lines)
615
+
616
+ CI enforces a **hard 500-line limit** per test file (FR-006). Files exceeding this limit fail the quality gate and block PR merge.
617
+
618
+ ### Checking file length
619
+
620
+ ```bash
621
+ # Check a single file
622
+ wc -l tests/unit/scripts/my-test.test.ts
623
+
624
+ # Find all test files exceeding 400 lines (early warning threshold)
625
+ find tests/ -name "*.test.ts" -exec wc -l {} \; | sort -rn | awk '$1 > 400'
626
+ ```
627
+
628
+ ### Splitting pattern
629
+
630
+ When a test file approaches or exceeds 500 lines, split it by extracting cohesive `describe()` blocks into a new file with a descriptive suffix:
631
+
632
+ 1. **Identify natural boundaries.** Group `describe()` blocks by functional area (e.g., SHA resolution, validation, merge logic).
633
+ 2. **Create the new file** with a descriptive suffix: `<module>-<area>.test.ts` (e.g., `release-notes-fragments-sha.test.ts`, `release-notes-fragments-validation.test.ts`).
634
+ 3. **Move shared imports and helpers.** Either:
635
+ - Duplicate shared imports in both files (simple, for small overlap), OR
636
+ - Extract shared test helpers to a utility module (e.g., `tests/helpers/<module>-shared.ts`) and import from both files (preferred for complex shared setup).
637
+ 4. **Extract testable pure functions.** If the source module has inline validation logic (e.g., `isValidPrNumber`), extract it as an exported pure function so both test files can target it independently.
638
+ 5. **Verify both files are under 500 lines.**
639
+ 6. **Run both files independently AND co-run** to verify no mock isolation breakage:
640
+ ```bash
641
+ bun --smol test tests/unit/scripts/release-notes-fragments.test.ts --timeout 60000
642
+ bun --smol test tests/unit/scripts/release-notes-fragments-sha.test.ts --timeout 60000
643
+ bun --smol test tests/unit/scripts/release-notes-fragments*.test.ts --timeout 60000
644
+ ```
645
+
646
+ ### When to split vs refactor
647
+
648
+ - **Split** when there are natural `describe()` boundaries (e.g., one file per functional area).
649
+ - **Refactor** when the file is a single monolithic test with no clear boundaries — consolidate test logic instead.
650
+ - **Warning:** If a previously split file exceeds 500 lines again, the test suite is structurally too large. Reorganize by module rather than continuing to split.
651
+
652
+ ### Reference
653
+
654
+ See PR #1762 for a real-world example: `release-notes-fragments.test.ts` was split into `release-notes-fragments.test.ts` (379 lines) + `release-notes-fragments-sha.test.ts` (261 lines).
655
+
613
656
  ## Cross-Entry Invariants (config maps)
614
657
 
615
658
  When you modify any entry of a "map of agents/tools/roles" in `src/config/constants.ts` (`AGENT_TOOL_MAP`, `DEFAULT_MODELS`, `QA_AGENTS`, `PIPELINE_AGENTS`, etc.) or tool-name registration in `src/tools/tool-names.ts`, there are tests that assert **parity across sibling entries**, not just shape of one entry.
@@ -12,8 +12,8 @@ import {
12
12
  shouldRunOnStartup,
13
13
  writeBackupArtifact,
14
14
  writeDoctorArtifact
15
- } from "./index-xehrmy76.js";
16
- import"./index-cq2hgvd2.js";
15
+ } from "./index-0gf6de8b.js";
16
+ import"./index-1ey3dxq7.js";
17
17
  import"./index-bfwt3abw.js";
18
18
  import"./index-5e4e2hvv.js";
19
19
  import"./index-p0arc26j.js";
@@ -1,16 +1,16 @@
1
1
  // @bun
2
2
  import {
3
3
  createCuratorLLMDelegate
4
- } from "./index-snmfqhez.js";
5
- import"./index-npxmfj48.js";
4
+ } from "./index-npd9rypp.js";
5
+ import"./index-8f256d7t.js";
6
6
  import"./index-c8s9a3zh.js";
7
- import"./index-xehrmy76.js";
8
- import"./index-cq2hgvd2.js";
7
+ import"./index-0gf6de8b.js";
8
+ import"./index-1ey3dxq7.js";
9
9
  import"./index-d0w40jm3.js";
10
10
  import"./index-scww5b77.js";
11
11
  import"./index-9fxs0rm1.js";
12
12
  import"./index-q1jaeynn.js";
13
- import"./index-82v0d72f.js";
13
+ import"./index-h389ed8y.js";
14
14
  import"./index-30ejh1tx.js";
15
15
  import"./index-t6wfwdm1.js";
16
16
  import"./index-hpp2r0sd.js";
@@ -12,16 +12,16 @@ import {
12
12
  runCuratorInit,
13
13
  runCuratorPhase,
14
14
  writeCuratorSummary
15
- } from "./index-snmfqhez.js";
16
- import"./index-npxmfj48.js";
15
+ } from "./index-npd9rypp.js";
16
+ import"./index-8f256d7t.js";
17
17
  import"./index-c8s9a3zh.js";
18
- import"./index-xehrmy76.js";
19
- import"./index-cq2hgvd2.js";
18
+ import"./index-0gf6de8b.js";
19
+ import"./index-1ey3dxq7.js";
20
20
  import"./index-d0w40jm3.js";
21
21
  import"./index-scww5b77.js";
22
22
  import"./index-9fxs0rm1.js";
23
23
  import"./index-q1jaeynn.js";
24
- import"./index-82v0d72f.js";
24
+ import"./index-h389ed8y.js";
25
25
  import"./index-30ejh1tx.js";
26
26
  import"./index-t6wfwdm1.js";
27
27
  import"./index-hpp2r0sd.js";
@@ -1,17 +1,17 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-9fjpecax.js";
5
- import"./index-snmfqhez.js";
6
- import"./index-npxmfj48.js";
4
+ } from "./index-ky8eb4q6.js";
5
+ import"./index-npd9rypp.js";
6
+ import"./index-8f256d7t.js";
7
7
  import"./index-c8s9a3zh.js";
8
- import"./index-xehrmy76.js";
9
- import"./index-cq2hgvd2.js";
8
+ import"./index-0gf6de8b.js";
9
+ import"./index-1ey3dxq7.js";
10
10
  import"./index-d0w40jm3.js";
11
11
  import"./index-scww5b77.js";
12
12
  import"./index-9fxs0rm1.js";
13
13
  import"./index-q1jaeynn.js";
14
- import"./index-82v0d72f.js";
14
+ import"./index-h389ed8y.js";
15
15
  import"./index-30ejh1tx.js";
16
16
  import"./index-t6wfwdm1.js";
17
17
  import"./index-hpp2r0sd.js";
@@ -1,9 +1,9 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailLog
4
- } from "./index-w84stmkx.js";
5
- import"./index-npxmfj48.js";
6
- import"./index-cq2hgvd2.js";
4
+ } from "./index-r9fjzr3k.js";
5
+ import"./index-8f256d7t.js";
6
+ import"./index-1ey3dxq7.js";
7
7
  import"./index-bfwt3abw.js";
8
8
  import"./index-5e4e2hvv.js";
9
9
  import"./index-p0arc26j.js";
@@ -5,16 +5,16 @@ import {
5
5
  isHiveEligible,
6
6
  promoteFromSwarm,
7
7
  promoteToHive
8
- } from "./index-snmfqhez.js";
9
- import"./index-npxmfj48.js";
8
+ } from "./index-npd9rypp.js";
9
+ import"./index-8f256d7t.js";
10
10
  import"./index-c8s9a3zh.js";
11
- import"./index-xehrmy76.js";
12
- import"./index-cq2hgvd2.js";
11
+ import"./index-0gf6de8b.js";
12
+ import"./index-1ey3dxq7.js";
13
13
  import"./index-d0w40jm3.js";
14
14
  import"./index-scww5b77.js";
15
15
  import"./index-9fxs0rm1.js";
16
16
  import"./index-q1jaeynn.js";
17
- import"./index-82v0d72f.js";
17
+ import"./index-h389ed8y.js";
18
18
  import"./index-30ejh1tx.js";
19
19
  import"./index-t6wfwdm1.js";
20
20
  import"./index-hpp2r0sd.js";
@@ -5,7 +5,7 @@ import {
5
5
  GateConfigSchema,
6
6
  PluginConfigSchema,
7
7
  stripKnownSwarmPrefix
8
- } from "./index-cq2hgvd2.js";
8
+ } from "./index-1ey3dxq7.js";
9
9
  import {
10
10
  log
11
11
  } from "./index-zgwm4ryv.js";
@@ -1839,9 +1839,17 @@ var KnowledgeApplicationConfigSchema = exports_external.object({
1839
1839
  "Task"
1840
1840
  ])
1841
1841
  });
1842
+ var SkillPropagationAudienceSchema = exports_external.string().max(64).refine((value) => value !== "swarm-plugin", {
1843
+ message: "swarm-plugin is reserved for plugin-internal skill routing"
1844
+ }).refine((value) => !value.startsWith("runner:"), {
1845
+ message: "runner:* values are derived from the active runner"
1846
+ }).regex(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/, {
1847
+ message: "audiences must be lowercase domain tokens"
1848
+ });
1842
1849
  var SkillPropagationConfigSchema = exports_external.object({
1843
1850
  enabled: exports_external.boolean().default(true),
1844
- enforce: exports_external.boolean().default(false)
1851
+ enforce: exports_external.boolean().default(false),
1852
+ audiences: exports_external.array(SkillPropagationAudienceSchema).max(16).transform((values) => [...new Set(values)]).default([])
1845
1853
  });
1846
1854
  var SkillImproverConfigSchema = exports_external.object({
1847
1855
  enabled: exports_external.boolean().default(false),
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  stripKnownSwarmPrefix
4
- } from "./index-cq2hgvd2.js";
4
+ } from "./index-1ey3dxq7.js";
5
5
  import {
6
6
  init_logger,
7
7
  warn