code-foundry 1.20.1 → 1.22.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 (44) hide show
  1. package/.github/CONTRIBUTING.md +5 -2
  2. package/.github/code-foundry.yml +1 -0
  3. package/.github/release-please-foundry.json +56 -0
  4. package/.github/workflows/ci.yml +61 -0
  5. package/.github/workflows/cloudflare-delivery.yml +9 -0
  6. package/.github/workflows/cloudflare-deploy.yml +8 -1
  7. package/.github/workflows/opencode-security_self-ci.yml +1 -1
  8. package/.github/workflows/qualified-foundry-publish.yml +6 -7
  9. package/.github/workflows/release.yml +58 -10
  10. package/.github/workflows/release_self-ci.yml +211 -8
  11. package/.github/workflows/test.yml +75 -0
  12. package/.github/workflows/validation-no-codeql.yml +7 -0
  13. package/.github/workflows/validation.yml +7 -0
  14. package/.gitignore +1 -1
  15. package/AGENTS.md +4 -1
  16. package/CHANGELOG.md +60 -0
  17. package/README.md +20 -17
  18. package/docs/CONFIGURATION.md +182 -152
  19. package/docs/EXTENSIONS.md +28 -7
  20. package/docs/INITIALIZATION.md +13 -9
  21. package/docs/PERFORMANCE.md +67 -59
  22. package/docs/PUBLISHING.md +39 -14
  23. package/docs/README.md +37 -22
  24. package/docs/RELEASES.md +18 -9
  25. package/docs/WORKFLOWS.md +11 -0
  26. package/docs/agent-validation.md +6 -5
  27. package/docs/cloudflare-delivery.md +20 -8
  28. package/docs/consumer-qualification.md +11 -9
  29. package/docs/fleet-release-eligibility.md +14 -15
  30. package/docs/fleet-rollouts.md +3 -3
  31. package/docs/merge-queues.md +21 -21
  32. package/docs/product-quality.md +9 -10
  33. package/docs/qualified-publication.md +166 -93
  34. package/docs/release-integrity.md +7 -6
  35. package/docs/required-capabilities.md +42 -15
  36. package/package.json +1 -1
  37. package/src/commands/cloudflare-delivery.mjs +6 -1
  38. package/src/commands/qualified-publication.mjs +23 -10
  39. package/src/commands/release-integrity.mjs +9 -0
  40. package/src/commands/sync.mjs +29 -24
  41. package/src/lib/product-quality.mjs +220 -16
  42. package/src/runtime-core.mjs +12 -8
  43. package/src/runtime.mjs +1 -1
  44. package/src/templates/gitignore +1 -1
@@ -12,32 +12,235 @@ on:
12
12
  default: false
13
13
 
14
14
  permissions:
15
- actions: write
16
- contents: write
17
- issues: write
18
- pull-requests: write
19
- id-token: write
15
+ contents: read
16
+
17
+ # One owner for draft staging and npm publication. Never cancel an active release.
18
+ concurrency:
19
+ group: code-foundry-qualified-release-${{ github.repository }}-${{ github.ref }}
20
+ cancel-in-progress: false
20
21
 
21
22
  jobs:
22
23
  qualification:
23
24
  name: Qualify consumers
24
- if: vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)
25
+ if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true))
25
26
  uses: ./.github/workflows/consumer-qualification.yml
26
27
  with:
27
28
  billing-pause-bypass: ${{ github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true }}
28
29
  permissions:
29
30
  actions: read
30
31
  contents: read
32
+
33
+ preflight:
34
+ name: Verify release prerequisites
35
+ if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true))
36
+ runs-on: ubuntu-24.04
37
+ timeout-minutes: 5
38
+ steps:
39
+ - name: Checkout trusted verifier
40
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
41
+ with:
42
+ persist-credentials: false
43
+ ref: ${{ github.sha }}
44
+ - name: Setup Node
45
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
46
+ with:
47
+ node-version: '24'
48
+ - name: Require activation and verify immutability without changing it
49
+ env:
50
+ REQUIRE_IMMUTABLE_RELEASES: ${{ vars.REQUIRE_IMMUTABLE_RELEASES }}
51
+ CODE_FOUNDRY_TOKEN: ${{ secrets.CODE_FOUNDRY_TOKEN }}
52
+ GH_TOKEN: ${{ github.token }}
53
+ run: |
54
+ set -euo pipefail
55
+ test "$REQUIRE_IMMUTABLE_RELEASES" = true || { echo 'Enable and review immutable release prerequisites before this cutover.' >&2; exit 1; }
56
+ gh release verify --help >/dev/null
57
+ gh release verify-asset --help >/dev/null
58
+ # Match the reusable producer's credential selection. A rejected
59
+ # configured token must not make the later staging job use it.
60
+ if [ -n "$CODE_FOUNDRY_TOKEN" ] && GH_TOKEN="$CODE_FOUNDRY_TOKEN" gh api "repos/${GITHUB_REPOSITORY}" --jq '.full_name' >/dev/null 2>&1; then
61
+ GH_TOKEN="$CODE_FOUNDRY_TOKEN" node src/commands/release-integrity.mjs settings --repo "$GITHUB_REPOSITORY"
62
+ else
63
+ node src/commands/release-integrity.mjs settings --repo "$GITHUB_REPOSITORY"
64
+ fi
65
+
31
66
  release:
32
67
  name: Release
33
- needs: qualification
34
- if: vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)
68
+ needs: [qualification, preflight]
69
+ if: github.ref == 'refs/heads/main' && (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true))
35
70
  uses: ./.github/workflows/release.yml
36
71
  with:
37
72
  billing-pause-bypass: ${{ github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true }}
38
73
  runner: ubuntu-slim
39
74
  runtime-repository: 0xPlayerOne/code-foundry
40
75
  runtime-ref: ${{ github.sha }}
76
+ config-file: .github/release-please-foundry.json
77
+ defer-publication: true
78
+ permissions:
79
+ actions: write
80
+ contents: write
81
+ issues: write
82
+ pull-requests: write
83
+ # Required by the reusable legacy workflow's declared npm job, which is disabled here.
84
+ id-token: write
41
85
  secrets:
42
86
  CODE_FOUNDRY_TOKEN: ${{ secrets.CODE_FOUNDRY_TOKEN }}
87
+
88
+ recovery:
89
+ name: Recover qualified draft identity
90
+ needs: [qualification, release]
91
+ if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && needs.qualification.result == 'success' && needs.release.result == 'success'
92
+ runs-on: ubuntu-24.04
93
+ timeout-minutes: 5
94
+ permissions:
95
+ contents: read
96
+ outputs:
97
+ found: ${{ steps.recover.outputs.found }}
98
+ tag: ${{ steps.recover.outputs.tag }}
99
+ sha: ${{ steps.recover.outputs.sha }}
100
+ env:
101
+ RELEASE_CREATED: ${{ needs.release.outputs.release_created }}
102
+ SOURCE_SHA: ${{ needs.qualification.outputs.source-sha }}
103
+ steps:
104
+ - name: Checkout trusted recovery runtime
105
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
106
+ with:
107
+ persist-credentials: false
108
+ ref: ${{ github.sha }}
109
+ - name: Setup Node
110
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
111
+ with:
112
+ node-version: '24'
113
+ - name: Locate an exact existing draft
114
+ id: recover
115
+ env:
116
+ GH_TOKEN: ${{ github.token }}
117
+ GITHUB_REPOSITORY: ${{ github.repository }}
118
+ run: |
119
+ set -euo pipefail
120
+ node --input-type=module <<'NODE'
121
+ import { appendFileSync, readFileSync } from "node:fs"
122
+ import { resolveTagCommit, runGh } from "./src/commands/release-integrity.mjs"
123
+ const output = process.env.GITHUB_OUTPUT
124
+ const write = (found, tag = "", sha = "") => appendFileSync(output, `found=${found}\ntag=${tag}\nsha=${sha}\n`)
125
+ if (process.env.RELEASE_CREATED === "true") {
126
+ write("false")
127
+ process.exit(0)
128
+ }
129
+ const version = JSON.parse(readFileSync("package.json", "utf8")).version
130
+ const tag = `v${version}`
131
+ // GitHub's get-by-tag endpoint does not return draft releases. Use
132
+ // the authenticated release list and inspect every page instead.
133
+ const endpoint = `repos/${process.env.GITHUB_REPOSITORY}/releases?per_page=100`
134
+ const response = runGh(["api", "--paginate", "--slurp", endpoint])
135
+ if (response.status !== 0) throw new Error("Could not inspect the expected draft release; recovery remains blocked")
136
+ let pages
137
+ try {
138
+ pages = JSON.parse(response.stdout)
139
+ } catch {
140
+ throw new Error("GitHub returned an invalid release response; recovery remains blocked")
141
+ }
142
+ if (!Array.isArray(pages) || pages.some((page) => !Array.isArray(page))) {
143
+ throw new Error("GitHub returned an invalid release list; recovery remains blocked")
144
+ }
145
+ const release = pages.flat().find((entry) => entry?.tag_name === tag)
146
+ if (!release || release.draft !== true) {
147
+ write("false")
148
+ process.exit(0)
149
+ }
150
+ if (release.tag_name !== tag) throw new Error("Existing draft tag does not match the expected package version")
151
+ const sourceSha = resolveTagCommit(process.env.GITHUB_REPOSITORY, tag, runGh)
152
+ if (sourceSha.toLowerCase() !== process.env.SOURCE_SHA.toLowerCase()) throw new Error("Existing draft tag does not match the qualified source SHA")
153
+ write("true", tag, sourceSha)
154
+ NODE
155
+
156
+ stage:
157
+ name: Stage qualified release
158
+ needs: [qualification, release, recovery]
159
+ if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && github.ref == 'refs/heads/main' && (needs.release.outputs.release_created == 'true' || needs.recovery.outputs.found == 'true')
160
+ runs-on: ubuntu-24.04
161
+ timeout-minutes: 15
162
+ permissions:
163
+ actions: read
164
+ attestations: read
165
+ contents: write
166
+ env:
167
+ RELEASE_TAG: ${{ needs.release.outputs.tag_name || needs.recovery.outputs.tag }}
168
+ RELEASE_SHA: ${{ needs.release.outputs.sha || needs.recovery.outputs.sha }}
169
+ SOURCE_SHA: ${{ needs.qualification.outputs.source-sha }}
170
+ PACKAGE: ${{ needs.qualification.outputs.filename }}
171
+ CANDIDATE_SHA256: ${{ needs.qualification.outputs.sha256 }}
172
+ CANDIDATE_ARTIFACT: ${{ needs.qualification.outputs.candidate-artifact }}
173
+ steps:
174
+ - name: Checkout trusted staging runtime
175
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
176
+ with:
177
+ persist-credentials: false
178
+ ref: ${{ github.sha }}
179
+ - name: Setup Node
180
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
181
+ with:
182
+ node-version: '24'
183
+ - name: Require the release to identify this qualified candidate
184
+ run: |
185
+ node --input-type=module <<'NODE'
186
+ import assert from 'node:assert/strict'
187
+ import { readFileSync } from 'node:fs'
188
+ const env = process.env
189
+ const version = JSON.parse(readFileSync('package.json', 'utf8')).version
190
+ assert.match(env.SOURCE_SHA, /^[a-f0-9]{40}$/)
191
+ assert.equal(env.SOURCE_SHA, env.GITHUB_SHA)
192
+ assert.equal(env.RELEASE_SHA, env.SOURCE_SHA)
193
+ assert.equal(env.RELEASE_TAG, `v${version}`)
194
+ assert.equal(env.PACKAGE, `code-foundry-${version}.tgz`)
195
+ assert.match(env.CANDIDATE_SHA256, /^[a-f0-9]{64}$/)
196
+ assert.equal(env.CANDIDATE_ARTIFACT, `qualification-candidate-${env.GITHUB_RUN_ID}-${env.GITHUB_RUN_ATTEMPT}`)
197
+ NODE
198
+ - name: Retrieve only this attempt's qualified bytes and reports
199
+ env:
200
+ GH_TOKEN: ${{ github.token }}
201
+ run: |
202
+ set -euo pipefail
203
+ gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" \
204
+ --name "$CANDIDATE_ARTIFACT" --dir "$RUNNER_TEMP/candidate"
205
+ test "$(sha256sum "$RUNNER_TEMP/candidate/$PACKAGE" | cut -d' ' -f1)" = "$CANDIDATE_SHA256"
206
+ for node in 20 22 24; do
207
+ gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" \
208
+ --name "consumer-qualification-$GITHUB_RUN_ATTEMPT-node-$node" \
209
+ --dir "$RUNNER_TEMP/reports/$node"
210
+ done
211
+ - name: Stage identical assets then publish and verify the immutable release
212
+ env:
213
+ GH_TOKEN: ${{ needs.release.outputs.token_source == 'configured' && secrets.CODE_FOUNDRY_TOKEN || github.token }}
214
+ run: |
215
+ set -euo pipefail
216
+ node src/commands/qualified-publication.mjs stage \
217
+ "$GITHUB_REPOSITORY" "$RELEASE_TAG" "$SOURCE_SHA" "$PACKAGE" \
218
+ "$RUNNER_TEMP/candidate" \
219
+ "$RUNNER_TEMP/reports/20/report.json" \
220
+ "$RUNNER_TEMP/reports/22/report.json" \
221
+ "$RUNNER_TEMP/reports/24/report.json" > "$RUNNER_TEMP/staging.json"
222
+ - name: Retain staging identity
223
+ if: always()
224
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
225
+ with:
226
+ name: release-staging-${{ github.run_id }}-${{ github.run_attempt }}
227
+ path: ${{ runner.temp }}/staging.json
228
+ if-no-files-found: warn
229
+ retention-days: 14
230
+
231
+ publish:
232
+ name: Publish verified package
233
+ needs: [qualification, release, recovery, stage]
234
+ if: (vars.CI_BILLING_PAUSED != 'true' || (github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true)) && github.ref == 'refs/heads/main' && needs.stage.result == 'success' && (needs.release.outputs.release_created == 'true' || needs.recovery.outputs.found == 'true')
235
+ uses: ./.github/workflows/qualified-foundry-publish.yml
236
+ permissions:
237
+ actions: read
238
+ attestations: read
239
+ contents: read
240
+ id-token: write
241
+ with:
242
+ tag: ${{ needs.release.outputs.tag_name || needs.recovery.outputs.tag }}
243
+ asset: ${{ needs.qualification.outputs.filename }}
244
+ billing-pause-bypass: ${{ github.event_name == 'workflow_dispatch' && inputs['release-while-paused'] == true }}
245
+ secrets:
43
246
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -33,6 +33,11 @@ on:
33
33
  required: false
34
34
  type: boolean
35
35
  default: false
36
+ artifact-prefix:
37
+ description: Prefix for task receipts; use a unique value when calling this workflow more than once.
38
+ required: false
39
+ type: string
40
+ default: task-result
36
41
  secrets:
37
42
  TURBO_TOKEN:
38
43
  required: false
@@ -104,8 +109,22 @@ jobs:
104
109
  task-python: ${{ steps.applicability.outputs.python }}
105
110
  task-rust: ${{ steps.applicability.outputs.rust }}
106
111
  - name: Unit
112
+ id: execute
107
113
  if: steps.applicability.outputs.applicable == 'true'
108
114
  run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci unit
115
+ - name: Retain task receipt
116
+ if: >-
117
+ always() &&
118
+ (steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
119
+ steps.applicability.outputs.applicable == 'false') &&
120
+ hashFiles('.code-foundry/results/unit.json') != ''
121
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
122
+ with:
123
+ name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-unit
124
+ path: .code-foundry/results/unit.json
125
+ include-hidden-files: true
126
+ if-no-files-found: error
127
+ retention-days: 14
109
128
  - name: Upload coverage
110
129
  if: >-
111
130
  always() &&
@@ -171,8 +190,22 @@ jobs:
171
190
  task-python: ${{ steps.applicability.outputs.python }}
172
191
  task-rust: ${{ steps.applicability.outputs.rust }}
173
192
  - name: Performance
193
+ id: execute
174
194
  if: steps.applicability.outputs.applicable == 'true'
175
195
  run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci performance
196
+ - name: Retain task receipt
197
+ if: >-
198
+ always() &&
199
+ (steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
200
+ steps.applicability.outputs.applicable == 'false') &&
201
+ hashFiles('.code-foundry/results/performance.json') != ''
202
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
203
+ with:
204
+ name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-performance
205
+ path: .code-foundry/results/performance.json
206
+ include-hidden-files: true
207
+ if-no-files-found: error
208
+ retention-days: 14
176
209
  - name: Upload performance results
177
210
  if: >-
178
211
  always() &&
@@ -236,8 +269,22 @@ jobs:
236
269
  task-python: ${{ steps.applicability.outputs.python }}
237
270
  task-rust: ${{ steps.applicability.outputs.rust }}
238
271
  - name: Integration
272
+ id: execute
239
273
  if: steps.applicability.outputs.applicable == 'true'
240
274
  run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci integration
275
+ - name: Retain task receipt
276
+ if: >-
277
+ always() &&
278
+ (steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
279
+ steps.applicability.outputs.applicable == 'false') &&
280
+ hashFiles('.code-foundry/results/integration.json') != ''
281
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
282
+ with:
283
+ name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-integration
284
+ path: .code-foundry/results/integration.json
285
+ include-hidden-files: true
286
+ if-no-files-found: error
287
+ retention-days: 14
241
288
 
242
289
  e2e:
243
290
  name: E2E
@@ -298,8 +345,22 @@ jobs:
298
345
  task-python: ${{ steps.applicability.outputs.python }}
299
346
  task-rust: ${{ steps.applicability.outputs.rust }}
300
347
  - name: E2E
348
+ id: execute
301
349
  if: steps.applicability.outputs.applicable == 'true'
302
350
  run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci e2e
351
+ - name: Retain task receipt
352
+ if: >-
353
+ always() &&
354
+ (steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
355
+ steps.applicability.outputs.applicable == 'false') &&
356
+ hashFiles('.code-foundry/results/e2e.json') != ''
357
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
358
+ with:
359
+ name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-e2e
360
+ path: .code-foundry/results/e2e.json
361
+ include-hidden-files: true
362
+ if-no-files-found: error
363
+ retention-days: 14
303
364
 
304
365
  smoke:
305
366
  name: Smoke
@@ -348,5 +409,19 @@ jobs:
348
409
  task-python: ${{ steps.applicability.outputs.python }}
349
410
  task-rust: ${{ steps.applicability.outputs.rust }}
350
411
  - name: Smoke
412
+ id: execute
351
413
  if: steps.applicability.outputs.applicable == 'true'
352
414
  run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci smoke
415
+ - name: Retain task receipt
416
+ if: >-
417
+ always() &&
418
+ (steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
419
+ steps.applicability.outputs.applicable == 'false') &&
420
+ hashFiles('.code-foundry/results/smoke.json') != ''
421
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
422
+ with:
423
+ name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-smoke
424
+ path: .code-foundry/results/smoke.json
425
+ include-hidden-files: true
426
+ if-no-files-found: error
427
+ retention-days: 14
@@ -62,6 +62,11 @@ on:
62
62
  required: false
63
63
  type: number
64
64
  default: 1
65
+ artifact-prefix:
66
+ description: Prefix for task receipts; use a unique value when calling this workflow more than once.
67
+ required: false
68
+ type: string
69
+ default: task-result
65
70
  secrets:
66
71
  TURBO_TOKEN:
67
72
  required: false
@@ -86,6 +91,7 @@ jobs:
86
91
  runtime-repository: ${{ inputs.runtime-repository }}
87
92
  runtime-ref: ${{ inputs.runtime-ref }}
88
93
  runner: ${{ inputs.ci-runner }}
94
+ artifact-prefix: ${{ inputs.artifact-prefix }}
89
95
  secrets:
90
96
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
91
97
  NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
@@ -101,6 +107,7 @@ jobs:
101
107
  unit-runner: ${{ inputs.unit-runner }}
102
108
  performance-runner: ${{ inputs.performance-runner }}
103
109
  unit-only: ${{ inputs.mode == 'fast' }}
110
+ artifact-prefix: ${{ inputs.artifact-prefix }}
104
111
  secrets:
105
112
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
106
113
 
@@ -62,6 +62,11 @@ on:
62
62
  required: false
63
63
  type: number
64
64
  default: 1
65
+ artifact-prefix:
66
+ description: Prefix for task receipts; use a unique value when calling this workflow more than once.
67
+ required: false
68
+ type: string
69
+ default: task-result
65
70
  secrets:
66
71
  TURBO_TOKEN:
67
72
  required: false
@@ -86,6 +91,7 @@ jobs:
86
91
  runtime-repository: ${{ inputs.runtime-repository }}
87
92
  runtime-ref: ${{ inputs.runtime-ref }}
88
93
  runner: ${{ inputs.ci-runner }}
94
+ artifact-prefix: ${{ inputs.artifact-prefix }}
89
95
  secrets:
90
96
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
91
97
  NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
@@ -101,6 +107,7 @@ jobs:
101
107
  unit-runner: ${{ inputs.unit-runner }}
102
108
  performance-runner: ${{ inputs.performance-runner }}
103
109
  unit-only: ${{ inputs.mode == 'fast' }}
110
+ artifact-prefix: ${{ inputs.artifact-prefix }}
104
111
  secrets:
105
112
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
106
113
 
package/.gitignore CHANGED
@@ -46,7 +46,7 @@ cache/
46
46
  !.github/actions/cache/action.yml
47
47
  typechain/
48
48
  coverage.json
49
- performance-results/
49
+ .code-foundry/
50
50
  tests/.bin/
51
51
  **/.app/
52
52
 
package/AGENTS.md CHANGED
@@ -106,7 +106,10 @@ This repository uses the `direct` workflow. Topic pull requests target `main`.
106
106
  - Keep ordinary pull requests in draft while preparing them. The generated
107
107
  Draft Guard converts ready ordinary pull requests to draft when they are
108
108
  opened or reopened, and runner-heavy validation starts only after an
109
- explicit `ready_for_review` transition.
109
+ explicit `ready_for_review` transition unless `draft_protection: false` is
110
+ configured for generated callers. That opt-out does not disable Draft Guard
111
+ or draft-PR automation. Cloudflare reusable callers use
112
+ `draft-protection: false`.
110
113
  - Run local validation and finish review preparation before marking an ordinary
111
114
  pull request ready. Ready pull requests stay ready when new commits arrive,
112
115
  and validation reruns for the current head; draft updates allocate no
package/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.22.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.22.0...v1.22.1) (2026-09-09)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **release:** use shell-safe recovery script ([#574](https://github.com/0xPlayerOne/code-foundry/issues/574)) ([264036f](https://github.com/0xPlayerOne/code-foundry/commit/264036f34afa4d7bc405285918395f2ce075bde3))
9
+
10
+ ## [1.22.0](https://github.com/0xPlayerOne/code-foundry/compare/v1.21.3...v1.22.0) (2026-09-09)
11
+
12
+
13
+ ### Features
14
+
15
+ * **workflows:** protect draft pull requests by default ([#572](https://github.com/0xPlayerOne/code-foundry/issues/572)) ([6fa4ce1](https://github.com/0xPlayerOne/code-foundry/commit/6fa4ce1b2dde5eddff361b819722912fed7a9815))
16
+
17
+ ## [1.21.3](https://github.com/0xPlayerOne/code-foundry/compare/v1.21.2...v1.21.3) (2026-09-09)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **release:** unblock draft publication and PR deployment links ([#570](https://github.com/0xPlayerOne/code-foundry/issues/570)) ([35ab90f](https://github.com/0xPlayerOne/code-foundry/commit/35ab90f185483b0cd6a9c622b9793e46c90dd60c))
23
+
24
+ ## [1.21.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.21.1...v1.21.2) (2026-09-09)
25
+
26
+
27
+ ### Documentation
28
+
29
+ * refresh documentation for current workflows ([#568](https://github.com/0xPlayerOne/code-foundry/issues/568)) ([d0028e0](https://github.com/0xPlayerOne/code-foundry/commit/d0028e096371e1019c466f72f8744bdf832e067c))
30
+
31
+ ## [1.21.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.21.0...v1.21.1) (2026-09-09)
32
+
33
+
34
+ ### Bug Fixes
35
+
36
+ * release-hook-and-performance-safety ([#565](https://github.com/0xPlayerOne/code-foundry/issues/565)) ([0d410c6](https://github.com/0xPlayerOne/code-foundry/commit/0d410c68abfbc0d9fdd03b7aa81676489e459c19))
37
+ * **release:** automate self publication after CI ([91b2655](https://github.com/0xPlayerOne/code-foundry/commit/91b265585aa03e742e5bfc8a839b2794bf234491))
38
+
39
+
40
+ ### Performance
41
+
42
+ * **ci:** batch native Rust test targets by category ([#564](https://github.com/0xPlayerOne/code-foundry/issues/564)) ([6def882](https://github.com/0xPlayerOne/code-foundry/commit/6def8826e90b23d021a4f4587fa843dbb37df0e7))
43
+
44
+ ## [1.21.0](https://github.com/0xPlayerOne/code-foundry/compare/v1.20.2...v1.21.0) (2026-09-09)
45
+
46
+
47
+ ### Features
48
+
49
+ * **release:** wire qualified draft staging and immutable publication ([#558](https://github.com/0xPlayerOne/code-foundry/issues/558)) ([19aea32](https://github.com/0xPlayerOne/code-foundry/commit/19aea32f50c747cdc5642d1752a9f02008c157f6))
50
+
51
+
52
+ ### Bug Fixes
53
+
54
+ * **quality:** harden HTML filtering ([#562](https://github.com/0xPlayerOne/code-foundry/issues/562)) ([ea45590](https://github.com/0xPlayerOne/code-foundry/commit/ea45590db24dc05ef12db57e80c7405fa217514f))
55
+
56
+ ## [1.20.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.20.1...v1.20.2) (2026-09-09)
57
+
58
+
59
+ ### Bug Fixes
60
+
61
+ * **ci:** retain scoped task receipts across success and failure ([#557](https://github.com/0xPlayerOne/code-foundry/issues/557)) ([ee0fe45](https://github.com/0xPlayerOne/code-foundry/commit/ee0fe45e821621e3193831d70a533077b7bcf8a9))
62
+
3
63
  ## [1.20.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.20.0...v1.20.1) (2026-09-09)
4
64
 
5
65
 
package/README.md CHANGED
@@ -58,7 +58,7 @@ preserved and can coexist with the standard baseline.
58
58
  The generated configuration is the one place to control the baseline. See
59
59
  [Configuration reference](docs/CONFIGURATION.md) for the visual configuration
60
60
  guide and [Initialization and synchronization](docs/INITIALIZATION.md) for
61
- the two-command workflow.
61
+ the init, sync, and doctor workflow.
62
62
 
63
63
  New repositories default to GPL-3.0-or-later. Existing projects preserve an
64
64
  authored license unless a replacement is explicitly selected. Our maintained
@@ -82,9 +82,11 @@ PRs: it converts them to drafts without checking out PR code. It verifies the
82
82
  event head and update timestamp before changing state, and excludes Release
83
83
  Please version PRs whose release workflow owns readiness. Pull-request
84
84
  validation runs on the ready-for-review transition and on new commits while a
85
- PR remains ready; draft updates allocate no validation runner. Converting a PR
86
- to draft runs only the lightweight cancellation control. Scheduled and manually
87
- dispatched audits are unaffected.
85
+ PR remains ready; draft updates allocate no validation runner by default. Set
86
+ `draft_protection: false` in generated consumer configuration, or pass
87
+ `draft-protection: false` to a Cloudflare reusable workflow, to run those gates
88
+ for drafts. Converting a PR to draft runs only the lightweight cancellation
89
+ control. Scheduled and manually dispatched audits are unaffected.
88
90
 
89
91
  Jobs are language-aware and skip irrelevant setup inside the applicable
90
92
  aggregate checks. TypeScript uses Oxlint, Oxfmt, and Bun's native
@@ -125,9 +127,16 @@ workflow runs.
125
127
  In the default `direct` flow, changes reach `main` through feature pull
126
128
  requests and Release Please opens a versioned release PR against `main`. In
127
129
  the `staging-release` flow, a promotion PR promotes `staging` into `main`
128
- first. Either way, Release Please creates a GitHub release after the version
129
- PR is merged. npm publication is opt-in through `npm_publish: true` and
130
- supports npm trusted publishing or an `NPM_TOKEN` fallback.
130
+ first. Generated consumer callers create a GitHub release after the version PR
131
+ is merged; npm publication is opt-in through `npm_publish: true` and supports
132
+ npm trusted publishing or an `NPM_TOKEN` fallback.
133
+
134
+ Code Foundry's own release caller is stricter: it qualifies the package across
135
+ Node 20, 22, and 24, stages the exact qualified archive, publishes the
136
+ immutable GitHub Release, and publishes that archive through the verified
137
+ publisher. See
138
+ [Consumer qualification](docs/consumer-qualification.md) and [Qualified
139
+ publication](docs/qualified-publication.md).
131
140
 
132
141
  Read [Release management](docs/RELEASES.md),
133
142
  [Release integrity and build provenance](docs/release-integrity.md), and
@@ -137,16 +146,10 @@ copied into other repositories.
137
146
 
138
147
  ## Documentation and extensions
139
148
 
140
- The `docs/` directory contains generalized operational guides. Add
141
- repository-specific documentation there as well; synchronization does not
142
- replace files in `docs/`.
143
-
144
- - [Documentation index](docs/README.md)
145
- - [Initialization and synchronization](docs/INITIALIZATION.md)
146
- - [Workflow and CI conventions](docs/WORKFLOWS.md)
147
- - [Release management](docs/RELEASES.md)
148
- - [Publishing packages](docs/PUBLISHING.md)
149
- - [Caching and remote caching](docs/CACHING.md)
149
+ The `docs/` directory contains generalized operational guides. Start with the
150
+ [documentation index](docs/README.md), which groups every guide by task. Add
151
+ repository-specific documentation there as well; synchronization preserves
152
+ files in `docs/`.
150
153
 
151
154
  ## License
152
155