Skip to content

ci: open or update an issue when a scheduled Unlocked Resolution run fails (#1242) - #1243

Merged
frankbria merged 2 commits into
mainfrom
feature/issue-1242-unlocked-resolution-scheduled-failure
Sep 17, 2026
Merged

frankbria merged 2 commits into
mainfrom
feature/issue-1242-unlocked-resolution-scheduled-failure

Conversation

@frankbria

Copy link
Copy Markdown
Owner

Summary

Implements #1242: Wire unlocked-resolution.yml into the scheduled-failure issue signal from #1239.

  • unlocked-resolution.yml gains the same report-scheduled-failure job test.yml and web-ui-audit.yml carry: needs: [unlocked-install], if: failure() && github.event_name == 'schedule', uses: ./.github/workflows/scheduled-failure-issue.yml, permissions: issues: write. The header note that said a red scheduled run "does not open an issue" is replaced.
  • release.yml grants contents: read + issues: write on its unlocked-resolution call, with the same comment deploy.yml carries for test.yml (ci: open or update an issue when a scheduled Test Suite or Web UI Audit run fails (#1239) #1241), so the upstream check-workflow-calls guard does not refuse the release run.
  • "unlocked-resolution.yml": ["unlocked-install"] added to CALLERS in tests/ci/test_scheduled_failure_signal_1239.py (RED first: 3 failures, then GREEN).
  • CLAUDE.md: one clause in the Unlocked Resolution paragraph naming the signal.

Acceptance Criteria

Test Plan

  • Guard test extended first (TDD): 3 RED → 16 GREEN across tests/ci/test_scheduled_failure_signal_1239.py + test_unlocked_resolution_guard_1169.py; tests/ci 90 passed
  • Diff coverage gate: n/a (workflow YAML + one test table entry; no new executable lines)
  • actionlint .github/workflows/*.yml clean; ruff check clean
  • Internal code review (advisory) completed
  • Cross-family review pass: codex (gpt-5.5) — no findings
  • Test mutation sanity check: with the new job deleted, the 3 new parametrized cases fail (captured in the demo)

Known Limitations / Intentionally Deferred

Implementation Notes

  • Inside a workflow_call, github.event_name is the caller's event, so when release.yml calls this workflow the new job never fires. The grant on that call exists only to satisfy GitHub's reusable-workflow scope rule.

Closes #1242

…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-actions

github-actions Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ GLM review did not complete

This run did not finish, so there is no verdict for this commit.
That is not the same as a clean bill of health — a review that ran
and found nothing says so explicitly ("✅ GLM review: no defects
found.").

Reason: the run was cancelled before the agent finished — usually a newer push superseding it, since this workflow cancels in-progress runs for the same pull request. Nothing is wrong with the review itself: the newest commit is reviewed by its own run, which posts its verdict as a separate comment. This notice is not replaced by that run and can be disregarded once it appears.

View job run — the agent transcript is attached there as a
glm-review-transcript-* artifact when the agent got far enough to
write one.

What this comment held when the run stopped

Claude Code is working…

I'll analyze this and get back to you.

View job run

@frankbria

Copy link
Copy Markdown
Owner Author

Cross-family review — codex (gpt-5.5), pre-PR pass, diff unchanged at 233a5d8

codex review --base main over the branch diff. Codex read scheduled-failure-issue.yml, both sibling callers, deploy.yml's #1241 grant, and the #1239 guard, then reported no findings. Its closing assessment, verbatim:

The workflow changes are consistent with the existing scheduled-failure signaling pattern, and the added permissions on the release caller address the reusable-workflow permission requirement without changing normal release behavior.

Internal (Claude, advisory) review also APPROVEd: no Critical/Major; one Suggestion to reword the inserted CLAUDE.md clause, which lands with the docs-sync commit.

Also verified locally with the pinned upstream check_workflow_calls.py (frankbria/glm-review@21fea32): OK on all 14 workflows; with the new release.yml grant removed it reports issues: needs write, caller grants none for the unlocked-resolution call, which is the startup_failure this grant prevents.

@frankbria

Copy link
Copy Markdown
Owner Author

Demo evidence (Phase 11) — Showboat, CLI-only

Criterion Action Outcome evidence Status
report-scheduled-failure job exists with needs/if/uses/permissions; header updated Parse unlocked-resolution.yml with PyYAML and print the job; grep header Job = needs: [unlocked-install], if: failure() && github.event_name == 'schedule', uses: ./.github/workflows/scheduled-failure-issue.yml, permissions: {issues: write}; header line 19 now says a red scheduled run opens or updates an issue VERIFIED
unlocked-resolution.yml in CALLERS of the #1239 guard pytest tests/ci/test_scheduled_failure_signal_1239.py -k unlocked 3 passed (existence+condition, needs completeness, scope grant) VERIFIED
Guard actually asserts the job (mutation) Delete the new job from the workflow, rerun -k unlocked, restore from git 3 failed, all three [unlocked-resolution.yml] cases VERIFIED
release.yml grants issues: write on its call Run pinned upstream check_workflow_calls.py (glm-review@21fea32) on all workflows OK: 14 file(s) checked — every reusable-workflow call grants the scopes its callee declares VERIFIED
The grant is what prevents startup_failure Same checker on a copy of release.yml with the block removed FAIL release.yml :: job unlocked-resolution -> unlocked-resolution.yml (job report-scheduled-failure) issues: needs write, caller grants none, exit 1 VERIFIED
Job never fires on PR / release runs; workflows compile actionlint .github/workflows/*.yml; condition gated on schedule (guard asserts no other event name) actionlint 0 findings; same mechanism live for deploy.yml → test.yml since #1241 VERIFIED

Full narrated demo: demo-1242.md in the session scratchpad (not committed).

@github-actions

github-actions Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @frankbria's task in 3m 7s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Sep 17, 2026

Copy link
Copy Markdown

Review

Small, well-scoped CI change — I traced the diff against the two sibling callers (test.yml, web-ui-audit.yml) and the shared scheduled-failure-issue.yml, plus the guard test.

Correctness

  • unlocked-resolution.yml's new report-scheduled-failure job is byte-for-byte structurally identical to the sibling jobs in test.yml/web-ui-audit.yml: needs: [unlocked-install] matches the job it depends on, if: failure() && github.event_name == 'schedule' matches the guard's required condition exactly, and permissions: issues: write is job-scoped only (not inherited from the workflow-level contents: read), consistent with how the other two callers do it.
  • The release.yml grant (contents: read + issues: write on the unlocked-resolution call) mirrors deploy.yml's existing grant on test.yml almost verbatim, including the explanatory comment. The PR description's own reproduction (removing the grant reproduces startup_failure) is a good sanity check that this is load-bearing rather than cosmetic.
  • The CALLERS addition in tests/ci/test_scheduled_failure_signal_1239.py ("unlocked-resolution.yml": ["unlocked-install"]) is correctly keyed to the real job name in that workflow, and I walked through the guard's three parametrized assertions (condition shape, needs completeness, scope grant) by hand against the new job — all satisfied.
  • The doc update in CLAUDE.md accurately replaces the old "does not open an issue" limitation note with the new behavior, and stays consistent with how the same fact is phrased in unlocked-resolution.yml's own header comment.

Design note (already called out in the PR, agree with the call): gating only on failure() (not cancelled()) matches the existing pattern in the other two callers, so this keeps behavior uniform rather than introducing a third variant. Fine to defer as noted.

Nothing blocking. The implementation note about github.event_name referring to the caller's event inside a workflow_call (so the job is a no-op when release.yml invokes it) is correct and worth having spelled out, since it's the kind of thing that's easy to get backwards when reading the YAML in isolation.

I wasn't able to execute the test suite or actionlint directly in this environment, so this review is based on careful static tracing of the workflow YAML against the guard's assertions and the two existing sibling implementations — the PR's own stated verification (actionlint clean, guard RED→GREEN, upstream check_workflow_calls.py OK/FAIL reproduction) covers the parts I couldn't independently execute.

@claude

claude Bot commented Sep 17, 2026

Copy link
Copy Markdown

Review

This is a clean, minimal change that wires unlocked-resolution.yml into the existing report-scheduled-failure signal (#1239), exactly mirroring the two prior callers.

Verified against the siblings:

  • report-scheduled-failure job in unlocked-resolution.yml (needs [unlocked-install], if: failure() && github.event_name == 'schedule', uses: ./.github/workflows/scheduled-failure-issue.yml, permissions: issues: write) is byte-for-byte structurally identical to the equivalent jobs in test.yml and web-ui-audit.yml.
  • The release.yml grant (contents: read + issues: write on the unlocked-resolution call) is word-for-word the same comment/shape as deploy.yml's existing grant on its test.yml call from ci: open or update an issue when a scheduled Test Suite or Web UI Audit run fails (#1239) #1241 — good consistency, and the reasoning (reusable-workflow callers must grant every scope the callee declares, even scopes gated off at runtime) is correct per GitHub's workflow_call permission model.
  • CALLERS in tests/ci/test_scheduled_failure_signal_1239.py now includes "unlocked-resolution.yml": ["unlocked-install"], which correctly lists the workflow's one real job as the needs dependency — matches what's actually in the workflow.
  • The if condition correctly gates on schedule only, so it stays dormant for the pull_request/workflow_dispatch/workflow_call triggers this workflow also has (important since release.yml invokes it via workflow_call, where github.event_name resolves to the caller's event, as the PR correctly notes in Implementation Notes).
  • Docs update in CLAUDE.md is a small, accurate clause addition consistent with the described behavior, and doesn't overstate what changed.

No correctness, security, or test-coverage issues found. This is a pure CI/workflow-config change (no application code), so the usual code-quality/performance concerns don't really apply — the main risk surface is "does the YAML shape match what the guard test and GitHub's reusable-workflow permission checker expect," and it does.

Two minor, non-blocking observations (not asking for changes):

  • The known-limitation note about failure()-only (no cancelled()) is a reasonable, explicitly-documented tradeoff — consistent with the two existing callers, so no inconsistency introduced.
  • As the PR itself notes, this path can't be exercised end-to-end from a PR since the reporting job is schedule-gated; the guard test plus the identical, already-proven pattern in the two sibling workflows is reasonable evidence in lieu of a live run.

Nice use of TDD (RED→GREEN) for the guard-table change, and the cross-family (codex) + internal review notes already on this thread line up with what I see in the diff.

@frankbria

Copy link
Copy Markdown
Owner Author

Final Triage Summary (PR #1243)

Cutoff: 2026-09-17T15:16:26Z
New findings since cutoff: 2 bot reviews (claude-review ×2), 0 Critical, 0 Major

Fixed

  • none needed

Skipped (with justification)

  • [claude-review] failure()-only gate, no cancelled() — observation only, agrees with the PR's documented decision; matches both sibling callers.
  • [claude-review] path not exercisable end-to-end from a PR — inherent to a schedule-gated job; the PR's own Unlocked Resolution run (35239121429) shows Report scheduled failure present and skipped on pull_request, and check / check passed the reusable-call permission audit.

Pre-PR: codex (cross-family) no findings; internal advisory APPROVE with one prose suggestion, applied in 67c164b.

@frankbria
frankbria merged commit 8884462 into main Sep 17, 2026
19 checks passed
@frankbria
frankbria deleted the feature/issue-1242-unlocked-resolution-scheduled-failure branch September 17, 2026 15:26
@frankbria

Copy link
Copy Markdown
Owner Author

Post-merge disposition: no new issues — the only deferred item (failure() vs cancelled()) is a recorded decision, not future work. Dependabot's critical/high alerts on main are all GitPython and already covered by open PR #1204. Lessons: 2 entries appended to tasks/lessons.md; the issue-lifecycle phase-07 codex invocation was corrected at the source.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P3.25] Wire unlocked-resolution.yml into the scheduled-failure issue signal from #1239

1 participant