code-foundry 1.10.0 → 1.11.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.
- package/.github/CONTRIBUTING.md +2 -2
- package/.github/code-foundry.yml +4 -0
- package/.github/workflows/ci.yml +4 -0
- package/.github/workflows/codeql.yml +2 -0
- package/.github/workflows/security.yml +2 -0
- package/.github/workflows/test.yml +5 -0
- package/.github/workflows/validation-no-codeql.yml +1 -0
- package/.github/workflows/validation.yml +1 -0
- package/.github/workflows/validation_self-ci.yml +27 -4
- package/CHANGELOG.md +15 -0
- package/README.md +1 -1
- package/docs/CONFIGURATION.md +6 -1
- package/docs/README.md +1 -0
- package/docs/WORKFLOWS.md +9 -4
- package/docs/required-capabilities.md +82 -0
- package/package.json +1 -1
- package/src/commands/doctor.mjs +2 -3
- package/src/commands/sync.mjs +29 -3
- package/src/lib/github-doctor.mjs +1 -19
- package/src/lib/lockfiles.mjs +15 -0
- package/src/lib/task-policy.mjs +290 -0
- package/src/runtime-core.mjs +794 -0
- package/src/runtime.mjs +179 -764
package/.github/CONTRIBUTING.md
CHANGED
|
@@ -181,11 +181,11 @@ Keep pull requests focused and reviewable. Include screenshots or recordings for
|
|
|
181
181
|
| Exact Release Please pull request targeting `main` | Full validation: CI, full tests, Security, and CodeQL, ending in `Validation / Gate` |
|
|
182
182
|
| Scheduled or manual validation | Full audit tier |
|
|
183
183
|
| Push to a working branch | Draft PR workflow |
|
|
184
|
-
| Push to `main` | Release workflow;
|
|
184
|
+
| Push to `main` | Release workflow plus default-branch CodeQL scan; validation ran on the merged PR |
|
|
185
185
|
|
|
186
186
|
Draft pull requests do not start validation. The lightweight Draft Guard also converts ordinary pull requests opened, reopened, or updated while ready back to draft; it never checks out pull-request code and it excludes Release Please version heads, whose release workflow owns their state. Marking a pull request ready for review starts the applicable validation tier. Convert it back to draft after an update, then mark it ready again after every update so the required checks attach to the current head. Converting it to draft runs only the lightweight cancellation control.
|
|
187
187
|
|
|
188
|
-
Pull-request validation keys concurrency by event and pull-request head, so a newer update cancels its superseded run. Scheduled and manual audits use a separate caller pinned to the protected default branch; this prevents caller-selected runtime code from executing with default-branch cache access.
|
|
188
|
+
Pull-request validation keys concurrency by event and pull-request head, so a newer update cancels its superseded run. Scheduled and manual audits use a separate caller pinned to the protected default branch; this prevents caller-selected runtime code from executing with default-branch cache access. Pull-request and scheduled/manual callers use the mode-aware orchestrator, which fans out only the required jobs and concludes with the stable aggregate gate. Main pushes run the default-branch CodeQL lane separately.
|
|
189
189
|
|
|
190
190
|
Required checks are enforced by branch protection rulesets/branch protection. Do not duplicate their checklists in the pull request description; document validation commands and results instead.
|
|
191
191
|
|
package/.github/code-foundry.yml
CHANGED
|
@@ -23,7 +23,11 @@ unit_runner: ubuntu-slim
|
|
|
23
23
|
performance_runner: ubuntu-latest
|
|
24
24
|
cache_packages: auto
|
|
25
25
|
cache_build: auto
|
|
26
|
+
required_capabilities:
|
|
27
|
+
coverage_enforcement: auto
|
|
26
28
|
coverage_minimum: 80
|
|
29
|
+
coverage_metrics: lines
|
|
30
|
+
coverage_report:
|
|
27
31
|
turbo_remote: auto
|
|
28
32
|
ci_runner: ubuntu-latest
|
|
29
33
|
test_runner: ubuntu-latest
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -63,6 +63,7 @@ jobs:
|
|
|
63
63
|
sparse-checkout: |
|
|
64
64
|
.github/actions
|
|
65
65
|
src/lib
|
|
66
|
+
src/runtime-core.mjs
|
|
66
67
|
src/runtime.mjs
|
|
67
68
|
- name: Install runtime
|
|
68
69
|
run: |
|
|
@@ -107,6 +108,7 @@ jobs:
|
|
|
107
108
|
sparse-checkout: |
|
|
108
109
|
.github/actions
|
|
109
110
|
src/lib
|
|
111
|
+
src/runtime-core.mjs
|
|
110
112
|
src/runtime.mjs
|
|
111
113
|
- name: Install runtime
|
|
112
114
|
run: |
|
|
@@ -146,6 +148,7 @@ jobs:
|
|
|
146
148
|
sparse-checkout: |
|
|
147
149
|
.github/actions
|
|
148
150
|
src/lib
|
|
151
|
+
src/runtime-core.mjs
|
|
149
152
|
src/runtime.mjs
|
|
150
153
|
- name: Install runtime
|
|
151
154
|
run: |
|
|
@@ -190,6 +193,7 @@ jobs:
|
|
|
190
193
|
sparse-checkout: |
|
|
191
194
|
.github/actions
|
|
192
195
|
src/lib
|
|
196
|
+
src/runtime-core.mjs
|
|
193
197
|
src/runtime.mjs
|
|
194
198
|
- name: Install runtime
|
|
195
199
|
run: |
|
|
@@ -118,6 +118,7 @@ jobs:
|
|
|
118
118
|
sparse-checkout: |
|
|
119
119
|
.github/actions
|
|
120
120
|
src/lib
|
|
121
|
+
src/runtime-core.mjs
|
|
121
122
|
src/runtime.mjs
|
|
122
123
|
- name: Install runtime
|
|
123
124
|
run: |
|
|
@@ -210,6 +211,7 @@ jobs:
|
|
|
210
211
|
sparse-checkout: |
|
|
211
212
|
.github/actions
|
|
212
213
|
src/lib
|
|
214
|
+
src/runtime-core.mjs
|
|
213
215
|
src/runtime.mjs
|
|
214
216
|
- name: Install runtime
|
|
215
217
|
if: ${{ matrix.entry.changed == true }}
|
|
@@ -88,6 +88,7 @@ jobs:
|
|
|
88
88
|
sparse-checkout: |
|
|
89
89
|
.github/actions
|
|
90
90
|
src/lib
|
|
91
|
+
src/runtime-core.mjs
|
|
91
92
|
src/runtime.mjs
|
|
92
93
|
- name: Install runtime
|
|
93
94
|
run: |
|
|
@@ -210,6 +211,7 @@ jobs:
|
|
|
210
211
|
sparse-checkout: |
|
|
211
212
|
.github/actions
|
|
212
213
|
src/lib
|
|
214
|
+
src/runtime-core.mjs
|
|
213
215
|
src/runtime.mjs
|
|
214
216
|
- name: Install runtime
|
|
215
217
|
run: |
|
|
@@ -78,6 +78,7 @@ jobs:
|
|
|
78
78
|
sparse-checkout: |
|
|
79
79
|
.github/actions
|
|
80
80
|
src/lib
|
|
81
|
+
src/runtime-core.mjs
|
|
81
82
|
src/runtime.mjs
|
|
82
83
|
- name: Install runtime
|
|
83
84
|
run: |
|
|
@@ -143,6 +144,7 @@ jobs:
|
|
|
143
144
|
sparse-checkout: |
|
|
144
145
|
.github/actions
|
|
145
146
|
src/lib
|
|
147
|
+
src/runtime-core.mjs
|
|
146
148
|
src/runtime.mjs
|
|
147
149
|
- name: Install runtime
|
|
148
150
|
run: |
|
|
@@ -208,6 +210,7 @@ jobs:
|
|
|
208
210
|
sparse-checkout: |
|
|
209
211
|
.github/actions
|
|
210
212
|
src/lib
|
|
213
|
+
src/runtime-core.mjs
|
|
211
214
|
src/runtime.mjs
|
|
212
215
|
- name: Install runtime
|
|
213
216
|
run: |
|
|
@@ -257,6 +260,7 @@ jobs:
|
|
|
257
260
|
sparse-checkout: |
|
|
258
261
|
.github/actions
|
|
259
262
|
src/lib
|
|
263
|
+
src/runtime-core.mjs
|
|
260
264
|
src/runtime.mjs
|
|
261
265
|
- name: Install runtime
|
|
262
266
|
run: |
|
|
@@ -318,6 +322,7 @@ jobs:
|
|
|
318
322
|
sparse-checkout: |
|
|
319
323
|
.github/actions
|
|
320
324
|
src/lib
|
|
325
|
+
src/runtime-core.mjs
|
|
321
326
|
src/runtime.mjs
|
|
322
327
|
- name: Install runtime
|
|
323
328
|
run: |
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
name: Code Foundry
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
4
6
|
pull_request:
|
|
5
7
|
branches: [main, staging]
|
|
6
8
|
types:
|
|
@@ -18,9 +20,9 @@ concurrency:
|
|
|
18
20
|
jobs:
|
|
19
21
|
mode:
|
|
20
22
|
name: Mode
|
|
21
|
-
# The
|
|
22
|
-
#
|
|
23
|
-
if: vars.CI_BILLING_PAUSED != 'true'
|
|
23
|
+
# The mode classifier is only needed for pull requests. Main pushes use
|
|
24
|
+
# the dedicated default-branch CodeQL lane below.
|
|
25
|
+
if: vars.CI_BILLING_PAUSED != 'true' && github.event_name == 'pull_request'
|
|
24
26
|
runs-on: ubuntu-slim
|
|
25
27
|
timeout-minutes: 10
|
|
26
28
|
permissions:
|
|
@@ -43,6 +45,7 @@ jobs:
|
|
|
43
45
|
path: .github/.code-foundry
|
|
44
46
|
sparse-checkout: |
|
|
45
47
|
src/lib
|
|
48
|
+
src/runtime-core.mjs
|
|
46
49
|
src/runtime.mjs
|
|
47
50
|
- name: Install runtime
|
|
48
51
|
run: mv .github/.code-foundry "$RUNNER_TEMP/code-foundry"
|
|
@@ -59,7 +62,7 @@ jobs:
|
|
|
59
62
|
validation:
|
|
60
63
|
name: Validation
|
|
61
64
|
needs: mode
|
|
62
|
-
if: vars.CI_BILLING_PAUSED != 'true'
|
|
65
|
+
if: vars.CI_BILLING_PAUSED != 'true' && github.event_name == 'pull_request'
|
|
63
66
|
# Reusable workflows can only maintain or reduce the caller job's scopes.
|
|
64
67
|
# The audit tier needs security-events: write for CodeQL uploads.
|
|
65
68
|
permissions:
|
|
@@ -87,3 +90,23 @@ jobs:
|
|
|
87
90
|
secrets:
|
|
88
91
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
89
92
|
NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
|
|
93
|
+
|
|
94
|
+
# CodeQL requires a push analysis to populate default-branch code-scanning
|
|
95
|
+
# alerts. Keep the full validation gate pull-request-only so main pushes do
|
|
96
|
+
# not duplicate CI, test, or security work.
|
|
97
|
+
default-branch-codeql:
|
|
98
|
+
name: CodeQL (default branch)
|
|
99
|
+
if: vars.CI_BILLING_PAUSED != 'true' && github.event_name == 'push'
|
|
100
|
+
permissions:
|
|
101
|
+
actions: read
|
|
102
|
+
contents: read
|
|
103
|
+
packages: read
|
|
104
|
+
security-events: write
|
|
105
|
+
uses: ./.github/workflows/codeql.yml
|
|
106
|
+
with:
|
|
107
|
+
runtime-repository: 0xPlayerOne/code-foundry
|
|
108
|
+
runtime-ref: ${{ github.sha }}
|
|
109
|
+
runner: ubuntu-latest
|
|
110
|
+
rust-shards: '["all"]'
|
|
111
|
+
rust-threads: '1'
|
|
112
|
+
rust-max-parallel: 1
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.11.0](https://github.com/0xPlayerOne/code-foundry/compare/v1.10.1...v1.11.0) (2026-09-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **validation:** enforce required capabilities, coverage, and task evidence ([#534](https://github.com/0xPlayerOne/code-foundry/issues/534)) ([2e341d9](https://github.com/0xPlayerOne/code-foundry/commit/2e341d94381599f1d78ec1394d42883157b30ea6))
|
|
9
|
+
|
|
10
|
+
## [1.10.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.10.0...v1.10.1) (2026-09-09)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **doctor:** honor shared config and correctly identify npm/Yarn lockfiles ([#533](https://github.com/0xPlayerOne/code-foundry/issues/533)) ([6464409](https://github.com/0xPlayerOne/code-foundry/commit/646440907ad3fc47f63e27b9621c8e0025459964))
|
|
16
|
+
* **validation:** scan default branch with CodeQL on push ([#540](https://github.com/0xPlayerOne/code-foundry/issues/540)) ([98d8241](https://github.com/0xPlayerOne/code-foundry/commit/98d8241522c78e1b6d3f4f29a44053d33888edba))
|
|
17
|
+
|
|
3
18
|
## [1.10.0](https://github.com/0xPlayerOne/code-foundry/compare/v1.9.13...v1.10.0) (2026-09-08)
|
|
4
19
|
|
|
5
20
|
|
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ when present and otherwise uses native language tooling; choose `native` or
|
|
|
70
70
|
|
|
71
71
|
The standard workflow triggers are:
|
|
72
72
|
|
|
73
|
-
- Pushes to `main` (and `staging` when `git_workflow: staging-release` is configured).
|
|
73
|
+
- Pushes to `main` (and `staging` when `git_workflow: staging-release` is configured); main pushes also run the default-branch CodeQL scan.
|
|
74
74
|
- Pull requests targeting `main` (and `staging` in the staging-release topology).
|
|
75
75
|
- Draft PR automation for supported feature/fix branches.
|
|
76
76
|
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -36,10 +36,15 @@ repository manifests and source
|
|
|
36
36
|
| `package_manager` | `bun`, `pnpm`, `yarn`, `npm`, `none` | JavaScript setup |
|
|
37
37
|
| `toolchain` | `auto`, `native`, `mise` | Environment setup policy; defaults to `auto` |
|
|
38
38
|
| `staging_validation_mode` | `fast`, `audit` | Staging-release-only validation tier; omitted from direct repositories |
|
|
39
|
-
| `performance` | `auto`, `true`, `false` |
|
|
39
|
+
| `performance` | `auto`, `true`, `false` | `auto` discovers an optional task; `true` requires a supported performance entrypoint; `false` disables discovery |
|
|
40
40
|
| `performance_command` | JSON argv array or array of argv arrays | One or more ordered commands for non-package harnesses; package scripts take precedence |
|
|
41
41
|
| `performance_profile` | empty, `node-package` | Optional shared package import, memory, archive, and dependency budget harness |
|
|
42
42
|
| `performance_budget_file` | repository path | Budget policy for the shared package harness; defaults to `performance-package-budgets.json` |
|
|
43
|
+
| `required_capabilities` | comma-separated task names | Fail closed when a required task or `coverage` evidence is unavailable; defaults to none |
|
|
44
|
+
| `coverage_enforcement` | `auto`, `required`, `off` | Shared coverage report policy; `auto` accepts explicit skips, `required` rejects missing reports, `off` skips it |
|
|
45
|
+
| `coverage_minimum` | percentage from `0` to `100` | Minimum measured coverage; defaults to `80` |
|
|
46
|
+
| `coverage_metrics` | `lines`, `functions`, `branches`, `statements` | Metrics checked by the shared coverage gate; defaults to `lines` |
|
|
47
|
+
| `coverage_report` | comma-separated repository paths | Istanbul summary or LCOV evidence files; defaults to standard coverage paths |
|
|
43
48
|
| `features` | `all` or a list | Standard workflow callers |
|
|
44
49
|
| `codeql` | `auto`, `true`, `false` | CodeQL policy; public repositories default to enabled, non-public repositories default to disabled |
|
|
45
50
|
| `codeql_rust_shards` | JSON array of paths | Rust scan scopes; `["all"]` keeps the safe single full scan |
|
package/docs/README.md
CHANGED
|
@@ -13,6 +13,7 @@ its own names, environments, and deployment details.
|
|
|
13
13
|
- [Publishing packages](PUBLISHING.md)
|
|
14
14
|
- [Caching and remote caching](CACHING.md)
|
|
15
15
|
- [Performance budgets and baselines](PERFORMANCE.md)
|
|
16
|
+
- [Required capabilities and task evidence](required-capabilities.md)
|
|
16
17
|
|
|
17
18
|
## Repository-specific documentation
|
|
18
19
|
|
package/docs/WORKFLOWS.md
CHANGED
|
@@ -4,16 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
Validation uses two callers so pull-request code and default-branch-capable
|
|
6
6
|
audit events never share a caller-selected runtime ref. The canonical
|
|
7
|
-
`validation.yml` caller handles pull requests
|
|
7
|
+
`validation.yml` caller handles pull requests and a lightweight default-branch
|
|
8
|
+
CodeQL lane:
|
|
8
9
|
|
|
9
10
|
```yaml
|
|
11
|
+
push:
|
|
12
|
+
branches: [main] # CodeQL only
|
|
10
13
|
pull_request:
|
|
11
14
|
branches: [main, staging] # staging-release topology
|
|
12
15
|
# direct topology: branches: [main]
|
|
13
16
|
```
|
|
14
17
|
|
|
15
|
-
The generated validation caller listens
|
|
16
|
-
pull requests register no validation checks and allocate no
|
|
18
|
+
The generated validation caller listens for `ready_for_review` and `push` to
|
|
19
|
+
`main`. Draft pull requests register no validation checks and allocate no
|
|
20
|
+
validation runner; main pushes run only the default-branch CodeQL lane, while
|
|
21
|
+
full validation remains pull-request-only.
|
|
17
22
|
A separate lightweight Draft Guard runs from the trusted base branch on
|
|
18
23
|
`opened`, `reopened`, and `synchronize`; it converts ordinary ready pull
|
|
19
24
|
requests back to draft without checking out pull-request code. It rechecks the
|
|
@@ -44,7 +49,7 @@ Scheduled and manual runs select the audit tier in both topologies. Draft PR
|
|
|
44
49
|
automation separately listens to supported topic-branch pushes and always
|
|
45
50
|
opens those PRs as drafts. Promotion automation listens to `staging` pushes
|
|
46
51
|
(staging-release only) and also always opens its PR as a draft. Release
|
|
47
|
-
automation
|
|
52
|
+
automation and default-branch CodeQL listen to `main` pushes.
|
|
48
53
|
Custom deployment, indexing, search, Slither, or other workflows are
|
|
49
54
|
repository-owned extensions and should use the same ready-transition policy.
|
|
50
55
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Required capabilities and task evidence
|
|
2
|
+
|
|
3
|
+
Declared requirements fail closed when discovery cannot find an executable task.
|
|
4
|
+
Existing optional task discovery remains available. Configure scalar values in
|
|
5
|
+
`.github/code-foundry.yml`:
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
required_capabilities: type_check,unit,e2e,performance,coverage
|
|
9
|
+
performance: true
|
|
10
|
+
coverage_enforcement: required
|
|
11
|
+
coverage_minimum: 80
|
|
12
|
+
coverage_metrics: lines,branches
|
|
13
|
+
coverage_report: coverage/coverage-summary.json
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Supported task capabilities are `format`, `lint`, `type_check`, `build`, `unit`,
|
|
17
|
+
`integration`, `e2e`, `smoke`, and `performance`. `coverage` additionally requires
|
|
18
|
+
unit tests. Unknown names, contradictory requirements, and invalid thresholds
|
|
19
|
+
are errors. `performance: true` now means required, not merely enabled when a
|
|
20
|
+
script happens to exist. Use `performance: auto` to retain optional discovery.
|
|
21
|
+
|
|
22
|
+
The public `src/runtime.mjs` entrypoint delegates ecosystem execution to the
|
|
23
|
+
unchanged private `src/runtime-core.mjs`. Keep both files and `src/lib` when
|
|
24
|
+
vendoring the runtime. Published packages and the reusable workflows' existing
|
|
25
|
+
cone-mode sparse checkout include both files. Do not call the private executor
|
|
26
|
+
from consumer CI: it intentionally does not enforce the public policy contract.
|
|
27
|
+
|
|
28
|
+
## Coverage migration
|
|
29
|
+
|
|
30
|
+
`coverage_enforcement` accepts:
|
|
31
|
+
|
|
32
|
+
- `auto` (default): missing reports are explicitly reported as skipped; present
|
|
33
|
+
reports must be fresh, valid, and meet the threshold.
|
|
34
|
+
- `required`: missing reports also fail, as does required capability `coverage`.
|
|
35
|
+
- `off`: skip the shared report gate. This does not disable a test runner's own
|
|
36
|
+
coverage threshold, and cannot be combined with required coverage.
|
|
37
|
+
|
|
38
|
+
The default report candidates are `coverage/coverage-summary.json` (Istanbul
|
|
39
|
+
summary) and `coverage/lcov.info`. `coverage_report` accepts a comma-separated
|
|
40
|
+
list of repository-relative files. When multiple reports exist, every report
|
|
41
|
+
must meet the selected metrics. Percentages are recomputed from measured counts,
|
|
42
|
+
not trusted from reported `pct` fields. Empty reports, unmeasured selected
|
|
43
|
+
metrics, stale files, traversal, and escaping symlinks fail. LCOV supports lines,
|
|
44
|
+
functions, and branches; use JSON summaries for statement coverage.
|
|
45
|
+
|
|
46
|
+
Configure the repository-owned unit-test command to generate the report on each
|
|
47
|
+
run before enabling required mode. The runtime does not inject coverage tooling,
|
|
48
|
+
install a new runner, delete old reports, or lower thresholds. Auto mode preserves
|
|
49
|
+
repos that declare a threshold but have not yet configured report generation;
|
|
50
|
+
a passing unit task with `coverage.status: skipped` is **not** coverage evidence.
|
|
51
|
+
|
|
52
|
+
## Results and validation tiers
|
|
53
|
+
|
|
54
|
+
Each executed CI task writes `.code-foundry/results/<task>.json`, including its
|
|
55
|
+
status (`passed`, `failed`, or `skipped`), discovery reason, delegated command and
|
|
56
|
+
exit status, source SHA when available, timestamps, and evidence paths. Coverage
|
|
57
|
+
has its own nested status. Discovery also records optional skips. Task reports
|
|
58
|
+
appear in the GitHub job summary when `GITHUB_STEP_SUMMARY` is available.
|
|
59
|
+
|
|
60
|
+
The recorded command is the delegated executor invocation, not a transcript of
|
|
61
|
+
all nested package scripts. No environment variables or captured command output
|
|
62
|
+
are copied into the report. Repository scripts remain responsible for sanitizing
|
|
63
|
+
their own logs. Upload `.code-foundry/results/*.json` with `if: always()` and
|
|
64
|
+
`include-hidden-files: true` to retain downloadable reports; only upload this
|
|
65
|
+
specific directory, not arbitrary hidden files or the entire checkout.
|
|
66
|
+
|
|
67
|
+
`node src/runtime.mjs ci plan` prints a JSON discovery plan without executing
|
|
68
|
+
checks. Discovery validates every required task before reusable workflows select
|
|
69
|
+
jobs. A fast/unit-only tier is still a subset: discovery proves that E2E exists,
|
|
70
|
+
not that E2E ran. Require the existing audit validation gate before merging.
|
|
71
|
+
|
|
72
|
+
Native task detection rejects known no-ops such as a JavaScript project with no
|
|
73
|
+
build script or a Python project with no supported type-check command. Add a
|
|
74
|
+
repository-owned script for unsupported layouts or toolchains. Do not infer that
|
|
75
|
+
an installed package manager or discovered project proves task execution.
|
|
76
|
+
|
|
77
|
+
## Verification
|
|
78
|
+
|
|
79
|
+
`node --test test/task-policy.test.mjs` exercises policy parsing, discovery,
|
|
80
|
+
coverage parsing/thresholds/freshness/path safety, exit propagation, and reports
|
|
81
|
+
with a deterministic executor fixture. The unchanged ecosystem executor remains
|
|
82
|
+
covered by `test/runtime.test.mjs` in the full suite.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-foundry",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
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/commands/doctor.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { join, resolve } from 'node:path'
|
|
|
5
5
|
import { spawnSync } from 'node:child_process'
|
|
6
6
|
import { includesValue, readConfig } from '../lib/config.mjs'
|
|
7
7
|
import { recommendRunners, resolveProfile } from '../lib/profile.mjs'
|
|
8
|
+
import { packageManagerForLockfile } from '../lib/lockfiles.mjs'
|
|
8
9
|
import { doctorGithub } from '../lib/github-doctor.mjs'
|
|
9
10
|
import { isGeneratedEventCaller } from './sync.mjs'
|
|
10
11
|
|
|
@@ -69,9 +70,7 @@ export function doctor(root, options = {}) {
|
|
|
69
70
|
if (lockfiles.length > 1) error('multiple JavaScript lockfiles found; keep one package manager')
|
|
70
71
|
if (packageJson.packageManager && lockfiles.length) {
|
|
71
72
|
const declared = String(packageJson.packageManager).split('@')[0]
|
|
72
|
-
const actual = lockfiles[0]
|
|
73
|
-
? 'bun'
|
|
74
|
-
: lockfiles[0]?.split('-')[0].replace('.yaml', '')
|
|
73
|
+
const actual = packageManagerForLockfile(lockfiles[0])
|
|
75
74
|
if (actual && declared !== actual)
|
|
76
75
|
error(`packageManager (${declared}) does not match ${actual} lockfile`)
|
|
77
76
|
}
|
package/src/commands/sync.mjs
CHANGED
|
@@ -537,12 +537,15 @@ function renderWorkflow(content, config, repository, ref, rustCodeql) {
|
|
|
537
537
|
let rendered = content.replaceAll(localPrefix, remotePrefix)
|
|
538
538
|
// An explicitly unavailable CodeQL capability selects an orchestrator that
|
|
539
539
|
// omits the job entirely, so GitHub does not register a misleading skipped
|
|
540
|
-
// check on every pull request. `auto` retains
|
|
540
|
+
// check on every pull request or an unused main-push run. `auto` retains
|
|
541
|
+
// runtime capability detection.
|
|
541
542
|
if (configured(config.codeql, 'auto') === 'false') {
|
|
542
543
|
rendered = rendered.replaceAll(
|
|
543
544
|
`${remotePrefix}validation.yml`,
|
|
544
545
|
`${remotePrefix}validation-no-codeql.yml`
|
|
545
546
|
)
|
|
547
|
+
rendered = removeWorkflowBlock(rendered, 'default-branch-codeql')
|
|
548
|
+
rendered = removeWorkflowBlock(rendered, 'push')
|
|
546
549
|
}
|
|
547
550
|
rendered = rendered.replace(
|
|
548
551
|
new RegExp(`${escapeRegExp(remotePrefix)}([^\\s@]+)`, 'g'),
|
|
@@ -640,6 +643,25 @@ function renderWorkflow(content, config, repository, ref, rustCodeql) {
|
|
|
640
643
|
return rendered
|
|
641
644
|
}
|
|
642
645
|
|
|
646
|
+
/**
|
|
647
|
+
* Remove a root-level YAML block by id while preserving the surrounding file.
|
|
648
|
+
* This is used for workflow jobs and triggers whose feature is disabled by
|
|
649
|
+
* configuration.
|
|
650
|
+
* @param {string} content
|
|
651
|
+
* @param {string} blockId
|
|
652
|
+
* @returns {string}
|
|
653
|
+
*/
|
|
654
|
+
function removeWorkflowBlock(content, blockId) {
|
|
655
|
+
const lines = content.split('\n')
|
|
656
|
+
const start = lines.findIndex((line) => line === ` ${blockId}:`)
|
|
657
|
+
if (start === -1) return content
|
|
658
|
+
let end = start + 1
|
|
659
|
+
while (end < lines.length && !/^ [A-Za-z0-9_-]+:\s*$/.test(lines[end])) end += 1
|
|
660
|
+
lines.splice(start, end - start)
|
|
661
|
+
if (content.endsWith('\n') && lines.at(-1) !== '') lines.push('')
|
|
662
|
+
return lines.join('\n')
|
|
663
|
+
}
|
|
664
|
+
|
|
643
665
|
/**
|
|
644
666
|
* Dependabot updates land on the repository's integration branch. Direct
|
|
645
667
|
* repositories have no staging branch, so every update targets main. Cargo,
|
|
@@ -779,8 +801,8 @@ const DIRECT_DOC_REPLACEMENTS = {
|
|
|
779
801
|
'7. Push to the fork and open a pull request targeting `main`.',
|
|
780
802
|
],
|
|
781
803
|
[
|
|
782
|
-
'| Event | Expected automation |\n| -------------------------------------------------- | ------------------------------------------------------------------------------------- |\n| Draft pull request targeting `staging` | No runner-heavy validation; run local checks before requesting review |\n| Ready pull request targeting `staging` | Fast validation: CI plus unit tests, ending in `Validation / Gate` |\n| Draft ordinary pull request targeting `main` | No runner-heavy validation; run local checks before requesting review |\n| Ready ordinary pull request targeting `main` | Audit validation: CI, full tests, Security, and CodeQL, ending in `Validation / Gate` |\n| Exact Release Please pull request targeting `main` | Full validation: CI, full tests, Security, and CodeQL, ending in `Validation / Gate` |\n| Scheduled or manual validation | Full audit tier |\n| Push to a working branch | Draft PR workflow |\n| Push to `staging` | Promotion PR workflow; canonical validation waits for the PR event |\n| Push to `main` | Release workflow;
|
|
783
|
-
'| Event | Expected automation |\n| -------------------------------------------------- | ------------------------------------------------------------------------------------- |\n| Draft pull request targeting `main` | No runner-heavy validation; run local checks before requesting review |\n| Ready pull request targeting `main` | Audit validation: CI, full tests, Security, and CodeQL, ending in `Validation / Gate` |\n| Exact Release Please pull request targeting `main` | Full validation: CI, full tests, Security, and CodeQL, ending in `Validation / Gate` |\n| Scheduled or manual validation | Full audit tier |\n| Push to a working branch | Draft PR workflow |\n| Push to `main` | Release workflow;
|
|
804
|
+
'| Event | Expected automation |\n| -------------------------------------------------- | ------------------------------------------------------------------------------------- |\n| Draft pull request targeting `staging` | No runner-heavy validation; run local checks before requesting review |\n| Ready pull request targeting `staging` | Fast validation: CI plus unit tests, ending in `Validation / Gate` |\n| Draft ordinary pull request targeting `main` | No runner-heavy validation; run local checks before requesting review |\n| Ready ordinary pull request targeting `main` | Audit validation: CI, full tests, Security, and CodeQL, ending in `Validation / Gate` |\n| Exact Release Please pull request targeting `main` | Full validation: CI, full tests, Security, and CodeQL, ending in `Validation / Gate` |\n| Scheduled or manual validation | Full audit tier |\n| Push to a working branch | Draft PR workflow |\n| Push to `staging` | Promotion PR workflow; canonical validation waits for the PR event |\n| Push to `main` | Release workflow plus default-branch CodeQL scan; validation ran on the merged PR |\n',
|
|
805
|
+
'| Event | Expected automation |\n| -------------------------------------------------- | ------------------------------------------------------------------------------------- |\n| Draft pull request targeting `main` | No runner-heavy validation; run local checks before requesting review |\n| Ready pull request targeting `main` | Audit validation: CI, full tests, Security, and CodeQL, ending in `Validation / Gate` |\n| Exact Release Please pull request targeting `main` | Full validation: CI, full tests, Security, and CodeQL, ending in `Validation / Gate` |\n| Scheduled or manual validation | Full audit tier |\n| Push to a working branch | Draft PR workflow |\n| Push to `main` | Release workflow plus default-branch CodeQL scan; validation ran on the merged PR |\n',
|
|
784
806
|
],
|
|
785
807
|
[
|
|
786
808
|
'| Change | Target | Merge method | Merge gate |\n| ---------------------------- | --------- | ----------------------------------------------- | --------------------------------------------------------- |\n| Working branch | `staging` | Squash | All applicable required checks pass |\n| `staging` → `main` promotion | `main` | Rebase (`merge_strategy`) | Current staging checks, release review, and rollout notes |\n| Release Please version PR | `main` | Rebase (`release_merge_strategy`, fails closed) | Validation gate and release policy pass |\n',
|
|
@@ -1266,7 +1288,11 @@ function createDefaultConfig(root, source, configuredWorkflow) {
|
|
|
1266
1288
|
prune_standard: 'false',
|
|
1267
1289
|
cache_packages: 'auto',
|
|
1268
1290
|
cache_build: 'auto',
|
|
1291
|
+
required_capabilities: '',
|
|
1292
|
+
coverage_enforcement: 'auto',
|
|
1269
1293
|
coverage_minimum: '80',
|
|
1294
|
+
coverage_metrics: 'lines',
|
|
1295
|
+
coverage_report: '',
|
|
1270
1296
|
turbo_remote: 'auto',
|
|
1271
1297
|
release_type: detectPackageManager(root) === 'none' ? 'auto' : 'node',
|
|
1272
1298
|
npm_publish: 'false',
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { readFileSync, readdirSync } from 'node:fs'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
import { spawnSync } from 'node:child_process'
|
|
6
|
+
import { readConfig } from './config.mjs'
|
|
6
7
|
|
|
7
8
|
/** @param {string} root @returns {{ errors: string[], warnings: string[], details: Record<string, unknown> }} */
|
|
8
9
|
export function doctorGithub(root) {
|
|
@@ -306,22 +307,3 @@ function ghJson(args) {
|
|
|
306
307
|
function commandExists(command) {
|
|
307
308
|
return spawnSync(command, ['--version'], { stdio: 'ignore' }).status === 0
|
|
308
309
|
}
|
|
309
|
-
|
|
310
|
-
/** @param {string} root @returns {Record<string, string>} */
|
|
311
|
-
function readConfig(root) {
|
|
312
|
-
const file = join(root, '.github/code-foundry.yml')
|
|
313
|
-
try {
|
|
314
|
-
return Object.fromEntries(
|
|
315
|
-
readFileSync(file, 'utf8')
|
|
316
|
-
.split(/\r?\n/)
|
|
317
|
-
.flatMap((line) => {
|
|
318
|
-
const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*?)\s*$/)
|
|
319
|
-
return match
|
|
320
|
-
? [[match[1], match[2].replace(/\s+#.*$/, '').replace(/^['"]|['"]$/g, '')]]
|
|
321
|
-
: []
|
|
322
|
-
})
|
|
323
|
-
)
|
|
324
|
-
} catch {
|
|
325
|
-
return {}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/** @type {Readonly<Record<string, string>>} */
|
|
4
|
+
const managers = Object.freeze({
|
|
5
|
+
'bun.lock': 'bun',
|
|
6
|
+
'bun.lockb': 'bun',
|
|
7
|
+
'pnpm-lock.yaml': 'pnpm',
|
|
8
|
+
'yarn.lock': 'yarn',
|
|
9
|
+
'package-lock.json': 'npm',
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
/** @param {string | undefined} file @returns {string | undefined} */
|
|
13
|
+
export function packageManagerForLockfile(file) {
|
|
14
|
+
return file && Object.hasOwn(managers, file) ? managers[file] : undefined
|
|
15
|
+
}
|