@plainconceptsplatform/workflows 0.4.34 → 0.6.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 (61) hide show
  1. package/README.md +88 -88
  2. package/dist/action-validation.test.js +5 -2
  3. package/dist/catalog-installation.js +50 -7
  4. package/dist/catalog-installation.test.js +59 -22
  5. package/dist/catalog-listing.test.js +4 -4
  6. package/dist/index.js +48 -48
  7. package/dist/index.test.js +9 -9
  8. package/dist/repository-inspection.test.js +2 -2
  9. package/dist/route-processing.js +24 -25
  10. package/dist/route-processing.test.js +166 -233
  11. package/dist/stack-defaults.js +14 -14
  12. package/dist/stack-defaults.test.js +79 -79
  13. package/dist/tui.test.js +10 -7
  14. package/dist/workflow-catalog.d.ts +1 -1
  15. package/dist/workflow-catalog.js +1 -5
  16. package/loops/actions/add-issue-labels/action.yml +2 -2
  17. package/loops/actions/agent-output.cjs +17 -17
  18. package/loops/actions/apply-agent-bundle/apply-bundle.sh +14 -1
  19. package/loops/actions/audit-close/action.yml +24 -0
  20. package/loops/actions/classify-route/action.yml +8 -3
  21. package/loops/actions/classify-route/classify-route.sh +58 -38
  22. package/loops/actions/cleanup-artifacts/action.yml +38 -12
  23. package/loops/actions/identify-gate-subject/action.yml +3 -1
  24. package/loops/actions/remove-issue-labels/action.yml +4 -2
  25. package/loops/actions/update-changelog/action.yml +113 -0
  26. package/loops/actions/validate-merge-gate-output/action.yml +40 -40
  27. package/loops/actions/validate-refine-output/validate-refine-output.sh +34 -5
  28. package/loops/actions/validate-review-output/action.yml +35 -35
  29. package/loops/actions/validate-triage-output/action.yml +36 -36
  30. package/loops/actions/verify-refine-output/verify-refine-output.sh +20 -0
  31. package/loops/actions/verify-route-matrix/verify-route-matrix.sh +375 -36
  32. package/loops/scripts/compile-agent-workflows.mjs +290 -6
  33. package/loops/scripts/merge-changelog.mjs +76 -0
  34. package/loops/templates/agentics/actionlint.yaml +13 -0
  35. package/loops/templates/agentics/agentics-checks.yml +125 -8
  36. package/loops/templates/agentics/agentics-maintenance.yml +121 -121
  37. package/loops/templates/ci/app-ci-dotnet-next.yml +330 -171
  38. package/loops/templates/ci/app-ci-node-monorepo.yml +260 -178
  39. package/loops/templates/issues/bug_report.yml +109 -109
  40. package/loops/templates/issues/feature_request.yml +75 -75
  41. package/loops/templates/opencode/opencode.ci.json +49 -47
  42. package/loops/templates/opencode/opencode.ci.json.md +49 -41
  43. package/loops/templates/release/github-release.yml +30 -30
  44. package/loops/workflows/agent-apply-review.md +469 -443
  45. package/loops/workflows/agent-audit.md +213 -197
  46. package/loops/workflows/agent-implement.md +640 -414
  47. package/loops/workflows/agent-merge-gate.md +844 -584
  48. package/loops/workflows/agent-refine.md +633 -418
  49. package/loops/workflows/agent-release.md +258 -0
  50. package/loops/workflows/agent-triage.md +447 -439
  51. package/loops/workflows/authorize-bot-work.yml +85 -82
  52. package/loops/workflows/shared/opencode-ci.md +206 -197
  53. package/loops/workflows/shared/platform-defaults.md +19 -16
  54. package/loops/workflows/work-router.yml +1038 -632
  55. package/package.json +7 -8
  56. package/dist/repository-state.d.ts +0 -18
  57. package/dist/repository-state.js +0 -77
  58. package/dist/repository-state.test.d.ts +0 -1
  59. package/dist/repository-state.test.js +0 -96
  60. package/loops/workflows/agent-direct.md +0 -375
  61. package/loops/workflows/agent-propose.md +0 -342
@@ -1,197 +1,168 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { addRouteExclusion, excludedWorkerFiles, processRoutes, stripRouteFromClassifier, stripRouteFromRouter, } from "./route-processing.js";
3
3
  import { routeNames } from "./workflow-catalog.js";
4
- const ROUTER_YAML = `# header
5
- name: "All Work Router"
6
-
7
- on:
8
- schedule:
9
- - cron: "17 1 * * 1"
10
- - cron: "43 3 * * *"
11
- - cron: "0 6 * * *"
12
- - cron: "*/5 * * * *"
13
- - cron: "0 */2 * * *"
14
- - cron: "29 7 * * *"
15
-
16
- workflow_dispatch:
17
- inputs:
18
- operation:
19
- description: "Operation to run"
20
- required: true
21
- type: choice
22
- options:
23
- - refine
24
- - implement
25
- - direct
26
- - triage
27
- - apply-review
28
- - merge-gate
29
- - audit
30
- - propose
31
- - audit-close
32
- - cleanup-artifacts
33
- - reconcile-bot-pr-runs
34
- - stale-recovery
35
- - validate
36
-
37
- jobs:
38
- call-refine:
39
- needs: [classify, authorize]
40
- if: needs.classify.outputs.route == 'refine' && needs.authorize.outputs.trusted == 'true'
41
- uses: ./.github/workflows/agent-refine.lock.yml
42
- secrets:
43
- OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
44
-
45
- call-implement:
46
- needs: [classify, authorize]
47
- if: needs.classify.outputs.route == 'implement' && needs.authorize.outputs.trusted == 'true'
48
- uses: ./.github/workflows/agent-implement.lock.yml
49
- secrets:
50
- OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
51
-
52
- call-triage:
53
- needs: [classify, authorize]
54
- if: needs.classify.outputs.route == 'triage' && needs.authorize.outputs.is_outside_collaborator == 'true'
55
- uses: ./.github/workflows/agent-triage.lock.yml
56
- secrets:
57
- OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
58
-
59
- call-audit:
60
- needs: classify
61
- if: needs.classify.outputs.route == 'audit'
62
- uses: ./.github/workflows/agent-audit.lock.yml
63
- secrets:
64
- OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
65
-
66
- call-propose:
67
- needs: classify
68
- if: needs.classify.outputs.route == 'propose'
69
- uses: ./.github/workflows/agent-propose.lock.yml
70
- secrets:
71
- OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
72
-
73
- audit-close:
74
- needs: classify
75
- if: needs.classify.outputs.route == 'audit-close'
76
- runs-on: ubuntu-latest
4
+ const ROUTER_YAML = `# header
5
+ name: "All Work Router"
6
+
7
+ on:
8
+ schedule:
9
+ - cron: "17 1 * * 1"
10
+ - cron: "43 3 * * *"
11
+ - cron: "0 6 * * *"
12
+ - cron: "*/5 * * * *"
13
+ - cron: "0 */2 * * *"
14
+
15
+ workflow_dispatch:
16
+ inputs:
17
+ operation:
18
+ description: "Operation to run"
19
+ required: true
20
+ type: choice
21
+ options:
22
+ - refine
23
+ - implement
24
+ - triage
25
+ - apply-review
26
+ - merge-gate
27
+ - audit
28
+ - audit-close
29
+ - cleanup-artifacts
30
+ - reconcile-bot-pr-runs
31
+ - stale-recovery
32
+ - validate
33
+
34
+ jobs:
35
+ call-refine:
36
+ needs: [classify, authorize]
37
+ if: needs.classify.outputs.route == 'refine' && needs.authorize.outputs.trusted == 'true'
38
+ uses: ./.github/workflows/agent-refine.lock.yml
39
+ secrets:
40
+ OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
41
+
42
+ call-implement:
43
+ needs: [classify, authorize]
44
+ if: needs.classify.outputs.route == 'implement' && needs.authorize.outputs.trusted == 'true'
45
+ uses: ./.github/workflows/agent-implement.lock.yml
46
+ secrets:
47
+ OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
48
+
49
+ call-triage:
50
+ needs: [classify, authorize]
51
+ if: needs.classify.outputs.route == 'triage' && needs.authorize.outputs.is_outside_collaborator == 'true'
52
+ uses: ./.github/workflows/agent-triage.lock.yml
53
+ secrets:
54
+ OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
55
+
56
+ call-audit:
57
+ needs: classify
58
+ if: needs.classify.outputs.route == 'audit'
59
+ uses: ./.github/workflows/agent-audit.lock.yml
60
+ secrets:
61
+ OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
62
+
63
+ audit-close:
64
+ needs: classify
65
+ if: needs.classify.outputs.route == 'audit-close'
66
+ runs-on: ubuntu-latest
77
67
  `;
78
- const CLASSIFIER_SH = `#!/usr/bin/env bash
79
- set -euo pipefail
80
-
81
- readonly AUDIT_CRON="17 1 * * 1"
82
- readonly AUDIT_CLOSE_CRON="43 3 * * *"
83
- readonly CLEANUP_ARTIFACTS_CRON="0 6 * * *"
84
- readonly RECONCILE_BOT_PR_RUNS_CRON="*/5 * * * *"
85
- readonly STALE_RECOVERY_CRON="0 */2 * * *"
86
- readonly PROPOSE_CRON="29 7 * * *"
87
-
88
- classify_route() {
89
- local route="none" error=""
90
-
91
- case "\${EVENT:-}" in
92
- schedule)
93
- trigger_kind="scheduled"
94
- case "\${SCHEDULE:-}" in
95
- "\$AUDIT_CRON") route="audit" ;;
96
- "\$AUDIT_CLOSE_CRON") route="audit-close" ;;
97
- "\$CLEANUP_ARTIFACTS_CRON") route="cleanup-artifacts" ;;
98
- "\$RECONCILE_BOT_PR_RUNS_CRON") route="reconcile-bot-pr-runs" ;;
99
- "\$STALE_RECOVERY_CRON") route="stale-recovery" ;;
100
- "\$PROPOSE_CRON") route="propose" ;;
101
- *) error="no route for cron '\${SCHEDULE:-}'" ;;
102
- esac
103
- ;;
104
-
105
- workflow_dispatch)
106
- trigger_kind="manual"
107
- case "\${OPERATION:-}" in
108
- refine | implement | direct)
109
- route="\${OPERATION}"
110
- ;;
111
- apply-review)
112
- route="apply-review"
113
- ;;
114
- merge-gate)
115
- route="merge-gate"
116
- ;;
117
- audit | propose)
118
- route="\${OPERATION}"
119
- trigger_kind="\${INPUT_TRIGGER_KIND:-manual}"
120
- ;;
121
- audit-close | cleanup-artifacts | reconcile-bot-pr-runs | stale-recovery | validate)
122
- route="\${OPERATION}"
123
- ;;
124
- *)
125
- error="unknown operation '\${OPERATION:-}'"
126
- ;;
127
- esac
128
- ;;
129
- esac
130
-
131
- cat <<EOF
132
- route=\${route}
133
- error=\${error}
134
- EOF
135
- }
136
-
137
- if [ "\${BASH_SOURCE[0]}" = "\$0" ]; then
138
- classify_route
139
- fi
68
+ const CLASSIFIER_SH = `#!/usr/bin/env bash
69
+ set -euo pipefail
70
+
71
+ readonly AUDIT_CRON="17 1 * * 1"
72
+ readonly AUDIT_CLOSE_CRON="43 3 * * *"
73
+ readonly CLEANUP_ARTIFACTS_CRON="0 6 * * *"
74
+ readonly RECONCILE_BOT_PR_RUNS_CRON="*/5 * * * *"
75
+ readonly STALE_RECOVERY_CRON="0 */2 * * *"
76
+
77
+ classify_route() {
78
+ local route="none" error=""
79
+
80
+ case "\${EVENT:-}" in
81
+ schedule)
82
+ trigger_kind="scheduled"
83
+ case "\${SCHEDULE:-}" in
84
+ "\$AUDIT_CRON") route="audit" ;;
85
+ "\$AUDIT_CLOSE_CRON") route="audit-close" ;;
86
+ "\$CLEANUP_ARTIFACTS_CRON") route="cleanup-artifacts" ;;
87
+ "\$RECONCILE_BOT_PR_RUNS_CRON") route="reconcile-bot-pr-runs" ;;
88
+ "\$STALE_RECOVERY_CRON") route="stale-recovery" ;;
89
+ *) error="no route for cron '\${SCHEDULE:-}'" ;;
90
+ esac
91
+ ;;
92
+
93
+ workflow_dispatch)
94
+ trigger_kind="manual"
95
+ case "\${OPERATION:-}" in
96
+ refine | implement)
97
+ route="\${OPERATION}"
98
+ ;;
99
+ apply-review)
100
+ route="apply-review"
101
+ ;;
102
+ merge-gate)
103
+ route="merge-gate"
104
+ ;;
105
+ audit)
106
+ route="\${OPERATION}"
107
+ trigger_kind="\${INPUT_TRIGGER_KIND:-manual}"
108
+ ;;
109
+ audit-close | cleanup-artifacts | reconcile-bot-pr-runs | stale-recovery | validate)
110
+ route="\${OPERATION}"
111
+ ;;
112
+ *)
113
+ error="unknown operation '\${OPERATION:-}'"
114
+ ;;
115
+ esac
116
+ ;;
117
+ esac
118
+
119
+ cat <<EOF
120
+ route=\${route}
121
+ error=\${error}
122
+ EOF
123
+ }
124
+
125
+ if [ "\${BASH_SOURCE[0]}" = "\$0" ]; then
126
+ classify_route
127
+ fi
140
128
  `;
141
- const MATRIX_SH = `#!/usr/bin/env bash
142
- set -euo pipefail
143
-
144
- echo "── Router wiring ─────────────────────────────────────────────────────────"
145
- for route in refine implement direct triage apply-review merge-gate audit propose bot-approve \\
146
- audit-close cleanup-artifacts reconcile-bot-pr-runs stale-recovery validate; do
147
- if grep -q "route == '\${route}'" "\$ROUTER_YML"; then
148
- PASS=\$((PASS + 1))
149
- else
150
- FAIL=\$((FAIL + 1))
151
- echo "FAIL: work-router.yml has no job for route '\${route}'" >&2
152
- fi
153
- done
154
-
155
- while read -r operation; do
156
- if grep -q "route == '\${operation}'" "\$ROUTER_YML"; then
157
- PASS=\$((PASS + 1))
158
- else
159
- FAIL=\$((FAIL + 1))
160
- echo "FAIL: dispatch operation '\${operation}' has no job in work-router.yml" >&2
161
- fi
162
- done < <(sed -n '/^ operation:/,/^ issue-number:/p' "\$ROUTER_YML" | sed -n 's/^ - //p')
163
-
164
- echo
165
- if [ "\$FAIL" -eq 0 ]; then
166
- echo "Route matrix: \${PASS} passed"
167
- else
168
- echo "Route matrix: \${PASS} passed, \${FAIL} FAILED" >&2
169
- fi
170
-
171
- exit \$((FAIL > 0))
129
+ const MATRIX_SH = `#!/usr/bin/env bash
130
+ set -euo pipefail
131
+
132
+ echo "── Router wiring ─────────────────────────────────────────────────────────"
133
+ for route in refine implement triage apply-review merge-gate audit bot-approve \\
134
+ audit-close cleanup-artifacts reconcile-bot-pr-runs stale-recovery validate; do
135
+ if grep -q "route == '\${route}'" "\$ROUTER_YML"; then
136
+ PASS=\$((PASS + 1))
137
+ else
138
+ FAIL=\$((FAIL + 1))
139
+ echo "FAIL: work-router.yml has no job for route '\${route}'" >&2
140
+ fi
141
+ done
142
+
143
+ while read -r operation; do
144
+ if grep -q "route == '\${operation}'" "\$ROUTER_YML"; then
145
+ PASS=\$((PASS + 1))
146
+ else
147
+ FAIL=\$((FAIL + 1))
148
+ echo "FAIL: dispatch operation '\${operation}' has no job in work-router.yml" >&2
149
+ fi
150
+ done < <(sed -n '/^ operation:/,/^ issue-number:/p' "\$ROUTER_YML" | sed -n 's/^ - //p')
151
+
152
+ echo
153
+ if [ "\$FAIL" -eq 0 ]; then
154
+ echo "Route matrix: \${PASS} passed"
155
+ else
156
+ echo "Route matrix: \${PASS} passed, \${FAIL} FAILED" >&2
157
+ fi
158
+
159
+ exit \$((FAIL > 0))
172
160
  `;
173
161
  describe("stripRouteFromRouter", () => {
174
- it("removes the propose cron entry", () => {
175
- const result = stripRouteFromRouter(ROUTER_YAML, "propose");
176
- expect(result).not.toContain('cron: "29 7 * * *"');
177
- expect(result).toContain('cron: "17 1 * * 1"');
178
- });
179
- it("removes the call-propose job block", () => {
180
- const result = stripRouteFromRouter(ROUTER_YAML, "propose");
181
- expect(result).not.toContain("call-propose");
182
- expect(result).toContain("call-refine");
183
- expect(result).toContain("call-audit");
184
- });
185
- it("removes propose from the dispatch options", () => {
186
- const result = stripRouteFromRouter(ROUTER_YAML, "propose");
187
- expect(result).not.toMatch(/^\s+- propose$/m);
188
- expect(result).toMatch(/^\s+- refine$/m);
189
- expect(result).toMatch(/^\s+- audit$/m);
190
- });
191
162
  it("removes the audit cron entry", () => {
192
163
  const result = stripRouteFromRouter(ROUTER_YAML, "audit");
193
164
  expect(result).not.toContain('cron: "17 1 * * 1"');
194
- expect(result).toContain('cron: "29 7 * * *"');
165
+ expect(result).toContain('cron: "43 3 * * *"');
195
166
  });
196
167
  it("removes the call-audit job block", () => {
197
168
  const result = stripRouteFromRouter(ROUTER_YAML, "audit");
@@ -209,42 +180,27 @@ describe("stripRouteFromRouter", () => {
209
180
  });
210
181
  });
211
182
  describe("stripRouteFromClassifier", () => {
212
- it("removes the PROPOSE_CRON constant", () => {
213
- const result = stripRouteFromClassifier(CLASSIFIER_SH, "propose");
214
- expect(result).not.toContain('readonly PROPOSE_CRON');
215
- expect(result).toContain('readonly AUDIT_CRON');
216
- });
217
- it("removes the propose schedule case", () => {
218
- const result = stripRouteFromClassifier(CLASSIFIER_SH, "propose");
219
- expect(result).not.toContain('"$PROPOSE_CRON") route="propose"');
220
- expect(result).toContain('"$AUDIT_CRON") route="audit"');
221
- });
222
183
  it("removes the AUDIT_CRON constant", () => {
223
184
  const result = stripRouteFromClassifier(CLASSIFIER_SH, "audit");
224
185
  expect(result).not.toContain('readonly AUDIT_CRON');
225
- expect(result).toContain('readonly PROPOSE_CRON');
186
+ expect(result).toContain('readonly AUDIT_CLOSE_CRON');
226
187
  });
227
188
  it("removes the audit schedule case", () => {
228
189
  const result = stripRouteFromClassifier(CLASSIFIER_SH, "audit");
229
190
  expect(result).not.toContain('"$AUDIT_CRON") route="audit"');
230
- expect(result).toContain('"$PROPOSE_CRON") route="propose"');
231
- });
232
- it("removes propose from the dispatch case union", () => {
233
- const result = stripRouteFromClassifier(CLASSIFIER_SH, "propose");
234
- expect(result).not.toContain("audit | propose)");
235
- expect(result).toContain("audit)");
191
+ expect(result).toContain('"$AUDIT_CLOSE_CRON") route="audit-close"');
236
192
  });
237
193
  });
238
194
  describe("addRouteExclusion", () => {
239
195
  it("adds an exclusion assertion for the route", () => {
240
- const result = addRouteExclusion(MATRIX_SH, "propose");
241
- expect(result).toContain("excluded route 'propose'");
242
- expect(result).toContain("propose correctly excluded from work-router.yml");
196
+ const result = addRouteExclusion(MATRIX_SH, "audit");
197
+ expect(result).toContain("excluded route 'audit'");
198
+ expect(result).toContain("audit correctly excluded from work-router.yml");
243
199
  });
244
200
  it("does not add the exclusion twice", () => {
245
- const once = addRouteExclusion(MATRIX_SH, "propose");
246
- const twice = addRouteExclusion(once, "propose");
247
- const matchCount = (twice.match(/excluded route 'propose'/g) ?? []).length;
201
+ const once = addRouteExclusion(MATRIX_SH, "audit");
202
+ const twice = addRouteExclusion(once, "audit");
203
+ const matchCount = (twice.match(/excluded route 'audit'/g) ?? []).length;
248
204
  expect(matchCount).toBe(1);
249
205
  });
250
206
  });
@@ -266,33 +222,13 @@ describe("processRoutes", () => {
266
222
  ]);
267
223
  const result = processRoutes(files, []);
268
224
  const router = result.get(".github/workflows/work-router.yml");
269
- expect(router).not.toContain("call-propose");
270
225
  expect(router).not.toContain("call-audit");
271
- expect(router).not.toContain("- propose");
272
226
  expect(router).not.toContain("- refine");
273
227
  const classifier = result.get(".github/actions/classify-route/classify-route.sh");
274
228
  expect(classifier).toBe(CLASSIFIER_SH);
275
229
  const matrix = result.get(".github/actions/verify-route-matrix/verify-route-matrix.sh");
276
230
  expect(matrix).toContain("Route matrix: selected routes valid");
277
- expect(matrix).toContain("for route in refine implement direct triage apply-review merge-gate audit propose");
278
- });
279
- it("strips propose from all three files when unselected", () => {
280
- const files = new Map([
281
- [".github/workflows/work-router.yml", ROUTER_YAML],
282
- [".github/actions/classify-route/classify-route.sh", CLASSIFIER_SH],
283
- [".github/actions/verify-route-matrix/verify-route-matrix.sh", MATRIX_SH],
284
- ]);
285
- const selectedRoutes = routeNames.filter((r) => r !== "propose");
286
- const result = processRoutes(files, selectedRoutes);
287
- const router = result.get(".github/workflows/work-router.yml");
288
- expect(router).not.toContain("call-propose");
289
- expect(router).not.toContain('cron: "29 7 * * *"');
290
- expect(router).not.toMatch(/^\s+- propose$/m);
291
- const classifier = result.get(".github/actions/classify-route/classify-route.sh");
292
- expect(classifier).toBe(CLASSIFIER_SH);
293
- const matrix = result.get(".github/actions/verify-route-matrix/verify-route-matrix.sh");
294
- expect(matrix).toContain("for route in refine implement direct triage apply-review merge-gate audit");
295
- expect(matrix).toContain("for route in propose");
231
+ expect(matrix).toContain("for route in refine implement triage apply-review merge-gate audit");
296
232
  });
297
233
  it("strips audit from all three files when unselected", () => {
298
234
  const files = new Map([
@@ -320,7 +256,6 @@ describe("processRoutes", () => {
320
256
  expect(router).toContain("call-refine");
321
257
  expect(router).toContain("call-implement");
322
258
  expect(router).not.toContain("call-audit");
323
- expect(router).not.toContain("call-propose");
324
259
  });
325
260
  });
326
261
  describe("excludedWorkerFiles", () => {
@@ -330,7 +265,6 @@ describe("excludedWorkerFiles", () => {
330
265
  expect(excluded.has("agent-refine.md")).toBe(false);
331
266
  expect(excluded.has("agent-implement.md")).toBe(false);
332
267
  expect(excluded.has("agent-audit.md")).toBe(true);
333
- expect(excluded.has("agent-propose.md")).toBe(true);
334
268
  });
335
269
  it("returns an empty set when all routes are selected", () => {
336
270
  const excluded = excludedWorkerFiles([...routeNames]);
@@ -338,13 +272,12 @@ describe("excludedWorkerFiles", () => {
338
272
  });
339
273
  it("returns all worker files when no routes are selected", () => {
340
274
  const excluded = excludedWorkerFiles([]);
341
- expect(excluded.size).toBe(8);
275
+ expect(excluded.size).toBe(6);
342
276
  expect(excluded.has("agent-refine.md")).toBe(true);
343
277
  expect(excluded.has("agent-implement.md")).toBe(true);
344
- expect(excluded.has("agent-direct.md")).toBe(true);
278
+ expect(excluded.has("agent-triage.md")).toBe(true);
345
279
  expect(excluded.has("agent-apply-review.md")).toBe(true);
346
280
  expect(excluded.has("agent-merge-gate.md")).toBe(true);
347
281
  expect(excluded.has("agent-audit.md")).toBe(true);
348
- expect(excluded.has("agent-propose.md")).toBe(true);
349
282
  });
350
283
  });
@@ -32,24 +32,24 @@ export function generateOpencodeCi(baseContent, inspection) {
32
32
  let result = baseContent;
33
33
  if (inspection.stackHints.solutionFiles.length > 0) {
34
34
  const solutionPath = inspection.stackHints.solutionFiles[0].replaceAll("\\", "/");
35
- const nugetSteps = ` - name: Cache NuGet packages
36
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
37
- with:
38
- path: ~/.nuget/packages
39
- key: nuget-\${{ runner.os }}-\${{ hashFiles('**/*.slnx', '**/Directory.Packages.props') }}
40
- restore-keys: nuget-\${{ runner.os }}-
41
-
42
- - name: Restore .NET dependencies
43
- run: dotnet restore ${solutionPath}
35
+ const nugetSteps = ` - name: Cache NuGet packages
36
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
37
+ with:
38
+ path: ~/.nuget/packages
39
+ key: nuget-\${{ runner.os }}-\${{ hashFiles('**/*.slnx', '**/Directory.Packages.props') }}
40
+ restore-keys: nuget-\${{ runner.os }}-
41
+
42
+ - name: Restore .NET dependencies
43
+ run: dotnet restore ${solutionPath}
44
44
  `;
45
45
  result = insertBeforeMarker(result, nugetSteps, " - name: Install workspace dependencies");
46
46
  }
47
47
  if (inspection.stackHints.openSpec) {
48
- const openspecStep = ` - name: Install OpenSpec CLI
49
- run: |
50
- set -euo pipefail
51
- npm install -g "@fission-ai/openspec@1.8.0"
52
- openspec --version
48
+ const openspecStep = ` - name: Install OpenSpec CLI
49
+ run: |
50
+ set -euo pipefail
51
+ npm install -g "@fission-ai/openspec@1.8.0"
52
+ openspec --version
53
53
  `;
54
54
  result = insertBeforeMarker(result, openspecStep, " - name: Install workspace dependencies");
55
55
  }