@plainconceptsplatform/workflows 0.4.33 → 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 (75) 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 +4 -3
  5. package/loops/actions/classify-route/classify-route.sh +69 -29
  6. package/loops/actions/load-issue-context/action.yml +2 -0
  7. package/loops/actions/update-changelog/action.yml +113 -0
  8. package/loops/actions/validate-merge-gate-output/action.yml +40 -0
  9. package/loops/actions/validate-merge-gate-output/validate-merge-gate-output.sh +34 -0
  10. package/loops/actions/validate-refine-output/validate-refine-output.sh +16 -3
  11. package/loops/actions/validate-review-output/action.yml +35 -0
  12. package/loops/actions/validate-review-output/validate-review-output.sh +30 -0
  13. package/loops/actions/validate-triage-output/action.yml +36 -0
  14. package/loops/actions/validate-triage-output/validate-triage-output.sh +36 -0
  15. package/loops/actions/verify-route-matrix/verify-route-matrix.sh +121 -36
  16. package/loops/scripts/compile-agent-workflows.mjs +195 -6
  17. package/loops/templates/agentics/agentics-checks.yml +86 -86
  18. package/loops/templates/agentics/agentics-maintenance.yml +121 -121
  19. package/loops/templates/ci/app-ci-dotnet-next.yml +268 -171
  20. package/loops/templates/ci/app-ci-node-monorepo.yml +208 -178
  21. package/loops/templates/issues/bug_report.yml +109 -109
  22. package/loops/templates/issues/feature_request.yml +75 -75
  23. package/loops/templates/opencode/opencode.ci.json +49 -47
  24. package/loops/templates/opencode/opencode.ci.json.md +46 -41
  25. package/loops/templates/release/github-release.yml +30 -30
  26. package/loops/workflows/agent-apply-review.md +465 -373
  27. package/loops/workflows/agent-audit.md +213 -197
  28. package/loops/workflows/agent-implement.md +538 -374
  29. package/loops/workflows/agent-merge-gate.md +730 -485
  30. package/loops/workflows/agent-refine.md +609 -379
  31. package/loops/workflows/agent-release.md +258 -0
  32. package/loops/workflows/agent-triage.md +447 -0
  33. package/loops/workflows/authorize-bot-work.yml +85 -81
  34. package/loops/workflows/shared/opencode-ci.md +206 -197
  35. package/loops/workflows/shared/platform-defaults.md +19 -16
  36. package/loops/workflows/work-router.yml +862 -551
  37. package/package.json +42 -42
  38. package/dist/catalog-installation.d.ts +0 -30
  39. package/dist/catalog-installation.js +0 -352
  40. package/dist/catalog-installation.test.d.ts +0 -1
  41. package/dist/catalog-installation.test.js +0 -448
  42. package/dist/catalog-listing.d.ts +0 -13
  43. package/dist/catalog-listing.js +0 -70
  44. package/dist/catalog-listing.test.d.ts +0 -1
  45. package/dist/catalog-listing.test.js +0 -150
  46. package/dist/index.d.ts +0 -2
  47. package/dist/index.js +0 -218
  48. package/dist/index.test.d.ts +0 -1
  49. package/dist/index.test.js +0 -273
  50. package/dist/repository-inspection.d.ts +0 -23
  51. package/dist/repository-inspection.js +0 -113
  52. package/dist/repository-inspection.test.d.ts +0 -1
  53. package/dist/repository-inspection.test.js +0 -77
  54. package/dist/repository-state.d.ts +0 -18
  55. package/dist/repository-state.js +0 -77
  56. package/dist/repository-state.test.d.ts +0 -1
  57. package/dist/repository-state.test.js +0 -96
  58. package/dist/route-processing.d.ts +0 -6
  59. package/dist/route-processing.js +0 -150
  60. package/dist/route-processing.test.d.ts +0 -1
  61. package/dist/route-processing.test.js +0 -342
  62. package/dist/stack-defaults.d.ts +0 -11
  63. package/dist/stack-defaults.js +0 -104
  64. package/dist/stack-defaults.test.d.ts +0 -1
  65. package/dist/stack-defaults.test.js +0 -266
  66. package/dist/tui.d.ts +0 -25
  67. package/dist/tui.js +0 -287
  68. package/dist/tui.test.d.ts +0 -1
  69. package/dist/tui.test.js +0 -246
  70. package/dist/workflow-catalog.d.ts +0 -25
  71. package/dist/workflow-catalog.js +0 -57
  72. package/dist/workflow-catalog.test.d.ts +0 -1
  73. package/dist/workflow-catalog.test.js +0 -29
  74. package/loops/workflows/agent-direct.md +0 -363
  75. package/loops/workflows/agent-propose.md +0 -342
@@ -1,171 +1,268 @@
1
- # Managed by @plainconceptsplatform/workflows. Source: loops/templates/ci/app-ci-dotnet-next.yml. Update with `workflows update --force`; consumer edits may be overwritten.
2
- name: "App: CI"
3
-
4
- on:
5
- pull_request:
6
- schedule:
7
- - cron: "0 6 * * 1"
8
- workflow_dispatch:
9
- workflow_call:
10
- secrets:
11
- NPM_REGISTRY_TOKEN:
12
- required: false
13
-
14
- env:
15
- DOTNET_VERSION: "10.0.x"
16
- NODE_VERSION: "22"
17
- PNPM_VERSION: "10"
18
- API_WORKING_DIRECTORY: apps/api
19
- DOTNET_SOLUTION: Numa.slnx
20
- UNIT_TEST_PROJECT: tests/Numa.UnitTests/Numa.UnitTests.csproj
21
- PARITY_TEST_PROJECT: tests/Numa.ParityTests/Numa.ParityTests.csproj
22
- INTEGRATION_TEST_PROJECT: tests/Numa.IntegrationTests/Numa.IntegrationTests.csproj
23
- COVERAGE_SETTINGS: coverage.runsettings
24
- COVERAGE_CHECK_COMMAND: node ../../scripts/check-coverage.mjs TestResults/merged/Cobertura.xml
25
- MIGRATION_PROJECT: src/Numa.Infrastructure/Numa.Infrastructure.csproj
26
- MIGRATION_STARTUP_PROJECT: src/Numa.Api/Numa.Api.csproj
27
- SEED_PROJECT: src/Numa.Seed
28
- PRIMARY_DATABASE: numa
29
- DATA_LAYER_DATABASE: numa_dl
30
- SQL_SA_PASSWORD: "YourStrong!Passw0rd"
31
- SQL_PORT: "1433"
32
- WEB_PACKAGE: "@numa/web"
33
- NPM_REGISTRY_URL: "//npm.pkg.github.com/"
34
- TRUFFLEHOG_IMAGE: "trufflesecurity/trufflehog:latest"
35
- SEMGREP_IMAGE: "semgrep/semgrep:1.168.0"
36
- SBOM_ARTIFACT_NAME: numa-sbom.cyclonedx.json
37
- MIGRATION_CONTEXT: NumaDbContext
38
-
39
- permissions:
40
- contents: read
41
-
42
- concurrency:
43
- group: ci-${{ github.workflow }}-${{ github.head_ref || github.ref }}
44
- cancel-in-progress: ${{ github.event_name == 'pull_request' }}
45
-
46
- jobs:
47
- api:
48
- name: API (.NET)
49
- if: github.event_name != 'schedule'
50
- runs-on: RunnerLandingZone
51
- timeout-minutes: 30
52
- services:
53
- sqlserver:
54
- image: mcr.microsoft.com/mssql/server:2022-latest
55
- env:
56
- ACCEPT_EULA: "Y"
57
- MSSQL_SA_PASSWORD: ${{ env.SQL_SA_PASSWORD }}
58
- ports:
59
- - ${{ env.SQL_PORT }}:1433
60
- options: >-
61
- --health-cmd "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P '${{ env.SQL_SA_PASSWORD }}' -Q 'SELECT 1' -C || exit 1"
62
- --health-interval 10s --health-timeout 5s --health-retries 12 --health-start-period 30s
63
- defaults:
64
- run:
65
- working-directory: ${{ env.API_WORKING_DIRECTORY }}
66
- steps:
67
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
68
- - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6
69
- with:
70
- dotnet-version: ${{ env.DOTNET_VERSION }}
71
- - name: Restore tools, solution, and build
72
- run: |
73
- dotnet tool restore
74
- dotnet restore "$DOTNET_SOLUTION"
75
- dotnet build "$DOTNET_SOLUTION" --no-restore -c Release
76
- - name: Unit tests with coverage
77
- run: dotnet test "$UNIT_TEST_PROJECT" --no-build -c Release --collect:"XPlat Code Coverage" --settings "$COVERAGE_SETTINGS" --results-directory TestResults
78
- - name: Parity tests
79
- run: dotnet test "$PARITY_TEST_PROJECT" --no-build -c Release
80
- - name: Create test databases
81
- run: |
82
- for database in "$PRIMARY_DATABASE" "$DATA_LAYER_DATABASE"; do
83
- docker exec ${{ job.services.sqlserver.id }} /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$SQL_SA_PASSWORD" -C -Q "IF DB_ID('$database') IS NULL CREATE DATABASE [$database];"
84
- done
85
- - name: Apply migrations and seed test data
86
- env:
87
- ConnectionStrings__Numa: Server=localhost,${{ env.SQL_PORT }};Database=${{ env.PRIMARY_DATABASE }};User Id=sa;Password=${{ env.SQL_SA_PASSWORD }};TrustServerCertificate=true
88
- DataLayer__ConnectionString: Server=localhost,${{ env.SQL_PORT }};Database=${{ env.DATA_LAYER_DATABASE }};User Id=sa;Password=${{ env.SQL_SA_PASSWORD }};TrustServerCertificate=true
89
- DATALAYER_SEED: "true"
90
- run: |
91
- dotnet ef database update -p "$MIGRATION_PROJECT" -s "$MIGRATION_STARTUP_PROJECT" --context "$MIGRATION_CONTEXT"
92
- dotnet run --project "$SEED_PROJECT" --no-build -c Release
93
- - name: Integration tests with coverage
94
- env:
95
- ConnectionStrings__Numa: Server=localhost,${{ env.SQL_PORT }};Database=${{ env.PRIMARY_DATABASE }};User Id=sa;Password=${{ env.SQL_SA_PASSWORD }};TrustServerCertificate=true
96
- DataLayer__ConnectionString: Server=localhost,${{ env.SQL_PORT }};Database=${{ env.DATA_LAYER_DATABASE }};User Id=sa;Password=${{ env.SQL_SA_PASSWORD }};TrustServerCertificate=true
97
- run: dotnet test "$INTEGRATION_TEST_PROJECT" --no-build -c Release --collect:"XPlat Code Coverage" --settings "$COVERAGE_SETTINGS" --results-directory TestResults
98
- - name: Enforce merged coverage floor
99
- run: |
100
- dotnet tool install --global dotnet-reportgenerator-globaltool
101
- reportgenerator -reports:"TestResults/**/coverage.cobertura.xml" -targetdir:TestResults/merged -reporttypes:Cobertura
102
- bash -c "$COVERAGE_CHECK_COMMAND"
103
-
104
- web:
105
- name: Web (Next.js)
106
- if: github.event_name != 'schedule'
107
- runs-on: RunnerLandingZone
108
- timeout-minutes: 15
109
- steps:
110
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
111
- - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
112
- with:
113
- version: ${{ env.PNPM_VERSION }}
114
- run_install: false
115
- - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
116
- with:
117
- node-version: ${{ env.NODE_VERSION }}
118
- cache: pnpm
119
- cache-dependency-path: pnpm-lock.yaml
120
- - name: Configure optional private registry authentication
121
- env:
122
- NPM_REGISTRY_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}
123
- run: |
124
- if [ -n "$NPM_REGISTRY_TOKEN" ]; then
125
- echo "${NPM_REGISTRY_URL}:_authToken=$NPM_REGISTRY_TOKEN" >> .npmrc
126
- fi
127
- - run: pnpm install --frozen-lockfile
128
- - name: Lint, test, verify boundaries, and build
129
- run: |
130
- pnpm --filter "$WEB_PACKAGE" run lint
131
- pnpm --filter "$WEB_PACKAGE" run test
132
- pnpm --filter "$WEB_PACKAGE" run lint:fsd
133
- pnpm --filter "$WEB_PACKAGE" run build
134
-
135
- secret-scan:
136
- name: Secret scan (TruffleHog)
137
- runs-on: RunnerLandingZone
138
- steps:
139
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
140
- - run: docker run --rm -v "${{ github.workspace }}:/repo" "$TRUFFLEHOG_IMAGE" filesystem /repo --only-verified --fail --no-update
141
-
142
- deps-and-iac-scan:
143
- name: Dependencies and IaC (Trivy)
144
- runs-on: RunnerLandingZone
145
- steps:
146
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
147
- - uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
148
- with:
149
- scan-type: fs
150
- scanners: vuln,misconfig,secret
151
- severity: CRITICAL,HIGH
152
- ignore-unfixed: true
153
- exit-code: "1"
154
- format: table
155
-
156
- sast-scan:
157
- name: SAST (Semgrep)
158
- runs-on: RunnerLandingZone
159
- steps:
160
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
161
- - run: docker run --rm -v "${{ github.workspace }}:/src" -w /src "$SEMGREP_IMAGE" semgrep scan --error --metrics=off --oss-only --disable-version-check --config p/default --config p/security-audit --config p/secrets --config p/csharp --config p/typescript --config p/react --exclude-rule yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag
162
-
163
- sbom:
164
- name: SBOM (CycloneDX)
165
- runs-on: RunnerLandingZone
166
- steps:
167
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
168
- - uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
169
- with:
170
- format: cyclonedx-json
171
- artifact-name: ${{ env.SBOM_ARTIFACT_NAME }}
1
+ # Managed by @plainconceptsplatform/workflows. Source: loops/templates/ci/app-ci-dotnet-next.yml. Update with `workflows update --force`; consumer edits may be overwritten.
2
+ name: "App: CI"
3
+
4
+ on:
5
+ pull_request:
6
+ schedule:
7
+ - cron: "0 6 * * 1"
8
+ workflow_dispatch:
9
+ workflow_call:
10
+ secrets:
11
+ NPM_REGISTRY_TOKEN:
12
+ required: false
13
+
14
+ env:
15
+ DOTNET_VERSION: "10.0.x"
16
+ NODE_VERSION: "22"
17
+ PNPM_VERSION: "10"
18
+ API_WORKING_DIRECTORY: apps/api
19
+ DOTNET_SOLUTION: App.slnx
20
+ UNIT_TEST_PROJECT: tests/App.UnitTests/App.UnitTests.csproj
21
+ PARITY_TEST_PROJECT: tests/App.ParityTests/App.ParityTests.csproj
22
+ INTEGRATION_TEST_PROJECT: tests/App.IntegrationTests/App.IntegrationTests.csproj
23
+ # Per-PR mutation gate. Stryker mutates only files changed since the PR base (--since),
24
+ # so a green unit suite that never actually pins behaviour fails here instead of shipping.
25
+ # dotnet-stryker is pinned in the api tools manifest (.config/dotnet-tools.json / dotnet-tools.json).
26
+ MUTATION_TARGET_PROJECT: src/App.Domain/App.Domain.csproj
27
+ MUTATION_TEST_PROJECT: tests/App.UnitTests/App.UnitTests.csproj
28
+ MUTATION_THRESHOLD: "60"
29
+ COVERAGE_SETTINGS: coverage.runsettings
30
+ COVERAGE_CHECK_COMMAND: node ../../scripts/check-coverage.mjs TestResults/merged/Cobertura.xml
31
+ MIGRATION_PROJECT: src/App.Infrastructure/App.Infrastructure.csproj
32
+ MIGRATION_STARTUP_PROJECT: src/App.Api/App.Api.csproj
33
+ SEED_PROJECT: src/App.Seed
34
+ PRIMARY_DATABASE: app
35
+ DATA_LAYER_DATABASE: numa_dl
36
+ SQL_SA_PASSWORD: "YourStrong!Passw0rd"
37
+ WEB_PACKAGE: "@app/web"
38
+ NPM_REGISTRY_URL: "//npm.pkg.github.com/"
39
+ TRUFFLEHOG_IMAGE: "trufflesecurity/trufflehog:latest"
40
+ SEMGREP_IMAGE: "semgrep/semgrep:1.168.0"
41
+ SBOM_ARTIFACT_NAME: app-sbom.cyclonedx.json
42
+ MIGRATION_CONTEXT: AppDbContext
43
+
44
+ permissions:
45
+ contents: read
46
+
47
+ concurrency:
48
+ group: ci-${{ github.workflow }}-${{ github.head_ref || github.ref }}
49
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
50
+
51
+ jobs:
52
+ api:
53
+ name: API (.NET)
54
+ if: github.event_name != 'schedule'
55
+ runs-on: agents-arc
56
+ timeout-minutes: 30
57
+ services:
58
+ sqlserver:
59
+ image: mcr.microsoft.com/mssql/server:2022-latest
60
+ env:
61
+ ACCEPT_EULA: "Y"
62
+ MSSQL_SA_PASSWORD: ${{ env.SQL_SA_PASSWORD }}
63
+ ports:
64
+ # Ephemeral host port. Four runners share this machine, so a fixed 1433 means the
65
+ # second concurrent job fails at Initialize containers with "Docker start fail with
66
+ # exit code 1". Steps read the assigned port from the job.services context.
67
+ - 1433
68
+ options: >-
69
+ --health-cmd "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P '${{ env.SQL_SA_PASSWORD }}' -Q 'SELECT 1' -C || exit 1"
70
+ --health-interval 10s --health-timeout 5s --health-retries 12 --health-start-period 30s
71
+ defaults:
72
+ run:
73
+ working-directory: ${{ env.API_WORKING_DIRECTORY }}
74
+ steps:
75
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
76
+ - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6
77
+ with:
78
+ dotnet-version: ${{ env.DOTNET_VERSION }}
79
+ - name: Restore tools, solution, and build
80
+ run: |
81
+ dotnet tool restore
82
+ dotnet restore "$DOTNET_SOLUTION"
83
+ dotnet build "$DOTNET_SOLUTION" --no-restore -c Release
84
+ - name: Unit tests with coverage
85
+ run: dotnet test "$UNIT_TEST_PROJECT" --no-build -c Release --collect:"XPlat Code Coverage" --settings "$COVERAGE_SETTINGS" --results-directory TestResults
86
+ - name: Parity tests
87
+ run: dotnet test "$PARITY_TEST_PROJECT" --no-build -c Release
88
+ - name: Create test databases
89
+ run: |
90
+ for database in "$PRIMARY_DATABASE" "$DATA_LAYER_DATABASE"; do
91
+ docker exec ${{ job.services.sqlserver.id }} /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$SQL_SA_PASSWORD" -C -Q "IF DB_ID('$database') IS NULL CREATE DATABASE [$database];"
92
+ done
93
+ - name: Apply migrations and seed test data
94
+ env:
95
+ ConnectionStrings__App: Server=localhost,${{ job.services.sqlserver.ports['1433'] }};Database=${{ env.PRIMARY_DATABASE }};User Id=sa;Password=${{ env.SQL_SA_PASSWORD }};TrustServerCertificate=true
96
+ DataLayer__ConnectionString: Server=localhost,${{ job.services.sqlserver.ports['1433'] }};Database=${{ env.DATA_LAYER_DATABASE }};User Id=sa;Password=${{ env.SQL_SA_PASSWORD }};TrustServerCertificate=true
97
+ DATALAYER_SEED: "true"
98
+ run: |
99
+ dotnet ef database update -p "$MIGRATION_PROJECT" -s "$MIGRATION_STARTUP_PROJECT" --context "$MIGRATION_CONTEXT"
100
+ dotnet run --project "$SEED_PROJECT" --no-build -c Release
101
+ - name: Integration tests with coverage
102
+ env:
103
+ ConnectionStrings__App: Server=localhost,${{ job.services.sqlserver.ports['1433'] }};Database=${{ env.PRIMARY_DATABASE }};User Id=sa;Password=${{ env.SQL_SA_PASSWORD }};TrustServerCertificate=true
104
+ DataLayer__ConnectionString: Server=localhost,${{ job.services.sqlserver.ports['1433'] }};Database=${{ env.DATA_LAYER_DATABASE }};User Id=sa;Password=${{ env.SQL_SA_PASSWORD }};TrustServerCertificate=true
105
+ run: dotnet test "$INTEGRATION_TEST_PROJECT" --no-build -c Release --collect:"XPlat Code Coverage" --settings "$COVERAGE_SETTINGS" --results-directory TestResults
106
+ - name: Enforce merged coverage floor
107
+ run: |
108
+ dotnet tool install --global dotnet-reportgenerator-globaltool
109
+ export PATH="$PATH:$HOME/.dotnet/tools" # ephemeral ARC runners do not have dotnet tools on PATH
110
+ reportgenerator -reports:"TestResults/**/coverage.cobertura.xml" -targetdir:TestResults/merged -reporttypes:Cobertura
111
+ bash -c "$COVERAGE_CHECK_COMMAND"
112
+
113
+ mutation-api:
114
+ name: Mutation (.NET, diff)
115
+ if: github.event_name == 'pull_request'
116
+ runs-on: agents-arc
117
+ timeout-minutes: 25
118
+ defaults:
119
+ run:
120
+ working-directory: ${{ env.API_WORKING_DIRECTORY }}
121
+ steps:
122
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
123
+ with:
124
+ # Stryker --since diffs against the PR base; it needs history, not a shallow tip.
125
+ fetch-depth: 0
126
+ - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6
127
+ with:
128
+ dotnet-version: ${{ env.DOTNET_VERSION }}
129
+ - name: Detect backend changes
130
+ id: scope
131
+ working-directory: ${{ github.workspace }}
132
+ run: |
133
+ git fetch origin "$GITHUB_BASE_REF" --depth=1
134
+ if git diff --name-only "origin/$GITHUB_BASE_REF" HEAD | grep -qE "^${API_WORKING_DIRECTORY}/"; then
135
+ echo "run=true" >> "$GITHUB_OUTPUT"
136
+ else
137
+ echo "run=false" >> "$GITHUB_OUTPUT"
138
+ echo "No ${API_WORKING_DIRECTORY} changes in this PR; skipping .NET mutation."
139
+ fi
140
+ - name: Restore tools and build
141
+ if: steps.scope.outputs.run == 'true'
142
+ run: |
143
+ dotnet tool restore
144
+ dotnet build "$DOTNET_SOLUTION" -c Release
145
+ - name: Stryker.NET (changed files only)
146
+ if: steps.scope.outputs.run == 'true'
147
+ run: |
148
+ export PATH="$PATH:$HOME/.dotnet/tools" # ephemeral ARC runners do not have dotnet tools on PATH
149
+ dotnet stryker \
150
+ --project "$MUTATION_TARGET_PROJECT" \
151
+ --test-projects "$MUTATION_TEST_PROJECT" \
152
+ --since:"origin/$GITHUB_BASE_REF" \
153
+ --concurrency 2 \
154
+ --threshold-high "$MUTATION_THRESHOLD" \
155
+ --threshold-low "$MUTATION_THRESHOLD" \
156
+ --break-at "$MUTATION_THRESHOLD" \
157
+ --reporter progress --reporter json
158
+
159
+ web:
160
+ name: Web (Next.js)
161
+ if: github.event_name != 'schedule'
162
+ runs-on: agents-arc
163
+ timeout-minutes: 15
164
+ steps:
165
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
166
+ - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
167
+ with:
168
+ version: ${{ env.PNPM_VERSION }}
169
+ run_install: false
170
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
171
+ with:
172
+ node-version: ${{ env.NODE_VERSION }}
173
+ cache: pnpm
174
+ cache-dependency-path: pnpm-lock.yaml
175
+ - name: Configure optional private registry authentication
176
+ env:
177
+ NPM_REGISTRY_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}
178
+ run: |
179
+ if [ -n "$NPM_REGISTRY_TOKEN" ]; then
180
+ echo "${NPM_REGISTRY_URL}:_authToken=$NPM_REGISTRY_TOKEN" >> .npmrc
181
+ fi
182
+ - run: pnpm install --frozen-lockfile
183
+ - name: Lint, test, verify boundaries, and build
184
+ run: |
185
+ pnpm --filter "$WEB_PACKAGE" run lint
186
+ pnpm --filter "$WEB_PACKAGE" run test
187
+ pnpm --filter "$WEB_PACKAGE" run lint:fsd
188
+ pnpm --filter "$WEB_PACKAGE" run build
189
+
190
+ mutation-web:
191
+ name: Mutation (web, diff)
192
+ if: github.event_name == 'pull_request'
193
+ runs-on: agents-arc
194
+ timeout-minutes: 25
195
+ steps:
196
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
197
+ with:
198
+ fetch-depth: 0
199
+ - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
200
+ with:
201
+ version: ${{ env.PNPM_VERSION }}
202
+ run_install: false
203
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
204
+ with:
205
+ node-version: ${{ env.NODE_VERSION }}
206
+ cache: pnpm
207
+ cache-dependency-path: pnpm-lock.yaml
208
+ - name: Detect web changes
209
+ id: scope
210
+ run: |
211
+ git fetch origin "$GITHUB_BASE_REF" --depth=1
212
+ if git diff --name-only "origin/$GITHUB_BASE_REF" HEAD | grep -qE "^apps/web/"; then
213
+ echo "run=true" >> "$GITHUB_OUTPUT"
214
+ else
215
+ echo "run=false" >> "$GITHUB_OUTPUT"
216
+ echo "No apps/web changes in this PR; skipping web mutation."
217
+ fi
218
+ - name: Configure optional private registry authentication
219
+ if: steps.scope.outputs.run == 'true'
220
+ env:
221
+ NPM_REGISTRY_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}
222
+ run: |
223
+ if [ -n "$NPM_REGISTRY_TOKEN" ]; then
224
+ echo "${NPM_REGISTRY_URL}:_authToken=$NPM_REGISTRY_TOKEN" >> .npmrc
225
+ fi
226
+ - name: Install and run StrykerJS (changed files only)
227
+ if: steps.scope.outputs.run == 'true'
228
+ run: |
229
+ pnpm install --frozen-lockfile
230
+ pnpm --filter "$WEB_PACKAGE" exec stryker run --since="origin/$GITHUB_BASE_REF"
231
+
232
+ secret-scan:
233
+ name: Secret scan (TruffleHog)
234
+ runs-on: agents-arc
235
+ steps:
236
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
237
+ - run: docker run --rm -v "${{ github.workspace }}:/repo" "$TRUFFLEHOG_IMAGE" filesystem /repo --only-verified --fail --no-update
238
+
239
+ deps-and-iac-scan:
240
+ name: Dependencies and IaC (Trivy)
241
+ runs-on: agents-arc
242
+ steps:
243
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
244
+ - uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
245
+ with:
246
+ scan-type: fs
247
+ scanners: vuln,misconfig,secret
248
+ severity: CRITICAL,HIGH
249
+ ignore-unfixed: true
250
+ exit-code: "1"
251
+ format: table
252
+
253
+ sast-scan:
254
+ name: SAST (Semgrep)
255
+ runs-on: agents-arc
256
+ steps:
257
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
258
+ - run: docker run --rm -v "${{ github.workspace }}:/src" -w /src "$SEMGREP_IMAGE" semgrep scan --error --metrics=off --oss-only --disable-version-check --config p/default --config p/security-audit --config p/secrets --config p/csharp --config p/typescript --config p/react --exclude-rule yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag
259
+
260
+ sbom:
261
+ name: SBOM (CycloneDX)
262
+ runs-on: agents-arc
263
+ steps:
264
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
265
+ - uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
266
+ with:
267
+ format: cyclonedx-json
268
+ artifact-name: ${{ env.SBOM_ARTIFACT_NAME }}