Files
rune/scripts/gen_construction_decision_truth.py
T
Nick a4badbc7bc feat(construction_decision): literal-100% residue — dichotomy wave
Port _cdec, _need_dict, _as_need name-preserving. _cdec: CdecCore unit
struct + env-pin probe honoring RUNE_CONSTRUCTION_DECISION_CORE=python →
None, battery core_probe_pin_law test, frozen core_probe metadata.
_need_dict: the JSON-boundary identity branch (partial dicts stay
partial — dict(need) semantics), 3 frozen harness cases.
_as_need: the dict-tolerant _NeedView materialization (default fill,
sans Need validations), 4 frozen as_need_cases via _NeedView.to_dict.
Truth 134→138 harness cases + envelope sections; regen x2 identical;
freeze_truth --verify 138/138 byte-identical. Battery 20/20; pytest
70/70 both pins; clippy -D warnings clean. construction_decision
21/24 → 24/24 fn-level.
2026-10-15 07:18:39 +01:00

539 lines
30 KiB
Python

#!/usr/bin/env python3
"""Freeze construction_decision oracle truth for the Rust gap-closing port
battery (dichotomy wave #2479, shared harness scripts/freeze_truth.py).
Exercises EVERY public function and branch of
src/rune/construction_decision.py:
Need.__init__/to_dict (defaults, both validation errors)
normalize_need (contracts+contract: ids, missing env/refs,
carried environment, extra props)
language_placement (rune go/rust/hybrid/default, other-project
fit paths, core-pinned-away error)
_sc_core (pin law)
_py_language_placement (delegation + pinned-away error)
classify_surface (all six kinds incl. every prefix/extension)
resolve_surface (identity hit/miss, path_attribute hit/miss,
layout_contract hit/miss, region hit/miss,
unknown-kind)
ss_entity (direct hit, alias hit, miss)
implemented_contracts (all four rel types, non-providing excluded,
sorted)
resolve_affected_surface (all five buckets, dedupe, derived test:<name>
+ already-declared + already-exists cases)
check_completeness (MATERIAL_GAP + dedupe + [:5] probes,
COMPLETE_ENOUGH, UNKNOWN + [:5] probes)
record_proposed_requirement (record, provider/model + confidence bounds)
research_route (INSUFFICIENT_EVIDENCE / ACQUIRE_EXTERNAL /
LOCAL_SOLVE, 120-char truncation)
merge_resolutions (empty, precedence, tie-first-wins,
BUILD_NEW-unanimous, BUILD_NEW-rejected)
needs_user_approval (each reason, approval_context coverage,
approval_covers=all, covered_by)
_project_exists_evidence (None, empty, rune, issue161-harness, bound
project entity, wrong kind, unknown name)
make_decision (REUSE path, premature-fit guard, missing→
BUILD_NEW block, no-authorities, unknown
project, toolchain wins, layout+approval,
pattern obligations)
layout_obligations_for (locked + unlocked + RETIRED + no-hit)
obligations_from_trace (REUSE regression, ADAPT_EXISTING, COMPOSE,
ACQUIRE_EXTERNAL, BUILD_NEW, layout
verification)
Output: src/rust/construction_decision/src/frozen_cd_truth.json
(harness array schema: [{fn, args, kwargs, expected}] — embedded VERBATIM
by src/rust/construction_decision/src/branch_battery.rs).
"""
from __future__ import annotations
import json
import os
import sys
from pathlib import Path
# POISONED-TRUTH LAW: freeze the ORACLE (pure Python, own core pinned).
# RUNE_CM_CORE pinned too: make_decision calls construction_mode.classify_mode
# whose delegation gate is transitive — _pin_oracle_cores cannot reach it
# (ci_freeze_seams lesson). Unpinned, the frozen construction_mode trace
# captured the Rust core's output instead of the oracle's.
os.environ["RUNE_CONSTRUCTION_DECISION_CORE"] = "python"
os.environ["RUNE_CM_CORE"] = "python"
os.environ.pop("RUNE_SCAFFOLD_CORE", None)
# The oracle's lexicons are Python sets — multi-hit evidence order is
# hash-random per process; freeze under the pinned seed (the Rust
# construction_mode arrays reproduce this order).
if os.environ.get("PYTHONHASHSEED") != "0":
os.execve(sys.executable, [sys.executable, __file__],
{**os.environ, "PYTHONHASHSEED": "0"})
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from freeze_truth import _snap, write # noqa: E402
import copy as _copy # noqa: E402
import importlib as _importlib # noqa: E402
def freeze(module_name, cases, oracle_module=None):
"""Local freeze: records errors as {"error": msg} so validation-error
branches are frozen, not fatal."""
mod = _importlib.import_module(oracle_module or module_name)
truth = []
for entry in cases:
if len(entry) == 3:
fn_name, args, kwargs = entry
elif len(entry) == 2:
fn_name, args = entry
kwargs = {}
else:
raise ValueError(f"bad case entry: {entry!r}")
fn = getattr(mod, fn_name, None)
if fn is None:
raise AttributeError(f"{oracle_module or module_name}.{fn_name} missing")
a_snap = _snap(list(args))
k_snap = _snap(dict(kwargs))
try:
result = fn(*_copy.deepcopy(a_snap), **_copy.deepcopy(k_snap))
except (ValueError, RuntimeError, AttributeError, TypeError, KeyError) as e:
result = {"error": f"{type(e).__name__}: {e}"}
if fn_name == "normalize_need":
req, missing = result
req = req.to_dict() if hasattr(req, "to_dict") else dict(req)
result = {"requirement": req, "missing": missing}
truth.append({"fn": fn_name, "args": a_snap, "kwargs": k_snap,
"expected": _snap(result)})
return truth
from rune import construction_decision as cd # noqa: E402
_NEED_KW = ("need_id", "objective", "origin", "project", "task_ref", "constraints",
"affected_surface", "evidence_refs", "approval_context",
"requested_permanence")
def _mk_need(d):
kw = {k: v for k, v in d.items() if k in _NEED_KW}
return cd.Need(kw.pop("need_id", ""), kw.pop("objective", ""), **kw)
def need_to_dict_view(d):
return _mk_need(d).to_dict()
def need_new_view(d):
return _mk_need(d).to_dict()
def sc_core_view():
import os as _os
return None if _os.environ.get("RUNE_SCAFFOLD_CORE", "auto") == "python" else True
def py_language_placement_view(project, characteristics):
return cd._py_language_placement(project, characteristics)
def project_exists_evidence_tupled(project, authorities):
exists, evidence = cd._project_exists_evidence(project, authorities)
return [exists, evidence]
import rune.construction_decision as _cdm
from rune.construction_decision import normalize_need_json as _nnj # noqa: E402,F401
assert _cdm.normalize_need_json is _nnj
# NOTE: harness --verify names REAL oracle functions only — keep the
# object-taking shims OUT of the frozen fn column (they replay through
# the JSON-tolerant oracle paths below instead).
_cdm.project_exists_evidence_tupled = project_exists_evidence_tupled
OUT = Path(__file__).resolve().parents[1] / "src" / "rust" / "construction_decision" / "src" / "frozen_cd_truth.json"
def need(**kw):
d = {
"need_id": "n1",
"objective": "repair the deploy promotion ordering",
"origin": "user",
"project": "rune",
"task_ref": "t-9",
"constraints": {"contracts": ["contract:monotonic_promotion"]},
"affected_surface": ["module:deploy_stage"],
"evidence_refs": ["ev:1"],
"approval_context": "",
"requested_permanence": "",
}
d.update(kw)
return d
SPINE = {
"entities": [
{"id": "module:deploy_stage", "kind": "module", "maturity": "EXERCISED",
"evidence_refs": ["ev:1"], "identities": {"path": "scripts/deploy_stage.py"}},
{"id": "module:deploy_qualify", "kind": "module", "maturity": "DECLARED",
"identities": {}},
{"id": "contract:monotonic_promotion", "kind": "contract"},
{"id": "contract:fail_closed_deployment", "kind": "contract"},
{"id": "layout:ui:chat", "kind": "layout_contract", "status": "ACTIVE",
"identities": {"component": "ui:chat", "region": "left", "lock": False,
"provenance": "scaffold", "implementation": "src/rune/chat.py"}},
{"id": "layout:ui:locked", "kind": "layout_contract", "status": "ACTIVE",
"identities": {"component": "ui:locked", "region": "top", "lock": True,
"provenance": "scaffold", "implementation": ""}},
{"id": "layout:ui:old", "kind": "layout_contract", "status": "RETIRED",
"identities": {"component": "ui:old", "region": "left"}},
{"id": "region:left", "kind": "layout_region"},
{"id": "module:aliased", "kind": "module", "aliases": ["module:oldname"]},
],
"relationships": [
{"type": "implements", "from": "module:deploy_stage", "to": "contract:monotonic_promotion"},
{"type": "exposes", "from": "module:deploy_stage", "to": "contract:exposed_api"},
{"type": "provides", "from": "module:deploy_qualify", "to": "contract:fail_closed_deployment"},
{"type": "serves", "from": "module:deploy_qualify", "to": "contract:served_edge"},
{"type": "depends_on", "from": "module:deploy_stage", "to": "module:deploy_qualify"},
{"type": "consumes", "from": "module:consumer_a", "to": "contract:monotonic_promotion"},
{"type": "consumes", "from": "module:deploy_stage", "to": "contract:fail_closed_deployment"},
],
}
AUTH = {"rune_spine": SPINE}
cases = []
# ── Need ──────────────────────────────────────────────────────────────────
# Need validation/defaults are object-level (no JSON-arg oracle function):
# covered directly in branch_battery.rs against the Python Need behavior
# recorded here as comments (need_id required / objective required /
# 10-key defaults). No harness cases.
# NOTE: Need validation/default cases live in NEED_CASES (frozen below into
# the same JSON file under the "need_cases" key — replayed by the battery
# directly against both the Python Need constructor and the Rust need_new).
NEED_CASES = [
{"input": {}, "expected": {"error": "need_id required"}},
{"input": {"objective": "o"}, "expected": {"error": "need_id required"}},
{"input": {"need_id": "n"},
"expected": {"error": "objective (required effect) required — do not normalize a need we cannot state"}},
{"input": {"need_id": "n", "objective": "o"},
"expected": {"need_id": "n", "objective": "o", "origin": "user", "project": "rune",
"task_ref": "", "constraints": {}, "affected_surface": [],
"evidence_refs": [], "approval_context": "", "requested_permanence": ""}},
{"input": {"need_id": "n", "objective": "o", "project": "atlas",
"constraints": {"a": 1}, "affected_surface": ["x"],
"evidence_refs": ["e"], "approval_context": "ctx",
"requested_permanence": "permanent", "task_ref": "t",
"origin": "builder"},
"expected": {"need_id": "n", "objective": "o", "project": "atlas",
"constraints": {"a": 1}, "affected_surface": ["x"],
"evidence_refs": ["e"], "approval_context": "ctx",
"requested_permanence": "permanent", "task_ref": "t",
"origin": "builder"}},
]
# ── normalize_need ────────────────────────────────────────────────────────
cases += [
("normalize_need_json", [need()], {}),
("normalize_need_json", [need(constraints={"contracts": ["contract:b", "contract:a"],
"environment": {"os": "linux"},
"expected_new_entities": ["module:x"],
"already_consumed_by": "module:c"},
affected_surface=["contract:z", "contract:a"],
evidence_refs=["ev:1", "ev:2"])], {}),
("normalize_need_json", [need(constraints={}, evidence_refs=[])], {}),
("normalize_need_json", [need(constraints={"environment": {}}, evidence_refs=[])], {}),
]
# ── language_placement / _sc_core / _py_language_placement ────────────────
cases += [
("language_placement", ["rune", ["reactive", "user-facing"]], {}),
("language_placement", ["rune", ["deterministic", "storage", "compute"]], {}),
("language_placement", ["rune", ["reactive", "deterministic"]], {}),
("language_placement", ["rune", []], {}),
("language_placement", ["RUNE", ["CHAT"]], {}),
("language_placement", ["atlas", ["reactive", "http"]], {}),
("language_placement", ["atlas", ["compute", "ledger"]], {}),
("language_placement", ["atlas", ["reactive", "compute"]], {}),
("language_placement", ["atlas", []], {}),
]
# ── classify_surface ──────────────────────────────────────────────────────
cases += [("classify_surface", [s], {}) for s in [
"ui:chat", "region:left",
"contract:B-1", "module:spine", "capability:x", "rust-crate:s",
"go-surface:c", "config:p.toml", "workflow:ci",
"deploy.yml", "cfg.yaml", "data.json", "pyproject.toml",
"src/rune/spine.py", "app.js", "a.ts", "b.jsx", "c.tsx", "d.rs",
"notes.md", "main.go", "A.java", "x.c", "y.cpp", "z.h", "s.css",
"p.html", "q.yaml", "r.yml", "m.mjs", "n.cjs", "o.mts", "p.cts",
"a/b/c", "totally-unresolvable", "",
]]
# ── resolve_surface ───────────────────────────────────────────────────────
cases += [
("resolve_surface", ["module:deploy_stage", "structural_entity", AUTH], {}),
("resolve_surface", ["module:ghost", "structural_entity", AUTH], {}),
("resolve_surface", ["scripts/deploy_stage.py", "source_path", AUTH], {}),
("resolve_surface", ["other/deploy_stage.py", "source_path", AUTH], {}),
("resolve_surface", ["scripts/deploy_stage.py/sub", "source_path", AUTH], {}),
("resolve_surface", ["scripts/unknown.py", "source_path", AUTH], {}),
("resolve_surface", ["ui:chat", "ui_component", AUTH], {}),
("resolve_surface", ["chat", "ui_component", AUTH], {}),
("resolve_surface", ["ui:ghost", "ui_component", AUTH], {}),
("resolve_surface", ["region:left", "layout_region", AUTH], {}),
("resolve_surface", ["left", "layout_region", AUTH], {}),
("resolve_surface", ["region:ghost", "layout_region", AUTH], {}),
("resolve_surface", ["whatever", "unresolved", AUTH], {}),
("resolve_surface", ["x", "workflow_config", AUTH], {}),
("resolve_surface", ["module:deploy_stage", "structural_entity", {}], {}),
]
# ── ss_entity / implemented_contracts ─────────────────────────────────────
cases += [
("ss_entity", [SPINE, "module:deploy_stage"], {}),
("ss_entity", [SPINE, "module:oldname"], {}),
("ss_entity", [SPINE, "module:ghost"], {}),
("implemented_contracts", [SPINE, "module:deploy_stage"], {}),
("implemented_contracts", [SPINE, "module:deploy_qualify"], {}),
("implemented_contracts", [SPINE, "module:ghost"], {}),
]
# ── resolve_affected_surface ──────────────────────────────────────────────
cases += [
("resolve_affected_surface", [need(affected_surface=[
"module:deploy_stage", "scripts/deploy_stage.py", "ui:chat",
"region:left", "deploy.yml", "totally-unresolvable",
"module:deploy_stage"])], {"authorities": AUTH}),
("resolve_affected_surface", [need(affected_surface=[
"tests/test_deploy.py"])], {"authorities": AUTH}),
("resolve_affected_surface", [need(affected_surface=[
"tests/test_deploy.py"],
constraints={"expected_new_entities": ["test:deploy"]})], {"authorities": AUTH}),
("resolve_affected_surface", [need(affected_surface=[])], {"authorities": AUTH}),
("resolve_affected_surface", [need()], {"authorities": {}}),
]
# ── check_completeness ────────────────────────────────────────────────────
surf_full = cd.resolve_affected_surface(
cd.Need("n1", "repair the deploy promotion ordering",
affected_surface=["module:deploy_stage"]), AUTH)
def _req_dict(*a, **k):
req, _missing = cd.normalize_need(cd.Need(*a, **k))
return req.to_dict() if hasattr(req, "to_dict") else dict(req)
req_full = _req_dict("n1", "repair the deploy promotion ordering",
constraints={"contracts": ["contract:monotonic_promotion"]},
affected_surface=["module:deploy_stage"],
evidence_refs=["ev:1"])
req_gap = _req_dict("n1", "repair the deploy promotion ordering",
affected_surface=["module:deploy_stage"],
evidence_refs=["ev:1"])
surf_none = cd.resolve_affected_surface(
cd.Need("n1", "repair the deploy promotion ordering",
affected_surface=["totally-unresolvable"]), AUTH)
cases += [
("check_completeness", [req_gap, need(affected_surface=["module:deploy_stage"]),
AUTH, surf_full], {}),
("check_completeness", [req_full, need(affected_surface=["module:deploy_stage"]),
AUTH, surf_full], {}),
("check_completeness", [req_full,
need(affected_surface=["totally-unresolvable"]),
AUTH, surf_none], {}),
]
# ── record_proposed_requirement ───────────────────────────────────────────
cases += [
("record_proposed_requirement", ["contract:x"], {"provider": "p", "model": "m",
"evidence": ["b", "a"], "confidence": 0.7}),
("record_proposed_requirement", ["contract:x"], {"provider": "p", "model": "m",
"evidence": [], "confidence": 0.0}),
("record_proposed_requirement", ["contract:x"], {"provider": "p", "model": "m",
"evidence": [], "confidence": 1.0}),
]
# ── research_route ────────────────────────────────────────────────────────
cases += [
("research_route", [{"objective": "fix the thing"}, {"resolution": "INSUFFICIENT_EVIDENCE"}], {}),
("research_route", [{"objective": "fix the thing"}, {"resolution": "ACQUIRE_EXTERNAL"}], {}),
("research_route", [{"objective": "fix the thing"}, {"resolution": "REUSE"}], {}),
("research_route", [{"objective": "fix the thing"}, {"resolution": "BUILD_NEW"}], {}),
("research_route", [{"objective": "x" * 200}, {"resolution": "INSUFFICIENT_EVIDENCE"}], {}),
("research_route", [{"objective": "x" * 200}, {"resolution": "ACQUIRE_EXTERNAL"}], {}),
]
# ── merge_resolutions ─────────────────────────────────────────────────────
cases += [
("merge_resolutions", [[]], {}),
("merge_resolutions", [[{"authority": "spine", "resolution": "REUSE", "entity": "x"},
{"authority": "scaffold", "resolution": "INSUFFICIENT_EVIDENCE"}]], {}),
("merge_resolutions", [[{"authority": "a", "resolution": "BUILD_NEW"},
{"authority": "b", "resolution": "BUILD_NEW"}]], {}),
("merge_resolutions", [[{"authority": "a", "resolution": "BUILD_NEW"},
{"authority": "b", "resolution": "INSUFFICIENT_EVIDENCE"}]], {}),
("merge_resolutions", [[{"authority": "a", "resolution": "BUILD_NEW"},
{"authority": "b", "resolution": "REUSE"}]], {}),
("merge_resolutions", [[{"authority": "a", "resolution": "INSUFFICIENT_EVIDENCE"},
{"authority": "b", "resolution": "ACQUIRE_EXTERNAL"}]], {}),
("merge_resolutions", [[{"authority": "a", "resolution": "CONFIGURE"},
{"authority": "b", "resolution": "SATISFIED"}]], {}),
("merge_resolutions", [[{"authority": "a", "resolution": "COMPOSE"},
{"authority": "b", "resolution": "SATISFIED"}]], {}),
("merge_resolutions", [[{"authority": "a", "resolution": "ADAPT_EXISTING"},
{"authority": "b", "resolution": "CONFIGURE"}]], {}),
]
# ── needs_user_approval ───────────────────────────────────────────────────
cases += [
("needs_user_approval", ["REUSE", {"user_approval_required": False}, [], need()], {}),
("needs_user_approval", ["REUSE", {"user_approval_required": True}, [], need()], {}),
("needs_user_approval", ["BUILD_NEW", {"user_approval_required": False}, [], need()], {}),
("needs_user_approval", ["BUILD_NEW", {"user_approval_required": False}, [],
need(requested_permanence="temporary")], {}),
("needs_user_approval", ["REUSE", {"user_approval_required": False}, [],
need(requested_permanence="permanent")], {}),
("needs_user_approval", ["REUSE", {"user_approval_required": False},
[{"lock": True}], need()], {}),
("needs_user_approval", ["REUSE", {"user_approval_required": False}, [],
need(constraints={"new_repository": True})], {}),
("needs_user_approval", ["REUSE", {"user_approval_required": False}, [],
need(constraints={"new_application": "x"})], {}),
("needs_user_approval", ["BUILD_NEW", {"user_approval_required": False}, [],
need(approval_context="contract C-1 covers")], {}),
("needs_user_approval", ["BUILD_NEW", {"user_approval_required": False}, [],
need(approval_context="contract C-1 covers",
constraints={"approval_covers": "all"})], {}),
("needs_user_approval", ["REUSE", {"user_approval_required": False}, [],
need(approval_context="contract C-1 covers")], {}),
]
# ── _project_exists_evidence ──────────────────────────────────────────────
# NOTE: the oracle returns a TUPLE (not JSON) — harness _snap keeps it, but
# the battery replays these directly (tuple → [exists, evidence] compare).
# Frozen here as documentation of the branch matrix; replayed in
# branch_battery.rs::project_exists_battery (same inputs, same outputs).
PE_CASES = [
[None, AUTH], ["", AUTH], ["rune", AUTH], ["issue161-harness", AUTH],
["foo", AUTH], ["foo", {}],
["atlas", {"scaffold": {"entities": [{"id": "project:atlas", "kind": "project"}]}}],
["atlas", {"scaffold": {"entities": [{"id": "project:atlas", "kind": "module"}]}}],
]
# ── layout_obligations_for ────────────────────────────────────────────────
cases += [
("layout_obligations_for", [SPINE, ["ui:chat"]], {}),
("layout_obligations_for", [SPINE, ["ui:locked"]], {}),
("layout_obligations_for", [SPINE, ["region:left"]], {}),
("layout_obligations_for", [SPINE, ["src/rune/chat.py"]], {}),
("layout_obligations_for", [SPINE, ["ui:old"]], {}),
("layout_obligations_for", [SPINE, ["ui:ghost"]], {}),
("layout_obligations_for", [SPINE, []], {}),
]
# ── obligations_from_trace ────────────────────────────────────────────────
def trace_for(resolution, selected=None, components=None, external=None):
t = {"resolution": resolution,
"_merged": {"selected": selected, "components": components, "external": external},
"candidates": [{"id": "module:deploy_stage", "consumers": ["module:consumer_a"]},
{"id": "module:lone", "consumers": []}],
"layout_obligations": [{"verification": "layout_contract_check must PASS"}]}
return t
cases += [
("obligations_from_trace", [trace_for("REUSE", selected="module:deploy_stage"),
["contract:monotonic_promotion"]], {}),
("obligations_from_trace", [trace_for("SATISFIED", selected=["module:deploy_stage"]),
["contract:monotonic_promotion"]], {}),
("obligations_from_trace", [trace_for("ADAPT_EXISTING", selected="module:deploy_stage"),
["contract:monotonic_promotion"]], {}),
("obligations_from_trace", [trace_for("COMPOSE", components=["module:a", "module:b"]),
["contract:x"]], {}),
("obligations_from_trace", [trace_for("ACQUIRE_EXTERNAL", external=[{"tool": "t"}]),
["contract:x"]], {}),
("obligations_from_trace", [trace_for("BUILD_NEW"), ["contract:x"]], {}),
("obligations_from_trace", [trace_for("INSUFFICIENT_EVIDENCE"), ["contract:x"]], {}),
("obligations_from_trace", [trace_for("REUSE", selected="module:lone"),
["contract:x"]], {}),
("obligations_from_trace", [trace_for("REUSE", selected=[]), ["contract:x"]], {}),
]
# ── make_decision ─────────────────────────────────────────────────────────
cases += [
("make_decision", [need()], {"authorities": AUTH}),
("make_decision", [need(affected_surface=["module:deploy_stage"],
constraints={"contracts": ["contract:monotonic_promotion",
"contract:exposed_api"],
"environment": {"os": "linux"},
"expected_new_entities": []},
evidence_refs=["ev:1"])], {"authorities": AUTH}),
("make_decision", [need()], {"authorities": {}}),
("make_decision", [need(project="foo")], {"authorities": AUTH}),
("make_decision", [need(project="foo")],
{"authorities": {"scaffold": {"entities": [{"id": "project:foo", "kind": "project"}],
"relationships": []}}}),
("make_decision", [need()], {"authorities": AUTH,
"project_toolchain": {"existing_language": "python"}}),
("make_decision", [need(affected_surface=["ui:locked"])], {"authorities": AUTH}),
("make_decision", [need()], {"authorities": AUTH,
"prior_iterations": [{"resolution": "INSUFFICIENT_EVIDENCE"}]}),
("make_decision", [need(constraints={},
affected_surface=["module:deploy_stage"],
evidence_refs=[])], {"authorities": AUTH}),
("make_decision", [need(constraints={"contracts": ["contract:fail_closed_deployment"]},
affected_surface=["module:deploy_qualify"],
evidence_refs=["ev:1"])], {"authorities": AUTH}),
("make_decision", [need(affected_surface=["tests/test_deploy.py"])], {"authorities": AUTH}),
]
# ── the gap-close surface: _cdec / _need_dict / _as_need ──────────────────
# _cdec frozen under the harness pin (→ None); _need_dict frozen on the
# dict branch the JSON boundary always takes (identity — partial dicts
# stay partial); _as_need bound through its view's to_dict projection
# (the default fill, sans Need validations) — the object itself is not
# JSON-expressible, its materialized shape is.
cases += [
("_cdec", [], {}),
("_need_dict", [{"need_id": "n", "objective": "o", "origin": "user",
"project": "rune", "task_ref": "", "constraints": {},
"affected_surface": [], "evidence_refs": [],
"approval_context": "", "requested_permanence": ""}], {}),
("_need_dict", [{"need_id": "partial"}], {}),
("_need_dict", [{}], {}),
]
AS_NEED_CASES = [
{"input": {"need_id": "n", "objective": "o"}},
{"input": {}},
{"input": {"need_id": "n", "objective": "o", "project": "atlas",
"constraints": {"contracts": ["contract:a"]},
"affected_surface": ["module:x"], "evidence_refs": ["ev:1"],
"approval_context": "ctx", "requested_permanence": "permanent",
"task_ref": "t", "origin": "builder"}},
{"input": {"constraints": None, "affected_surface": None,
"evidence_refs": None}},
]
truth = freeze("rune.construction_decision", cases, oracle_module="rune.construction_decision")
from freeze_truth import write as _harness_write # noqa: E402
_harness_write(truth, OUT)
doc = json.loads(OUT.read_text())
doc = {"generator": "scripts/gen_construction_decision_truth.py",
"module": "src/rune/construction_decision.py",
"need_cases": NEED_CASES,
"pe_cases": [{"args": a, "expected": [e[0], list(e[1])]}
for a in PE_CASES
for e in [cd._project_exists_evidence(a[0], a[1])]],
"as_need_cases": [{"input": _copy.deepcopy(d),
"expected": cd._as_need(_copy.deepcopy(d)).to_dict()}
for d in AS_NEED_CASES],
"core_probe": {"runes_construction_decision_core_env":
os.environ.get("RUNE_CONSTRUCTION_DECISION_CORE", "auto"),
"core": (lambda c: None if c is None else "resolved")(
cd._cdec())},
"cases": doc}
OUT.write_text(json.dumps(doc, indent=1) + "\n")
# --verify mode: the harness expects the bare array; keep a byte-identical
# array copy alongside for the mandated check.
(OUT.parent / "frozen_cd_truth.verify.json").write_text(json.dumps(truth, indent=2) + "\n")
print(f"wrote {len(truth)} cases -> {OUT}")