@plainconceptsplatform/workflows 0.1.0

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/dist/catalog-installation.d.ts +12 -0
  2. package/dist/catalog-installation.js +81 -0
  3. package/dist/catalog-installation.test.d.ts +1 -0
  4. package/dist/catalog-installation.test.js +85 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +57 -0
  7. package/dist/repository-inspection.d.ts +23 -0
  8. package/dist/repository-inspection.js +91 -0
  9. package/dist/repository-state.d.ts +18 -0
  10. package/dist/repository-state.js +77 -0
  11. package/dist/repository-state.test.d.ts +1 -0
  12. package/dist/repository-state.test.js +96 -0
  13. package/dist/workflow-catalog.d.ts +17 -0
  14. package/dist/workflow-catalog.js +39 -0
  15. package/dist/workflow-catalog.test.d.ts +1 -0
  16. package/dist/workflow-catalog.test.js +14 -0
  17. package/loops/actions/add-issue-labels/action.yml +29 -0
  18. package/loops/actions/agent-output.cjs +16 -0
  19. package/loops/actions/apply-agent-bundle/action.yml +23 -0
  20. package/loops/actions/apply-agent-bundle/apply-bundle.sh +45 -0
  21. package/loops/actions/apply-agent-comments/action.yml +41 -0
  22. package/loops/actions/apply-agent-labels/action.yml +54 -0
  23. package/loops/actions/apply-agent-output/action.yml +107 -0
  24. package/loops/actions/audit-close/action.yml +103 -0
  25. package/loops/actions/classify-route/action.yml +90 -0
  26. package/loops/actions/classify-route/classify-route.sh +172 -0
  27. package/loops/actions/cleanup-artifacts/action.yml +64 -0
  28. package/loops/actions/close-agent-issues/action.yml +42 -0
  29. package/loops/actions/create-agent-issues/action.yml +51 -0
  30. package/loops/actions/create-issue-comment/action.yml +28 -0
  31. package/loops/actions/download-agent-output/action.yml +52 -0
  32. package/loops/actions/identify-gate-subject/action.yml +96 -0
  33. package/loops/actions/link-pr-to-issue/action.yml +39 -0
  34. package/loops/actions/list-open-issues/action.yml +32 -0
  35. package/loops/actions/load-issue-context/action.yml +42 -0
  36. package/loops/actions/merge-agent-pr/action.yml +35 -0
  37. package/loops/actions/push-agent-branch/action.yml +44 -0
  38. package/loops/actions/remove-issue-labels/action.yml +34 -0
  39. package/loops/actions/stale-recovery/action.yml +287 -0
  40. package/loops/actions/update-agent-issues/action.yml +57 -0
  41. package/loops/actions/validate-refine-output/action.yml +43 -0
  42. package/loops/actions/validate-refine-output/validate-refine-output.sh +46 -0
  43. package/loops/actions/verify-composite-actions/action.yml +8 -0
  44. package/loops/actions/verify-composite-actions/verify-composite-actions.sh +50 -0
  45. package/loops/actions/verify-refine-output/action.yml +8 -0
  46. package/loops/actions/verify-refine-output/verify-refine-output.sh +65 -0
  47. package/loops/actions/verify-route-matrix/action.yml +8 -0
  48. package/loops/actions/verify-route-matrix/verify-route-matrix.sh +212 -0
  49. package/loops/aw/repo-config.md +12 -0
  50. package/loops/scripts/compile-agent-workflows.mjs +13 -0
  51. package/loops/workflows/agent-apply-review.md +372 -0
  52. package/loops/workflows/agent-audit.md +197 -0
  53. package/loops/workflows/agent-direct.md +362 -0
  54. package/loops/workflows/agent-implement.md +329 -0
  55. package/loops/workflows/agent-merge-gate.md +470 -0
  56. package/loops/workflows/agent-propose.md +342 -0
  57. package/loops/workflows/agent-refine.md +338 -0
  58. package/loops/workflows/shared/opencode-ci.md +26 -0
  59. package/loops/workflows/shared/platform-defaults.md +14 -0
  60. package/loops/workflows/work-router.yml +391 -0
  61. package/package.json +28 -0
@@ -0,0 +1,26 @@
1
+ ---
2
+ env:
3
+ AGENTMEMORY_VERSION: "0.9.28"
4
+ CODEGRAPH_VERSION: "1.5.0"
5
+ RTK_VERSION: "0.44.1"
6
+ RTK_SHA256: "986f29704469b3d1051e2474105c6c75ab8b73651068dcd61612c1fb3938ad95"
7
+ description: Shared CI setup for Platform agent workflows.
8
+
9
+ pre-agent-steps:
10
+ - name: Create agent scratch directory
11
+ run: mkdir -p .opencode/.tmp
12
+ - name: Activate the pnpm version package.json pins
13
+ run: |
14
+ set -euo pipefail
15
+ corepack enable
16
+ corepack prepare --activate
17
+ pnpm --version
18
+ - name: Cache the pnpm store
19
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
20
+ with:
21
+ path: ~/.local/share/pnpm/store
22
+ key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
23
+ restore-keys: pnpm-store-${{ runner.os }}-
24
+ - name: Install workspace dependencies
25
+ run: pnpm install --frozen-lockfile
26
+ ---
@@ -0,0 +1,14 @@
1
+ ---
2
+ env: {}
3
+ description: Shared network and safe-output defaults for catalog agent workflows.
4
+
5
+ network:
6
+ allowed:
7
+ - defaults
8
+ - forge.plainconcepts.com
9
+ - node
10
+ - github
11
+
12
+ safe-outputs:
13
+ threat-detection: false
14
+ ---
@@ -0,0 +1,391 @@
1
+ name: "All Work Router"
2
+
3
+ # Owns every repository automation trigger. Classifies each event into exactly one route,
4
+ # then calls at most one worker or runs one deterministic job. No model runs here.
5
+
6
+ # GitHub titles an issue-triggered run with the issue title, so every run on one issue reads
7
+ # the same. Name it after the event instead. This is evaluated before any job, so it can
8
+ # describe the trigger but not the route the classifier picks.
9
+ run-name: >-
10
+ ${{ github.event_name == 'issues'
11
+ && format('{0} label on #{1} by {2}', github.event.label.name, github.event.issue.number, github.actor)
12
+ || github.event_name == 'issue_comment'
13
+ && format('comment on #{0} by {1}', github.event.issue.number, github.actor)
14
+ || github.event_name == 'pull_request_review'
15
+ && format('review on PR #{0} by {1}', github.event.pull_request.number, github.actor)
16
+ || github.event_name == 'pull_request_review_comment'
17
+ && format('review comment on PR #{0} by {1}', github.event.pull_request.number, github.actor)
18
+ || github.event_name == 'pull_request_target'
19
+ && format('PR #{0} {1} by {2}', github.event.pull_request.number, github.event.action, github.actor)
20
+ || github.event_name == 'workflow_run'
21
+ && format('CI {0} on {1}', github.event.workflow_run.conclusion, github.event.workflow_run.head_branch)
22
+ || github.event_name == 'workflow_dispatch'
23
+ && format('dispatch: {0}', inputs.operation)
24
+ || github.event_name == 'schedule'
25
+ && format('schedule {0}', github.event.schedule)
26
+ || github.event_name }}
27
+
28
+ on:
29
+ issues:
30
+ types: [labeled]
31
+ issue_comment:
32
+ types: [created]
33
+ pull_request_review_comment:
34
+ types: [created]
35
+ pull_request_review:
36
+ types: [submitted]
37
+
38
+ pull_request_target:
39
+ types: [opened, synchronize, reopened]
40
+ workflow_run:
41
+ workflows: ["App: CI"]
42
+ types: [completed]
43
+ branches:
44
+ - "fix/*"
45
+ - "bugfix/*"
46
+ - "feature/*"
47
+ - "bot/*"
48
+ - "patch/*"
49
+ - "hotfix/*"
50
+
51
+ # Cron to route mapping lives in .github/actions/classify-route/classify-route.sh.
52
+ schedule:
53
+ - cron: "17 1 * * 1"
54
+ - cron: "43 3 * * *"
55
+ - cron: "0 6 * * *"
56
+ - cron: "0 */2 * * *"
57
+ - cron: "29 7 * * *"
58
+
59
+ workflow_dispatch:
60
+ inputs:
61
+ operation:
62
+ description: "Operation to run"
63
+ required: true
64
+ type: choice
65
+ options:
66
+ - refine
67
+ - implement
68
+ - direct
69
+ - apply-review
70
+ - merge-gate
71
+ - audit
72
+ - propose
73
+ - audit-close
74
+ - cleanup-artifacts
75
+ - stale-recovery
76
+ - validate
77
+ issue-number:
78
+ description: "Issue number (required for refine / implement / direct)"
79
+ required: false
80
+ type: string
81
+ pr-number:
82
+ description: "Pull request number (required for apply-review / merge-gate)"
83
+ required: false
84
+ type: string
85
+ mode:
86
+ description: "Refine mode: first or rerefine"
87
+ required: false
88
+ type: choice
89
+ options:
90
+ - first
91
+ - rerefine
92
+ ci-conclusion:
93
+ description: "CI conclusion for merge-gate dispatch"
94
+ required: false
95
+ type: choice
96
+ options:
97
+ - success
98
+ - failure
99
+ - action_required
100
+ ci-run-id:
101
+ description: "CI workflow run ID for merge-gate dispatch"
102
+ required: false
103
+ type: string
104
+ trigger-kind:
105
+ description: "Audit trigger: scheduled or manual"
106
+ required: false
107
+ type: choice
108
+ options:
109
+ - manual
110
+ - scheduled
111
+
112
+ permissions:
113
+ contents: read
114
+
115
+ jobs:
116
+ # Public repositories must check repository permission before an event can start a model.
117
+ authorize:
118
+ runs-on: ubuntu-latest
119
+ timeout-minutes: 5
120
+ permissions:
121
+ contents: read
122
+ outputs:
123
+ trusted: ${{ steps.check.outputs.trusted }}
124
+ steps:
125
+ - name: Check the actor can start an agent
126
+ id: check
127
+ env:
128
+ GH_TOKEN: ${{ github.token }}
129
+ ACTOR: ${{ github.actor }}
130
+ EVENT: ${{ github.event_name }}
131
+ run: |
132
+ set -euo pipefail
133
+
134
+ # Events with no human actor. `schedule` and `workflow_run` are the fleet triggering
135
+ # itself; `workflow_dispatch` is already restricted by GitHub to write and above.
136
+ case "$EVENT" in
137
+ schedule | workflow_run | workflow_dispatch)
138
+ echo "trusted=true" >> "$GITHUB_OUTPUT"
139
+ echo "::notice::$EVENT has no untrusted actor."
140
+ exit 0
141
+ ;;
142
+ esac
143
+
144
+ if [ "$ACTOR" = "github-actions[bot]" ]; then
145
+ echo "trusted=true" >> "$GITHUB_OUTPUT"
146
+ exit 0
147
+ fi
148
+
149
+ permission=$(gh api "repos/$GITHUB_REPOSITORY/collaborators/$ACTOR/permission" \
150
+ --jq '.permission' 2>/dev/null || echo "none")
151
+
152
+ case "$permission" in
153
+ admin | maintain | write)
154
+ echo "trusted=true" >> "$GITHUB_OUTPUT"
155
+ echo "::notice::$ACTOR has $permission permission."
156
+ ;;
157
+ *)
158
+ echo "trusted=false" >> "$GITHUB_OUTPUT"
159
+ echo "::notice::$ACTOR has '$permission' permission on this repository. No agent will run."
160
+ ;;
161
+ esac
162
+
163
+ classify:
164
+ runs-on: ubuntu-latest
165
+ timeout-minutes: 5
166
+ permissions:
167
+ contents: read
168
+ pull-requests: read
169
+ outputs:
170
+ route: ${{ steps.route.outputs.route }}
171
+ issue-number: ${{ steps.route.outputs.issue-number }}
172
+ pr-number: ${{ steps.route.outputs.pr-number }}
173
+ linked-issue: ${{ steps.route.outputs.linked-issue }}
174
+ ci-conclusion: ${{ steps.route.outputs.ci-conclusion }}
175
+ ci-run-id: ${{ steps.route.outputs.ci-run-id }}
176
+ refine-mode: ${{ steps.route.outputs.refine-mode }}
177
+ direct-mode: ${{ steps.route.outputs.direct-mode }}
178
+ trigger-kind: ${{ steps.route.outputs.trigger-kind }}
179
+ steps:
180
+ - name: Checkout workflow actions
181
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
182
+ with:
183
+ persist-credentials: false
184
+ - name: Classify event into a single route
185
+ id: route
186
+ uses: ./.github/actions/classify-route
187
+ with:
188
+ token: ${{ github.token }}
189
+
190
+ call-refine:
191
+ needs: [classify, authorize]
192
+ if: needs.classify.outputs.route == 'refine' && needs.authorize.outputs.trusted == 'true'
193
+ uses: ./.github/workflows/agent-refine.lock.yml
194
+ concurrency:
195
+ group: refine-${{ needs.classify.outputs.issue-number }}
196
+ cancel-in-progress: false
197
+ # write-all, not a narrower map: every worker's agent job declares `permissions: read-all`,
198
+ # which requests read on every scope. A caller granting less cannot satisfy it, and GitHub
199
+ # rejects the call with a startup failure before any job is created. The real boundary is the
200
+ # worker's own read-all plus safe-outputs, not this grant.
201
+ permissions: write-all
202
+ with:
203
+ issue-number: ${{ needs.classify.outputs.issue-number }}
204
+ mode: ${{ needs.classify.outputs.refine-mode }}
205
+ secrets:
206
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
207
+ CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
208
+ BOT_APP_ID: ${{ secrets.BOT_APP_ID }}
209
+ BOT_PRIVATE_KEY: ${{ secrets.BOT_PRIVATE_KEY }}
210
+
211
+ call-implement:
212
+ needs: [classify, authorize]
213
+ if: needs.classify.outputs.route == 'implement' && needs.authorize.outputs.trusted == 'true'
214
+ uses: ./.github/workflows/agent-implement.lock.yml
215
+ concurrency:
216
+ group: write-pipeline-${{ needs.classify.outputs.issue-number }}
217
+ cancel-in-progress: false
218
+ permissions: write-all
219
+ with:
220
+ issue-number: ${{ needs.classify.outputs.issue-number }}
221
+ secrets:
222
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
223
+ CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
224
+ BOT_APP_ID: ${{ secrets.BOT_APP_ID }}
225
+ BOT_PRIVATE_KEY: ${{ secrets.BOT_PRIVATE_KEY }}
226
+
227
+ call-direct:
228
+ needs: [classify, authorize]
229
+ if: needs.classify.outputs.route == 'direct' && needs.authorize.outputs.trusted == 'true'
230
+ uses: ./.github/workflows/agent-direct.lock.yml
231
+ concurrency:
232
+ group: direct-${{ needs.classify.outputs.issue-number }}
233
+ cancel-in-progress: false
234
+ permissions: write-all
235
+ with:
236
+ issue-number: ${{ needs.classify.outputs.issue-number }}
237
+ mode: ${{ needs.classify.outputs.direct-mode }}
238
+ secrets:
239
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
240
+ CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
241
+ BOT_APP_ID: ${{ secrets.BOT_APP_ID }}
242
+ BOT_PRIVATE_KEY: ${{ secrets.BOT_PRIVATE_KEY }}
243
+
244
+ call-apply-review:
245
+ needs: [classify, authorize]
246
+ if: needs.classify.outputs.route == 'apply-review' && needs.authorize.outputs.trusted == 'true'
247
+ uses: ./.github/workflows/agent-apply-review.lock.yml
248
+ concurrency:
249
+ group: pr-feedback-${{ needs.classify.outputs.pr-number }}
250
+ cancel-in-progress: false
251
+ permissions: write-all
252
+ with:
253
+ pr-number: ${{ needs.classify.outputs.pr-number }}
254
+ linked-issue: ${{ needs.classify.outputs.linked-issue }}
255
+ secrets:
256
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
257
+ CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
258
+ BOT_APP_ID: ${{ secrets.BOT_APP_ID }}
259
+ BOT_PRIVATE_KEY: ${{ secrets.BOT_PRIVATE_KEY }}
260
+
261
+ # Shares the write-pipeline group with implement so the two never race on one issue.
262
+ # Falls back to the pull request number only when the closing issue cannot be resolved.
263
+ call-merge-gate:
264
+ needs: classify
265
+ if: needs.classify.outputs.route == 'merge-gate'
266
+ uses: ./.github/workflows/agent-merge-gate.lock.yml
267
+ concurrency:
268
+ group: write-pipeline-${{ needs.classify.outputs.linked-issue || format('pr-{0}', needs.classify.outputs.pr-number) }}
269
+ cancel-in-progress: false
270
+ permissions: write-all
271
+ with:
272
+ pr-number: ${{ needs.classify.outputs.pr-number }}
273
+ linked-issue: ${{ needs.classify.outputs.linked-issue }}
274
+ ci-conclusion: ${{ needs.classify.outputs.ci-conclusion }}
275
+ ci-run-id: ${{ needs.classify.outputs.ci-run-id }}
276
+ secrets:
277
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
278
+ CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
279
+ BOT_APP_ID: ${{ secrets.BOT_APP_ID }}
280
+ BOT_PRIVATE_KEY: ${{ secrets.BOT_PRIVATE_KEY }}
281
+
282
+ call-audit:
283
+ needs: classify
284
+ if: needs.classify.outputs.route == 'audit'
285
+ uses: ./.github/workflows/agent-audit.lock.yml
286
+ concurrency:
287
+ group: audit
288
+ cancel-in-progress: false
289
+ permissions: write-all
290
+ with:
291
+ trigger-kind: ${{ needs.classify.outputs.trigger-kind }}
292
+ secrets:
293
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
294
+ CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
295
+ BOT_APP_ID: ${{ secrets.BOT_APP_ID }}
296
+ BOT_PRIVATE_KEY: ${{ secrets.BOT_PRIVATE_KEY }}
297
+
298
+ call-propose:
299
+ needs: classify
300
+ if: needs.classify.outputs.route == 'propose'
301
+ uses: ./.github/workflows/agent-propose.lock.yml
302
+ concurrency:
303
+ group: propose
304
+ cancel-in-progress: false
305
+ permissions: write-all
306
+ with:
307
+ trigger-kind: ${{ needs.classify.outputs.trigger-kind }}
308
+ secrets:
309
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
310
+ CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
311
+ BOT_APP_ID: ${{ secrets.BOT_APP_ID }}
312
+ BOT_PRIVATE_KEY: ${{ secrets.BOT_PRIVATE_KEY }}
313
+
314
+ audit-close:
315
+ needs: classify
316
+ if: needs.classify.outputs.route == 'audit-close'
317
+ runs-on: ubuntu-latest
318
+ timeout-minutes: 15
319
+ concurrency:
320
+ group: audit-close
321
+ cancel-in-progress: false
322
+ permissions:
323
+ contents: read
324
+ issues: write
325
+ steps:
326
+ - name: Checkout workflow actions
327
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
328
+ with:
329
+ persist-credentials: false
330
+ - uses: ./.github/actions/audit-close
331
+ with:
332
+ token: ${{ github.token }}
333
+
334
+ cleanup-artifacts:
335
+ needs: classify
336
+ if: needs.classify.outputs.route == 'cleanup-artifacts' && !github.event.repository.fork
337
+ runs-on: ubuntu-latest
338
+ timeout-minutes: 15
339
+ concurrency:
340
+ group: cleanup-artifacts
341
+ cancel-in-progress: false
342
+ permissions:
343
+ contents: read
344
+ actions: write
345
+ steps:
346
+ - name: Checkout workflow actions
347
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
348
+ with:
349
+ persist-credentials: false
350
+ - uses: ./.github/actions/cleanup-artifacts
351
+ with:
352
+ token: ${{ github.token }}
353
+ artifact-retention-days: ${{ vars.ARTIFACT_RETENTION_DAYS }}
354
+
355
+ stale-recovery:
356
+ needs: classify
357
+ if: needs.classify.outputs.route == 'stale-recovery'
358
+ runs-on: ubuntu-latest
359
+ timeout-minutes: 15
360
+ concurrency:
361
+ group: stale-recovery
362
+ cancel-in-progress: false
363
+ permissions:
364
+ contents: read
365
+ issues: write
366
+ pull-requests: read
367
+ actions: write
368
+ steps:
369
+ - name: Checkout workflow actions
370
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
371
+ with:
372
+ persist-credentials: false
373
+ - uses: ./.github/actions/stale-recovery
374
+ with:
375
+ token: ${{ github.token }}
376
+ stale-threshold-hours: ${{ vars.STALE_THRESHOLD_HOURS }}
377
+
378
+ validate:
379
+ needs: classify
380
+ if: needs.classify.outputs.route == 'validate'
381
+ runs-on: ubuntu-latest
382
+ timeout-minutes: 10
383
+ permissions:
384
+ contents: read
385
+ steps:
386
+ - name: Checkout workflow actions
387
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
388
+ with:
389
+ persist-credentials: false
390
+ - uses: ./.github/actions/verify-route-matrix
391
+ - uses: ./.github/actions/verify-composite-actions
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@plainconceptsplatform/workflows",
3
+ "version": "0.1.0",
4
+ "description": "Install and update Platform GitHub agentic workflows.",
5
+ "type": "module",
6
+ "bin": {
7
+ "platform-workflows": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "loops"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc --project tsconfig.json",
15
+ "test": "vitest run",
16
+ "typecheck": "tsc --noEmit --project tsconfig.json",
17
+ "release": "pnpm build && pnpm test && npm publish --access public",
18
+ "prepack": "node ./scripts/copy-loops.mjs"
19
+ },
20
+ "engines": {
21
+ "node": ">=20.19"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.0.0",
25
+ "typescript": "^5.8.0",
26
+ "vitest": "^3.0.0"
27
+ }
28
+ }