chore: refresh org SBOM inventory - #1678
opencode-agent[bot] wants to merge 324 commits into
Conversation
There was a problem hiding this comment.
Note
This report is out of date. Scroll down for Devin Review's latest report on this PR.
Devin Review found 3 potential issues.
⚠️ 3 issues in files not directly in the diff
⚠️ Partial inventory appears complete
With 34 of 63 SBOM fetches returning 404, repo_count reports all 63 while component totals cover only 29. The inventory presents partial organization evidence as complete.
⚠️ Permitted licenses trigger violations
For MPL-2.0 components without another prohibited license, flagged reports at least 137 policy violations. Governance therefore escalates expressly permitted dependencies.
⚠️ Permissive alternatives trigger violations
When an SPDX expression offers a permissive alternative, flagged rejects it because another alternative is copyleft. Seven selectable permissive dependencies become policy violations.
There was a problem hiding this comment.
Devin Review found 1 new potential issue.
⚠️ 1 issue in files not directly in the diff
⚠️ Restricted licenses escape policy review
Seven LicenseRef-NVIDIA-Proprietary components are marked unflagged. Unknown and non-standard licenses also disappear from the commercial-permissive policy review.
|
Diagnosis of the
This is deterministic and content-blind, not an Nginx finding. The regenerated Fix: #1946 makes the policy follow the Contents response's blob sha to the Git Blobs API (bounded at 11 MiB, bound back to the same sha and size) and scan the bytes like any inline file. Once it is on Generated by Claude Code |
…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>
… from per-commit evidence retention Review corrections on ea0a429: the 14:27Z measurement supports five runs holding runner slots and four waiting in the queue, not nine slots; and a newer push scan covers the current tree in full without being a record of every earlier commit's findings — code that entered and left between two heads, or findings a retired run never uploaded, are absent, and a per-commit retention guarantee would need its own contract. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
…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>
…on and slot wording The workflow comment still said the newest head 'subsumes every older one' and that a cancelled push scan 'loses no evidence', which the same PR's doctoring amendment and contract-test docstring no longer claim. It also counted all nine outstanding runs as holding slots. Both now read the same way: a complete scan of the current tree rather than a per-commit record, and five holding runner slots with four queued. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
…ancelling concurrency The header still said Strix runs 'intentionally do not cancel in progress' and that GitHub keeps one active and one pending run per group, describing cancel-in-progress: false. The workflow has run cancel-in-progress: true with a repository+PR group for some time, and this PR extends that to push events per protected branch. The header now states the real grouping (repo+PR for native and dispatch PR runs, push-<ref> for pushes, unique run id for schedule and PR-less dispatch) and that a cancellation is a supersede signal rather than passing evidence, with the merge scheduler still requiring exact-head evidence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
Measures run counts, cancelled/superseded share, and run-level queue/ duration percentiles per (repo, workflow, trigger) group across all 79 non-archived org repos in the last 24h, per the ci-centralization task. The baseline's live signature (single-digit in_progress vs 200+ queued on fast-mlsirm/.github, and fast-mlsirm PR #1882-#1884 check suites stuck QUEUED while #1885-#1886 succeeded) reproduces the org-wide concurrent-job ceiling already documented in docs/doctoring/actions-plan-concurrency-ceiling-20260903.md, not a workflow-file duplication problem. ADR 0030 scopes what workflow centralization can and can't do given that constraint, and records that the one low-risk fix this task's Step 3 proposed (add missing concurrency: blocks to required workflows) was already done org-wide before this session started. No workflow behavior changes in this PR.
…260916-and-scoping-adr docs: CI queue-stall baseline (24h) + scoping ADR
…able publish_fallback_diff_review() published its product-diff review as a bare COMMENT whenever the model pool was unavailable and coverage evidence had failed. COMMENT is not in opencode_review_receipt_gate.FORMAL_STATES, so the required workflow's "Fail closed without a current-head OpenCode verdict" job never finds a receipt, opencode-review-dispatch.yml's rerun step (gated on that receipt) never fires, and the required opencode-review check fails closed permanently -- confirmed live on fast-mlsirm PR #1887 (run 34952034128): opencode-agent posted this exact fallback body as COMMENTED, and no rerun was ever attempted. request_changes_for_coverage_evidence_failure() only touches the status-overview comment, never a PR review -- previously true by design per #47 (a coverage-only body must not replace the product-diff review), pinned by test_publisher_workflow_cannot_replace_review_with_coverage_finding. That constraint still holds: this change does not touch that function or add a body to it. It fixes the other half -- the fallback path must still resolve to a formal, receipt-eligible state -- by publishing the already-built, PRODUCT_MARKERS-carrying fallback body as REQUEST_CHANGES instead of COMMENT, matching the tested-but-unwired intent of review_event_when_coverage_blocks() and the surrounding comment's own description ("so a miss never looks finished; next action stays 'fix coverage evidence, then rerun'"). Adds a regression test asserting the fallback event is REQUEST_CHANGES, and an integration test proving the exact fallback body is accepted by opencode_review_receipt_gate.evaluate_receipts() once given that event. Updates the REVIEW_DISPATCH_BLOB_SHA pin in test_pr_review_autofix_nvidia_nim_contract.py to match the new blob. Fixes ContextualWisdomLab/fast-mlsirm#1907 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KqyszG8JYY8Y2xPdb3b8sM
…overage-fallback-request-changes fix(opencode-review): coverage-failure fallback must publish a formal receipt, not a bare COMMENT
Root-caused the dominant opencode-review fallback cause on fast-mlsirm: of the last 10 fallback reviews (PRs #1868-#1892), 9 carried "Coverage gate: failure" and every one of those 9 traced to the same systemic sandbox limitation, not a real regression in the reviewed PR. 8/9 failed Python test collection with `ImportError: cannot import name '_core' from partially initialized module 'fast_mlsirm'` (410-411 errors), because fast-mlsirm is a maturin/PyO3 project and nothing in the generic Python coverage path (safe_pytest_command.py) builds the compiled extension before running pytest. 1/9 failed `cargo llvm-cov` directly with `Could not resolve host: index.crates.io`, because the coverage-measurement container runs `docker run --network=none` and Rust/Cargo had no offline dependency path -- unlike Python (materialize_base_python_requirements.py) and JavaScript (materialize_base_javascript_packages.py), which already vendor their base-pinned dependency closures on the runner, before the network-isolated container exists. Adds materialize_base_rust_dependencies.py, mirroring that same trust model: it reads only the validated base commit's Cargo manifests (never the pull request's), runs `cargo vendor` while the runner still has network, and writes a `[source.crates-io] replace-with` config plus the vendored crates for the Docker build to bake in. Wires it into opencode-review-dispatch.yml: materialized alongside the existing Python/JS steps, COPY'd into the trusted image, and copied into the sandboxed CARGO_HOME right before any cargo/pytest command runs. This directly fixes the `cargo llvm-cov` network-resolution failure (the 1/9 case). It also lays the required infrastructure for the dominant 8/9 `_core` ImportError case, but does not complete that fix: fast-mlsirm's `pyproject.toml` needs `maturin` to build offline, and `maturin` is not in requirements-opencode-review-ci-hashes.txt. Regenerating that hash-pinned lock (`uv pip compile --upgrade --generate-hashes ...`) changes the resolved/hashed dependency set of the entire org's OpenCode review toolchain image, not just fast-mlsirm's; that is deliberately left as follow-up work for a session that can validate the regenerated lock end-to-end, rather than landing it unverified in this PR. Regression tests: 23 new tests for the materializer (100% line+branch coverage, 100% docstring coverage), including one that proves a locked base dependency vendors and builds fully offline (`cargo build --offline` against only the vendored config), and one that proves a PR-added dependency absent from the base lock is never vendored. Updates the REVIEW_DISPATCH_BLOB_SHA pin in test_pr_review_autofix_nvidia_nim_contract.py to match the new workflow blob. Full local suite: 3134 passed, 3 skipped; 100% coverage and 100% docstrings on scripts/ci (two pre-existing, unrelated gaps in noema_review_document.py/noema_review_gate.py are not touched by this change). Refs ContextualWisdomLab/fast-mlsirm#1907 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013tbpBhMXjXEUcz5oWZFKTH
…-fallback-cause fix(opencode-review): vendor Cargo deps offline for the coverage sandbox
…verage Adds maturin==1.15.0 to the hash-pinned opencode-review CI lock (hashes verified against PyPI JSON metadata) and teaches run_python_test_coverage's new build_maturin_extension_if_needed helper to run `maturin build --offline --release` with CARGO_NET_OFFLINE=true and CARGO_BUILD_JOBS=1 against the vendored base-commit Cargo dependencies before pytest, for every tracked Python project whose pyproject.toml declares build-backend = "maturin". Closes the gap materialize_base_rust_dependencies.py (#2222, #2223) left open: the base Cargo dependency graph was vendored for `cargo llvm-cov`, but nothing ever built the PyO3 extension itself, so `coverage run -m pytest` kept failing collection with `ImportError: cannot import name '_core'` on 8 of the last 10 fast-mlsirm fallbacks. tests/test_maturin_offline_build_contract.py proves both halves against a real PyO3 fixture crate: the vendored-offline build produces an importable `_core` extension, and a dependency only a pull request's head added (never seen by the base-commit materializer) is never fetched -- the offline build fails closed on the missing crate. Refs ContextualWisdomLab/fast-mlsirm#1907 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FpnJ9UuQwdTUfhzmqueXCz
…-maturin-lock fix(opencode-review): build PyO3/maturin extensions offline before coverage
…job queue wait Measured job-level timestamps on two completed opencode-review-dispatch.yml runs: under saturation, actual job execution is ~21 minutes of a ~14-hour run (2.5%) -- the rest is queue wait between needs:-chained job boundaries re-entering the org's global runner queue. The identical job chain under lighter load completes in under 15 minutes. Rules out model latency, polling, and duplicated builds as the dominant cause; confirms job-folding is blocked by an existing, deliberate trust boundary between coverage-evidence (untrusted code, actions:read only) and the jobs that hold write/app tokens. Also checked bandscope's 89% cancellation rate: cancellation fires within 1-3s of push, before any runner admission, so it is not wasting runner-seconds. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015gdR6WuShb6HH6niXc1wkj
…capacity-root-cause-20260917 docs(doctoring): root-cause the multi-hour review durations as inter-job queue wait
…eue-wait hop docs/doctoring/actions-capacity-root-cause-20260917.md measured that this workflow's multi-hour durations are ~97.5% inter-job global runner-queue wait, compounding once per needs: edge under org-wide saturation. Of its 3 job boundaries, one (validate-pr-metadata -> coverage-source-tree) sits entirely within the same trust level: both jobs only ever exchange the OpenCode app token for READ-scoped data (target-repository metadata, then the PR merge tree) via id-token: write, and neither executes untrusted PR-head content or holds a write-capable token. Folding them into one job removes that queue-wait hop without touching the trust boundary that must stay separate: coverage-evidence (untrusted test/build execution, actions: read only) stays isolated from both the merged read-token job and opencode-review-target's write-capable publication job. 4 jobs -> 3; 3 needs:-chained queue-wait boundaries -> 2. The remaining two (into coverage-evidence, into opencode-review-target) cross a real trust boundary and cannot be folded further without letting untrusted PR-head code run in a process that holds or recently held elevated tokens. Also re-pins REVIEW_DISPATCH_BLOB_SHA in test_pr_review_autofix_nvidia_nim_contract.py to this workflow's new blob hash -- that test deliberately pins the reviewed dispatch workflow byte-for-byte so a change like this one gets a conscious re-pin rather than passing silently. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015gdR6WuShb6HH6niXc1wkj
…capacity-job-fold-20260917 fix(opencode-review-dispatch): fold same-trust-level jobs to cut a queue-wait hop
…image-only diffs docs/doctoring/required-workflow-path-filter-boundary.md already established that trigger-level path filters are inert for org-required-workflow runs in sibling repos, and built a job-level changed-scope gate (fails open) as the fix -- already applied to strix.yml, security-scan.yml, sast-semgrep.yml, and codeql-pr.yml. opencode-review.yml and noema-review.yml, the two most expensive review workflows (p50 8.4h / p50 5.3h per docs/ci-baseline-20260916.md), never got the same gate: every PR event, including a docs-only or changelog-only diff with zero reviewable code, dispatched the full multi-hour chain. This is a demand-side fix -- fewer dispatches -- distinct from #2228's supply-side (queue-wait) fix. opencode-review.yml's copy of the classifier drops its retry loop (a single gh api attempt, falling through to the existing "scan everything" fallback on failure) because this file is contract-tested to never retry or poll (tests/test_opencode_required_verdict_regression.py); every other copy of this classifier keeps the 3-attempt retry since only this one file carries that constraint. noema-review.yml reuses its already-tested "no PR context" skip path (the same one repository_dispatch retries with no payload already hit) by clearing PR_NUMBER when changed-scope reports a docs/image-only diff, instead of adding a second condition to each of its dozen existing `if: env.PR_NUMBER != ''` step guards. Both gates fail open: an unreadable, empty, or truncated file list reviews everything, matching the established pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015gdR6WuShb6HH6niXc1wkj
…capacity-demand-gate-20260917 fix(opencode-review,noema-review): skip the AI review chain for docs/image-only diffs
…t-head opencode-review.yml -- the required per-repo wrapper, not just opencode-review-dispatch.yml fixed in #2228 -- carries the same needs:-chained inter-job queue-wait pattern under org-wide saturation. Measured on .github PR #2183, run 35042040116: required-workflow-bootstrap completed at 05:37:51 but admit-current-head did not start until 10:24:12, a 4h46m gap between two jobs that each run in single-digit seconds. The admitted job's own steps array was empty when fetched, confirming it never picked up a runner during that gap (pure queue wait, not a hung gh api call -- corrects an earlier hypothesis raised while investigating this). Both jobs are the same trust level: read-only PR/workflow metadata via the default github.token, no untrusted PR-content execution, no elevated token. admit-current-head was not itself a required branch-protection context (only required-workflow-bootstrap, coverage-evidence, and opencode-review are, per docs/doctoring/required-workflow-path-filter-boundary.md's live-fetched contexts list), so folding its single step into required-workflow-bootstrap keeps every required context name intact while removing one job-to-job runner-queue re-entry. Downstream jobs (coverage-source-tree, coverage-evidence, opencode-review-target) now depend on required-workflow-bootstrap's `admitted` output directly. Directed by the maintainer via the coordinator after reviewing this finding (a demand-side investigation turned up this separate, previously-undiagnosed supply-side gap). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015gdR6WuShb6HH6niXc1wkj
…capacity-opencode-review-fold-20260917 fix(opencode-review): fold required-workflow-bootstrap + admit-current-head
opencode-review.yml dispatches a full multi-hour OpenCode review chain on every synchronize push, even when several land within seconds of each other. Measured across 4 org repositories (419 consecutive-push gaps, 2026-09-17): density roughly halves right at 300s, the clearest inflection point in an otherwise continuous, non-bimodal distribution -- so 300s is the coalescing window. This PR is the mechanism, defaulting to fully inert: - scripts/ci/pr_review_merge_scheduler_core.py: head_stable_for_seconds() reads the already-fetched head commit's committedDate (no new GraphQL fields needed); coalesce_enabled()/coalesce_window_seconds() read env vars, both defaulting to today's behavior (disabled, 300s). A new gate at the top of dispatch_opencode_review() returns "coalescing" instead of dispatching when the head is younger than the window AND coalescing is enabled -- every one of its 6 callers gets an explicit "coalescing" branch alongside its existing "already_running" handling, all covered by new tests (100% line/branch coverage maintained). - .github/workflows/opencode-review-coalesce-tick.yml: a new, org-wide, non-stacking (concurrency group, cancel-in-progress: false) 5-minute scheduled tick. Its job's own `if:` requires the OPENCODE_REVIEW_COALESCE_ENABLED repository variable, so merging this file changes nothing until that variable is set. GitHub's required-workflow ruleset only propagates pull_request_target-family events to sibling repositories, never `schedule:` -- a per-repo cron would only ever fire for this repository's own PRs -- so this tick instead lists every open PR across the organization with one GraphQL search, then re-invokes the existing, unmodified scripts/ci/pr_review_merge_scheduler.py once per repository that has one, reusing 100% of its existing same-head dedup, admission-budget, and live-head-revalidation logic (plus, now, the coalescing gate above) instead of duplicating any of it. Scoped to review dispatch only (--no-enable-auto-merge --no-update-branches): merge scheduling and branch freshness stay owned by the regular per-push/per-review invocations. PR2 (follow-up, not in this PR) sets OPENCODE_REVIEW_COALESCE_ENABLED after the tick is verified on live PRs, and suppresses the redundant immediate per-push dispatch paths (opencode-review.yml's own dispatch step, scan-pr-queue's own per-push invocation) so the tick becomes the sole dispatcher for synchronize-triggered reviews. The flag-off path here stays tested and unchanged either way. Directed by the maintainer via the coordinator, including the four conditions this PR satisfies: wider push-gap sample, a cheap non-stacking org-level tick (not per-repo crons, which schedule triggers can't propagate anyway), a feature flag with flag-off tested, and the two-PR split. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015gdR6WuShb6HH6niXc1wkj
…capacity-coalesce-tick-20260917 feat(scheduler): add push-burst coalescing, inert by default (PR1/2)
Automated central SBOM inventory refresh for live non-fork repositories. Review reciprocal, restricted, and NOASSERTION license evidence in docs/sbom/inventory.md against the product's actual distribution and hosted-service model.
Current blocking state (2026-09-08)
e6b292ce5e104212eaa032579afff1514b865019; livemaincomparison is ahead 70 / behind 0. The effective customer-evidence delta remainsdocs/sbom/inventory.jsonanddocs/sbom/inventory.md.required-workflow-bootstrapis deterministically RED before policy analysis because the Contents API does not inline the 1,148,611-byte JSON and protectedmain's policy caps that evidence path at 1 MiB. This is not an Nginx finding.1cb8cceb8719eb054979d84141cda9a95c0c6873, ahead 5 / behind 0, with bounded Git Blobs fallback plus malicious-content and malformed-evidence tests. Both original and Ready-event same-head Security, SAST, Python Security, and CodeQL PR runs are terminal GREEN. Noema and Strix were materialized but failed on centralorchestrator/freeavailability (HTTP 429 / zero ready routes); OpenCode's derivedCHANGES_REQUESTEDcontains no leaf-source finding. Keep this inventory PR Draft until fix(pingora): read Contents-API-oversized files through the Git Blobs API #1946 reaches protectedmain, this branch non-force integrates that release, and new exact-head checks plus independent review are valid.orchestrator/freeHTTP 429 is a separate review-transport blocker. Keep this PR Draft until fix(pingora): read Contents-API-oversized files through the Git Blobs API #1946 reaches protectedmain, this branch non-force integrates that release, and all new exact-head checks plus independent review are valid.