From cb899ec5235c35736d972b50766902db5de28f54 Mon Sep 17 00:00:00 2001 From: Moon-Young Date: Wed, 22 Jul 2026 04:50:28 +0000 Subject: [PATCH 1/2] Add honest tiered code-health conformance Co-Authored-By: Claude Opus 4.8 (1M context) --- .../tiered_mixed/evidence-contract.json | 31 ++ .../tiered_mixed/expected-verdict.json | 39 +++ .../tiered_mixed/health/architecture.exit | 1 + .../tiered_mixed/health/architecture.log | 1 + .../tiered_mixed/health/complexity.exit | 1 + .../tiered_mixed/health/complexity.log | 1 + .../tiered_mixed/health/format.exit | 1 + .../tiered_mixed/health/format.log | 1 + depone/verify/engine.py | 108 +++++++ depone/verify/evidence_contract.py | 107 ++++++- scripts/revalidate_code_health.py | 95 ++++++ tests/test_code_health_contract.py | 270 ++++++++++++++++++ 12 files changed, 655 insertions(+), 1 deletion(-) create mode 100644 depone/fixtures/code_health/tiered_mixed/evidence-contract.json create mode 100644 depone/fixtures/code_health/tiered_mixed/expected-verdict.json create mode 100644 depone/fixtures/code_health/tiered_mixed/health/architecture.exit create mode 100644 depone/fixtures/code_health/tiered_mixed/health/architecture.log create mode 100644 depone/fixtures/code_health/tiered_mixed/health/complexity.exit create mode 100644 depone/fixtures/code_health/tiered_mixed/health/complexity.log create mode 100644 depone/fixtures/code_health/tiered_mixed/health/format.exit create mode 100644 depone/fixtures/code_health/tiered_mixed/health/format.log create mode 100755 scripts/revalidate_code_health.py create mode 100644 tests/test_code_health_contract.py diff --git a/depone/fixtures/code_health/tiered_mixed/evidence-contract.json b/depone/fixtures/code_health/tiered_mixed/evidence-contract.json new file mode 100644 index 00000000..b0d300f0 --- /dev/null +++ b/depone/fixtures/code_health/tiered_mixed/evidence-contract.json @@ -0,0 +1,31 @@ +{ + "schema_version": "v111.code_health", + "code_health": { + "gates": [ + { + "gate": "format", + "tool": "black", + "enforcement": "block", + "expected_exit_code": 0, + "exit_code_path": "health/format.exit", + "log_path": "health/format.log" + }, + { + "gate": "complexity", + "tool": "ruff-c901", + "enforcement": "advisory", + "expected_exit_code": 0, + "exit_code_path": "health/complexity.exit", + "log_path": "health/complexity.log" + }, + { + "gate": "architecture", + "tool": "import-linter", + "enforcement": "block", + "expected_exit_code": 0, + "exit_code_path": "health/architecture.exit", + "log_path": "health/architecture.log" + } + ] + } +} diff --git a/depone/fixtures/code_health/tiered_mixed/expected-verdict.json b/depone/fixtures/code_health/tiered_mixed/expected-verdict.json new file mode 100644 index 00000000..00f5f789 --- /dev/null +++ b/depone/fixtures/code_health/tiered_mixed/expected-verdict.json @@ -0,0 +1,39 @@ +{ + "decision": "fail", + "error_codes": [ + "ERR_HEALTH_GATE_VIOLATION", + "ERR_HEALTH_GATE_VIOLATION" + ], + "health_conformance": { + "overall": "fail", + "axes": [ + { + "gate": "format", + "tool": "black", + "status": "pass", + "enforcement": "block", + "blocks_handoff": false, + "error_code": null, + "evidence_path": null + }, + { + "gate": "complexity", + "tool": "ruff-c901", + "status": "fail", + "enforcement": "advisory", + "blocks_handoff": false, + "error_code": "ERR_HEALTH_GATE_VIOLATION", + "evidence_path": "health/complexity.exit" + }, + { + "gate": "architecture", + "tool": "import-linter", + "status": "fail", + "enforcement": "block", + "blocks_handoff": true, + "error_code": "ERR_HEALTH_GATE_VIOLATION", + "evidence_path": "health/architecture.exit" + } + ] + } +} diff --git a/depone/fixtures/code_health/tiered_mixed/health/architecture.exit b/depone/fixtures/code_health/tiered_mixed/health/architecture.exit new file mode 100644 index 00000000..0cfbf088 --- /dev/null +++ b/depone/fixtures/code_health/tiered_mixed/health/architecture.exit @@ -0,0 +1 @@ +2 diff --git a/depone/fixtures/code_health/tiered_mixed/health/architecture.log b/depone/fixtures/code_health/tiered_mixed/health/architecture.log new file mode 100644 index 00000000..045b4322 --- /dev/null +++ b/depone/fixtures/code_health/tiered_mixed/health/architecture.log @@ -0,0 +1 @@ +import-linter observed the declared block architecture gate failing diff --git a/depone/fixtures/code_health/tiered_mixed/health/complexity.exit b/depone/fixtures/code_health/tiered_mixed/health/complexity.exit new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/depone/fixtures/code_health/tiered_mixed/health/complexity.exit @@ -0,0 +1 @@ +1 diff --git a/depone/fixtures/code_health/tiered_mixed/health/complexity.log b/depone/fixtures/code_health/tiered_mixed/health/complexity.log new file mode 100644 index 00000000..eaca8efd --- /dev/null +++ b/depone/fixtures/code_health/tiered_mixed/health/complexity.log @@ -0,0 +1 @@ +ruff-c901 observed the declared advisory complexity gate failing diff --git a/depone/fixtures/code_health/tiered_mixed/health/format.exit b/depone/fixtures/code_health/tiered_mixed/health/format.exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/depone/fixtures/code_health/tiered_mixed/health/format.exit @@ -0,0 +1 @@ +0 diff --git a/depone/fixtures/code_health/tiered_mixed/health/format.log b/depone/fixtures/code_health/tiered_mixed/health/format.log new file mode 100644 index 00000000..b33a801d --- /dev/null +++ b/depone/fixtures/code_health/tiered_mixed/health/format.log @@ -0,0 +1 @@ +black observed the declared format gate passing diff --git a/depone/verify/engine.py b/depone/verify/engine.py index ca4f52a3..c2e1bf0a 100644 --- a/depone/verify/engine.py +++ b/depone/verify/engine.py @@ -138,6 +138,23 @@ class PolicyConformance: axes: list[PolicyAxisConformance] = field(default_factory=list) +@dataclass +class HealthAxisConformance: + gate: str + tool: str + status: Literal["pass", "fail"] + enforcement: Literal["block", "advisory"] + blocks_handoff: bool + error_code: str | None = None + evidence_path: str | None = None + + +@dataclass +class HealthConformance: + overall: Literal["pass", "fail"] + axes: list[HealthAxisConformance] = field(default_factory=list) + + @dataclass class VerificationReport: """Verification result. @@ -165,6 +182,7 @@ class VerificationReport: default_factory=list ) policy_conformance: PolicyConformance | None = None + health_conformance: HealthConformance | None = None verdict: Literal["verified", "refuted", "insufficient-evidence"] = "verified" @@ -198,6 +216,39 @@ def _is_advisory_skill_routing_entry( return isinstance(directive, dict) and directive.get("enforcement") == "advisory" +def _health_entry_matches_gate( + entry: EvidenceContractEntry, + gate: dict[str, Any], +) -> bool: + return ( + entry.code == "ERR_HEALTH_GATE_VIOLATION" + and gate.get("exit_code_path") == entry.evidence_path + and f"gate={gate.get('gate')!r}" in entry.message + and f"tool={gate.get('tool')!r}" in entry.message + and f"enforcement={gate.get('enforcement')!r}" in entry.message + ) + + +def _is_advisory_health_entry( + contract: dict[str, Any] | None, + entry: EvidenceContractEntry, +) -> bool: + if entry.code != "ERR_HEALTH_GATE_VIOLATION" or contract is None: + return False + directive = contract.get("code_health") + if not isinstance(directive, dict): + return False + gates = directive.get("gates") + if not isinstance(gates, list): + return False + return any( + isinstance(gate, dict) + and gate.get("enforcement") == "advisory" + and _health_entry_matches_gate(entry, gate) + for gate in gates + ) + + def _blocking_evidence_contract_entries( contract: dict[str, Any] | None, evidence_contract: list[EvidenceContractEntry], @@ -206,6 +257,7 @@ def _blocking_evidence_contract_entries( entry for entry in evidence_contract if not _is_advisory_skill_routing_entry(contract, entry) + and not _is_advisory_health_entry(contract, entry) ] @@ -359,6 +411,61 @@ def _policy_conformance( return PolicyConformance(overall=overall, axes=axes) +def _health_conformance( + contract: dict[str, Any] | None, + evidence_contract: list[EvidenceContractEntry], +) -> HealthConformance | None: + if contract is None: + return None + directive = contract.get("code_health") + if not isinstance(directive, dict): + return None + gates = directive.get("gates") + if not isinstance(gates, list): + return None + + axes: list[HealthAxisConformance] = [] + for gate in gates: + if not isinstance(gate, dict): + continue + gate_id = gate.get("gate") + tool = gate.get("tool") + enforcement = gate.get("enforcement") + exit_code_path = gate.get("exit_code_path") + if ( + not isinstance(gate_id, str) + or not isinstance(tool, str) + or enforcement not in {"block", "advisory"} + or not isinstance(exit_code_path, str) + ): + continue + failure = next( + ( + entry + for entry in evidence_contract + if _health_entry_matches_gate(entry, gate) + ), + None, + ) + status: Literal["pass", "fail"] = "fail" if failure else "pass" + axes.append( + HealthAxisConformance( + gate=gate_id, + tool=tool, + status=status, + enforcement=enforcement, + blocks_handoff=status == "fail" and enforcement == "block", + error_code=failure.code if failure else None, + evidence_path=failure.evidence_path if failure else None, + ) + ) + + overall: Literal["pass", "fail"] = ( + "fail" if any(axis.status == "fail" for axis in axes) else "pass" + ) + return HealthConformance(overall=overall, axes=axes) + + def _resolve_handoff_path( handoff: dict[str, Any], evidence_map: dict[str, Any], @@ -943,5 +1050,6 @@ def run_verification( review_signals=review_signals, role_capability_conformance=role_capability_conformance, policy_conformance=_policy_conformance(role_capability_conformance, contract), + health_conformance=_health_conformance(contract, evidence_contract), verdict=overall, ) diff --git a/depone/verify/evidence_contract.py b/depone/verify/evidence_contract.py index 9c94ca19..732144e5 100644 --- a/depone/verify/evidence_contract.py +++ b/depone/verify/evidence_contract.py @@ -34,6 +34,7 @@ class EvidenceContractEntry: _ROLE_CAPABILITY_SKILL_ROUTING_CONTRACT_SCHEMA_VERSION = ( "v110.role_capability_skill_routing" ) +_CODE_HEALTH_CONTRACT_SCHEMA_VERSION = "v111.code_health" _OBSERVED_TOUCHED_FILES_FILENAME = "observed-touched-files.txt" _OBSERVED_SKILLS_FILENAME = "observed-skills.txt" # Deprecated compatibility alias for evidence sealed before the honest rename. @@ -67,6 +68,7 @@ class EvidenceContractEntry: _ERR_ROLE_CAPABILITY_SKILL_ROUTING_VIOLATION = ( "ERR_ROLE_CAPABILITY_SKILL_ROUTING_VIOLATION" ) +_ERR_HEALTH_GATE_VIOLATION = "ERR_HEALTH_GATE_VIOLATION" _ERR_ROLE_CAPABILITY_OBSERVATION_UNBOUND = "ERR_ROLE_CAPABILITY_OBSERVATION_UNBOUND" _ERR_ROLE_CAPABILITY_OBSERVATION_DIGEST_MISMATCH = ( "ERR_ROLE_CAPABILITY_OBSERVATION_DIGEST_MISMATCH" @@ -218,6 +220,8 @@ def _has_enforcement_directive(contract: dict[str, Any]) -> bool: return True if isinstance(contract.get("role_capability_skill_routing"), dict): return True + if "code_health" in contract: + return True if isinstance(contract.get("advisory_provenance"), dict): return True return contract.get("forbid_test_weakening") is True and _has_non_empty_str_list( @@ -238,6 +242,7 @@ def _validate_contract_semantics( _ADVISORY_PROVENANCE_EXECUTED_RED_CONTRACT_SCHEMA_VERSION, _ROLE_CAPABILITY_BOUND_OBSERVATION_CONTRACT_SCHEMA_VERSION, _ROLE_CAPABILITY_SKILL_ROUTING_CONTRACT_SCHEMA_VERSION, + _CODE_HEALTH_CONTRACT_SCHEMA_VERSION, }: return EvidenceContractEntry( code=_ERR_CONTRACT_INVALID, @@ -249,7 +254,8 @@ def _validate_contract_semantics( f"{_ADVISORY_PROVENANCE_CONTRACT_SCHEMA_VERSION!r} or " f"{_ADVISORY_PROVENANCE_EXECUTED_RED_CONTRACT_SCHEMA_VERSION!r} or " f"{_ROLE_CAPABILITY_BOUND_OBSERVATION_CONTRACT_SCHEMA_VERSION!r} or " - f"{_ROLE_CAPABILITY_SKILL_ROUTING_CONTRACT_SCHEMA_VERSION!r}" + f"{_ROLE_CAPABILITY_SKILL_ROUTING_CONTRACT_SCHEMA_VERSION!r} or " + f"{_CODE_HEALTH_CONTRACT_SCHEMA_VERSION!r}" ), evidence_path=_EVIDENCE_CONTRACT_FILENAME, ) @@ -303,6 +309,18 @@ def _validate_contract_semantics( ), evidence_path=_EVIDENCE_CONTRACT_FILENAME, ) + if ( + "code_health" in contract + and schema_version != _CODE_HEALTH_CONTRACT_SCHEMA_VERSION + ): + return EvidenceContractEntry( + code=_ERR_CONTRACT_INVALID, + message=( + "code_health requires schema_version " + f"{_CODE_HEALTH_CONTRACT_SCHEMA_VERSION!r}" + ), + evidence_path=_EVIDENCE_CONTRACT_FILENAME, + ) if isinstance(contract.get("advisory_provenance"), dict) and schema_version not in { _ADVISORY_PROVENANCE_CONTRACT_SCHEMA_VERSION, _ADVISORY_PROVENANCE_EXECUTED_RED_CONTRACT_SCHEMA_VERSION, @@ -754,6 +772,91 @@ def _validate_role_capability_skill_routing( return [] +def _validate_code_health( + evidence: EvidenceContext, + contract: dict[str, Any], +) -> list[EvidenceContractEntry]: + if "code_health" not in contract: + return [] + directive = contract.get("code_health") + if not isinstance(directive, dict): + return [ + EvidenceContractEntry( + code=_ERR_CONTRACT_INVALID, + message="code_health must be an object", + evidence_path=_EVIDENCE_CONTRACT_FILENAME, + ) + ] + + gates = directive.get("gates") + if not isinstance(gates, list) or not gates: + return [ + EvidenceContractEntry( + code=_ERR_CONTRACT_INVALID, + message="code_health.gates must be a non-empty list", + evidence_path=_EVIDENCE_CONTRACT_FILENAME, + ) + ] + + results: list[EvidenceContractEntry] = [] + for index, gate in enumerate(gates): + prefix = f"code_health.gates[{index}]" + if not isinstance(gate, dict): + return [ + EvidenceContractEntry( + code=_ERR_CONTRACT_INVALID, + message=f"{prefix} must be an object", + evidence_path=_EVIDENCE_CONTRACT_FILENAME, + ) + ] + for key in ("gate", "tool", "exit_code_path", "log_path"): + if not isinstance(gate.get(key), str) or not gate[key]: + return [ + EvidenceContractEntry( + code=_ERR_CONTRACT_INVALID, + message=f"{prefix}.{key} must be a non-empty string", + evidence_path=_EVIDENCE_CONTRACT_FILENAME, + ) + ] + enforcement = gate.get("enforcement") + if enforcement not in {"block", "advisory"}: + return [ + EvidenceContractEntry( + code=_ERR_CONTRACT_INVALID, + message=( + f"{prefix}.enforcement must be 'block' or 'advisory'" + ), + evidence_path=_EVIDENCE_CONTRACT_FILENAME, + ) + ] + expected_exit_code = gate.get("expected_exit_code") + if type(expected_exit_code) is not int: + return [ + EvidenceContractEntry( + code=_ERR_CONTRACT_INVALID, + message=f"{prefix}.expected_exit_code must be an integer", + evidence_path=_EVIDENCE_CONTRACT_FILENAME, + ) + ] + + exit_code_path = gate["exit_code_path"] + actual_exit_code = _read_exit_code(evidence, exit_code_path) + if actual_exit_code != expected_exit_code: + results.append( + EvidenceContractEntry( + code=_ERR_HEALTH_GATE_VIOLATION, + message=( + "code health gate exit code mismatch: " + f"gate={gate['gate']!r}, tool={gate['tool']!r}, " + f"enforcement={enforcement!r}, " + f"expected={expected_exit_code}, got={actual_exit_code}" + ), + evidence_path=exit_code_path, + ) + ) + return results + + def _load_bound_run_intent( evidence: EvidenceContext, run_intent_path: str, @@ -1757,6 +1860,8 @@ def validate_evidence_contract( verified_signature_anchors=verified_signature_anchors, ): _append_unique_entry(results, entry) + for entry in _validate_code_health(evidence, contract): + _append_unique_entry(results, entry) test_patterns = _as_str_list(contract.get("test_file_patterns")) forbidden_test_files = set(_as_str_list(contract.get("forbidden_test_files"))) diff --git a/scripts/revalidate_code_health.py b/scripts/revalidate_code_health.py new file mode 100755 index 00000000..e8728b27 --- /dev/null +++ b/scripts/revalidate_code_health.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import sys +from dataclasses import asdict +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) + +from depone.verify.adapters.generic import read_evidence +from depone.verify.engine import run_verification + + +FIXTURE_ROOT = ROOT / "depone/fixtures/code_health/tiered_mixed" + + +def _plan() -> dict[str, object]: + return { + "schema_version": "0.5", + "plan_id": "code-health-revalidation", + "phases": [{"id": "phase-1"}], + } + + +def _actual_result() -> dict[str, Any]: + report = run_verification(_plan(), read_evidence(str(FIXTURE_ROOT))) + return { + "decision": report.decision, + "error_codes": [entry.code for entry in report.evidence_contract], + "health_conformance": asdict(report.health_conformance), + } + + +def _expected_result() -> dict[str, Any]: + parsed = json.loads( + (FIXTURE_ROOT / "expected-verdict.json").read_text(encoding="utf-8") + ) + if not isinstance(parsed, dict): + raise AssertionError("expected-verdict.json must contain an object") + return parsed + + +def _assert_result(actual: dict[str, Any], expected: dict[str, Any]) -> None: + if actual != expected: + raise AssertionError( + "code health fixture mismatch:\n" + f"expected={json.dumps(expected, sort_keys=True)}\n" + f"actual={json.dumps(actual, sort_keys=True)}" + ) + + +def revalidate() -> dict[str, Any]: + actual = _actual_result() + _assert_result(actual, _expected_result()) + return actual + + +def self_test() -> None: + actual = revalidate() + wrong = dict(actual) + wrong["decision"] = "pass" + try: + _assert_result(actual, wrong) + except AssertionError: + pass + else: + raise AssertionError("self-test failed: mismatched decision was accepted") + print("code health revalidator self-test: pass") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--self-test", action="store_true") + args = parser.parse_args() + if args.self_test: + self_test() + return 0 + actual = revalidate() + for axis in actual["health_conformance"]["axes"]: + print( + f"{axis['gate']}: {axis['status']} " + f"enforcement={axis['enforcement']} " + f"blocks_handoff={axis['blocks_handoff']}" + ) + print("code health fixture: pass") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_code_health_contract.py b/tests/test_code_health_contract.py new file mode 100644 index 00000000..16592cec --- /dev/null +++ b/tests/test_code_health_contract.py @@ -0,0 +1,270 @@ +from __future__ import annotations + +import hashlib +import json +import unittest +from dataclasses import asdict +from pathlib import Path + +from depone.verify.adapters.base import EvidenceContext, EvidenceFile +from depone.verify.adapters.generic import read_evidence +from depone.verify.engine import _health_conformance, run_verification +from depone.verify.evidence_contract import validate_evidence_contract + + +def _sha(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def _file(path: str, value: object) -> EvidenceFile: + content = ( + value + if isinstance(value, str) + else json.dumps(value, sort_keys=True, separators=(",", ":")) + ) + return EvidenceFile(path=path, content=content, sha256=_sha(content)) + + +def _gate( + gate: str, + tool: str, + enforcement: str, + *, + expected_exit_code: object = 0, +) -> dict[str, object]: + return { + "gate": gate, + "tool": tool, + "enforcement": enforcement, + "expected_exit_code": expected_exit_code, + "exit_code_path": f"health/{gate}.exit", + "log_path": f"health/{gate}.log", + } + + +def _evidence( + gates: object, + exit_codes: dict[str, int] | None = None, + *, + schema_version: str = "v111.code_health", +) -> EvidenceContext: + contract = { + "schema_version": schema_version, + "code_health": {"gates": gates}, + } + files = [_file("evidence-contract.json", contract)] + if isinstance(gates, list): + for gate in gates: + if not isinstance(gate, dict): + continue + exit_code_path = gate.get("exit_code_path") + log_path = gate.get("log_path") + gate_id = gate.get("gate") + if isinstance(exit_code_path, str) and isinstance(gate_id, str): + actual = (exit_codes or {}).get(gate_id, 0) + files.append(_file(exit_code_path, f"{actual}\n")) + if isinstance(log_path, str): + files.append(_file(log_path, "recorded gate output\n")) + return EvidenceContext(run_id="code-health-test", files=files, raw={}) + + +def _plan() -> dict[str, object]: + return { + "schema_version": "0.5", + "plan_id": "code-health-plan", + "phases": [{"id": "phase-1"}], + } + + +class CodeHealthContractTests(unittest.TestCase): + def test_block_gate_passes_when_recorded_exit_code_matches(self) -> None: + errors = validate_evidence_contract( + _evidence([_gate("format", "black", "block")]) + ) + + self.assertEqual(errors, []) + + def test_advisory_gate_failure_records_health_violation_details(self) -> None: + errors = validate_evidence_contract( + _evidence( + [_gate("complexity", "ruff-c901", "advisory")], + {"complexity": 1}, + ) + ) + + self.assertEqual([entry.code for entry in errors], ["ERR_HEALTH_GATE_VIOLATION"]) + self.assertEqual(errors[0].evidence_path, "health/complexity.exit") + self.assertIn("gate='complexity'", errors[0].message) + self.assertIn("tool='ruff-c901'", errors[0].message) + self.assertIn("enforcement='advisory'", errors[0].message) + + def test_block_gate_failure_records_health_violation(self) -> None: + errors = validate_evidence_contract( + _evidence( + [_gate("architecture", "import-linter", "block")], + {"architecture": 2}, + ) + ) + + self.assertEqual([entry.code for entry in errors], ["ERR_HEALTH_GATE_VIOLATION"]) + self.assertEqual(errors[0].evidence_path, "health/architecture.exit") + + def test_malformed_code_health_directive_is_refused(self) -> None: + cases = { + "missing gates": ({}, "code_health.gates must be a non-empty list"), + "invalid gates": ("not-a-list", "code_health.gates must be a non-empty list"), + "invalid gate entry": (["not-an-object"], "code_health.gates[0] must be an object"), + "bad enforcement": ( + [_gate("lint", "ruff", "warn")], + "code_health.gates[0].enforcement must be 'block' or 'advisory'", + ), + "non-int exit": ( + [_gate("lint", "ruff", "block", expected_exit_code="0")], + "code_health.gates[0].expected_exit_code must be an integer", + ), + "bool exit": ( + [_gate("lint", "ruff", "block", expected_exit_code=True)], + "code_health.gates[0].expected_exit_code must be an integer", + ), + } + for name, (gates, expected_message) in cases.items(): + with self.subTest(name=name): + if gates == {}: + evidence = _evidence([]) + contract = json.loads(evidence.files[0].content) + contract["code_health"] = {} + evidence.files[0] = _file("evidence-contract.json", contract) + else: + evidence = _evidence(gates) + errors = validate_evidence_contract(evidence) + + self.assertEqual( + [entry.code for entry in errors], + ["ERR_EVIDENCE_CONTRACT_INVALID"], + ) + self.assertIn(expected_message, errors[0].message) + + def test_code_health_requires_v111_schema(self) -> None: + errors = validate_evidence_contract( + _evidence( + [_gate("format", "black", "block")], + schema_version="v110.role_capability_skill_routing", + ) + ) + + self.assertEqual( + [entry.code for entry in errors], + ["ERR_EVIDENCE_CONTRACT_INVALID"], + ) + self.assertIn("code_health requires schema_version 'v111.code_health'", errors[0].message) + + def test_health_conformance_rolls_up_three_declared_gates(self) -> None: + gates = [ + _gate("format", "black", "block"), + _gate("complexity", "ruff-c901", "advisory"), + _gate("architecture", "import-linter", "block"), + ] + evidence = _evidence(gates, {"format": 0, "complexity": 1, "architecture": 2}) + contract = json.loads(evidence.files[0].content) + entries = validate_evidence_contract(evidence) + + conformance = _health_conformance(contract, entries) + + self.assertEqual( + asdict(conformance), + { + "overall": "fail", + "axes": [ + { + "gate": "format", + "tool": "black", + "status": "pass", + "enforcement": "block", + "blocks_handoff": False, + "error_code": None, + "evidence_path": None, + }, + { + "gate": "complexity", + "tool": "ruff-c901", + "status": "fail", + "enforcement": "advisory", + "blocks_handoff": False, + "error_code": "ERR_HEALTH_GATE_VIOLATION", + "evidence_path": "health/complexity.exit", + }, + { + "gate": "architecture", + "tool": "import-linter", + "status": "fail", + "enforcement": "block", + "blocks_handoff": True, + "error_code": "ERR_HEALTH_GATE_VIOLATION", + "evidence_path": "health/architecture.exit", + }, + ], + }, + ) + + def test_advisory_health_failure_is_reported_without_blocking_decision(self) -> None: + report = run_verification( + _plan(), + _evidence( + [_gate("complexity", "ruff-c901", "advisory")], + {"complexity": 1}, + ), + ) + + self.assertEqual(report.decision, "pass") + self.assertEqual(report.verdict, "verified") + self.assertEqual(report.health_conformance.overall, "fail") + self.assertFalse(report.health_conformance.axes[0].blocks_handoff) + + def test_block_health_failure_refutes_decision(self) -> None: + report = run_verification( + _plan(), + _evidence( + [_gate("architecture", "import-linter", "block")], + {"architecture": 1}, + ), + ) + + self.assertEqual(report.decision, "fail") + self.assertEqual(report.verdict, "refuted") + self.assertEqual(report.health_conformance.overall, "fail") + self.assertTrue(report.health_conformance.axes[0].blocks_handoff) + + def test_health_conformance_is_none_without_code_health_directive(self) -> None: + evidence = _evidence([_gate("format", "black", "block")]) + contract = json.loads(evidence.files[0].content) + contract.pop("code_health") + + self.assertIsNone(_health_conformance(contract, [])) + + def test_committed_tiered_fixture_rederives_mixed_health_result(self) -> None: + evidence = read_evidence( + str(Path("depone/fixtures/code_health/tiered_mixed")) + ) + + report = run_verification(_plan(), evidence) + + self.assertEqual(report.decision, "fail") + self.assertEqual( + [entry.code for entry in report.evidence_contract], + ["ERR_HEALTH_GATE_VIOLATION", "ERR_HEALTH_GATE_VIOLATION"], + ) + self.assertEqual( + [ + (axis.gate, axis.status, axis.enforcement, axis.blocks_handoff) + for axis in report.health_conformance.axes + ], + [ + ("format", "pass", "block", False), + ("complexity", "fail", "advisory", False), + ("architecture", "fail", "block", True), + ], + ) + + +if __name__ == "__main__": + unittest.main() From 39749525e0f4ce1b12df04d859692215ea0ed61b Mon Sep 17 00:00:00 2001 From: Moon-Young Date: Wed, 22 Jul 2026 05:30:13 +0000 Subject: [PATCH 2/2] release: Depone 0.2.8 (v111.code_health tiered conformance) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the v111.code_health evidence-contract axis + HealthConformance rollup: per-gate enforcement tiers (block|advisory) with the honest advisory seam — an advisory gate failure is reported (overall fail, axis status fail) but blocks_handoff:false and does NOT refute the decision; a block gate failure does. Mirrors the role_capability_skill_routing axis. Enables ORRO code-health tiered verdicts (complexity/architecture advisory, format/lint/type block). Co-Authored-By: Claude Opus 4.8 (1M context) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bd1c8f78..fae175a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "depone" -version = "0.2.7" +version = "0.2.8" description = "Workflow designer + cross-platform evidence verifier for AI agent orchestration" license = { text = "MIT" } readme = "README.md"