@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.
@@ -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@v4
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
- implement-attempts: ${{ steps.route.outputs.implement-attempts }}
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.implement-attempts }}
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@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
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@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
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.20.3",
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
+ }