fix: harden DGR-017 contract continuity

This commit is contained in:
Dobromir Popov
2026-07-14 01:10:33 +03:00
parent ad2d17541c
commit 7364ed6731
4 changed files with 81 additions and 30 deletions

View File

@@ -161,13 +161,13 @@ seeing a result. Each of those moves now has a test that names it:
- admit the dense fallback → `test_admitting_the_dense_attention_fallback_after_the_fact_is_rejected`
- shrink the reserve → `test_relaxing_the_per_node_reserve_after_the_fact_is_rejected`
**Be precise about what the seal does.** `contract_sha256` is tamper-*evidence*, not
tamper-proofing. Nothing checked into a repo can stop an agent that rewrites the
threshold *and* re-seals the digest — `test_resealing_a_mutated_contract_changes_its_digest`
asserts that path openly. What the seal removes is the *silent* mutation: the digest
moves, and the change becomes a visible diff on a file whose entire purpose is to not
change, still claiming `contract_id: glm-5.2-max-alpha/v1`. Reviewers, not hashes, are
the enforcement; the hash makes sure there is something to review.
**Contract continuity is fail-closed.** The documents `contract_sha256` detects
accidental edits, and the approved v1 digest is pinned independently in code. A caller
that changes a threshold and re-seals it under `glm-5.2-max-alpha/v1` is rejected by
`test_resealing_a_mutated_v1_contract_is_rejected`; amendments require a new supported
contract identity under human review. Parsed nested state is recursively immutable,
so thresholds cannot change between validation and use; `to_dict()` returns an isolated
copy rather than exposing the validated object.
## 6. Upstream status — the gating risk for DGR-004/DGR-018

View File

@@ -98,3 +98,18 @@ $VP -m pytest -q tests/test_tracker_routing.py::test_tracker_dashboard_can_cance
-> 1 passed, repeated 5/5 in isolation
$VP -m pytest -q # integrated rerun
-> 852 passed, 13 skipped in 253.30s (0:04:13)
# ---------------------------------------------------------------------------
# 7. Late independent-review repair (2026-07-14)
# ---------------------------------------------------------------------------
PYTHONPATH=packages/node $VP -m pytest -q tests/test_glm_alpha_target.py
-> 99 passed in 0.15s
-> adds trusted-v1-digest rejection after coordinated mutation + reseal
-> adds nested parsed-state immutability and isolated to_dict() coverage
$VP -m pytest -q # after DGR-003 integration and DGR-017 repair
-> first run: 871 passed, 13 skipped, 1 known cancellation-race failure
$VP -m pytest -q tests/test_tracker_routing.py::test_tracker_dashboard_can_cancel_inflight_proxy
-> 5/5 passed in isolation
$VP -m pytest -q # integrated rerun
-> 872 passed, 13 skipped in 253.46s (0:04:13)

View File

@@ -6,16 +6,15 @@ the fact: *what would have counted as success?*
Its thresholds are locked before the target ever runs (DGR-017), and DGR-020 reads
them back to publish an ``alpha`` or ``stop`` verdict. The whole point is that the
gap between those two moments is where a threshold quietly becomes "0.1 tokens/sec
was always the goal". So the document carries ``contract_sha256`` over its own
canonical content, and :func:`load_alpha_contract` recomputes it on every load. An
agent that edits a threshold and forgets the digest is rejected; an agent that
edits both has left a diff on a file whose whole purpose is to not change.
was always the goal". So the document carries ``contract_sha256`` over its canonical content, while the
approved v1 digest is pinned independently in code. :func:`load_alpha_contract`
recomputes the self-digest and then requires that trusted pre-execution digest.
Changing a threshold and re-sealing under the same identity is rejected; an
amendment requires a new supported contract identity under human review.
This is a tamper-*evidence* mechanism, not tamper-proofing. It cannot stop a
determined rewrite of the file and the digest together — nothing in-repo can. What
it does is remove the possibility of a silent one, which is the failure mode that
actually happens: a number nudged mid-run, with no reviewer ever seeing that it
moved.
The parsed contract recursively freezes nested mappings and sequences. Thresholds
therefore cannot change between verification and use, and :meth:`AlphaContract.to_dict`
returns an isolated mutable copy for diagnostics and tests.
"""
from __future__ import annotations
@@ -25,6 +24,7 @@ import re
from dataclasses import dataclass
from importlib.resources import files
from pathlib import Path
from types import MappingProxyType
from typing import Any, Mapping
from .manifest import (
@@ -39,6 +39,7 @@ from .manifest import (
ALPHA_CONTRACT_SCHEMA_VERSION = 1
ALPHA_CONTRACT_VERSION = 1
ALPHA_CONTRACT_ID = "glm-5.2-max-alpha/v1"
ALPHA_CONTRACT_V1_SHA256 = "aab23220280c053a3c14ff559df3cb5c9e1bf7f0f7188c6519e2e9d9ad036ed9"
_CONTRACT_RESOURCE = "alpha-contract.json"
@@ -72,7 +73,23 @@ def contract_signing_payload(document: Mapping[str, Any]) -> dict:
def compute_contract_digest(document: Mapping[str, Any]) -> str:
"""SHA-256 over the canonical contract content."""
return canonical_sha256(contract_signing_payload(document))
return canonical_sha256(contract_signing_payload(_thaw_json(document)))
def _freeze_json(value: Any) -> Any:
if isinstance(value, Mapping):
return MappingProxyType({str(key): _freeze_json(item) for key, item in value.items()})
if isinstance(value, list):
return tuple(_freeze_json(item) for item in value)
return value
def _thaw_json(value: Any) -> Any:
if isinstance(value, Mapping):
return {str(key): _thaw_json(item) for key, item in value.items()}
if isinstance(value, tuple):
return [_thaw_json(item) for item in value]
return value
@dataclass(frozen=True)
@@ -107,7 +124,7 @@ class AlphaContract:
return block[key]
def to_dict(self) -> dict:
return dict(self.raw)
return _thaw_json(self.raw)
def parse_alpha_contract(data: Any, source: str = "<memory>") -> AlphaContract:
@@ -229,18 +246,28 @@ def parse_alpha_contract(data: Any, source: str = "<memory>") -> AlphaContract:
if not isinstance(amendment_policy, str) or not amendment_policy.strip():
raise AlphaContractError(f"{source} must state its amendment policy")
if declared != ALPHA_CONTRACT_V1_SHA256:
raise AlphaContractError(
f"{source} is a re-sealed mutation of {ALPHA_CONTRACT_ID}: digest "
f"{declared} does not match the trusted pre-execution digest "
f"{ALPHA_CONTRACT_V1_SHA256}. An amendment requires a new supported "
"contract identity under human review."
)
frozen = _freeze_json(data)
return AlphaContract(
schema_version=schema_version,
contract_version=contract_version,
contract_id=contract_id,
locked_at=str(data["locked_at"]),
locked_by=str(data["locked_by"]),
target=target,
sections={name: data[name] for name in REQUIRED_SECTIONS},
target=frozen["target"],
sections=MappingProxyType({name: frozen[name] for name in REQUIRED_SECTIONS}),
verdicts=tuple(verdicts),
amendment_policy=amendment_policy,
digest=declared,
raw=data,
raw=frozen,
source=source,
)

View File

@@ -80,7 +80,7 @@ def snapshot_doc(snapshot):
@pytest.fixture
def contract_doc(contract):
return copy.deepcopy(dict(contract.raw))
return contract.to_dict()
# --------------------------------------------------------------------------
@@ -746,7 +746,7 @@ def test_the_contract_locks_the_roadmap_thresholds(contract):
assert contract.threshold("performance", "quality_pass_with_speed_fail_verdict") == "stop"
assert contract.threshold("reliability", "synthetic_workers_satisfy_alpha") is False
assert contract.threshold("storage", "forbidden_path_prefixes") == ["/home"]
assert contract.threshold("storage", "forbidden_path_prefixes") == ("/home",)
def test_the_contract_offers_only_alpha_or_stop(contract):
@@ -820,17 +820,26 @@ def test_a_dropped_acceptance_section_is_rejected(contract_doc):
parse_alpha_contract(contract_doc)
def test_resealing_a_mutated_contract_changes_its_digest(contract, contract_doc):
"""Tamper-evidence, not tamper-proofing: a rewrite is legal but never silent."""
def test_resealing_a_mutated_v1_contract_is_rejected(contract, contract_doc):
contract_doc["performance"]["min_median_decode_tokens_per_second"] = 0.05
resealed = seal_contract(contract_doc)
# It parses — nothing in-repo can stop a determined rewrite of file plus digest.
reparsed = parse_alpha_contract(resealed)
with pytest.raises(AlphaContractError, match="trusted pre-execution digest"):
parse_alpha_contract(resealed)
assert resealed["contract_sha256"] != contract.digest
# But the digest moved, so the change is a visible diff on a file whose entire
# purpose is to not change, and the contract_id still claims to be v1.
assert reparsed.digest != contract.digest
def test_parsed_contract_nested_state_is_immutable(contract):
with pytest.raises(TypeError):
contract.raw["performance"]["min_median_decode_tokens_per_second"] = 0.05
with pytest.raises(TypeError):
contract.target["reasoning_effort"] = "high"
def test_contract_to_dict_returns_an_isolated_mutable_copy(contract):
copied = contract.to_dict()
copied["performance"]["min_median_decode_tokens_per_second"] = 0.05
assert contract.threshold("performance", "min_median_decode_tokens_per_second") == 0.5
def test_an_unknown_threshold_cannot_be_invented_at_read_time(contract):