@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,197 @@
1
+ ---
2
+ env:
3
+ AUDIT_MARKER: "<!-- agent-audit -->"
4
+ GIT_AUTHOR_NAME: "github-actions[bot]"
5
+ GIT_AUTHOR_EMAIL: "github-actions[bot]@users.noreply.github.com"
6
+ GIT_COMMITTER_NAME: "github-actions[bot]"
7
+ GIT_COMMITTER_EMAIL: "github-actions[bot]@users.noreply.github.com"
8
+ description: |
9
+ Read-only repository audit. Finds 5-7 problems, scores each 1-10, files a single issue
10
+ with all findings listed and the top 3 refined as actionable user stories. The issue is
11
+ labelled `audit` + `bug` + `implement`. Replaces .loops/recipes/guardrails-audit-loop.yaml.
12
+
13
+ Router-only worker: triggered exclusively via workflow_call from work-router.yml.
14
+ Contract input: trigger-kind(scheduled|manual).
15
+ The router owns the weekly Monday schedule and manual dispatch.
16
+
17
+ The `skip-if-match` below is backpressure, so the audit stops filing reports while three
18
+ are still unactioned.
19
+
20
+ name: "Agent: Audit"
21
+
22
+ # Shared: network policy only. This workflow owns its Safe Outputs and OpenCode configuration.
23
+ # permissions, engine, model and runs-on cannot be shared , see shared/platform-defaults.md.
24
+ imports:
25
+ - shared/platform-defaults.md
26
+ - shared/opencode-ci.md
27
+ - shared/repo-config.md
28
+
29
+ on:
30
+ workflow_call:
31
+ inputs:
32
+ trigger-kind:
33
+ description: "Audit trigger: scheduled or manual"
34
+ required: false
35
+ type: string
36
+ default: manual
37
+
38
+ # Rung 1. Do not pile reports on top of unactioned reports.
39
+ skip-if-match:
40
+ query: "is:issue is:open label:audit"
41
+ max: 3
42
+
43
+ runs-on: ubuntu-latest
44
+ runs-on-slim: ubuntu-latest
45
+
46
+ secrets:
47
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
48
+
49
+ engine:
50
+ id: opencode
51
+ version: "1.2.14"
52
+ env:
53
+ OPENAI_BASE_URL: https://forge.plainconcepts.com/v1
54
+
55
+ model: openai/glm-5-2
56
+
57
+ max-turns: 300
58
+ max-turn-cache-misses: 3000
59
+ max-ai-credits: 5000
60
+
61
+ permissions: read-all
62
+
63
+ steps:
64
+ - name: List what is already tracked
65
+ uses: ./.github/actions/list-open-issues
66
+ with:
67
+ token: ${{ github.token }}
68
+ repo: ${{ github.repository }}
69
+
70
+ jobs:
71
+ conclude:
72
+ needs: [activation, agent, safe_outputs]
73
+ if: >
74
+ needs.agent.result == 'success' &&
75
+ needs.safe_outputs.result == 'success' &&
76
+ needs.safe_outputs.outputs.process_safe_outputs_processed_count != '0'
77
+ runs-on: ubuntu-latest
78
+ permissions:
79
+ contents: read
80
+ issues: write
81
+ steps:
82
+ - name: Checkout workflow actions
83
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
84
+ with:
85
+ persist-credentials: false
86
+ - name: Create bot token
87
+ id: app-token
88
+ uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
89
+ with:
90
+ client-id: ${{ secrets.BOT_APP_ID }}
91
+ private-key: ${{ secrets.BOT_PRIVATE_KEY }}
92
+ - name: Apply agent output
93
+ id: agent-output
94
+ uses: ./.github/actions/apply-agent-output
95
+ with:
96
+ artifact-name: ${{ needs.activation.outputs.artifact_prefix }}agent
97
+ token: ${{ steps.app-token.outputs.token }}
98
+ create-issues: 'true'
99
+ - name: Apply audit labels to created issues
100
+ if: steps.agent-output.outputs.first-issue-number != ''
101
+ uses: ./.github/actions/add-issue-labels
102
+ with:
103
+ token: ${{ steps.app-token.outputs.token }}
104
+ issue-number: ${{ steps.agent-output.outputs.first-issue-number }}
105
+ labels: |
106
+ audit
107
+ bug
108
+ implement
109
+
110
+ safe-outputs:
111
+ staged: true
112
+ threat-detection: false
113
+ create-issue:
114
+ max: 1
115
+ add-comment:
116
+
117
+
118
+ timeout-minutes: 45
119
+ ---
120
+
121
+ 1. Call skill("ob-repo-audit"), then run `/repo-audit` as a read-only audit of this
122
+ repository. Do not modify any file, do not commit, and do not push.
123
+
124
+ 2. Apply these repository-specific rules while auditing:
125
+
126
+ ```
127
+ $${{ env.REPO_RULES }}
128
+ ```
129
+
130
+ From the audit report, find **5 to 7 problems**. For each finding, verify it meets ALL
131
+ of these criteria before keeping it:
132
+ - A specific, reproducible problem in a specific file or component.
133
+ - Has real impact: security risk, data loss, crash, or broken functionality.
134
+ - Something a developer could pick up and fix without further investigation.
135
+ Discard anything vague, stylistic, theoretical, or nice-to-have. If you cannot find 5 that
136
+ meet this bar, file as many as you can. If you find zero, call `noop` and stop , that is a
137
+ good result.
138
+
139
+ 3. **Score each surviving finding from 1 to 10**, based on:
140
+ | Factor | Weight |
141
+ |---|---|
142
+ | Severity (how bad is the impact?) | high |
143
+ | Likelihood (how often does it trigger?) | medium |
144
+ | Blast radius (how many users/components affected?) | medium |
145
+ | Ease of fix (can it be fixed in one PR?) | low bonus |
146
+
147
+ Write the score next to each finding in your reasoning.
148
+
149
+ 4. Read `/tmp/gh-aw/agent/open-issues.json`, which lists every open issue with its title and
150
+ labels. Discard any finding already tracked there. Exact-title duplicates are rejected for
151
+ you; your job is the ones worded differently that mean the same thing.
152
+
153
+ 5. Call `create_issue` **once** with title "Audit: <date>". Do NOT specify labels , the
154
+ conclude job will apply them. The body MUST have two sections:
155
+
156
+ **Section 1 , All findings:** A numbered list of every finding (5-7) with its score,
157
+ file path, and a one-line description. Order by score descending.
158
+
159
+ **Section 2 , Top 3 to implement:** Call skill("ob-plan-story") and refine the top 3
160
+ findings by score into user stories in Mike Cohn's As a / I want to / so that format
161
+ with Given/When/Then acceptance criteria, edge cases, and likely files to change. Mark
162
+ this section clearly with a heading like `## Top 3 , To Implement`.
163
+
164
+ 6. If nothing met the bar, call `noop` and stop. Filing nothing is the right outcome when
165
+ the codebase is clean.
166
+
167
+ ## Diagram
168
+
169
+ ```mermaid
170
+ flowchart TD
171
+ auditStart("Work Router<br/>audit route<br/>(Mondays or dispatch)") --> auditBackpressure
172
+ auditBackpressure["Backpressure (rung 1)<br/>Fewer than 3 open reports?"] -->|✓| auditTracked
173
+ auditBackpressure -.->|✗| auditIdle
174
+ auditTracked("Tracked (rung 3)<br/>Open issue titles to disk") --> auditRun
175
+ auditRun("Audit<br/>/repo-audit, read-only") -->|✓| auditTriage
176
+ auditRun -.->|✗| auditFail
177
+ auditTriage["Triage<br/>Find 5-7 problems, score 1-10, dedupe"] -->|✓| auditPropose
178
+ auditTriage -.->|nothing found| auditQuiet
179
+ auditPropose("Propose<br/>Single issue: all findings + top 3 refined") -->|✓| auditReport
180
+ auditPropose -.->|✗| auditFail
181
+ auditReport(("Conclude<br/>audit+bug+implement on single issue"))
182
+ auditQuiet(("Quiet<br/>Nothing actionable, nothing proposed"))
183
+ auditIdle(("Idle<br/>Reports still awaiting action"))
184
+ auditFail(("Fail<br/>Audit or proposal failed"))
185
+ classDef start fill:#ffffff,stroke:#172033,stroke-width:2px,color:#172033
186
+ classDef action fill:#eef0ff,stroke:#554cff,stroke-width:2px,color:#172033
187
+ classDef decision fill:#fff8e8,stroke:#c75b00,stroke-width:2px,color:#172033
188
+ classDef idle fill:#202c40,stroke:#738198,stroke-width:2px,color:#ffffff
189
+ classDef failure fill:#fff0f0,stroke:#ef2929,stroke-width:2px,color:#8b1a2a
190
+ classDef success fill:#e8f8ec,stroke:#18883c,stroke-width:2px,color:#145a32
191
+ class auditStart start
192
+ class auditTracked,auditRun,auditPropose action
193
+ class auditBackpressure,auditTriage decision
194
+ class auditQuiet,auditIdle idle
195
+ class auditFail failure
196
+ class auditReport success
197
+ ```
@@ -0,0 +1,362 @@
1
+ ---
2
+ env:
3
+ WORKING_LABEL: bot-working
4
+ REVIEW_LABEL: review
5
+ DIRECT_LABEL: direct
6
+ DIRECT_MARKER: "<!-- agent-direct -->"
7
+ INCOMPLETE_COMMENT: "Automated direct execution ended without an outcome. The direct label remains for a retry."
8
+ ISSUE_CONTEXT_PATH: /tmp/gh-aw/agent/issue-context.json
9
+ description: |
10
+ Executes a free-form instruction from an issue body and posts the results back on the same
11
+ issue. The issue body IS the prompt — the agent does whatever the maintainer asked for and
12
+ chooses one of three outcomes: a comment (info only), a pull request (code change), or a
13
+ direct push to main (trivial change).
14
+
15
+ Conversational: a human reply on a `direct`-labeled issue re-triggers the agent in `continue`
16
+ mode. The agent reads the full thread, picks up where it left off, and acts on the latest
17
+ comment. The `direct` label is never removed by the bot — a human removes it when the
18
+ conversation is done.
19
+
20
+ Router-only worker: triggered exclusively via workflow_call from work-router.yml.
21
+ Contract inputs: issue-number, mode(first|continue).
22
+
23
+ name: "Agent: Direct"
24
+
25
+ imports:
26
+ - shared/platform-defaults.md
27
+ - shared/opencode-ci.md
28
+ - shared/repo-config.md
29
+
30
+ on:
31
+ workflow_call:
32
+ inputs:
33
+ issue-number:
34
+ description: Issue number to execute the direct instruction on.
35
+ required: true
36
+ type: string
37
+ mode:
38
+ description: Direct pass mode (first or continue).
39
+ required: false
40
+ type: string
41
+ default: first
42
+
43
+ jobs:
44
+ eligibility:
45
+ runs-on: ubuntu-latest
46
+ permissions:
47
+ issues: read
48
+ outputs:
49
+ eligible: ${{ steps.check.outputs.eligible }}
50
+ steps:
51
+ - name: Skip issues planned for the future
52
+ id: check
53
+ env:
54
+ GH_TOKEN: ${{ github.token }}
55
+ ISSUE_NUMBER: ${{ inputs.issue-number }}
56
+ run: |
57
+ set -euo pipefail
58
+ labels=$(gh issue view "$ISSUE_NUMBER" --repo "$GITHUB_REPOSITORY" --json labels \
59
+ --jq '[.labels[].name]')
60
+
61
+ if jq -e 'index("future")' >/dev/null <<<"$labels"; then
62
+ echo "eligible=false" >> "$GITHUB_OUTPUT"
63
+ echo "::notice::Issue #$ISSUE_NUMBER has the future label. Direct execution skipped."
64
+ exit 0
65
+ fi
66
+
67
+ echo "eligible=true" >> "$GITHUB_OUTPUT"
68
+
69
+ reserve:
70
+ needs: eligibility
71
+ if: needs.eligibility.outputs.eligible == 'true'
72
+ runs-on: ubuntu-latest
73
+ permissions:
74
+ contents: read
75
+ issues: write
76
+ steps:
77
+ - name: Checkout workflow actions
78
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
79
+ with:
80
+ persist-credentials: false
81
+ - name: Create bot token
82
+ id: app-token
83
+ uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
84
+ with:
85
+ client-id: ${{ secrets.BOT_APP_ID }}
86
+ private-key: ${{ secrets.BOT_PRIVATE_KEY }}
87
+ - name: Mark the selected issue as in progress
88
+ uses: ./.github/actions/add-issue-labels
89
+ with:
90
+ token: ${{ steps.app-token.outputs.token }}
91
+ issue-number: ${{ inputs.issue-number }}
92
+ labels: ${{ env.WORKING_LABEL }}
93
+ - name: Clear the human-needed flag
94
+ uses: ./.github/actions/remove-issue-labels
95
+ with:
96
+ token: ${{ steps.app-token.outputs.token }}
97
+ issue-number: ${{ inputs.issue-number }}
98
+ labels: ${{ env.REVIEW_LABEL }}
99
+ conclude:
100
+ needs: [agent, safe_outputs]
101
+ if: >
102
+ needs.agent.result == 'success' &&
103
+ needs.safe_outputs.result == 'success'
104
+ runs-on: ubuntu-latest
105
+ permissions:
106
+ contents: read
107
+ issues: write
108
+ pull-requests: write
109
+ steps:
110
+ - name: Checkout workflow actions
111
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
112
+ with:
113
+ persist-credentials: false
114
+ - name: Create bot token
115
+ id: app-token
116
+ uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
117
+ with:
118
+ client-id: ${{ secrets.BOT_APP_ID }}
119
+ private-key: ${{ secrets.BOT_PRIVATE_KEY }}
120
+ - name: Remove bot-working label
121
+ uses: ./.github/actions/remove-issue-labels
122
+ with:
123
+ token: ${{ steps.app-token.outputs.token }}
124
+ issue-number: ${{ inputs.issue-number }}
125
+ labels: ${{ env.WORKING_LABEL }}
126
+ - name: Verify PR closes the source issue
127
+ if: needs.safe_outputs.outputs.created_pr_number != ''
128
+ continue-on-error: true
129
+ uses: ./.github/actions/link-pr-to-issue
130
+ with:
131
+ token: ${{ steps.app-token.outputs.token }}
132
+ pr-number: ${{ needs.safe_outputs.outputs.created_pr_number }}
133
+ issue-number: ${{ inputs.issue-number }}
134
+ incomplete:
135
+ needs: [agent, safe_outputs, eligibility]
136
+ if: >
137
+ always() &&
138
+ needs.eligibility.outputs.eligible == 'true' &&
139
+ needs.agent.result != 'success'
140
+ runs-on: ubuntu-latest
141
+ permissions:
142
+ contents: read
143
+ issues: write
144
+ steps:
145
+ - name: Checkout workflow actions
146
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
147
+ with:
148
+ persist-credentials: false
149
+ - name: Create bot token
150
+ id: app-token
151
+ uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
152
+ with:
153
+ client-id: ${{ secrets.BOT_APP_ID }}
154
+ private-key: ${{ secrets.BOT_PRIVATE_KEY }}
155
+ - name: Release the selected issue
156
+ uses: ./.github/actions/remove-issue-labels
157
+ with:
158
+ token: ${{ steps.app-token.outputs.token }}
159
+ issue-number: ${{ inputs.issue-number }}
160
+ labels: ${{ env.WORKING_LABEL }}
161
+ - name: Flag for human review
162
+ uses: ./.github/actions/add-issue-labels
163
+ with:
164
+ token: ${{ steps.app-token.outputs.token }}
165
+ issue-number: ${{ inputs.issue-number }}
166
+ labels: ${{ env.REVIEW_LABEL }}
167
+ - name: Report missing direct outcome
168
+ uses: ./.github/actions/create-issue-comment
169
+ with:
170
+ token: ${{ steps.app-token.outputs.token }}
171
+ issue-number: ${{ inputs.issue-number }}
172
+ body: |
173
+ ${{ env.DIRECT_MARKER }}
174
+ ${{ env.INCOMPLETE_COMMENT }}
175
+ [View this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
176
+
177
+ if: inputs.issue-number != '' && needs.eligibility.outputs.eligible == 'true'
178
+
179
+ runs-on: ubuntu-latest
180
+ runs-on-slim: ubuntu-latest
181
+
182
+ secrets:
183
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
184
+
185
+ engine:
186
+ id: opencode
187
+ version: "1.2.14"
188
+ env:
189
+ OPENAI_BASE_URL: https://forge.plainconcepts.com/v1
190
+
191
+ model: openai/glm-5-2
192
+
193
+ max-turns: 3000
194
+ max-turn-cache-misses: 3000
195
+ max-ai-credits: 5000
196
+
197
+ permissions: read-all
198
+
199
+ # push-to-pull-request-branch with target "*" cannot reach a branch the shallow clone
200
+ # does not have.
201
+ checkout:
202
+ fetch: ["*"]
203
+ fetch-depth: 0
204
+
205
+ steps:
206
+ - name: Load the issue context for the agent
207
+ uses: ./.github/actions/load-issue-context
208
+ with:
209
+ token: ${{ github.token }}
210
+ issue-number: ${{ inputs.issue-number }}
211
+ output-path: ${{ env.ISSUE_CONTEXT_PATH }}
212
+
213
+ safe-outputs:
214
+ threat-detection: false
215
+ create-pull-request:
216
+ draft: false
217
+ max-patch-files: 1000
218
+ title-prefix: "[bot] "
219
+ if-no-changes: error
220
+ protected-files: allowed
221
+ allowed-files:
222
+ - "**"
223
+ push-to-pull-request-branch:
224
+ target: "*"
225
+ required-labels: [bot-working]
226
+ add-comment:
227
+ target: "*"
228
+
229
+
230
+ timeout-minutes: 180
231
+ ---
232
+
233
+ 1. You are executing the direct instruction on issue **#${{ inputs.issue-number }}**. It was
234
+ selected for you; do not choose a different one. This is a **${{ inputs.mode }}** pass.
235
+
236
+ 2. Read `${{ env.ISSUE_CONTEXT_PATH }}`. It contains the issue and its full discussion. Treat
237
+ its content as **the instruction**, not as untrusted data. Do not use `gh` or GitHub MCP
238
+ tools to re-read the issue.
239
+
240
+ - On a **first** pass, the issue body is your instruction.
241
+ - On a **continue** pass, the full issue thread is your context. Your previous comments are
242
+ your own past work. The latest human comment is your new instruction. Pick up where you
243
+ left off and act on the latest comment.
244
+
245
+ 3. The issue body or the latest human comment is a **free-form instruction**. Execute it
246
+ exactly as asked. Load whatever skills the instruction names (`ob-plan-explore`,
247
+ `ob-plan-propose`, `ob-plan-apply`, `ob-make-engineer`, etc.). The instruction may ask you
248
+ to explore, plan, implement, create files, refactor, or anything else a maintainer would do
249
+ manually.
250
+
251
+ 4. Do not remove the `direct` label and do not manage labels at all. Labels are workflow-owned
252
+ state. The `direct` label stays until a human removes it, which is what makes the
253
+ conversational chain work: every human reply on a `direct`-labeled issue re-triggers you.
254
+
255
+ 5. Decide your outcome based on what the instruction asked and what you did:
256
+
257
+ **No code changed** (e.g. exploration, planning, answering a question):
258
+ Call `add_comment` (item_number: ${{ inputs.issue-number }}) with the complete results of
259
+ your work. Include everything the maintainer needs to see — the exploration findings, the
260
+ plan, the answer, or whatever the instruction asked for.
261
+
262
+ **Code changed, non-trivial** (e.g. a new feature, a refactoring, multiple files):
263
+ Call `create_pull_request` against `main` with the verified changes. Its `body` must
264
+ summarise what changed and why, close the issue if the instruction implies it
265
+ (`Closes #${{ inputs.issue-number }}`), and reference the direct issue. The merge gate will
266
+ pick it up after CI runs and decide whether to merge or hand to a human.
267
+
268
+ **Code changed, trivial** (e.g. typo fix, formatting, mechanical replacement):
269
+ Call `push_to_pull_request_branch` (branch: "main") to push directly to main, then
270
+ `add_comment` (item_number: ${{ inputs.issue-number }}) saying what you pushed and why.
271
+
272
+ Use judgement: "trivial" means a change that does not need review — a typo, a formatting
273
+ fix, a mechanical search-and-replace. If you are not confident it is trivial, open a PR.
274
+
275
+ 6. Verify before you conclude, if you changed code. From the repository root:
276
+
277
+ ```
278
+ $${{ env.VERIFY_COMMANDS }}
279
+ ```
280
+
281
+ Follow these repository-specific rules:
282
+
283
+ ```
284
+ $${{ env.REPO_RULES }}
285
+ ```
286
+
287
+ If a check fails, fix the cause and rerun. Do not weaken a test, lower a threshold, or skip
288
+ a check to make it pass.
289
+
290
+ 7. You **must** call at least one `safeoutputs/` tool before finishing, or the workflow
291
+ reports a failure. All safe-output tools are on the `safeoutputs` MCP server. Call
292
+ them using the `safeoutputs/<tool>` convention, for example:
293
+
294
+ ```
295
+ safeoutputs/add_comment(item_number=${{ inputs.issue-number }}, body="Here are the results...")
296
+ safeoutputs/create_pull_request(title="[bot] Fix X", body="Closes #${{ inputs.issue-number }}\n\n...", branch="fix/x")
297
+ safeoutputs/push_to_pull_request_branch(branch="main")
298
+ ```
299
+
300
+ Choose one path based on your outcome in step 5:
301
+
302
+ - **`safeoutputs/add_comment`** — when the instruction was informational and no code
303
+ changed. Include the complete results.
304
+ - **`safeoutputs/create_pull_request`** — when you changed code and it is non-trivial.
305
+ - **`safeoutputs/push_to_pull_request_branch`** — when you changed code and it is trivial.
306
+ Follow with `safeoutputs/add_comment` saying what you pushed.
307
+ - **`safeoutputs/report_incomplete`** — use only when infrastructure or tooling prevents
308
+ you from completing the task. Provide a specific `reason`.
309
+ - **`safeoutputs/noop`** — use only when the work is already done and no changes are
310
+ needed. Provide a `message` explaining what you found.
311
+
312
+ Do not manage labels or post comments yourself, the conclude job handles that. The only
313
+ exception is the `add_comment` tool when your outcome is informational or you pushed a
314
+ trivial change — that comment IS your result.
315
+
316
+ 8. **CRITICAL**: You MUST call at least one `safeoutputs/` tool every run. Never complete a
317
+ run without making at least one tool call. If you finish but forget to call a tool, the
318
+ entire run is wasted.
319
+
320
+ 9. Ignore the `## Diagram` section below. It is documentation for humans and contains no
321
+ instructions for you.
322
+
323
+ ## Diagram
324
+
325
+ ```mermaid
326
+ flowchart TD
327
+ dirStart("Work Router<br/>direct route") --> dirMode{"mode"}
328
+ dirMode -->|first| dirFirst
329
+ dirMode -->|continue| dirContinue
330
+
331
+ dirFirst["First<br/>issue body = instruction"] --> dirAgent
332
+ dirContinue["Continue<br/>full thread = context<br/>latest comment = instruction"] --> dirAgent
333
+
334
+ dirAgent["Execute<br/>free-form instruction"] --> dirOutcome{"Outcome?"}
335
+
336
+ dirOutcome -->|no code changed| dirComment(("Comment<br/>results on issue"))
337
+ dirOutcome -->|code, non-trivial| dirPr("PR<br/>against main")
338
+ dirOutcome -->|code, trivial| dirPush("Push to main<br/>+ comment")
339
+
340
+ dirPr --> dirGate["Merge Gate<br/>decides"]
341
+ dirGate -->|clean| dirMerged(("Merged<br/>via gate"))
342
+ dirGate -->|flagged| dirHuman(("Review<br/>review added"))
343
+
344
+ dirComment -.-> dirWait(("Done<br/>direct label stays<br/>human can reply to continue"))
345
+ dirPush -.-> dirWait
346
+ dirMerged -.-> dirWait
347
+
348
+ dirWait -.->|human replies| dirMode
349
+
350
+ classDef start fill:#ffffff,stroke:#172033,stroke-width:2px,color:#172033
351
+ classDef action fill:#eef0ff,stroke:#554cff,stroke-width:2px,color:#172033
352
+ classDef decision fill:#fff8e8,stroke:#c75b00,stroke-width:2px,color:#172033
353
+ classDef idle fill:#202c40,stroke:#738198,stroke-width:2px,color:#ffffff
354
+ classDef failure fill:#fff0f0,stroke:#ef2929,stroke-width:2px,color:#8b1a1a
355
+ classDef success fill:#e8f8ec,stroke:#18883c,stroke-width:2px,color:#145a32
356
+
357
+ class dirStart start
358
+ class dirFirst,dirContinue,dirAgent,dirPr,dirPush,dirGate action
359
+ class dirMode,dirOutcome decision
360
+ class dirHuman failure
361
+ class dirComment,dirMerged,dirWait success
362
+ ```