opencode-swarm 7.124.0 → 7.125.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 (70) hide show
  1. package/.opencode/skills/critic-gate/SKILL.md +8 -0
  2. package/.opencode/skills/swarm/SKILL.md +12 -8
  3. package/.opencode/skills/swarm-pr-feedback/SKILL.md +245 -171
  4. package/.opencode/skills/swarm-pr-feedback/references/bot-claim-verification.md +71 -0
  5. package/.opencode/skills/swarm-pr-feedback/references/operational-gotchas.md +49 -0
  6. package/.opencode/skills/swarm-pr-review/SKILL.md +238 -412
  7. package/.opencode/skills/swarm-pr-review/references/parser-dry-run.md +235 -0
  8. package/.opencode/skills/swarm-pr-review/references/prompt-templates.md +103 -0
  9. package/.opencode/skills/writing-tests/SKILL.md +4 -123
  10. package/.opencode/skills/writing-tests/references/mock-and-seam-inventory.md +43 -0
  11. package/README.md +3 -1
  12. package/dist/background/lane-output-store.d.ts +10 -0
  13. package/dist/background/pending-delegations.d.ts +3 -0
  14. package/dist/background/workspace-snapshot.d.ts +59 -0
  15. package/dist/cli/{config-doctor-53mnpeke.js → config-doctor-g3m2q646.js} +2 -2
  16. package/dist/cli/{curation-policy-ny033330.js → curation-policy-vgbx8530.js} +2 -2
  17. package/dist/cli/{curator-llm-factory-e0tjz8qq.js → curator-llm-factory-7pvqy94w.js} +15 -14
  18. package/dist/cli/{curator-nvn1pdq6.js → curator-xk4g1nx2.js} +15 -14
  19. package/dist/cli/{evidence-summary-service-9hq7n9k9.js → evidence-summary-service-v4h2w487.js} +1 -1
  20. package/dist/cli/{guardrail-explain-d3dd57nf.js → guardrail-explain-wfk16bpr.js} +16 -15
  21. package/dist/cli/{guardrail-log-3q2z9axs.js → guardrail-log-5zz9xx6z.js} +3 -3
  22. package/dist/cli/{hive-promoter-9gbpepgm.js → hive-promoter-njwc5rt0.js} +15 -14
  23. package/dist/cli/{pending-delegations-e3bcqj77.js → index-0cz1ws47.js} +4 -18
  24. package/dist/cli/{index-3yyr4ynk.js → index-3k8pg467.js} +1 -1
  25. package/dist/cli/{index-x2mmnrnx.js → index-83rwq7pb.js} +1 -1
  26. package/dist/cli/{index-c6crt5rs.js → index-8yvqkbw7.js} +12 -2
  27. package/dist/cli/{index-z0m4angq.js → index-93cq8t5b.js} +5 -5
  28. package/dist/cli/{index-8pz0ypxz.js → index-cz6fv84h.js} +17 -16
  29. package/dist/cli/{index-mkkx1a4f.js → index-dsf1yv4s.js} +1588 -891
  30. package/dist/cli/{index-4rzzzjtg.js → index-fs02c7tz.js} +277 -16
  31. package/dist/cli/{index-zjedv2bd.js → index-ghteqdy1.js} +2 -2
  32. package/dist/cli/{index-13d2g7k9.js → index-kvrk3h8p.js} +1 -1
  33. package/dist/cli/{index-eeg04r0x.js → index-kws8zn6y.js} +1 -1
  34. package/dist/cli/index-mg54y5gc.js +564 -0
  35. package/dist/cli/{index-qd77aft9.js → index-mhhmecsw.js} +2 -2
  36. package/dist/cli/{index-2x4maxte.js → index-mwdcvh0e.js} +4 -4
  37. package/dist/cli/{index-e19mxn9f.js → index-pbjenws7.js} +1 -1
  38. package/dist/cli/{index-wqwpzhv1.js → index-rpg8qqjs.js} +2 -2
  39. package/dist/cli/{index-tyg0zezw.js → index-tn3shyqj.js} +3 -3
  40. package/dist/cli/{index-htbjqftb.js → index-txragda9.js} +1 -1
  41. package/dist/cli/{index-v5ecczts.js → index-z4sc6yxc.js} +1 -1
  42. package/dist/cli/index.js +15 -14
  43. package/dist/cli/{knowledge-escalator-r1s01m3z.js → knowledge-escalator-jtyykzqc.js} +3 -3
  44. package/dist/cli/{knowledge-events-a9hmxvke.js → knowledge-events-qhr97sy9.js} +1 -1
  45. package/dist/cli/{knowledge-store-jt31br5k.js → knowledge-store-332x3pjv.js} +1 -1
  46. package/dist/cli/{knowledge-validator-9vrwxr3z.js → knowledge-validator-3t8tgzsz.js} +4 -4
  47. package/dist/cli/pending-delegations-7j8xqg5f.js +34 -0
  48. package/dist/cli/{scan-cursor-bkjtsawg.js → scan-cursor-q21c9k9q.js} +2 -2
  49. package/dist/cli/{schema-y3z416z7.js → schema-y2d7j2jk.js} +1 -1
  50. package/dist/cli/{skill-generator-794hyndw.js → skill-generator-3gkxx98d.js} +5 -5
  51. package/dist/cli/workspace-snapshot-c9pxxt9a.js +48 -0
  52. package/dist/commands/close.d.ts +4 -2
  53. package/dist/commands/registry.d.ts +1 -1
  54. package/dist/config/schema.d.ts +4 -0
  55. package/dist/evidence/documents-retention.d.ts +116 -0
  56. package/dist/evidence/manager.d.ts +17 -0
  57. package/dist/hooks/pr-workflow-gate.d.ts +211 -0
  58. package/dist/hooks/pr-workflow-response-gate.d.ts +23 -0
  59. package/dist/hooks/pr-workflow-session-resolver.d.ts +14 -0
  60. package/dist/index.js +563 -523
  61. package/dist/tools/complete-pr-workflow.d.ts +5 -0
  62. package/dist/tools/dispatch-lanes.d.ts +44 -0
  63. package/dist/tools/index.d.ts +2 -0
  64. package/dist/tools/manifest.d.ts +2 -0
  65. package/dist/tools/run-pr-feedback-stage-a.d.ts +69 -0
  66. package/dist/tools/tool-metadata.d.ts +8 -0
  67. package/dist/tools/write-pr-review-trigger-eval.d.ts +56 -46
  68. package/package.json +1 -1
  69. package/dist/cli/index-dqh3zhhc.js +0 -133
  70. package/dist/cli/workspace-snapshot-eyf6gd0d.js +0 -22
@@ -0,0 +1,235 @@
1
+ # Dry-Run: Parser-Based Candidate Extraction
2
+
3
+ This section demonstrates the new parser-based extraction path end-to-end
4
+ using synthetic data. It is concrete enough to implement the same pattern in
5
+ another skill.
6
+
7
+ ### Scenario
8
+
9
+ A PR review has dispatched six base explorer lanes via `dispatch_lanes_async`.
10
+ The batch completed and `collect_lane_results` returned:
11
+
12
+ ```json
13
+ {
14
+ "batch_id": "batch-a1b2c3",
15
+ "lane_results": [
16
+ {
17
+ "lane_id": "pr_review_lane1_correctness",
18
+ "status": "completed",
19
+ "output_ref": "L1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
20
+ "output_degraded": false
21
+ },
22
+ {
23
+ "lane_id": "pr_review_lane2_security",
24
+ "status": "completed",
25
+ "output_ref": "L1:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
26
+ "output_degraded": false
27
+ }
28
+ ]
29
+ }
30
+ ```
31
+
32
+ ### Step 1 — Call the parser
33
+
34
+ The orchestrator calls `parse_lane_candidates` for each `output_ref`:
35
+
36
+ ```json
37
+ {
38
+ "tool": "parse_lane_candidates",
39
+ "arguments": {
40
+ "output_ref": "L1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
41
+ "producer": "swarm-pr-review",
42
+ "expected_family": "base_explorer"
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Step 2 — Structured response
48
+
49
+ The parser returns a `ParseResultWithSidecar`. On success, `error` and `error_code` are absent:
50
+
51
+ ```json
52
+ {
53
+ "candidates": [
54
+ {
55
+ "record_type": "candidate",
56
+ "row_format_family": "base_explorer",
57
+ "row_format_version": 1,
58
+ "record_version": { "major": 1, "minor": 1 },
59
+ "source_output_ref": "L1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
60
+ "source_batch_id": "B-2025-06-22-001",
61
+ "source_lane_id": "explorer-1",
62
+ "source_agent": "paid_explorer",
63
+ "source_digest": "sha256:abc123def456...",
64
+ "extracted_from_partial_source": false,
65
+ "sessionId": "ses_01HXYZ...",
66
+ "parentSessionId": "ses_01HABC...",
67
+ "producer": "swarm-pr-review",
68
+ "candidate_id": "C-001",
69
+ "lane": "Lane 1: Correctness and edge cases",
70
+ "micro_lane": null,
71
+ "severity": "HIGH",
72
+ "category": "null-safety",
73
+ "file_line": "src/utils/cache.ts:142",
74
+ "claim": "Uncached getter may return undefined on cold start",
75
+ "evidence_summary": "The `getCached` function returns `cache[key]` without a fallback when the cache is empty.",
76
+ "impact_context": "Downstream callers in `src/handlers/*.ts` expect a defined value and call `.toString()` directly.",
77
+ "invariant_violated": null,
78
+ "confidence": "HIGH"
79
+ },
80
+ {
81
+ "record_type": "candidate",
82
+ "row_format_family": "base_explorer",
83
+ "row_format_version": 1,
84
+ "record_version": { "major": 1, "minor": 1 },
85
+ "source_output_ref": "L1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
86
+ "source_batch_id": "B-2025-06-22-001",
87
+ "source_lane_id": "explorer-1",
88
+ "source_agent": "paid_explorer",
89
+ "source_digest": "sha256:abc123def456...",
90
+ "extracted_from_partial_source": false,
91
+ "sessionId": "ses_01HXYZ...",
92
+ "parentSessionId": "ses_01HABC...",
93
+ "producer": "swarm-pr-review",
94
+ "candidate_id": "C-002",
95
+ "lane": "Lane 1: Correctness and edge cases",
96
+ "micro_lane": null,
97
+ "severity": "MEDIUM",
98
+ "category": "async-ordering",
99
+ "file_line": "src/services/queue.ts:88",
100
+ "claim": "Race between `drain` and `processNext` may drop items",
101
+ "evidence_summary": "`drain` sets `active = false` before awaiting `processNext`, which also checks `active`.",
102
+ "impact_context": "Items submitted during the drain window are silently dropped.",
103
+ "invariant_violated": null,
104
+ "confidence": "MEDIUM"
105
+ }
106
+ ],
107
+ "invocation_envelope": {
108
+ "record_type": "invocation",
109
+ "source_output_ref": "L1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
110
+ "source_batch_id": "B-2025-06-22-001",
111
+ "source_lane_id": "explorer-1",
112
+ "source_agent": "paid_explorer",
113
+ "source_digest": "sha256:abc123def456...",
114
+ "row_format_version": 1,
115
+ "record_version": { "major": 1, "minor": 1 },
116
+ "sessionId": "ses_01HXYZ...",
117
+ "parentSessionId": "ses_01HABC...",
118
+ "producer": "swarm-pr-review",
119
+ "produced_at": "2025-06-22T14:30:00.000Z",
120
+ "format_families_detected": ["base_explorer"],
121
+ "candidate_count": 2,
122
+ "parse_errors": 0,
123
+ "malformed_rows": 0,
124
+ "clean_attestation_count": 0
125
+ },
126
+ "diagnostics": {
127
+ "candidate_count": 2,
128
+ "parse_errors": 0,
129
+ "parse_error_details": [],
130
+ "malformed_rows": 0,
131
+ "duplicate_id_count": 0,
132
+ "duplicate_id_warnings": [],
133
+ "degraded_source_count": 0,
134
+ "incomplete_source_count": 0,
135
+ "format_families_detected": ["base_explorer"],
136
+ "clean_attestation_count": 0
137
+ }
138
+ }
139
+ ```
140
+ > **Note**: callers pass `expected_family` for each dispatch batch. A recognizable
141
+ > conflicting header fails closed with `expected-family-mismatch`; when the flag
142
+ > is absent, the recognized header controls the mapping and positional detection
143
+ > is only a legacy unknown-header fallback. Marker-prefixed data rows remain
144
+ > accepted for compatibility. Valid canonical rows produce `parse_errors: 0`.
145
+
146
+ On refusal (e.g. `output_ref` does not exist), `error` and `error_code` are present; `candidates` is `[]`; `invocation_envelope` and `diagnostics` are populated with empty fields for traceability:
147
+
148
+ ```json
149
+ {
150
+ "error": "Artifact reference not found in store",
151
+ "error_code": "ref-not-found",
152
+ "candidates": [],
153
+ "invocation_envelope": {
154
+ "record_type": "invocation",
155
+ "source_output_ref": "L1:1111111111111111111111111111111111111111111111111111111111111111:2222222222222222222222222222222222222222222222222222222222222222:3333333333333333333333333333333333333333333333333333333333333333",
156
+ "source_batch_id": "",
157
+ "source_lane_id": "",
158
+ "source_agent": "",
159
+ "source_digest": "",
160
+ "row_format_version": 1,
161
+ "record_version": { "major": 1, "minor": 1 },
162
+ "produced_at": "2025-06-22T14:30:00.000Z",
163
+ "format_families_detected": [],
164
+ "candidate_count": 0,
165
+ "parse_errors": 0,
166
+ "malformed_rows": 0,
167
+ "clean_attestation_count": 0
168
+ },
169
+ "diagnostics": {
170
+ "candidate_count": 0,
171
+ "parse_errors": 0,
172
+ "parse_error_details": [],
173
+ "malformed_rows": 0,
174
+ "duplicate_id_count": 0,
175
+ "duplicate_id_warnings": [],
176
+ "degraded_source_count": 0,
177
+ "incomplete_source_count": 0,
178
+ "format_families_detected": [],
179
+ "clean_attestation_count": 0
180
+ }
181
+ }
182
+ ```
183
+
184
+ ### Step 3 — Filter and group
185
+
186
+ The orchestrator filters the returned `candidates[]` array by `producer: "swarm-pr-review"` and the exact allowed `source_batch_id` / `source_lane_id` tuples, then groups
187
+ the candidates. In this synthetic example, the two candidates above are grouped
188
+ by file area:
189
+
190
+ - **Chunk A — `src/utils/`** (1 candidate): C-001
191
+ - **Chunk B — `src/services/`** (1 candidate): C-002
192
+
193
+ If there were more candidates, the orchestrator would also group by category
194
+ (e.g., `null-safety`, `async-ordering`) and cap each chunk at 50 candidates.
195
+
196
+ ### Step 4 — Dispatch reviewer lanes
197
+
198
+ The orchestrator dispatches one reviewer lane per chunk:
199
+
200
+ ```text
201
+ You are the independent reviewer. Validate only the candidates assigned below.
202
+ Do not search for new issues except where needed to validate reachability or
203
+ mitigation. Do not trust explorer severity.
204
+
205
+ Context pack summary:
206
+ - scope: ...
207
+ - obligations: ...
208
+ - impact cone: ...
209
+ - deterministic signals: ...
210
+ - relevant Swarm artifacts / knowledge: ...
211
+ - base_ref: <commit SHA of base branch>
212
+ - head_ref: <commit SHA of PR head branch>
213
+
214
+ Candidates (Chunk A — src/utils/):
215
+ - C-001 | HIGH | null-safety | src/utils/cache.ts:142 | Uncached getter may return undefined on cold start
216
+
217
+ For each candidate, return:
218
+ [REVIEWED] | candidate_id | CONFIRMED/DISPROVED/UNVERIFIED/PRE_EXISTING | evidence_type | final_severity | introduced_by_pr | file:line | rationale | falsification_probe | reviewer_id
219
+
220
+ You must check caller context, reachability, schema/middleware/framework mitigations, state-machine constraints, test coverage, PR-introducedness, and severity.
221
+
222
+ IMPORTANT: If a finding claims behavior is "new" or "introduced by the PR", you MUST read the equivalent code on the base branch (git show <base_ref>:<file>) to verify it was not present before. A reviewer claim of "this is new" is invalid without base-branch evidence. Do not compare the new code to an idealized baseline — compare it to what actually existed on the base branch at the time of the PR.
223
+ ```
224
+
225
+ ### Key invariants
226
+
227
+ - The parser reads the **full artifact**, not a preview. Truncation in the
228
+ `dispatch_lanes` preview does not affect candidate extraction.
229
+ - The orchestrator never classifies candidates — it only filters, groups, and
230
+ routes them.
231
+ - Each reviewer receives a bounded chunk. A chunk with more than 50 candidates
232
+ is split before dispatch.
233
+ - The `invocation_envelope` in the parser response provides audit provenance
234
+ for every extracted candidate.
235
+
@@ -0,0 +1,103 @@
1
+ # Reviewer Prompt Template
2
+
3
+ Use this template when dispatching reviewer subagents:
4
+
5
+ ```text
6
+ You are the independent reviewer. Validate only the candidates assigned below.
7
+ Do not search for new issues except where needed to validate reachability or mitigation.
8
+ Do not trust explorer severity.
9
+
10
+ Context pack summary:
11
+ - scope: ...
12
+ - obligations: ...
13
+ - impact cone: ...
14
+ - deterministic signals: ...
15
+ - relevant Swarm artifacts / knowledge: ...
16
+ - base_ref: <commit SHA of base branch>
17
+ - head_ref: <commit SHA of PR head branch>
18
+
19
+ Candidates:
20
+ - ...
21
+
22
+ For each candidate, return:
23
+ [REVIEWED] | candidate_id | CONFIRMED/DISPROVED/UNVERIFIED/PRE_EXISTING | evidence_type | final_severity | introduced_by_pr | file:line | rationale | falsification_probe | reviewer_id
24
+
25
+ You must check caller context, reachability, schema/middleware/framework mitigations, state-machine constraints, test coverage, PR-introducedness, and severity.
26
+
27
+ IMPORTANT: If a finding claims behavior is "new" or "introduced by the PR", you MUST read the equivalent code on the base branch (git show <base_ref>:<file>) to verify it was not present before. A reviewer claim of "this is new" is invalid without base-branch evidence. Do not compare the new code to an idealized baseline — compare it to what actually existed on the base branch at the time of the PR.
28
+ ```
29
+
30
+ ---
31
+
32
+ # Critic Prompt Template
33
+
34
+ Use this template when dispatching critic subagents:
35
+
36
+ ```text
37
+ You are the adversarial critic. Challenge only reviewer-confirmed findings assigned below.
38
+ Your goal is to reduce false positives, severity inflation, and non-actionable reports.
39
+
40
+ For each finding, challenge:
41
+ - whether evidence proves the claim,
42
+ - whether the path is reachable,
43
+ - whether mitigations apply,
44
+ - whether severity is inflated,
45
+ - whether it is PR-introduced,
46
+ - whether suggested fixes are safe/actionable,
47
+ - whether related files were missed,
48
+ - whether multiple findings should be grouped.
49
+
50
+ Return:
51
+ [CRITIC] | finding_id | UPHELD/DOWNGRADED/DISPROVED/NEEDS_MORE_EVIDENCE | final_severity | reason | required_report_change
52
+
53
+ REQUIRED FINAL LINE — your final line MUST be exactly the row above (no variations, no labeled fields, no placeholders):
54
+ [CRITIC] | finding_id | UPHELD/DOWNGRADED/DISPROVED/NEEDS_MORE_EVIDENCE | final_severity | reason | required_report_change
55
+
56
+ A response without this exact row is treated as a planning preamble and re-dispatched. Do not output only a planning or investigation message.
57
+ ```
58
+
59
+ ---
60
+
61
+ # Explorer Prompt Template
62
+
63
+ Use this template when dispatching base explorer or micro-lane agents:
64
+
65
+ ```text
66
+ You are an explorer. Optimize for recall, not final judgment.
67
+ Return candidates only. Do not use CONFIRMED, DISPROVED, or PRE_EXISTING.
68
+
69
+ Lane:
70
+ Scope:
71
+ base_ref:
72
+ head_ref:
73
+ Obligations:
74
+ Changed files/hunks:
75
+ Impact cone:
76
+ Relevant deterministic signals:
77
+ Relevant Swarm artifacts / knowledge:
78
+ Checklist:
79
+
80
+ You must inspect or mark unavailable:
81
+ 1. changed hunk,
82
+ 2. caller/consumer,
83
+ 3. callee/dependency,
84
+ 4. sibling implementation or prior pattern,
85
+ 5. nearest test or missing-test location,
86
+ 6. deterministic signals,
87
+ 7. Swarm artifacts/knowledge,
88
+ 8. the exact `base_sha...pr_head_sha` merge-base range and both endpoint revisions.
89
+
90
+ Return:
91
+ [CANDIDATE] | candidate_id | lane | severity | category | file:line | claim | evidence_summary | impact_context | confidence
92
+ Emit the marker-bearing header once, then unprefixed data rows.
93
+ For a clean micro-lane, emit `[CLEAN] | micro_lane | coverage_scope | evidence`.
94
+ For a clean base lane, emit `[CLEAN] | workflow_lane | coverage_scope | evidence`.
95
+ ```
96
+
97
+ The orchestrator extracts candidates from the full lane artifact via
98
+ `parse_lane_candidates` as the primary mechanism. The `[CANDIDATE]` row
99
+ format above is a fallback convention for environments where the parser is
100
+ unavailable. Explorers should still emit structured records regardless of
101
+ whether the parser is present.
102
+
103
+ Do not let speed degrade validation quality.
@@ -619,44 +619,6 @@ authority checks:
619
619
 
620
620
  For the full splitting protocol (describe-block extraction, shared helper management, pure-function extraction, mock isolation verification, cascading-split detection), read `file:.swarm/bundled-skills/test-file-split/SKILL.md`.
621
621
 
622
- ### Checking file length
623
-
624
- ```bash
625
- # Check a single file
626
- wc -l tests/unit/scripts/my-test.test.ts
627
-
628
- # Find all test files exceeding 400 lines (early warning threshold)
629
- find tests/ -name "*.test.ts" -exec wc -l {} \; | sort -rn | awk '$1 > 400'
630
- ```
631
-
632
- ### Splitting pattern
633
-
634
- When a test file approaches or exceeds 500 lines, split it by extracting cohesive `describe()` blocks into a new file with a descriptive suffix:
635
-
636
- 1. **Identify natural boundaries.** Group `describe()` blocks by functional area (e.g., SHA resolution, validation, merge logic).
637
- 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`).
638
- 3. **Move shared imports and helpers.** Either:
639
- - Duplicate shared imports in both files (simple, for small overlap), OR
640
- - 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).
641
- 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.
642
- 5. **Verify both files are under 500 lines.**
643
- 6. **Run both files independently AND co-run** to verify no mock isolation breakage:
644
- ```bash
645
- bun --smol test tests/unit/scripts/release-notes-fragments.test.ts --timeout 60000
646
- bun --smol test tests/unit/scripts/release-notes-fragments-sha.test.ts --timeout 60000
647
- bun --smol test tests/unit/scripts/release-notes-fragments*.test.ts --timeout 60000
648
- ```
649
-
650
- ### When to split vs refactor
651
-
652
- - **Split** when there are natural `describe()` boundaries (e.g., one file per functional area).
653
- - **Refactor** when the file is a single monolithic test with no clear boundaries — consolidate test logic instead.
654
- - **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.
655
-
656
- ### Reference
657
-
658
- 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).
659
-
660
622
  ## Cross-Entry Invariants (config maps)
661
623
 
662
624
  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.
@@ -847,50 +809,7 @@ try {
847
809
 
848
810
  ## Running Tests
849
811
 
850
- ### bash (Linux / macOS)
851
-
852
- ```bash
853
- # Single file
854
- bun test src/hooks/scope-guard.test.ts
855
-
856
- # Batch directory (safe for dirs without mock conflicts)
857
- bun --smol test tests/unit/hooks --timeout 30000
858
-
859
- # Per-file loop (required for tools/services/agents — prevents mock poisoning)
860
- for f in tests/unit/tools/*.test.ts; do bun --smol test "$f" --timeout 30000; done
861
-
862
- # CI-equivalent run for batch steps (step 2: cli; step 3: commands + config)
863
- bun --smol test tests/unit/cli --timeout 120000
864
- bun --smol test tests/unit/commands tests/unit/config --timeout 120000
865
- ```
866
-
867
- ### PowerShell (Windows)
868
-
869
- ```powershell
870
- # Single file
871
- bun test src/hooks/scope-guard.test.ts
872
-
873
- # Batch directory (safe for dirs without mock conflicts)
874
- bun --smol test tests/unit/hooks --timeout 30000
875
-
876
- # Per-file loop (required for tools/services/agents — prevents mock poisoning)
877
- Get-ChildItem tests/unit/tools/*.test.ts | ForEach-Object { bun --smol test $_.FullName --timeout 30000 }
878
-
879
- # CI-equivalent run for batch steps (step 2: cli; step 3: commands + config)
880
- bun --smol test tests/unit/cli --timeout 120000
881
- bun --smol test tests/unit/commands tests/unit/config --timeout 120000
882
-
883
- # Capture output to file (avoids truncation when output is large)
884
- bun --smol test tests/unit/agents --timeout 60000 | Out-File "$env:TEMP\test_out.txt"; Get-Content "$env:TEMP\test_out.txt" | Select-Object -Last 50
885
- ```
886
-
887
- **Note:** `for f in ...; do` bash syntax is invalid in PowerShell. Use `Get-ChildItem | ForEach-Object` instead. `Select-String -Last N` is also invalid — use `Select-Object -Last N`.
888
-
889
- **Warning:** Running `bun --smol test tests/unit/tools` as a single batch will cause mock poisoning failures. Always use the per-file loop for the per-file-isolation steps (1a, 1b, 4-6: tools, services, agents, etc.).
890
-
891
- The `--smol` flag reduces Bun's memory footprint. Use it when running large directories (50+ files).
892
-
893
- The `--timeout 120000` flag sets per-test timeout to 120 seconds. Individual tests should complete in under 5 seconds. If a test needs more than 10 seconds, it's doing too much — split it or mock the slow dependency.
812
+ For the full test execution commands (bash and PowerShell per-file isolation loops, CI integration), read `file:.swarm/bundled-skills/running-tests/SKILL.md`. The key principle: always run one test file per process (`bun --smol test <file> --timeout 30000`) to prevent `mock.module` cross-contamination.
894
813
 
895
814
  ## Before Submitting
896
815
 
@@ -905,44 +824,6 @@ The `--timeout 120000` flag sets per-test timeout to 120 seconds. Individual tes
905
824
 
906
825
  Pre-existing and flaky failures are tracked in the per-platform quarantine ledgers (`scripts/ci/quarantined-tests.txt`, `quarantined-tests-macos.txt`, `quarantined-tests-windows.txt`), not in this skill. To confirm a failure is pre-existing, reproduce it in a clean worktree on `origin/main` (see the worktree verify protocol in `running-tests`).
907
826
 
908
- ## Known Cross-module mock.module Locations
909
-
910
- The following directories contain test files that use cross-module `mock.module` (permitted under two-tier convention):
911
-
912
- - `tests/unit/commands/` — mocks tools, hooks, services, state
913
- - `tests/unit/hooks/` — mocks knowledge-store, knowledge-validator, knowledge-reader, telemetry, utils
914
- - `tests/unit/tools/` — mocks Node built-ins (fs, child_process), sast-baseline, build/discovery
915
- - `tests/unit/services/` — mocks path-security
916
- - `tests/unit/config/` — mocks node:fs/promises
917
- - `tests/unit/background/` — mocks utils, event-bus, evidence-summary-service
918
- - `tests/unit/council/` — mocks node:fs
919
- - `tests/unit/plan/` — mocks spec-hash
920
- - `tests/unit/mutation/` — mocks node:child_process
921
- - `tests/unit/git/` — mocks node:child_process
922
- - `tests/integration/` — mocks co-change-analyzer, knowledge-store
923
- - `src/__tests__/` — mocks plan/manager, preflight-service, telemetry
924
- - `src/hooks/` — mocks logger, event-bus
925
- - `src/tools/__tests__/` — mocks test-impact/analyzer, build/discovery, path-security
926
- - `src/mutation/__tests__/` — mocks state
927
- - `src/agents/` — mocks node:fs/promises
928
- - `src/background/` — mocks vulnerability trigger
929
-
930
- ## Dead-code _internals Seams
931
-
932
- The following source modules export `_internals` but have no test consumers (as of this writing). They are harmless but may be removed in future cleanup:
933
-
934
- - `src/tools/secretscan.ts`
935
- - `src/tools/knowledge-recall.ts`
936
- - `src/tools/lint.ts`
937
- - `src/tools/sast-scan.ts`
938
- - `src/tools/sast-baseline.ts`
939
- - `src/mutation/gate.ts`
940
- - `src/mutation/equivalence.ts`
941
- - `src/mutation/engine.ts`
942
- - `src/db/qa-gate-profile.ts`
943
- - `src/config/schema.ts`
944
- - `src/config/index.ts`
945
- - `src/commands/registry.ts`
946
- - `src/background/manager.ts`
947
- - `src/background/event-bus.ts`
948
- - `src/agents/critic.ts`
827
+ ## Mock and Seam Inventories
828
+
829
+ For the current cross-module `mock.module` location inventory and dead-code `_internals` seam inventory, read `references/mock-and-seam-inventory.md`.
@@ -0,0 +1,43 @@
1
+ # Mock and Seam Inventory
2
+
3
+ ## Known Cross-module mock.module Locations
4
+
5
+ The following directories contain test files that use cross-module `mock.module` (permitted under two-tier convention):
6
+
7
+ - `tests/unit/commands/` — mocks tools, hooks, services, state
8
+ - `tests/unit/hooks/` — mocks knowledge-store, knowledge-validator, knowledge-reader, telemetry, utils
9
+ - `tests/unit/tools/` — mocks Node built-ins (fs, child_process), sast-baseline, build/discovery
10
+ - `tests/unit/services/` — mocks path-security
11
+ - `tests/unit/config/` — mocks node:fs/promises
12
+ - `tests/unit/background/` — mocks utils, event-bus, evidence-summary-service
13
+ - `tests/unit/council/` — mocks node:fs
14
+ - `tests/unit/plan/` — mocks spec-hash
15
+ - `tests/unit/mutation/` — mocks node:child_process
16
+ - `tests/unit/git/` — mocks node:child_process
17
+ - `tests/integration/` — mocks co-change-analyzer, knowledge-store
18
+ - `src/__tests__/` — mocks plan/manager, preflight-service, telemetry
19
+ - `src/hooks/` — mocks logger, event-bus
20
+ - `src/tools/__tests__/` — mocks test-impact/analyzer, build/discovery, path-security
21
+ - `src/mutation/__tests__/` — mocks state
22
+ - `src/agents/` — mocks node:fs/promises
23
+ - `src/background/` — mocks vulnerability trigger
24
+
25
+ ## Dead-code _internals Seams
26
+
27
+ The following source modules export `_internals` but have no test consumers (as of this writing). They are harmless but may be removed in future cleanup:
28
+
29
+ - `src/tools/secretscan.ts`
30
+ - `src/tools/knowledge-recall.ts`
31
+ - `src/tools/lint.ts`
32
+ - `src/tools/sast-scan.ts`
33
+ - `src/tools/sast-baseline.ts`
34
+ - `src/mutation/gate.ts`
35
+ - `src/mutation/equivalence.ts`
36
+ - `src/mutation/engine.ts`
37
+ - `src/db/qa-gate-profile.ts`
38
+ - `src/config/schema.ts`
39
+ - `src/config/index.ts`
40
+ - `src/commands/registry.ts`
41
+ - `src/background/manager.ts`
42
+ - `src/background/event-bus.ts`
43
+ - `src/agents/critic.ts`
package/README.md CHANGED
@@ -810,7 +810,9 @@ Every candidate passes a 3-gate pipeline before entering quarantine:
810
810
  | generate_mutants | Architect-only: generates LLM-based mutation patches (5–10 per function across 6 types: off-by-one, null substitution, operator swap, guard removal, branch swap, side-effect deletion) for direct consumption by the mutation_test tool; returns SKIP verdict on LLM failure rather than throwing |
811
811
  | write_mutation_evidence | Architect-only: writes mutation gate results atomically to `.swarm/evidence/{phase}/mutation-gate.json`; accepts verdict (PASS/WARN/FAIL/SKIP), kill rate metrics, and optional survived mutant details; normalizes uppercase-to-lowercase before persisting |
812
812
  | parse_lane_candidates | Architect-only: parses `[CANDIDATE]` rows and micro-lane `[CLEAN]` attestations from a lane `output_ref`; supports asserted batch families, preserves dispatch provenance, and persists sidecar JSONL records |
813
- | write_pr_review_trigger_eval | Architect-only: validates the exact PR-review trigger-map ID set, requires provenance for every matched micro-lane, and atomically persists `.swarm/pr-review/<run_id>/trigger-eval.json` |
813
+ | write_pr_review_trigger_eval | Architect-only: validates the exact mandatory PR-review micro-lane ID set, requires completed provenance for all 11 repository-agnostic lanes, verifies the claimed merge base against an exact live base ref, rejects every `NO-MATCH` waiver, and atomically persists `.swarm/pr-review/<run_id>/trigger-eval.json` |
814
+ | complete_pr_workflow | Architect-only: validates terminal PR-review or PR-feedback coverage; feedback uses a two-call arm/publish protocol and clears only after the approved content is observed on both the bound remote-tracking ref and the actual remote branch |
815
+ | run_pr_feedback_stage_a | Architect-only: executes targeted reproduction/regression and exact `git diff --check` plus every mechanically applicable workspace/category/source build, typecheck, and lint obligation, then persists content-bound Stage A receipts before ordered feedback review gates |
814
816
  | git_blame | Per-line git blame metadata (sha, author, date, summary) via `git blame --porcelain`; supports optional line range filtering |
815
817
  | diff | Structured git diff with contract change detection; supports `summaryOnly` mode returning file list with additions/deletions counts |
816
818
  | suggest_patch | Reviewer-safe structured patch suggestion; supports `format` parameter ('json' or 'unified') where unified outputs valid unified diff with `diff --git` headers, hunks, and context |
@@ -11,6 +11,11 @@ declare const LaneOutputArtifactSchema: z.ZodObject<{
11
11
  sessionId: z.ZodOptional<z.ZodString>;
12
12
  parentSessionId: z.ZodOptional<z.ZodString>;
13
13
  mode: z.ZodOptional<z.ZodString>;
14
+ workflowLane: z.ZodOptional<z.ZodString>;
15
+ prHeadSha: z.ZodOptional<z.ZodString>;
16
+ gitHead: z.ZodOptional<z.ZodString>;
17
+ revisionDigest: z.ZodOptional<z.ZodString>;
18
+ scope: z.ZodOptional<z.ZodString>;
14
19
  source: z.ZodEnum<{
15
20
  dispatch_lanes: "dispatch_lanes";
16
21
  collect_lane_results: "collect_lane_results";
@@ -34,6 +39,11 @@ export interface StoreLaneOutputInput {
34
39
  sessionId?: string;
35
40
  parentSessionId?: string;
36
41
  mode?: string;
42
+ workflowLane?: string;
43
+ prHeadSha?: string;
44
+ gitHead?: string;
45
+ revisionDigest?: string;
46
+ scope?: string;
37
47
  source: LaneOutputSource;
38
48
  text: string;
39
49
  messageCount?: number;
@@ -52,6 +52,8 @@ export interface BackgroundDelegationRecord {
52
52
  laneId?: string;
53
53
  /** Advisory workflow/mode that launched the lane. */
54
54
  mode?: string;
55
+ /** Mechanical PR workflow obligation identifier, distinct from retry-safe laneId. */
56
+ workflowLane?: string;
55
57
  /** Canonical hash of prompt/provenance inputs captured at dispatch time. */
56
58
  promptHash?: string;
57
59
  /** Project/root provenance captured at dispatch time. */
@@ -119,6 +121,7 @@ export interface RecordPendingInput {
119
121
  batchId?: string;
120
122
  laneId?: string;
121
123
  mode?: string;
124
+ workflowLane?: string;
122
125
  promptHash?: string;
123
126
  workspace?: BackgroundWorkspaceSnapshot;
124
127
  taskChangeContext?: BackgroundTaskChangeContext;
@@ -11,8 +11,65 @@ interface CaptureWorkspaceSnapshotOptions {
11
11
  */
12
12
  resolveCurrentPrHeadSha?: boolean;
13
13
  }
14
+ /**
15
+ * Read one bounded repository file from an immutable commit without consulting
16
+ * the mutable checkout. Callers compare this text with the checkout copy before
17
+ * treating a PR-provided contract as authority.
18
+ */
19
+ export declare function readGitTextAtRevision(directory: string, revision: string, relativePath: string): string | null;
20
+ /** Resolve the exact checked-out commit through the bounded Git snapshot path. */
21
+ export declare function resolveCurrentGitHead(directory: string): string | null;
22
+ /** Resolve one exact PR merge base without shell interpolation or unbounded I/O. */
23
+ export declare function resolveExactMergeBase(directory: string, baseRef: string, prHeadSha: string): string | null;
24
+ /** Resolve the current branch's exact remote-tracking publication target. */
25
+ export declare function resolveCurrentUpstreamRemoteRef(directory: string): string | null;
26
+ export interface PrUpstreamPushTarget {
27
+ remoteName: string;
28
+ remoteBranchRef: string;
29
+ remoteTrackingRef: string;
30
+ }
31
+ /** Resolve a branch's upstream without guessing where a remote name ends. */
32
+ export declare function resolveCurrentUpstreamPushTarget(directory: string): PrUpstreamPushTarget | null;
33
+ /** Query the actual remote ref; local tracking refs are not publication proof. */
34
+ export declare function resolveExactRemoteBranchHead(directory: string, remoteName: string, remoteBranchRef: string): string | null;
35
+ /** Bind Stage-A execution to Git refs, config, HEAD, and index metadata. */
36
+ export declare function resolveGitControlStateDigest(directory: string): string | null;
37
+ /** Require all independently reviewed content to be captured by the commit. */
38
+ export declare function resolveIsWorkingTreeClean(directory: string): boolean | null;
39
+ /** Count commits on the ancestry path from the immutable intake head to HEAD. */
40
+ export declare function resolveCommitCountSince(directory: string, baseHeadSha: string, currentHeadSha: string): number | null;
41
+ /** Prove the publication commit is a non-merge direct child of intake HEAD. */
42
+ export declare function resolveIsExactSingleChildCommit(directory: string, baseHeadSha: string, currentHeadSha: string): boolean | null;
43
+ /**
44
+ * Return remote-tracking refs whose tip equals the exact checked-out commit.
45
+ * Publication closeout uses this bounded local observation after a successful
46
+ * push; an empty list is not proof that the approved revision was published.
47
+ */
48
+ export declare function resolveRemoteRefsContainingHead(directory: string, headSha: string): string[] | null;
49
+ /**
50
+ * Bind a mutable working tree to its actual content, not merely porcelain status.
51
+ * This lets PR-feedback approvals fail closed when a same-path edit occurs after a
52
+ * gate. Paths come from Git, are containment-checked, and are hashed without
53
+ * following symlinks outside the project.
54
+ */
55
+ export declare function resolvePrWorkflowRevisionDigest(directory: string, baseHeadSha: string): string | null;
56
+ /**
57
+ * Asynchronously bind a mutable working tree to its actual content. File
58
+ * reads are chunked with bounded cooperative yields so a large, permitted
59
+ * revision cannot stall the synchronous plugin gate path.
60
+ */
61
+ export declare function resolvePrWorkflowRevisionDigestAsync(directory: string, baseHeadSha: string): Promise<string | null>;
14
62
  /** Parse `git status --porcelain=v1 -z`, including both sides of renames. */
15
63
  export declare function parsePorcelainPaths(output: string): string[] | null;
64
+ interface PorcelainV2Snapshot {
65
+ gitHead: string | null;
66
+ changedFiles: string[];
67
+ }
68
+ /**
69
+ * Parse the one-command `git status --porcelain=v2 --branch -z` snapshot used
70
+ * by background evidence capture. A malformed or unknown record fails closed.
71
+ */
72
+ declare function parsePorcelainV2Snapshot(output: string): PorcelainV2Snapshot | null;
16
73
  export declare function captureWorkspaceSnapshot(directory: string, optionsOrScope?: string | null | CaptureWorkspaceSnapshotOptions, prHeadShaArg?: string | null): BackgroundWorkspaceSnapshot;
17
74
  /**
18
75
  * Conservatively derive final paths changed since a pre-task snapshot.
@@ -34,5 +91,7 @@ export declare function compareWorkspaceSnapshots(expected: BackgroundWorkspaceS
34
91
  export declare function digest(text: string): string;
35
92
  export declare const _internals: {
36
93
  spawnSync: SpawnSync;
94
+ yieldControl: () => Promise<void>;
95
+ parsePorcelainV2Snapshot: typeof parsePorcelainV2Snapshot;
37
96
  };
38
97
  export {};