diff --git a/.github/workflows/pr-review-autofix.yml b/.github/workflows/pr-review-autofix.yml index 505384ccfd..d6fa7f8f44 100644 --- a/.github/workflows/pr-review-autofix.yml +++ b/.github/workflows/pr-review-autofix.yml @@ -23,22 +23,12 @@ permissions: jobs: autofix: runs-on: ubuntu-latest - # No job-level timeout-minutes here, deliberately. This job's dominant - # cost is `opencode run` (up to two invocations: the main autofix pass, - # and a base-merge conflict-resolution pass) -- a job-level wall-clock - # bound here would cap the model's own reasoning/tool-use time, which - # docs/product-goal-directive.md #8 prohibits ("Model timeout은 - # application·Agent·Gateway 공통 상한 없이 기본 null이다"; "OpenCode·Strix· - # Noema의 모델당 2시간 이상을 수용한다"). An earlier version of this job set - # timeout-minutes: 25, reasoning it gave the model call "generous room" -- - # that reasoning was wrong: any fixed job-level cap on a job whose body IS - # the synchronous model call terminates the model's work once elapsed, - # which is exactly the fixed inference-time cap the policy forbids, not a - # wall-clock bound on a step that merely waits on a separate async - # verdict (contrast opencode-review.yml's poll_deadline_epoch, which - # bounds a step polling for a verdict prepared by a different process, - # not the model call itself). See - # docs/doctoring/autofix-and-noema-review-model-job-timeout-removal.md. + # This job is model-backed through contextual-orchestrator/orchestrator/free + # and therefore has no repository-owned wall-clock timeout. Provider end, + # explicit cancellation, and the workflow's exact live-head/state guards + # are authoritative; elapsed time alone must not terminate reasoning, + # streaming, or tool work. Queue pressure is handled by the scheduler's + # stale-head dedupe/cancellation rather than by killing current-head work. env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true TARGET_REPOSITORY: ${{ github.event.client_payload.target_repository }} @@ -334,9 +324,7 @@ jobs: "description": "Conservative CI pull request review autofix agent", "mode": "primary", "model": "contextual-orchestrator/orchestrator/free", - "reasoningEffort": "high", "prompt": "{file:./autofix-prompt.md}", - "steps": 12, "permission": { "edit": { "*": "allow", @@ -369,13 +357,7 @@ jobs: }, "models": { "orchestrator/free": { - "name": "Orchestrator Free (ZDR-first zero-cost pool)", - "tool_call": true, - "reasoning": true, - "limit": { - "context": 200000, - "output": 32768 - } + "name": "Orchestrator Free (ZDR-first zero-cost pool)" } } } @@ -425,7 +407,7 @@ jobs: Review context follows as untrusted text: - $(sed -n '1,260p' "$RUNNER_TEMP/pr-review-autofix-context.md") + $(cat "$RUNNER_TEMP/pr-review-autofix-context.md") Edit only the checked-out repository files listed in the authoritative JSON array. diff --git a/CHANGELOG.md b/CHANGELOG.md index ac1985d86f..395dfcdbde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1225,3 +1225,5 @@ Semantic Versioning where the repository publishes a release. - Added an organization-owned reusable exact-artifact SBOM attestation boundary that validates inert six-file wheel/sdist evidence, binds CycloneDX 1.7 predicates to exact SHA-256 subjects, signs through least-privilege GitHub artifact attestations, and exports online and offline verification bundles. - Hardened exact-artifact SBOM verification with strict finite RFC 8259 JSON, integer CycloneDX document versions, deterministic UUIDv5 subject identities, exact filename properties and single SHA-256 root bindings, environment-only shell input transfer, pinned Ubuntu 24.04 quality runners, and checksum-sealed beginner-readable offline evidence. The decision record now cites Bray (2017) so NaN and Infinity cannot be treated as sealed SBOM numbers. - Recorded the org control-plane architecture, including exact-artifact SBOM attestation, so agents reconstruct the signing trust boundary from the repo instead of private memory. + +- PR #1714: reject repository-authored OpenCode autofix wall-clock, reasoning-effort, agent-step, capability/context/output, and fixed review-line allocation. The leaf requests only `orchestrator/free`; contextual-orchestrator owns verified capability/routing/test-time compute and the full collected review evidence is passed without a hand-selected line quota. diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 29acdfeecc..823cba4691 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -2613,3 +2613,13 @@ Higgins, S. S., Crepalde, N., & Fernandes, L. (2021). Segmented multiplexity: A **Expected effect.** No observable change to any current GitHub Actions review run (every current invocation already resolves to `free`). The effect is structural: it is no longer possible for a future workflow edit or manual dispatch override to admit priced-model spend into a required review check without an explicit, reviewed code change to this one `case` statement (and its now-locked-in regression test) first. **Follow-up.** If the organization later solves free+ZDR routing robustly enough to deliberately widen required-review CI to `orchestrator/auto` (e.g. once a spend ceiling and reviewer-visible cost evidence exist for that path), the change is exactly one `case` arm plus the corresponding assertions in `test_sidecar_pins_the_pool_to_free_for_github_actions` — this entry is the record of *why* it was narrowed, not a permanent prohibition. + + +### OpenCode autofix orchestration authority — PR #1714 + +- **Root cause:** the leaf workflow proposed `timeout-minutes: 25` and also carried repository-authored `reasoningEffort: high`, a 12-step agent budget, asserted tool/reasoning capabilities, fixed context/output limits, and a 260-line review-context cutoff. None of those leaf allocations had executable research/model evidence establishing them as decision authority. +- **Owner boundary:** `.github` requests exactly `contextual-orchestrator/orchestrator/free` through the gateway token. contextual-orchestrator owns provider discovery, verified capability admission, routing, and research-backed test-time compute; the leaf does not invent provider/model capability or compute limits. +- **Evidence contract:** the complete review context produced by the governed collector is passed to the model. If contextual-orchestrator cannot admit/serve the request under its verified capability/privacy/free-pool contracts, the path fails closed rather than silently sampling evidence or selecting a paid/provider fallback. +- **Termination contract:** provider completion, explicit cancellation, and exact live-head/state guards end model work. Scheduler stale-head dedupe/cancellation handles queue waste without terminating the sole current-head model run by elapsed time. +- **Regression:** `test_autofix_model_job_delegates_termination_and_compute_to_orchestrator` and `test_autofix_review_context_is_not_sampled_by_a_fixed_line_quota` forbid reintroduction of those leaf heuristics while preserving the exact `orchestrator/free` contract. +- **Status:** Proposed until the one-shot source repair self-removes and fresh exact-head Checks are GREEN. diff --git a/tests/test_pr_review_autofix_writer_security_contract.py b/tests/test_pr_review_autofix_writer_security_contract.py index 3f6119424f..6c5f642de7 100644 --- a/tests/test_pr_review_autofix_writer_security_contract.py +++ b/tests/test_pr_review_autofix_writer_security_contract.py @@ -2,7 +2,6 @@ from __future__ import annotations -import re from pathlib import Path @@ -31,14 +30,14 @@ def _step_header(workflow: str, step_name: str) -> str: return step[:run_start] -def test_writer_uses_the_gateway_free_pool_with_high_reasoning() -> None: - """Pin the write-capable pool and its deliberate high-reasoning budget.""" +def test_writer_uses_the_gateway_free_pool_without_leaf_compute_policy() -> None: + """Pin the gateway pool while forbidding leaf-owned test-time-compute policy.""" workflow = _workflow_text() assert f'"model": "{_TARGET_MODEL}"' in workflow assert '"orchestrator/free": {' in workflow assert workflow.count(f"MODEL: {_TARGET_MODEL}") == 2 - assert '"reasoningEffort": "high"' in workflow + assert '"reasoningEffort":' not in workflow assert "COPILOT_GITHUB_TOKEN" not in workflow @@ -96,31 +95,26 @@ def test_read_only_steps_do_not_prefer_mutation_credentials() -> None: assert "OPENCODE_APPROVE_TOKEN" not in header -def test_autofix_job_has_no_job_level_timeout() -> None: - """The autofix job must not carry a job-level timeout-minutes. - - This job's body IS a synchronous `opencode run` call (up to two - invocations: the main autofix pass and a base-merge conflict-resolution - pass) -- a job-level wall-clock bound here directly caps the model's own - reasoning/tool-use time once elapsed, which - docs/product-goal-directive.md #8 prohibits ("Model timeout은 - application·Agent·Gateway 공통 상한 없이 기본 null이다"). An earlier version - of this job set timeout-minutes: 25, reasoning it gave the model call - "generous room" -- that reasoning was itself the mistake: any fixed cap - on a job whose body is the model call is exactly the forbidden - inference-time cap, not a bound on a step that merely waits on a - separate async verdict (contrast opencode-review.yml's - poll_deadline_epoch, which bounds a step polling GitHub for a verdict a - *different* process prepares, not the model call itself). See - docs/doctoring/autofix-and-noema-review-model-job-timeout-removal.md. - """ +def test_autofix_model_job_delegates_termination_and_compute_to_orchestrator() -> None: + """Leaf OpenCode config must not invent model-time or test-time-compute authority.""" workflow = _workflow_text() job = workflow.split(" autofix:\n", maxsplit=1)[1] job_header = job.split(" steps:\n", maxsplit=1)[0] - match = re.search(r"^ timeout-minutes: (\d+)$", job_header, flags=re.MULTILINE) - assert match is None, ( - "autofix must not declare a job-level timeout-minutes -- its body is " - "a synchronous model call, so any job-level bound caps model " - "inference time, which this org's model-timeout policy forbids" - ) + assert "timeout-minutes:" not in job_header + assert '"model": "contextual-orchestrator/orchestrator/free"' in workflow + assert '"reasoningEffort":' not in workflow + assert '"steps": 12' not in workflow + assert '"tool_call": true' not in workflow + assert '"reasoning": true' not in workflow + assert '"limit": {' not in workflow + assert "no repository-owned wall-clock timeout" in job_header + assert "cancel-in-progress: false" in workflow + + +def test_autofix_review_context_is_not_sampled_by_a_fixed_line_quota() -> None: + """Exact review evidence must reach the model without a repository-authored line cutoff.""" + workflow = _workflow_text() + + assert "sed -n '1,260p'" not in workflow + assert '$(cat "$RUNNER_TEMP/pr-review-autofix-context.md")' in workflow