Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@

<!-- CWL-ENTRY -->
> **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** <https://github.com/orgs/ContextualWisdomLab/projects/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.

Trusted-uv download retries 5xx/429 only; other 4xx fail closed on the first attempt. See [`ARCHITECTURE.md`](ARCHITECTURE.md) and [`docs/doctoring/trusted-uv-transient-download-retry.md`](docs/doctoring/trusted-uv-transient-download-retry.md).
85 changes: 85 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# 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<br/>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
```

## Trusted-uv download retry gate

```mermaid
flowchart TD
Fetch["Fetch pinned releases.astral.sh archive"]
Kind{"Exception class?"}
Retry{"Attempts remaining?"}
Success["Checksum and member verify"]
FailClosed["Fail closed: trust-boundary or exhausted"]

Fetch --> Kind
Kind -->|"OSError including HTTPError"| Retry
Kind -->|"RuntimeError redirect or size"| FailClosed
Retry -->|"yes"| Fetch
Retry -->|"no"| FailClosed
Fetch -->|"bytes"| Success
```

CWE-755: a lone origin 503 is an `OSError` and must retry. A redirect or
oversized payload is a `RuntimeError` and must not.

## 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.
- Reviewer agents stay `edit: deny`.
- Logs redact credential shapes. They do not mask operational PII.
- LLM and scheduled agents bind `NVIDIA_NIM_API_KEY`. They never use
`COPILOT_GITHUB_TOKEN`.
- Rust remains the psychometric arithmetic owner.

## Quality gates

`scripts/ci/` ships with 100% statement/branch coverage and 100%
docstrings.

## Related durable documents

- [`docs/CWL-MASTER-CONTEXT.md`](docs/CWL-MASTER-CONTEXT.md)
- [`docs/doctoring/trusted-uv-transient-download-retry.md`](docs/doctoring/trusted-uv-transient-download-retry.md)
- [`docs/agent-github-project-protocol.md`](docs/agent-github-project-protocol.md)
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Trusted-uv download retry now fail-closes on the first HTTP 4xx except 429, so a missing archive or forbidden origin is not probed three times. 503/429 and non-HTTP `OSError` still use the bounded linear backoff.
- Retried the trusted-uv archive download a bounded number of times on transient network failures instead of failing the whole `coverage-evidence` job on a single `HTTPError`/`URLError` from the shared `releases.astral.sh` origin, which every pull request's review across the organization fetches; every attempt still runs the unchanged redirect-rejection, host/port pin, size bound, and checksum/member verification, so the fix adds resilience without weakening the trust boundary. Exhaustion tests now assert the linear backoff sequence. The contract now raises the production `HTTPError` 503 type so CWE-755 cannot recast that OSError subclass as an unretried trust-boundary failure.
- 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.
Expand Down
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ Details: `README.md` and `PR_GOVERNANCE_AUDIT.md`.
dependency sets (see below).
- `fuzz/` + `.clusterfuzzlite/` — Atheris fuzz targets for the review-output normalizer and the
ClusterFuzzLite discovery marker.
- `ARCHITECTURE.md` — control-plane mermaid (system context, trusted-uv
retry gate, review sequence, trust boundaries). Reconstruct from the repo,
not private agent memory.
- `docs/` — master context, Project protocol, `org-required-workflow-rollout.md`,
`scorecard-governance.md`, SBOM inventory.
- `.jules/` — recorded performance (`bolt.md`) and security (`sentinel.md`) learnings from past work
Expand Down Expand Up @@ -126,3 +129,5 @@ repeatable compile command.
cross-repo references as `owner/repo#num` or full URLs; durable knowledge in the repo/Project, not
private memory; one roadmap phase at a time) are defined in `docs/CWL-MASTER-CONTEXT.md` §7 and
apply here.

Trusted-uv download retries 5xx/429 only. See `ARCHITECTURE.md`.
33 changes: 33 additions & 0 deletions docs/doctoring/trusted-uv-transient-download-retry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Trusted-uv transient download retry

## Incident and buyer impact

Every organization pull request runs `coverage-evidence`, which downloads one
pinned `uv` archive from `releases.astral.sh`. A single transient `HTTPError`
on that shared origin failed the gate and produced a false-negative
OpenCode `REQUEST_CHANGES` on otherwise healthy heads
(`ContextualWisdomLab/naruon#1293`, `ContextualWisdomLab/naruon#1300`).

## Decision

Retry only transient `OSError` a bounded three times with linear backoff:
HTTP 5xx, 429, and non-HTTP network errors. HTTP 4xx other than 429 fail
on the first attempt (RFC 9110 client-side policy). Trust-boundary
violations (`RuntimeError` for redirect, host/port, or oversized payload)
are never retried. Each attempt still pins scheme, host, port, size,
checksum, and archive member.

This follows the HTTP retry discipline for transient server/network failures
and does not retry client-side policy failures (Fielding et al., 2022,
§15.5). CWE-755 forbids mishandling exceptional conditions (MITRE, 2026):
`HTTPError` is an `OSError` subclass, so a lone 503 must enter the bounded
retry; `RuntimeError` trust-boundary violations must not.

## References

MITRE. (2026). *CWE-755: Improper handling of exceptional conditions*.
https://cwe.mitre.org/data/definitions/755.html

Fielding, R., Nottingham, M., & Reschke, J. (Eds.). (2022). *HTTP semantics*
(RFC 9110). Internet Engineering Task Force.
https://doi.org/10.17487/RFC9110
127 changes: 86 additions & 41 deletions scripts/ci/materialize_base_python_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import sys
import tarfile
import tempfile
import time
import urllib.error
import urllib.parse
import urllib.request
from typing import Any
Expand Down Expand Up @@ -50,6 +52,8 @@
TRUSTED_UV_DOWNLOAD_MAX_BYTES = 64 * 1024 * 1024
TRUSTED_UV_BINARY_MAX_BYTES = 64 * 1024 * 1024
TRUSTED_UV_VERSION_TIMEOUT_SECONDS = 10
TRUSTED_UV_DOWNLOAD_ATTEMPTS = 3
TRUSTED_UV_DOWNLOAD_RETRY_DELAY_SECONDS = 2.0


class _RejectTrustedUvRedirects(urllib.request.HTTPRedirectHandler):
Expand Down Expand Up @@ -165,53 +169,94 @@ def _git(repo_root: pathlib.Path, *args: str) -> bytes:
return completed.stdout


def _download_trusted_uv_archive() -> bytes:
"""Download the fixed uv release archive through one HTTPS trust boundary."""
_install_trusted_uv_url_opener()
try:
# Keep the audited URL literal at the network sink so static analysis can
# prove that neither user data nor repository content selects a scheme,
# host, path, query, fragment, method, or request header.
with urllib.request.urlopen( # nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected # nosec B310
"https://releases.astral.sh/github/uv/releases/download/0.12.1/"
"uv-x86_64-unknown-linux-gnu.tar.gz",
timeout=TRUSTED_UV_DOWNLOAD_TIMEOUT_SECONDS,
) as response:
final_url = urllib.parse.urlparse(response.geturl())
try:
final_port = final_url.port
except ValueError as exc:
raise RuntimeError(
"trusted uv archive redirected outside the fixed "
"releases.astral.sh HTTPS origin"
) from exc
if (
(final_url.scheme, final_url.hostname)
!= ("https", "releases.astral.sh")
or final_port not in (None, 443)
):
raise RuntimeError(
"trusted uv archive redirected outside the fixed "
"releases.astral.sh HTTPS origin"
)
payload = bytearray()
while len(payload) <= TRUSTED_UV_DOWNLOAD_MAX_BYTES:
chunk = response.read(
TRUSTED_UV_DOWNLOAD_MAX_BYTES + 1 - len(payload)
)
if not chunk:
break
payload.extend(chunk)
except OSError as exc:
raise RuntimeError(
f"trusted uv archive download failed: {type(exc).__name__}"
) from exc
def _fetch_trusted_uv_archive_once() -> bytes:
"""Perform one download attempt through the fixed HTTPS trust boundary.

Raises ``OSError`` (the network-level failure) on a transient fetch
problem so the caller can decide whether to retry, and ``RuntimeError``
for every trust-boundary violation (unsafe redirect, oversized payload),
which must fail closed on the first occurrence and is never retried.
"""
# Keep the audited URL literal at the network sink so static analysis can
# prove that neither user data nor repository content selects a scheme,
# host, path, query, fragment, method, or request header.
with urllib.request.urlopen( # nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected # nosec B310
"https://releases.astral.sh/github/uv/releases/download/0.12.1/"
"uv-x86_64-unknown-linux-gnu.tar.gz",
timeout=TRUSTED_UV_DOWNLOAD_TIMEOUT_SECONDS,
) as response:
final_url = urllib.parse.urlparse(response.geturl())
try:
final_port = final_url.port
except ValueError as exc:
raise RuntimeError(
"trusted uv archive redirected outside the fixed "
"releases.astral.sh HTTPS origin"
) from exc
if (
(final_url.scheme, final_url.hostname) != ("https", "releases.astral.sh")
or final_port not in (None, 443)
):
raise RuntimeError(
"trusted uv archive redirected outside the fixed "
"releases.astral.sh HTTPS origin"
)
payload = bytearray()
while len(payload) <= TRUSTED_UV_DOWNLOAD_MAX_BYTES:
chunk = response.read(TRUSTED_UV_DOWNLOAD_MAX_BYTES + 1 - len(payload))
if not chunk:
break
payload.extend(chunk)

if len(payload) > TRUSTED_UV_DOWNLOAD_MAX_BYTES:
raise RuntimeError("trusted uv archive exceeded the bounded download size")
return bytes(payload)


def _trusted_uv_download_is_transient(exc: BaseException) -> bool:
"""Return True when one failed fetch may be retried.

RFC 9110 treats 4xx as client-side policy failures except 429. 5xx
and non-HTTP ``OSError`` (timeout, reset) stay retryable.
"""
if isinstance(exc, urllib.error.HTTPError):
return exc.code >= 500 or exc.code == 429
return isinstance(exc, OSError)


def _download_trusted_uv_archive() -> bytes:
"""Download the fixed uv release archive through one HTTPS trust boundary.

The single shared ``releases.astral.sh`` origin is fetched by every
coverage-evidence run across the organization, so it occasionally answers
a transient network or HTTP error under concurrent load even though the
file itself is healthy (observed directly: repeated
``trusted uv archive download failed: HTTPError`` failures on
unmodified, otherwise-passing pull requests). A bounded number of
attempts absorbs that transient condition; every attempt still runs the
full trust boundary unchanged (redirect rejection, host/port pin, size
bound, and the checksum/member verification performed by the caller), so
retrying strictly adds resilience and never weakens a check.
"""
_install_trusted_uv_url_opener()
last_error: OSError | None = None
for attempt in range(TRUSTED_UV_DOWNLOAD_ATTEMPTS):
if attempt:
time.sleep(TRUSTED_UV_DOWNLOAD_RETRY_DELAY_SECONDS * attempt)
try:
return _fetch_trusted_uv_archive_once()
except OSError as exc:
if not _trusted_uv_download_is_transient(exc):
raise RuntimeError(
f"trusted uv archive download failed: {type(exc).__name__}"
) from exc
last_error = exc
continue
raise RuntimeError(
f"trusted uv archive download failed: {type(last_error).__name__}"
) from last_error


def _verified_uv_binary(archive_payload: bytes) -> bytes:
"""Return the bounded uv executable after archive and member verification."""
digest = hashlib.sha256(archive_payload).hexdigest()
Expand Down
Loading
Loading