@plainconceptsplatform/workflows 0.4.34 → 0.5.1

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 (73) hide show
  1. package/README.md +88 -88
  2. package/loops/actions/agent-output.cjs +17 -17
  3. package/loops/actions/audit-close/action.yml +24 -0
  4. package/loops/actions/classify-route/action.yml +1 -3
  5. package/loops/actions/classify-route/classify-route.sh +40 -35
  6. package/loops/actions/update-changelog/action.yml +113 -0
  7. package/loops/actions/validate-merge-gate-output/action.yml +40 -40
  8. package/loops/actions/validate-refine-output/validate-refine-output.sh +16 -3
  9. package/loops/actions/validate-review-output/action.yml +35 -35
  10. package/loops/actions/validate-triage-output/action.yml +36 -36
  11. package/loops/actions/verify-route-matrix/verify-route-matrix.sh +91 -36
  12. package/loops/scripts/compile-agent-workflows.mjs +195 -6
  13. package/loops/templates/agentics/agentics-checks.yml +86 -86
  14. package/loops/templates/agentics/agentics-maintenance.yml +121 -121
  15. package/loops/templates/ci/app-ci-dotnet-next.yml +268 -171
  16. package/loops/templates/ci/app-ci-node-monorepo.yml +208 -178
  17. package/loops/templates/issues/bug_report.yml +109 -109
  18. package/loops/templates/issues/feature_request.yml +75 -75
  19. package/loops/templates/opencode/opencode.ci.json +49 -47
  20. package/loops/templates/opencode/opencode.ci.json.md +46 -41
  21. package/loops/templates/release/github-release.yml +30 -30
  22. package/loops/workflows/agent-apply-review.md +465 -443
  23. package/loops/workflows/agent-audit.md +213 -197
  24. package/loops/workflows/agent-implement.md +538 -414
  25. package/loops/workflows/agent-merge-gate.md +730 -584
  26. package/loops/workflows/agent-refine.md +609 -418
  27. package/loops/workflows/agent-release.md +258 -0
  28. package/loops/workflows/agent-triage.md +447 -439
  29. package/loops/workflows/authorize-bot-work.yml +85 -82
  30. package/loops/workflows/shared/opencode-ci.md +206 -197
  31. package/loops/workflows/shared/platform-defaults.md +19 -16
  32. package/loops/workflows/work-router.yml +862 -632
  33. package/package.json +7 -8
  34. package/dist/action-validation.test.d.ts +0 -1
  35. package/dist/action-validation.test.js +0 -84
  36. package/dist/catalog-installation.d.ts +0 -30
  37. package/dist/catalog-installation.js +0 -379
  38. package/dist/catalog-installation.test.d.ts +0 -1
  39. package/dist/catalog-installation.test.js +0 -448
  40. package/dist/catalog-listing.d.ts +0 -13
  41. package/dist/catalog-listing.js +0 -70
  42. package/dist/catalog-listing.test.d.ts +0 -1
  43. package/dist/catalog-listing.test.js +0 -150
  44. package/dist/index.d.ts +0 -2
  45. package/dist/index.js +0 -218
  46. package/dist/index.test.d.ts +0 -1
  47. package/dist/index.test.js +0 -273
  48. package/dist/repository-inspection.d.ts +0 -23
  49. package/dist/repository-inspection.js +0 -113
  50. package/dist/repository-inspection.test.d.ts +0 -1
  51. package/dist/repository-inspection.test.js +0 -77
  52. package/dist/repository-state.d.ts +0 -18
  53. package/dist/repository-state.js +0 -77
  54. package/dist/repository-state.test.d.ts +0 -1
  55. package/dist/repository-state.test.js +0 -96
  56. package/dist/route-processing.d.ts +0 -6
  57. package/dist/route-processing.js +0 -150
  58. package/dist/route-processing.test.d.ts +0 -1
  59. package/dist/route-processing.test.js +0 -350
  60. package/dist/stack-defaults.d.ts +0 -11
  61. package/dist/stack-defaults.js +0 -104
  62. package/dist/stack-defaults.test.d.ts +0 -1
  63. package/dist/stack-defaults.test.js +0 -266
  64. package/dist/tui.d.ts +0 -25
  65. package/dist/tui.js +0 -287
  66. package/dist/tui.test.d.ts +0 -1
  67. package/dist/tui.test.js +0 -246
  68. package/dist/workflow-catalog.d.ts +0 -25
  69. package/dist/workflow-catalog.js +0 -59
  70. package/dist/workflow-catalog.test.d.ts +0 -1
  71. package/dist/workflow-catalog.test.js +0 -29
  72. package/loops/workflows/agent-direct.md +0 -375
  73. package/loops/workflows/agent-propose.md +0 -342
@@ -1,82 +1,85 @@
1
- # Human adds implement/refine/direct label validates permission bot adds bot-working
2
- # This ensures the bot is the actor for all agentic workflows.
3
- #
4
- # IMPORTANT: Only triggers for HUMAN actors. When the bot transitions refine→implement,
5
- # it adds bot-working itself, so authorize-bot-work must not fire again.
6
- name: "Authorize Bot Work"
7
-
8
- run-name: "Authorizing: ${{ github.event.issue.title }} (#${{ github.event.issue.number }})"
9
-
10
- on:
11
- issues:
12
- types: [labeled]
13
-
14
- permissions:
15
- contents: read
16
-
17
- jobs:
18
- authorize:
19
- # Only trigger for implement/refine/direct labels from HUMANS (not bots),
20
- # and only if neither another bot run nor human review currently owns the issue.
21
- if: >
22
- (github.event.label.name == 'implement' ||
23
- github.event.label.name == 'refine' ||
24
- github.event.label.name == 'direct') &&
25
- !contains(github.event.issue.labels.*.name, 'bot-working') &&
26
- !contains(github.event.issue.labels.*.name, 'review') &&
27
- !endsWith(github.actor, '[bot]')
28
- runs-on: RunnerLandingZone
29
- timeout-minutes: 5
30
- permissions:
31
- contents: read
32
- issues: write
33
- steps:
34
- - name: Check actor permission
35
- id: check
36
- env:
37
- GH_TOKEN: ${{ github.token }}
38
- ACTOR: ${{ github.actor }}
39
- run: |
40
- set -euo pipefail
41
-
42
- permission=$(gh api "repos/$GITHUB_REPOSITORY/collaborators/$ACTOR/permission" \
43
- --jq '.permission' 2>/dev/null || echo "none")
44
-
45
- case "$permission" in
46
- admin | maintain | write)
47
- echo "authorized=true" >> "$GITHUB_OUTPUT"
48
- echo "::notice::$ACTOR has $permission permission - authorized"
49
- ;;
50
- *)
51
- echo "authorized=false" >> "$GITHUB_OUTPUT"
52
- echo "::warning::$ACTOR has '$permission' permission - not authorized"
53
- ;;
54
- esac
55
-
56
- - name: Checkout for App token action
57
- if: steps.check.outputs.authorized == 'true'
58
- uses: actions/checkout@v4
59
- with:
60
- persist-credentials: false
61
-
62
- - name: Generate App token
63
- if: steps.check.outputs.authorized == 'true'
64
- id: app-token
65
- uses: actions/create-github-app-token@v1
66
- with:
67
- app-id: ${{ secrets.BOT_APP_ID }}
68
- private-key: ${{ secrets.BOT_PRIVATE_KEY }}
69
-
70
- - name: Bot adds bot-working label
71
- if: steps.check.outputs.authorized == 'true'
72
- env:
73
- GH_TOKEN: ${{ steps.app-token.outputs.token }}
74
- ISSUE_NUMBER: ${{ github.event.issue.number }}
75
- LABEL: ${{ github.event.label.name }}
76
- run: |
77
- set -euo pipefail
78
-
79
- echo "Adding bot-working label to issue #$ISSUE_NUMBER (triggered by $LABEL)"
80
- gh issue edit "$ISSUE_NUMBER" --add-label "bot-working"
81
-
82
- echo "::notice::bot-working label added - bot will now own the $LABEL workflow"
1
+ # Managed by @plainconceptsplatform/workflows. Source: loops/workflows/authorize-bot-work.yml. Update with `workflows update --force`; consumer edits may be overwritten.
2
+ # Human adds implement/refine/direct/feature label validates permission bot adds bot-working
3
+ # This ensures the bot is the actor for all agentic workflows.
4
+ #
5
+ # IMPORTANT: Only triggers for HUMAN actors. When the bot transitions refine→implement,
6
+ # it adds bot-working itself, so authorize-bot-work must not fire again.
7
+ name: "Authorize Bot Work"
8
+
9
+ run-name: "Authorizing: ${{ github.event.issue.title }} (#${{ github.event.issue.number }})"
10
+
11
+ on:
12
+ issues:
13
+ types: [labeled]
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ authorize:
20
+ # Only trigger for work labels from HUMANS (not bots),
21
+ # and only if neither another bot run nor human review currently owns the issue.
22
+ if: >
23
+ (github.event.label.name == 'implement' ||
24
+ github.event.label.name == 'refine') &&
25
+ !contains(github.event.issue.labels.*.name, 'bot-working') &&
26
+ !contains(github.event.issue.labels.*.name, 'review') &&
27
+ !endsWith(github.actor, '[bot]')
28
+ runs-on: ubuntu-latest
29
+ timeout-minutes: 5
30
+ concurrency:
31
+ group: authorize-${{ github.event.issue.number }}
32
+ cancel-in-progress: false
33
+ permissions:
34
+ contents: read
35
+ issues: write
36
+ steps:
37
+ - name: Check actor permission
38
+ id: check
39
+ env:
40
+ GH_TOKEN: ${{ github.token }}
41
+ ACTOR: ${{ github.actor }}
42
+ run: |
43
+ set -euo pipefail
44
+
45
+ permission=$(gh api "repos/$GITHUB_REPOSITORY/collaborators/$ACTOR/permission" \
46
+ --jq '.permission' 2>/dev/null || echo "none")
47
+
48
+ case "$permission" in
49
+ admin | maintain | write)
50
+ echo "authorized=true" >> "$GITHUB_OUTPUT"
51
+ echo "::notice::$ACTOR has $permission permission - authorized"
52
+ ;;
53
+ *)
54
+ echo "authorized=false" >> "$GITHUB_OUTPUT"
55
+ echo "::warning::$ACTOR has '$permission' permission - not authorized"
56
+ ;;
57
+ esac
58
+
59
+ - name: Checkout for App token action
60
+ if: steps.check.outputs.authorized == 'true'
61
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
62
+ with:
63
+ persist-credentials: false
64
+
65
+ - name: Generate App token
66
+ if: steps.check.outputs.authorized == 'true'
67
+ id: app-token
68
+ uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
69
+ with:
70
+ app-id: ${{ secrets.BOT_APP_ID }}
71
+ private-key: ${{ secrets.BOT_PRIVATE_KEY }}
72
+
73
+ - name: Bot adds bot-working label
74
+ if: steps.check.outputs.authorized == 'true'
75
+ env:
76
+ GH_TOKEN: ${{ steps.app-token.outputs.token }}
77
+ ISSUE_NUMBER: ${{ github.event.issue.number }}
78
+ LABEL: ${{ github.event.label.name }}
79
+ run: |
80
+ set -euo pipefail
81
+
82
+ echo "Adding bot-working label to issue #$ISSUE_NUMBER (triggered by $LABEL)"
83
+ gh issue edit "$ISSUE_NUMBER" --add-label "bot-working"
84
+
85
+ echo "::notice::bot-working label added - bot will now own the $LABEL workflow"
@@ -1,197 +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.5.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: Start OpenCode server (persistent warm server for faster agent runs)
25
- run: |
26
- set -euo pipefail
27
-
28
- OPENCODE_PORT=4096
29
- mkdir -p /tmp/opencode-data /tmp/gh-aw
30
-
31
- # Check if server is already running
32
- if curl -sf "http://127.0.0.1:${OPENCODE_PORT}/health" >/dev/null 2>&1; then
33
- echo "OpenCode server already running on port ${OPENCODE_PORT}"
34
- exit 0
35
- fi
36
-
37
- echo "Starting OpenCode server on port ${OPENCODE_PORT}..."
38
- export XDG_DATA_HOME=/tmp/opencode-data
39
- nohup opencode serve --port "${OPENCODE_PORT}" --hostname 127.0.0.1 \
40
- > /tmp/gh-aw/opencode-server.log 2>&1 &
41
-
42
- SERVER_PID=$!
43
- echo "Server PID: ${SERVER_PID}"
44
-
45
- # Wait for server to be ready (max 30 seconds)
46
- for i in $(seq 1 30); do
47
- if curl -sf "http://127.0.0.1:${OPENCODE_PORT}/health" >/dev/null 2>&1; then
48
- echo "OpenCode server is ready on port ${OPENCODE_PORT}"
49
- exit 0
50
- fi
51
- sleep 1
52
- done
53
-
54
- # Server didn't start in time - continue without it (agent will cold-start)
55
- echo "::warning::OpenCode server did not start in 30s, agent will run without warm server"
56
- kill "${SERVER_PID}" 2>/dev/null || true
57
-
58
- - name: Install ripgrep
59
- run: |
60
- set -euo pipefail
61
-
62
- if command -v rg > /dev/null 2>&1; then
63
- echo "ripgrep already installed: $(rg --version | head -1)"
64
- exit 0
65
- fi
66
-
67
- sudo apt-get update
68
- sudo apt-get install --yes ripgrep
69
- rg --version
70
-
71
- - name: Activate the pnpm version package.json pins
72
- run: |
73
- set -euo pipefail
74
- corepack enable
75
- corepack prepare --activate
76
- pnpm --version
77
-
78
- - name: Cache the pnpm store
79
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
80
- with:
81
- path: ~/.local/share/pnpm/store
82
- key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
83
- restore-keys: pnpm-store-${{ runner.os }}-
84
-
85
- - name: Install RTK
86
- run: |
87
- set -euo pipefail
88
-
89
- if command -v rtk > /dev/null 2>&1 && rtk --version 2>/dev/null | grep -q "${RTK_VERSION}"; then
90
- echo "RTK ${RTK_VERSION} already installed"
91
- exit 0
92
- fi
93
-
94
- tarball="$RUNNER_TEMP/rtk.tar.gz"
95
- curl -fsSL -o "$tarball" \
96
- "https://github.com/rtk-ai/rtk/releases/download/v${RTK_VERSION}/rtk-x86_64-unknown-linux-musl.tar.gz"
97
- echo "${RTK_SHA256} $tarball" | sha256sum --check --strict
98
-
99
- tar -xzf "$tarball" -C "$RUNNER_TEMP"
100
- sudo install -m 0755 "$RUNNER_TEMP/rtk" /usr/local/bin/rtk
101
-
102
- rtk --version
103
- rtk init -g --opencode --auto-patch
104
-
105
- - name: Install agentmemory
106
- run: |
107
- set -euo pipefail
108
-
109
- if command -v agentmemory > /dev/null 2>&1 && agentmemory --version 2>/dev/null | grep -q "${AGENTMEMORY_VERSION}"; then
110
- echo "agentmemory ${AGENTMEMORY_VERSION} already installed"
111
- exit 0
112
- fi
113
-
114
- npm install -g "@agentmemory/agentmemory@${AGENTMEMORY_VERSION}"
115
- agentmemory --version
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 have been removed from the
143
- # shared CI baseline. Visual evidence capture now runs in a separate "Visual
144
- # evidence" workflow that executes on the raw runner (not inside the awf
145
- # sandbox), where Docker and headless Chromium are available. The agent's
146
- # /ops-evidence skill writes a capturePlan in evidence.json when blocked;
147
- # the Visual Evidence workflow reads and executes it. If you need playwright-cli
148
- # or SQL Server inside the agent sandbox for other reasons, add them as
149
- # consumer-specific steps after the shared baseline.
150
-
151
- - name: Cache NuGet packages
152
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
153
- with:
154
- path: ~/.nuget/packages
155
- key: nuget-${{ runner.os }}-${{ hashFiles('**/*.slnx', '**/Directory.Packages.props') }}
156
- restore-keys: nuget-${{ runner.os }}-
157
-
158
- - name: Install workspace dependencies
159
- run: pnpm install --frozen-lockfile
160
-
161
- - name: Merge the CI-only OpenCode provider into opencode.jsonc
162
- run: |
163
- set -euo pipefail
164
-
165
- CONFIG=opencode.jsonc
166
- FRAGMENT=opencode.ci.json
167
-
168
- [ -f "$FRAGMENT" ] || { echo "::error::$FRAGMENT is missing from the checkout"; exit 1; }
169
-
170
- # Pure JSON on purpose, not JSONC: jq cannot parse `//` comments, and a naive
171
- # comment-stripper would corrupt the `http://` inside the provider's api URL.
172
- jq -e . "$FRAGMENT" > /dev/null \
173
- || { echo "::error::$FRAGMENT is not valid JSON. Comments are not allowed in it."; exit 1; }
174
-
175
- # Despite the .jsonc name, this file is committed in this repository and is read by
176
- # jq below, so it must contain no comments. A single `//` line fails the merge with
177
- # "Invalid numeric literal", which names neither the file nor the reason.
178
- if [ -f "$CONFIG" ] && ! jq -e . "$CONFIG" > /dev/null 2>&1; then
179
- echo "::error::$CONFIG is tracked and must be comment-free JSON: jq cannot parse it."
180
- exit 1
181
- fi
182
-
183
- # opencode.jsonc is untracked in most repositories, so it usually does not exist here.
184
- # Create it from the fragment when absent, merge when a checkout did provide one.
185
- if [ -f "$CONFIG" ]; then
186
- merged=$(jq -s '.[0] * .[1]' "$CONFIG" "$FRAGMENT")
187
- else
188
- merged=$(jq -S . "$FRAGMENT")
189
- fi
190
- printf '%s\n' "$merged" > "$CONFIG"
191
-
192
- # gh-aw's own "Write OpenCode Config" step runs next and merges its base config with
193
- # `$existing * $base`. Base wins on conflicting keys, but it defines neither `model`
194
- # nor this provider, so both survive and `awf-proxy` is added alongside.
195
- echo "Wrote $CONFIG from $FRAGMENT:"
196
- jq -r '" model: \(.model // "unset")", " plugins: \(.plugin // [] | join(", "))", " providers: \(.provider // {} | keys | join(", "))"' "$CONFIG"
197
- ---
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,16 +1,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.
4
-
5
- network:
6
- allowed:
7
- - defaults
8
- - forge.plainconcepts.com
9
- - node
10
- - github
11
- - dotnet
12
- - fonts
13
-
14
- safe-outputs:
15
- threat-detection: false
16
- ---
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.
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
+ ---