Skip to content

chore: refresh org SBOM inventory - #1678

Draft
opencode-agent[bot] wants to merge 324 commits into
mainfrom
automation/sbom-inventory
Draft

opencode-agent[bot] wants to merge 324 commits into
mainfrom
automation/sbom-inventory

Conversation

@opencode-agent

@opencode-agent opencode-agent Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

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.


Devin Review

Current blocking state (2026-09-08)

  • Exact head: e6b292ce5e104212eaa032579afff1514b865019; live main comparison is ahead 70 / behind 0. The effective customer-evidence delta remains docs/sbom/inventory.json and docs/sbom/inventory.md.
  • required-workflow-bootstrap is deterministically RED before policy analysis because the Contents API does not inline the 1,148,611-byte JSON and protected main's policy caps that evidence path at 1 MiB. This is not an Nginx finding.
  • Canonical owner repair #1946 sits at exact head 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 central orchestrator/free availability (HTTP 429 / zero ready routes); OpenCode's derived CHANGES_REQUESTED contains no leaf-source finding. Keep this inventory PR Draft until fix(pingora): read Contents-API-oversized files through the Git Blobs API #1946 reaches protected main, this branch non-force integrates that release, and new exact-head checks plus independent review are valid.
  • Noema's orchestrator/free HTTP 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 protected main, this branch non-force integrates that release, and all new exact-head checks plus independent review are valid.

@opencode-agent
opencode-agent Bot requested a review from seonghobae as a code owner September 2, 2026 04:27

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Devin Review

@seonghobae seonghobae added priority: medium Normal-priority or P2 work status: needs-review Open pull request requiring current-head review or checks type: maintenance Maintenance, build, dependency, or operational upkeep labels Sep 2, 2026 — with ChatGPT Codex Connector

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 0 new potential issues.

Devin Review

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 0 new potential issues.

Devin Review

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Devin Review

Copy link
Copy Markdown
Contributor

Diagnosis of the required-workflow-bootstrap failure on this branch (run 33989047645, 5 s, exit 2):

Pingora edge policy could not establish complete evidence: GitHub content evidence for docs/sbom/inventory.json exceeds the size contract

This is deterministic and content-blind, not an Nginx finding. The regenerated docs/sbom/inventory.json is 1,148,611 bytes (236 bytes on main). GitHub omits the diff patch for a file that large, .json is neither a documentation suffix nor a binary document format, so pingora_edge_policy.py must fetch the content, and the Contents API stops inlining at 1 MiB (encoding: "none"), which the policy turns into a fail-closed ContentSizeExceededError. Scanned offline with main's own scan_content, the file has zero denied forms and does not contain the string nginx at all. Every push of this branch will hit the same wall until the evidence route changes.

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 main, one push that merges main into this branch re-binds the required workflow's trusted source and this context can pass on its merits; re-running the job before then reproduces the same exit 2.


Generated by Claude Code

cwl-sbom-inventory[bot] and others added 13 commits September 6, 2026 06:17
…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
cwl-sbom-inventory[bot] and others added 30 commits September 16, 2026 05:48
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance priority: medium Normal-priority or P2 work status: needs-review Open pull request requiring current-head review or checks type: maintenance Maintenance, build, dependency, or operational upkeep

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants