Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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.

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/strix-hf-xet-pin.md`](docs/doctoring/strix-hf-xet-pin.md).
81 changes: 81 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# 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
```

## Strix hash-pin gate

```mermaid
flowchart TD
Lock["requirements-strix-ci-hashes.txt"]
Pin{"hf-xet==1.6.0 with every wheel SHA-256?"}
Install["pip install --require-hashes"]
Reject["Fail closed"]

Lock --> Pin
Pin -->|"yes"| Install
Pin -->|"no"| Reject
```

CWE-494 / CWE-829: a version without matching hashes is an unreviewed
control sphere.

## 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/strix-hf-xet-pin.md`](docs/doctoring/strix-hf-xet-pin.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

- 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.
- Pinned Strix CI `hf-xet==1.6.0` with all published SHA-256 platform-wheel digests and a contract that rejects a leftover 1.5.1 line so `--require-hashes` cannot install an unreviewed native wheel (CWE-494, CWE-829).
- 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
3 changes: 3 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, Strix hash
pin 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
31 changes: 31 additions & 0 deletions docs/doctoring/strix-hf-xet-pin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Strix hf-xet hash pin

## Incident and buyer impact

Dependabot rewrote `requirements-strix-ci-hashes.txt` from
`hf-xet==1.5.1` to `1.6.0`. Strix installs that lock with
`pip install --require-hashes`. A version line without every published
platform wheel digest, or a leftover 1.5.1 pin, would either fail closed
or install an unreviewed native wheel.

## Decision

Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include; a lone `--require-hashes` line is not lock evidence.

Keep `hf-xet==1.6.0` with all seventeen published SHA-256 digests.
`hf-xet` is Apache-2.0 (permissive SPDX). Do not hand-edit the hashes
file later: regenerate from `requirements-strix-ci.txt`.

CWE-494 forbids downloading code without integrity check (MITRE, 2026).
CWE-829 forbids including functionality from an untrusted control sphere.

## References

MITRE. (2026). *CWE-494: Download of code without integrity check*.
https://cwe.mitre.org/data/definitions/494.html

MITRE. (2026). *CWE-829: Inclusion of functionality from untrusted
control sphere*. https://cwe.mitre.org/data/definitions/829.html

Hugging Face. (n.d.). *hf-xet*. PyPI. Retrieved August 13, 2026, from
https://pypi.org/project/hf-xet/1.6.0/
44 changes: 18 additions & 26 deletions requirements-strix-ci-hashes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -876,32 +876,24 @@ h11==0.16.0 \
# via
# httpcore
# uvicorn
hf-xet==1.5.1 \
--hash=sha256:0c97106032ef70467b4f6bc2d0ccc266d7613ee076afc56516c502f87ce1c4a6 \
--hash=sha256:3474760d10e3bb6f92ff3f024fcb00c0b3e4001e9b035c7483e49a5dd17aa70f \
--hash=sha256:4f561cbbb92f80960772059864b7fb07eae879adde1b2e781ec6f86f6ac26c59 \
--hash=sha256:51ef4500dab3764b41135ee1381a4b62ce56fc54d4c92b719b59e597d6df5bf6 \
--hash=sha256:6071d5ccb4d8d2cbd5fea5cc798da4f0ba3f44e25369591c4e89a4987050e61d \
--hash=sha256:6208adb15d192b90e4c2ad2a27ed864359b2cb0f2494eb6d7c7f3699ac02e2bf \
--hash=sha256:6762d89b9e3267dfd502b29b2a327b4525f33b17e7b509a78d94e2151a30ce30 \
--hash=sha256:6abd35c3221eff63836618ddfb954dcf84798603f71d8e33e3ed7b04acfdbe6e \
--hash=sha256:6f7a04a8ad962422e225bc49fbbac99dc1806764b1f3e54dbd154bffa7593947 \
--hash=sha256:8298485c1e36e7e67cbd01eeb1376619b7af43d4f1ec245caae306f890a8a32d \
--hash=sha256:892e3a3a3aecc12aded8b93cf4f9cd059282c7de0732f7d55026f3abdf474350 \
--hash=sha256:93d090b57b211133f6c0dab0205ef5cb6d89162979ba75a74845045cc3063b8e \
--hash=sha256:94e761bbd266bf4c03cee73753916062665ce8365aa40ed321f45afcb934b41e \
--hash=sha256:97f212a88d14bbf573619a74b7fecb238de77d08fc702e54dec6f78276ca3283 \
--hash=sha256:a93df2039190502835b1db8cd7e178b0b7b889fe9ab51299d5ced26e0dd879a4 \
--hash=sha256:bf67e6ed10260cef62e852789dc91ebb03f382d5bdc4b1dbeb64763ea275e7d6 \
--hash=sha256:c6b6cd08ca095058780b50b8ce4d6cbf6787bcf27841705d58a9d32246e3e47a \
--hash=sha256:d48199c2bf4f8df0adc55d31d1368b6ec0e4d4f45bc86b08038089c23db0bed8 \
--hash=sha256:dbf48c0d02cf0b2e568944330c60d9120c272dabe013bd892d48e25bc6797577 \
--hash=sha256:e1af0de8ca6f190d4294a28b88023db64a1e2d1d719cab044baf75bec569e7a9 \
--hash=sha256:e78e4e5192ad2b674c2e1160b651cb9134db974f8ae1835bdfbfb0166b894a43 \
--hash=sha256:e7dbb40617410f432182d918e37c12303fe6700fd6aa6c5964e30a535a4461d6 \
--hash=sha256:f4ad3ebd4c32dd2b27099d69dc7b2df821e30767e46fb6ee6a0713778243b8ff \
--hash=sha256:f61e3665892a6c8c5e765395838b8ddf36185da835253d4bc4509a81e49fb342 \
--hash=sha256:f7b3002f95d1c13e24bcb4537baa8f0eb3838957067c91bb4959bc004a6435f5
hf-xet==1.6.0 \
--hash=sha256:0e6e21fa3cdfcdcd76748564bf593870a5e013f47d97cf10aed63aa222cff5b7 \
--hash=sha256:23379c2f9ec8696d952b16414a2bae72cad86a52df869b050698ba60f538c675 \
--hash=sha256:2e58454a340b3556dfa4972d5451aff4fba8dd42a236600ba1a1d2b1514f0fef \
--hash=sha256:35cec30d75c6f9eb9c16a77cef68e85a103b72e24d4b473714ec9ff06428bab9 \
--hash=sha256:3dc3e35441ba395006af5aaacc40ef2e603c51ef46c3530b9156185f00935ea3 \
--hash=sha256:4fc74352a17015bd0ee90038bc9efe38db894cde45f268b6712b04fce8cd0acb \
--hash=sha256:5153e6bb103ad49d6ea9f1b2e230db5a2ea32551ad09a706d2f61d7c7c80d80e \
--hash=sha256:5789835d7c6bc9436962853192082374297fb72d7eff7e7762ec25ceb7e25338 \
--hash=sha256:633dc0cd71d32da58ab8c03ad38e2fac452c15c2b0a2866ebf6ededfe0a5061d \
--hash=sha256:70cbb9c896901600128cb9b6f06e132954fbede1db30f31f7c6c63f84cb7c31d \
--hash=sha256:75765820ce4700db3750c94acc8fe27c5fae4c9ec000a0dbac3ca082acf97765 \
--hash=sha256:8fb4f71cba6129110c3374a33f919001ff130488fc23553698e34cc1c2a1198c \
--hash=sha256:948f15d3a9545cfe5932f6bd8b440f6ae630aee108f14b7bd6c561f7c2dcc522 \
--hash=sha256:d62671bb130879cef0ee4c9ebe47a14af6c66ec53e6d84dc15936e5ffdfac82f \
--hash=sha256:f0906082d9932ae0c0057fa194041c22b4e2cdb46b2592ef3b91f020d62a081a \
--hash=sha256:f2f7278c05c22fd60cb436cda1269649b3e81db65ecdc8496e5e164aa4143e7b \
--hash=sha256:fb4fadde1b2b70bf4c0c14a6dccbe7194b1c28947fefd5bbe3fed9d940676c3b
# via huggingface-hub
httpcore==1.0.9 \
--hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \
Expand Down
85 changes: 69 additions & 16 deletions scripts/ci/materialize_base_python_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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)
Expand Down
35 changes: 33 additions & 2 deletions tests/test_materialize_base_python_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -150,9 +163,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(
Expand Down Expand Up @@ -644,6 +672,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,
Expand Down Expand Up @@ -690,6 +719,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,
Expand Down Expand Up @@ -721,6 +751,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,
Expand Down
Loading
Loading