@plainconceptsplatform/workflows 0.17.0 → 0.20.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 (60) hide show
  1. package/dist/catalog-installation.js +25 -13
  2. package/dist/stack-defaults.js +16 -16
  3. package/dist/worker-env.js +24 -21
  4. package/loops/actions/add-issue-labels/action.yml +50 -50
  5. package/loops/actions/agent-output.cjs +17 -17
  6. package/loops/actions/apply-agent-bundle/action.yml +24 -24
  7. package/loops/actions/apply-agent-comments/action.yml +42 -42
  8. package/loops/actions/apply-agent-labels/action.yml +55 -55
  9. package/loops/actions/apply-agent-output/action.yml +108 -108
  10. package/loops/actions/assess-blast-radius/action.yml +148 -0
  11. package/loops/actions/assess-blast-radius/assess-blast-radius.sh +138 -0
  12. package/loops/actions/classify-route/action.yml +100 -100
  13. package/loops/actions/cleanup-artifacts/action.yml +91 -91
  14. package/loops/actions/close-agent-issues/action.yml +43 -43
  15. package/loops/actions/create-agent-issues/action.yml +52 -52
  16. package/loops/actions/create-issue-comment/action.yml +29 -29
  17. package/loops/actions/download-agent-output/action.yml +53 -53
  18. package/loops/actions/housekeeping/action.yml +55 -2
  19. package/loops/actions/identify-gate-subject/action.yml +134 -122
  20. package/loops/actions/link-pr-to-issue/action.yml +40 -40
  21. package/loops/actions/list-open-issues/action.yml +33 -33
  22. package/loops/actions/load-issue-context/action.yml +45 -45
  23. package/loops/actions/merge-agent-pr/action.yml +49 -49
  24. package/loops/actions/push-agent-branch/action.yml +45 -45
  25. package/loops/actions/remove-issue-labels/action.yml +37 -37
  26. package/loops/actions/require-open-issue/action.yml +59 -0
  27. package/loops/actions/update-agent-issues/action.yml +58 -58
  28. package/loops/actions/validate-merge-gate-output/action.yml +62 -40
  29. package/loops/actions/validate-merge-gate-output/validate-merge-gate-output.sh +147 -31
  30. package/loops/actions/validate-refine-output/action.yml +48 -44
  31. package/loops/actions/validate-refine-output/validate-refine-output.sh +15 -4
  32. package/loops/actions/validate-review-output/action.yml +35 -35
  33. package/loops/actions/validate-triage-output/action.yml +36 -36
  34. package/loops/actions/verify-composite-actions/action.yml +9 -9
  35. package/loops/actions/verify-refine-output/action.yml +9 -9
  36. package/loops/actions/verify-refine-output/verify-refine-output.sh +6 -1
  37. package/loops/actions/verify-route-matrix/action.yml +9 -9
  38. package/loops/actions/verify-route-matrix/verify-gate-metrics.mjs +51 -0
  39. package/loops/actions/verify-route-matrix/verify-route-matrix.sh +630 -8
  40. package/loops/scripts/compile-agent-workflows.mjs +331 -331
  41. package/loops/templates/agentics/agentics-maintenance.yml +121 -121
  42. package/loops/templates/ci/app-ci-dotnet-next.yml +330 -330
  43. package/loops/templates/ci/app-ci-node-monorepo.yml +260 -260
  44. package/loops/templates/issues/bug_report.yml +109 -109
  45. package/loops/templates/issues/feature_request.yml +75 -75
  46. package/loops/templates/opencode/opencode.ci.json +55 -49
  47. package/loops/templates/opencode/opencode.ci.json.md +59 -49
  48. package/loops/templates/release/github-release.yml +30 -30
  49. package/loops/workflows/agent-apply-review.md +33 -3
  50. package/loops/workflows/agent-audit.md +36 -0
  51. package/loops/workflows/agent-implement.md +77 -5
  52. package/loops/workflows/agent-merge-gate.md +368 -148
  53. package/loops/workflows/agent-refine.md +93 -17
  54. package/loops/workflows/agent-release.md +4 -4
  55. package/loops/workflows/agent-triage.md +33 -1
  56. package/loops/workflows/authorize-bot-work.yml +105 -105
  57. package/loops/workflows/shared/opencode-ci.md +206 -206
  58. package/loops/workflows/shared/platform-defaults.md +19 -19
  59. package/loops/workflows/work-router.yml +25 -11
  60. package/package.json +2 -2
@@ -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@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,22 +1,22 @@
1
- ---
2
- # Managed by @plainconceptsplatform/workflows. Source: loops/workflows/shared/platform-defaults.md. Update with `workflows update --force`; consumer edits may be overwritten.
3
- description: Shared network and safe-output defaults for catalog agent workflows, plus the verification rules every worker that builds or pushes has to follow. The body below is runtime-imported into each worker's prompt ahead of its own steps.
4
-
5
- network:
6
- allowed:
7
- - defaults
8
- - forge.plainconcepts.com
9
- - node
10
- - github
11
- - dotnet
12
- - fonts
13
- - login.microsoftonline.com
14
- # centralised AgentMemory (App Service); the MCP shim needs it through the egress proxy
15
- - agentmemory-pro-01.azurewebsites.net
16
-
17
- safe-outputs:
18
- threat-detection: false
19
- ---
1
+ ---
2
+ # Managed by @plainconceptsplatform/workflows. Source: loops/workflows/shared/platform-defaults.md. Update with `workflows update --force`; consumer edits may be overwritten.
3
+ description: Shared network and safe-output defaults for catalog agent workflows, plus the verification rules every worker that builds or pushes has to follow. The body below is runtime-imported into each worker's prompt ahead of its own steps.
4
+
5
+ network:
6
+ allowed:
7
+ - defaults
8
+ - forge.plainconcepts.com
9
+ - node
10
+ - github
11
+ - dotnet
12
+ - fonts
13
+ - login.microsoftonline.com
14
+ # centralised AgentMemory (App Service); the MCP shim needs it through the egress proxy
15
+ - agentmemory-pro-01.azurewebsites.net
16
+
17
+ safe-outputs:
18
+ threat-detection: false
19
+ ---
20
20
 
21
21
  ## Verification, for every worker that builds or pushes
22
22
 
@@ -242,7 +242,7 @@ permissions:
242
242
  jobs:
243
243
  # Public repositories must check repository permission before an event can start a model.
244
244
  authorize:
245
- runs-on: ubuntu-latest
245
+ runs-on: RunnerLandingZone
246
246
  timeout-minutes: 5
247
247
  permissions:
248
248
  contents: read
@@ -345,7 +345,7 @@ jobs:
345
345
  esac
346
346
 
347
347
  classify:
348
- runs-on: ubuntu-latest
348
+ runs-on: RunnerLandingZone
349
349
  timeout-minutes: 5
350
350
  permissions:
351
351
  contents: read
@@ -402,7 +402,7 @@ jobs:
402
402
  check-implement-pr:
403
403
  needs: [classify, authorize]
404
404
  if: needs.classify.outputs.route == 'implement' && needs.authorize.outputs.trusted == 'true'
405
- runs-on: ubuntu-latest
405
+ runs-on: RunnerLandingZone
406
406
  timeout-minutes: 3
407
407
  permissions:
408
408
  contents: read
@@ -493,7 +493,7 @@ jobs:
493
493
  dispatch-triage:
494
494
  needs: [classify, authorize]
495
495
  if: needs.classify.outputs.route == 'triage' && needs.authorize.outputs.is_outside_collaborator == 'true'
496
- runs-on: ubuntu-latest
496
+ runs-on: RunnerLandingZone
497
497
  timeout-minutes: 5
498
498
  permissions:
499
499
  actions: write
@@ -621,7 +621,7 @@ jobs:
621
621
  if: >
622
622
  needs.classify.outputs.route == 'bot-approve' &&
623
623
  (github.actor == 'app/github-actions' || github.actor == 'platform-devbox[bot]')
624
- runs-on: ubuntu-latest
624
+ runs-on: RunnerLandingZone
625
625
  timeout-minutes: 5
626
626
  permissions:
627
627
  actions: write
@@ -678,7 +678,7 @@ jobs:
678
678
  github.event.pull_request.head.repo.full_name == github.repository
679
679
  )
680
680
  )
681
- runs-on: ubuntu-latest
681
+ runs-on: RunnerLandingZone
682
682
  timeout-minutes: 10
683
683
  permissions:
684
684
  contents: read
@@ -871,7 +871,7 @@ jobs:
871
871
  reconcile-bot-pr-runs:
872
872
  needs: classify
873
873
  if: needs.classify.outputs.route == 'reconcile-bot-pr-runs'
874
- runs-on: ubuntu-latest
874
+ runs-on: RunnerLandingZone
875
875
  timeout-minutes: 5
876
876
  permissions:
877
877
  contents: read
@@ -1079,12 +1079,26 @@ jobs:
1079
1079
  -f "inputs[ci-conclusion]=$ci_conclusion" \
1080
1080
  -f "inputs[ci-run-id]=$ci_run_id" \
1081
1081
  -f "inputs[attempts_so_far]=$attempts"
1082
+
1083
+ # One gate per tick, because that is all the concurrency group can hold. The gate
1084
+ # runs in `merge-belt`, and GitHub keeps only the most recent *pending* run in a
1085
+ # group -- `cancel-in-progress: false` does not change that, it only protects a run
1086
+ # that has already started. A tick that dispatched three gates therefore ran the
1087
+ # last and silently cancelled the other two: no comment, no attempt recorded, and
1088
+ # nothing on the pull request to say it had been passed over. Seen on Pliny-Bot,
1089
+ # where dispatching a gate for PR #135 cancelled the one queued for PR #136.
1090
+ #
1091
+ # Stopping here loses nothing: every pull request eligible this tick is still
1092
+ # eligible next tick, and a run that never started consumes no attempt. The belt
1093
+ # now does one thing and says so, instead of appearing to start work it discarded.
1094
+ echo "One gate dispatched this tick; any other eligible pull request waits for the next one."
1095
+ break
1082
1096
  done
1083
1097
 
1084
1098
  audit-close:
1085
1099
  needs: classify
1086
1100
  if: needs.classify.outputs.route == 'audit-close'
1087
- runs-on: ubuntu-latest
1101
+ runs-on: RunnerLandingZone
1088
1102
  timeout-minutes: 15
1089
1103
  concurrency:
1090
1104
  group: audit-close
@@ -1109,7 +1123,7 @@ jobs:
1109
1123
  cleanup-artifacts:
1110
1124
  needs: classify
1111
1125
  if: needs.classify.outputs.route == 'cleanup-artifacts' && !github.event.repository.fork
1112
- runs-on: ubuntu-latest
1126
+ runs-on: RunnerLandingZone
1113
1127
  timeout-minutes: 15
1114
1128
  concurrency:
1115
1129
  group: cleanup-artifacts
@@ -1133,7 +1147,7 @@ jobs:
1133
1147
  housekeeping:
1134
1148
  needs: classify
1135
1149
  if: needs.classify.outputs.route == 'housekeeping' && !github.event.repository.fork
1136
- runs-on: ubuntu-latest
1150
+ runs-on: RunnerLandingZone
1137
1151
  timeout-minutes: 15
1138
1152
  concurrency:
1139
1153
  group: housekeeping
@@ -1172,7 +1186,7 @@ jobs:
1172
1186
  validate:
1173
1187
  needs: classify
1174
1188
  if: needs.classify.outputs.route == 'validate'
1175
- runs-on: ubuntu-latest
1189
+ runs-on: RunnerLandingZone
1176
1190
  timeout-minutes: 10
1177
1191
  permissions:
1178
1192
  contents: read
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plainconceptsplatform/workflows",
3
- "version": "0.17.0",
3
+ "version": "0.20.0",
4
4
  "description": "Install and update Platform GitHub agentic workflows.",
5
5
  "keywords": [
6
6
  "github-actions",
@@ -26,7 +26,7 @@
26
26
  "README.md"
27
27
  ],
28
28
  "scripts": {
29
- "build": "tsc --project tsconfig.build.json",
29
+ "build": "tsc --project tsconfig.build.json && node ./scripts/copy-loops.mjs",
30
30
  "test": "vitest run",
31
31
  "typecheck": "tsc --noEmit --project tsconfig.json",
32
32
  "release": "pnpm build && pnpm test && npm publish --access public",