@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.
- package/dist/catalog-installation.d.ts +12 -0
- package/dist/catalog-installation.js +81 -0
- package/dist/catalog-installation.test.d.ts +1 -0
- package/dist/catalog-installation.test.js +85 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +57 -0
- package/dist/repository-inspection.d.ts +23 -0
- package/dist/repository-inspection.js +91 -0
- package/dist/repository-state.d.ts +18 -0
- package/dist/repository-state.js +77 -0
- package/dist/repository-state.test.d.ts +1 -0
- package/dist/repository-state.test.js +96 -0
- package/dist/workflow-catalog.d.ts +17 -0
- package/dist/workflow-catalog.js +39 -0
- package/dist/workflow-catalog.test.d.ts +1 -0
- package/dist/workflow-catalog.test.js +14 -0
- package/loops/actions/add-issue-labels/action.yml +29 -0
- package/loops/actions/agent-output.cjs +16 -0
- package/loops/actions/apply-agent-bundle/action.yml +23 -0
- package/loops/actions/apply-agent-bundle/apply-bundle.sh +45 -0
- package/loops/actions/apply-agent-comments/action.yml +41 -0
- package/loops/actions/apply-agent-labels/action.yml +54 -0
- package/loops/actions/apply-agent-output/action.yml +107 -0
- package/loops/actions/audit-close/action.yml +103 -0
- package/loops/actions/classify-route/action.yml +90 -0
- package/loops/actions/classify-route/classify-route.sh +172 -0
- package/loops/actions/cleanup-artifacts/action.yml +64 -0
- package/loops/actions/close-agent-issues/action.yml +42 -0
- package/loops/actions/create-agent-issues/action.yml +51 -0
- package/loops/actions/create-issue-comment/action.yml +28 -0
- package/loops/actions/download-agent-output/action.yml +52 -0
- package/loops/actions/identify-gate-subject/action.yml +96 -0
- package/loops/actions/link-pr-to-issue/action.yml +39 -0
- package/loops/actions/list-open-issues/action.yml +32 -0
- package/loops/actions/load-issue-context/action.yml +42 -0
- package/loops/actions/merge-agent-pr/action.yml +35 -0
- package/loops/actions/push-agent-branch/action.yml +44 -0
- package/loops/actions/remove-issue-labels/action.yml +34 -0
- package/loops/actions/stale-recovery/action.yml +287 -0
- package/loops/actions/update-agent-issues/action.yml +57 -0
- package/loops/actions/validate-refine-output/action.yml +43 -0
- package/loops/actions/validate-refine-output/validate-refine-output.sh +46 -0
- package/loops/actions/verify-composite-actions/action.yml +8 -0
- package/loops/actions/verify-composite-actions/verify-composite-actions.sh +50 -0
- package/loops/actions/verify-refine-output/action.yml +8 -0
- package/loops/actions/verify-refine-output/verify-refine-output.sh +65 -0
- package/loops/actions/verify-route-matrix/action.yml +8 -0
- package/loops/actions/verify-route-matrix/verify-route-matrix.sh +212 -0
- package/loops/aw/repo-config.md +12 -0
- package/loops/scripts/compile-agent-workflows.mjs +13 -0
- package/loops/workflows/agent-apply-review.md +372 -0
- package/loops/workflows/agent-audit.md +197 -0
- package/loops/workflows/agent-direct.md +362 -0
- package/loops/workflows/agent-implement.md +329 -0
- package/loops/workflows/agent-merge-gate.md +470 -0
- package/loops/workflows/agent-propose.md +342 -0
- package/loops/workflows/agent-refine.md +338 -0
- package/loops/workflows/shared/opencode-ci.md +26 -0
- package/loops/workflows/shared/platform-defaults.md +14 -0
- package/loops/workflows/work-router.yml +391 -0
- 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
|
+
```
|