@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.
- package/README.md +88 -88
- package/dist/action-validation.test.js +5 -2
- package/dist/catalog-installation.js +50 -7
- package/dist/catalog-installation.test.js +59 -22
- package/dist/catalog-listing.test.js +4 -4
- package/dist/index.js +48 -48
- package/dist/index.test.js +9 -9
- package/dist/repository-inspection.test.js +2 -2
- package/dist/route-processing.js +24 -25
- package/dist/route-processing.test.js +166 -233
- package/dist/stack-defaults.js +14 -14
- package/dist/stack-defaults.test.js +79 -79
- package/dist/tui.test.js +10 -7
- package/dist/workflow-catalog.d.ts +1 -1
- package/dist/workflow-catalog.js +1 -5
- package/loops/actions/add-issue-labels/action.yml +2 -2
- package/loops/actions/agent-output.cjs +17 -17
- package/loops/actions/apply-agent-bundle/apply-bundle.sh +14 -1
- package/loops/actions/audit-close/action.yml +24 -0
- package/loops/actions/classify-route/action.yml +8 -3
- package/loops/actions/classify-route/classify-route.sh +58 -38
- package/loops/actions/cleanup-artifacts/action.yml +38 -12
- package/loops/actions/identify-gate-subject/action.yml +3 -1
- package/loops/actions/remove-issue-labels/action.yml +4 -2
- package/loops/actions/update-changelog/action.yml +113 -0
- package/loops/actions/validate-merge-gate-output/action.yml +40 -40
- package/loops/actions/validate-refine-output/validate-refine-output.sh +34 -5
- package/loops/actions/validate-review-output/action.yml +35 -35
- package/loops/actions/validate-triage-output/action.yml +36 -36
- package/loops/actions/verify-refine-output/verify-refine-output.sh +20 -0
- package/loops/actions/verify-route-matrix/verify-route-matrix.sh +375 -36
- package/loops/scripts/compile-agent-workflows.mjs +290 -6
- package/loops/scripts/merge-changelog.mjs +76 -0
- package/loops/templates/agentics/actionlint.yaml +13 -0
- package/loops/templates/agentics/agentics-checks.yml +125 -8
- package/loops/templates/agentics/agentics-maintenance.yml +121 -121
- package/loops/templates/ci/app-ci-dotnet-next.yml +330 -171
- package/loops/templates/ci/app-ci-node-monorepo.yml +260 -178
- package/loops/templates/issues/bug_report.yml +109 -109
- package/loops/templates/issues/feature_request.yml +75 -75
- package/loops/templates/opencode/opencode.ci.json +49 -47
- package/loops/templates/opencode/opencode.ci.json.md +49 -41
- package/loops/templates/release/github-release.yml +30 -30
- package/loops/workflows/agent-apply-review.md +469 -443
- package/loops/workflows/agent-audit.md +213 -197
- package/loops/workflows/agent-implement.md +640 -414
- package/loops/workflows/agent-merge-gate.md +844 -584
- package/loops/workflows/agent-refine.md +633 -418
- package/loops/workflows/agent-release.md +258 -0
- package/loops/workflows/agent-triage.md +447 -439
- package/loops/workflows/authorize-bot-work.yml +85 -82
- package/loops/workflows/shared/opencode-ci.md +206 -197
- package/loops/workflows/shared/platform-defaults.md +19 -16
- package/loops/workflows/work-router.yml +1038 -632
- package/package.json +7 -8
- package/dist/repository-state.d.ts +0 -18
- package/dist/repository-state.js +0 -77
- package/dist/repository-state.test.d.ts +0 -1
- package/dist/repository-state.test.js +0 -96
- package/loops/workflows/agent-direct.md +0 -375
- 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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"\$
|
|
96
|
-
"\$
|
|
97
|
-
"\$
|
|
98
|
-
"\$
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
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
|
|
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: "
|
|
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
|
|
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('"$
|
|
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, "
|
|
241
|
-
expect(result).toContain("excluded route '
|
|
242
|
-
expect(result).toContain("
|
|
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, "
|
|
246
|
-
const twice = addRouteExclusion(once, "
|
|
247
|
-
const matchCount = (twice.match(/excluded route '
|
|
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
|
|
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(
|
|
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-
|
|
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
|
});
|
package/dist/stack-defaults.js
CHANGED
|
@@ -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
|
}
|