code-foundry 1.46.3 → 1.46.4

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.
@@ -447,6 +447,9 @@ jobs:
447
447
  # when it validated, github.token after fallback. The token value
448
448
  # lives only in this masked env expression, never in step outputs.
449
449
  AUTO_MERGE: ${{ steps.credentials.outputs.auto_merge }}
450
+ # Read Actions ownership with the workflow credential, not an
451
+ # automation token that may only have release/PR write permissions.
452
+ VALIDATION_TOKEN: ${{ github.token }}
450
453
  GH_TOKEN: ${{ steps.credentials.outputs.token_source == 'configured' && (secrets.CODE_FOUNDRY_TOKEN) || github.token }}
451
454
  run: |
452
455
  set -euo pipefail
@@ -464,12 +467,87 @@ jobs:
464
467
  fi
465
468
 
466
469
  for pr in "${release_prs[@]}"; do
467
- PR_DRAFT=$(gh pr view "$pr" --repo "$GITHUB_REPOSITORY" --json isDraft --jq '.isDraft')
470
+ pr_state=$(gh pr view "$pr" --repo "$GITHUB_REPOSITORY" \
471
+ --json isDraft,headRefOid,headRefName \
472
+ --jq '[.isDraft, .headRefOid, .headRefName] | @tsv')
473
+ read -r PR_DRAFT PR_HEAD PR_BRANCH <<< "$pr_state"
474
+ if [[ ! "$PR_DRAFT" =~ ^(true|false)$ || ! "$PR_HEAD" =~ ^[a-f0-9]{40}$ ]]; then
475
+ echo 'Unable to verify generated release PR state.' >&2
476
+ exit 1
477
+ fi
478
+ case "$PR_BRANCH" in
479
+ release-please--branches--main|release-please--branches--main--*) ;;
480
+ *) echo 'Release PR head left the generated namespace; refusing readiness mutation.' >&2; exit 1 ;;
481
+ esac
468
482
  if [ "$AUTO_MERGE" = true ]; then
469
483
  if [ "$PR_DRAFT" = false ]; then
470
- # A Release Please PR can be updated while already ready. Reset
471
- # it so ready_for_review always starts validation for the exact
472
- # current release head.
484
+ # A ready head already receives synchronize validation. Preserve
485
+ # its live/completed owner instead of cancelling and reallocating
486
+ # CI every time another queued main release run prepares it.
487
+ runs=$(GH_TOKEN="$VALIDATION_TOKEN" gh api \
488
+ "repos/$GITHUB_REPOSITORY/actions/runs" --method GET \
489
+ -f event=pull_request -f head_sha="$PR_HEAD" -f per_page=100)
490
+ validation=$(jq -er --arg head "$PR_HEAD" --arg branch "$PR_BRANCH" \
491
+ --arg repo "$GITHUB_REPOSITORY" '
492
+ def active: .status == "queued" or .status == "in_progress" or
493
+ .status == "waiting" or .status == "pending" or .status == "requested";
494
+ def valid_run:
495
+ type == "object" and
496
+ (.id | type == "number" and . > 0 and floor == .) and
497
+ (.run_attempt | type == "number" and . >= 1 and floor == .) and
498
+ (.head_sha | type == "string" and test("^[a-f0-9]{40}$")) and
499
+ (.head_branch | type == "string" and length > 0) and
500
+ (.event | type == "string" and length > 0) and
501
+ (.repository | type == "object") and
502
+ (.repository.full_name | type == "string" and length > 0) and
503
+ (.path | type == "string" and length > 0) and
504
+ ((active and has("conclusion") and .conclusion == null) or
505
+ (.status == "completed" and
506
+ (.conclusion == "success" or .conclusion == "failure" or
507
+ .conclusion == "timed_out" or .conclusion == "action_required" or
508
+ .conclusion == "cancelled" or .conclusion == "skipped" or
509
+ .conclusion == "neutral")));
510
+ if (.workflow_runs | type) != "array" or
511
+ (.total_count | type) != "number" then
512
+ error("Invalid validation owner response")
513
+ elif .total_count != (.workflow_runs | length) then
514
+ error("Incomplete validation owner response")
515
+ elif any(.workflow_runs[]; valid_run | not) then
516
+ error("Invalid validation run identity or state")
517
+ else
518
+ [.workflow_runs[] | (.path | split("@")[0]) as $workflow | select(
519
+ .head_sha == $head and .head_branch == $branch and
520
+ .event == "pull_request" and .repository.full_name == $repo and
521
+ ($workflow == ".github/workflows/validation.yml" or
522
+ $workflow == ".github/workflows/validation_self-ci.yml")
523
+ )] | sort_by(.id) | last |
524
+ if . == null then "missing"
525
+ elif active or (.status == "completed" and .conclusion == "success") then "owned"
526
+ elif .status == "completed" and
527
+ (.conclusion == "failure" or .conclusion == "timed_out" or
528
+ .conclusion == "action_required") then "failed"
529
+ elif .status == "completed" and
530
+ (.conclusion == "cancelled" or .conclusion == "skipped" or
531
+ .conclusion == "neutral") then "missing"
532
+ else error("Unconfirmed validation owner state") end
533
+ end' <<< "$runs")
534
+ fresh_state=$(gh pr view "$pr" --repo "$GITHUB_REPOSITORY" \
535
+ --json isDraft,headRefOid,headRefName \
536
+ --jq '[.isDraft, .headRefOid, .headRefName] | @tsv')
537
+ if [ "$fresh_state" != "$pr_state" ]; then
538
+ echo 'Release PR state changed during validation lookup; refusing a stale readiness mutation.' >&2
539
+ exit 1
540
+ fi
541
+ if [ "$validation" = owned ]; then
542
+ echo "Release PR #$pr already has current-head validation; preserving readiness."
543
+ continue
544
+ fi
545
+ if [ "$validation" = failed ]; then
546
+ echo "Current-head validation failed for release PR #$pr; resolve or explicitly retry it before release." >&2
547
+ exit 1
548
+ fi
549
+ # A missing, cancelled or skipped owner still needs the existing
550
+ # explicit ready_for_review transition with the validated token.
473
551
  gh pr ready --undo "$pr" --repo "$GITHUB_REPOSITORY"
474
552
  fi
475
553
  gh pr ready "$pr" --repo "$GITHUB_REPOSITORY"
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.46.4](https://github.com/0xPlayerOne/code-foundry/compare/v1.46.3...v1.46.4) (2026-10-08)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **release:** preserve current-head validation ownership ([#737](https://github.com/0xPlayerOne/code-foundry/issues/737)) ([aa8bfcf](https://github.com/0xPlayerOne/code-foundry/commit/aa8bfcfbfcd3c213d15ba9e1fbfb5c65ab8ddb6b))
9
+
3
10
  ## [1.46.3](https://github.com/0xPlayerOne/code-foundry/compare/v1.46.2...v1.46.3) (2026-10-07)
4
11
 
5
12
 
@@ -133,16 +140,9 @@
133
140
 
134
141
  * **pre-commit:** make the commit gate change-aware ([#702](https://github.com/0xPlayerOne/code-foundry/issues/702)) ([f5bca91](https://github.com/0xPlayerOne/code-foundry/commit/f5bca91c177b30b99f5d62287255d191617b4788))
135
142
 
136
- ## [1.43.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.43.1...v1.43.2) (2026-10-03)
137
-
138
-
139
- ### Bug Fixes
140
-
141
- * require candidate asset qualification before stable promotion ([#705](https://github.com/0xPlayerOne/code-foundry/issues/705)) ([f42e4f5](https://github.com/0xPlayerOne/code-foundry/commit/f42e4f5fb063ea22fa25791a13c75c90d41bf17a))
142
-
143
143
  <!-- code-foundry: packaged changelog -->
144
144
 
145
145
  ---
146
146
 
147
- This packaged changelog lists the 20 most recent releases; 238 older releases are omitted to keep the package small.
147
+ This packaged changelog lists the 20 most recent releases; 239 older releases are omitted to keep the package small.
148
148
  See the [full changelog](https://github.com/0xPlayerOne/code-foundry/blob/main/CHANGELOG.md) for the complete history.
package/docs/WORKFLOWS.md CHANGED
@@ -82,6 +82,23 @@ to run generated gates for drafts. Cloudflare callers use their equivalent
82
82
  `draft-protection: false` input. These opt-outs do not remove Draft Guard or
83
83
  draft-PR automation; they only allow the protected gates to run for drafts.
84
84
 
85
+ Generated release PR preparation preserves a ready head when the canonical
86
+ generated `.github/workflows/validation.yml` (or Code Foundry
87
+ `validation_self-ci.yml`) pull-request run for that exact head, branch, and
88
+ repository is the newest matching run and is active or successful. API workflow
89
+ paths may include a Git ref suffix; ownership compares the canonical filename.
90
+ The workflow credential reads Actions ownership; the validated automation credential still performs readiness writes.
91
+ A newer failed qualification blocks release preparation until it is resolved or
92
+ explicitly retried. Missing, cancelled, or skipped validation still receives the
93
+ explicit draft/ready transition; a new draft receives its first readiness event.
94
+ An unavailable or malformed ownership response, an incomplete Actions page, or a
95
+ PR state change during the lookup fails before mutation. An older live run cannot
96
+ mask a newer failed qualification. Invalid automation credentials retain the
97
+ manual draft fallback. This changes readiness only: release creation, qualification,
98
+ tag publication, and branch-policy-gated exact-head merging still run through
99
+ the existing release pipeline. Consumers must adopt the fixed reusable workflow
100
+ pin before they receive this behavior; older pins still reset ready heads.
101
+
85
102
  ## Billing pause
86
103
 
87
104
  Code Foundry consumers can stop all generated jobs from allocating GitHub-hosted
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-foundry",
3
- "version": "1.46.3",
3
+ "version": "1.46.4",
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": {