chore(security): integrate protected main into dependency-review owner - #1995
chore(security): integrate protected main into dependency-review owner#1995seonghobae wants to merge 236 commits into
Conversation
kaefa and nonnest2 each carried a hand-copied R-CMD-check.yaml generated from the same upstream r-lib template. Consolidate the shared checkout -> setup-pandoc -> [setup-tinytex] -> setup-r -> setup-r-dependencies -> check-r-package sequence into one workflow_call workflow with inputs for the fields that genuinely vary per repo (r_matrix, needs_tinytex, extra_packages, check_args, pre_check_script). See docs/adr/0023-r-cmd-check-reusable-workflow-consolidation.md and docs/doctoring/r-cmd-check-reusable-workflow-consolidation.md for the full field-by-field audit, including two non-uniform fields (extra-packages, check-r-package args) the initial survey missed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The dependency-review.yml consolidation's caller PRs surfaced a real Devin security finding: uses: <reusable-workflow>@main runs an unreviewed central change against every caller's PR checks with no review in the calling repo. Fixed there (all four callers pinned to a commit SHA); apply the same correction to this not-yet-merged reusable workflow's own documented example before any caller PR copies the unsafe pattern. Also notes the separate required-status-check-name gotcha (converting a job to uses: renames its published check) to check for in each caller repo before merging. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…no change needed Investigated whether org-queue-sweep (pr-review-merge-scheduler.yml) can be replaced with native GitHub Actions scheduling/filter/condition primitives to reduce the org's shared API rate-limit pressure. Conclusion: no change warranted right now. - Its own cadence and its sibling scan-pr-queue's cadence were both already lengthened this week for exactly this reason (#1630, #1704), offset from each other so the two heartbeats don't collide. - A native strategy:matrix per-repo replacement wouldn't reduce API call volume, only parallelize it across up to ~74 concurrent runners -- worse for the already-documented floating-runner-image starvation incident recorded two entries above this one. - Removing the schedule trigger entirely would reintroduce the exact "approved but unmerged, no later event" gap #1630's own root-cause section already fixed -- GitHub Actions has no native event for a PR's mergeability changing due to elapsed time or a base-branch advance. - Shrinking ORG_SWEEP_MAX_PRS would reintroduce a different already-fixed gap (the BandScope 34-PR queue-omission incident) for an unrelated symptom. Recorded so a future pass doesn't re-propose the same three rejected alternatives from scratch, and named the actual next lever to check if rate-limit pressure persists (cron-offset against the required review workflows' own event-triggered runs, not sweep cadence again). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ue-sweep-investigation # Conflicts: # docs/product-technical-gap-baseline.md
…olved (#1758) Bypass-merged per explicit user authorization (chicken-and-egg: org Actions queue congestion prevents required checks from even starting). Zero-risk change: documentation plus one small, well-precedented codeql-pr.yml branch-restriction fix, already through 4 rounds of Devin review with every finding addressed and every thread resolved. No CHANGES_REQUESTED review state, mergeable=true, blocked only by unstarted queued checks.
Queue-congestion investigation: 9,368 checks queued organization-wide, ~3
in-progress, queue depth roughly equal to open-PR-count times
required-workflow-count. A doc-only PR still admits every expensive required
job (Strix, Semgrep, CodeQL, Trivy, OSV, Scorecard) because org ruleset
18156473 runs each central workflow *inside the target repository's own
context* and, confirmed live via `gh api orgs/ContextualWisdomLab/rulesets/
18156473` plus `bandscope`'s workflow directory (no local codeql-pr.yml/
strix.yml/security-scan.yml, yet ruleset-injected runs of all three exist),
ignores that target repository's `on:` filters (paths, paths-ignore,
branches, types) entirely.
Mechanism chosen: job-level `if:` cheap-skip, not a trigger-level
paths-ignore. A trigger-level filter is a no-go -- it would be inert in the
40+ ruleset-covered repos (never evaluated) and merge-breaking in `.github`
itself, whose classic branch protection (confirmed live: 14 named required
contexts, strict: true) leaves a path-filtered context Pending forever
instead of reporting a conclusion. A job-level `if:` is safe in both cases:
the ruleset cannot skip a job's own runtime `if:` evaluation (it happens
inside the actual run, using the real PR payload), and in `.github` the gate
job itself always runs and always reports skipped/success so no context is
ever left Pending.
Adds a `changed-scope` job (byte-identical across its five copies apart from
one `if:` line, pinned by tests/test_docs_only_pr_runner_admission.py) as the
first job in security-scan.yml, sast-semgrep.yml, strix.yml, scorecard-pr.yml,
and osv-scanner-pr.yml. It classifies the PR's changed files via `gh api
.../pulls/<n>/files`, fails OPEN on any read/count mismatch, and publishes
`code`/`deps` outputs that downstream jobs AND into their existing
`github.event.action != 'closed'` guard via `needs:`.
codeql-pr.yml gets the same classifier as a step in `detect-languages`, but
`analyze-head` is gated at STEP level, not job level: live run 33708209086
proved a job-level skip on a job whose `strategy.matrix` comes from another
job's output publishes the literal unexpanded `${{ matrix.language }}`
check-run name instead of the required `CodeQL compatibility analysis
(actions|python)` contexts, so those checks never appear. `analyze-merge`
(required nowhere) keeps a job-level guard and doubles as the future
observation point for whether analyze-head could safely follow.
strix.yml keeps its existing paths-ignore (the one documented exception --
live run-event census shows its runs are native, not ruleset-injected, in
the three repositories 18156473 excludes: .github, noema,
IRT-bibliography-set) with corrected comments, plus the same job-level gate
for its ruleset-covered runs.
sbom-generation.yml drops its `pull_request` trigger for `push`+`release`
only: nothing gated on the PR-scoped SBOM artifact, and its
`dependency-snapshot: true` submission is the only feeder of the dependency
graph sbom-inventory-scheduler.yml reads hourly -- a PR-head snapshot was
transiently polluting that graph with unmerged dependencies.
The doc/image pattern list replaces `LICENSE.*` (matches the executable
LICENSE.py) with explicit LICENSE/LICENSE.txt/COPYING/COPYING.txt/NOTICE/
NOTICE.txt names; every ambiguity resolves toward scanning.
No security-scanning coverage is weakened: every gate defaults to scanning
on read failure or ambiguity, scheduled-security-scan.yml and
scorecard-analysis.yml remain full unfiltered backstops, secret-scan.yml is
untouched (already diff-scoped), and codeql-pr.yml's detect-languages keeps
its unconditional if: because gating it destroys the two required CodeQL
contexts (same run 33708209086 evidence).
Deferred: python-security.yml (D6) is skipped in this change -- it is
required nowhere and its detect-python step is the target of a fragile
regression test (test_workflow_file_detection_pipefail_regression.py) that
extracts the step body by raw text position; the design itself calls this
piece lowest-value and safe to ship separately. Two owner actions are
intentionally not self-executed: triaging the pre-existing, independent
codeql-pr.yml startup_failure in every ruleset-covered repo (blocks CodeQL
merges org-wide regardless of this change), and removing
scorecard-pr.yml/osv-scanner-pr.yml from the ruleset (admin:org).
Full evidence, live re-verification, and the matrix-hazard writeup:
docs/doctoring/required-workflow-path-filter-boundary.md.
Local verification (org runner admission is near zero, so CI cannot
validate): coverage run -m pytest tests && coverage report -- 2650 passed,
1 skipped, 100% line coverage on scripts/ci; interrogate -- 100% docstring
coverage; every touched workflow YAML-parses; actionlint reports no new
findings (the one pre-existing SC2129 style note in strix.yml is unchanged
from origin/main, just shifted by line count).
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…riction (#1767) Bypass-merged per the standing chicken-and-egg authorization (org Actions queue congestion prevents required checks from even starting). Docs-only change recording an already-live admin:org fix (codeql-pr.yml removed from ruleset 18156473) -- independently triple-confirmed by two peer sessions before this merge. mergeable=true, no CHANGES_REQUESTED, blocked only by unstarted queued checks.
…#1769) Prevent the queue-cleanup worker from cancelling itself during high-frequency pull-request synchronize events. The coalescer now completes the active cleanup and queues a successor invocation. A focused regression contract guards the active concurrency block. Chicken-and-Eggs bypass rationale: required Actions were all queued behind the same organization-wide ceiling this control-plane change reduces. Post-merge protected-main revalidation is required.
…manent coverage audit, fix concurrency gaps (#1768) 4 rounds of Devin Review addressed and independently re-verified; codeql-pr.yml removed from ruleset 18156473 (100% startup_failure, hard GitHub platform restriction), 23 repos given real CodeQL coverage via default-setup, a permanent scheduled audit added so the gap self-detects going forward, and a stale pre-existing production allowlist + a missing concurrency block on scorecard-analysis.yml fixed along the way.
…false (#1775) Bypass-merge authorized by the user (2026-09-03, repeated across multiple /loop sessions) for the confirmed chicken-and-egg situation: this PR's own required checks (noema-review, required-workflow-bootstrap, etc.) have sat `queued` for hours under the org's 60-concurrent-job Actions plan ceiling, the exact structural blocker this PR's own fix partially addresses. Content fully independently verified before merge: full suite 2,651 passed, coverage 100%, interrogate 100%, and the fix (queue: max on the coalescer's concurrency group) directly closes a live regression from #1769 that this backlog's own item 13 depends on. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
… for strix.yml on owner directive (#1779) Owner-directed: workflow-repo-PR concurrency scoping, applied to strix.yml (the one remaining exception), after owner explicitly overrode the 2026-08-23/24 rate-limit-storm history and confirmed independent NVIDIA NIM key rate limits.
…lose #1568 instead (#1781) Explicit user directive (2026-09-03): head-SHA-scoped concurrency groups (added for Devin Review's #1568 finding -- a delayed, out-of-order run for an older head could cancel the authoritative run already active for a newer head) mean every push to a PR gets its own group, so rapid successive pushes no longer cancel each other's in-flight runs -- they queue up independently instead. That directly worsens the self-inflicted queue-thrashing pattern this org measured directly today (236/300 cancelled runs attributed to concurrent push volume). Refined during cross-session review (host 1's finding, independently verified before adopting): the real fix isn't to re-key the group but to stop cancelling within it. cancel-in-progress: true is what actually causes the #1568 wrongful kill, independent of whether SHA is in the group key -- scope by repo+PR-number only, but flip cancel-in-progress to false. With false, nothing in the group is ever preempted regardless of arrival order, so the #1568 race is structurally impossible here, not just less likely. A now-queued older-head run still gets a turn once the active run finishes, but the poll step's own live-head/live-state revalidation (already run every iteration, needed for correctness regardless of this setting) makes it self-exit within one poll_interval_seconds instead of running to completion or publishing stale evidence. Plain repo+PR-number scoping also means rapid pushes naturally serialize through one queue instead of spawning N independent per-head groups, which is what actually bounds queue depth here. Updated the workflow's own concurrency comment and three test assertions (two in test_opencode_required_verdict_regression.py, one in test_required_workflow_queue_contract.py) that pinned the old head-SHA + cancel-in-progress:true shape. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
) Bypass-merged per standing user authorization for queue/backlog-root-cause workflow fixes (chicken-and-egg: this PR's own required checks -- Strix, OpenCode Review, and others -- sit pending in the same 60-job Actions-plan admission queue this fix relieves, so they cannot complete without the fix already on main). Local verification fully green before merge: coverage run -m pytest tests (2681 passed, 1 skipped), coverage report --show-missing (100%), interrogate (100%), actionlint clean (one pre-existing unrelated shellcheck note only), yaml.safe_load OK. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
#1786) Devin Review caught a real deadlock in #1781's redesign, independently confirmed by two peer sessions before I acted on it: the workflow-level concurrency: block (line 23, before permissions:/jobs:) applied to the ENTIRE run as a unit -- every job in the file, including the structurally-separate cancel-superseded-opencode-review-runs job. With cancel-in-progress: false, a new push's ENTIRE run -- cleanup job included -- could not even start until the group freed up, which only happens when the older run's own opencode-review-target job finishes. Since OpenCode/Noema inference deliberately has no wall-clock deadline, a long-running older-head review could then block the newer head's review indefinitely -- the opposite of what #1781 was supposed to fix. Fixed by moving concurrency: from workflow-level into job-level, scoped only to opencode-review-target (the job that actually runs the long dispatch+poll). This leaves cancel-superseded-opencode-review-runs and the lightweight bootstrap/coverage jobs completely unblocked: they start immediately on every push, and the cleanup job's own direct Actions API cancellation is what frees up the job-level slot for the new push's poll -- no deadlock, and the #1568 stale-cancels-fresh race stays structurally closed at the same time. Matches strix.yml's existing job-scoped-only reference pattern (confirmed to never have had workflow-level concurrency). host 1 applied the equivalent fix to noema-review.yml on #1661 (extracting its cleanup into a genuinely separate job) after finding this same class of bug there first. Updated two test files' assertions to match the new job-level placement, plus added explicit regression guards (no top-level `^concurrency:`, a job-level `^ concurrency:` exists) so this can't silently regress back to workflow-level scoping. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…is refuted (#1760) Bypass-merge authorized by the user (repeated across multiple /loop sessions today) for the confirmed chicken-and-egg situation: this PR has been open 5h18m, docs-only content fully verified (multiple rounds of Devin/CodeRabbit review, all threads resolved), and its required checks have sat queued for over 1.5 hours since the last update under the org's Actions capacity constraints. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…y-design (#1763) Bypass-merge authorized by the user (repeated across multiple /loop sessions today) for the confirmed chicken-and-egg situation: this PR has been open 4h45m, docs-only content fully verified (multiple rounds of Devin/CodeRabbit review, all threads resolved), and its required checks have sat queued for over 1.5 hours since the last update under the org's Actions capacity constraints. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
… 649.5s connecting (#1765) Bypass-merge authorized by the user (repeated across multiple /loop sessions today) for the confirmed chicken-and-egg situation: this PR has been open 4h20m, docs-only content fully verified (multiple rounds of Devin/CodeRabbit review, all threads resolved), and its required checks have sat queued for over 1.5 hours since the last update under the org's Actions capacity constraints. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…ository_dispatch (#1772) codeql-pr.yml cannot run codeql-action inside a required workflow (GitHub platform restriction, root-caused in docs/doctoring/codeql-pr-required-workflow-always-fails.md and fixed there by removing it from ruleset 18156473). This ADR designs the follow-up: mirror the strix.yml/opencode-review.yml dispatch+poll pattern so codeql-pr.yml stays required-workflow-safe while the actual codeql-action work runs natively in .github via repository_dispatch. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…#1774) * refactor(codeql): extract the Medium+ SARIF gate into a shared, tested script codeql-pr.yml duplicated the same ~70-line inline Python severity gate in both analyze-head and analyze-merge. Extract it to scripts/ci/codeql_sarif_gate.py (100% coverage/docstrings, its own unit tests) so both jobs call one script, and so the dispatch handler designed in docs/adr/0025-codeql-required-workflow-dispatch-architecture.md can reuse it as a third caller without a third copy of the logic. tests/test_codeql_pr_workflow_contract.py pins exact workflow prose; updated its assertions to match the delegation and to exercise the real script file via subprocess instead of re-executing extracted inline script text. This is step 1 of ADR 0025's implementation follow-up. No codeql-action reference is touched — codeql-pr.yml is not currently in the required-workflow ruleset (removed in #1767), so this carries none of that admission-check risk. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(codeql): stop citing a file that doesn't exist on this branch yet Devin review on #1774 flagged the module docstring's reference to docs/adr/0025-codeql-required-workflow-dispatch-architecture.md -- that file only exists on the separate, still-unmerged .github#1772 branch, not here or on main, so the citation was dangling regardless of which PR merges first. Point at the PR instead of a file path that may or may not exist yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* feat(codeql): add the native CodeQL scan dispatch handler (not wired up yet)
Step 2 of ADR 0025's implementation follow-up: the native execution half of
the dispatch+poll design that lets codeql-pr.yml stay required-workflow-safe
while the actual codeql-action work runs unrestricted in .github.
codeql-scan-dispatch.yml mirrors the proven validate/checkout/publish
patterns already used by strix.yml and opencode-review-dispatch.yml:
- validate-dispatch re-authenticates the dispatch actor/sender against the
same OPENCODE_REPOSITORY_DISPATCH_ACTOR/_TARGETS allowlist those handlers
already use, validates the matrix payload shape, and cross-checks every
supplied field against a live `gh api pulls/{n}` read before trusting it.
- scan re-validates live PR metadata again immediately before the privileged
work (closing the TOCTOU window between jobs), materializes the target
repo's exact head SHA via manual git (matching strix.yml's isolation
posture -- no actions/checkout with a foreign token), runs codeql-action
with zero source-root override (so it scans $GITHUB_WORKSPACE exactly the
way codeql-pr.yml and scheduled-security-scan.yml already do), gates on
scripts/ci/codeql_sarif_gate.py fetched at the exact dispatching commit,
and publishes a codeql-dispatch/<language> commit status back onto the
target repo with the same multi-token fallback chain strix.yml uses.
Deliberately has NO workflow_dispatch trigger: an early draft added one for
manual testing, but tests/test_required_workflow_queue_contract.py's
test_no_central_workflow_exposes_branch_selected_manual_dispatch forbids
workflow_dispatch on every central workflow, since it would let a caller run
this token-minting, cross-repo-status-publishing workflow from an arbitrary
non-default ref. A real repository_dispatch POST is the way to test this
end-to-end before wiring it into codeql-pr.yml.
NOT YET WIRED UP: codeql-pr.yml does not dispatch here yet. That rewrite --
the highest-blast-radius part, since it touches the org's central required
workflow and cannot be tested live before merging -- is a separate follow-up
PR, deliberately kept out of this change so this handler can be reviewed on
its own first.
New tests/test_codeql_scan_dispatch_workflow_contract.py checks bash syntax
on every run: block, structural invariants, and exercises the real
validate-dispatch shell logic (actor/target/matrix/live-metadata rejection
paths and the accepting happy path) against a faked `gh`.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(codeql): stop citing a file that doesn't exist on this branch yet
Same class of issue Devin flagged on #1774: this file and its test cited
docs/adr/0025-codeql-required-workflow-dispatch-architecture.md, which only
exists on the separate, still-unmerged .github#1772 branch. Point at the PR
instead of a file path that may or may not exist yet regardless of merge
order.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(codeql): stop scoping CodeQL dispatch to the OpenCode rollout allowlist
Drafting the codeql-pr.yml dispatch step surfaced a real gap here: this
handler reused vars.OPENCODE_REPOSITORY_DISPATCH_TARGETS, a ~12-repo
allowlist that scopes a deliberately gradual OpenCode review rollout.
Confirmed live (gh api orgs/ContextualWisdomLab/rulesets/18156473) that
ruleset 18156473 covers ~ALL org repos except noema/.github/IRT-bibliography-set.
Reusing the narrower list would have silently broken CodeQL dispatch for
every repo not already on the OpenCode rollout list, the moment this handler
gets wired up and re-admitted to the ruleset.
Keeps the actor-identity check (same token-exchange mechanism as
opencode-review-dispatch.yml) but replaces the target-repo allowlist with the
existing ^ContextualWisdomLab/ regex check further down -- CodeQL is meant to
run for every org repo, not a curated subset.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* feat(codeql): rewrite codeql-pr.yml as dispatch+poll (not wired to the ruleset) Step 3 of ADR 0025's implementation follow-up (docs/adr/0025-codeql-required-workflow-dispatch-architecture.md, .github#1772, still open): the last and highest-risk piece, since this is the org's central required-workflow file. Removes every github/codeql-action reference (the platform restriction root-caused in docs/doctoring/codeql-pr-required-workflow-always-fails.md and fixed by removing this file from ruleset 18156473 in #1767) -- this PR does NOT re-admit it to the ruleset, so merging carries zero required-workflow admission risk; it can only self-trigger on .github's own PRs until someone explicitly does that re-admission as a separate, later step. - detect-languages: unchanged. - analyze-head: two sequential steps in ONE job (mirroring opencode-review.yml's opencode-review-target job exactly) -- "Request current-head CodeQL scan dispatch" then "Fail closed without a current-head CodeQL dispatch verdict". Dispatch+poll live in the same job, not two jobs linked by `needs:`, specifically so a dispatch failure fails the job directly with no needs-based skip to reason about. - analyze-merge: deleted. Required nowhere per PR #1766; migrating it doubles this change's risk for a check that gates nothing today (ADR's explicit scope decision). Two bugs caught and fixed during implementation, before either was pushed: 1. A job-level `if:` on analyze-head would have reintroduced the exact unexpanded-matrix-name bug live evidence (run 33708209086) already proved real -- caught by the existing test_codeql_pr_gates_analyze_head_at_step_level_not_job_level contract test. Fixed by keeping analyze-head's admission unconditional (matching the original's proven-safe `needs: detect-languages` with no job-level `if:`) and gating only at step level. 2. An initial two-job (dispatch-analysis + analyze-head) split would have let analyze-head's matrix duplicate the dispatch N times (once per language), each carrying the full language matrix -- triggering N redundant full-matrix scans on the .github side. Fixed by merging dispatch+poll into one job and restricting the dispatch step to fire from only the first matrix shard via `matrix.language == fromJSON(needs.detect-languages.outputs.matrix).include[0].language`. tests/test_codeql_pr_workflow_contract.py rewritten for the new structure (previously pinned the old codeql-action/inline-SARIF-gate shape byte-for-byte). tests/test_docs_only_pr_runner_admission.py's job-vs-step-level gating test updated to match the new two-step shape; its core assertion (no job-level `if:` on analyze-head) is unchanged and still enforced. Depends on #1776 (the native dispatch handler) existing before this dispatch step can ever succeed against a real PR -- opened as draft for that reason, and because this is genuinely untestable live before merge. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(codeql): dispatch per-shard so a dispatch failure fails closed, not silently Peer review on #1778 found a real gap: only the first matrix shard dispatched (carrying the full language matrix), so if THAT dispatch failed, every other shard had no way to know -- each would poll the full 3-hour deadline before self-timing-out for a scan that was never actually requested. A repo with 3 CodeQL languages could turn one dispatch failure into ~9 wasted runner-hours, working directly against the org's active 60-job-ceiling capacity fight. Fixed by having every shard dispatch, but only its own single language (not the full matrix): N single-language dispatches cost the same total .github-side work as one N-language dispatch, while letting each shard read its own steps.dispatch.outcome and fail closed immediately instead of only detecting the failure 3 hours later. The peer's second finding (scope the concurrency group by exact head SHA, mirroring opencode-review.yml) does NOT apply here as a drop-in fix: tests/test_required_workflow_queue_contract.py::test_pull_request_close_events_cancel_superseded_runs_without_heavy_jobs explicitly requires codeql-pr.yml's group to omit head SHA, because this file has no dedicated cancel-on-close cleanup job. Adding head SHA without one would let a stale in-flight run for a superseded head survive a close event indefinitely (it and the closing run would land in different groups and never cancel each other) -- opencode-review.yml can safely add head SHA only because it also runs a separate cancel-superseded-opencode-review-runs job that sweeps stale runs via direct API calls regardless of head SHA. Documented the real, narrower residual risk in a code comment and left it as a tracked follow-up requiring a dedicated cleanup job, not a one-line group change that would regress an existing, deliberately-tested invariant. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(codeql): verify dispatch-status creator identity, not just context codeql-pr.yml's poll step matched a commit status by context alone ("codeql-dispatch/<language>"), which ADR 0025's own Security considerations section already flagged as unresolved: anyone with statuses:write on the target repository can publish an arbitrary context, so a malicious PR could forge its own passing status and skip being scanned entirely. codeql-scan-dispatch.yml mints its publishing token via the same OIDC audience (opencode-github-action) opencode-review-dispatch.yml uses, so the legitimate status always carries that app's bot identity. Mirror opencode-review.yml's existing opencode-agent/opencode-agent[bot] creator check in the poll's jq filter instead of trusting the context name alone. Adds two real-shell-exec regression tests against a faked `gh`: one proving a forged success status from another creator is ignored in favor of the legitimate (here, failing) verdict, one proving the legitimate creator's status is accepted normally. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
(#1788) PR #1779 changed strix.yml's concurrency group.format() expression from the 2-argument form to the 3-argument form format('{0}-{1}-{2}', ...) to restore PR-scoped concurrency (fixing a queue-saturation chicken-egg problem) while keeping repository+event-class isolation. The bash contract test scripts/ci/test_strix_quick_gate.sh was never updated to match, so two assertions in assert_strix_workflow_pr_trigger_hardened() kept checking for the old 2-argument format('{0}-{1}', ...) literal and now fail on every PR regardless of that PR's own diff. The parallel Python contract in tests/test_required_workflow_queue_contract.py was already correctly updated for the 3-argument form at the time of PR #1779 -- only the bash side drifted, the same class of bug PR #1750 fixes for a stale cron assertion in this same file. Updated both stale assertions to the current 3-argument format('{0}-{1}-{2}', ...) literal, preserving their semantic intent and messages unchanged. Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4 Co-authored-by: Claude <noreply@anthropic.com>
…1630 (#1750) pr-review-merge-scheduler.yml's repository-local heartbeat was lengthened from cron: "*/30 * * * *" to cron: "30 * * * *" by #1630 to reduce Actions-capacity pressure during organization-wide queue saturation. tests/test_actions_queue_saturation_scheduler_cadence.py was updated to match at the time, but the parallel bash contract in scripts/ci/test_strix_quick_gate.sh was not, and kept asserting the literal old string -- a genuine, reproducible defect on protected main itself (confirmed failing on a fresh unmodified main clone before this change), not a symptom of any one PR being stale. Since exact-head-path-policy runs this trusted base-branch script against every PR's own exact head, this silently blocked an unbounded number of unrelated PRs across the whole .github queue until fixed at the root. Updates the one stale assertion to the current cron string and corrects an adjacent stale "15-minute organization sweep / 30-minute scheduled scan" description to the current hourly/hourly cadence. Verified: bash scripts/ci/test_strix_quick_gate.sh -- FAIL before this change on unmodified main, PASS after. Full suite: coverage run -m pytest tests -q -- all passed; coverage report --fail-under=100 -- 100% on scripts/ci/; interrogate -- 100%. Claude-Session: https://claude.ai/code/session_015Gs7KmNvH75nxz1sL8mKjw Co-authored-by: Claude <noreply@anthropic.com>
…retry (#1953) * fix(strix): name the sandbox bootstrap failure and give it a bounded retry When Strix's sandbox container comes up without its Caido proxy, Strix fails its fixed ten loginAsGuest attempts and exits; the gate then printed "STRIX_PROVIDER_UNAVAILABLE: contextual-orchestrator/orchestrator/free exhausted" -- blaming a component the run never called. The gate already recognises this class (is_caido_bootstrap_timing_error) and documents a same-model retry for it, but that retry draws on STRIX_TRANSIENT_RETRY_PER_MODEL, which is 0 in production because the gateway owns model failover, so it has never run. - STRIX_SANDBOX_BOOTSTRAP_RETRIES (default 1): once the per-model budget is spent, a sandbox-class failure may extend the attempt loop by one, up to this budget. The budget is charged in the same branch that grants the attempt: an adversarial verification pass (three independent lenses) showed that the first draft, which charged it in the retry-reason elif chain behind the gateway classes, let a log matching both the sandbox class and a rate-limit or connection class extend the loop on every iteration without charging, with nothing in production bounding it but GitHub's six-hour default. Gateway failures at per-model budget 0 still get no retry. - run_current_target_scan: for the sandbox class the verdict is "STRIX_PROVIDER_UNAVAILABLE: STRIX_SANDBOX_UNAVAILABLE: the last Strix attempt ended in the sandbox bootstrap (...) after N sandbox-specific same-model retries (budget B); this verdict names Strix's sandbox, not the LLM gateway." N is the observed count (SANDBOX_RETRIES_USED). The leading token is unchanged, so strix.yml's finding-free classification and its tests are untouched; the second token lets the review census split sandbox outages from gateway ones. Evidence: argos Strix run 34013128112 (2026-09-06): sidecar preflight ready 4 / deferred 4, then "Docker image ready", loginAsGuest failed after 10 attempts on 127.0.0.1:48080, Strix exit after 240 s, one attempt, the gateway verdict; a second artifact (9983313170) identical; two of the six most recent strix-reports artifacts are this class. Tests (tests/test_strix_caido_bootstrap_timing_retry.py, production functions extracted, run_strix_once stubbed with a self-capping stub): sandbox retry at per-model 0 (2 attempts), bounded (budget 2 -> 3, 0 -> 1), gateway retries not widened, mixed sandbox+rate-limit log stays bounded, sandbox budget on top of per-model (1+1 -> 3), verdict names the sandbox with the observed count, gateway verdict unchanged. Negative controls: three fail on main's gate; the mixed-log test fails on the first draft (runaway caught by the stub cap). Gate: 2927 passed, 1 skipped, coverage 100% (0 missed), interrogate 100%. Refs #1948, #1935. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(strix): report only sandbox retries that actually ran Lane peer 1's verification note on #1953: the reporting variable was set where the extra attempt is granted, but a granted attempt can still be vetoed by the timeout / transient checks that follow, so a log carrying both the sandbox and a timeout signature was charged, not retried, and reported as "after 1 sandbox-specific same-model retries". SANDBOX_RETRIES_USED is now assigned only when the retry really proceeds (just before the attempt counter advances); the budget charge stays in the grant branch, so the bound is unchanged. The constant's comment notes that a sandbox retry waits the same inter-attempt backoff as any other retry -- a pause between container attempts, not an inference deadline. Test: sandbox+timeout log -> 1 call, reported 0; plain sandbox log -> 2 calls, reported 1 (the harness echoes SANDBOX_RETRIES_USED). Module 14/14; negative control on main's gate 7 failed / 7 passed. Gate: 2928 passed, 1 skipped, coverage 100% (0 missed), interrogate 100%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ission (#1958) opencode-review-dispatch.yml carried its concurrency group only on the long opencode-review-target job. A job-level group is never evaluated while the whole run waits behind the organization job ceiling, so two dispatches for one pull request each queued for hours and each was allocated a runner before the older one could be discarded. Measured on 2026-09-06: of the five dispatch runs that passed validate-pr-metadata, four were then rejected by the privileged metadata check because the head had moved while they queued (34002473295, 34010256951, 34015973300, 34016922761), every one of them after coverage-source-tree and coverage-evidence had already run. The privileged check behaved correctly; the cost is that a runner slot is spent discovering that the review's subject no longer exists. Add the workflow-level group keyed by the dispatched pull request, matching codeql-scan-dispatch.yml's workflow-level group and the rationale recorded in strix.yml, noema-review.yml and opencode-review.yml. The job-level group stays. No behaviour changes between two runs that are both executing -- the job-level group already cancels there; what changes is that a superseded run is now cancelled while queued. Co-authored-by: Seongho Bae <seongho.iopsy@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…of banning them (#1957) #1949's account rule sets aside an account's remaining candidates after two consecutive 429s. When a walk runs out of candidates it is willing to probe it ENDS -- with probe budget in hand and the readiness target unmet -- and the stage fails closed; because deferral needs one ready route (#1947), nothing is served either. Sixteen sidecar artifacts were collected on 2026-09-06 across .github, argos, bandscope and naruon; fourteen ran the merged rule (argos 34013128112 and bandscope 34013146167 still carry the pre-#1949 report shape). Those fourteen fall into three classes, not two: eight boots at probed/skipped/ready 16/4/5-6 spend the whole budget in the first pass and are unchanged by this commit; ONE (argos 34014143870, 06:56Z) reads 12/12/3 -- it served, yet exhausted its candidates under target with four probes unspent; five read 6/18/0 and failed closed. The sixth ready route in the healthy class (llama-3.2-11b on the second NVIDIA key, catalog position 17, ready in exactly those eight artifacts) is reached only because four OpenRouter probes were set aside -- the rule's designed benefit, which this commit keeps. .github run 34016207820's six probes were refused 429 between 07:49:35.111Z and 07:49:35.767Z; because the walk round-robins three accounts, "two consecutive 429s" on one account is two requests about 310 ms apart (nvidia_nim at .111 and .422). keyverse#143's 08:20Z noema repeated the shape in a second repository. A refusal is not a verdict on the account: run 34016093772 was inside its own preflight during that burst and its llama-3.2-11b probes on the same two NVIDIA keys answered ready at 07:50:58.7Z and 07:50:59.0Z, 84 s after those keys refused. Not claimed: that the ten unspent probes would have found a ready route inside the burst. No artifact answers it, which is why this also records retry_after_s. The change rests on the structural defect alone. A set-aside candidate is now postponed to the end of the walk; once the first pass ends under target with budget left, the postponed candidates are probed in catalog order until the sixteen-probe budget is spent. Both passes share one stop condition, so probes per stage stay <= 16, and exhaustion uses a dedicated sentinel so a None candidate cannot truncate the walk. The second pass never draws on the shared escalation budget (#1458): a postponed candidate answering "budget too small" is rejected as escalation_reserved_for_first_pass, because otherwise candidates the previous design never probed take escalations from the priced stage that had them, and a measured two-stage run stops serving a route it used to serve. _safe_retry_after_seconds records a refused probe's Retry-After as retry_after_s when it is whole delta-seconds in range. It gates on isdecimal, not isdigit: the header is provider-controlled, "²".isdigit() is True while int() on it raises, and this runs inside the probe walk's exception handler whose callers catch only ReviewPreflightError -- so a ValueError there would kill the boot before any evidence file is written. No code waits on the value (ADR-0003). Cost, stated in the ADR and PR body against the 60-job ceiling work: about 120 ms per refused probe, up to 10 x 90 s ~= 15 minutes when the postponed tail is silent (gemma-4-31b answered TimeoutError in 15 of the 19 probes that reached it), and 8 -> 24 requests on the two-stage auto path, where the priced stage doubles from 4 probes to 8. All inside the probe budget ADR-0029 bounds. Report: postponed_probed_count added, skipped_count now means "postponed and never reached". ADR-0029 amended, and its two superseded sentences marked in place. Verified by a three-lens adversarial refutation before push (control flow, evidence and design, test fidelity): all three returned refuted=true with 20 findings, each reproduced against the artifacts before acting. The blocker above, the escalation-budget regression, the miscounted evidence table, the false "healthy-minute walk is unchanged" claim, the 310 ms spacing, the sibling run's real relationship to the burst and the superseded ADR sentences all come from that pass. Gate on this tree: 2945 passed, 1 skipped, 21 subtests; coverage 100% (0 missed); interrogate 100%. Negative control on origin/main's launcher with this test file: 8 failed, 93 passed. Refs #1948, #1949. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…#1959) A completed scan (run.json completed, SARIF 0 results, attempt exit 0) was failed closed as STRIX_PROVIDER_UNAVAILABLE on .github#1689 run 34013778497 because three `strix.core.execution: transient model/provider error for <agent>; replaying turn (attempt n/m, backoff Ns): …` WARNING lines survived sanitize_known_strix_report_warnings and tripped the report WARNING scan. strix-agent 1.5.3 emits that line only inside its bounded transient-retry branch (strix/core/execution.py:763), immediately before the replay runs; an exhausted retry logs `agent run failed for …; marking failed` at ERROR with a traceback and exits non-zero, and both of those still fail the gate. Two tests cover the production argument shape, where the reports root is passed and has_strix_report_failure_signal narrows to the newest run directory via latest_strix_report_dir, so the sanitized tree and the scanned tree are demonstrably the same one. The CHANGELOG records one side effect: a provider 503 body that appears only inside a retry line's exception repr is removed with that line, which can make the report-only branch of is_model_retryable_error read an outage as non-retryable. The direction is fail-closed and the contextual-orchestrator verdict branch answers first, so no path changes outcome today. Coupled to the strix-agent 1.5.3 execution.py:763 message format, like the two existing alternatives — re-verify on every strix-agent bump. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…#1960) `opencode-review-dispatch.yml`'s `emit_strix_provider_failure_finding` rendered one fixed finding for every `STRIX_PROVIDER_UNAVAILABLE` line, whose Root cause read "The contextual-orchestrator gateway or its discovered provider pool was unavailable for this run". #1953 had just given the Strix sandbox bootstrap failure its own second verdict token, `STRIX_SANDBOX_UNAVAILABLE`, precisely because that attribution is wrong for it: the sandbox container never reaches its Caido proxy, so the run dies before the gateway serves anything. This consumer re-applied the wrong attribution one step downstream, into the review findings and the failure census -- the misattribution #1953 fixed in the gate, surviving in the reader. The emitter now branches on the second token. A sandbox verdict gets a finding that names Strix's sandbox, states that the verdict does not name the gateway, and tells the reader not to change gateway or provider configuration on its strength. A `STRIX_PROVIDER_UNAVAILABLE` line without the token keeps its existing four lines verbatim, so the gateway class has no regression surface. No test covered this finding text at all before ("gateway or its discovered provider pool" and "provider availability blocked" both matched nothing under tests/). tests/test_opencode_dispatch_strix_sandbox_finding.py runs the production emitter, extracted from the published run block with the existing _extract_run_block harness, and pins three directions: the sandbox token, a gateway failure without it, and evidence carrying no provider-unavailable signal at all. Editing the workflow moves its blob, so REVIEW_DISPATCH_BLOB_SHA in tests/test_pr_review_autofix_nvidia_nim_contract.py is recomputed to 694c04b with git hash-object (lane peer 1 flagged this pin in advance). Gate on this tree: 2931 passed, 1 skipped, 21 subtests; coverage 100% (0 missed); interrogate 100%. Negative control on origin/main's workflow with this test file: 1 failed, 2 passed -- only the sandbox direction fails there. Refs #1953, #1935. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
) The contract asserted that expressions appear in the concurrency block, which the block's own documentation satisfies while the key says something else. Slice to the group's value with comments stripped so the assertion tests the key. Author: separate session. Verified independently: mutant controls 7/7 caught on the branch, 7/7 missed on main, adversarial helper inputs leak no comments. Merger verification (this session): head matched the verified SHA exactly, 0 behind main, merge tree identical to the branch tree, tests-only (2 files, no path outside tests/). Own discriminating control -- collapse the group key to the repository alone while moving the expressions into the comment beside it: main 58 passed (misses it), branch 1 failed (catches it). Full gate on the merge tree: 2958 passed, 1 skipped, coverage 100%, interrogate 100%. Merged under the standing chicken-and-egg authorization: the required contexts CodeQL compatibility analysis (actions)/(python) cannot be produced for a code-touching pull request in this repository -- codeql-scan-dispatch.yml has never succeeded (0 of 1931) because its actor allowlist admits no identity that dispatches it. See #1929. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#1964) Move the concurrency group from job level to workflow level in both agent-mention dispatch workflows so a superseded mention is coalesced while it is still queued, instead of holding its queue slot until a runner frees up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ck (#1975) The helper sliced from `permissions:`, so it raised IndexError on the two workflows that declare permissions first, and it returned a folded key's raw newlines rather than the value YAML produces. Nine of twenty-nine workflow-level keys are folded, including every required review workflow. Author and verifier were separate sessions. Merger verification (this session, independent runs): head matched GitHub exactly, 0 behind main, merge tree identical to the branch tree, tests-only. Exact-match against a yaml oracle across every workflow: 29 match, 0 mismatch, 0 exception. Seven adversarial inputs pass, three of them designed here rather than reused -- a comment quoting `group:` before the real key, a job-level concurrency block appearing first in the file, and literal `|`/`|-` scalars, which are refused rather than silently folded into a value YAML never produces. Two-way control on the live hole: flipping noema-review.yml's cancel-in-progress to false behind a comment passes on main (2961) and fails here. pr-review-autofix.yml's deliberate cancel-in-progress: false is preserved. Gate: 2964 passed, 1 skipped, coverage 100%, interrogate 100%. Bypass basis stated plainly: this change is tests-only and does not itself unblock anything, so it does not meet the narrower 'the PR's own diff edits review-pipeline files' reading recorded in docs/product-technical-gap-baseline.md. It is merged under this session's standing instruction to fix the queue, which names verifying `cancel-in-progress: true` as part of that work. The required CodeQL contexts remain unreachable for any code-touching PR here (#1929). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ot materialize (#1973) Materializing the PR merge tree is a precondition of coverage-source-tree, so a conflicting head can only produce a failed dispatch. The guard returns before review_dispatch_admitted, preserving the bounded admission budget for a PR a review could actually finish. UNKNOWN is deliberately not blocked. Authored, verified and merged by three separate sessions. Merger verification, my own runs: head matched GitHub, 0 behind main, merge tree identical to the branch tree. Against current main the diff is 2 files, 206 insertions, 0 deletions; the production change is 29 added lines and nothing removed. The guard sits at line 3691 and review_dispatch_admitted at 3706, so the budget is preserved; 6 call sites handle the new return value. Negative control, removing only the 15 guard lines: exactly 2 tests fail -- test_review_dispatch_skips_a_head_whose_merge_tree_cannot_materialize and test_review_dispatch_reads_the_rest_merge_state_not_only_graphql -- asserting merge_conflict against a received dispatched. Gate: 2968 passed, 1 skipped, coverage 100% (13196 statements, 0 missed), interrogate 100%. The cited measurement was corrected before merge. It read '20 dispatches across 80.5 hours'; two sessions independently recounted .github#1529 as 27 dispatches across 100.8 hours with zero successes (20 cancelled, 7 failed). The original figure came from a run window that silently truncated before the pull request existed. The comment now carries the corrected numbers and the window. Authorization is this session's standing instruction to clear the queue, which lives outside this repository's text -- as docs/product-technical-gap-baseline.md itself records after a 2026-09-01 correction, and that file is annotated '(not merge authorization)' at every entry point. Corroborating rather than authorizing: this PR's own diff edits scripts/ci/ review-pipeline code, so it cannot validate itself across the pull_request_target trust boundary, which is the conservative condition that document records one earlier pass imposing on itself. On this head all 12 required contexts are unsatisfiable: 9 never reported, 3 queued, 0 passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…fails (#1979) strix.yml's workflow-level cancel-in-progress was guarded by nothing: its own test asserted the string as a substring, and the list in test_required_pull_request_workflows_cancel_superseded_runs did not include it. Authored and verified by separate sessions. Merger verification, my own runs: head agreed across three paths (local ref, ls-remote, PR head) after the author hit a push/PR-creation mismatch on this branch; 0 behind main; merge tree identical to the branch tree; 13 files, all under tests/. Assertion forms: 24 helper calls, 4 line-anchored regexes, 19 assertions replaced. Two-way control, comment out strix.yml's flag and set it false: main reports 2968 passed and does not catch it; this branch fails 2 tests. Gate on the branch tree: 2968 passed, 1 skipped, coverage 100% (13196 statements, 0 missed), interrogate 100%. Four workflows are deliberately left uncontracted because no test states their intended value, and writing one would invent policy. One of them, scheduled-security-scan.yml, does produce the required context 'Detect CodeQL languages' -- which strengthens rather than weakens that choice: its cancel-in-progress: true with a github.ref-shared group is what starved it to 228 cancellations and 0 completions over 2026-09-01..09-05, so the correct value is an open policy question on #1800, not a contract to fix here. Authorization is this session's standing instruction to clear the queue, which lives outside this repository's text. Corroborating, not authorizing: on this head none of the 12 required contexts can pass -- the CodeQL pair is unreachable while codeql-scan-dispatch has never succeeded (#1929). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…an workflows (#1980) python-security.yml and sast-semgrep.yml had only the presence of cancel-in-progress asserted, so flipping it to false passed the whole suite. This pins the value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…review runs (#1983) `active_review_run_refs` matched a workflow run's `name` exactly against the review workflow aliases. But eight workflows in this repository define `run-name:`, and that set contains every workflow whose runs this matcher looks for -- `opencode-review.yml` ("Required OpenCode Review"), `opencode-review-dispatch.yml` ("OpenCode Review Dispatch") and `strix.yml` ("Strix Security Scan"). For such a workflow GitHub reports the *rendered* run name in `name` -- the same string as `display_title`, e.g. OpenCode Review Dispatch #834e748ee6... Sampled 2026-09-07: 100 of 100 opencode-review-dispatch runs carry that form and none carries the bare workflow name. So the exact match dropped every production dispatch run at this line, before the `event == "repository_dispatch"` branch immediately below that exists to read them. Two consequences: * `already_running` never suppressed a same-head repeat. .github#1529 took 27 dispatches on one unchanged head over 100.8 hours; each new run's creation preceded the previous run's cancellation by about three seconds, so the previous run was demonstrably still active when the check ran and did not see it. * `stale` never populated, so older-head central runs were never cancelled. A first count of the live queue said "20 duplicates of 45 active runs" and was wrong: it grouped by repository and PR without the workflow, so runs of different dispatch workflows on one PR were counted as duplicates of each other. Regrouped by (workflow, repository, PR): active repository_dispatch runs, queued + in_progress 33 codeql-scan-dispatch.yml 25 runs / 12 keys / 13 same-head duplicates opencode-review-dispatch.yml 5 runs / 5 keys / 0 duplicates pr-review-autofix.yml 3 runs / 3 keys / 0 duplicates So the workflows this matcher governs show no live duplication at this instant. The harm this fix addresses is the historical chain on .github#1529 and a suppression that has never once fired, not a backlog visible right now. The 13 duplicates all belong to CodeQL Scan Dispatch, which this matcher does not govern; that workflow also defines `run-name:`, which makes it a separate lead rather than evidence for this change. Reviving stale cancellation is separately safe: of 163 non-terminal central runs, 49 are review or dispatch kind and 4 become cancellable, all of them subjects that no longer exist (3 closed or merged PRs, 1 moved head). The fix is confined to the run comparison. `OPENCODE_WORKFLOW_NAMES` is unchanged, because its other consumer compares a *workflow* object's name, which is genuinely bare. `active_review_run_refs` has exactly two call sites, OpenCode's and Strix's, so both are fixed here; the Strix side is pinned by its own test so a later narrowing to the OpenCode aliases cannot silently reopen half of it. This is one instance of a class, and the file already contains the stable form. `run.name` is compared as an identifier at four places -- `:1250`, `:3198`, `:3254` (this one) and `:3783` -- while `:3060` keys on `run.get("workflow_id") or run.get("path") or run.get("name")`, which cannot be rewritten by a `run-name:`. `:1250` in particular feeds the REST fallback's workflow-level policy boundary and would see a rendered title where it expects a workflow name. Fixing the whole class means moving the callers from display names to paths, which also touches how `dispatch_title_prefixes` is built, so it is deliberately left out of this change; .github#1941 is the same root seen from the `display_title` side. Recorded here so the next reader does not rediscover it as a fifth instance. Note the new behaviour this enables: while a same-head central run is active, a repeat is now suppressed. A run that never terminates would therefore hold the PR, where before the check simply never fired. The existing fixture sets a bare `name` beside a rendered `display_title`, a payload GitHub never emits for a `run-name:` workflow, which is why 100 percent line coverage of that branch never revealed that production could not reach it. Developer experience: the scheduler's same-head suppression and stale-run cancellation work against real payloads instead of a shape only the tests produce. User experience: a pull request stops accumulating duplicate concurrent review runs that cancel each other, so a review that starts can finish. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ree cannot materialize (#1973)" (#1985) This reverts commit ad0779b. I wrote that guard and its justification is false. It blocked every OpenCode dispatch on a DIRTY/CONFLICTING head on the grounds that "a conflicting head can only produce a failed dispatch". The run object does conclude failure, but the review is published anyway: the reviewer reads the pull request diff, not a merge tree -- "Coverage is a separate gate", in the review's own words -- and only `coverage-source-tree` needs the merge commit. Measured on .github#1529, the PR that motivated the guard: the last of its 27 dispatches published a 2404-character review at 2026-09-05T19:40:58Z. That review body carries its own run id, 33969161561, which concluded failure at the receipt gate nine seconds later. It is the only OpenCode review that head has (4 reviews total, 1 by opencode-agent[bot]) and it is why the PR now reads as reviewed. The guard would have discarded it. The pattern is not unique to #1529: .github#1555 is CONFLICTING right now and its current head carries a 2019-character CHANGES_REQUESTED review from the same reviewer. I reached "produced nothing" by reading run conclusions, which are roll-ups that cannot name what a run did. The cost the guard claimed to protect is also wrong by two orders of magnitude. Across all 27 dispatches of that head, 109 jobs: 27 were allocated a runner for 0.24 h in total, 82 never were, and the rest of the elapsed time was queue residency. Blocking the whole chain would have saved fourteen minutes of runner time. The repeat itself is a real problem, and it is fixed at its cause rather than here. `active_review_run_refs` matched a run's `name` exactly against the review workflow aliases, but the central review workflows define `run-name:`, so GitHub sends the rendered title in that field and every dispatch run was filtered out before the check could see it -- `already_running` never fired. With that repaired, a conflicting head receives one dispatch and the next is suppressed while it runs, which is the outcome this guard was reaching for without discarding the review. Developer experience: the scheduler no longer prints a skip reason that asserts an outcome contradicted by the runs it cites. User experience: a conflicting pull request receives a review and repair guidance, instead of repair guidance alone. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…etector (#1987) Two unrelated audits share one job in audit-central-ruleset.yml, and the ruleset step runs first. It has exited 1 since at least 2026-09-04 on owner-configured governance drift: ERROR: exactly two approving reviews are not required ERROR: last-push approval protection is disabled FAIL: ruleset 18156473 has 2 governance drift reason(s) Live values on ruleset 18156473 today are `required_approving_review_count: 1` and `require_last_push_approval: false`, against the 2 and true the audit asserts. Both are owner-configured settings, and this change does not touch either them or the audit's expectations: with every session sharing one GitHub identity and unable to approve another's pull request, a two-approval requirement may well have been relaxed deliberately, in which case the stale side is the assertion rather than the configuration. Deciding that is an owner call. What is not an owner call is the collateral damage. Because the failure exits a shared job, the two steps below it never ran: the CodeQL coverage detector and the backlog-38 bootstrap that opens CodeQL setup pull requests. So the detector that would have reported a coverage gap has been dead for days, and its workflow was red the whole time for an unrelated reason -- red status, wrong subject, and no signal about coverage either way. The coverage step now carries `if: always()`. It builds its own repository list into its own temp file and the step above exports nothing to GITHUB_ENV or GITHUB_OUTPUT, so it has no data dependency to lose; the job still fails overall. The bootstrap step deliberately does not get the same guard, because it opens pull requests and running a mutation after an unexplained upstream failure is a different decision from running a read-only detector. A contract test pins both halves. The detector also needed a correction of its own. It accepted `default_setup_state == "configured"` as coverage, but a repository can report `configured` with an **empty** `languages` list, which scans nothing. Measured 2026-09-07: life-os, aFIPC and inkspan all report that shape, and life-os has zero CodeQL analyses of any language while codeql-pr.yml still runs on every pull request head. The control holds in both directions -- html4tree, naruon and wardnet have non-empty language lists and do have `dynamic/` analyses for exactly those languages. The audit workflow now collects `languages` alongside `state`, the predicate requires a non-empty list, and a payload missing the new key fails closed rather than falling back to the state alone. Gaps of the two kinds are reported as different sentences, because they need different fixes: enable languages on an existing setup, versus set coverage up at all. A reviewer then asked whether the newly-unblocked step could run and audit zero repositories while still passing, and one layer below the fix it could: $ echo '[]' | python3 scripts/ci/audit_org_codeql_coverage.py PASS: all 0 repositories have real CodeQL coverage exit 0 The calling workflow already refuses that -- its sentinel check requires known private repositories to appear in the enumeration, which an empty list fails -- but the script is directly runnable against a JSON path or stdin, so the guard did not cover every entry point. `main` now refuses an empty payload. This is the same vacuous-pass shape as the `configured`-with-no-languages case above, one level down, which is where it was found: a pass that examines nothing is not a pass. The step's independence from the failing step above it is established mechanically rather than by reading the YAML. Steps can only share state through `GITHUB_ENV`, `GITHUB_OUTPUT`, `GITHUB_PATH` or files, since each `run:` is a separate shell. The ruleset step uses none of those channels, and the two steps' `$RUNNER_TEMP` paths are disjoint: `central-required-workflow-*`, `ruleset-probe-*` and `stacked-opencode-ruleset.*` against `codeql-coverage-*`, `codeql-analysis-*` and `codeql-default-setup-*`. Developer experience: a governance-drift failure no longer hides an unrelated security-coverage result, and the audit says which of the two problems a repository has. User experience: a repository whose CodeQL setup is switched on but scanning nothing is reported as uncovered instead of counted as covered. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…1989) #1987 taught the CodeQL coverage audit to refuse an empty payload, because "PASS: all 0 repositories have real CodeQL coverage" reads as success over a run that examined nothing. Reviewing that change, host 2 fed it a payload of a single *archived* repository: PASS: all 1 repositories have real CodeQL coverage exit 0 Non-empty, so it clears the new guard, and archived repositories are then legitimately skipped by the coverage loop. The subject set is empty again, by a different route -- the same defect the guard was added to close, one input shape sideways from the one it checks. The count that matters is what the audit examined. `auditable_repositories()` is now the single place that decides which repositories are in scope, shared by the loop and by the guard, so the two cannot drift apart when the archived rule changes. The guard refuses an empty examined set however it became empty, and the PASS line reports the examined count rather than the supplied one, so an organization of nothing but archived repositories can no longer be reported as fully covered. Developer experience: the pass line's number is the number of repositories the run actually checked. User experience: an audit that examined nothing says so instead of passing. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
1 similar comment
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
|
|
||
| def test_redact_scalar_json(): | ||
| """Verify that scalar JSON values are parsed but fall through to unstructured redaction.""" | ||
| source = '"token=secret123456789"' |
|
|
||
| def test_redact_literal_prefix_collision(): | ||
| """Verify that a plain-text line starting with 't' (but not 'true') is safely handled.""" | ||
| source = 'token=secret123456789' |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
이 PR의 head가
|
|
OWNER-PATH RETIREMENT — fresh state immediately before this action:
This reverse PR has no unique owner source: its head is the protected default branch. Leaving it open re-materializes PR workflows whenever Retiring this reverse PR is therefore the smallest causal queue repair. Future main adoption for #1725 must happen directly on the owner branch as an ordinary non-force two-parent commit after a fresh ref check, followed by fresh exact-head evidence. No predecessor checks transfer, and closure is not acceptance of #1725 or of the dependency-review gate. |
정정: 재발화 기전은 반증됐고, 비율도 취소합니다앞선 코멘트에서 제가 두 가지를 적었습니다. 하나는 반증됐고, 하나는 정밀도가 없었습니다. 1. "main 전진마다 재발화" — 반증됨다른 세션이 측정했고 제 추론과 반대입니다. main이 움직이지 않았고 head도 하나인데 실행은 계속 생성됩니다. 앞선 코멘트에 "구조적 귀결이고 재발화 사건을 직접 세지는 않았다"고 범위를 적어 두었는데, 그 미확정 부분이 측정으로 반증된 것입니다. 실행을 만드는 것은 2. 큐 기여 비율 — 정밀도 취소제가 47/177(27%)을 적었고 다른 세션은 69/170(41%)을 적었는데, 두 값이 다른 이유가 측정 방법에 있습니다. 같은 질의를 반복하면 178과 101이 나옵니다. 그러므로 이 PR의 큐 점유 비율은 인용하지 않습니다. 창을 3. 남는 사실과 그 의미건수는 크지만 회수할 러너 시간이 없습니다. 따라서 앞선 코멘트가 이 PR을 용량 문제로 읽히게 했다면 그 부분을 취소합니다. 이 PR은 큐 건수의 상당 부분을 차지하지만 그것이 다른 작업을 밀어내고 있다는 근거는 없습니다. 유효하게 남는 것head가 관련해서 🤖 Generated with Claude Code |
Purpose
Non-force restack of the canonical Dependency Review owner branch for #1725.
main@c9052e607e5f3cc76e73207e7786b21500721b79.c2e8ab0e535245f8f53801ad6a11e107fe492341.Merge with a merge commit only after terminal exact-head evidence. No squash, rebase, force-push, administrator bypass, or predecessor-evidence transfer. After merge, #1725 must earn fresh successor-head checks and remain Draft until current review/evidence is clean.