Skip to content

Commit e1547f6

Browse files
authored
Add unit tests for IX cognition evidence bundle
1 parent 586e8a3 commit e1547f6

1 file changed

Lines changed: 146 additions & 0 deletions

File tree

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import io
2+
import json
3+
import tempfile
4+
import unittest
5+
from contextlib import redirect_stdout
6+
from pathlib import Path
7+
8+
from ix.cli import main
9+
from ix.evidence import EvidenceBundleWriter
10+
from ix.parser import parse_ix
11+
from ix.runtime import run_ix
12+
13+
14+
COGNITION_CONTRACT = '''
15+
attempt wave6_measured_cognition {
16+
purpose "Test measured correction"
17+
non_goal "Do not claim AGI"
18+
claim_boundary "Research candidate only"
19+
require human_approval reason "Human review required"
20+
handoff_contract IX-CognitionKernel schema ix.cognition.contract.v1
21+
22+
obligation prediction_before_trial {
23+
evidence_required prediction_record
24+
falsify_if prediction_missing
25+
}
26+
}
27+
'''
28+
29+
30+
class TestIXCognitionEvidenceBundle(unittest.TestCase):
31+
def test_evidence_writer_exports_cognition_contract_artifacts(self):
32+
source_file = self._write_ix(COGNITION_CONTRACT)
33+
program = parse_ix(source_file.read_text(encoding="utf-8"), filename=str(source_file))
34+
result = run_ix(program)
35+
output_dir = Path(self._tempdir.name) / "cognition-bundle"
36+
37+
bundle = EvidenceBundleWriter().write_bundle(
38+
result,
39+
source_file=source_file,
40+
output_dir=output_dir,
41+
command="unit-test",
42+
)
43+
44+
relative_files = set(bundle.relative_files())
45+
self.assertIn("contract.json", relative_files)
46+
self.assertIn("obligations.json", relative_files)
47+
self.assertIn("falsification-gates.json", relative_files)
48+
self.assertIn("claim-boundaries.json", relative_files)
49+
50+
manifest = json.loads((output_dir / "manifest.json").read_text(encoding="utf-8"))
51+
summary = json.loads((output_dir / "summary.json").read_text(encoding="utf-8"))
52+
contract = json.loads((output_dir / "contract.json").read_text(encoding="utf-8"))
53+
obligations = json.loads((output_dir / "obligations.json").read_text(encoding="utf-8"))
54+
gates = json.loads((output_dir / "falsification-gates.json").read_text(encoding="utf-8"))
55+
boundaries = json.loads((output_dir / "claim-boundaries.json").read_text(encoding="utf-8"))
56+
57+
self.assertIn("contract.json", manifest["artifact_files"])
58+
self.assertEqual(summary["counts"]["contract_attempts"], 1)
59+
self.assertEqual(summary["counts"]["contract_obligations"], 1)
60+
self.assertEqual(summary["counts"]["contract_evidence_requirements"], 1)
61+
self.assertEqual(summary["counts"]["contract_falsification_gates"], 1)
62+
self.assertEqual(contract["runtime_semantics"], "metadata_only_not_executed")
63+
self.assertEqual(contract["attempts"][0]["name"], "wave6_measured_cognition")
64+
self.assertEqual(obligations["obligations"][0]["id"], "prediction_before_trial")
65+
self.assertEqual(
66+
obligations["obligations"][0]["evidence_required"],
67+
["prediction_record"],
68+
)
69+
self.assertEqual(
70+
gates["falsification_gates"],
71+
[
72+
{
73+
"attempt": "wave6_measured_cognition",
74+
"obligation": "prediction_before_trial",
75+
"condition": "prediction_missing",
76+
}
77+
],
78+
)
79+
self.assertEqual(boundaries["attempts"][0]["purpose"], ["Test measured correction"])
80+
self.assertEqual(boundaries["attempts"][0]["non_goals"], ["Do not claim AGI"])
81+
self.assertEqual(
82+
boundaries["attempts"][0]["claim_boundaries"],
83+
["Research candidate only"],
84+
)
85+
self.assertIn(
86+
"does not certify AGI",
87+
(output_dir / "assurance-claims.md").read_text(encoding="utf-8"),
88+
)
89+
90+
def test_plain_ix_bundle_exports_empty_cognition_contract_artifacts(self):
91+
source_file = self._write_ix('reply "Plain IX"')
92+
program = parse_ix(source_file.read_text(encoding="utf-8"), filename=str(source_file))
93+
result = run_ix(program)
94+
output_dir = Path(self._tempdir.name) / "plain-bundle"
95+
96+
EvidenceBundleWriter().write_bundle(
97+
result,
98+
source_file=source_file,
99+
output_dir=output_dir,
100+
command="unit-test",
101+
)
102+
103+
summary = json.loads((output_dir / "summary.json").read_text(encoding="utf-8"))
104+
contract = json.loads((output_dir / "contract.json").read_text(encoding="utf-8"))
105+
obligations = json.loads((output_dir / "obligations.json").read_text(encoding="utf-8"))
106+
gates = json.loads((output_dir / "falsification-gates.json").read_text(encoding="utf-8"))
107+
boundaries = json.loads((output_dir / "claim-boundaries.json").read_text(encoding="utf-8"))
108+
109+
self.assertEqual(summary["counts"]["contract_attempts"], 0)
110+
self.assertEqual(summary["counts"]["contract_obligations"], 0)
111+
self.assertEqual(contract["attempts"], [])
112+
self.assertEqual(obligations["obligations"], [])
113+
self.assertEqual(gates["falsification_gates"], [])
114+
self.assertEqual(boundaries["attempts"], [])
115+
116+
def test_cli_evidence_command_lists_cognition_artifacts(self):
117+
source_file = self._write_ix(COGNITION_CONTRACT)
118+
output_dir = Path(self._tempdir.name) / "cli-cognition-bundle"
119+
120+
stdout = io.StringIO()
121+
with redirect_stdout(stdout):
122+
code = main(["evidence", str(source_file), "--out", str(output_dir)])
123+
124+
self.assertEqual(code, 0)
125+
output = stdout.getvalue()
126+
self.assertIn("- contract.json", output)
127+
self.assertIn("- obligations.json", output)
128+
self.assertIn("- falsification-gates.json", output)
129+
self.assertIn("- claim-boundaries.json", output)
130+
self.assertTrue((output_dir / "contract.json").exists())
131+
132+
def setUp(self):
133+
self._tempdir = tempfile.TemporaryDirectory()
134+
135+
def tearDown(self):
136+
self._tempdir.cleanup()
137+
138+
def _write_ix(self, source: str) -> Path:
139+
count = len(list(Path(self._tempdir.name).glob("*.ix")))
140+
path = Path(self._tempdir.name) / f"program_{count}.ix"
141+
path.write_text(source, encoding="utf-8")
142+
return path
143+
144+
145+
if __name__ == "__main__":
146+
unittest.main()

0 commit comments

Comments
 (0)