K17 (4.1): mode matrix lives ONCE in toolexec modegate.go (CHAT/TASK/ PLAN profiles, typed refusals via errors.As, RunGated proves ops never execute on refusal, unknown modes fail closed, RUNE_MODE env keeps parity batteries byte-green). Serve surface: /v1/mode hot-state (PLAN needs consent), /v1/tools/call always gated (typed tool_unreachable_ in_mode), task rows carry lane_activity research styling, sleep/night background hooks never widen the gate. Live CLI: CHAT shell exit 3 no side effect, TASK runs. K1 (0.1): authority map reconciled — deployed v0.128.0 is an ancestor of master (pure lag, 1522 commits behind, zero divergence); nothing deployed-only; qualification chain CI->stamps->ancestry-fenced symlink swap verified. Live attestation: launcher->current->identity->runtime self-report->master ancestry, fail-closed (16/16 frozen red-first; 71 neighborhood green). RUNE_HOME finding: dev checkout IS the live system's data home.
280 lines
11 KiB
Python
280 lines
11 KiB
Python
"""test_runtime_attestation.py — Spec 0.1 (docs/authority-map.md §5): the
|
|
LIVE runtime must BE the qualified artifact. Every link of the chain
|
|
launcher → current symlink → generation → _build_identity.json → runtime
|
|
self-report → master ancestry is attested fail-closed: any mismatch,
|
|
missing stamp, unqualified commit, or dev-tree runtime is a FAILURE, never
|
|
a silent pass. Hermetic fixtures below fake a full deploy layout in tmp
|
|
dirs; the LIVE test binds the real machine (~/.local/bin/rune,
|
|
/mnt/workdrive/rune-code) and skips only where no deployment exists at all.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
|
|
|
REPO = os.path.join(os.path.dirname(__file__), "..")
|
|
DEPLOY_ROOT = "/mnt/workdrive/rune-code"
|
|
LAUNCHER = os.path.expanduser("~/.local/bin/rune")
|
|
VENV_PY = "/home/nick/.venvs/memory/bin/python3"
|
|
|
|
|
|
# ── hermetic chain builder ───────────────────────────────────────────────────
|
|
|
|
|
|
def _git(repo, *args):
|
|
r = subprocess.run(["git", "-C", repo, *args], capture_output=True, text=True)
|
|
assert r.returncode == 0, r.stderr
|
|
return r.stdout.strip()
|
|
|
|
|
|
def make_qualified_chain(tmp_path, *, commit=None, version="9.9.9", dir_name=None, init_version=None):
|
|
"""Build a synthetic but structurally exact deploy layout + git repo.
|
|
|
|
Returns (deploy_root, repo, full_sha, launcher_path).
|
|
"""
|
|
deploy_root = tmp_path / "rune-code"
|
|
deploy_root.mkdir()
|
|
repo = str(tmp_path / "repo")
|
|
os.makedirs(repo)
|
|
os.environ.setdefault("GIT_AUTHOR_NAME", "t")
|
|
os.environ.setdefault("GIT_COMMITTER_NAME", "t")
|
|
os.environ.setdefault("GIT_AUTHOR_EMAIL", "t@t")
|
|
os.environ.setdefault("GIT_COMMITTER_EMAIL", "t@t")
|
|
_git(repo, "init", "-q", "-b", "master", repo)
|
|
(tmp_path / "repo" / "seed.txt").write_text("qualified\n")
|
|
_git(repo, "add", ".")
|
|
_git(repo, "commit", "-q", "-m", "qualified generation")
|
|
full_sha = commit or _git(repo, "rev-parse", "HEAD")
|
|
name = dir_name or full_sha[:12]
|
|
gen = deploy_root / name / "src" / "rune"
|
|
gen.mkdir(parents=True)
|
|
(gen / "_build_identity.json").write_text(
|
|
json.dumps({"commit": full_sha, "version": version, "deployed_at": "2026-10-17T00:00:00Z"})
|
|
)
|
|
(gen / "__init__.py").write_text(
|
|
'__version__ = "%s"\n'
|
|
"import json as _j\n"
|
|
"from pathlib import Path as _Path\n"
|
|
"def build_info():\n"
|
|
" d = _j.loads(_Path(__file__).with_name('_build_identity.json')"
|
|
".read_text())\n"
|
|
" return {'version': d['version'], 'commit': d['commit'],\n"
|
|
" 'deployed_at': d['deployed_at'], 'source': 'deployed'}\n" % (init_version or version)
|
|
)
|
|
os.symlink(deploy_root / name, deploy_root / "current")
|
|
launcher = tmp_path / "rune"
|
|
launcher.write_text(
|
|
'#!/bin/bash\nexec run -- bash -c "cd %s && py -m rune $*"\n' % (deploy_root / "current" / "src")
|
|
)
|
|
return str(deploy_root), repo, full_sha, str(launcher)
|
|
|
|
|
|
def report(commit, version="9.9.9", source="deployed"):
|
|
return {"commit": commit, "version": version, "source": source, "deployed_at": "2026-10-17T00:00:00Z"}
|
|
|
|
|
|
def failures(tmp_path, **kw):
|
|
from rune import runtime_attestation as ra
|
|
|
|
deploy_root, repo, sha, launcher = make_qualified_chain(tmp_path)
|
|
return (
|
|
ra.collect_failures(launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report(sha)),
|
|
deploy_root,
|
|
repo,
|
|
sha,
|
|
launcher,
|
|
)
|
|
|
|
|
|
# ── healthy chain ────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_healthy_synthetic_chain_attests(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
assert fl == [], fl
|
|
ev = ra.attest_runtime(launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report(sha))
|
|
assert ev["identity"]["commit"] == sha
|
|
assert ev["attested"] is True
|
|
|
|
|
|
# ── frozen negative fixtures: each broken link fails ─────────────────────────
|
|
|
|
|
|
def test_missing_launcher_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
assert any(
|
|
"launcher" in f
|
|
for f in ra.collect_failures(
|
|
launcher=str(tmp_path / "absent"), deploy_root=deploy_root, repo=repo, runtime_report=report(sha)
|
|
)
|
|
)
|
|
|
|
|
|
def test_repointed_launcher_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
bad = tmp_path / "bad-rune"
|
|
bad.write_text("#!/bin/bash\ncd /mnt/workdrive/rune && py -m rune\n")
|
|
fl = ra.collect_failures(launcher=str(bad), deploy_root=deploy_root, repo=repo, runtime_report=report(sha))
|
|
assert any("launcher target" in f for f in fl), fl
|
|
|
|
|
|
def test_current_not_a_symlink_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
os.unlink(os.path.join(deploy_root, "current"))
|
|
os.mkdir(os.path.join(deploy_root, "current"))
|
|
fl = ra.collect_failures(launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report(sha))
|
|
assert any("current" in f and "symlink" in f for f in fl), fl
|
|
|
|
|
|
def test_dangling_current_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
os.unlink(os.path.join(deploy_root, "current"))
|
|
os.symlink(os.path.join(deploy_root, "deadbeef0000"), os.path.join(deploy_root, "current"))
|
|
fl = ra.collect_failures(launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report(sha))
|
|
assert fl, "dangling current must not attest"
|
|
|
|
|
|
def test_current_outside_deploy_root_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
outside = tmp_path / "outside-gen"
|
|
outside.mkdir()
|
|
os.unlink(os.path.join(deploy_root, "current"))
|
|
os.symlink(outside, os.path.join(deploy_root, "current"))
|
|
fl = ra.collect_failures(launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report(sha))
|
|
assert any("outside" in f for f in fl), fl
|
|
|
|
|
|
def test_missing_identity_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
os.remove(os.path.join(deploy_root, sha[:12], "src", "rune", "_build_identity.json"))
|
|
fl = ra.collect_failures(launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report(sha))
|
|
assert any("identity" in f for f in fl), fl
|
|
|
|
|
|
def test_malformed_identity_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
p = os.path.join(deploy_root, sha[:12], "src", "rune", "_build_identity.json")
|
|
open(p, "w").write("{not json")
|
|
fl = ra.collect_failures(launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report(sha))
|
|
assert any("malformed" in f for f in fl), fl
|
|
|
|
|
|
def test_identity_directory_mismatch_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
# directory claims one sha, identity stamps another → impersonation
|
|
deploy_root, repo, sha, launcher = make_qualified_chain(tmp_path, dir_name="abcdefabcdef")
|
|
fl = ra.collect_failures(launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report(sha))
|
|
assert any("directory" in f for f in fl), fl
|
|
|
|
|
|
def test_dev_runtime_self_report_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
fl = ra.collect_failures(
|
|
launcher=launcher,
|
|
deploy_root=deploy_root,
|
|
repo=repo,
|
|
runtime_report={"commit": "dev", "version": "9.9.9", "source": "dev"},
|
|
)
|
|
assert any("self-report" in f for f in fl), fl
|
|
|
|
|
|
def test_runtime_commit_mismatch_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
fl = ra.collect_failures(launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report("e" * 40))
|
|
assert any("self-report" in f for f in fl), fl
|
|
|
|
|
|
def test_unqualified_commit_not_ancestor_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
unqualified = "1234567890abcdef1234567890abcdef12345678"
|
|
# the identity ITSELF stamps a commit outside the release line
|
|
deploy_root, repo, sha, launcher = make_qualified_chain(tmp_path, commit=unqualified)
|
|
fl = ra.collect_failures(launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report(unqualified))
|
|
assert any("ancestor" in f for f in fl), fl
|
|
|
|
|
|
def test_version_incoherence_fails(tmp_path):
|
|
from rune import runtime_attestation as ra
|
|
|
|
fl, deploy_root, repo, sha, launcher = failures(tmp_path)
|
|
fl = ra.collect_failures(
|
|
launcher=launcher, deploy_root=deploy_root, repo=repo, runtime_report=report(sha, version="0.0.1-mismatch")
|
|
)
|
|
assert any("version" in f for f in fl), fl
|
|
|
|
|
|
def test_cli_exit_codes(tmp_path, capsys):
|
|
from rune import runtime_attestation as ra
|
|
|
|
deploy_root, repo, sha, launcher = make_qualified_chain(tmp_path)
|
|
assert ra.main(["--launcher", launcher, "--deploy-root", deploy_root, "--repo", repo]) == 0
|
|
assert ra.main(["--launcher", str(tmp_path / "absent"), "--deploy-root", deploy_root, "--repo", repo]) == 1
|
|
|
|
|
|
# ── LIVE attestation — the actual Spec 0.1 enforcement ──────────────────────
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not (os.path.isdir(DEPLOY_ROOT) and os.path.islink(os.path.join(DEPLOY_ROOT, "current"))),
|
|
reason="local deployment layout not present (non-deploy machine)",
|
|
)
|
|
class TestLiveChain:
|
|
def test_live_runtime_is_the_qualified_artifact(self):
|
|
from rune import runtime_attestation as ra
|
|
|
|
ev = ra.attest_runtime(
|
|
launcher=LAUNCHER,
|
|
deploy_root=DEPLOY_ROOT,
|
|
repo=REPO,
|
|
venv_python=VENV_PY if os.path.exists(VENV_PY) else None,
|
|
)
|
|
assert ev["attested"] is True
|
|
ident = ev["identity"]
|
|
assert ident["commit"].startswith(os.path.basename(os.path.realpath(os.path.join(DEPLOY_ROOT, "current"))))
|
|
assert ident["version"] == ev["runtime"]["version"]
|
|
assert ev["ancestry"] == "ancestor"
|
|
|
|
def test_deployed_commit_is_ancestor_of_master(self):
|
|
from rune import runtime_attestation as ra
|
|
|
|
r = subprocess.run(
|
|
[
|
|
"git",
|
|
"-C",
|
|
REPO,
|
|
"merge-base",
|
|
"--is-ancestor",
|
|
ra.read_identity(os.path.join(DEPLOY_ROOT, "current"))["commit"],
|
|
"master",
|
|
],
|
|
capture_output=True,
|
|
)
|
|
assert r.returncode == 0, "deployed generation left the release line"
|