@plainconceptsplatform/workflows 0.20.3 → 0.23.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/index.js +0 -0
- package/loops/actions/classify-route/action.yml +100 -100
- package/loops/actions/classify-route/classify-route.sh +10 -9
- package/loops/actions/decide-agent-retry/action.yml +72 -0
- package/loops/actions/verify-route-matrix/verify-route-matrix.sh +223 -19
- package/loops/templates/ci/app-ci-dotnet-next.yml +9 -9
- package/loops/templates/ci/app-ci-node-monorepo.yml +11 -11
- package/loops/templates/release/github-release.yml +1 -1
- package/loops/workflows/agent-apply-review.md +65 -0
- package/loops/workflows/agent-implement.md +99 -6
- package/loops/workflows/agent-merge-gate.md +42 -7
- package/loops/workflows/agent-refine.md +66 -0
- package/loops/workflows/agent-triage.md +66 -0
- package/loops/workflows/shared/opencode-ci.md +206 -206
- package/loops/workflows/work-router.yml +7 -4
- package/package.json +8 -9
|
@@ -17,6 +17,18 @@ env:
|
|
|
17
17
|
STALLED_LABEL: stalled
|
|
18
18
|
REFINE_LABEL: refine
|
|
19
19
|
TRIAGE_MARKER: "<!-- agent-triage -->"
|
|
20
|
+
# A run that died before it produced anything is worth repeating; one that worked and then
|
|
21
|
+
# failed produced an answer that was wrong, and repeating it buys the same wrong answer later.
|
|
22
|
+
# Duration is what separates them. Odyssey #190 died in three minutes on `Model 'glm-5-3' not
|
|
23
|
+
# found`, a gateway fault that clears in seconds, and waited on the janitor's six-hourly sweep
|
|
24
|
+
# because only the implement worker could do this.
|
|
25
|
+
TRIAGE_ATTEMPT_MARKER: "<!-- agent-triage-attempt -->"
|
|
26
|
+
MAX_ATTEMPTS: "5"
|
|
27
|
+
PARK_AT_ATTEMPT: "4"
|
|
28
|
+
# Ten rather than six: the failure that prompted this took three minutes, and a slower one on a
|
|
29
|
+
# worse day would fall outside a six-minute window and park for a fault that clears by itself.
|
|
30
|
+
RETRY_UNDER_MINUTES: "10"
|
|
31
|
+
RETRY_COMMENT: "That is what a provider outage looks like -- the run ended before it could produce an answer -- so this is being tried again from the start. It is a fresh run rather than a continuation: nothing is carried over from the attempt that failed."
|
|
20
32
|
MAX_TRIAGE_ROUNDS: "3"
|
|
21
33
|
INCOMPLETE_COMMENT: "Automated triage ended without an outcome. The triage label remains for a retry."
|
|
22
34
|
SAFE_OUTPUT_COMMENT_PREFIX: "Triage assessment"
|
|
@@ -61,6 +73,11 @@ imports:
|
|
|
61
73
|
on:
|
|
62
74
|
workflow_call:
|
|
63
75
|
inputs:
|
|
76
|
+
attempts_so_far:
|
|
77
|
+
description: Runs already made for this work that died before producing an answer. Filled by the worker when it re-dispatches itself, not by people.
|
|
78
|
+
required: false
|
|
79
|
+
type: string
|
|
80
|
+
default: '0'
|
|
64
81
|
issue-number:
|
|
65
82
|
description: Issue number to triage.
|
|
66
83
|
required: true
|
|
@@ -295,6 +312,8 @@ jobs:
|
|
|
295
312
|
permissions:
|
|
296
313
|
contents: read
|
|
297
314
|
issues: write
|
|
315
|
+
# the retry re-enters through the router, which is a workflow_dispatch
|
|
316
|
+
actions: write
|
|
298
317
|
steps:
|
|
299
318
|
- name: Checkout workflow actions
|
|
300
319
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -304,13 +323,59 @@ jobs:
|
|
|
304
323
|
with:
|
|
305
324
|
client-id: ${{ secrets.BOT_APP_ID }}
|
|
306
325
|
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
|
326
|
+
- name: Decide whether this failure is worth repeating
|
|
327
|
+
id: decide
|
|
328
|
+
uses: ./.github/actions/decide-agent-retry
|
|
329
|
+
with:
|
|
330
|
+
token: ${{ github.token }}
|
|
331
|
+
attempts-so-far: ${{ inputs.attempts_so_far }}
|
|
332
|
+
park-at: ${{ env.PARK_AT_ATTEMPT }}
|
|
333
|
+
under-minutes: ${{ env.RETRY_UNDER_MINUTES }}
|
|
334
|
+
# Recorded before any label moves, so a failure in the steps below leaves a run that can be
|
|
335
|
+
# counted rather than work released with nothing to show for it.
|
|
336
|
+
- name: Report the failed attempt
|
|
337
|
+
if: steps.decide.outputs.retry == 'true'
|
|
338
|
+
uses: ./.github/actions/create-issue-comment
|
|
339
|
+
with:
|
|
340
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
341
|
+
issue-number: ${{ inputs.issue-number }}
|
|
342
|
+
body: |
|
|
343
|
+
${{ env.TRIAGE_ATTEMPT_MARKER }}
|
|
344
|
+
Attempt ${{ steps.decide.outputs.next }} of ${{ env.MAX_ATTEMPTS }} ended after ${{ steps.decide.outputs.minutes }} minutes, before the run could produce an answer.
|
|
345
|
+
${{ env.RETRY_COMMENT }}
|
|
346
|
+
[View this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
|
347
|
+
- name: Release the reservation for the retry
|
|
348
|
+
if: steps.decide.outputs.retry == 'true'
|
|
349
|
+
uses: ./.github/actions/remove-issue-labels
|
|
350
|
+
with:
|
|
351
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
352
|
+
issue-number: ${{ inputs.issue-number }}
|
|
353
|
+
labels: ${{ env.WORKING_LABEL }}
|
|
354
|
+
- name: Send the work back through the router
|
|
355
|
+
if: steps.decide.outputs.retry == 'true'
|
|
356
|
+
env:
|
|
357
|
+
GH_TOKEN: ${{ github.token }}
|
|
358
|
+
REPO: ${{ github.repository }}
|
|
359
|
+
REF: ${{ github.event.repository.default_branch }}
|
|
360
|
+
SUBJECT: ${{ inputs.issue-number }}
|
|
361
|
+
NEXT: ${{ steps.decide.outputs.next }}
|
|
362
|
+
MODE: ${{ inputs.mode }}
|
|
363
|
+
run: |
|
|
364
|
+
set -euo pipefail
|
|
365
|
+
# The provider recovers in seconds, so pause before re-entering rather than dispatching
|
|
366
|
+
# back into the same outage. The router's own classify and authorize jobs add more.
|
|
367
|
+
sleep 30
|
|
368
|
+
gh workflow run work-router.yml --repo "$REPO" --ref "$REF" -f operation=triage -f issue-number="$SUBJECT" -f mode="$MODE" -f attempts_so_far="$NEXT"
|
|
369
|
+
echo "Re-dispatched triage for $SUBJECT as attempt $NEXT."
|
|
307
370
|
- name: Release the issue
|
|
371
|
+
if: steps.decide.outputs.retry != 'true'
|
|
308
372
|
uses: ./.github/actions/remove-issue-labels
|
|
309
373
|
with:
|
|
310
374
|
token: ${{ steps.app-token.outputs.token }}
|
|
311
375
|
issue-number: ${{ inputs.issue-number }}
|
|
312
376
|
labels: ${{ env.WORKING_LABEL }}
|
|
313
377
|
- name: Flag for human review
|
|
378
|
+
if: steps.decide.outputs.retry != 'true'
|
|
314
379
|
uses: ./.github/actions/add-issue-labels
|
|
315
380
|
with:
|
|
316
381
|
token: ${{ steps.app-token.outputs.token }}
|
|
@@ -319,6 +384,7 @@ jobs:
|
|
|
319
384
|
${{ env.REVIEW_LABEL }}
|
|
320
385
|
${{ env.STALLED_LABEL }}
|
|
321
386
|
- name: Report missing triage outcome
|
|
387
|
+
if: steps.decide.outputs.retry != 'true'
|
|
322
388
|
uses: ./.github/actions/create-issue-comment
|
|
323
389
|
with:
|
|
324
390
|
token: ${{ steps.app-token.outputs.token }}
|
|
@@ -1,206 +1,206 @@
|
|
|
1
|
-
---
|
|
2
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/shared/opencode-ci.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
|
-
env:
|
|
4
|
-
AGENTMEMORY_VERSION: "0.9.28"
|
|
5
|
-
CODEGRAPH_VERSION: "1.6.0"
|
|
6
|
-
RTK_VERSION: "0.44.1"
|
|
7
|
-
RTK_SHA256: "986f29704469b3d1051e2474105c6c75ab8b73651068dcd61612c1fb3938ad95"
|
|
8
|
-
description: |
|
|
9
|
-
Shared CI setup for Platform agent workflows. Installs pinned tooling and merges
|
|
10
|
-
opencode.ci.json into opencode.jsonc so the CI agent gets its provider and model config.
|
|
11
|
-
|
|
12
|
-
Consumer-specific steps (NuGet, .NET restore, OpenSpec, etc.) should be added after the
|
|
13
|
-
shared baseline in the consumer copy. The merge step below is package-owned and required
|
|
14
|
-
for the agent to resolve its provider and its models.
|
|
15
|
-
|
|
16
|
-
# Consumer repositories should add stack-specific steps (NuGet cache, dotnet restore,
|
|
17
|
-
# OpenSpec, Playwright, etc.) after the shared baseline. The merge step at the end is
|
|
18
|
-
# package-owned and required for the agent to resolve its provider and models. Do not
|
|
19
|
-
# remove it.
|
|
20
|
-
pre-agent-steps:
|
|
21
|
-
- name: Create agent scratch directory
|
|
22
|
-
run: mkdir -p .opencode/.tmp
|
|
23
|
-
|
|
24
|
-
- name: Install the Docker Compose plugin
|
|
25
|
-
# The ARC runner image ships the docker CLI without the compose plugin, and awf drives its
|
|
26
|
-
# sandbox with `docker compose up`. User-space install; no root required.
|
|
27
|
-
run: |
|
|
28
|
-
set -euo pipefail
|
|
29
|
-
if docker compose version >/dev/null 2>&1; then
|
|
30
|
-
echo "compose already present"; exit 0
|
|
31
|
-
fi
|
|
32
|
-
mkdir -p "$HOME/.docker/cli-plugins"
|
|
33
|
-
curl -sSfL "https://github.com/docker/compose/releases/download/v2.39.2/docker-compose-linux-x86_64" -o "$HOME/.docker/cli-plugins/docker-compose"
|
|
34
|
-
chmod +x "$HOME/.docker/cli-plugins/docker-compose"
|
|
35
|
-
docker compose version
|
|
36
|
-
|
|
37
|
-
- name: Install ripgrep
|
|
38
|
-
run: |
|
|
39
|
-
set -euo pipefail
|
|
40
|
-
|
|
41
|
-
if command -v rg > /dev/null 2>&1; then
|
|
42
|
-
echo "ripgrep already installed: $(rg --version | head -1)"
|
|
43
|
-
exit 0
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
# ARC runner pods have no root, so ripgrep comes from its release tarball into a
|
|
47
|
-
# user-writable prefix instead of apt.
|
|
48
|
-
mkdir -p "$HOME/.local/bin"
|
|
49
|
-
curl -sSfL https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-x86_64-unknown-linux-musl.tar.gz | tar -xz --strip-components=1 -C "$HOME/.local/bin" --wildcards "*/rg"
|
|
50
|
-
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
51
|
-
rg --version
|
|
52
|
-
|
|
53
|
-
- name: Activate the pnpm version package.json pins
|
|
54
|
-
run: |
|
|
55
|
-
set -euo pipefail
|
|
56
|
-
corepack enable
|
|
57
|
-
corepack prepare --activate
|
|
58
|
-
pnpm --version
|
|
59
|
-
|
|
60
|
-
- name: Cache the pnpm store
|
|
61
|
-
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
62
|
-
with:
|
|
63
|
-
path: ~/.local/share/pnpm/store
|
|
64
|
-
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
|
65
|
-
restore-keys: pnpm-store-${{ runner.os }}-
|
|
66
|
-
|
|
67
|
-
- name: Install RTK
|
|
68
|
-
run: |
|
|
69
|
-
set -euo pipefail
|
|
70
|
-
|
|
71
|
-
if command -v rtk > /dev/null 2>&1 && rtk --version 2>/dev/null | grep -q "${RTK_VERSION}"; then
|
|
72
|
-
echo "RTK ${RTK_VERSION} already installed"
|
|
73
|
-
exit 0
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
tarball="$RUNNER_TEMP/rtk.tar.gz"
|
|
77
|
-
curl -fsSL -o "$tarball" \
|
|
78
|
-
"https://github.com/rtk-ai/rtk/releases/download/v${RTK_VERSION}/rtk-x86_64-unknown-linux-musl.tar.gz"
|
|
79
|
-
echo "${RTK_SHA256} $tarball" | sha256sum --check --strict
|
|
80
|
-
|
|
81
|
-
tar -xzf "$tarball" -C "$RUNNER_TEMP"
|
|
82
|
-
mkdir -p "$HOME/.local/bin"
|
|
83
|
-
install -m 0755 "$RUNNER_TEMP/rtk" "$HOME/.local/bin/rtk"
|
|
84
|
-
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
85
|
-
|
|
86
|
-
rtk --version
|
|
87
|
-
rtk init -g --opencode --auto-patch
|
|
88
|
-
|
|
89
|
-
- name: Install agentmemory
|
|
90
|
-
run: |
|
|
91
|
-
set -euo pipefail
|
|
92
|
-
|
|
93
|
-
if command -v agentmemory > /dev/null 2>&1 && agentmemory --version 2>/dev/null | grep -q "${AGENTMEMORY_VERSION}"; then
|
|
94
|
-
echo "agentmemory ${AGENTMEMORY_VERSION} already installed"
|
|
95
|
-
exit 0
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
npm install -g "@agentmemory/agentmemory@${AGENTMEMORY_VERSION}"
|
|
99
|
-
agentmemory --version
|
|
100
|
-
|
|
101
|
-
- name: Restore the CodeGraph index
|
|
102
|
-
# Ephemeral ARC runners start empty, so without this every run rebuilds the whole graph.
|
|
103
|
-
# actions/cache is repository-scoped by design, which keeps consumers isolated, and
|
|
104
|
-
# each run saves an immutable snapshot under its own key at job end (the action's built-in
|
|
105
|
-
# post step), so two concurrent runs never write the same entry: the newest snapshot simply
|
|
106
|
-
# wins the next restore. The index lives in .codegraph inside the checkout plus a small
|
|
107
|
-
# registry in ~/.codegraph.
|
|
108
|
-
uses: actions/cache@
|
|
109
|
-
with:
|
|
110
|
-
path: |
|
|
111
|
-
.codegraph
|
|
112
|
-
~/.codegraph
|
|
113
|
-
key: codegraph-${{ github.repository_id }}-${{ github.run_id }}
|
|
114
|
-
restore-keys: |
|
|
115
|
-
codegraph-${{ github.repository_id }}-
|
|
116
|
-
|
|
117
|
-
- name: Install codegraph and index the repository
|
|
118
|
-
continue-on-error: true
|
|
119
|
-
run: |
|
|
120
|
-
set -euo pipefail
|
|
121
|
-
|
|
122
|
-
if command -v codegraph > /dev/null 2>&1 && codegraph --version 2>/dev/null | grep -q "${CODEGRAPH_VERSION}"; then
|
|
123
|
-
echo "codegraph ${CODEGRAPH_VERSION} already installed"
|
|
124
|
-
else
|
|
125
|
-
npm install -g "@colbymchenry/codegraph@${CODEGRAPH_VERSION}"
|
|
126
|
-
fi
|
|
127
|
-
|
|
128
|
-
codegraph init
|
|
129
|
-
|
|
130
|
-
- name: Install OpenSpec CLI
|
|
131
|
-
run: |
|
|
132
|
-
set -euo pipefail
|
|
133
|
-
|
|
134
|
-
if command -v openspec > /dev/null 2>&1 && openspec --version 2>/dev/null | grep -q "1.8.0"; then
|
|
135
|
-
echo "openspec 1.8.0 already installed"
|
|
136
|
-
exit 0
|
|
137
|
-
fi
|
|
138
|
-
|
|
139
|
-
npm install -g "@fission-ai/openspec@1.8.0"
|
|
140
|
-
openspec --version
|
|
141
|
-
|
|
142
|
-
# NOTE: playwright-cli and SQL Server startup steps are not part of the shared CI
|
|
143
|
-
# baseline. The agent path captures no visual evidence: it cannot reach a browser
|
|
144
|
-
# from inside the awf sandbox, and a screenshot with no baseline to compare against
|
|
145
|
-
# gated nothing. Run /ops-evidence locally when a change warrants screenshots. If
|
|
146
|
-
# you need playwright-cli or SQL Server inside the agent sandbox for other reasons,
|
|
147
|
-
# add them as consumer-specific steps after the shared baseline.
|
|
148
|
-
|
|
149
|
-
- name: Setup .NET
|
|
150
|
-
# Ephemeral VMs carry no SDK; the old host had it baked in, and GitHub-hosted images made
|
|
151
|
-
# the dependency invisible. setup-dotnet caches per run from the MS CDN.
|
|
152
|
-
uses: actions/setup-dotnet@v5
|
|
153
|
-
with:
|
|
154
|
-
dotnet-version: 10.0.x
|
|
155
|
-
env:
|
|
156
|
-
# the default /usr/share/dotnet needs root; the tool cache belongs to the runner user
|
|
157
|
-
DOTNET_INSTALL_DIR: ${{ runner.tool_cache }}/dotnet
|
|
158
|
-
|
|
159
|
-
- name: Cache NuGet packages
|
|
160
|
-
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
161
|
-
with:
|
|
162
|
-
path: ~/.nuget/packages
|
|
163
|
-
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.slnx', '**/Directory.Packages.props') }}
|
|
164
|
-
restore-keys: nuget-${{ runner.os }}-
|
|
165
|
-
|
|
166
|
-
- name: Install workspace dependencies
|
|
167
|
-
run: pnpm install --frozen-lockfile
|
|
168
|
-
|
|
169
|
-
- name: Merge the CI-only OpenCode provider into opencode.jsonc
|
|
170
|
-
run: |
|
|
171
|
-
set -euo pipefail
|
|
172
|
-
|
|
173
|
-
CONFIG=opencode.jsonc
|
|
174
|
-
FRAGMENT=opencode.ci.json
|
|
175
|
-
|
|
176
|
-
[ -f "$FRAGMENT" ] || { echo "::error::$FRAGMENT is missing from the checkout"; exit 1; }
|
|
177
|
-
|
|
178
|
-
# Pure JSON on purpose, not JSONC: jq cannot parse `//` comments, and a naive
|
|
179
|
-
# comment-stripper would corrupt the `http://` inside the provider's api URL.
|
|
180
|
-
jq -e . "$FRAGMENT" > /dev/null \
|
|
181
|
-
|| { echo "::error::$FRAGMENT is not valid JSON. Comments are not allowed in it."; exit 1; }
|
|
182
|
-
|
|
183
|
-
# Despite the .jsonc name, this file is committed in this repository and is read by
|
|
184
|
-
# jq below, so it must contain no comments. A single `//` line fails the merge with
|
|
185
|
-
# "Invalid numeric literal", which names neither the file nor the reason.
|
|
186
|
-
if [ -f "$CONFIG" ] && ! jq -e . "$CONFIG" > /dev/null 2>&1; then
|
|
187
|
-
echo "::error::$CONFIG is tracked and must be comment-free JSON: jq cannot parse it."
|
|
188
|
-
exit 1
|
|
189
|
-
fi
|
|
190
|
-
|
|
191
|
-
# opencode.jsonc is untracked in most repositories, so it usually does not exist here.
|
|
192
|
-
# Create it from the fragment when absent, merge when a checkout did provide one.
|
|
193
|
-
if [ -f "$CONFIG" ]; then
|
|
194
|
-
merged=$(jq -s '.[0] * .[1]' "$CONFIG" "$FRAGMENT")
|
|
195
|
-
else
|
|
196
|
-
merged=$(jq -S . "$FRAGMENT")
|
|
197
|
-
fi
|
|
198
|
-
|
|
199
|
-
printf '%s\n' "$merged" > "$CONFIG"
|
|
200
|
-
|
|
201
|
-
# gh-aw's own "Write OpenCode Config" step runs next and merges its base config with
|
|
202
|
-
# `$existing * $base`. Base wins on conflicting keys, but it defines neither `model`
|
|
203
|
-
# nor this provider, so both survive and `awf-proxy` is added alongside.
|
|
204
|
-
echo "Wrote $CONFIG from $FRAGMENT:"
|
|
205
|
-
jq -r '" model: \(.model // "unset")", " plugins: \(.plugin // [] | join(", "))", " providers: \(.provider // {} | keys | join(", "))"' "$CONFIG"
|
|
206
|
-
---
|
|
1
|
+
---
|
|
2
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/shared/opencode-ci.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
|
+
env:
|
|
4
|
+
AGENTMEMORY_VERSION: "0.9.28"
|
|
5
|
+
CODEGRAPH_VERSION: "1.6.0"
|
|
6
|
+
RTK_VERSION: "0.44.1"
|
|
7
|
+
RTK_SHA256: "986f29704469b3d1051e2474105c6c75ab8b73651068dcd61612c1fb3938ad95"
|
|
8
|
+
description: |
|
|
9
|
+
Shared CI setup for Platform agent workflows. Installs pinned tooling and merges
|
|
10
|
+
opencode.ci.json into opencode.jsonc so the CI agent gets its provider and model config.
|
|
11
|
+
|
|
12
|
+
Consumer-specific steps (NuGet, .NET restore, OpenSpec, etc.) should be added after the
|
|
13
|
+
shared baseline in the consumer copy. The merge step below is package-owned and required
|
|
14
|
+
for the agent to resolve its provider and its models.
|
|
15
|
+
|
|
16
|
+
# Consumer repositories should add stack-specific steps (NuGet cache, dotnet restore,
|
|
17
|
+
# OpenSpec, Playwright, etc.) after the shared baseline. The merge step at the end is
|
|
18
|
+
# package-owned and required for the agent to resolve its provider and models. Do not
|
|
19
|
+
# remove it.
|
|
20
|
+
pre-agent-steps:
|
|
21
|
+
- name: Create agent scratch directory
|
|
22
|
+
run: mkdir -p .opencode/.tmp
|
|
23
|
+
|
|
24
|
+
- name: Install the Docker Compose plugin
|
|
25
|
+
# The ARC runner image ships the docker CLI without the compose plugin, and awf drives its
|
|
26
|
+
# sandbox with `docker compose up`. User-space install; no root required.
|
|
27
|
+
run: |
|
|
28
|
+
set -euo pipefail
|
|
29
|
+
if docker compose version >/dev/null 2>&1; then
|
|
30
|
+
echo "compose already present"; exit 0
|
|
31
|
+
fi
|
|
32
|
+
mkdir -p "$HOME/.docker/cli-plugins"
|
|
33
|
+
curl -sSfL "https://github.com/docker/compose/releases/download/v2.39.2/docker-compose-linux-x86_64" -o "$HOME/.docker/cli-plugins/docker-compose"
|
|
34
|
+
chmod +x "$HOME/.docker/cli-plugins/docker-compose"
|
|
35
|
+
docker compose version
|
|
36
|
+
|
|
37
|
+
- name: Install ripgrep
|
|
38
|
+
run: |
|
|
39
|
+
set -euo pipefail
|
|
40
|
+
|
|
41
|
+
if command -v rg > /dev/null 2>&1; then
|
|
42
|
+
echo "ripgrep already installed: $(rg --version | head -1)"
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# ARC runner pods have no root, so ripgrep comes from its release tarball into a
|
|
47
|
+
# user-writable prefix instead of apt.
|
|
48
|
+
mkdir -p "$HOME/.local/bin"
|
|
49
|
+
curl -sSfL https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-x86_64-unknown-linux-musl.tar.gz | tar -xz --strip-components=1 -C "$HOME/.local/bin" --wildcards "*/rg"
|
|
50
|
+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
51
|
+
rg --version
|
|
52
|
+
|
|
53
|
+
- name: Activate the pnpm version package.json pins
|
|
54
|
+
run: |
|
|
55
|
+
set -euo pipefail
|
|
56
|
+
corepack enable
|
|
57
|
+
corepack prepare --activate
|
|
58
|
+
pnpm --version
|
|
59
|
+
|
|
60
|
+
- name: Cache the pnpm store
|
|
61
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
62
|
+
with:
|
|
63
|
+
path: ~/.local/share/pnpm/store
|
|
64
|
+
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
|
65
|
+
restore-keys: pnpm-store-${{ runner.os }}-
|
|
66
|
+
|
|
67
|
+
- name: Install RTK
|
|
68
|
+
run: |
|
|
69
|
+
set -euo pipefail
|
|
70
|
+
|
|
71
|
+
if command -v rtk > /dev/null 2>&1 && rtk --version 2>/dev/null | grep -q "${RTK_VERSION}"; then
|
|
72
|
+
echo "RTK ${RTK_VERSION} already installed"
|
|
73
|
+
exit 0
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
tarball="$RUNNER_TEMP/rtk.tar.gz"
|
|
77
|
+
curl -fsSL -o "$tarball" \
|
|
78
|
+
"https://github.com/rtk-ai/rtk/releases/download/v${RTK_VERSION}/rtk-x86_64-unknown-linux-musl.tar.gz"
|
|
79
|
+
echo "${RTK_SHA256} $tarball" | sha256sum --check --strict
|
|
80
|
+
|
|
81
|
+
tar -xzf "$tarball" -C "$RUNNER_TEMP"
|
|
82
|
+
mkdir -p "$HOME/.local/bin"
|
|
83
|
+
install -m 0755 "$RUNNER_TEMP/rtk" "$HOME/.local/bin/rtk"
|
|
84
|
+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
85
|
+
|
|
86
|
+
rtk --version
|
|
87
|
+
rtk init -g --opencode --auto-patch
|
|
88
|
+
|
|
89
|
+
- name: Install agentmemory
|
|
90
|
+
run: |
|
|
91
|
+
set -euo pipefail
|
|
92
|
+
|
|
93
|
+
if command -v agentmemory > /dev/null 2>&1 && agentmemory --version 2>/dev/null | grep -q "${AGENTMEMORY_VERSION}"; then
|
|
94
|
+
echo "agentmemory ${AGENTMEMORY_VERSION} already installed"
|
|
95
|
+
exit 0
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
npm install -g "@agentmemory/agentmemory@${AGENTMEMORY_VERSION}"
|
|
99
|
+
agentmemory --version
|
|
100
|
+
|
|
101
|
+
- name: Restore the CodeGraph index
|
|
102
|
+
# Ephemeral ARC runners start empty, so without this every run rebuilds the whole graph.
|
|
103
|
+
# actions/cache is repository-scoped by design, which keeps consumers isolated, and
|
|
104
|
+
# each run saves an immutable snapshot under its own key at job end (the action's built-in
|
|
105
|
+
# post step), so two concurrent runs never write the same entry: the newest snapshot simply
|
|
106
|
+
# wins the next restore. The index lives in .codegraph inside the checkout plus a small
|
|
107
|
+
# registry in ~/.codegraph.
|
|
108
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
109
|
+
with:
|
|
110
|
+
path: |
|
|
111
|
+
.codegraph
|
|
112
|
+
~/.codegraph
|
|
113
|
+
key: codegraph-${{ github.repository_id }}-${{ github.run_id }}
|
|
114
|
+
restore-keys: |
|
|
115
|
+
codegraph-${{ github.repository_id }}-
|
|
116
|
+
|
|
117
|
+
- name: Install codegraph and index the repository
|
|
118
|
+
continue-on-error: true
|
|
119
|
+
run: |
|
|
120
|
+
set -euo pipefail
|
|
121
|
+
|
|
122
|
+
if command -v codegraph > /dev/null 2>&1 && codegraph --version 2>/dev/null | grep -q "${CODEGRAPH_VERSION}"; then
|
|
123
|
+
echo "codegraph ${CODEGRAPH_VERSION} already installed"
|
|
124
|
+
else
|
|
125
|
+
npm install -g "@colbymchenry/codegraph@${CODEGRAPH_VERSION}"
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
codegraph init
|
|
129
|
+
|
|
130
|
+
- name: Install OpenSpec CLI
|
|
131
|
+
run: |
|
|
132
|
+
set -euo pipefail
|
|
133
|
+
|
|
134
|
+
if command -v openspec > /dev/null 2>&1 && openspec --version 2>/dev/null | grep -q "1.8.0"; then
|
|
135
|
+
echo "openspec 1.8.0 already installed"
|
|
136
|
+
exit 0
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
npm install -g "@fission-ai/openspec@1.8.0"
|
|
140
|
+
openspec --version
|
|
141
|
+
|
|
142
|
+
# NOTE: playwright-cli and SQL Server startup steps are not part of the shared CI
|
|
143
|
+
# baseline. The agent path captures no visual evidence: it cannot reach a browser
|
|
144
|
+
# from inside the awf sandbox, and a screenshot with no baseline to compare against
|
|
145
|
+
# gated nothing. Run /ops-evidence locally when a change warrants screenshots. If
|
|
146
|
+
# you need playwright-cli or SQL Server inside the agent sandbox for other reasons,
|
|
147
|
+
# add them as consumer-specific steps after the shared baseline.
|
|
148
|
+
|
|
149
|
+
- name: Setup .NET
|
|
150
|
+
# Ephemeral VMs carry no SDK; the old host had it baked in, and GitHub-hosted images made
|
|
151
|
+
# the dependency invisible. setup-dotnet caches per run from the MS CDN.
|
|
152
|
+
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
|
|
153
|
+
with:
|
|
154
|
+
dotnet-version: 10.0.x
|
|
155
|
+
env:
|
|
156
|
+
# the default /usr/share/dotnet needs root; the tool cache belongs to the runner user
|
|
157
|
+
DOTNET_INSTALL_DIR: ${{ runner.tool_cache }}/dotnet
|
|
158
|
+
|
|
159
|
+
- name: Cache NuGet packages
|
|
160
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
161
|
+
with:
|
|
162
|
+
path: ~/.nuget/packages
|
|
163
|
+
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.slnx', '**/Directory.Packages.props') }}
|
|
164
|
+
restore-keys: nuget-${{ runner.os }}-
|
|
165
|
+
|
|
166
|
+
- name: Install workspace dependencies
|
|
167
|
+
run: pnpm install --frozen-lockfile
|
|
168
|
+
|
|
169
|
+
- name: Merge the CI-only OpenCode provider into opencode.jsonc
|
|
170
|
+
run: |
|
|
171
|
+
set -euo pipefail
|
|
172
|
+
|
|
173
|
+
CONFIG=opencode.jsonc
|
|
174
|
+
FRAGMENT=opencode.ci.json
|
|
175
|
+
|
|
176
|
+
[ -f "$FRAGMENT" ] || { echo "::error::$FRAGMENT is missing from the checkout"; exit 1; }
|
|
177
|
+
|
|
178
|
+
# Pure JSON on purpose, not JSONC: jq cannot parse `//` comments, and a naive
|
|
179
|
+
# comment-stripper would corrupt the `http://` inside the provider's api URL.
|
|
180
|
+
jq -e . "$FRAGMENT" > /dev/null \
|
|
181
|
+
|| { echo "::error::$FRAGMENT is not valid JSON. Comments are not allowed in it."; exit 1; }
|
|
182
|
+
|
|
183
|
+
# Despite the .jsonc name, this file is committed in this repository and is read by
|
|
184
|
+
# jq below, so it must contain no comments. A single `//` line fails the merge with
|
|
185
|
+
# "Invalid numeric literal", which names neither the file nor the reason.
|
|
186
|
+
if [ -f "$CONFIG" ] && ! jq -e . "$CONFIG" > /dev/null 2>&1; then
|
|
187
|
+
echo "::error::$CONFIG is tracked and must be comment-free JSON: jq cannot parse it."
|
|
188
|
+
exit 1
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# opencode.jsonc is untracked in most repositories, so it usually does not exist here.
|
|
192
|
+
# Create it from the fragment when absent, merge when a checkout did provide one.
|
|
193
|
+
if [ -f "$CONFIG" ]; then
|
|
194
|
+
merged=$(jq -s '.[0] * .[1]' "$CONFIG" "$FRAGMENT")
|
|
195
|
+
else
|
|
196
|
+
merged=$(jq -S . "$FRAGMENT")
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
printf '%s\n' "$merged" > "$CONFIG"
|
|
200
|
+
|
|
201
|
+
# gh-aw's own "Write OpenCode Config" step runs next and merges its base config with
|
|
202
|
+
# `$existing * $base`. Base wins on conflicting keys, but it defines neither `model`
|
|
203
|
+
# nor this provider, so both survive and `awf-proxy` is added alongside.
|
|
204
|
+
echo "Wrote $CONFIG from $FRAGMENT:"
|
|
205
|
+
jq -r '" model: \(.model // "unset")", " plugins: \(.plugin // [] | join(", "))", " providers: \(.provider // {} | keys | join(", "))"' "$CONFIG"
|
|
206
|
+
---
|
|
@@ -358,7 +358,7 @@ jobs:
|
|
|
358
358
|
ci-conclusion: ${{ steps.route.outputs.ci-conclusion }}
|
|
359
359
|
ci-run-id: ${{ steps.route.outputs.ci-run-id }}
|
|
360
360
|
merge-gate-attempts: ${{ steps.route.outputs.merge-gate-attempts }}
|
|
361
|
-
|
|
361
|
+
attempts: ${{ steps.route.outputs.attempts }}
|
|
362
362
|
refine-mode: ${{ steps.route.outputs.refine-mode }}
|
|
363
363
|
triage-mode: ${{ steps.route.outputs.triage-mode }}
|
|
364
364
|
trigger-kind: ${{ steps.route.outputs.trigger-kind }}
|
|
@@ -388,6 +388,7 @@ jobs:
|
|
|
388
388
|
with:
|
|
389
389
|
issue-number: ${{ needs.classify.outputs.issue-number }}
|
|
390
390
|
mode: ${{ needs.classify.outputs.refine-mode }}
|
|
391
|
+
attempts_so_far: ${{ needs.classify.outputs.attempts }}
|
|
391
392
|
secrets:
|
|
392
393
|
OPENAI_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
393
394
|
CODEX_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
@@ -481,7 +482,7 @@ jobs:
|
|
|
481
482
|
permissions: write-all
|
|
482
483
|
with:
|
|
483
484
|
issue-number: ${{ needs.classify.outputs.issue-number }}
|
|
484
|
-
attempts_so_far: ${{ needs.classify.outputs.
|
|
485
|
+
attempts_so_far: ${{ needs.classify.outputs.attempts }}
|
|
485
486
|
secrets:
|
|
486
487
|
OPENAI_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
487
488
|
CODEX_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
@@ -533,6 +534,7 @@ jobs:
|
|
|
533
534
|
with:
|
|
534
535
|
issue-number: ${{ needs.classify.outputs.issue-number }}
|
|
535
536
|
mode: ${{ needs.classify.outputs.triage-mode }}
|
|
537
|
+
attempts_so_far: ${{ needs.classify.outputs.attempts }}
|
|
536
538
|
secrets:
|
|
537
539
|
OPENAI_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
538
540
|
CODEX_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
@@ -552,6 +554,7 @@ jobs:
|
|
|
552
554
|
with:
|
|
553
555
|
pr-number: ${{ needs.classify.outputs.pr-number }}
|
|
554
556
|
linked-issue: ${{ needs.classify.outputs.linked-issue }}
|
|
557
|
+
attempts_so_far: ${{ needs.classify.outputs.attempts }}
|
|
555
558
|
secrets:
|
|
556
559
|
OPENAI_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
557
560
|
CODEX_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
@@ -698,7 +701,7 @@ jobs:
|
|
|
698
701
|
persist-credentials: false
|
|
699
702
|
- name: Generate App token
|
|
700
703
|
id: app-token
|
|
701
|
-
uses: actions/create-github-app-token@
|
|
704
|
+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
702
705
|
with:
|
|
703
706
|
app-id: ${{ secrets.BOT_APP_ID }}
|
|
704
707
|
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
|
@@ -896,7 +899,7 @@ jobs:
|
|
|
896
899
|
persist-credentials: false
|
|
897
900
|
- name: Generate App token
|
|
898
901
|
id: app-token
|
|
899
|
-
uses: actions/create-github-app-token@
|
|
902
|
+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
900
903
|
with:
|
|
901
904
|
app-id: ${{ secrets.BOT_APP_ID }}
|
|
902
905
|
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plainconceptsplatform/workflows",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Install and update Platform GitHub agentic workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github-actions",
|
|
@@ -25,13 +25,6 @@
|
|
|
25
25
|
"loops",
|
|
26
26
|
"README.md"
|
|
27
27
|
],
|
|
28
|
-
"scripts": {
|
|
29
|
-
"build": "tsc --project tsconfig.build.json && node ./scripts/copy-loops.mjs",
|
|
30
|
-
"test": "vitest run",
|
|
31
|
-
"typecheck": "tsc --noEmit --project tsconfig.json",
|
|
32
|
-
"release": "pnpm build && pnpm test && npm publish --access public",
|
|
33
|
-
"prepack": "node ./scripts/copy-loops.mjs"
|
|
34
|
-
},
|
|
35
28
|
"engines": {
|
|
36
29
|
"node": ">=20.19"
|
|
37
30
|
},
|
|
@@ -42,5 +35,11 @@
|
|
|
42
35
|
"@types/node": "^22.0.0",
|
|
43
36
|
"typescript": "^5.8.0",
|
|
44
37
|
"vitest": "^3.0.0"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc --project tsconfig.build.json && node ./scripts/copy-loops.mjs",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"typecheck": "tsc --noEmit --project tsconfig.json",
|
|
43
|
+
"release": "pnpm build && pnpm test && npm publish --access public"
|
|
45
44
|
}
|
|
46
|
-
}
|
|
45
|
+
}
|