Refuse stale installed owner packages despite matching version metadata
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
tegwick 2026-09-09 23:02:29 +02:00
parent be42de7caf
commit 7a61383e59
4 changed files with 86 additions and 3 deletions

View file

@ -237,10 +237,15 @@ owner discrepancy for bwrap. It is not a claim that every remote backend's exter
hosts/templates/tools are packaged or admitted. The ephemeral Messages binding may
select an exact runtime path/digest; usual overlap and default-deny checks apply.
Required `make check`: lint clean, 201 tests passed. Rein's
Required `make check`: lint clean, 205 tests passed. Rein's
`scripts/prove-metered-runtime.py` consumes the candidate to test installed imports,
packaged definitions, explicit one-cycle bootstrap, actual pinned CLI/metered stream,
insufficient-capacity refusal and teardown against fixtures. The resulting receipt
belongs to `prj-helixforge-factory/evidence/2026-09-09-owner-bootstrap.json`.
Production installation and credential-holder/empty-egress profile/placement admission
remain SAND-WP-0015-T04 and HFACT-WP-0001-T03/T04; no prior profile is promoted.
The first standalone candidate exposed a stale cached local wheel despite current
Git metadata. Owner builds now refresh all local distributions and compare every
installed package source/definition file with its tracked source; stale, missing or
extra files refuse the build. Four content-conformance regressions cover that gap.

View file

@ -43,6 +43,33 @@ def install_claude(output: Path, source: Path, sha256: str, version: str) -> dic
"expected_version": version, "source": str(source)}
def verify_source_files(site: Path, mappings: list[tuple[Path, str, str]]) -> dict:
"""Refuse stale/missing/extra package contents even when versions match."""
expected = {}
for source, prefix, destination in mappings:
tracked = checked(["git", "-C", str(source), "ls-files", prefix]).splitlines()
if not tracked:
raise ValueError("owner package source mapping is empty")
for name in tracked:
relative = Path(destination) / Path(name).relative_to(prefix)
wanted = hashlib.sha256((source / name).read_bytes()).hexdigest()
installed = site / relative
if (not installed.is_file() or installed.is_symlink()
or hashlib.sha256(installed.read_bytes()).hexdigest() != wanted):
raise ValueError(f"owner package content mismatch: {relative}")
expected[relative.as_posix()] = wanted
actual = set()
for package in {Path(name).parts[0] for name in expected}:
for path in (site / package).rglob("*"):
if path.is_file() and "__pycache__" not in path.parts:
actual.add(path.relative_to(site).as_posix())
if actual != set(expected):
raise ValueError("owner package has unexpected installed files")
return {"files_verified": len(expected), "manifest_sha256": hashlib.sha256(
json.dumps(expected, sort_keys=True, separators=(",", ":")).encode()
).hexdigest()}
def build(output: Path, rein_source: Path, llm_source: Path,
claude_binary: Path | None = None, claude_sha256: str | None = None,
claude_version: str | None = None, *, owner_runtime: bool = False) -> dict:
@ -68,8 +95,10 @@ def build(output: Path, rein_source: Path, llm_source: Path,
output.mkdir(parents=True, exist_ok=False)
checked(["/usr/bin/python3", "-m", "venv", "--copies", "--without-pip", str(output)])
if owner_runtime:
refresh = [arg for name in revisions for arg in ("--refresh-package", name)]
checked(["uv", "sync", "--project", str(rein_source), "--frozen", "--no-editable",
"--no-dev", "--extra", "glas", "--extra", "llm", "--python", "/usr/bin/python3"],
"--no-dev", "--extra", "glas", "--extra", "llm", "--python", "/usr/bin/python3",
*refresh],
env={**os.environ, "UV_PROJECT_ENVIRONMENT": str(output)})
else:
checked(["uv", "pip", "install", "--python", str(output / "bin/python3"),
@ -93,6 +122,23 @@ def build(output: Path, rein_source: Path, llm_source: Path,
metadata["claude"] = install_claude(output, claude_binary, claude_sha256, claude_version)
metadata["source_revisions"] = revisions
if owner_runtime:
lock = json.loads((rein_source / "deploy/runtime-contract-lock.json").read_text())
sources = {x["distribution"]: (rein_source / x["source"]).resolve()
for x in lock["dependencies"]}
site = Path(checked([str(output / "bin/python3"), "-I", "-B", "-c",
"import sysconfig; print(sysconfig.get_path('purelib'))"]))
if not site.resolve().is_relative_to(output.resolve()):
raise ValueError("owner site-packages is outside artifact")
metadata["source_contents"] = verify_source_files(site, [
(rein_source, "rein_aharness", "rein_aharness"),
(sources["llm-connect"], "llm_connect", "llm_connect"),
(sources["glas-harness"], "src/glas_harness", "glas_harness"),
(sources["glas-harness"], "profiles", "glas_harness/data/profiles"),
(sources["glas-harness"], "registry/reins", "glas_harness/data/reins"),
(sources["sandboxer"], "src/sandboxer", "sandboxer"),
(sources["sandboxer"], "profiles", "sandboxer/data/profiles"),
(sources["sandboxer"], "extensions", "sandboxer/data/extensions"),
])
metadata["owner_runtime"] = True
metadata["runtime_contract"] = lock_report
metadata["uv_lock_sha256"] = hashlib.sha256(

View file

@ -69,3 +69,30 @@ def test_owner_build_requires_matching_sibling_lock_before_output(tmp_path, monk
with pytest.raises(ValueError, match="does not match"):
builder.build(output, rein, llm, owner_runtime=True)
assert not output.exists()
@pytest.mark.parametrize("change", [None, "stale", "missing", "extra"])
def test_installed_package_contents_must_match_committed_source(tmp_path, change):
import subprocess
source = tmp_path / "source"
package = source / "fixture"
package.mkdir(parents=True)
(package / "__init__.py").write_text("VERSION = 2\n")
subprocess.run(["git", "init", "-q", str(source)], check=True)
subprocess.run(["git", "-C", str(source), "add", "fixture"], check=True)
site = tmp_path / "site"
installed = site / "fixture"
installed.mkdir(parents=True)
(installed / "__init__.py").write_text("VERSION = 2\n")
if change == "stale":
(installed / "__init__.py").write_text("VERSION = 1\n")
elif change == "missing":
(installed / "__init__.py").unlink()
elif change == "extra":
(installed / "retired.py").write_text("old = True\n")
if change:
with pytest.raises(ValueError, match="owner package"):
builder.verify_source_files(site, [(source, "fixture", "fixture")])
else:
result = builder.verify_source_files(site, [(source, "fixture", "fixture")])
assert result["files_verified"] == 1

View file

@ -268,7 +268,7 @@ with non-editable installation and recorded lock/source/package pins. Sand-boxer
wheels now carry their profile and extension definitions, fixing a bwrap owner
failure that source-checkout tests could hide. The trusted Messages binding can
select the digest-pinned runtime without adding an API or profile override.
`make check`: lint clean, 201 passed. Existing standalone workload builds remain
`make check`: lint clean, 205 passed. Existing standalone workload builds remain
supported. See docs/bwrap-runtime.md and rein's docs/owner-bootstrap.md.
The project's `evidence/2026-09-09-owner-bootstrap.json` records the actual candidate
@ -276,3 +276,8 @@ build and isolated installed-interpreter/CLI proof. T04 remains wait for accepte
credential-to-owner delivery, current protected installation and Railiance placement,
provider compatibility and G0. Source packaging does not reopen completed T01-T03,
T05-T06, broaden the old CCRs or activate the previously installed 2.1.263 artifact.
The first standalone candidate exposed a stale cached local wheel despite current
Git metadata. Owner builds now refresh all local distributions and compare every
installed package source/definition file with its tracked source; stale, missing or
extra files refuse the build. Four content-conformance regressions cover that gap.