From 233a5d802315be53dba23eb8cea9d224d42b8571 Mon Sep 17 00:00:00 2001 From: Frank Bria <136862992+frankbria@users.noreply.github.com> Date: Thu, 17 Sep 2026 08:12:39 -0700 Subject: [PATCH 1/2] ci: open or update an issue when a scheduled Unlocked Resolution run fails (#1242) unlocked-resolution.yml was the third scheduled-only check and the only one still without the #1239 signal. Add the same report-scheduled-failure job, add it to the 1239 guard's CALLERS, and grant issues: write on release.yml's call so check-workflow-calls does not refuse the release run. --- .github/workflows/release.yml | 8 ++++++++ .github/workflows/unlocked-resolution.yml | 17 +++++++++++++++-- CLAUDE.md | 5 +++-- tests/ci/test_scheduled_failure_signal_1239.py | 1 + 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2fbfd17..0643b2b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,6 +25,14 @@ jobs: unlocked-resolution: name: Unlocked resolution uses: ./.github/workflows/unlocked-resolution.yml + # unlocked-resolution.yml's `report-scheduled-failure` job declares + # `issues: write` (#1242). It never fires here — it is gated on the schedule + # event — but a reusable call must grant every scope the callee declares or + # GitHub refuses the run as a `startup_failure` with no log + # (check-workflow-calls.yml; same as deploy.yml's call of test.yml, #1241). + permissions: + contents: read + issues: write build: name: Build sdist + wheel diff --git a/.github/workflows/unlocked-resolution.yml b/.github/workflows/unlocked-resolution.yml index bde05ecf..55fe15a2 100644 --- a/.github/workflows/unlocked-resolution.yml +++ b/.github/workflows/unlocked-resolution.yml @@ -16,8 +16,9 @@ name: Unlocked Resolution # The schedule is the point: this break arrives from upstream, not from a commit # of ours, so it can appear on a day nobody pushed anything. # -# Known limitation: a red scheduled run notifies via GitHub's normal workflow -# notifications; it does not open an issue. Deliberate — one moving part. +# A red scheduled run opens or updates an issue (#1239, #1242) via the shared +# scheduled-failure-issue.yml job below — the Actions tab alone is not a signal +# anyone reads on a day nobody pushed. on: schedule: @@ -80,3 +81,15 @@ jobs: tests/adapters/test_sdk_kwargs_guard_614.py \ tests/adapters/test_model_defaults_guard_1112.py \ -q + + # Same signal test.yml and web-ui-audit.yml use (#1239): a red scheduled run + # has no PR to be red on, so open or update the one issue for this workflow. + # `failure()` only, as in the other two callers: a cancelled run is usually a + # deliberate cancel, and the next morning's run reports on its own. + report-scheduled-failure: + name: Report scheduled failure + needs: [unlocked-install] + if: failure() && github.event_name == 'schedule' + uses: ./.github/workflows/scheduled-failure-issue.yml + permissions: + issues: write diff --git a/CLAUDE.md b/CLAUDE.md index 685758ee..e8f592ef 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -204,8 +204,9 @@ the lock. Before that, the **Unlocked Resolution** workflow `pyproject.toml` ranges with no lockfile against live PyPI, installs into a throwaway env, and runs `cf --help` plus both SDK guards against *that* env. It runs daily on a schedule — this break arrives from upstream, so it can appear on -a day nobody pushed — on PRs touching `pyproject.toml` or the guards, and as a -required gate on `release.yml`. `tests/ci/test_unlocked_resolution_guard_1169.py` +a day nobody pushed, which is why a red scheduled run opens or updates an issue +via `scheduled-failure-issue.yml` (#1239, #1242) — on PRs touching +`pyproject.toml` or the guards, and as a required gate on `release.yml`. `tests/ci/test_unlocked_resolution_guard_1169.py` pins those properties. #1170 moved where the risk lives, so the #614 guard now carries two halves. The diff --git a/tests/ci/test_scheduled_failure_signal_1239.py b/tests/ci/test_scheduled_failure_signal_1239.py index b6bceff9..74de83d1 100644 --- a/tests/ci/test_scheduled_failure_signal_1239.py +++ b/tests/ci/test_scheduled_failure_signal_1239.py @@ -29,6 +29,7 @@ CALLERS = { "test.yml": ["test-summary", "e2e-browser-full", "e2e-backend-tests"], "web-ui-audit.yml": ["npm-audit"], + "unlocked-resolution.yml": ["unlocked-install"], } From 67c164bdfa33237f6a8535d1258b020b2824dd5d Mon Sep 17 00:00:00 2001 From: Frank Bria <136862992+frankbria@users.noreply.github.com> Date: Thu, 17 Sep 2026 08:16:06 -0700 Subject: [PATCH 2/2] docs: keep the Unlocked Resolution sentence parallel after naming the #1242 signal --- CLAUDE.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index e8f592ef..79609d56 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -204,9 +204,10 @@ the lock. Before that, the **Unlocked Resolution** workflow `pyproject.toml` ranges with no lockfile against live PyPI, installs into a throwaway env, and runs `cf --help` plus both SDK guards against *that* env. It runs daily on a schedule — this break arrives from upstream, so it can appear on -a day nobody pushed, which is why a red scheduled run opens or updates an issue -via `scheduled-failure-issue.yml` (#1239, #1242) — on PRs touching -`pyproject.toml` or the guards, and as a required gate on `release.yml`. `tests/ci/test_unlocked_resolution_guard_1169.py` +a day nobody pushed — on PRs touching `pyproject.toml` or the guards, and as a +required gate on `release.yml`. A red *scheduled* run opens or updates an issue +via `scheduled-failure-issue.yml` (#1239, #1242), like the other two daily +checks. `tests/ci/test_unlocked_resolution_guard_1169.py` pins those properties. #1170 moved where the risk lives, so the #614 guard now carries two halves. The