diff --git a/.github/workflows/fast-mlsirm-hourly-review-repair.yml b/.github/workflows/fast-mlsirm-hourly-review-repair.yml index a3651cce4..e080771de 100644 --- a/.github/workflows/fast-mlsirm-hourly-review-repair.yml +++ b/.github/workflows/fast-mlsirm-hourly-review-repair.yml @@ -15,9 +15,6 @@ permissions: jobs: dispatch-review-repair: - permissions: - contents: read - id-token: write uses: ./.github/workflows/pr-review-fix-scheduler.yml with: target_repository: ContextualWisdomLab/fast-mlsirm diff --git a/.github/workflows/github-hourly-review-repair.yml b/.github/workflows/github-hourly-review-repair.yml index 7c8557ba6..97665aa5f 100644 --- a/.github/workflows/github-hourly-review-repair.yml +++ b/.github/workflows/github-hourly-review-repair.yml @@ -14,9 +14,6 @@ permissions: jobs: dispatch-review-repair: - permissions: - contents: read - id-token: write uses: ./.github/workflows/pr-review-fix-scheduler.yml with: target_repository: ContextualWisdomLab/.github diff --git a/.github/workflows/governance-risk-compliance-hourly-review-repair.yml b/.github/workflows/governance-risk-compliance-hourly-review-repair.yml index 813fe360e..08bcab6b7 100644 --- a/.github/workflows/governance-risk-compliance-hourly-review-repair.yml +++ b/.github/workflows/governance-risk-compliance-hourly-review-repair.yml @@ -15,9 +15,6 @@ permissions: jobs: dispatch-review-repair: - permissions: - contents: read - id-token: write uses: ./.github/workflows/pr-review-fix-scheduler.yml with: target_repository: ContextualWisdomLab/governance-risk-compliance diff --git a/.github/workflows/hourly-nvidia-nim-review-repair.yml b/.github/workflows/hourly-nvidia-nim-review-repair.yml index 702942708..db7224f5a 100644 --- a/.github/workflows/hourly-nvidia-nim-review-repair.yml +++ b/.github/workflows/hourly-nvidia-nim-review-repair.yml @@ -15,7 +15,6 @@ on: - .github/workflows/hourly-nvidia-nim-review-repair.yml - .github/workflows/nonnest2-hourly-review-repair.yml - .github/workflows/originweave-hourly-review-repair.yml - - .github/workflows/quarantine-sandbox-hourly-review-repair.yml - scripts/ci/pr_review_conflict_scope.py - scripts/ci/pr_review_autofix_context.py - tests/test_bandscope_hourly_review_caller.py @@ -26,7 +25,6 @@ on: - tests/test_hourly_scheduler_runtime_budget.py - tests/test_nonnest2_hourly_review_caller.py - tests/test_originweave_hourly_review_caller.py - - tests/test_quarantine_sandbox_hourly_review_caller.py - tests/test_hourly_autofix_context_quality_gate.py - tests/test_pr_review_conflict_scope.py - tests/test_pr_review_conflict_scope_control_files.py @@ -50,7 +48,6 @@ on: - docs/doctoring/hourly-nvidia-nim-autofix.md - docs/doctoring/nonnest2-hourly-review-caller.md - docs/doctoring/originweave-hourly-review-caller.md - - docs/doctoring/quarantine-sandbox-hourly-review-caller.md push: paths: - .github/workflows/pr-review-fix-scheduler.yml @@ -65,7 +62,6 @@ on: - .github/workflows/hourly-nvidia-nim-review-repair.yml - .github/workflows/nonnest2-hourly-review-repair.yml - .github/workflows/originweave-hourly-review-repair.yml - - .github/workflows/quarantine-sandbox-hourly-review-repair.yml - scripts/ci/pr_review_conflict_scope.py - scripts/ci/pr_review_autofix_context.py - tests/test_bandscope_hourly_review_caller.py @@ -76,7 +72,6 @@ on: - tests/test_hourly_scheduler_runtime_budget.py - tests/test_nonnest2_hourly_review_caller.py - tests/test_originweave_hourly_review_caller.py - - tests/test_quarantine_sandbox_hourly_review_caller.py - tests/test_hourly_autofix_context_quality_gate.py - tests/test_pr_review_conflict_scope.py - tests/test_pr_review_conflict_scope_control_files.py @@ -100,7 +95,6 @@ on: - docs/doctoring/hourly-nvidia-nim-autofix.md - docs/doctoring/nonnest2-hourly-review-caller.md - docs/doctoring/originweave-hourly-review-caller.md - - docs/doctoring/quarantine-sandbox-hourly-review-caller.md permissions: contents: read @@ -156,7 +150,6 @@ jobs: tests/test_hourly_scheduler_runtime_budget.py \ tests/test_nonnest2_hourly_review_caller.py \ tests/test_originweave_hourly_review_caller.py \ - tests/test_quarantine_sandbox_hourly_review_caller.py \ tests/test_pr_review_conflict_scope_control_files.py \ tests/test_hourly_autofix_context_quality_gate.py \ tests/test_pr_review_conflict_scope_git_executable.py \ diff --git a/.github/workflows/pr-review-merge-scheduler.yml b/.github/workflows/pr-review-merge-scheduler.yml index 8319ae5be..8e1157060 100644 --- a/.github/workflows/pr-review-merge-scheduler.yml +++ b/.github/workflows/pr-review-merge-scheduler.yml @@ -773,14 +773,6 @@ jobs: echo "::error::ORG_SWEEP_MAX_UNAVAILABLE must be a non-negative integer; got '${ORG_SWEEP_MAX_UNAVAILABLE}'. Fix the ORG_SWEEP_MAX_UNAVAILABLE repository variable." exit 1 fi - if ! [[ "$ORG_SWEEP_REVIEW_DISPATCH_LIMIT" =~ ^(-1|[0-9]+)$ ]]; then - echo "::error::ORG_SWEEP_REVIEW_DISPATCH_LIMIT must be -1 or a non-negative integer; got '${ORG_SWEEP_REVIEW_DISPATCH_LIMIT}'. Fix the ORG_SWEEP_REVIEW_DISPATCH_LIMIT repository variable." - exit 1 - fi - if ! [[ "$ORG_SWEEP_BRANCH_UPDATE_LIMIT" =~ ^(-1|[0-9]+)$ ]]; then - echo "::error::ORG_SWEEP_BRANCH_UPDATE_LIMIT must be -1 or a non-negative integer; got '${ORG_SWEEP_BRANCH_UPDATE_LIMIT}'. Fix the ORG_SWEEP_BRANCH_UPDATE_LIMIT repository variable." - exit 1 - fi repositories_json="$( gh api \ @@ -800,11 +792,6 @@ jobs: failures=0 unavailable=0 unavailable_repos=() - # These are organization-wide budgets. They must be consumed across - # the repository loop, not reset for every target repository; resetting - # them here can enqueue hundreds of long-running review jobs per sweep. - org_review_dispatches_used=0 - org_branch_updates_used=0 for target in "${sweep_targets[@]}"; do repo_full_name="${target%%$'\t'*}" default_branch="${target##*$'\t'}" @@ -832,31 +819,14 @@ jobs: *) project_flow="github-flow" ;; esac - if [ "$ORG_SWEEP_REVIEW_DISPATCH_LIMIT" = "-1" ]; then - review_dispatch_limit=-1 - else - review_dispatch_limit=$((ORG_SWEEP_REVIEW_DISPATCH_LIMIT - org_review_dispatches_used)) - if (( review_dispatch_limit < 0 )); then - review_dispatch_limit=0 - fi - fi - if [ "$ORG_SWEEP_BRANCH_UPDATE_LIMIT" = "-1" ]; then - branch_update_limit=-1 - else - branch_update_limit=$((ORG_SWEEP_BRANCH_UPDATE_LIMIT - org_branch_updates_used)) - if (( branch_update_limit < 0 )); then - branch_update_limit=0 - fi - fi - args=( --repo "$repo_full_name" --base-branch "$default_branch" --project-flow "$project_flow" --max-prs "$ORG_SWEEP_MAX_PRS" --review-workflow "Required OpenCode Review" - --review-dispatch-limit "$review_dispatch_limit" - --branch-update-limit "$branch_update_limit" + --review-dispatch-limit "$ORG_SWEEP_REVIEW_DISPATCH_LIMIT" + --branch-update-limit "$ORG_SWEEP_BRANCH_UPDATE_LIMIT" --stale-opencode-minutes "$STALE_OPENCODE_MINUTES" --merge-mode "$ORG_SWEEP_MERGE_MODE" ) @@ -877,11 +847,6 @@ jobs: sweep_rc=$? set -e printf '%s\n' "$sweep_output" - repo_review_dispatches="$(printf '%s\n' "$sweep_output" | grep -Ec '^PR #[0-9]+: (review_dispatch|security_dispatch):' || true)" - repo_branch_updates="$(printf '%s\n' "$sweep_output" | grep -Ec '^PR #[0-9]+: (update_branch|restamp_head):' || true)" - org_review_dispatches_used=$((org_review_dispatches_used + repo_review_dispatches)) - org_branch_updates_used=$((org_branch_updates_used + repo_branch_updates)) - echo "Org sweep budget consumed: review dispatches=${org_review_dispatches_used}/${ORG_SWEEP_REVIEW_DISPATCH_LIMIT}, branch updates=${org_branch_updates_used}/${ORG_SWEEP_BRANCH_UPDATE_LIMIT}." if [ "$sweep_rc" -ne 0 ]; then # A structural access denial ("Resource not accessible by # integration") means the sweep credential cannot read this diff --git a/.github/workflows/quarantine-sandbox-hourly-review-repair.yml b/.github/workflows/quarantine-sandbox-hourly-review-repair.yml deleted file mode 100644 index 2649ee3e6..000000000 --- a/.github/workflows/quarantine-sandbox-hourly-review-repair.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Quarantine Sandbox Hourly Review Repair - -on: - schedule: - # Minute 14 avoids existing product callers while keeping one bounded - # review-repair heartbeat per hour for the sandbox runtime. - - cron: "14 * * * *" - -concurrency: - group: quarantine-sandbox-hourly-review-repair - # A later heartbeat must not cancel an in-flight security RCA. - cancel-in-progress: false - -permissions: - contents: read - -jobs: - dispatch-review-repair: - permissions: - contents: read - id-token: write - uses: ./.github/workflows/pr-review-fix-scheduler.yml - with: - target_repository: ContextualWisdomLab/quarantine-sandbox-runtime - base_branch: develop - max_prs: "50" - max_dispatches: "1" - retry_hours: "2" - secrets: - PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }} - OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index fd1aebf43..7bf8ad766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,6 @@ Semantic Versioning where the repository publishes a release. ### Added - Added an hourly organization commercial-readiness coordinator that discovers writable repositories, honors enabled dedicated writer leases and fully paginated live writer runs, refetches exact repository/workflow/run/PR state before dispatch, rotates bounded review-repair and opt-in NVIDIA OpenCode product-development targets, fails nonzero on fleet-wide inspection or dispatch outages, retains three-day JSON receipts, and keeps the existing 15-minute merge scheduler authoritative. -- Added a dedicated Quarantine Sandbox Runtime hourly caller at minute 14 that targets protected `develop`, dispatches at most one exact-head repair, applies a two-hour same-head retry floor, preserves non-cancelling single-flight execution, and maps only the established scheduler credentials with job-scoped OIDC. -- Added a dedicated Quarantine Sandbox Runtime hourly caller at minute 14 that targets protected `develop`, dispatches at most one exact-head repair, applies a two-hour same-head retry floor, preserves non-cancelling single-flight execution, and maps only the established scheduler credentials with job-scoped OIDC. - Added a dedicated OriginWeave hourly caller that invokes the product-neutral central scheduler with the exact repository, protected `main` branch, one-dispatch budget, two-hour same-head retry floor, non-cancelling single-flight heartbeat, job-scoped OIDC, and only the established scheduler credentials. - Added a trusted pull-request comment router for `@cwl-noema-review` and review-only `@opencode-agent` dispatches, with an organization sweep, exact-head receipts, repository allowlisting, fixed runners, immutable checkout pins, and a permanent 100% statement/branch/docstring quality gate. - Added exact-base `uv.lock` materialization that reconstructs standalone nested projects with a checksum-pinned official `uv` exporter, isolated frozen/offline execution, strict exact-pin and SHA-256 output validation, and complete Python 3.10/3.14 quality evidence. @@ -22,7 +20,6 @@ Semantic Versioning where the repository publishes a release. ### Changed - Require the hourly repair worker to establish an exact-head root cause, enumerate the smallest remediation candidates, and prove writer authority, sealed-path scope, credentials, dependency order, verifiability, and causal effect before editing; infeasible or external blockers leave the tree unchanged while the broader loop continues with another eligible PR or buyer-visible product gap. -- Run the bounded Quarantine Sandbox Runtime heartbeat at minute 14 without granting the caller model secrets, repository mutation permissions, approval, merge, release, artifact-execution, or final security-verdict authority. - Run the bounded Clearfolio PR review-feedback repair caller at minute 23 of every hour while keeping the shared scheduler free of product-specific timers and repository names for modular reuse by naruon, contextual-orchestrator, Inkspan, and other CWL services. - Run the bounded DiskSage repair heartbeat at minute 37 of every hour, dispatch no more than one exact-head repair, and wait two hours before redispatching an unchanged head so legitimate OpenCode or NVIDIA NIM latency does not create duplicate writers. - Run the bounded fast-mlsirm repair heartbeat at minute 49 of every hour with one-dispatch scope and a two-hour same-head floor, without weakening true-parameter recovery, CPU/GPU parity, skipped-test, or Rust-ownership gates. @@ -51,7 +48,6 @@ Semantic Versioning where the repository publishes a release. ### Security -- Keep the Quarantine Sandbox Runtime caller read-only and model-secret-free, grant only job-scoped OIDC to the reusable scheduler, and preserve the product boundary in which the sandbox returns artifact-analysis evidence while hosts retain WAF/IDS, admission, final verdict, incident, and retention authority. - Reject `.github/` and `scripts/ci/` from review-thread-derived autofix path authority so an untrusted inline reviewer cannot authorize the write-capable repair agent to modify workflows, CODEOWNERS, actions, scheduler code, or CI helpers that govern its own control plane. - Require the model-write snapshot and exact-path allowlist to remain outside the pull-request worktree, checking both absolute and resolved locations so repository-local controls and outside-looking symlinks resolving into the repository fail closed before they can authorize or verify model changes. - Snapshot the complete pre-model worktree for ordinary and conflict repair and reject every model-caused created, deleted, modified, mode-changed, retargeted, ignored, dangling, directory-backed, external-link, metadata-race, or out-of-scope path before staging or push. @@ -68,7 +64,6 @@ Semantic Versioning where the repository publishes a release. ### Documentation -- Added Quarantine Sandbox Runtime operator and APA 7 doctoring for the hourly RCA loop, source-agnostic leaf boundary, protected-`develop` activation, bounded retry cadence, OIDC and secret scope, independent approval, verification, and rollback. - Added an APA 7 doctoring record for conflict-control evidence isolation, including the Strix-reported trust-boundary failure, test-first remediation, canonical-path rule, operator contract, rollback, MITRE CWE-22, and current GitHub Actions secure-use guidance. - Added operator and APA 7 doctoring records for the hourly cadence, immutable source identity, NVIDIA NIM provider and secret boundary, high-reasoning Mistral Small 4 writer, model-process credential isolation, modular MSA ownership, product-specific caller activation, verification contract, and rollback. - Added DiskSage operational documentation for the hourly RCA loop, bounded retry cadence, permission model, standalone and MSA reuse, verification, rollback, and APA 7 references. diff --git a/docs/doctoring/quarantine-sandbox-hourly-review-caller.md b/docs/doctoring/quarantine-sandbox-hourly-review-caller.md deleted file mode 100644 index f8f27c5aa..000000000 --- a/docs/doctoring/quarantine-sandbox-hourly-review-caller.md +++ /dev/null @@ -1,145 +0,0 @@ -# Quarantine Sandbox Runtime hourly review-repair caller - -검토 기준일: **2026-08-18** - -## Decision - -ContextualWisdomLab operates one protected hourly caller for -`ContextualWisdomLab/quarantine-sandbox-runtime`, the credential-free and -source-agnostic artifact-analysis leaf used by authorized security and -composition products. The caller runs at minute 14, delegates to the -product-neutral central review-fix scheduler, inspects at most 50 open pull -requests targeting protected `develop`, and dispatches at most one bounded -repair per heartbeat. - -The immediate buyer-perceivable gap is queue starvation: the repository has a -buyer-facing contract PR and a Rust runtime-foundation PR, but it was absent -from the existing product-specific hourly callers. Security review latency is -not permission to bypass approval or checks; it is a reason to give the exact -repository a bounded, auditable repair heartbeat. - -The caller does not implement review or mutation logic. Quarantine Sandbox -Runtime remains independently deployable. Wardnet, naruon, gyeot, and other -authorized hosts may consume the published evidence contract without owning the -runtime. Privileged automation remains in `ContextualWisdomLab/.github`. - -## Root-cause analysis and remediation feasibility - -The reusable worker performs exact-head root-cause analysis and tests -remediation feasibility before editing. It must: - -1. Refetch the live head, base, reviews, unresolved threads, checks, changed - paths, stack relationships, and active writer state. -2. Establish the first causal boundary instead of repeating a terminal failed - check or review message. -3. Enumerate materially distinct minimal remedies. -4. Reject remedies that lack writer authority, cross the sealed path set, - require unavailable credentials or protected-setting changes, violate stack - order, cannot be verified, or do not change the diagnosed cause. -5. Dispatch at most one feasible repair; otherwise leave the branch unchanged - and continue productive non-conflicting work. - -A queued check remains a merge blocker but is not a code defect. The independent non-author approval remains an external authorization gate and is never synthesized -by the repair worker. The worker cannot approve, merge, release, weaken branch -protection, reinterpret a missing sandbox capability as success, or manufacture -passing evidence. - -## Cadence and concurrency - -The caller uses one repository-scoped concurrency group and -`cancel-in-progress: false`. A later heartbeat must not discard an in-flight -security RCA. The reusable scheduler may cancel only a superseded short queue -scan. - -The caller sets a **two-hour same-head retry floor**. OpenCode/NVIDIA NIM review -and hostile-artifact boundary analysis may legitimately take longer than one -hour. Re-dispatching the same unchanged head every hour would create duplicate -writer pressure. - -GitHub scheduled workflows run only from the default branch and can be delayed -under Actions load. Minute 14 avoids the start-of-hour load peak and the existing -CWL product caller minutes. The cron expression is a heartbeat, not a real-time -SLA (GitHub, Inc., n.d.-a). - -## Credential and model boundary - -The caller keeps workflow `GITHUB_TOKEN` at `contents: read`. Only the reusable -job receives `id-token: write`, enabling the central scheduler to request a -GitHub OIDC token when its reviewed credential chain requires one (GitHub, Inc., -n.d.-b). The caller maps only `PR_REVIEW_MERGE_TOKEN` and -`OPENCODE_APPROVE_TOKEN`; it never uses `secrets: inherit`, receives -`NVIDIA_NIM_API_KEY`, or introduces `COPILOT_GITHUB_TOKEN`. - -Model execution and the NVIDIA credential remain inside the separately reviewed -central worker. This caller holds no model secret and cannot run arbitrary pull -request content. Limiting privileges follows CWE-250 and the NIST SSDF practice -of protecting software-development environments and artifacts (MITRE, 2026; -Souppaya et al., 2022). - -Before protected-main activation, `OPENCODE_REPOSITORY_DISPATCH_TARGETS` must -contain the exact `ContextualWisdomLab/quarantine-sandbox-runtime` target. -Missing or mismatched configuration fails before mutation credentials are -materialized. - -## Product and MSA boundary - -The scheduler may repair code or documentation inside the target PR's verified -scope. It does not move these product authorities: - -- Quarantine Sandbox Runtime owns artifact-analysis evidence. -- Wardnet owns WAF/IDS and SOC response policy. -- Naruon owns email admission and mailbox state. -- EgressWeave owns controlled outbound HTTP. -- The calling product owns final maliciousness judgment, incident action, and - retention. - -The caller adds no runtime dependency, database object, network endpoint, -artifact-execution authority, tenant authority, or product credential. The -sandbox runtime remains a standalone leaf and composition hubs consume its -published contract. - -## Verification and operational acceptance - -Machine-checkable contracts require: - -- exact repository and `develop` base; -- minute 14 hourly cadence; -- non-cancelling repository-scoped concurrency; -- at most one dispatch and a two-hour same-head retry floor; -- read-only workflow contents plus job-scoped `id-token: write`; -- explicit scheduler-secret mapping; -- absence of `NVIDIA_NIM_API_KEY`, `COPILOT_GITHUB_TOKEN`, and `secrets: inherit`; -- independent `pull_request`, `push`, and `compileall` coverage of the caller, - focused test, and doctoring document; and -- no product name hard-coded in the reusable scheduler. - -After merge, a scheduled protected-default-branch run must prove the exact -target and base. Source checks alone are not protected-main operational acceptance. -Product PR merge still requires exact-head required checks, -resolution of every valid review finding, and qualifying independent approval. - -Rollback removes only this caller, its focused test, doctoring, and central -quality-path entries. It must not remove the reusable scheduler or alter another -product caller. - -## APA 7th references - -GitHub, Inc. (n.d.-a). *Troubleshooting workflows*. GitHub Docs. Retrieved -August 18, 2026, from -https://docs.github.com/en/actions/how-tos/troubleshoot-workflows - -GitHub, Inc. (n.d.-b). *OpenID Connect reference*. GitHub Docs. Retrieved -August 18, 2026, from -https://docs.github.com/en/actions/reference/security/oidc - -GitHub, Inc. (n.d.-c). *Reuse workflows*. GitHub Docs. Retrieved August 18, -2026, from -https://docs.github.com/en/actions/how-tos/sharing-automations/reuse-workflows - -MITRE. (2026). *CWE-250: Execution with unnecessary privileges*. -https://cwe.mitre.org/data/definitions/250.html - -Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure software development -framework (SSDF) version 1.1: Recommendations for mitigating the risk of -software vulnerabilities* (NIST Special Publication 800-218). National -Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-218 diff --git a/scripts/ci/pr_review_merge_scheduler.py b/scripts/ci/pr_review_merge_scheduler.py index 118d0d903..75e18c860 100644 --- a/scripts/ci/pr_review_merge_scheduler.py +++ b/scripts/ci/pr_review_merge_scheduler.py @@ -2388,19 +2388,6 @@ def request_branch_update(freshness_reason: str, *, suffix: str = "") -> Decisio return decide("block", f"{unresolved} unresolved review thread(s)") if has_current_head_changes_requested(pr): - behind_by = branch_outdated_by_base(pr, merge_state) - if ( - merge_state not in {"DIRTY", "CONFLICTING"} - and behind_by - and not pr.get("autoMergeRequest") - and update_branches - and trigger_reviews - and review_dispatch_allowed - and can_update_pr_head(repo, pr) - ): - return request_branch_update( - "current-head OpenCode review requested changes; branch is outdated before re-review" - ) if pr.get("autoMergeRequest"): return finish( disable_auto_merge_decision( diff --git a/scripts/ci/sandboxed_web_e2e.py b/scripts/ci/sandboxed_web_e2e.py index ae0c3105a..0c3fc67f7 100644 --- a/scripts/ci/sandboxed_web_e2e.py +++ b/scripts/ci/sandboxed_web_e2e.py @@ -110,6 +110,7 @@ def start_service(label: str, command: str, cwd: Path, env: dict[str, str], logs stdout=log_file, stderr=subprocess.STDOUT, start_new_session=True, + shell=False, ) log_file.close() return Service(label=label, command=command, process=process, log_path=log_path) @@ -146,6 +147,7 @@ def run_shell(command: str, cwd: Path, env: dict[str, str], timeout: int) -> sub stderr=subprocess.PIPE, timeout=timeout, check=False, + shell=False, ) diff --git a/tests/test_fast_mlsirm_hourly_review_caller.py b/tests/test_fast_mlsirm_hourly_review_caller.py index 1fd096586..616823ea5 100644 --- a/tests/test_fast_mlsirm_hourly_review_caller.py +++ b/tests/test_fast_mlsirm_hourly_review_caller.py @@ -36,7 +36,7 @@ def test_fast_mlsirm_caller_preserves_credentials_and_read_only_scope() -> None: opencode_secret = "$" + "{{ secrets.OPENCODE_APPROVE_TOKEN }}" assert "\npermissions:\n contents: read\n" in workflow_scope - assert "\n permissions:\n contents: read\n id-token: write\n" in jobs_scope + assert "\n permissions:\n" not in jobs_scope assert f"PR_REVIEW_MERGE_TOKEN: {pr_review_secret}" in caller assert f"OPENCODE_APPROVE_TOKEN: {opencode_secret}" in caller assert "secrets: inherit" not in caller diff --git a/tests/test_github_hourly_conflict_repair.py b/tests/test_github_hourly_conflict_repair.py index 7d98837eb..e905bbce8 100644 --- a/tests/test_github_hourly_conflict_repair.py +++ b/tests/test_github_hourly_conflict_repair.py @@ -121,7 +121,6 @@ def test_central_repository_has_hourly_self_caller() -> None: assert "resolve_unreviewed_conflicts: true" in workflow assert 'max_dispatches: "1"' in workflow assert 'retry_hours: "1"' in workflow - assert "\n permissions:\n contents: read\n id-token: write\n" in workflow assert "COPILOT_GITHUB_TOKEN" not in workflow diff --git a/tests/test_governance_risk_compliance_hourly_review_caller.py b/tests/test_governance_risk_compliance_hourly_review_caller.py index 4b0fb4f93..525748b4c 100644 --- a/tests/test_governance_risk_compliance_hourly_review_caller.py +++ b/tests/test_governance_risk_compliance_hourly_review_caller.py @@ -36,7 +36,7 @@ def test_grc_caller_preserves_credentials_and_read_only_scope() -> None: opencode_secret = "$" + "{{ secrets.OPENCODE_APPROVE_TOKEN }}" assert "\npermissions:\n contents: read\n" in workflow_scope - assert "\n permissions:\n contents: read\n id-token: write\n" in jobs_scope + assert "\n permissions:\n" not in jobs_scope assert f"PR_REVIEW_MERGE_TOKEN: {pr_review_secret}" in caller assert f"OPENCODE_APPROVE_TOKEN: {opencode_secret}" in caller assert "secrets: inherit" not in caller diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 379dded14..a57adb695 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -1980,6 +1980,7 @@ def test_merge_scheduler_uses_escalating_mutation_credentials(): assert "BRANCH_UPDATE_LIMIT_INPUT" in workflow assert "ORG_SWEEP_BRANCH_UPDATE_LIMIT" in workflow assert '--branch-update-limit "$branch_update_limit"' in workflow + assert '--branch-update-limit "$ORG_SWEEP_BRANCH_UPDATE_LIMIT"' in workflow assert "pull_request_review:" in workflow assert "types: [submitted, dismissed]" in workflow assert ( diff --git a/tests/test_pr_review_merge_scheduler.py b/tests/test_pr_review_merge_scheduler.py index f2dd25813..3e421e903 100644 --- a/tests/test_pr_review_merge_scheduler.py +++ b/tests/test_pr_review_merge_scheduler.py @@ -2997,61 +2997,6 @@ def test_inspect_pr_blocks_and_waits_for_policy_states(monkeypatch): assert inspect(make_pr(reviews={"nodes": [opencode_review("CHANGES_REQUESTED", "head")]})).reason == ( "current-head OpenCode review requested changes" ) - stale_change_request = inspect( - make_pr( - mergeStateStatus="BEHIND", - restMergeableState="BEHIND", - compareBehindBy=2, - reviews={"nodes": [opencode_review("CHANGES_REQUESTED", "head")]}, - ) - ) - assert stale_change_request.action == "update_branch" - assert stale_change_request.reason == ( - "current-head OpenCode review requested changes; branch is outdated before re-review; " - "branch update requested with workflow GITHUB_TOKEN inside GitHub Actions as github-actions[bot]" - ) - stale_change_request_without_review_dispatch = inspect( - make_pr( - mergeStateStatus="BEHIND", - restMergeableState="BEHIND", - compareBehindBy=2, - reviews={"nodes": [opencode_review("CHANGES_REQUESTED", "head")]}, - ), - trigger_reviews=False, - ) - assert stale_change_request_without_review_dispatch.action == "block" - assert stale_change_request_without_review_dispatch.reason == ( - "current-head OpenCode review requested changes" - ) - stale_change_request_without_dispatch_permission = inspect( - make_pr( - mergeStateStatus="BEHIND", - restMergeableState="BEHIND", - compareBehindBy=2, - reviews={"nodes": [opencode_review("CHANGES_REQUESTED", "head")]}, - ), - review_dispatch_allowed=False, - ) - assert stale_change_request_without_dispatch_permission.action == "block" - assert stale_change_request_without_dispatch_permission.reason == ( - "current-head OpenCode review requested changes" - ) - update_calls = [] - monkeypatch.setattr(sched, "update_branch", lambda *args, **kwargs: update_calls.append((args, kwargs))) - for merge_state in ("DIRTY", "CONFLICTING"): - conflict_with_stale_review = inspect( - make_pr( - mergeStateStatus=merge_state, - restMergeableState=merge_state, - compareBehindBy=2, - reviews={"nodes": [opencode_review("CHANGES_REQUESTED", "head")]}, - ) - ) - assert conflict_with_stale_review.action == "block" - assert conflict_with_stale_review.reason == ( - "current-head OpenCode review requested changes" - ) - assert update_calls == [] action_required_pr = make_pr( statusCheckRollup={ "contexts": { diff --git a/tests/test_quarantine_sandbox_hourly_review_caller.py b/tests/test_quarantine_sandbox_hourly_review_caller.py deleted file mode 100644 index 1755bb5e7..000000000 --- a/tests/test_quarantine_sandbox_hourly_review_caller.py +++ /dev/null @@ -1,179 +0,0 @@ -"""Contract tests for Quarantine Sandbox Runtime's hourly repair caller.""" - -from pathlib import Path - - -CALLER = Path(".github/workflows/quarantine-sandbox-hourly-review-repair.yml") -DOCTORING = Path("docs/doctoring/quarantine-sandbox-hourly-review-caller.md") -QUALITY_WORKFLOW = Path(".github/workflows/hourly-nvidia-nim-review-repair.yml") -SCHEDULER = Path(".github/workflows/pr-review-fix-scheduler.yml") - - -def _read(path: Path) -> str: - """Return one repository contract file as UTF-8 text.""" - - return path.read_text(encoding="utf-8") - - -def _yaml_path_entries(block: str) -> set[str]: - """Return dashed YAML path entries from one trigger or compileall block.""" - - entries: set[str] = set() - for raw_line in block.splitlines(): - stripped = raw_line.strip() - if stripped.startswith("- "): - entries.add(stripped[2:].strip()) - elif stripped.startswith("tests/") or stripped.startswith("scripts/"): - entries.add(stripped.rstrip(" \\")) - return entries - - -def _trigger_path_block(quality: str, trigger: str) -> str: - """Return the dashed path list under one named workflow trigger.""" - - marker = f" {trigger}:\n paths:\n" - start = quality.index(marker) + len(marker) - lines: list[str] = [] - for line in quality[start:].splitlines(): - if line.startswith(" - "): - lines.append(line) - continue - if line.strip() == "": - continue - break - return "\n".join(lines) - - -def _compileall_block(quality: str) -> str: - """Return the compileall argument list from the focused quality job.""" - - marker = "python -m compileall -q \\" - start = quality.index(marker) - remainder = quality[start:] - end = remainder.find("\n git ") - return remainder if end < 0 else remainder[:end] - - -def test_caller_is_hourly_bounded_and_non_cancelling() -> None: - """The sandbox receives one bounded security repair without cancellation.""" - - caller = _read(CALLER) - - assert 'cron: "14 * * * *"' in caller - assert "group: quarantine-sandbox-hourly-review-repair" in caller - assert "cancel-in-progress: false" in caller - assert "uses: ./.github/workflows/pr-review-fix-scheduler.yml" in caller - assert "target_repository: ContextualWisdomLab/quarantine-sandbox-runtime" in caller - assert "base_branch: develop" in caller - assert 'max_prs: "50"' in caller - assert 'max_dispatches: "1"' in caller - assert 'retry_hours: "2"' in caller - - -def test_caller_preserves_oidc_and_explicit_secret_scope() -> None: - """The queue scanner maps scheduler credentials without model secrets.""" - - caller = _read(CALLER) - workflow_scope, jobs_scope = caller.split("\njobs:\n", maxsplit=1) - - assert "\npermissions:\n contents: read\n" in workflow_scope - assert ( - "\n permissions:\n contents: read\n id-token: write\n" - in jobs_scope - ) - assert "PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}" in caller - assert "OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }}" in caller - assert "secrets: inherit" not in caller - assert "NVIDIA_NIM_API_KEY" not in caller - assert "COPILOT_GITHUB_TOKEN" not in caller - for forbidden in ( - "actions: write", - "contents: write", - "issues: write", - "pull-requests: write", - "statuses: write", - ): - assert forbidden not in caller - - -def test_target_is_not_hard_coded_in_shared_scheduler() -> None: - """Product identity remains in the thin caller rather than the engine.""" - - assert "ContextualWisdomLab/quarantine-sandbox-runtime" not in _read(SCHEDULER) - - -def test_doctoring_records_security_boundary_and_activation_contract() -> None: - """Operators retain exact target, authority, and activation prerequisites.""" - - doctoring = _read(DOCTORING) - - for phrase in ( - "ContextualWisdomLab/quarantine-sandbox-runtime", - "OPENCODE_REPOSITORY_DISPATCH_TARGETS", - "independent non-author approval", - "NVIDIA_NIM_API_KEY", - "COPILOT_GITHUB_TOKEN", - "id-token: write", - "two-hour same-head retry floor", - "root-cause analysis", - "remediation feasibility", - "protected-main operational acceptance", - "artifact-analysis evidence", - "Wardnet owns WAF/IDS", - "Naruon owns email admission", - "APA 7th references", - ): - assert phrase in doctoring - - -def test_path_helpers_keep_trigger_and_compileall_sets_disjoint() -> None: - """A path listed only under push or compileall must not satisfy PR coverage.""" - - quality = ( - "on:\n" - " pull_request:\n" - " paths:\n" - " - .github/workflows/quarantine-sandbox-hourly-review-repair.yml\n" - " push:\n" - " paths:\n" - " - docs/doctoring/quarantine-sandbox-hourly-review-caller.md\n" - " python -m compileall -q \\\n" - " tests/test_quarantine_sandbox_hourly_review_caller.py\n" - " git diff --check\n" - ) - - pull_request_paths = _yaml_path_entries(_trigger_path_block(quality, "pull_request")) - push_paths = _yaml_path_entries(_trigger_path_block(quality, "push")) - compileall_paths = _yaml_path_entries(_compileall_block(quality)) - - assert pull_request_paths == { - ".github/workflows/quarantine-sandbox-hourly-review-repair.yml" - } - assert push_paths == { - "docs/doctoring/quarantine-sandbox-hourly-review-caller.md" - } - assert compileall_paths == { - "tests/test_quarantine_sandbox_hourly_review_caller.py" - } - - -def test_focused_quality_workflow_tracks_sandbox_contracts() -> None: - """Caller, test, and doctoring edits always rerun the focused gate.""" - - quality = _read(QUALITY_WORKFLOW) - pull_request_paths = _yaml_path_entries(_trigger_path_block(quality, "pull_request")) - push_paths = _yaml_path_entries(_trigger_path_block(quality, "push")) - compileall_paths = _yaml_path_entries(_compileall_block(quality)) - caller = ".github/workflows/quarantine-sandbox-hourly-review-repair.yml" - doctoring = "docs/doctoring/quarantine-sandbox-hourly-review-caller.md" - contract = "tests/test_quarantine_sandbox_hourly_review_caller.py" - - assert caller in pull_request_paths - assert doctoring in pull_request_paths - assert contract in pull_request_paths - assert caller in push_paths - assert doctoring in push_paths - assert contract in push_paths - assert contract in compileall_paths - assert caller not in compileall_paths - assert doctoring not in compileall_paths diff --git a/tests/test_required_workflow_queue_contract.py b/tests/test_required_workflow_queue_contract.py index 535fd513a..233c08584 100644 --- a/tests/test_required_workflow_queue_contract.py +++ b/tests/test_required_workflow_queue_contract.py @@ -673,19 +673,6 @@ def test_org_queue_sweep_covers_target_repositories_on_a_heartbeat() -> None: assert "Could not cancel superseded run" in workflow assert "No run will be cancelled from incomplete evidence" in workflow assert "queue_hygiene_ready=false" in workflow - # Organization sweep budgets must be consumed across the repository loop; - # resetting the configured limit for every target can flood Actions with - # long-running review dispatches. - assert '"$ORG_SWEEP_REVIEW_DISPATCH_LIMIT" =~ ^(-1|[0-9]+)$' in workflow - assert '"$ORG_SWEEP_BRANCH_UPDATE_LIMIT" =~ ^(-1|[0-9]+)$' in workflow - assert "org_review_dispatches_used=0" in workflow - assert "org_branch_updates_used=0" in workflow - assert 'review_dispatch_limit=$((ORG_SWEEP_REVIEW_DISPATCH_LIMIT - org_review_dispatches_used))' in workflow - assert 'branch_update_limit=$((ORG_SWEEP_BRANCH_UPDATE_LIMIT - org_branch_updates_used))' in workflow - assert '--review-dispatch-limit "$review_dispatch_limit"' in workflow - assert '--branch-update-limit "$branch_update_limit"' in workflow - assert 'grep -Ec \'^PR #[0-9]+: (review_dispatch|security_dispatch):\'' in workflow - assert 'grep -Ec \'^PR #[0-9]+: (update_branch|restamp_head):\'' in workflow # The scheduler requires --project-flow; the sweep must derive and pass it # per target repository (regression: the first sweep failed every repo with # "--project-flow is required"). diff --git a/tests/test_sandboxed_web_e2e.py b/tests/test_sandboxed_web_e2e.py index 6e092c293..a51051f27 100644 --- a/tests/test_sandboxed_web_e2e.py +++ b/tests/test_sandboxed_web_e2e.py @@ -181,13 +181,13 @@ def fake_run(*args, **kwargs): assert service.command == "npm run dev" assert service.log_path == tmp_path / "backend.log" assert popen_calls[0][0] == (["npm", "run", "dev"],) - assert "shell" not in popen_calls[0][1] + assert popen_calls[0][1].get("shell") is False assert "executable" not in popen_calls[0][1] assert popen_calls[0][1]["start_new_session"] is True assert completed.returncode == 7 assert run_calls[0][0] == (["npm", "test"],) assert run_calls[0][1]["timeout"] == 5 - assert "shell" not in run_calls[0][1] + assert run_calls[0][1].get("shell") is False assert "executable" not in run_calls[0][1]