code-foundry 1.53.5 → 1.53.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/eval.yml +10 -1
- package/.github/workflows/test.yml +22 -5
- package/.github/workflows/validation-billing.yml +14 -1
- package/.github/workflows/validation-no-codeql.yml +20 -3
- package/.github/workflows/validation.yml +20 -3
- package/.github/workflows/validation_self-ci.yml +8 -0
- package/CHANGELOG.md +8 -8
- package/docs/WORKFLOWS.md +10 -0
- package/package.json +1 -1
- package/src/runtime-core.mjs +23 -0
|
@@ -26,6 +26,15 @@ on:
|
|
|
26
26
|
required: false
|
|
27
27
|
type: string
|
|
28
28
|
default: task-result
|
|
29
|
+
idle-lanes:
|
|
30
|
+
description: >-
|
|
31
|
+
Comma-separated lanes the caller's planner found no entrypoint for
|
|
32
|
+
(integration, e2e, smoke, performance, eval). Their jobs are
|
|
33
|
+
skipped instead of allocating a runner for a no-op. Empty runs every
|
|
34
|
+
lane.
|
|
35
|
+
required: false
|
|
36
|
+
type: string
|
|
37
|
+
default: ''
|
|
29
38
|
|
|
30
39
|
permissions:
|
|
31
40
|
contents: read
|
|
@@ -51,7 +60,7 @@ concurrency:
|
|
|
51
60
|
jobs:
|
|
52
61
|
eval:
|
|
53
62
|
name: Eval
|
|
54
|
-
if: vars.CI_BILLING_PAUSED != 'true'
|
|
63
|
+
if: vars.CI_BILLING_PAUSED != 'true' && !contains(format(',{0},', inputs.idle-lanes), ',eval,')
|
|
55
64
|
runs-on: ${{ inputs.runner }}
|
|
56
65
|
timeout-minutes: 30
|
|
57
66
|
steps:
|
|
@@ -53,6 +53,15 @@ on:
|
|
|
53
53
|
required: false
|
|
54
54
|
type: string
|
|
55
55
|
default: '1'
|
|
56
|
+
idle-lanes:
|
|
57
|
+
description: >-
|
|
58
|
+
Comma-separated lanes the caller's planner found no entrypoint for
|
|
59
|
+
(integration, e2e, smoke, performance, eval). Their jobs are
|
|
60
|
+
skipped instead of allocating a runner for a no-op. Empty runs every
|
|
61
|
+
lane.
|
|
62
|
+
required: false
|
|
63
|
+
type: string
|
|
64
|
+
default: ''
|
|
56
65
|
secrets:
|
|
57
66
|
TURBO_TOKEN:
|
|
58
67
|
required: false
|
|
@@ -171,7 +180,7 @@ jobs:
|
|
|
171
180
|
|
|
172
181
|
performance:
|
|
173
182
|
name: Performance
|
|
174
|
-
if: vars.CI_BILLING_PAUSED != 'true'
|
|
183
|
+
if: vars.CI_BILLING_PAUSED != 'true' && !contains(format(',{0},', inputs.idle-lanes), ',performance,')
|
|
175
184
|
runs-on: ${{ inputs.performance-runner }}
|
|
176
185
|
timeout-minutes: 30
|
|
177
186
|
steps:
|
|
@@ -260,7 +269,9 @@ jobs:
|
|
|
260
269
|
|
|
261
270
|
integration:
|
|
262
271
|
name: Integration
|
|
263
|
-
if:
|
|
272
|
+
if: >-
|
|
273
|
+
vars.CI_BILLING_PAUSED != 'true' && inputs.unit-only != true && inputs.integration-total-shards != '2' &&
|
|
274
|
+
!contains(format(',{0},', inputs.idle-lanes), ',integration,')
|
|
264
275
|
runs-on: ${{ inputs.runner }}
|
|
265
276
|
timeout-minutes: 30
|
|
266
277
|
steps:
|
|
@@ -337,7 +348,9 @@ jobs:
|
|
|
337
348
|
|
|
338
349
|
integration-sharded:
|
|
339
350
|
name: Integration shard
|
|
340
|
-
if:
|
|
351
|
+
if: >-
|
|
352
|
+
vars.CI_BILLING_PAUSED != 'true' && inputs.unit-only != true && inputs.integration-total-shards == '2' &&
|
|
353
|
+
!contains(format(',{0},', inputs.idle-lanes), ',integration,')
|
|
341
354
|
runs-on: ${{ inputs.runner }}
|
|
342
355
|
timeout-minutes: 30
|
|
343
356
|
strategy:
|
|
@@ -417,7 +430,9 @@ jobs:
|
|
|
417
430
|
|
|
418
431
|
e2e:
|
|
419
432
|
name: E2E
|
|
420
|
-
if:
|
|
433
|
+
if: >-
|
|
434
|
+
vars.CI_BILLING_PAUSED != 'true' && inputs.unit-only != true &&
|
|
435
|
+
!contains(format(',{0},', inputs.idle-lanes), ',e2e,')
|
|
421
436
|
runs-on: ${{ inputs.runner }}
|
|
422
437
|
timeout-minutes: 45
|
|
423
438
|
# Shards duplicate the whole job, but each shard owns a runner-local test
|
|
@@ -514,7 +529,9 @@ jobs:
|
|
|
514
529
|
|
|
515
530
|
smoke:
|
|
516
531
|
name: Smoke
|
|
517
|
-
if:
|
|
532
|
+
if: >-
|
|
533
|
+
vars.CI_BILLING_PAUSED != 'true' && inputs.unit-only != true &&
|
|
534
|
+
!contains(format(',{0},', inputs.idle-lanes), ',smoke,')
|
|
518
535
|
runs-on: ${{ inputs.runner }}
|
|
519
536
|
timeout-minutes: 15
|
|
520
537
|
steps:
|
|
@@ -115,6 +115,15 @@ on:
|
|
|
115
115
|
required: false
|
|
116
116
|
type: string
|
|
117
117
|
default: '1'
|
|
118
|
+
idle-lanes:
|
|
119
|
+
description: >-
|
|
120
|
+
Comma-separated lanes the caller's planner found no entrypoint for
|
|
121
|
+
(integration, e2e, smoke, performance, eval). Only the separate E2E
|
|
122
|
+
job is skipped here; the merged fast lanes keep their own Detect
|
|
123
|
+
steps. Empty runs every lane.
|
|
124
|
+
required: false
|
|
125
|
+
type: string
|
|
126
|
+
default: ''
|
|
118
127
|
secrets:
|
|
119
128
|
TURBO_TOKEN:
|
|
120
129
|
required: false
|
|
@@ -787,7 +796,11 @@ jobs:
|
|
|
787
796
|
|
|
788
797
|
e2e:
|
|
789
798
|
name: E2E
|
|
790
|
-
|
|
799
|
+
# The gate already accepts a skipped E2E lane; a planner-idle E2E lane
|
|
800
|
+
# skips here instead of allocating a runner for a no-op.
|
|
801
|
+
if: >-
|
|
802
|
+
vars.CI_BILLING_PAUSED != 'true' && inputs.mode == 'audit' &&
|
|
803
|
+
!contains(format(',{0},', inputs.idle-lanes), ',e2e,')
|
|
791
804
|
permissions:
|
|
792
805
|
actions: read
|
|
793
806
|
contents: read
|
|
@@ -75,6 +75,15 @@ on:
|
|
|
75
75
|
required: false
|
|
76
76
|
type: number
|
|
77
77
|
default: 1
|
|
78
|
+
idle-lanes:
|
|
79
|
+
description: >-
|
|
80
|
+
Comma-separated lanes the caller's planner found no entrypoint for
|
|
81
|
+
(integration, e2e, smoke, performance, eval). Their jobs are
|
|
82
|
+
skipped instead of allocating a runner for a no-op. Empty runs every
|
|
83
|
+
lane.
|
|
84
|
+
required: false
|
|
85
|
+
type: string
|
|
86
|
+
default: ''
|
|
78
87
|
artifact-prefix:
|
|
79
88
|
description: Prefix for task receipts; use a unique value when calling this workflow more than once.
|
|
80
89
|
required: false
|
|
@@ -181,6 +190,7 @@ jobs:
|
|
|
181
190
|
integration-total-shards: ${{ inputs.integration-total-shards }}
|
|
182
191
|
e2e-shard-list: ${{ inputs.e2e-shard-list }}
|
|
183
192
|
e2e-total-shards: ${{ inputs.e2e-total-shards }}
|
|
193
|
+
idle-lanes: ${{ inputs.idle-lanes }}
|
|
184
194
|
secrets:
|
|
185
195
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
186
196
|
|
|
@@ -209,6 +219,7 @@ jobs:
|
|
|
209
219
|
runtime-ref: ${{ inputs.runtime-ref }}
|
|
210
220
|
runner: ${{ inputs.eval-runner }}
|
|
211
221
|
artifact-prefix: ${{ inputs.artifact-prefix }}
|
|
222
|
+
idle-lanes: ${{ inputs.idle-lanes }}
|
|
212
223
|
|
|
213
224
|
# Aggregate check: "Validation / Gate" (caller job name + this job name).
|
|
214
225
|
# Always evaluates; only jobs required for the mode must have succeeded.
|
|
@@ -229,7 +240,11 @@ jobs:
|
|
|
229
240
|
FOUNDRY_CI: ${{ needs.ci.result }}
|
|
230
241
|
FOUNDRY_TEST: ${{ needs.test.result }}
|
|
231
242
|
FOUNDRY_SECURITY: ${{ needs.security.result }}
|
|
232
|
-
|
|
243
|
+
# An eval lane the caller planned as idle (no eval script, no
|
|
244
|
+
# eval_command, or eval: false) never starts, so the whole Eval
|
|
245
|
+
# workflow reports skipped. Count that exactly like the no-op success
|
|
246
|
+
# it reported before; any other skip still fails the gate.
|
|
247
|
+
FOUNDRY_EVAL: ${{ (needs.eval.result == 'skipped' && contains(format(',{0},', inputs.idle-lanes), ',eval,') && 'success') || needs.eval.result }}
|
|
233
248
|
FOUNDRY_SOURCE_QUALIFICATION: ${{ needs.qualification.outputs.reuse }}
|
|
234
249
|
FOUNDRY_QUALIFICATION: ${{ needs.qualification.result }}
|
|
235
250
|
# This workflow is selected only for repositories whose canonical
|
|
@@ -276,7 +291,9 @@ jobs:
|
|
|
276
291
|
inputs.release-qualification-receipts == true &&
|
|
277
292
|
github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'main' &&
|
|
278
293
|
needs.ci.result == 'success' && needs.test.result == 'success' &&
|
|
279
|
-
needs.security.result == 'success' &&
|
|
294
|
+
needs.security.result == 'success' &&
|
|
295
|
+
(needs.eval.result == 'success' ||
|
|
296
|
+
(needs.eval.result == 'skipped' && contains(format(',{0},', inputs.idle-lanes), ',eval,'))) &&
|
|
280
297
|
needs.gate.result == 'success'
|
|
281
298
|
permissions:
|
|
282
299
|
actions: read
|
|
@@ -291,5 +308,5 @@ jobs:
|
|
|
291
308
|
runtime-repository: ${{ inputs.runtime-repository }}
|
|
292
309
|
runtime-ref: ${{ inputs.runtime-ref }}
|
|
293
310
|
job-results-json: >-
|
|
294
|
-
{"ci":"${{ needs.ci.result }}","test":"${{ needs.test.result }}","security":"${{ needs.security.result }}","codeql":"not-configured","eval":"${{ needs.eval.result }}","gate":"${{ needs.gate.result }}"}
|
|
311
|
+
{"ci":"${{ needs.ci.result }}","test":"${{ needs.test.result }}","security":"${{ needs.security.result }}","codeql":"not-configured","eval":"${{ (needs.eval.result == 'skipped' && contains(format(',{0},', inputs.idle-lanes), ',eval,') && 'success') || needs.eval.result }}","gate":"${{ needs.gate.result }}"}
|
|
295
312
|
required-job-names-json: '["Validation / Gate"]'
|
|
@@ -90,6 +90,15 @@ on:
|
|
|
90
90
|
required: false
|
|
91
91
|
type: string
|
|
92
92
|
default: '1'
|
|
93
|
+
idle-lanes:
|
|
94
|
+
description: >-
|
|
95
|
+
Comma-separated lanes the caller's planner found no entrypoint for
|
|
96
|
+
(integration, e2e, smoke, performance, eval). Their jobs are
|
|
97
|
+
skipped instead of allocating a runner for a no-op. Empty runs every
|
|
98
|
+
lane.
|
|
99
|
+
required: false
|
|
100
|
+
type: string
|
|
101
|
+
default: ''
|
|
93
102
|
artifact-prefix:
|
|
94
103
|
description: Prefix for task receipts; use a unique value when calling this workflow more than once.
|
|
95
104
|
required: false
|
|
@@ -181,6 +190,7 @@ jobs:
|
|
|
181
190
|
integration-total-shards: ${{ inputs.integration-total-shards }}
|
|
182
191
|
e2e-shard-list: ${{ inputs.e2e-shard-list }}
|
|
183
192
|
e2e-total-shards: ${{ inputs.e2e-total-shards }}
|
|
193
|
+
idle-lanes: ${{ inputs.idle-lanes }}
|
|
184
194
|
secrets:
|
|
185
195
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
186
196
|
|
|
@@ -228,6 +238,7 @@ jobs:
|
|
|
228
238
|
runtime-ref: ${{ inputs.runtime-ref }}
|
|
229
239
|
runner: ${{ inputs.eval-runner }}
|
|
230
240
|
artifact-prefix: ${{ inputs.artifact-prefix }}
|
|
241
|
+
idle-lanes: ${{ inputs.idle-lanes }}
|
|
231
242
|
|
|
232
243
|
# Aggregate check: "Validation / Gate" (caller job name + this job name).
|
|
233
244
|
# Always evaluates; only jobs required for the mode must have succeeded.
|
|
@@ -249,7 +260,11 @@ jobs:
|
|
|
249
260
|
FOUNDRY_TEST: ${{ needs.test.result }}
|
|
250
261
|
FOUNDRY_SECURITY: ${{ needs.security.result }}
|
|
251
262
|
FOUNDRY_CODEQL: ${{ needs.codeql.result }}
|
|
252
|
-
|
|
263
|
+
# An eval lane the caller planned as idle (no eval script, no
|
|
264
|
+
# eval_command, or eval: false) never starts, so the whole Eval
|
|
265
|
+
# workflow reports skipped. Count that exactly like the no-op success
|
|
266
|
+
# it reported before; any other skip still fails the gate.
|
|
267
|
+
FOUNDRY_EVAL: ${{ (needs.eval.result == 'skipped' && contains(format(',{0},', inputs.idle-lanes), ',eval,') && 'success') || needs.eval.result }}
|
|
253
268
|
FOUNDRY_SOURCE_QUALIFICATION: ${{ needs.qualification.outputs.reuse }}
|
|
254
269
|
FOUNDRY_QUALIFICATION: ${{ needs.qualification.result }}
|
|
255
270
|
steps:
|
|
@@ -293,7 +308,9 @@ jobs:
|
|
|
293
308
|
github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'main' &&
|
|
294
309
|
needs.ci.result == 'success' && needs.test.result == 'success' &&
|
|
295
310
|
needs.security.result == 'success' && needs.codeql.result == 'success' &&
|
|
296
|
-
needs.eval.result == 'success'
|
|
311
|
+
(needs.eval.result == 'success' ||
|
|
312
|
+
(needs.eval.result == 'skipped' && contains(format(',{0},', inputs.idle-lanes), ',eval,'))) &&
|
|
313
|
+
needs.gate.result == 'success'
|
|
297
314
|
permissions:
|
|
298
315
|
actions: read
|
|
299
316
|
contents: read
|
|
@@ -307,5 +324,5 @@ jobs:
|
|
|
307
324
|
runtime-repository: ${{ inputs.runtime-repository }}
|
|
308
325
|
runtime-ref: ${{ inputs.runtime-ref }}
|
|
309
326
|
job-results-json: >-
|
|
310
|
-
{"ci":"${{ needs.ci.result }}","test":"${{ needs.test.result }}","security":"${{ needs.security.result }}","codeql":"${{ needs.codeql.result }}","eval":"${{ needs.eval.result }}","gate":"${{ needs.gate.result }}"}
|
|
327
|
+
{"ci":"${{ needs.ci.result }}","test":"${{ needs.test.result }}","security":"${{ needs.security.result }}","codeql":"${{ needs.codeql.result }}","eval":"${{ (needs.eval.result == 'skipped' && contains(format(',{0},', inputs.idle-lanes), ',eval,') && 'success') || needs.eval.result }}","gate":"${{ needs.gate.result }}"}
|
|
311
328
|
required-job-names-json: '["Validation / Gate"]'
|
|
@@ -34,6 +34,7 @@ jobs:
|
|
|
34
34
|
contents: read
|
|
35
35
|
outputs:
|
|
36
36
|
mode: ${{ steps.classify.outputs.mode }}
|
|
37
|
+
idle-lanes: ${{ steps.lanes.outputs.idle_lanes }}
|
|
37
38
|
steps:
|
|
38
39
|
# The runtime classifies policy from the consumer repository's config.
|
|
39
40
|
# Keep this checkout before the isolated runtime checkout below.
|
|
@@ -61,6 +62,12 @@ jobs:
|
|
|
61
62
|
FOUNDRY_BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
62
63
|
FOUNDRY_HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
|
63
64
|
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" validation mode >> "$GITHUB_OUTPUT"
|
|
65
|
+
# Lanes without an entrypoint (no package script, configured command,
|
|
66
|
+
# or test files) are skipped by the orchestrator rather than started on
|
|
67
|
+
# a runner only to report a no-op. Detection errors keep the lane.
|
|
68
|
+
- name: Plan lanes
|
|
69
|
+
id: lanes
|
|
70
|
+
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" validation lanes >> "$GITHUB_OUTPUT"
|
|
64
71
|
|
|
65
72
|
# The stable aggregate check name is the caller job name plus the
|
|
66
73
|
# orchestrator gate job name: "Validation / Gate". Keep both names fixed.
|
|
@@ -107,6 +114,7 @@ jobs:
|
|
|
107
114
|
integration-total-shards: '1'
|
|
108
115
|
e2e-shard-list: '1'
|
|
109
116
|
e2e-total-shards: '1'
|
|
117
|
+
idle-lanes: ${{ needs.mode.outputs.idle-lanes }}
|
|
110
118
|
secrets:
|
|
111
119
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
112
120
|
NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.53.6](https://github.com/0xPlayerOne/code-foundry/compare/v1.53.5...v1.53.6) (2026-10-31)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Performance
|
|
7
|
+
|
|
8
|
+
* **validation:** skip lane jobs that have nothing to run ([#825](https://github.com/0xPlayerOne/code-foundry/issues/825)) ([4d04f62](https://github.com/0xPlayerOne/code-foundry/commit/4d04f627bfac572bc056b5a5f448b66c17500c00))
|
|
9
|
+
|
|
3
10
|
## [1.53.5](https://github.com/0xPlayerOne/code-foundry/compare/v1.53.4...v1.53.5) (2026-10-31)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -143,16 +150,9 @@
|
|
|
143
150
|
|
|
144
151
|
* support isolated integration test shards ([#783](https://github.com/0xPlayerOne/code-foundry/issues/783)) ([ad1123f](https://github.com/0xPlayerOne/code-foundry/commit/ad1123ff3bb9654bfbdaef4219490cca3e7e3d20))
|
|
145
152
|
|
|
146
|
-
## [1.48.9](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.8...v1.48.9) (2026-10-26)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
### Bug Fixes
|
|
150
|
-
|
|
151
|
-
* **release:** preserve fallback stdin and paginate existing releases ([#781](https://github.com/0xPlayerOne/code-foundry/issues/781)) ([ede4d2c](https://github.com/0xPlayerOne/code-foundry/commit/ede4d2c7e3bc7d1174580c9a0bc83bdaa160c2da))
|
|
152
|
-
|
|
153
153
|
<!-- code-foundry: packaged changelog -->
|
|
154
154
|
|
|
155
155
|
---
|
|
156
156
|
|
|
157
|
-
This packaged changelog lists the 20 most recent releases;
|
|
157
|
+
This packaged changelog lists the 20 most recent releases; 277 older releases are omitted to keep the package small.
|
|
158
158
|
See the [full changelog](https://github.com/0xPlayerOne/code-foundry/blob/main/CHANGELOG.md) for the complete history.
|
package/docs/WORKFLOWS.md
CHANGED
|
@@ -310,6 +310,16 @@ repositories by failing closed unless every generated check succeeds. An
|
|
|
310
310
|
empty analysis matrix while analysis is enabled fails the build instead of
|
|
311
311
|
silently skipping every analyzer.
|
|
312
312
|
|
|
313
|
+
The caller's `Mode` job also plans lanes: it runs the same applicability check
|
|
314
|
+
each lane's Detect step runs and passes the integration, E2E, smoke,
|
|
315
|
+
performance, and eval lanes that have no package script, configured command,
|
|
316
|
+
or test files to the orchestrator as `idle-lanes`. Those jobs are skipped
|
|
317
|
+
instead of queuing a runner only to report a no-op. Unit always keeps its job,
|
|
318
|
+
and a lane whose detection errors keeps its job so the error surfaces there.
|
|
319
|
+
The gate counts a planned idle Eval workflow (which reports `skipped`) as the
|
|
320
|
+
no-op success it reported before; any other skip, and every lane with work,
|
|
321
|
+
is still required. Callers that pass no `idle-lanes` run every lane as before.
|
|
322
|
+
|
|
313
323
|
## Security behavior
|
|
314
324
|
|
|
315
325
|
CodeQL is a separate workflow using GitHub's official actions. The default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-foundry",
|
|
3
|
-
"version": "1.53.
|
|
3
|
+
"version": "1.53.6",
|
|
4
4
|
"description": "A fast, language-aware repository factory for agent-ready workflows, testing, security, and release automation.",
|
|
5
5
|
"homepage": "https://github.com/0xPlayerOne/code-foundry#readme",
|
|
6
6
|
"bugs": {
|
package/src/runtime-core.mjs
CHANGED
|
@@ -354,6 +354,13 @@ function downgradeDocsOnlyPullRequest(mode) {
|
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Lanes that own a whole runner job and can be skipped when they have no work.
|
|
359
|
+
* Unit stays out: it is the one test lane every tier requires, so it always
|
|
360
|
+
* keeps its job.
|
|
361
|
+
*/
|
|
362
|
+
const PLANNED_LANES = ['integration', 'e2e', 'smoke', 'performance', 'eval']
|
|
363
|
+
|
|
357
364
|
/** @param {string} task */
|
|
358
365
|
function validation(task) {
|
|
359
366
|
if (task === 'mode') {
|
|
@@ -370,6 +377,22 @@ function validation(task) {
|
|
|
370
377
|
)
|
|
371
378
|
return
|
|
372
379
|
}
|
|
380
|
+
if (task === 'lanes') {
|
|
381
|
+
// Lanes with no entrypoint would only allocate a runner to report a
|
|
382
|
+
// no-op. The caller's mode job already holds the checkout, so it names
|
|
383
|
+
// them once and the orchestrator skips those jobs. This mirrors each
|
|
384
|
+
// lane's own Detect step (`relevant`) and fails open: a lane whose
|
|
385
|
+
// detection throws keeps its job so the job reports the error itself.
|
|
386
|
+
const idle = PLANNED_LANES.filter((lane) => {
|
|
387
|
+
try {
|
|
388
|
+
return !relevant(lane)
|
|
389
|
+
} catch {
|
|
390
|
+
return false
|
|
391
|
+
}
|
|
392
|
+
})
|
|
393
|
+
writeOutput('idle_lanes', idle.join(','))
|
|
394
|
+
return
|
|
395
|
+
}
|
|
373
396
|
if (task === 'gate') {
|
|
374
397
|
const mode = process.env.FOUNDRY_MODE ?? ''
|
|
375
398
|
const gate = evaluateValidationGate({
|