From 172154ea6da582b0fcb18adf9b7e6eafe194f349 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 9 Aug 2026 10:50:50 +0000 Subject: [PATCH 01/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ci/assert_opencode_reasoning_effort.py | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/scripts/ci/assert_opencode_reasoning_effort.py b/scripts/ci/assert_opencode_reasoning_effort.py index cee898619..ea4bd0adb 100644 --- a/scripts/ci/assert_opencode_reasoning_effort.py +++ b/scripts/ci/assert_opencode_reasoning_effort.py @@ -47,39 +47,29 @@ def validate_candidate(config: dict[str, Any], candidate: str) -> list[str]: except ValueError as exc: return [str(exc)] - if not config_for_model and ( - provider == "github-models" or is_known_reasoning_capable(model_name) - ): - return [ - f"OpenCode candidate {candidate} is not defined in opencode.jsonc " - f"under provider {provider}." - ] if not config_for_model: + if provider == "github-models" or is_known_reasoning_capable(model_name): + return [ + f"OpenCode candidate {candidate} is not defined in opencode.jsonc " + f"under provider {provider}." + ] return [] configured_reasoning = config_for_model.get("reasoning") is True - should_require_effort = configured_reasoning or is_known_reasoning_capable(model_name) - if not should_require_effort: + if not (configured_reasoning or is_known_reasoning_capable(model_name)): return [] errors: list[str] = [] + prefix = f"OpenCode reasoning-capable candidate {candidate} must set" + suffix = "in opencode.jsonc." + if not configured_reasoning: - errors.append( - f"OpenCode reasoning-capable candidate {candidate} must set reasoning=true " - "in opencode.jsonc." - ) + errors.append(f"{prefix} reasoning=true {suffix}") if (config_for_model.get("options") or {}).get("reasoningEffort") != "high": - errors.append( - f"OpenCode reasoning-capable candidate {candidate} must set " - "options.reasoningEffort=high in opencode.jsonc." - ) - if ((config_for_model.get("variants") or {}).get("high") or {}).get( - "reasoningEffort" - ) != "high": - errors.append( - f"OpenCode reasoning-capable candidate {candidate} must set " - "variants.high.reasoningEffort=high in opencode.jsonc." - ) + errors.append(f"{prefix} options.reasoningEffort=high {suffix}") + if ((config_for_model.get("variants") or {}).get("high") or {}).get("reasoningEffort") != "high": + errors.append(f"{prefix} variants.high.reasoningEffort=high {suffix}") + return errors From 2733ac752ba9659ffe66c1eff1fb0227a5233241 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 9 Aug 2026 12:43:33 +0000 Subject: [PATCH 02/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From f5da593565e888a8280b96ef52ce9d88b5a9954f Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 9 Aug 2026 15:16:16 +0000 Subject: [PATCH 03/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 5ab30352fc93b91b097119b0c2809b3c9571fcfd Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 11 Aug 2026 07:12:17 +0000 Subject: [PATCH 04/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 21b3c1e39520743427b63954aab3dd27d012db69 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 11 Aug 2026 07:34:23 +0000 Subject: [PATCH 05/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 630373e80b8e6cfe84989b6e98bb643b52f708f8 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 11 Aug 2026 08:12:42 +0000 Subject: [PATCH 06/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 4122456466da134a8eaac9263fb7ef3fcf9e4867 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 11 Aug 2026 09:07:36 +0000 Subject: [PATCH 07/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 492cb3779549843c24388008567a0f640d81787e Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:16:01 +0000 Subject: [PATCH 08/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From ea5dceb60836aaf42a8357b4d5c602f996f389b5 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:28:29 +0000 Subject: [PATCH 09/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 53a0458568c66fc8f755cac4881f2da23578caf6 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:26:16 +0000 Subject: [PATCH 10/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 38cea4c8bc7d4d038320e2aaaa1ff444a313687e Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:59:50 +0000 Subject: [PATCH 11/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From f996ee3ed36c6229d34b2190af8378c071910351 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 13:29:26 +0900 Subject: [PATCH 12/18] docs(opencode): cite Fowler extract-variable reasoning messages Keep installer tests on the documented linux x86_64 path, record the reasoning-effort gate, and cite Fowler plus ISO/IEC 25010 so the three fail-closed checks cannot drift. --- ARCHITECTURE.md | 92 +++++++++++++++++++ CHANGELOG.md | 2 + CLAUDE.md | 4 +- ...opencode-reasoning-effort-message-shape.md | 26 ++++++ ...st_materialize_base_python_requirements.py | 10 ++ 5 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 ARCHITECTURE.md create mode 100644 docs/doctoring/opencode-reasoning-effort-message-shape.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 000000000..e3524c105 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,92 @@ +# Architecture — ContextualWisdomLab `.github` + +This repository is the organization control plane. It is not naruon and it +does not own product data. Sibling products remain standalone modules; this +repo publishes org profile assets, reusable required workflows, and the +review/merge schedulers those products consume. + +## System context + +```mermaid +flowchart LR + Buyer["Commercial buyer / reviewer"] + Agents["Agents on AGENTS.md"] + Project["GitHub Project #1"] + Hub["This repo: org .github"] + Products["Owned products
naruon · orchestrator · engines"] + Runner["Required workflows in each repo context"] + + Buyer --> Hub + Agents --> Project + Agents --> Hub + Project --> Hub + Hub --> Runner + Runner --> Products + Products -->|"standalone or as module"| Buyer +``` + +## Reasoning-effort gate + +```mermaid +flowchart TD + Candidate["provider/model candidate"] + Known{"defined and reasoning-capable?"} + Checks["reasoning=true + effort=high + variant high"] + Ok["no errors"] + Err["stable fail-closed messages"] + + Candidate --> Known + Known -->|"no"| Ok + Known -->|"yes"| Checks + Checks -->|"all set"| Ok + Checks -->|"any missing"| Err +``` + +## Control-plane data flow + +```mermaid +sequenceDiagram + participant PR as Pull request + participant RW as Required workflows + participant OC as OpenCode reviewer + participant SV as sandboxed_verify / web E2E + participant MS as Merge scheduler + + PR->>RW: pull_request_target on trusted base + RW->>OC: bounded evidence + NVIDIA NIM / OpenCode + OC->>SV: PoC command in isolated copy + SV-->>OC: redacted stdout/stderr + command metadata + OC-->>PR: APPROVE or request changes + MS->>PR: merge only on current-head approval + green checks +``` + +## Trust boundaries + +- Required review workflows execute **base-branch** scripts. A PR that edits + those workflows cannot widen its own `pull_request_target` token. +- Reviewer agents stay `edit: deny`. They judge; they do not implement. +- Sandbox helpers copy the workspace, drop secret environment values unless + explicitly allowlisted by **name**, and run subprocesses with `shell=False`. +- Logs and review receipts redact credential shapes (tokens, bearer values, + known provider prefixes). They do not mask operational PII that the + control plane must process. +- LLM and scheduled agents bind `NVIDIA_NIM_API_KEY` (env may be + `NVIDIA_API_KEY`). They never use `COPILOT_GITHUB_TOKEN`. Existing + review-agent key schemes stay unchanged. + +## Quality gates + +`scripts/ci/` ships with 100% statement/branch coverage and 100% docstrings. +CI installs Python tools only with `pip install --require-hashes`. Contract +tests pin workflow structure and governance prose so drift fails closed. + +## Related durable documents + +- [`docs/CWL-MASTER-CONTEXT.md`](docs/CWL-MASTER-CONTEXT.md) — mission and + ecosystem. +- [`docs/agent-github-project-protocol.md`](docs/agent-github-project-protocol.md) + — Project #1 operation. +- [`PR_GOVERNANCE_AUDIT.md`](PR_GOVERNANCE_AUDIT.md) — live review/merge + contract. +- [`docs/doctoring/opencode-reasoning-effort-message-shape.md`](docs/doctoring/opencode-reasoning-effort-message-shape.md) + — current increment's Extract Variable decision and APA 7th citations. diff --git a/CHANGELOG.md b/CHANGELOG.md index bf30091dd..1de35731f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,11 @@ Semantic Versioning where the repository publishes a release. - 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. +- Recorded the org control-plane architecture, including the reasoning-effort gate, so agents reconstruct the trust boundary from the repo instead of private memory. ### Fixed +- Extracted the shared OpenCode reasoning-effort error prefix and suffix so the three fail-closed checks cannot drift while the candidate contract stays unchanged. - Bounded the Strix quality self-test's deterministic timeout fixtures to 3-second process and 5-second fake-sleep budgets so exact-head policy evidence completes inside the existing job limit without changing production Strix scanner timeouts, providers, credentials, or review semantics. - Allowed commas and ASCII parentheses in the bounded Strix changed-file path policy so legal tracked Packrat fixtures can receive exact-head security analysis, while rejecting raw `..` components before normalization and keeping controls, backslashes, whitespace ambiguity, and shell punctuation fail-closed. - Bound each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped. diff --git a/CLAUDE.md b/CLAUDE.md index 1c7bdb2f6..362e6fa92 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -64,7 +64,9 @@ Details: `README.md` and `PR_GOVERNANCE_AUDIT.md`. - `fuzz/` + `.clusterfuzzlite/` — Atheris fuzz targets for the review-output normalizer and the ClusterFuzzLite discovery marker. - `docs/` — master context, Project protocol, `org-required-workflow-rollout.md`, - `scorecard-governance.md`, SBOM inventory. + `scorecard-governance.md`, SBOM inventory. Doctoring records live under + `docs/doctoring/`. [`ARCHITECTURE.md`](ARCHITECTURE.md) is the control-plane + diagram for review, reasoning-effort validation, and merge trust boundaries. - `.jules/` — recorded performance (`bolt.md`) and security (`sentinel.md`) learnings from past work on `scripts/ci/`; worth scanning before optimizing or hardening those scripts. diff --git a/docs/doctoring/opencode-reasoning-effort-message-shape.md b/docs/doctoring/opencode-reasoning-effort-message-shape.md new file mode 100644 index 000000000..b0646e280 --- /dev/null +++ b/docs/doctoring/opencode-reasoning-effort-message-shape.md @@ -0,0 +1,26 @@ +# OpenCode reasoning-effort validation message shape + +## Decision + +`validate_candidate` still requires reasoning-capable models to declare +`reasoning=true`, `options.reasoningEffort=high`, and +`variants.high.reasoningEffort=high`. The repair only extracts the shared +error prefix and suffix so those three checks cannot drift apart. + +Fowler (2018) records Extract Variable as the smallest way to keep repeated +literal fragments identical. ISO/IEC 25010:2023 treats analysability and +modifiability as maintainability characteristics (International Organization +for Standardization, 2023). The extracted fragments do not change the +fail-closed contract: an undefined GitHub Models or known-reasoning +candidate still errors, and a non-reasoning model without `reasoning=true` +still passes. + +## References + +Fowler, M. (2018). *Refactoring: Improving the design of existing code* +(2nd ed.). Addison-Wesley. + +International Organization for Standardization. (2023). *Systems and +software engineering—Systems and software Quality Requirements and +Evaluation (SQuaRE)—Product quality model* (ISO/IEC 25010:2023). +https://www.iso.org/standard/78176.html diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 8a383f0c2..10f682b3e 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -30,6 +30,13 @@ def _created_tool_directory(path: Path) -> str: return str(path) +def _force_linux_x86_64_installer(monkeypatch: pytest.MonkeyPatch) -> None: + """Exercise the installer path that GitHub-hosted linux x86_64 runners use.""" + monkeypatch.setattr(materializer.sys, "platform", "linux") + monkeypatch.setattr(materializer.platform, "machine", lambda: "x86_64") + materializer._install_trusted_uv.cache_clear() + + def test_materializes_only_regular_hash_locks_from_exact_base(tmp_path: Path) -> None: """A PR-modified lock cannot enter the networked coverage image build context.""" repo = tmp_path / "repo" @@ -644,6 +651,7 @@ def test_install_trusted_uv_verifies_version_and_caches_path( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """The installer writes one executable, verifies its version, and caches it.""" + _force_linux_x86_64_installer(monkeypatch) tool_dir = tmp_path / "uv" monkeypatch.setattr( materializer.tempfile, @@ -690,6 +698,7 @@ def test_install_trusted_uv_rejects_version_process_failures( failure: OSError | subprocess.TimeoutExpired, ) -> None: """A missing or hung downloaded executable is removed and rejected.""" + _force_linux_x86_64_installer(monkeypatch) tool_dir = tmp_path / "uv" monkeypatch.setattr( materializer.tempfile, @@ -721,6 +730,7 @@ def test_install_trusted_uv_rejects_wrong_version_or_exit_status( completed: subprocess.CompletedProcess[bytes], ) -> None: """Unexpected version output or a nonzero status cannot satisfy the pin.""" + _force_linux_x86_64_installer(monkeypatch) tool_dir = tmp_path / f"uv-{completed.returncode}-{len(completed.stdout)}" monkeypatch.setattr( materializer.tempfile, From c1cc55ad1dd0ad2810d8ac59a08aeb7440ffd4c2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 01:19:37 +0900 Subject: [PATCH 13/18] fix(coverage): accept only bounded relative requirement includes Materialize a base Python lock only when every package line is an exact SHA-256 pin or a two-token relative -r/--requirement include of a candidate lock path. A lone --require-hashes directive, ./dotted paths, and -r other-hashes.txt no longer enter the trusted build context. --- AGENTS.md | 2 + CHANGELOG.md | 1 + ...opencode-reasoning-effort-message-shape.md | 2 + .../materialize_base_python_requirements.py | 85 +++++++++++++++---- ...st_materialize_base_python_requirements.py | 19 ++++- 5 files changed, 91 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 688b33035..be64e8585 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,3 +2,5 @@ > **Agents: read the master context FIRST.** Before any work, read [`docs/CWL-MASTER-CONTEXT.md`](docs/CWL-MASTER-CONTEXT.md) (mission · naruon-as-platform + inter-component UML · cross-cutting disciplines · conventions · roadmap · current state), the live **GitHub Project #1** (work/roadmap source of truth), the full spec **ContextualWisdomLab/naruon#974**, and operate the Project per [`docs/agent-github-project-protocol.md`](docs/agent-github-project-protocol.md). The repo/Project — not any private agent memory — is the source of truth. + +Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include (no `.`/`..`); a lone `--require-hashes` directive is not trust evidence. See [`docs/doctoring/opencode-reasoning-effort-message-shape.md`](docs/doctoring/opencode-reasoning-effort-message-shape.md). diff --git a/CHANGELOG.md b/CHANGELOG.md index 1de35731f..6f3aa0140 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Semantic Versioning where the repository publishes a release. ### Fixed +- Materialized base Python locks only when every package line is an exact SHA-256 pin or a bounded relative `-r`/`--requirement` include. A lone `--require-hashes` directive, a dotted include such as `./lock.txt`, or `-r other-hashes.txt` no longer enters the trusted build context. - Extracted the shared OpenCode reasoning-effort error prefix and suffix so the three fail-closed checks cannot drift while the candidate contract stays unchanged. - Bounded the Strix quality self-test's deterministic timeout fixtures to 3-second process and 5-second fake-sleep budgets so exact-head policy evidence completes inside the existing job limit without changing production Strix scanner timeouts, providers, credentials, or review semantics. - Allowed commas and ASCII parentheses in the bounded Strix changed-file path policy so legal tracked Packrat fixtures can receive exact-head security analysis, while rejecting raw `..` components before normalization and keeping controls, backslashes, whitespace ambiguity, and shell punctuation fail-closed. diff --git a/docs/doctoring/opencode-reasoning-effort-message-shape.md b/docs/doctoring/opencode-reasoning-effort-message-shape.md index b0646e280..ef54a4616 100644 --- a/docs/doctoring/opencode-reasoning-effort-message-shape.md +++ b/docs/doctoring/opencode-reasoning-effort-message-shape.md @@ -2,6 +2,8 @@ ## Decision +Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include; a lone `--require-hashes` line is not lock evidence. + `validate_candidate` still requires reasoning-capable models to declare `reasoning=true`, `options.reasoningEffort=high`, and `variants.high.reasoningEffort=high`. The repair only extracts the shared diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 98cdad459..7a9c204b8 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -87,6 +87,58 @@ def _is_candidate_lock_name(name: str) -> bool: ) + +def _is_candidate_lock_path(path: pathlib.PurePosixPath) -> bool: + """Return whether one safe tracked path can name a pip requirements lock. + + In addition to conventional ``requirements*.txt`` names, repositories often + keep concrete environment closures as direct children such as + ``requirements/ci.txt`` or ``service/requirements/package.txt``. Only direct + ``.txt`` children of a directory named ``requirements`` gain this path-based + eligibility; content must still pass the independent complete hash-pin + validation before it reaches the trusted image build context. + """ + return _is_candidate_lock_name(path.name) or ( + path.suffix == ".txt" and path.parent.name == "requirements" + ) + + +def _is_bounded_requirement_include(line: str) -> bool: + """Return whether one requirements include names a bounded relative file. + + Includes are accepted only as a two-token ``-r``/``--requirement`` form + whose target is itself a candidate lock path written as a normalized + relative POSIX path. Absolute paths, ``.`` or ``..`` components, double + slashes, URLs, option-like targets, shell/Windows path separators, + fragments, queries, extra inline options or hashes, and includes of + non-lock files are rejected before a base-owned file can enter the + trusted build context. + The downstream installer still proves that the candidate is an independently + complete hash closure; this predicate grants syntax eligibility only. + """ + fields = line.split() + if len(fields) != 2 or fields[0] not in {"-r", "--requirement"}: + return False + target = fields[1] + if ( + target.startswith(("-", "~")) + or "\\" in target + or ":" in target + or "?" in target + or "#" in target + ): + return False + include_path = pathlib.PurePosixPath(target) + return ( + bool(include_path.parts) + and target == include_path.as_posix() + and not include_path.is_absolute() + and "." not in include_path.parts + and ".." not in include_path.parts + and _is_candidate_lock_path(include_path) + ) + + def _requirement_lines(content: bytes) -> list[str]: """Return logical requirement lines, joining backslash line-continuations. @@ -107,26 +159,27 @@ def _requirement_lines(content: bytes) -> list[str]: def _is_hash_pinned(content: bytes) -> bool: - """Return whether content carries hash pins and is safe to preflight. - - Discovery is content-based rather than name-based so hash-pinned locks in any - location (a service subdirectory, ``requirements-dev.txt``, - ``requirements-test.txt``) can be considered for offline coverage, while an - unpinned or PR-mutable requirements file is still excluded from the networked - build context. Hash syntax cannot prove that a file includes every transitive - dependency, so the trusted image installer separately preflights every - candidate as an independent ``--require-hashes`` closure. An empty file - carries no installable dependency and is not materialized. + """Return whether content carries only trusted pins or bounded includes. + + Discovery is content-based rather than name-based so exact hash-pinned locks + in service subdirectories and role-specific requirements files can be + considered for offline coverage. Candidate syntax is deliberately stricter + than a substring search: each package line must be an exact ``==`` pin with + one or more complete SHA-256 hashes, or a bounded relative requirements + include. A global ``--require-hashes`` directive is not trust evidence by + itself. The downstream installer separately preflights every candidate as an + independent ``pip --require-hashes`` closure, so syntax eligibility never + substitutes for dependency-closure proof. """ lines = _requirement_lines(content) - if not lines: + requirement_lines = [line for line in lines if line != "--require-hashes"] + if not requirement_lines: return False - return any(line == "--require-hashes" for line in lines) or all( - "--hash=" in line or line.startswith(("-r ", "--requirement ")) - for line in lines + return all( + _is_fully_hash_pinned_requirement(line) + or _is_bounded_requirement_include(line) + for line in requirement_lines ) - - def _is_fully_hash_pinned_requirement(line: str) -> bool: """Return whether one uv-export line is an exact package pin with SHA-256 hashes.""" fields = re.split(r"\s+(?=--hash=)", line) diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 10f682b3e..317ab5f5c 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -157,9 +157,24 @@ def test_lock_name_candidates_are_pip_requirements_files() -> None: def test_hash_pin_detection_includes_pinned_and_excludes_unpinned_or_empty() -> None: """Only fully hash-pinned, non-empty lock content is materialized.""" assert not materializer._is_hash_pinned(b"# comment only\n\n") - assert materializer._is_hash_pinned(b"--require-hashes\ndemo==1\n") + assert not materializer._is_hash_pinned(b"--require-hashes\ndemo==1\n") assert materializer._is_hash_pinned(b"demo==1 --hash=sha256:" + b"a" * 64 + b"\n") - assert materializer._is_hash_pinned(b"-r other-hashes.txt\n") + assert materializer._is_hash_pinned(b"-r requirements-other.txt\n") + assert not materializer._is_hash_pinned(b"-r other-hashes.txt\n") + assert not materializer._is_hash_pinned(b"-r ./requirements-other.txt\n") + assert not materializer._is_hash_pinned(b"-r ../escape.txt\n") + assert materializer._is_bounded_requirement_include( + "--requirement requirements-other.txt" + ) + assert not materializer._is_bounded_requirement_include("-r .") + assert not materializer._is_bounded_requirement_include("-r -evil.txt") + assert not materializer._is_bounded_requirement_include("-r ~evil.txt") + assert not materializer._is_bounded_requirement_include("-r C:foo.txt") + assert not materializer._is_bounded_requirement_include("-r foo?bar.txt") + assert not materializer._is_bounded_requirement_include("-r foo#bar.txt") + assert not materializer._is_bounded_requirement_include(r"-r foo\\bar.txt") + assert not materializer._is_bounded_requirement_include("-r") + assert not materializer._is_bounded_requirement_include("-r /abs/requirements.txt") assert not materializer._is_hash_pinned(b"untrusted==1\n") # uv export / pip-compile multi-line continuation format (spec, then --hash= lines). assert materializer._is_hash_pinned( From 089adfd9207f0537ec6959b9cdceef81129c11b9 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 13 Aug 2026 17:00:23 +0000 Subject: [PATCH 14/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 2 - ARCHITECTURE.md | 92 ------------------- CHANGELOG.md | 3 - CLAUDE.md | 4 +- ...opencode-reasoning-effort-message-shape.md | 28 ------ .../materialize_base_python_requirements.py | 85 ++++------------- ...st_materialize_base_python_requirements.py | 29 +----- 7 files changed, 19 insertions(+), 224 deletions(-) delete mode 100644 ARCHITECTURE.md delete mode 100644 docs/doctoring/opencode-reasoning-effort-message-shape.md diff --git a/AGENTS.md b/AGENTS.md index be64e8585..688b33035 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,5 +2,3 @@ > **Agents: read the master context FIRST.** Before any work, read [`docs/CWL-MASTER-CONTEXT.md`](docs/CWL-MASTER-CONTEXT.md) (mission · naruon-as-platform + inter-component UML · cross-cutting disciplines · conventions · roadmap · current state), the live **GitHub Project #1** (work/roadmap source of truth), the full spec **ContextualWisdomLab/naruon#974**, and operate the Project per [`docs/agent-github-project-protocol.md`](docs/agent-github-project-protocol.md). The repo/Project — not any private agent memory — is the source of truth. - -Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include (no `.`/`..`); a lone `--require-hashes` directive is not trust evidence. See [`docs/doctoring/opencode-reasoning-effort-message-shape.md`](docs/doctoring/opencode-reasoning-effort-message-shape.md). diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md deleted file mode 100644 index e3524c105..000000000 --- a/ARCHITECTURE.md +++ /dev/null @@ -1,92 +0,0 @@ -# Architecture — ContextualWisdomLab `.github` - -This repository is the organization control plane. It is not naruon and it -does not own product data. Sibling products remain standalone modules; this -repo publishes org profile assets, reusable required workflows, and the -review/merge schedulers those products consume. - -## System context - -```mermaid -flowchart LR - Buyer["Commercial buyer / reviewer"] - Agents["Agents on AGENTS.md"] - Project["GitHub Project #1"] - Hub["This repo: org .github"] - Products["Owned products
naruon · orchestrator · engines"] - Runner["Required workflows in each repo context"] - - Buyer --> Hub - Agents --> Project - Agents --> Hub - Project --> Hub - Hub --> Runner - Runner --> Products - Products -->|"standalone or as module"| Buyer -``` - -## Reasoning-effort gate - -```mermaid -flowchart TD - Candidate["provider/model candidate"] - Known{"defined and reasoning-capable?"} - Checks["reasoning=true + effort=high + variant high"] - Ok["no errors"] - Err["stable fail-closed messages"] - - Candidate --> Known - Known -->|"no"| Ok - Known -->|"yes"| Checks - Checks -->|"all set"| Ok - Checks -->|"any missing"| Err -``` - -## Control-plane data flow - -```mermaid -sequenceDiagram - participant PR as Pull request - participant RW as Required workflows - participant OC as OpenCode reviewer - participant SV as sandboxed_verify / web E2E - participant MS as Merge scheduler - - PR->>RW: pull_request_target on trusted base - RW->>OC: bounded evidence + NVIDIA NIM / OpenCode - OC->>SV: PoC command in isolated copy - SV-->>OC: redacted stdout/stderr + command metadata - OC-->>PR: APPROVE or request changes - MS->>PR: merge only on current-head approval + green checks -``` - -## Trust boundaries - -- Required review workflows execute **base-branch** scripts. A PR that edits - those workflows cannot widen its own `pull_request_target` token. -- Reviewer agents stay `edit: deny`. They judge; they do not implement. -- Sandbox helpers copy the workspace, drop secret environment values unless - explicitly allowlisted by **name**, and run subprocesses with `shell=False`. -- Logs and review receipts redact credential shapes (tokens, bearer values, - known provider prefixes). They do not mask operational PII that the - control plane must process. -- LLM and scheduled agents bind `NVIDIA_NIM_API_KEY` (env may be - `NVIDIA_API_KEY`). They never use `COPILOT_GITHUB_TOKEN`. Existing - review-agent key schemes stay unchanged. - -## Quality gates - -`scripts/ci/` ships with 100% statement/branch coverage and 100% docstrings. -CI installs Python tools only with `pip install --require-hashes`. Contract -tests pin workflow structure and governance prose so drift fails closed. - -## Related durable documents - -- [`docs/CWL-MASTER-CONTEXT.md`](docs/CWL-MASTER-CONTEXT.md) — mission and - ecosystem. -- [`docs/agent-github-project-protocol.md`](docs/agent-github-project-protocol.md) - — Project #1 operation. -- [`PR_GOVERNANCE_AUDIT.md`](PR_GOVERNANCE_AUDIT.md) — live review/merge - contract. -- [`docs/doctoring/opencode-reasoning-effort-message-shape.md`](docs/doctoring/opencode-reasoning-effort-message-shape.md) - — current increment's Extract Variable decision and APA 7th citations. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f3aa0140..bf30091dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,12 +10,9 @@ Semantic Versioning where the repository publishes a release. - 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. -- Recorded the org control-plane architecture, including the reasoning-effort gate, so agents reconstruct the trust boundary from the repo instead of private memory. ### Fixed -- Materialized base Python locks only when every package line is an exact SHA-256 pin or a bounded relative `-r`/`--requirement` include. A lone `--require-hashes` directive, a dotted include such as `./lock.txt`, or `-r other-hashes.txt` no longer enters the trusted build context. -- Extracted the shared OpenCode reasoning-effort error prefix and suffix so the three fail-closed checks cannot drift while the candidate contract stays unchanged. - Bounded the Strix quality self-test's deterministic timeout fixtures to 3-second process and 5-second fake-sleep budgets so exact-head policy evidence completes inside the existing job limit without changing production Strix scanner timeouts, providers, credentials, or review semantics. - Allowed commas and ASCII parentheses in the bounded Strix changed-file path policy so legal tracked Packrat fixtures can receive exact-head security analysis, while rejecting raw `..` components before normalization and keeping controls, backslashes, whitespace ambiguity, and shell punctuation fail-closed. - Bound each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped. diff --git a/CLAUDE.md b/CLAUDE.md index 362e6fa92..1c7bdb2f6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -64,9 +64,7 @@ Details: `README.md` and `PR_GOVERNANCE_AUDIT.md`. - `fuzz/` + `.clusterfuzzlite/` — Atheris fuzz targets for the review-output normalizer and the ClusterFuzzLite discovery marker. - `docs/` — master context, Project protocol, `org-required-workflow-rollout.md`, - `scorecard-governance.md`, SBOM inventory. Doctoring records live under - `docs/doctoring/`. [`ARCHITECTURE.md`](ARCHITECTURE.md) is the control-plane - diagram for review, reasoning-effort validation, and merge trust boundaries. + `scorecard-governance.md`, SBOM inventory. - `.jules/` — recorded performance (`bolt.md`) and security (`sentinel.md`) learnings from past work on `scripts/ci/`; worth scanning before optimizing or hardening those scripts. diff --git a/docs/doctoring/opencode-reasoning-effort-message-shape.md b/docs/doctoring/opencode-reasoning-effort-message-shape.md deleted file mode 100644 index ef54a4616..000000000 --- a/docs/doctoring/opencode-reasoning-effort-message-shape.md +++ /dev/null @@ -1,28 +0,0 @@ -# OpenCode reasoning-effort validation message shape - -## Decision - -Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include; a lone `--require-hashes` line is not lock evidence. - -`validate_candidate` still requires reasoning-capable models to declare -`reasoning=true`, `options.reasoningEffort=high`, and -`variants.high.reasoningEffort=high`. The repair only extracts the shared -error prefix and suffix so those three checks cannot drift apart. - -Fowler (2018) records Extract Variable as the smallest way to keep repeated -literal fragments identical. ISO/IEC 25010:2023 treats analysability and -modifiability as maintainability characteristics (International Organization -for Standardization, 2023). The extracted fragments do not change the -fail-closed contract: an undefined GitHub Models or known-reasoning -candidate still errors, and a non-reasoning model without `reasoning=true` -still passes. - -## References - -Fowler, M. (2018). *Refactoring: Improving the design of existing code* -(2nd ed.). Addison-Wesley. - -International Organization for Standardization. (2023). *Systems and -software engineering—Systems and software Quality Requirements and -Evaluation (SQuaRE)—Product quality model* (ISO/IEC 25010:2023). -https://www.iso.org/standard/78176.html diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 7a9c204b8..98cdad459 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -87,58 +87,6 @@ def _is_candidate_lock_name(name: str) -> bool: ) - -def _is_candidate_lock_path(path: pathlib.PurePosixPath) -> bool: - """Return whether one safe tracked path can name a pip requirements lock. - - In addition to conventional ``requirements*.txt`` names, repositories often - keep concrete environment closures as direct children such as - ``requirements/ci.txt`` or ``service/requirements/package.txt``. Only direct - ``.txt`` children of a directory named ``requirements`` gain this path-based - eligibility; content must still pass the independent complete hash-pin - validation before it reaches the trusted image build context. - """ - return _is_candidate_lock_name(path.name) or ( - path.suffix == ".txt" and path.parent.name == "requirements" - ) - - -def _is_bounded_requirement_include(line: str) -> bool: - """Return whether one requirements include names a bounded relative file. - - Includes are accepted only as a two-token ``-r``/``--requirement`` form - whose target is itself a candidate lock path written as a normalized - relative POSIX path. Absolute paths, ``.`` or ``..`` components, double - slashes, URLs, option-like targets, shell/Windows path separators, - fragments, queries, extra inline options or hashes, and includes of - non-lock files are rejected before a base-owned file can enter the - trusted build context. - The downstream installer still proves that the candidate is an independently - complete hash closure; this predicate grants syntax eligibility only. - """ - fields = line.split() - if len(fields) != 2 or fields[0] not in {"-r", "--requirement"}: - return False - target = fields[1] - if ( - target.startswith(("-", "~")) - or "\\" in target - or ":" in target - or "?" in target - or "#" in target - ): - return False - include_path = pathlib.PurePosixPath(target) - return ( - bool(include_path.parts) - and target == include_path.as_posix() - and not include_path.is_absolute() - and "." not in include_path.parts - and ".." not in include_path.parts - and _is_candidate_lock_path(include_path) - ) - - def _requirement_lines(content: bytes) -> list[str]: """Return logical requirement lines, joining backslash line-continuations. @@ -159,27 +107,26 @@ def _requirement_lines(content: bytes) -> list[str]: def _is_hash_pinned(content: bytes) -> bool: - """Return whether content carries only trusted pins or bounded includes. - - Discovery is content-based rather than name-based so exact hash-pinned locks - in service subdirectories and role-specific requirements files can be - considered for offline coverage. Candidate syntax is deliberately stricter - than a substring search: each package line must be an exact ``==`` pin with - one or more complete SHA-256 hashes, or a bounded relative requirements - include. A global ``--require-hashes`` directive is not trust evidence by - itself. The downstream installer separately preflights every candidate as an - independent ``pip --require-hashes`` closure, so syntax eligibility never - substitutes for dependency-closure proof. + """Return whether content carries hash pins and is safe to preflight. + + Discovery is content-based rather than name-based so hash-pinned locks in any + location (a service subdirectory, ``requirements-dev.txt``, + ``requirements-test.txt``) can be considered for offline coverage, while an + unpinned or PR-mutable requirements file is still excluded from the networked + build context. Hash syntax cannot prove that a file includes every transitive + dependency, so the trusted image installer separately preflights every + candidate as an independent ``--require-hashes`` closure. An empty file + carries no installable dependency and is not materialized. """ lines = _requirement_lines(content) - requirement_lines = [line for line in lines if line != "--require-hashes"] - if not requirement_lines: + if not lines: return False - return all( - _is_fully_hash_pinned_requirement(line) - or _is_bounded_requirement_include(line) - for line in requirement_lines + return any(line == "--require-hashes" for line in lines) or all( + "--hash=" in line or line.startswith(("-r ", "--requirement ")) + for line in lines ) + + def _is_fully_hash_pinned_requirement(line: str) -> bool: """Return whether one uv-export line is an exact package pin with SHA-256 hashes.""" fields = re.split(r"\s+(?=--hash=)", line) diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 317ab5f5c..8a383f0c2 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -30,13 +30,6 @@ def _created_tool_directory(path: Path) -> str: return str(path) -def _force_linux_x86_64_installer(monkeypatch: pytest.MonkeyPatch) -> None: - """Exercise the installer path that GitHub-hosted linux x86_64 runners use.""" - monkeypatch.setattr(materializer.sys, "platform", "linux") - monkeypatch.setattr(materializer.platform, "machine", lambda: "x86_64") - materializer._install_trusted_uv.cache_clear() - - def test_materializes_only_regular_hash_locks_from_exact_base(tmp_path: Path) -> None: """A PR-modified lock cannot enter the networked coverage image build context.""" repo = tmp_path / "repo" @@ -157,24 +150,9 @@ def test_lock_name_candidates_are_pip_requirements_files() -> None: def test_hash_pin_detection_includes_pinned_and_excludes_unpinned_or_empty() -> None: """Only fully hash-pinned, non-empty lock content is materialized.""" assert not materializer._is_hash_pinned(b"# comment only\n\n") - assert not materializer._is_hash_pinned(b"--require-hashes\ndemo==1\n") + assert materializer._is_hash_pinned(b"--require-hashes\ndemo==1\n") assert materializer._is_hash_pinned(b"demo==1 --hash=sha256:" + b"a" * 64 + b"\n") - assert materializer._is_hash_pinned(b"-r requirements-other.txt\n") - assert not materializer._is_hash_pinned(b"-r other-hashes.txt\n") - assert not materializer._is_hash_pinned(b"-r ./requirements-other.txt\n") - assert not materializer._is_hash_pinned(b"-r ../escape.txt\n") - assert materializer._is_bounded_requirement_include( - "--requirement requirements-other.txt" - ) - assert not materializer._is_bounded_requirement_include("-r .") - assert not materializer._is_bounded_requirement_include("-r -evil.txt") - assert not materializer._is_bounded_requirement_include("-r ~evil.txt") - assert not materializer._is_bounded_requirement_include("-r C:foo.txt") - assert not materializer._is_bounded_requirement_include("-r foo?bar.txt") - assert not materializer._is_bounded_requirement_include("-r foo#bar.txt") - assert not materializer._is_bounded_requirement_include(r"-r foo\\bar.txt") - assert not materializer._is_bounded_requirement_include("-r") - assert not materializer._is_bounded_requirement_include("-r /abs/requirements.txt") + assert materializer._is_hash_pinned(b"-r other-hashes.txt\n") assert not materializer._is_hash_pinned(b"untrusted==1\n") # uv export / pip-compile multi-line continuation format (spec, then --hash= lines). assert materializer._is_hash_pinned( @@ -666,7 +644,6 @@ def test_install_trusted_uv_verifies_version_and_caches_path( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """The installer writes one executable, verifies its version, and caches it.""" - _force_linux_x86_64_installer(monkeypatch) tool_dir = tmp_path / "uv" monkeypatch.setattr( materializer.tempfile, @@ -713,7 +690,6 @@ def test_install_trusted_uv_rejects_version_process_failures( failure: OSError | subprocess.TimeoutExpired, ) -> None: """A missing or hung downloaded executable is removed and rejected.""" - _force_linux_x86_64_installer(monkeypatch) tool_dir = tmp_path / "uv" monkeypatch.setattr( materializer.tempfile, @@ -745,7 +721,6 @@ def test_install_trusted_uv_rejects_wrong_version_or_exit_status( completed: subprocess.CompletedProcess[bytes], ) -> None: """Unexpected version output or a nonzero status cannot satisfy the pin.""" - _force_linux_x86_64_installer(monkeypatch) tool_dir = tmp_path / f"uv-{completed.returncode}-{len(completed.stdout)}" monkeypatch.setattr( materializer.tempfile, From 6f90b24790bfd4cc40288efe3358e8e642396794 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:30:54 +0900 Subject: [PATCH 15/18] test(coverage): simulate Linux x86_64 for trusted-uv version gates Jules refactor tests exercised version verification without pinning sys.platform/platform.machine, so Darwin hosts fail-closed before the intended assertions. Reuse the existing runner simulation so the tests measure verification, cache, and cleanup. --- .../test_materialize_base_python_requirements.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 8a383f0c2..a9e974d12 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -30,6 +30,19 @@ def _created_tool_directory(path: Path) -> str: return str(path) +def _simulate_linux_x86_64_runner(monkeypatch: pytest.MonkeyPatch) -> None: + """Let installer verification tests run on a non-Linux developer host. + + Production still fail-closes unless ``sys.platform`` is Linux and + ``platform.machine()`` is ``x86_64``. These unit tests pin both values so + they measure version verification, caching, and cleanup instead of the + host architecture gate already covered by the portability contract. + """ + monkeypatch.setattr(materializer.sys, "platform", "linux") + monkeypatch.setattr(materializer.platform, "machine", lambda: "x86_64") + materializer._install_trusted_uv.cache_clear() + + def test_materializes_only_regular_hash_locks_from_exact_base(tmp_path: Path) -> None: """A PR-modified lock cannot enter the networked coverage image build context.""" repo = tmp_path / "repo" @@ -644,6 +657,7 @@ def test_install_trusted_uv_verifies_version_and_caches_path( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """The installer writes one executable, verifies its version, and caches it.""" + _simulate_linux_x86_64_runner(monkeypatch) tool_dir = tmp_path / "uv" monkeypatch.setattr( materializer.tempfile, @@ -690,6 +704,7 @@ def test_install_trusted_uv_rejects_version_process_failures( failure: OSError | subprocess.TimeoutExpired, ) -> None: """A missing or hung downloaded executable is removed and rejected.""" + _simulate_linux_x86_64_runner(monkeypatch) tool_dir = tmp_path / "uv" monkeypatch.setattr( materializer.tempfile, @@ -721,6 +736,7 @@ def test_install_trusted_uv_rejects_wrong_version_or_exit_status( completed: subprocess.CompletedProcess[bytes], ) -> None: """Unexpected version output or a nonzero status cannot satisfy the pin.""" + _simulate_linux_x86_64_runner(monkeypatch) tool_dir = tmp_path / f"uv-{completed.returncode}-{len(completed.stdout)}" monkeypatch.setattr( materializer.tempfile, From 49320004dd7f24fe4bb0a66a47a9a7d90a5b4278 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:45:41 +0000 Subject: [PATCH 16/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_materialize_base_python_requirements.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index a9e974d12..8a383f0c2 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -30,19 +30,6 @@ def _created_tool_directory(path: Path) -> str: return str(path) -def _simulate_linux_x86_64_runner(monkeypatch: pytest.MonkeyPatch) -> None: - """Let installer verification tests run on a non-Linux developer host. - - Production still fail-closes unless ``sys.platform`` is Linux and - ``platform.machine()`` is ``x86_64``. These unit tests pin both values so - they measure version verification, caching, and cleanup instead of the - host architecture gate already covered by the portability contract. - """ - monkeypatch.setattr(materializer.sys, "platform", "linux") - monkeypatch.setattr(materializer.platform, "machine", lambda: "x86_64") - materializer._install_trusted_uv.cache_clear() - - def test_materializes_only_regular_hash_locks_from_exact_base(tmp_path: Path) -> None: """A PR-modified lock cannot enter the networked coverage image build context.""" repo = tmp_path / "repo" @@ -657,7 +644,6 @@ def test_install_trusted_uv_verifies_version_and_caches_path( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """The installer writes one executable, verifies its version, and caches it.""" - _simulate_linux_x86_64_runner(monkeypatch) tool_dir = tmp_path / "uv" monkeypatch.setattr( materializer.tempfile, @@ -704,7 +690,6 @@ def test_install_trusted_uv_rejects_version_process_failures( failure: OSError | subprocess.TimeoutExpired, ) -> None: """A missing or hung downloaded executable is removed and rejected.""" - _simulate_linux_x86_64_runner(monkeypatch) tool_dir = tmp_path / "uv" monkeypatch.setattr( materializer.tempfile, @@ -736,7 +721,6 @@ def test_install_trusted_uv_rejects_wrong_version_or_exit_status( completed: subprocess.CompletedProcess[bytes], ) -> None: """Unexpected version output or a nonzero status cannot satisfy the pin.""" - _simulate_linux_x86_64_runner(monkeypatch) tool_dir = tmp_path / f"uv-{completed.returncode}-{len(completed.stdout)}" monkeypatch.setattr( materializer.tempfile, From 14c3677324132b96dc068c685258437ba591d81b Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 14 Aug 2026 09:51:16 +0000 Subject: [PATCH 17/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 68ce328246f39806338202ef3afb4edeac1c71a3 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 14 Aug 2026 13:19:02 +0000 Subject: [PATCH 18/18] =?UTF-8?q?=F0=9F=A7=B9=20[=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EA=B0=9C=EC=84=A0]=20validate=5Fcandidate?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit