diff --git a/core/capability/domain_contract_predicates.py b/core/capability/domain_contract_predicates.py index 7b165634..3c8832b6 100644 --- a/core/capability/domain_contract_predicates.py +++ b/core/capability/domain_contract_predicates.py @@ -166,13 +166,18 @@ def _predicate_p2_gloss_closure( import json manifest = json.loads(manifest_path.read_text(encoding="utf-8")) - declared = manifest.get("checksums", {}).get("glosses_sha256") + # Canonical location is the top-level ``glosses_checksum`` field + # (in-tree packs use that form). ``checksums.glosses_sha256`` is + # accepted as an alternate location for forward compatibility. + declared = manifest.get("glosses_checksum") or manifest.get( + "checksums", {} + ).get("glosses_sha256") if not declared: return PredicateResult( predicate_id="P2", title="gloss/definition checksum valid", passed=True, - notes="manifest does not declare glosses_sha256; vacuously passes", + notes="manifest does not declare a gloss checksum; vacuously passes", ) actual = hashlib.sha256(glosses.read_bytes()).hexdigest() if actual != declared: diff --git a/language_packs/data/en_mathematics_logic_v1/manifest.json b/language_packs/data/en_mathematics_logic_v1/manifest.json index b4944ed7..a8e46f3c 100644 --- a/language_packs/data/en_mathematics_logic_v1/manifest.json +++ b/language_packs/data/en_mathematics_logic_v1/manifest.json @@ -12,5 +12,29 @@ "oov_policy": "tagged_fallback", "glosses_checksum": "b7d6e1db7469f9fb81d4e376776c790d6511ba7ee44fdb4a7ac58ec65366103a", "definitional_layer": false, - "provenance": "adr-0090:domain_seed:2026-05-21" + "provenance": "adr-0097:reviewed:2026-05-21", + "domain_contract_version": 1, + "domain_id": "mathematics_logic", + "axioms": null, + "rules": null, + "teaching_chains": ["mathematics_logic_chains_v1"], + "eval_lanes": [ + { + "lane": "elementary_mathematics_ood", + "version": "v1", + "splits": ["dev", "public", "holdout"] + }, + { + "lane": "inference_closure", + "version": "v1", + "splits": ["dev", "public", "holdout"] + }, + { + "lane": "fabrication_control", + "version": "v1", + "splits": ["dev", "public", "holdout"] + } + ], + "reviewers": ["shay-j"], + "known_gaps": [] } diff --git a/language_packs/schema.py b/language_packs/schema.py index 5308c32a..60f86499 100644 --- a/language_packs/schema.py +++ b/language_packs/schema.py @@ -24,6 +24,12 @@ class LanguageRole(str, Enum): ARTICULATION_SURFACE = "articulation_surface" DEPTH_ROOT = "depth_root" DEPTH_RELATION = "depth_relation" + # ADR-0097 — added to recognize the domain-pack role used by + # en_mathematics_logic_v1, en_physics_v1, en_systems_software_v1. + # These packs declared role="domain_seed" since landing but the + # enum was never widened; ADR-0093's P1 predicate exposed the + # latent mismatch. + DOMAIN_SEED = "domain_seed" class OOVPolicy(str, Enum): diff --git a/tests/test_adr_0097_mathematics_logic_ratification.py b/tests/test_adr_0097_mathematics_logic_ratification.py new file mode 100644 index 00000000..68de9f5e --- /dev/null +++ b/tests/test_adr_0097_mathematics_logic_ratification.py @@ -0,0 +1,123 @@ +"""ADR-0097 — Mathematics-Logic Reasoning-Capable Ratification. + +Pins the load-bearing invariant: the capability ledger emits a row +for ``domain_id: mathematics_logic`` with ``status: reasoning-capable``, +its provenance points at ADR-0097, and ``expert-demo`` remains gated +until a future ADR attaches the audit-tour-equivalent reports. +""" + +from __future__ import annotations + +from core.capability import ( + evaluate_domain_contract, + ledger_report, +) + + +PACK_ID = "en_mathematics_logic_v1" +DOMAIN_ID = "mathematics_logic" + + +def _ledger_row(domain_id: str) -> dict: + report = ledger_report() + rows = {row["domain"]: row for row in report["domains"]} + assert domain_id in rows, f"missing ledger row for {domain_id!r}" + return rows[domain_id] + + +class TestRatificationPredicates: + """The nine ADR-0091 predicates all pass for math/logic.""" + + def test_all_nine_predicates_pass(self) -> None: + report = evaluate_domain_contract(PACK_ID) + assert report.contract_present is True + assert report.contract_valid is True + assert report.all_passed is True + ids = [p.predicate_id for p in report.predicates] + assert ids == ["P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9"] + failing = [p.predicate_id for p in report.predicates if not p.passed] + assert failing == [] + + def test_domain_id_matches(self) -> None: + report = evaluate_domain_contract(PACK_ID) + assert report.domain_id == DOMAIN_ID + + def test_provenance_points_at_adr_0097(self) -> None: + row = _ledger_row(DOMAIN_ID) + contracts = row["domain_contracts"] + assert contracts, "math/logic ledger row has no domain_contracts" + provenances = [c["contract"]["provenance"] for c in contracts] + assert any( + p.startswith("adr-0097:reviewed:") for p in provenances + ), f"expected an adr-0097 provenance, got {provenances}" + + +class TestLedgerStatus: + """ADR-0097 invariant: ``mathematics_logic_reasoning_capable_ledger_row``.""" + + def test_status_is_reasoning_capable(self) -> None: + row = _ledger_row(DOMAIN_ID) + assert row["status"] == "reasoning-capable" + + def test_expert_demo_predicate_is_false(self) -> None: + """ADR-0097 explicitly defers expert-demo to a follow-up ADR.""" + row = _ledger_row(DOMAIN_ID) + assert row["predicates"]["reasoning_capable"] is True + assert row["predicates"]["expert_demo"] is False + + def test_no_open_gaps(self) -> None: + row = _ledger_row(DOMAIN_ID) + assert row["open_gaps"] == [] + + def test_intent_shapes_meet_minimum(self) -> None: + row = _ledger_row(DOMAIN_ID) + assert row["intent_shapes_present"] >= 3 + assert row["intent_shapes_required"] == 3 + + def test_operator_chain_coverage_meets_minimum(self) -> None: + row = _ledger_row(DOMAIN_ID) + coverage = row["operator_chain_coverage"] + for op_family in ("transitive", "proof_chain", "contradiction"): + assert coverage[op_family]["ready"] is True + assert coverage[op_family]["chains_present"] >= 8 + + +class TestContractFieldShape: + """Ratified contract carries the exact fields ADR-0097 declares.""" + + def test_contract_fields_present(self) -> None: + report = evaluate_domain_contract(PACK_ID) + assert report.contract_present is True + + def test_three_eval_lanes_declared(self) -> None: + row = _ledger_row(DOMAIN_ID) + contract = row["domain_contracts"][0]["contract"] + lane_names = {lane["lane"] for lane in contract["eval_lanes"]} + assert lane_names == { + "elementary_mathematics_ood", + "inference_closure", + "fabrication_control", + } + + def test_eval_lanes_have_all_three_splits(self) -> None: + row = _ledger_row(DOMAIN_ID) + contract = row["domain_contracts"][0]["contract"] + for lane in contract["eval_lanes"]: + assert set(lane["splits"]) == {"dev", "public", "holdout"} + + def test_teaching_chains_corpus(self) -> None: + row = _ledger_row(DOMAIN_ID) + contract = row["domain_contracts"][0]["contract"] + assert contract["teaching_chains"] == ["mathematics_logic_chains_v1"] + + def test_axioms_and_rules_null_at_v1(self) -> None: + """ADR-0097 §Manifest additions: axioms/rules stay null at v1.""" + row = _ledger_row(DOMAIN_ID) + contract = row["domain_contracts"][0]["contract"] + assert contract["axioms"] is None + assert contract["rules"] is None + + def test_primary_reviewer_resolves(self) -> None: + row = _ledger_row(DOMAIN_ID) + contract = row["domain_contracts"][0]["contract"] + assert contract["reviewers"] == ["shay-j"] diff --git a/tests/test_capability_cli.py b/tests/test_capability_cli.py index da4dcd53..efb03a01 100644 --- a/tests/test_capability_cli.py +++ b/tests/test_capability_cli.py @@ -102,12 +102,17 @@ def test_capability_artifact_json_existing_is_content_addressed() -> None: def test_capability_domain_contract_json_absent_contract_is_noop() -> None: + """Packs without a domain contract pass the legacy structural-only + path. ADR-0093 added the default predicate-running mode; this test + pins the legacy shape under ``--structural-only``. + """ res = _run( "capability", "domain-contract", "--pack-id", "en_core_cognition_v1", "--json", + "--structural-only", ) assert res.returncode == 0, res.stderr body = json.loads(res.stdout) diff --git a/tests/test_domain_contract_predicates.py b/tests/test_domain_contract_predicates.py index ac946232..96c81a34 100644 --- a/tests/test_domain_contract_predicates.py +++ b/tests/test_domain_contract_predicates.py @@ -403,7 +403,7 @@ class TestEvalLaneArtifacts: class TestCli: def test_cli_returns_nonzero_on_missing_contract(self) -> None: - """The in-tree math/logic pack has no contract yet; CLI exits 1.""" + """A pack without a domain contract exits 1 under default mode.""" import os import subprocess import sys @@ -418,7 +418,7 @@ class TestCli: "capability", "domain-contract", "--pack-id", - "en_mathematics_logic_v1", + "en_core_cognition_v1", "--json", ], cwd=REPO_ROOT, @@ -447,7 +447,7 @@ class TestCli: "capability", "domain-contract", "--pack-id", - "en_mathematics_logic_v1", + "en_core_cognition_v1", "--json", "--structural-only", ],