devlyn-cli 3.1.0 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/bin/devlyn.js +69 -23
- package/config/skills/_shared/adapters/claude.md +3 -2
- package/config/skills/_shared/adapters/codex.md +1 -1
- package/config/skills/_shared/archive_run.py +5 -3
- package/config/skills/_shared/codex-config.md +13 -8
- package/config/skills/_shared/codex-monitored.sh +12 -19
- package/config/skills/_shared/collect-codex-findings.py +1 -0
- package/config/skills/_shared/engine-preflight.md +2 -0
- package/config/skills/_shared/finish-gate.py +4 -1
- package/config/skills/_shared/invocation-receipt.py +194 -5
- package/config/skills/_shared/judge-role-evidence.py +33 -6
- package/config/skills/_shared/phase-prompt-render.py +2 -0
- package/config/skills/_shared/platform-support.py +370 -0
- package/config/skills/_shared/process-evidence.py +20 -3
- package/config/skills/_shared/resolve-bootstrap.py +131 -63
- package/config/skills/_shared/resolve-stop-hook.py +3 -1
- package/config/skills/_shared/role-config.py +7 -5
- package/config/skills/_shared/run-bounded.py +35 -21
- package/config/skills/_shared/spec-verify-check.py +393 -167
- package/config/skills/_shared/state-phase-write.py +26 -9
- package/config/skills/_shared/task-complete.py +337 -97
- package/config/skills/_shared/verify-merge-findings.py +9 -8
- package/config/skills/devlyn:ideate/references/spec-template.md +1 -1
- package/config/skills/devlyn:resolve/SKILL.md +10 -7
- package/config/skills/devlyn:resolve/references/free-form-mode.md +1 -1
- package/config/skills/devlyn:resolve/references/phases/build-gate.md +1 -1
- package/config/skills/devlyn:resolve/references/phases/verify.md +14 -10
- package/config/skills/devlyn:resolve/references/state-schema.md +1 -1
- package/config/skills/devlyn:resolve/references/task-completion.md +28 -1
- package/package.json +1 -1
- package/scripts/lint-skills.sh +6 -5
|
@@ -89,6 +89,7 @@ Exit codes:
|
|
|
89
89
|
|
|
90
90
|
from __future__ import annotations
|
|
91
91
|
|
|
92
|
+
import runpy
|
|
92
93
|
import importlib.util
|
|
93
94
|
import json
|
|
94
95
|
import hashlib
|
|
@@ -843,10 +844,21 @@ def validate_expected_shape(data) -> str | None:
|
|
|
843
844
|
return None
|
|
844
845
|
|
|
845
846
|
|
|
846
|
-
def
|
|
847
|
+
def validate_inline_shape(data: object) -> str | None:
|
|
848
|
+
if isinstance(data, dict):
|
|
849
|
+
unknown = sorted(set(data) - {"verification_commands"})
|
|
850
|
+
if unknown:
|
|
851
|
+
return (
|
|
852
|
+
f"unsupported inline key(s): {', '.join(unknown)}; inline carriers "
|
|
853
|
+
"support only verification_commands. Encode these checks as commands, "
|
|
854
|
+
"or use a real spec with sibling spec.expected.json."
|
|
855
|
+
)
|
|
856
|
+
return validate_shape(data) or validate_expected_shape(data)
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
def validate_expected_against_sibling_spec(spec_path: Path, data: object) -> str | None:
|
|
847
860
|
if not isinstance(data, dict):
|
|
848
861
|
return None
|
|
849
|
-
spec_path = expected_path.with_name("spec.md")
|
|
850
862
|
if not spec_path.is_file():
|
|
851
863
|
return None
|
|
852
864
|
try:
|
|
@@ -880,8 +892,7 @@ def validate_expected_against_sibling_spec(expected_path: Path, data: object) ->
|
|
|
880
892
|
)
|
|
881
893
|
|
|
882
894
|
|
|
883
|
-
def validate_sibling_spec_complexity(
|
|
884
|
-
spec_path = expected_path.with_name("spec.md")
|
|
895
|
+
def validate_sibling_spec_complexity(spec_path: Path) -> str | None:
|
|
885
896
|
if not spec_path.is_file():
|
|
886
897
|
return None
|
|
887
898
|
try:
|
|
@@ -1012,7 +1023,7 @@ def resolve_required_risk_probe_requirements(
|
|
|
1012
1023
|
return ([], err)
|
|
1013
1024
|
reqs = (data or {}).get("required_risk_probe_requirements", [])
|
|
1014
1025
|
else:
|
|
1015
|
-
_section_found, block = extract_verification_block(source_md.read_text())
|
|
1026
|
+
_section_found, block = extract_verification_block(source_md.read_text(encoding="utf-8"))
|
|
1016
1027
|
if block is None:
|
|
1017
1028
|
return ([], None)
|
|
1018
1029
|
try:
|
|
@@ -1022,7 +1033,7 @@ def resolve_required_risk_probe_requirements(
|
|
|
1022
1033
|
reqs = parsed.get("required_risk_probe_requirements", []) if isinstance(parsed, dict) else []
|
|
1023
1034
|
if not isinstance(reqs, list):
|
|
1024
1035
|
return ([], "required_risk_probe_requirements must be a list")
|
|
1025
|
-
verification_text = extract_verification_text(source_md.read_text())
|
|
1036
|
+
verification_text = extract_verification_text(source_md.read_text(encoding="utf-8"))
|
|
1026
1037
|
for i, req in enumerate(reqs):
|
|
1027
1038
|
err = validate_required_risk_probe_requirement(req, i, verification_text)
|
|
1028
1039
|
if err:
|
|
@@ -1044,12 +1055,12 @@ def load_risk_probes(
|
|
|
1044
1055
|
if source_md is None or not source_md.is_file():
|
|
1045
1056
|
return ([], "risk-probes.jsonl exists but source markdown is unavailable")
|
|
1046
1057
|
|
|
1047
|
-
verification_text = extract_verification_text(source_md.read_text())
|
|
1058
|
+
verification_text = extract_verification_text(source_md.read_text(encoding="utf-8"))
|
|
1048
1059
|
if not verification_text:
|
|
1049
1060
|
return ([], "risk-probes.jsonl exists but source has no <!-- devlyn:verification --> section")
|
|
1050
1061
|
|
|
1051
1062
|
probes: list[dict] = []
|
|
1052
|
-
for index, line in enumerate(probes_path.read_text().splitlines()):
|
|
1063
|
+
for index, line in enumerate(probes_path.read_text(encoding="utf-8").splitlines()):
|
|
1053
1064
|
if not line.strip():
|
|
1054
1065
|
continue
|
|
1055
1066
|
try:
|
|
@@ -1099,7 +1110,7 @@ def read_source(work: Path, devlyn_dir: Path) -> tuple[str | None, Path | None]:
|
|
|
1099
1110
|
if not state_path.is_file():
|
|
1100
1111
|
return (None, None)
|
|
1101
1112
|
try:
|
|
1102
|
-
state = loads_strict_json(state_path.read_text())
|
|
1113
|
+
state = loads_strict_json(state_path.read_text(encoding="utf-8"))
|
|
1103
1114
|
except (ValueError, OSError):
|
|
1104
1115
|
return (None, None)
|
|
1105
1116
|
src = state.get("source") or {}
|
|
@@ -1123,7 +1134,7 @@ def read_state(devlyn_dir: Path) -> dict:
|
|
|
1123
1134
|
if not state_path.is_file():
|
|
1124
1135
|
return {}
|
|
1125
1136
|
try:
|
|
1126
|
-
data = loads_strict_json(state_path.read_text())
|
|
1137
|
+
data = loads_strict_json(state_path.read_text(encoding="utf-8"))
|
|
1127
1138
|
except (ValueError, OSError):
|
|
1128
1139
|
return {}
|
|
1129
1140
|
return data if isinstance(data, dict) else {}
|
|
@@ -1176,7 +1187,7 @@ def source_integrity_error(src_type: str | None, state: dict, source_md: Path |
|
|
|
1176
1187
|
|
|
1177
1188
|
def load_expected_contract(expected_path: Path) -> tuple[dict | None, str | None]:
|
|
1178
1189
|
try:
|
|
1179
|
-
data = loads_strict_json(expected_path.read_text())
|
|
1190
|
+
data = loads_strict_json(expected_path.read_text(encoding="utf-8"))
|
|
1180
1191
|
except ValueError as e:
|
|
1181
1192
|
return (None, f"{expected_path} has invalid JSON: {e}")
|
|
1182
1193
|
except OSError as e:
|
|
@@ -1198,7 +1209,7 @@ def stage_from_source(md: Path, devlyn_dir: Path) -> tuple[bool, str | None]:
|
|
|
1198
1209
|
error=None → the sentinel is absent entirely (handwritten spec or
|
|
1199
1210
|
generated source missing the contract).
|
|
1200
1211
|
"""
|
|
1201
|
-
section_found, block = extract_verification_block(md.read_text())
|
|
1212
|
+
section_found, block = extract_verification_block(md.read_text(encoding="utf-8"))
|
|
1202
1213
|
if not section_found:
|
|
1203
1214
|
return (False, None)
|
|
1204
1215
|
if block is None:
|
|
@@ -1207,12 +1218,12 @@ def stage_from_source(md: Path, devlyn_dir: Path) -> tuple[bool, str | None]:
|
|
|
1207
1218
|
data = loads_strict_json(block)
|
|
1208
1219
|
except ValueError as e:
|
|
1209
1220
|
return (False, f"`<!-- devlyn:verification -->` ```json``` block in {md} has invalid JSON: {e}")
|
|
1210
|
-
err =
|
|
1221
|
+
err = validate_inline_shape(data)
|
|
1211
1222
|
if err:
|
|
1212
1223
|
return (False, f"`<!-- devlyn:verification -->` ```json``` block in {md}: {err}")
|
|
1213
1224
|
normalized = {"verification_commands": data["verification_commands"]}
|
|
1214
1225
|
devlyn_dir.mkdir(parents=True, exist_ok=True)
|
|
1215
|
-
(devlyn_dir / "spec-verify.json").write_text(json.dumps(normalized, indent=2) + "\n")
|
|
1226
|
+
(devlyn_dir / "spec-verify.json").write_text(json.dumps(normalized, indent=2) + "\n", encoding="utf-8")
|
|
1216
1227
|
return (True, None)
|
|
1217
1228
|
|
|
1218
1229
|
|
|
@@ -1235,6 +1246,9 @@ def stage_from_expected(
|
|
|
1235
1246
|
if err:
|
|
1236
1247
|
return (True, False, err, expected_path, None)
|
|
1237
1248
|
assert data is not None
|
|
1249
|
+
err = validate_sibling_spec_complexity(md) or validate_expected_against_sibling_spec(md, data)
|
|
1250
|
+
if err:
|
|
1251
|
+
return (True, False, f"{expected_path}: {err}", expected_path, None)
|
|
1238
1252
|
commands = data.get("verification_commands")
|
|
1239
1253
|
if not commands:
|
|
1240
1254
|
spec_path = devlyn_dir / "spec-verify.json"
|
|
@@ -1243,7 +1257,7 @@ def stage_from_expected(
|
|
|
1243
1257
|
return (True, False, None, expected_path, data)
|
|
1244
1258
|
normalized = {"verification_commands": commands}
|
|
1245
1259
|
devlyn_dir.mkdir(parents=True, exist_ok=True)
|
|
1246
|
-
(devlyn_dir / "spec-verify.json").write_text(json.dumps(normalized, indent=2) + "\n")
|
|
1260
|
+
(devlyn_dir / "spec-verify.json").write_text(json.dumps(normalized, indent=2) + "\n", encoding="utf-8")
|
|
1247
1261
|
return (True, True, None, expected_path, data)
|
|
1248
1262
|
|
|
1249
1263
|
|
|
@@ -1278,7 +1292,7 @@ def write_malformed_finding(
|
|
|
1278
1292
|
"blocking": True,
|
|
1279
1293
|
"status": "open",
|
|
1280
1294
|
}
|
|
1281
|
-
with findings_path.open("w") as fh:
|
|
1295
|
+
with findings_path.open("w", encoding="utf-8") as fh:
|
|
1282
1296
|
fh.write(json.dumps(finding) + "\n")
|
|
1283
1297
|
|
|
1284
1298
|
|
|
@@ -1300,7 +1314,7 @@ def write_risk_probe_integrity_finding(devlyn_dir: Path, error: str) -> None:
|
|
|
1300
1314
|
"blocking": True,
|
|
1301
1315
|
"status": "open",
|
|
1302
1316
|
}
|
|
1303
|
-
with findings_path.open("w") as fh:
|
|
1317
|
+
with findings_path.open("w", encoding="utf-8") as fh:
|
|
1304
1318
|
fh.write(json.dumps(finding) + "\n")
|
|
1305
1319
|
|
|
1306
1320
|
|
|
@@ -1321,14 +1335,14 @@ def diff_text_for_expected(work: Path, devlyn_dir: Path, state: dict) -> tuple[s
|
|
|
1321
1335
|
external_diff = devlyn_dir / "external-diff.patch"
|
|
1322
1336
|
if external_diff.is_file():
|
|
1323
1337
|
try:
|
|
1324
|
-
return (external_diff.read_text(), None)
|
|
1338
|
+
return (external_diff.read_text(encoding="utf-8"), None)
|
|
1325
1339
|
except OSError as e:
|
|
1326
1340
|
return ("", f"cannot read {external_diff}: {e}")
|
|
1327
1341
|
base_sha = ((state.get("base_ref") or {}).get("sha") or "").strip()
|
|
1328
1342
|
cmd = ["git", "diff"]
|
|
1329
1343
|
if base_sha:
|
|
1330
1344
|
cmd.append(base_sha)
|
|
1331
|
-
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True)
|
|
1345
|
+
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True, encoding="utf-8")
|
|
1332
1346
|
if proc.returncode != 0:
|
|
1333
1347
|
return ("", (proc.stderr or proc.stdout or "git diff failed").strip())
|
|
1334
1348
|
return (proc.stdout or "", None)
|
|
@@ -1340,7 +1354,7 @@ def count_deps_added(work: Path, state: dict) -> int:
|
|
|
1340
1354
|
if base_sha:
|
|
1341
1355
|
cmd.append(base_sha)
|
|
1342
1356
|
cmd.extend(["--", "package.json"])
|
|
1343
|
-
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True)
|
|
1357
|
+
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True, encoding="utf-8")
|
|
1344
1358
|
if proc.returncode != 0:
|
|
1345
1359
|
return 0
|
|
1346
1360
|
in_deps = False
|
|
@@ -1365,7 +1379,7 @@ def changed_files(work: Path, state: dict, devlyn_dir: Path) -> list[str]:
|
|
|
1365
1379
|
if external_diff.is_file():
|
|
1366
1380
|
names: list[str] = []
|
|
1367
1381
|
try:
|
|
1368
|
-
external_text = external_diff.read_text()
|
|
1382
|
+
external_text = external_diff.read_text(encoding="utf-8")
|
|
1369
1383
|
except OSError:
|
|
1370
1384
|
return []
|
|
1371
1385
|
for line in external_text.splitlines():
|
|
@@ -1378,7 +1392,7 @@ def changed_files(work: Path, state: dict, devlyn_dir: Path) -> list[str]:
|
|
|
1378
1392
|
cmd = ["git", "diff", "--name-only"]
|
|
1379
1393
|
if base_sha:
|
|
1380
1394
|
cmd.append(base_sha)
|
|
1381
|
-
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True)
|
|
1395
|
+
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True, encoding="utf-8")
|
|
1382
1396
|
if proc.returncode != 0:
|
|
1383
1397
|
return []
|
|
1384
1398
|
return [line.strip() for line in (proc.stdout or "").splitlines() if line.strip()]
|
|
@@ -1856,7 +1870,7 @@ def run_check_mode(md_path: Path) -> int:
|
|
|
1856
1870
|
if not md_path.is_file():
|
|
1857
1871
|
print(f"[spec-verify --check] error: {md_path} not found", file=sys.stderr)
|
|
1858
1872
|
return 2
|
|
1859
|
-
text = md_path.read_text()
|
|
1873
|
+
text = md_path.read_text(encoding="utf-8")
|
|
1860
1874
|
frontmatter_err = validate_present_spec_complexity(text)
|
|
1861
1875
|
if frontmatter_err:
|
|
1862
1876
|
print(f"[spec-verify --check] {md_path}: {frontmatter_err}", file=sys.stderr)
|
|
@@ -1891,7 +1905,7 @@ def run_check_mode(md_path: Path) -> int:
|
|
|
1891
1905
|
file=sys.stderr,
|
|
1892
1906
|
)
|
|
1893
1907
|
return 2
|
|
1894
|
-
err =
|
|
1908
|
+
err = validate_inline_shape(data)
|
|
1895
1909
|
if err:
|
|
1896
1910
|
print(f"[spec-verify --check] {md_path}: shape error: {err}", file=sys.stderr)
|
|
1897
1911
|
return 2
|
|
@@ -1914,11 +1928,11 @@ def run_check_expected_mode(expected_path: Path) -> int:
|
|
|
1914
1928
|
if err:
|
|
1915
1929
|
print(f"[spec-verify --check-expected] {expected_path}: shape error: {err}", file=sys.stderr)
|
|
1916
1930
|
return 2
|
|
1917
|
-
complexity_err = validate_sibling_spec_complexity(expected_path)
|
|
1931
|
+
complexity_err = validate_sibling_spec_complexity(expected_path.with_name("spec.md"))
|
|
1918
1932
|
if complexity_err:
|
|
1919
1933
|
print(f"[spec-verify --check-expected] {expected_path}: shape error: {complexity_err}", file=sys.stderr)
|
|
1920
1934
|
return 2
|
|
1921
|
-
sibling_err = validate_expected_against_sibling_spec(expected_path, _data)
|
|
1935
|
+
sibling_err = validate_expected_against_sibling_spec(expected_path.with_name("spec.md"), _data)
|
|
1922
1936
|
if sibling_err:
|
|
1923
1937
|
print(f"[spec-verify --check-expected] {expected_path}: shape error: {sibling_err}", file=sys.stderr)
|
|
1924
1938
|
return 2
|
|
@@ -1947,6 +1961,7 @@ def run_self_test() -> int:
|
|
|
1947
1961
|
cwd=help_tmp,
|
|
1948
1962
|
capture_output=True,
|
|
1949
1963
|
text=True,
|
|
1964
|
+
encoding="utf-8",
|
|
1950
1965
|
)
|
|
1951
1966
|
if help_result.returncode != 0 or "usage:" not in help_result.stdout:
|
|
1952
1967
|
print(f"{help_flag} did not return usage successfully", file=sys.stderr)
|
|
@@ -1959,6 +1974,7 @@ def run_self_test() -> int:
|
|
|
1959
1974
|
cwd=help_tmp,
|
|
1960
1975
|
capture_output=True,
|
|
1961
1976
|
text=True,
|
|
1977
|
+
encoding="utf-8",
|
|
1962
1978
|
)
|
|
1963
1979
|
if unknown_result.returncode != 2 or "unknown argument" not in unknown_result.stderr:
|
|
1964
1980
|
print("unknown option did not fail closed", file=sys.stderr)
|
|
@@ -1988,15 +2004,15 @@ def run_self_test() -> int:
|
|
|
1988
2004
|
else:
|
|
1989
2005
|
raise AssertionError(f"malformed brace glob accepted: {entry}")
|
|
1990
2006
|
spec_md = work / "spec.md"
|
|
1991
|
-
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n")
|
|
2007
|
+
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n", encoding="utf-8")
|
|
1992
2008
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
1993
2009
|
"source": {"type": "spec", "spec_path": str(spec_md)}
|
|
1994
|
-
}))
|
|
2010
|
+
}), encoding="utf-8")
|
|
1995
2011
|
(devlyn / "spec-verify.json").write_text(json.dumps({
|
|
1996
2012
|
"verification_commands": [
|
|
1997
2013
|
{"cmd": "printf ok", "exit_code": 0, "stdout_contains": ["ok"]}
|
|
1998
2014
|
]
|
|
1999
|
-
}) + "\n")
|
|
2015
|
+
}) + "\n", encoding="utf-8")
|
|
2000
2016
|
probes_dir = devlyn / "probes"
|
|
2001
2017
|
probes_dir.mkdir()
|
|
2002
2018
|
probe_script = probes_dir / "P1.py"
|
|
@@ -2018,7 +2034,7 @@ def run_self_test() -> int:
|
|
|
2018
2034
|
],
|
|
2019
2035
|
},
|
|
2020
2036
|
}
|
|
2021
|
-
(devlyn / "risk-probes.jsonl").write_text(json.dumps(risk_probe_payload) + "\n")
|
|
2037
|
+
(devlyn / "risk-probes.jsonl").write_text(json.dumps(risk_probe_payload) + "\n", encoding="utf-8")
|
|
2022
2038
|
loaded_probes, loaded_probe_error = load_risk_probes(
|
|
2023
2039
|
devlyn, spec_md, require_present=True
|
|
2024
2040
|
)
|
|
@@ -2068,7 +2084,7 @@ def run_self_test() -> int:
|
|
|
2068
2084
|
inline_timeout_spec, inline_timeout_devlyn
|
|
2069
2085
|
)
|
|
2070
2086
|
inline_staged = loads_strict_json(
|
|
2071
|
-
(inline_timeout_devlyn / "spec-verify.json").read_text()
|
|
2087
|
+
(inline_timeout_devlyn / "spec-verify.json").read_text(encoding="utf-8")
|
|
2072
2088
|
)
|
|
2073
2089
|
if (
|
|
2074
2090
|
not staged
|
|
@@ -2089,7 +2105,7 @@ def run_self_test() -> int:
|
|
|
2089
2105
|
"verification_commands": [
|
|
2090
2106
|
{"cmd": "printf ok", "timeout_sec": invalid_timeout}
|
|
2091
2107
|
]
|
|
2092
|
-
}) + "\n")
|
|
2108
|
+
}) + "\n", encoding="utf-8")
|
|
2093
2109
|
found, staged, sibling_error, _path, _data = stage_from_expected(
|
|
2094
2110
|
sibling_timeout_spec, sibling_timeout_devlyn
|
|
2095
2111
|
)
|
|
@@ -2104,12 +2120,12 @@ def run_self_test() -> int:
|
|
|
2104
2120
|
"verification_commands": [
|
|
2105
2121
|
{"cmd": "printf ok", "timeout_sec": 5}
|
|
2106
2122
|
]
|
|
2107
|
-
}) + "\n")
|
|
2123
|
+
}) + "\n", encoding="utf-8")
|
|
2108
2124
|
found, staged, sibling_error, _path, _data = stage_from_expected(
|
|
2109
2125
|
sibling_timeout_spec, sibling_timeout_devlyn
|
|
2110
2126
|
)
|
|
2111
2127
|
sibling_staged = loads_strict_json(
|
|
2112
|
-
(sibling_timeout_devlyn / "spec-verify.json").read_text()
|
|
2128
|
+
(sibling_timeout_devlyn / "spec-verify.json").read_text(encoding="utf-8")
|
|
2113
2129
|
)
|
|
2114
2130
|
if (
|
|
2115
2131
|
not found
|
|
@@ -2149,23 +2165,24 @@ def run_self_test() -> int:
|
|
|
2149
2165
|
"run_id": "rs-timeout-run",
|
|
2150
2166
|
"source": {"type": "spec", "spec_path": str(timeout_run_spec)},
|
|
2151
2167
|
"phases": {"build_gate": {"round": 2}},
|
|
2152
|
-
}))
|
|
2168
|
+
}), encoding="utf-8")
|
|
2153
2169
|
timeout_run = subprocess.run(
|
|
2154
2170
|
[sys.executable, script_path],
|
|
2155
2171
|
cwd=timeout_run_root,
|
|
2156
2172
|
capture_output=True,
|
|
2157
2173
|
text=True,
|
|
2174
|
+
encoding="utf-8",
|
|
2158
2175
|
)
|
|
2159
2176
|
if timeout_run.returncode == 0:
|
|
2160
2177
|
print("declared one-second timeout did not fail", file=sys.stderr)
|
|
2161
2178
|
return 1
|
|
2162
2179
|
timeout_document = loads_strict_json(
|
|
2163
|
-
(timeout_run_devlyn / "spec-verify.results.json").read_text()
|
|
2180
|
+
(timeout_run_devlyn / "spec-verify.results.json").read_text(encoding="utf-8")
|
|
2164
2181
|
)
|
|
2165
2182
|
timeout_results = timeout_document["commands"]
|
|
2166
2183
|
timeout_findings = [
|
|
2167
2184
|
loads_strict_json(line)
|
|
2168
|
-
for line in (timeout_run_devlyn / output_findings_name()).read_text().splitlines()
|
|
2185
|
+
for line in (timeout_run_devlyn / output_findings_name()).read_text(encoding="utf-8").splitlines()
|
|
2169
2186
|
if line.strip()
|
|
2170
2187
|
]
|
|
2171
2188
|
if (
|
|
@@ -2185,7 +2202,7 @@ def run_self_test() -> int:
|
|
|
2185
2202
|
".devlyn/process-evidence/rs-timeout-run/build_gate/round-2/manifest.json"
|
|
2186
2203
|
)
|
|
2187
2204
|
timeout_manifest = loads_strict_json(
|
|
2188
|
-
(timeout_run_root / timeout_manifest_path).read_text()
|
|
2205
|
+
(timeout_run_root / timeout_manifest_path).read_text(encoding="utf-8")
|
|
2189
2206
|
)
|
|
2190
2207
|
if (
|
|
2191
2208
|
timeout_carrier.get("manifest", {}).get("path") != timeout_manifest_path
|
|
@@ -2249,7 +2266,7 @@ def run_self_test() -> int:
|
|
|
2249
2266
|
"run_id": "rs-runner-env",
|
|
2250
2267
|
"source": {"type": "spec", "spec_path": str(runner_env_spec)},
|
|
2251
2268
|
"phases": {"verify": {"round": 3}},
|
|
2252
|
-
}))
|
|
2269
|
+
}), encoding="utf-8")
|
|
2253
2270
|
runner_env = os.environ.copy()
|
|
2254
2271
|
runner_env.update({
|
|
2255
2272
|
"SPEC_VERIFY_PHASE": "verify_mechanical",
|
|
@@ -2262,9 +2279,10 @@ def run_self_test() -> int:
|
|
|
2262
2279
|
env=runner_env,
|
|
2263
2280
|
capture_output=True,
|
|
2264
2281
|
text=True,
|
|
2282
|
+
encoding="utf-8",
|
|
2265
2283
|
)
|
|
2266
2284
|
runner_env_document = loads_strict_json(
|
|
2267
|
-
(runner_env_devlyn / "spec-verify.results.json").read_text()
|
|
2285
|
+
(runner_env_devlyn / "spec-verify.results.json").read_text(encoding="utf-8")
|
|
2268
2286
|
)
|
|
2269
2287
|
runner_env_results = runner_env_document["commands"]
|
|
2270
2288
|
if runner_env_run.returncode != 0 or not runner_env_results[0].get("pass"):
|
|
@@ -2285,6 +2303,7 @@ def run_self_test() -> int:
|
|
|
2285
2303
|
env=env,
|
|
2286
2304
|
capture_output=True,
|
|
2287
2305
|
text=True,
|
|
2306
|
+
encoding="utf-8",
|
|
2288
2307
|
)
|
|
2289
2308
|
if validate_without_digest.returncode != 0:
|
|
2290
2309
|
print("--validate-risk-probes rejected valid probes without digest", file=sys.stderr)
|
|
@@ -2296,6 +2315,7 @@ def run_self_test() -> int:
|
|
|
2296
2315
|
env=env,
|
|
2297
2316
|
capture_output=True,
|
|
2298
2317
|
text=True,
|
|
2318
|
+
encoding="utf-8",
|
|
2299
2319
|
)
|
|
2300
2320
|
if digest_run.returncode != 0:
|
|
2301
2321
|
print("--print-risk-probes-digest rejected valid probes", file=sys.stderr)
|
|
@@ -2312,23 +2332,24 @@ def run_self_test() -> int:
|
|
|
2312
2332
|
"risk_profile": {"risk_probes_enabled": True},
|
|
2313
2333
|
"risk_probes_digest": risk_digest,
|
|
2314
2334
|
"phases": {"build_gate": {"round": 4}},
|
|
2315
|
-
}))
|
|
2335
|
+
}), encoding="utf-8")
|
|
2316
2336
|
good = subprocess.run(
|
|
2317
2337
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2318
2338
|
cwd=work,
|
|
2319
2339
|
env=env,
|
|
2320
2340
|
capture_output=True,
|
|
2321
2341
|
text=True,
|
|
2342
|
+
encoding="utf-8",
|
|
2322
2343
|
)
|
|
2323
2344
|
if good.returncode != 0:
|
|
2324
2345
|
print(good.stderr, file=sys.stderr)
|
|
2325
2346
|
return 1
|
|
2326
2347
|
good_document = loads_strict_json(
|
|
2327
|
-
(devlyn / "spec-verify.results.json").read_text()
|
|
2348
|
+
(devlyn / "spec-verify.results.json").read_text(encoding="utf-8")
|
|
2328
2349
|
)
|
|
2329
2350
|
good_manifest_path = good_document.get("process_evidence", {}).get("manifest", {}).get("path")
|
|
2330
2351
|
good_manifest = (
|
|
2331
|
-
loads_strict_json((work / good_manifest_path).read_text())
|
|
2352
|
+
loads_strict_json((work / good_manifest_path).read_text(encoding="utf-8"))
|
|
2332
2353
|
if isinstance(good_manifest_path, str)
|
|
2333
2354
|
else {}
|
|
2334
2355
|
)
|
|
@@ -2349,6 +2370,7 @@ def run_self_test() -> int:
|
|
|
2349
2370
|
env=env,
|
|
2350
2371
|
capture_output=True,
|
|
2351
2372
|
text=True,
|
|
2373
|
+
encoding="utf-8",
|
|
2352
2374
|
)
|
|
2353
2375
|
if mutated_script.returncode == 0:
|
|
2354
2376
|
print("--include-risk-probes accepted mutated probe script bytes", file=sys.stderr)
|
|
@@ -2362,13 +2384,14 @@ def run_self_test() -> int:
|
|
|
2362
2384
|
|
|
2363
2385
|
mutated_payload = dict(risk_probe_payload)
|
|
2364
2386
|
mutated_payload["id"] = "P1-mutated"
|
|
2365
|
-
(devlyn / "risk-probes.jsonl").write_text(json.dumps(mutated_payload) + "\n")
|
|
2387
|
+
(devlyn / "risk-probes.jsonl").write_text(json.dumps(mutated_payload) + "\n", encoding="utf-8")
|
|
2366
2388
|
mutated_jsonl = subprocess.run(
|
|
2367
2389
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2368
2390
|
cwd=work,
|
|
2369
2391
|
env=env,
|
|
2370
2392
|
capture_output=True,
|
|
2371
2393
|
text=True,
|
|
2394
|
+
encoding="utf-8",
|
|
2372
2395
|
)
|
|
2373
2396
|
if mutated_jsonl.returncode == 0:
|
|
2374
2397
|
print("--include-risk-probes accepted mutated risk-probes.jsonl bytes", file=sys.stderr)
|
|
@@ -2378,18 +2401,19 @@ def run_self_test() -> int:
|
|
|
2378
2401
|
print("mutated risk-probes.jsonl did not emit correctness.risk-probe-integrity", file=sys.stderr)
|
|
2379
2402
|
print(integrity_findings, file=sys.stderr)
|
|
2380
2403
|
return 1
|
|
2381
|
-
(devlyn / "risk-probes.jsonl").write_text(json.dumps(risk_probe_payload) + "\n")
|
|
2404
|
+
(devlyn / "risk-probes.jsonl").write_text(json.dumps(risk_probe_payload) + "\n", encoding="utf-8")
|
|
2382
2405
|
|
|
2383
2406
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
2384
2407
|
"source": {"type": "spec", "spec_path": str(spec_md)},
|
|
2385
2408
|
"risk_profile": {"risk_probes_enabled": True},
|
|
2386
|
-
}))
|
|
2409
|
+
}), encoding="utf-8")
|
|
2387
2410
|
missing_digest = subprocess.run(
|
|
2388
2411
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2389
2412
|
cwd=work,
|
|
2390
2413
|
env=env,
|
|
2391
2414
|
capture_output=True,
|
|
2392
2415
|
text=True,
|
|
2416
|
+
encoding="utf-8",
|
|
2393
2417
|
)
|
|
2394
2418
|
if missing_digest.returncode == 0:
|
|
2395
2419
|
print("--include-risk-probes accepted enabled risk probes with missing digest", file=sys.stderr)
|
|
@@ -2407,6 +2431,7 @@ def run_self_test() -> int:
|
|
|
2407
2431
|
env=env,
|
|
2408
2432
|
capture_output=True,
|
|
2409
2433
|
text=True,
|
|
2434
|
+
encoding="utf-8",
|
|
2410
2435
|
)
|
|
2411
2436
|
if missing_jsonl_digest.returncode != 2:
|
|
2412
2437
|
print("--print-risk-probes-digest accepted missing risk-probes.jsonl", file=sys.stderr)
|
|
@@ -2420,13 +2445,14 @@ def run_self_test() -> int:
|
|
|
2420
2445
|
"source": {"type": "spec", "spec_path": str(spec_md)},
|
|
2421
2446
|
"risk_profile": {"risk_probes_enabled": True},
|
|
2422
2447
|
"risk_probes_digest": risk_digest,
|
|
2423
|
-
}))
|
|
2448
|
+
}), encoding="utf-8")
|
|
2424
2449
|
missing_required_probe = subprocess.run(
|
|
2425
2450
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2426
2451
|
cwd=work,
|
|
2427
2452
|
env=env,
|
|
2428
2453
|
capture_output=True,
|
|
2429
2454
|
text=True,
|
|
2455
|
+
encoding="utf-8",
|
|
2430
2456
|
)
|
|
2431
2457
|
if missing_required_probe.returncode == 0:
|
|
2432
2458
|
print("--include-risk-probes accepted missing required risk-probes.jsonl", file=sys.stderr)
|
|
@@ -2439,13 +2465,14 @@ def run_self_test() -> int:
|
|
|
2439
2465
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
2440
2466
|
"source": {"type": "spec", "spec_path": str(spec_md)},
|
|
2441
2467
|
"risk_profile": {"risk_probes_enabled": False},
|
|
2442
|
-
}))
|
|
2468
|
+
}), encoding="utf-8")
|
|
2443
2469
|
missing_optional_probe = subprocess.run(
|
|
2444
2470
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2445
2471
|
cwd=work,
|
|
2446
2472
|
env=env,
|
|
2447
2473
|
capture_output=True,
|
|
2448
2474
|
text=True,
|
|
2475
|
+
encoding="utf-8",
|
|
2449
2476
|
)
|
|
2450
2477
|
if missing_optional_probe.returncode != 0:
|
|
2451
2478
|
print("--include-risk-probes rejected optional missing risk-probes.jsonl", file=sys.stderr)
|
|
@@ -2455,13 +2482,14 @@ def run_self_test() -> int:
|
|
|
2455
2482
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
2456
2483
|
"source": {"type": "spec", "spec_path": str(spec_md)},
|
|
2457
2484
|
"risk_profile": {"risk_probes_enabled": "true"},
|
|
2458
|
-
}))
|
|
2485
|
+
}), encoding="utf-8")
|
|
2459
2486
|
malformed_risk_probe_state = subprocess.run(
|
|
2460
2487
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2461
2488
|
cwd=work,
|
|
2462
2489
|
env=env,
|
|
2463
2490
|
capture_output=True,
|
|
2464
2491
|
text=True,
|
|
2492
|
+
encoding="utf-8",
|
|
2465
2493
|
)
|
|
2466
2494
|
if malformed_risk_probe_state.returncode == 0:
|
|
2467
2495
|
print("--include-risk-probes accepted non-boolean risk_probes_enabled", file=sys.stderr)
|
|
@@ -2474,13 +2502,14 @@ def run_self_test() -> int:
|
|
|
2474
2502
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
2475
2503
|
"source": {"type": "spec", "spec_path": str(spec_md)},
|
|
2476
2504
|
"risk_profile": "enabled",
|
|
2477
|
-
}))
|
|
2505
|
+
}), encoding="utf-8")
|
|
2478
2506
|
malformed_risk_profile = subprocess.run(
|
|
2479
2507
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2480
2508
|
cwd=work,
|
|
2481
2509
|
env=env,
|
|
2482
2510
|
capture_output=True,
|
|
2483
2511
|
text=True,
|
|
2512
|
+
encoding="utf-8",
|
|
2484
2513
|
)
|
|
2485
2514
|
if malformed_risk_profile.returncode == 0:
|
|
2486
2515
|
print("--include-risk-probes accepted non-object risk_profile", file=sys.stderr)
|
|
@@ -2492,7 +2521,7 @@ def run_self_test() -> int:
|
|
|
2492
2521
|
|
|
2493
2522
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
2494
2523
|
"source": {"type": "spec", "spec_path": str(spec_md)}
|
|
2495
|
-
}))
|
|
2524
|
+
}), encoding="utf-8")
|
|
2496
2525
|
(devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
2497
2526
|
"id": "P1",
|
|
2498
2527
|
"derived_from": "probe must pass visible marker.",
|
|
@@ -2508,7 +2537,7 @@ def run_self_test() -> int:
|
|
|
2508
2537
|
"asserts_no_unexpected_output_keys",
|
|
2509
2538
|
],
|
|
2510
2539
|
},
|
|
2511
|
-
}) + "\n")
|
|
2540
|
+
}) + "\n", encoding="utf-8")
|
|
2512
2541
|
|
|
2513
2542
|
good_complexity = work / "good-complexity.md"
|
|
2514
2543
|
good_complexity.write_text(
|
|
@@ -2520,6 +2549,7 @@ def run_self_test() -> int:
|
|
|
2520
2549
|
cwd=work,
|
|
2521
2550
|
capture_output=True,
|
|
2522
2551
|
text=True,
|
|
2552
|
+
encoding="utf-8",
|
|
2523
2553
|
)
|
|
2524
2554
|
if good_complexity_check.returncode != 0:
|
|
2525
2555
|
print(good_complexity_check.stderr, file=sys.stderr)
|
|
@@ -2535,6 +2565,7 @@ def run_self_test() -> int:
|
|
|
2535
2565
|
cwd=work,
|
|
2536
2566
|
capture_output=True,
|
|
2537
2567
|
text=True,
|
|
2568
|
+
encoding="utf-8",
|
|
2538
2569
|
)
|
|
2539
2570
|
if bad_complexity_check.returncode == 0:
|
|
2540
2571
|
print("unsupported spec complexity was accepted", file=sys.stderr)
|
|
@@ -2556,6 +2587,7 @@ def run_self_test() -> int:
|
|
|
2556
2587
|
cwd=work,
|
|
2557
2588
|
capture_output=True,
|
|
2558
2589
|
text=True,
|
|
2590
|
+
encoding="utf-8",
|
|
2559
2591
|
)
|
|
2560
2592
|
if weak_solo_check.returncode == 0:
|
|
2561
2593
|
print("weak solo-headroom hypothesis was accepted by --check", file=sys.stderr)
|
|
@@ -2576,6 +2608,7 @@ def run_self_test() -> int:
|
|
|
2576
2608
|
cwd=work,
|
|
2577
2609
|
capture_output=True,
|
|
2578
2610
|
text=True,
|
|
2611
|
+
encoding="utf-8",
|
|
2579
2612
|
)
|
|
2580
2613
|
if weak_descriptive_check.returncode == 0:
|
|
2581
2614
|
print("descriptive backtick solo-headroom hypothesis was accepted by --check", file=sys.stderr)
|
|
@@ -2592,6 +2625,7 @@ def run_self_test() -> int:
|
|
|
2592
2625
|
cwd=work,
|
|
2593
2626
|
capture_output=True,
|
|
2594
2627
|
text=True,
|
|
2628
|
+
encoding="utf-8",
|
|
2595
2629
|
)
|
|
2596
2630
|
if strong_solo_check.returncode != 0:
|
|
2597
2631
|
print("actionable solo-headroom hypothesis was rejected by --check", file=sys.stderr)
|
|
@@ -2610,6 +2644,7 @@ def run_self_test() -> int:
|
|
|
2610
2644
|
cwd=work,
|
|
2611
2645
|
capture_output=True,
|
|
2612
2646
|
text=True,
|
|
2647
|
+
encoding="utf-8",
|
|
2613
2648
|
)
|
|
2614
2649
|
if docs_style_solo_check.returncode != 0:
|
|
2615
2650
|
print("docs-style solo-headroom hypothesis was rejected by --check", file=sys.stderr)
|
|
@@ -2627,6 +2662,7 @@ def run_self_test() -> int:
|
|
|
2627
2662
|
cwd=work,
|
|
2628
2663
|
capture_output=True,
|
|
2629
2664
|
text=True,
|
|
2665
|
+
encoding="utf-8",
|
|
2630
2666
|
)
|
|
2631
2667
|
if weak_solo_ceiling_check.returncode == 0:
|
|
2632
2668
|
print("weak solo ceiling avoidance was accepted by --check", file=sys.stderr)
|
|
@@ -2648,6 +2684,7 @@ def run_self_test() -> int:
|
|
|
2648
2684
|
cwd=work,
|
|
2649
2685
|
capture_output=True,
|
|
2650
2686
|
text=True,
|
|
2687
|
+
encoding="utf-8",
|
|
2651
2688
|
)
|
|
2652
2689
|
if strong_solo_ceiling_check.returncode != 0:
|
|
2653
2690
|
print("actionable solo ceiling avoidance was rejected by --check", file=sys.stderr)
|
|
@@ -2669,6 +2706,7 @@ def run_self_test() -> int:
|
|
|
2669
2706
|
cwd=work,
|
|
2670
2707
|
capture_output=True,
|
|
2671
2708
|
text=True,
|
|
2709
|
+
encoding="utf-8",
|
|
2672
2710
|
)
|
|
2673
2711
|
if inline_mismatched_check.returncode == 0:
|
|
2674
2712
|
print("mismatched inline solo-headroom command was accepted by --check", file=sys.stderr)
|
|
@@ -2693,6 +2731,7 @@ def run_self_test() -> int:
|
|
|
2693
2731
|
cwd=work,
|
|
2694
2732
|
capture_output=True,
|
|
2695
2733
|
text=True,
|
|
2734
|
+
encoding="utf-8",
|
|
2696
2735
|
)
|
|
2697
2736
|
if inline_matched_check.returncode != 0:
|
|
2698
2737
|
print("matched inline solo-headroom command was rejected by --check", file=sys.stderr)
|
|
@@ -2704,13 +2743,14 @@ def run_self_test() -> int:
|
|
|
2704
2743
|
"derived_from": "probe must pass visible marker.",
|
|
2705
2744
|
"cmd": "node $BENCH_FIXTURE_DIR/verifiers/hidden.js",
|
|
2706
2745
|
"exit_code": 0,
|
|
2707
|
-
}) + "\n")
|
|
2746
|
+
}) + "\n", encoding="utf-8")
|
|
2708
2747
|
bad = subprocess.run(
|
|
2709
2748
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2710
2749
|
cwd=work,
|
|
2711
2750
|
env=env,
|
|
2712
2751
|
capture_output=True,
|
|
2713
2752
|
text=True,
|
|
2753
|
+
encoding="utf-8",
|
|
2714
2754
|
)
|
|
2715
2755
|
if bad.returncode == 0:
|
|
2716
2756
|
print("hidden verifier path was accepted", file=sys.stderr)
|
|
@@ -2725,13 +2765,14 @@ def run_self_test() -> int:
|
|
|
2725
2765
|
"cmd": "python3 .devlyn/probes/Pscript.py",
|
|
2726
2766
|
"stdout_contains": ["script-ok"],
|
|
2727
2767
|
}
|
|
2728
|
-
(devlyn / "risk-probes.jsonl").write_text(json.dumps(script_probe_payload) + "\n")
|
|
2768
|
+
(devlyn / "risk-probes.jsonl").write_text(json.dumps(script_probe_payload) + "\n", encoding="utf-8")
|
|
2729
2769
|
good_script_probe = subprocess.run(
|
|
2730
2770
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2731
2771
|
cwd=work,
|
|
2732
2772
|
env=env,
|
|
2733
2773
|
capture_output=True,
|
|
2734
2774
|
text=True,
|
|
2775
|
+
encoding="utf-8",
|
|
2735
2776
|
)
|
|
2736
2777
|
if good_script_probe.returncode != 0:
|
|
2737
2778
|
print("risk probe script file was rejected", file=sys.stderr)
|
|
@@ -2743,13 +2784,14 @@ def run_self_test() -> int:
|
|
|
2743
2784
|
"id": "Pmixed",
|
|
2744
2785
|
"cmd": "python3 .devlyn/probes/Pscript.py && python3 ./.devlyn/probes/P1.py",
|
|
2745
2786
|
}
|
|
2746
|
-
(devlyn / "risk-probes.jsonl").write_text(json.dumps(mixed_script_payload) + "\n")
|
|
2787
|
+
(devlyn / "risk-probes.jsonl").write_text(json.dumps(mixed_script_payload) + "\n", encoding="utf-8")
|
|
2747
2788
|
mixed_script_probe = subprocess.run(
|
|
2748
2789
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2749
2790
|
cwd=work,
|
|
2750
2791
|
env=env,
|
|
2751
2792
|
capture_output=True,
|
|
2752
2793
|
text=True,
|
|
2794
|
+
encoding="utf-8",
|
|
2753
2795
|
)
|
|
2754
2796
|
if mixed_script_probe.returncode != 0:
|
|
2755
2797
|
print("canonical and ./-alias probe script references were rejected", file=sys.stderr)
|
|
@@ -2780,13 +2822,14 @@ def run_self_test() -> int:
|
|
|
2780
2822
|
"derived_from": "probe must pass visible marker.",
|
|
2781
2823
|
"cmd": "python3 ./.devlyn/probes/missing.py",
|
|
2782
2824
|
"exit_code": 0,
|
|
2783
|
-
}) + "\n")
|
|
2825
|
+
}) + "\n", encoding="utf-8")
|
|
2784
2826
|
missing_script_probe = subprocess.run(
|
|
2785
2827
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2786
2828
|
cwd=work,
|
|
2787
2829
|
env=env,
|
|
2788
2830
|
capture_output=True,
|
|
2789
2831
|
text=True,
|
|
2832
|
+
encoding="utf-8",
|
|
2790
2833
|
)
|
|
2791
2834
|
if missing_script_probe.returncode == 0:
|
|
2792
2835
|
print("risk probe missing script file was accepted", file=sys.stderr)
|
|
@@ -2801,6 +2844,7 @@ def run_self_test() -> int:
|
|
|
2801
2844
|
env=env,
|
|
2802
2845
|
capture_output=True,
|
|
2803
2846
|
text=True,
|
|
2847
|
+
encoding="utf-8",
|
|
2804
2848
|
)
|
|
2805
2849
|
if missing_script_digest.returncode != 2:
|
|
2806
2850
|
print("--print-risk-probes-digest accepted a missing referenced script", file=sys.stderr)
|
|
@@ -2822,13 +2866,14 @@ def run_self_test() -> int:
|
|
|
2822
2866
|
"derived_from": "probe must pass visible marker.",
|
|
2823
2867
|
"cmd": f"python3 {bad_form}",
|
|
2824
2868
|
"exit_code": 0,
|
|
2825
|
-
}) + "\n")
|
|
2869
|
+
}) + "\n", encoding="utf-8")
|
|
2826
2870
|
bad_ref_probe = subprocess.run(
|
|
2827
2871
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2828
2872
|
cwd=work,
|
|
2829
2873
|
env=env,
|
|
2830
2874
|
capture_output=True,
|
|
2831
2875
|
text=True,
|
|
2876
|
+
encoding="utf-8",
|
|
2832
2877
|
)
|
|
2833
2878
|
bad_ref_digest = subprocess.run(
|
|
2834
2879
|
[sys.executable, script_path, "--print-risk-probes-digest"],
|
|
@@ -2836,6 +2881,7 @@ def run_self_test() -> int:
|
|
|
2836
2881
|
env=env,
|
|
2837
2882
|
capture_output=True,
|
|
2838
2883
|
text=True,
|
|
2884
|
+
encoding="utf-8",
|
|
2839
2885
|
)
|
|
2840
2886
|
unrecognized = unrecognized_risk_probe_reference(f"python3 {bad_form}")
|
|
2841
2887
|
expected_error = f"risk-probes[0].cmd has {unrecognized}"
|
|
@@ -2856,13 +2902,14 @@ def run_self_test() -> int:
|
|
|
2856
2902
|
"derived_from": "probe must pass visible marker.",
|
|
2857
2903
|
"cmd": "python3 .devlyn/probes/Phidden.py",
|
|
2858
2904
|
"exit_code": 0,
|
|
2859
|
-
}) + "\n")
|
|
2905
|
+
}) + "\n", encoding="utf-8")
|
|
2860
2906
|
hidden_script_probe = subprocess.run(
|
|
2861
2907
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2862
2908
|
cwd=work,
|
|
2863
2909
|
env=env,
|
|
2864
2910
|
capture_output=True,
|
|
2865
2911
|
text=True,
|
|
2912
|
+
encoding="utf-8",
|
|
2866
2913
|
)
|
|
2867
2914
|
if hidden_script_probe.returncode == 0:
|
|
2868
2915
|
print("risk probe script containing a hidden fixture path was accepted", file=sys.stderr)
|
|
@@ -2878,13 +2925,14 @@ def run_self_test() -> int:
|
|
|
2878
2925
|
"derived_from": "probe must pass visible marker.",
|
|
2879
2926
|
"cmd": "python3 .devlyn/probes/Pexternal.py",
|
|
2880
2927
|
"exit_code": 0,
|
|
2881
|
-
}) + "\n")
|
|
2928
|
+
}) + "\n", encoding="utf-8")
|
|
2882
2929
|
external_script_probe = subprocess.run(
|
|
2883
2930
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2884
2931
|
cwd=work,
|
|
2885
2932
|
env=env,
|
|
2886
2933
|
capture_output=True,
|
|
2887
2934
|
text=True,
|
|
2935
|
+
encoding="utf-8",
|
|
2888
2936
|
)
|
|
2889
2937
|
if external_script_probe.returncode == 0:
|
|
2890
2938
|
print("risk probe script containing an external URL was accepted", file=sys.stderr)
|
|
@@ -2894,13 +2942,14 @@ def run_self_test() -> int:
|
|
|
2894
2942
|
print(external_script_probe.stderr, file=sys.stderr)
|
|
2895
2943
|
return 1
|
|
2896
2944
|
|
|
2897
|
-
(devlyn / "risk-probes.jsonl").write_text('{"id":NaN}\n')
|
|
2945
|
+
(devlyn / "risk-probes.jsonl").write_text('{"id":NaN}\n', encoding="utf-8")
|
|
2898
2946
|
bad_probe_nan = subprocess.run(
|
|
2899
2947
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2900
2948
|
cwd=work,
|
|
2901
2949
|
env=env,
|
|
2902
2950
|
capture_output=True,
|
|
2903
2951
|
text=True,
|
|
2952
|
+
encoding="utf-8",
|
|
2904
2953
|
)
|
|
2905
2954
|
if bad_probe_nan.returncode == 0:
|
|
2906
2955
|
print("NaN risk-probes JSONL was accepted", file=sys.stderr)
|
|
@@ -2917,13 +2966,14 @@ def run_self_test() -> int:
|
|
|
2917
2966
|
"exit_code": 0,
|
|
2918
2967
|
"tags": ["error_contract"],
|
|
2919
2968
|
"tag_evidence": {"error_contract": []},
|
|
2920
|
-
}) + "\n")
|
|
2969
|
+
}) + "\n", encoding="utf-8")
|
|
2921
2970
|
bad_error_ref = subprocess.run(
|
|
2922
2971
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2923
2972
|
cwd=work,
|
|
2924
2973
|
env=env,
|
|
2925
2974
|
capture_output=True,
|
|
2926
2975
|
text=True,
|
|
2976
|
+
encoding="utf-8",
|
|
2927
2977
|
)
|
|
2928
2978
|
if bad_error_ref.returncode == 0:
|
|
2929
2979
|
print("error_contract with unrelated derived_from was accepted", file=sys.stderr)
|
|
@@ -2951,13 +3001,14 @@ def run_self_test() -> int:
|
|
|
2951
3001
|
"asserts_no_unexpected_output_keys",
|
|
2952
3002
|
],
|
|
2953
3003
|
},
|
|
2954
|
-
}) + "\n")
|
|
3004
|
+
}) + "\n", encoding="utf-8")
|
|
2955
3005
|
bad_solo_headroom_probe = subprocess.run(
|
|
2956
3006
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2957
3007
|
cwd=work,
|
|
2958
3008
|
env=env,
|
|
2959
3009
|
capture_output=True,
|
|
2960
3010
|
text=True,
|
|
3011
|
+
encoding="utf-8",
|
|
2961
3012
|
)
|
|
2962
3013
|
if bad_solo_headroom_probe.returncode == 0:
|
|
2963
3014
|
print("risk probe missing solo-headroom command coverage was accepted", file=sys.stderr)
|
|
@@ -2982,13 +3033,14 @@ def run_self_test() -> int:
|
|
|
2982
3033
|
"asserts_no_unexpected_output_keys",
|
|
2983
3034
|
],
|
|
2984
3035
|
},
|
|
2985
|
-
}) + "\n")
|
|
3036
|
+
}) + "\n", encoding="utf-8")
|
|
2986
3037
|
bad_solo_headroom_derived_from = subprocess.run(
|
|
2987
3038
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2988
3039
|
cwd=work,
|
|
2989
3040
|
env=env,
|
|
2990
3041
|
capture_output=True,
|
|
2991
3042
|
text=True,
|
|
3043
|
+
encoding="utf-8",
|
|
2992
3044
|
)
|
|
2993
3045
|
if bad_solo_headroom_derived_from.returncode == 0:
|
|
2994
3046
|
print("risk probe with unrelated solo-headroom derived_from was accepted", file=sys.stderr)
|
|
@@ -3029,7 +3081,8 @@ def run_self_test() -> int:
|
|
|
3029
3081
|
"asserts_no_unexpected_output_keys",
|
|
3030
3082
|
],
|
|
3031
3083
|
},
|
|
3032
|
-
}) + "\n"
|
|
3084
|
+
}) + "\n",
|
|
3085
|
+
encoding="utf-8",
|
|
3033
3086
|
)
|
|
3034
3087
|
late_solo_headroom_probe = subprocess.run(
|
|
3035
3088
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
@@ -3037,6 +3090,7 @@ def run_self_test() -> int:
|
|
|
3037
3090
|
env=env,
|
|
3038
3091
|
capture_output=True,
|
|
3039
3092
|
text=True,
|
|
3093
|
+
encoding="utf-8",
|
|
3040
3094
|
)
|
|
3041
3095
|
if late_solo_headroom_probe.returncode == 0:
|
|
3042
3096
|
print("solo-headroom command in a later risk probe was accepted", file=sys.stderr)
|
|
@@ -3057,13 +3111,14 @@ def run_self_test() -> int:
|
|
|
3057
3111
|
"asserts_no_unexpected_output_keys",
|
|
3058
3112
|
],
|
|
3059
3113
|
},
|
|
3060
|
-
}) + "\n")
|
|
3114
|
+
}) + "\n", encoding="utf-8")
|
|
3061
3115
|
prefix_solo_headroom_probe = subprocess.run(
|
|
3062
3116
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3063
3117
|
cwd=work,
|
|
3064
3118
|
env=env,
|
|
3065
3119
|
capture_output=True,
|
|
3066
3120
|
text=True,
|
|
3121
|
+
encoding="utf-8",
|
|
3067
3122
|
)
|
|
3068
3123
|
if prefix_solo_headroom_probe.returncode == 0:
|
|
3069
3124
|
print("solo-headroom command prefix match was accepted", file=sys.stderr)
|
|
@@ -3084,13 +3139,14 @@ def run_self_test() -> int:
|
|
|
3084
3139
|
"asserts_no_unexpected_output_keys",
|
|
3085
3140
|
],
|
|
3086
3141
|
},
|
|
3087
|
-
}) + "\n")
|
|
3142
|
+
}) + "\n", encoding="utf-8")
|
|
3088
3143
|
good_solo_headroom_probe = subprocess.run(
|
|
3089
3144
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3090
3145
|
cwd=work,
|
|
3091
3146
|
env=env,
|
|
3092
3147
|
capture_output=True,
|
|
3093
3148
|
text=True,
|
|
3149
|
+
encoding="utf-8",
|
|
3094
3150
|
)
|
|
3095
3151
|
if good_solo_headroom_probe.returncode != 0:
|
|
3096
3152
|
print("risk probe covering solo-headroom command was rejected", file=sys.stderr)
|
|
@@ -3112,17 +3168,19 @@ def run_self_test() -> int:
|
|
|
3112
3168
|
"required_files": ["bin/cli.js"],
|
|
3113
3169
|
"forbidden_files": [],
|
|
3114
3170
|
"max_deps_added": 0,
|
|
3115
|
-
}) + "\n")
|
|
3171
|
+
}) + "\n", encoding="utf-8")
|
|
3116
3172
|
spec_md.write_text(
|
|
3117
3173
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
3118
3174
|
"- solo-headroom hypothesis: solo_claude should miss duplicate handling.\n"
|
|
3119
|
-
"- Observable command: `node check.js` exposes behavior.\n"
|
|
3175
|
+
"- Observable command: `node check.js` exposes behavior.\n",
|
|
3176
|
+
encoding="utf-8",
|
|
3120
3177
|
)
|
|
3121
3178
|
weak_sibling_solo = subprocess.run(
|
|
3122
3179
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3123
3180
|
cwd=work,
|
|
3124
3181
|
capture_output=True,
|
|
3125
3182
|
text=True,
|
|
3183
|
+
encoding="utf-8",
|
|
3126
3184
|
)
|
|
3127
3185
|
if weak_sibling_solo.returncode == 0:
|
|
3128
3186
|
print("weak sibling solo-headroom hypothesis was accepted by --check-expected", file=sys.stderr)
|
|
@@ -3143,6 +3201,7 @@ def run_self_test() -> int:
|
|
|
3143
3201
|
cwd=work,
|
|
3144
3202
|
capture_output=True,
|
|
3145
3203
|
text=True,
|
|
3204
|
+
encoding="utf-8",
|
|
3146
3205
|
)
|
|
3147
3206
|
if mismatched_sibling_solo.returncode == 0:
|
|
3148
3207
|
print("mismatched sibling solo-headroom command was accepted by --check-expected", file=sys.stderr)
|
|
@@ -3163,6 +3222,7 @@ def run_self_test() -> int:
|
|
|
3163
3222
|
cwd=work,
|
|
3164
3223
|
capture_output=True,
|
|
3165
3224
|
text=True,
|
|
3225
|
+
encoding="utf-8",
|
|
3166
3226
|
)
|
|
3167
3227
|
if matched_sibling_solo.returncode != 0:
|
|
3168
3228
|
print("matched sibling solo-headroom command was rejected by --check-expected", file=sys.stderr)
|
|
@@ -3180,6 +3240,7 @@ def run_self_test() -> int:
|
|
|
3180
3240
|
cwd=work,
|
|
3181
3241
|
capture_output=True,
|
|
3182
3242
|
text=True,
|
|
3243
|
+
encoding="utf-8",
|
|
3183
3244
|
)
|
|
3184
3245
|
if docs_style_sibling_solo.returncode != 0:
|
|
3185
3246
|
print("docs-style sibling solo-headroom command was rejected by --check-expected", file=sys.stderr)
|
|
@@ -3196,6 +3257,7 @@ def run_self_test() -> int:
|
|
|
3196
3257
|
cwd=work,
|
|
3197
3258
|
capture_output=True,
|
|
3198
3259
|
text=True,
|
|
3260
|
+
encoding="utf-8",
|
|
3199
3261
|
)
|
|
3200
3262
|
if weak_sibling_solo_ceiling.returncode == 0:
|
|
3201
3263
|
print("weak sibling solo ceiling avoidance was accepted by --check-expected", file=sys.stderr)
|
|
@@ -3216,18 +3278,20 @@ def run_self_test() -> int:
|
|
|
3216
3278
|
cwd=work,
|
|
3217
3279
|
capture_output=True,
|
|
3218
3280
|
text=True,
|
|
3281
|
+
encoding="utf-8",
|
|
3219
3282
|
)
|
|
3220
3283
|
if strong_sibling_solo_ceiling.returncode != 0:
|
|
3221
3284
|
print("actionable sibling solo ceiling avoidance was rejected by --check-expected", file=sys.stderr)
|
|
3222
3285
|
print(strong_sibling_solo_ceiling.stderr, file=sys.stderr)
|
|
3223
3286
|
return 1
|
|
3224
3287
|
|
|
3225
|
-
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n")
|
|
3288
|
+
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n", encoding="utf-8")
|
|
3226
3289
|
expected_good = subprocess.run(
|
|
3227
3290
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3228
3291
|
cwd=work,
|
|
3229
3292
|
capture_output=True,
|
|
3230
3293
|
text=True,
|
|
3294
|
+
encoding="utf-8",
|
|
3231
3295
|
)
|
|
3232
3296
|
if expected_good.returncode != 0:
|
|
3233
3297
|
print(expected_good.stderr, file=sys.stderr)
|
|
@@ -3242,6 +3306,7 @@ def run_self_test() -> int:
|
|
|
3242
3306
|
cwd=work,
|
|
3243
3307
|
capture_output=True,
|
|
3244
3308
|
text=True,
|
|
3309
|
+
encoding="utf-8",
|
|
3245
3310
|
)
|
|
3246
3311
|
if expected_bad_sibling_complexity.returncode == 0:
|
|
3247
3312
|
print("unsupported sibling spec complexity was accepted by --check-expected", file=sys.stderr)
|
|
@@ -3250,14 +3315,15 @@ def run_self_test() -> int:
|
|
|
3250
3315
|
print("--check-expected did not report unsupported sibling spec complexity", file=sys.stderr)
|
|
3251
3316
|
print(expected_bad_sibling_complexity.stderr, file=sys.stderr)
|
|
3252
3317
|
return 1
|
|
3253
|
-
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n")
|
|
3318
|
+
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n", encoding="utf-8")
|
|
3254
3319
|
|
|
3255
|
-
expected_json.write_text(json.dumps({"verification_commands": []}) + "\n")
|
|
3320
|
+
expected_json.write_text(json.dumps({"verification_commands": []}) + "\n", encoding="utf-8")
|
|
3256
3321
|
expected_empty_runtime = subprocess.run(
|
|
3257
3322
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3258
3323
|
cwd=work,
|
|
3259
3324
|
capture_output=True,
|
|
3260
3325
|
text=True,
|
|
3326
|
+
encoding="utf-8",
|
|
3261
3327
|
)
|
|
3262
3328
|
if expected_empty_runtime.returncode == 0:
|
|
3263
3329
|
print("empty verification_commands should fail for runtime specs", file=sys.stderr)
|
|
@@ -3280,6 +3346,7 @@ def run_self_test() -> int:
|
|
|
3280
3346
|
cwd=work,
|
|
3281
3347
|
capture_output=True,
|
|
3282
3348
|
text=True,
|
|
3349
|
+
encoding="utf-8",
|
|
3283
3350
|
)
|
|
3284
3351
|
if expected_empty_design.returncode != 0:
|
|
3285
3352
|
print("empty verification_commands should be valid for pure-design specs", file=sys.stderr)
|
|
@@ -3298,6 +3365,7 @@ def run_self_test() -> int:
|
|
|
3298
3365
|
cwd=work,
|
|
3299
3366
|
capture_output=True,
|
|
3300
3367
|
text=True,
|
|
3368
|
+
encoding="utf-8",
|
|
3301
3369
|
)
|
|
3302
3370
|
if pure_design_contradiction.returncode == 0:
|
|
3303
3371
|
print("pure_design: true with non-empty verification_commands was accepted", file=sys.stderr)
|
|
@@ -3316,6 +3384,7 @@ def run_self_test() -> int:
|
|
|
3316
3384
|
cwd=work,
|
|
3317
3385
|
capture_output=True,
|
|
3318
3386
|
text=True,
|
|
3387
|
+
encoding="utf-8",
|
|
3319
3388
|
)
|
|
3320
3389
|
if pure_design_not_boolean.returncode == 0:
|
|
3321
3390
|
print("non-boolean pure_design was accepted", file=sys.stderr)
|
|
@@ -3325,12 +3394,13 @@ def run_self_test() -> int:
|
|
|
3325
3394
|
print(pure_design_not_boolean.stderr, file=sys.stderr)
|
|
3326
3395
|
return 1
|
|
3327
3396
|
|
|
3328
|
-
expected_json.write_text(json.dumps({"unknown": True}) + "\n")
|
|
3397
|
+
expected_json.write_text(json.dumps({"unknown": True}) + "\n", encoding="utf-8")
|
|
3329
3398
|
expected_bad = subprocess.run(
|
|
3330
3399
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3331
3400
|
cwd=work,
|
|
3332
3401
|
capture_output=True,
|
|
3333
3402
|
text=True,
|
|
3403
|
+
encoding="utf-8",
|
|
3334
3404
|
)
|
|
3335
3405
|
if expected_bad.returncode == 0:
|
|
3336
3406
|
print("spec.expected.json with unknown key was accepted", file=sys.stderr)
|
|
@@ -3338,23 +3408,25 @@ def run_self_test() -> int:
|
|
|
3338
3408
|
|
|
3339
3409
|
expected_json.write_text(json.dumps({
|
|
3340
3410
|
"verification_commands": [{"cmd": "printf ok", "stdout_contians": ["ok"]}]
|
|
3341
|
-
}) + "\n")
|
|
3411
|
+
}) + "\n", encoding="utf-8")
|
|
3342
3412
|
expected_bad_command = subprocess.run(
|
|
3343
3413
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3344
3414
|
cwd=work,
|
|
3345
3415
|
capture_output=True,
|
|
3346
3416
|
text=True,
|
|
3417
|
+
encoding="utf-8",
|
|
3347
3418
|
)
|
|
3348
3419
|
if expected_bad_command.returncode == 0:
|
|
3349
3420
|
print("spec.expected.json command with unknown key was accepted", file=sys.stderr)
|
|
3350
3421
|
return 1
|
|
3351
3422
|
|
|
3352
|
-
expected_json.write_text("[1]\n")
|
|
3423
|
+
expected_json.write_text("[1]\n", encoding="utf-8")
|
|
3353
3424
|
expected_non_object = subprocess.run(
|
|
3354
3425
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3355
3426
|
cwd=work,
|
|
3356
3427
|
capture_output=True,
|
|
3357
3428
|
text=True,
|
|
3429
|
+
encoding="utf-8",
|
|
3358
3430
|
)
|
|
3359
3431
|
if expected_non_object.returncode == 0:
|
|
3360
3432
|
print("spec.expected.json top-level array was accepted", file=sys.stderr)
|
|
@@ -3367,12 +3439,13 @@ def run_self_test() -> int:
|
|
|
3367
3439
|
print("spec.expected.json top-level array produced a traceback", file=sys.stderr)
|
|
3368
3440
|
return 1
|
|
3369
3441
|
|
|
3370
|
-
expected_json.write_text("{broken\n")
|
|
3442
|
+
expected_json.write_text("{broken\n", encoding="utf-8")
|
|
3371
3443
|
expected_invalid_json = subprocess.run(
|
|
3372
3444
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3373
3445
|
cwd=work,
|
|
3374
3446
|
capture_output=True,
|
|
3375
3447
|
text=True,
|
|
3448
|
+
encoding="utf-8",
|
|
3376
3449
|
)
|
|
3377
3450
|
if expected_invalid_json.returncode == 0:
|
|
3378
3451
|
print("invalid spec.expected.json was accepted", file=sys.stderr)
|
|
@@ -3385,12 +3458,13 @@ def run_self_test() -> int:
|
|
|
3385
3458
|
print("invalid spec.expected.json produced a traceback", file=sys.stderr)
|
|
3386
3459
|
return 1
|
|
3387
3460
|
|
|
3388
|
-
expected_json.write_text('{"verification_commands": NaN}\n')
|
|
3461
|
+
expected_json.write_text('{"verification_commands": NaN}\n', encoding="utf-8")
|
|
3389
3462
|
expected_nan_json = subprocess.run(
|
|
3390
3463
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3391
3464
|
cwd=work,
|
|
3392
3465
|
capture_output=True,
|
|
3393
3466
|
text=True,
|
|
3467
|
+
encoding="utf-8",
|
|
3394
3468
|
)
|
|
3395
3469
|
if expected_nan_json.returncode == 0:
|
|
3396
3470
|
print("NaN spec.expected.json was accepted", file=sys.stderr)
|
|
@@ -3426,6 +3500,7 @@ def run_self_test() -> int:
|
|
|
3426
3500
|
)
|
|
3427
3501
|
external_diff_base_sha = subprocess.check_output(
|
|
3428
3502
|
["git", "rev-parse", "HEAD"], cwd=external_diff_root, text=True,
|
|
3503
|
+
encoding="utf-8",
|
|
3429
3504
|
).strip()
|
|
3430
3505
|
(external_diff_root / "outside.txt").write_text("worktree-only\n", encoding="utf-8")
|
|
3431
3506
|
(external_diff_devlyn / "external-diff.patch").write_text(
|
|
@@ -3449,26 +3524,28 @@ def run_self_test() -> int:
|
|
|
3449
3524
|
"base_ref": {"sha": external_diff_base_sha},
|
|
3450
3525
|
}
|
|
3451
3526
|
external_diff_state_path = external_diff_devlyn / "pipeline.state.json"
|
|
3452
|
-
external_diff_state_path.write_text(json.dumps(external_diff_state) + "\n")
|
|
3527
|
+
external_diff_state_path.write_text(json.dumps(external_diff_state) + "\n", encoding="utf-8")
|
|
3453
3528
|
external_diff_free_form = subprocess.run(
|
|
3454
3529
|
[sys.executable, script_path],
|
|
3455
3530
|
cwd=external_diff_root,
|
|
3456
3531
|
capture_output=True,
|
|
3457
3532
|
text=True,
|
|
3533
|
+
encoding="utf-8",
|
|
3458
3534
|
)
|
|
3459
3535
|
external_diff_findings_path = external_diff_devlyn / output_findings_name()
|
|
3460
3536
|
external_diff_free_form_findings = (
|
|
3461
|
-
external_diff_findings_path.read_text()
|
|
3537
|
+
external_diff_findings_path.read_text(encoding="utf-8")
|
|
3462
3538
|
if external_diff_findings_path.is_file()
|
|
3463
3539
|
else ""
|
|
3464
3540
|
)
|
|
3465
3541
|
external_diff_state["mode"] = "verify-only"
|
|
3466
|
-
external_diff_state_path.write_text(json.dumps(external_diff_state) + "\n")
|
|
3542
|
+
external_diff_state_path.write_text(json.dumps(external_diff_state) + "\n", encoding="utf-8")
|
|
3467
3543
|
external_diff_verify_only = subprocess.run(
|
|
3468
3544
|
[sys.executable, script_path],
|
|
3469
3545
|
cwd=external_diff_root,
|
|
3470
3546
|
capture_output=True,
|
|
3471
3547
|
text=True,
|
|
3548
|
+
encoding="utf-8",
|
|
3472
3549
|
)
|
|
3473
3550
|
if external_diff_free_form.returncode != 1:
|
|
3474
3551
|
print("non-verify-only mode accepted .devlyn/external-diff.patch", file=sys.stderr)
|
|
@@ -3514,12 +3591,13 @@ def run_self_test() -> int:
|
|
|
3514
3591
|
"spec_path": str(integrity_spec),
|
|
3515
3592
|
"spec_sha256": "0" * 64,
|
|
3516
3593
|
}
|
|
3517
|
-
}))
|
|
3594
|
+
}), encoding="utf-8")
|
|
3518
3595
|
spec_bad_hash_run = subprocess.run(
|
|
3519
3596
|
[sys.executable, script_path],
|
|
3520
3597
|
cwd=spec_integrity,
|
|
3521
3598
|
capture_output=True,
|
|
3522
3599
|
text=True,
|
|
3600
|
+
encoding="utf-8",
|
|
3523
3601
|
)
|
|
3524
3602
|
if spec_bad_hash_run.returncode == 0:
|
|
3525
3603
|
print("spec source with mismatched source.spec_sha256 was accepted", file=sys.stderr)
|
|
@@ -3536,17 +3614,18 @@ def run_self_test() -> int:
|
|
|
3536
3614
|
"spec_path": str(integrity_spec),
|
|
3537
3615
|
"spec_sha256": spec_hash,
|
|
3538
3616
|
}
|
|
3539
|
-
}))
|
|
3617
|
+
}), encoding="utf-8")
|
|
3540
3618
|
spec_hash_run = subprocess.run(
|
|
3541
3619
|
[sys.executable, script_path],
|
|
3542
3620
|
cwd=spec_integrity,
|
|
3543
3621
|
capture_output=True,
|
|
3544
3622
|
text=True,
|
|
3623
|
+
encoding="utf-8",
|
|
3545
3624
|
)
|
|
3546
3625
|
if spec_hash_run.returncode != 0:
|
|
3547
3626
|
print(spec_hash_run.stderr, file=sys.stderr)
|
|
3548
3627
|
return 1
|
|
3549
|
-
staged_spec_hash = loads_strict_json((spec_integrity_devlyn / "spec-verify.json").read_text())
|
|
3628
|
+
staged_spec_hash = loads_strict_json((spec_integrity_devlyn / "spec-verify.json").read_text(encoding="utf-8"))
|
|
3550
3629
|
if staged_spec_hash.get("verification_commands", [{}])[0].get("cmd") != "printf spec-hash-ok":
|
|
3551
3630
|
print("spec source with matching source.spec_sha256 was not staged", file=sys.stderr)
|
|
3552
3631
|
return 1
|
|
@@ -3564,12 +3643,13 @@ def run_self_test() -> int:
|
|
|
3564
3643
|
)
|
|
3565
3644
|
(generated_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3566
3645
|
"source": {"type": "generated", "criteria_path": str(generated_criteria)}
|
|
3567
|
-
}))
|
|
3646
|
+
}), encoding="utf-8")
|
|
3568
3647
|
generated_missing_hash_run = subprocess.run(
|
|
3569
3648
|
[sys.executable, script_path],
|
|
3570
3649
|
cwd=generated_user,
|
|
3571
3650
|
capture_output=True,
|
|
3572
3651
|
text=True,
|
|
3652
|
+
encoding="utf-8",
|
|
3573
3653
|
)
|
|
3574
3654
|
if generated_missing_hash_run.returncode == 0:
|
|
3575
3655
|
print("generated criteria without source.criteria_sha256 was accepted", file=sys.stderr)
|
|
@@ -3585,12 +3665,13 @@ def run_self_test() -> int:
|
|
|
3585
3665
|
"criteria_path": str(generated_criteria),
|
|
3586
3666
|
"criteria_sha256": "0" * 64,
|
|
3587
3667
|
}
|
|
3588
|
-
}))
|
|
3668
|
+
}), encoding="utf-8")
|
|
3589
3669
|
generated_bad_hash_run = subprocess.run(
|
|
3590
3670
|
[sys.executable, script_path],
|
|
3591
3671
|
cwd=generated_user,
|
|
3592
3672
|
capture_output=True,
|
|
3593
3673
|
text=True,
|
|
3674
|
+
encoding="utf-8",
|
|
3594
3675
|
)
|
|
3595
3676
|
if generated_bad_hash_run.returncode == 0:
|
|
3596
3677
|
print("generated criteria with mismatched source.criteria_sha256 was accepted", file=sys.stderr)
|
|
@@ -3607,21 +3688,70 @@ def run_self_test() -> int:
|
|
|
3607
3688
|
"criteria_path": str(generated_criteria),
|
|
3608
3689
|
"criteria_sha256": generated_hash,
|
|
3609
3690
|
}
|
|
3610
|
-
}))
|
|
3691
|
+
}), encoding="utf-8")
|
|
3611
3692
|
generated_run = subprocess.run(
|
|
3612
3693
|
[sys.executable, script_path],
|
|
3613
3694
|
cwd=generated_user,
|
|
3614
3695
|
capture_output=True,
|
|
3615
3696
|
text=True,
|
|
3697
|
+
encoding="utf-8",
|
|
3616
3698
|
)
|
|
3617
3699
|
if generated_run.returncode != 0:
|
|
3618
3700
|
print(generated_run.stderr, file=sys.stderr)
|
|
3619
3701
|
return 1
|
|
3620
|
-
staged_generated = loads_strict_json((generated_devlyn / "spec-verify.json").read_text())
|
|
3702
|
+
staged_generated = loads_strict_json((generated_devlyn / "spec-verify.json").read_text(encoding="utf-8"))
|
|
3621
3703
|
if staged_generated.get("verification_commands", [{}])[0].get("cmd") != "printf generated-ok":
|
|
3622
3704
|
print("generated criteria carrier was not staged into .devlyn/spec-verify.json", file=sys.stderr)
|
|
3623
3705
|
return 1
|
|
3624
3706
|
|
|
3707
|
+
inline_marker = generated_user / "inline-command-ran"
|
|
3708
|
+
inline_command = "printf bad > inline-command-ran; printf bad"
|
|
3709
|
+
for source_type in ("generated", "spec"):
|
|
3710
|
+
for contract, diagnostic in (
|
|
3711
|
+
({"verification_commands": [{"cmd": inline_command}],
|
|
3712
|
+
"required_files": ["missing.txt"]}, "unsupported inline key(s): required_files"),
|
|
3713
|
+
({"verification_commands": [{"cmd": inline_command,
|
|
3714
|
+
"stdout_not_contians": ["bad"]}]}, "unknown key(s): stdout_not_contians"),
|
|
3715
|
+
({"verification_commands": [{"cmd": inline_command,
|
|
3716
|
+
"stdout_not_contains": ["bad"]}]}, None),
|
|
3717
|
+
):
|
|
3718
|
+
inline_marker.unlink(missing_ok=True)
|
|
3719
|
+
(generated_devlyn / output_findings_name()).unlink(missing_ok=True)
|
|
3720
|
+
generated_criteria.write_text(
|
|
3721
|
+
"# Inline constraints\n\n<!-- devlyn:verification -->\n## Verification\n\n```json\n"
|
|
3722
|
+
+ json.dumps(contract) + "\n```\n", encoding="utf-8",
|
|
3723
|
+
)
|
|
3724
|
+
prefix = "criteria" if source_type == "generated" else "spec"
|
|
3725
|
+
(generated_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3726
|
+
"source": {
|
|
3727
|
+
"type": source_type,
|
|
3728
|
+
f"{prefix}_path": str(generated_criteria),
|
|
3729
|
+
f"{prefix}_sha256": hashlib.sha256(generated_criteria.read_bytes()).hexdigest(),
|
|
3730
|
+
}
|
|
3731
|
+
}), encoding="utf-8")
|
|
3732
|
+
# A stale valid carrier must not bypass malformed source validation.
|
|
3733
|
+
(generated_devlyn / "spec-verify.json").write_text(json.dumps({
|
|
3734
|
+
"verification_commands": [{"cmd": inline_command}]
|
|
3735
|
+
}), encoding="utf-8")
|
|
3736
|
+
inline_check = subprocess.run(
|
|
3737
|
+
[sys.executable, script_path, "--check", str(generated_criteria)],
|
|
3738
|
+
cwd=generated_user, capture_output=True, text=True, encoding="utf-8",
|
|
3739
|
+
)
|
|
3740
|
+
inline_run = subprocess.run(
|
|
3741
|
+
[sys.executable, script_path], cwd=generated_user,
|
|
3742
|
+
capture_output=True, text=True, encoding="utf-8",
|
|
3743
|
+
)
|
|
3744
|
+
assert inline_check.returncode == (2 if diagnostic else 0)
|
|
3745
|
+
assert inline_run.returncode == 1
|
|
3746
|
+
if diagnostic:
|
|
3747
|
+
assert diagnostic in inline_check.stderr and diagnostic in inline_run.stderr
|
|
3748
|
+
assert not inline_marker.exists(), "malformed inline constraint executed a command"
|
|
3749
|
+
else:
|
|
3750
|
+
assert inline_marker.exists(), "valid inline output guard was not executed"
|
|
3751
|
+
assert "correctness.spec-literal-mismatch" in (
|
|
3752
|
+
generated_devlyn / output_findings_name()
|
|
3753
|
+
).read_text(encoding="utf-8")
|
|
3754
|
+
|
|
3625
3755
|
generated_criteria.write_text(
|
|
3626
3756
|
"# Criteria\n\n## Verification\n\n- generated criteria omitted its machine-readable carrier.\n",
|
|
3627
3757
|
encoding="utf-8",
|
|
@@ -3633,12 +3763,13 @@ def run_self_test() -> int:
|
|
|
3633
3763
|
"criteria_path": str(generated_criteria),
|
|
3634
3764
|
"criteria_sha256": malformed_generated_hash,
|
|
3635
3765
|
}
|
|
3636
|
-
}))
|
|
3766
|
+
}), encoding="utf-8")
|
|
3637
3767
|
malformed_generated_run = subprocess.run(
|
|
3638
3768
|
[sys.executable, script_path],
|
|
3639
3769
|
cwd=generated_user,
|
|
3640
3770
|
capture_output=True,
|
|
3641
3771
|
text=True,
|
|
3772
|
+
encoding="utf-8",
|
|
3642
3773
|
)
|
|
3643
3774
|
if malformed_generated_run.returncode == 0:
|
|
3644
3775
|
print("generated criteria without a JSON carrier was accepted", file=sys.stderr)
|
|
@@ -3654,30 +3785,80 @@ def run_self_test() -> int:
|
|
|
3654
3785
|
real_devlyn.mkdir()
|
|
3655
3786
|
real_spec = real_user / "spec.md"
|
|
3656
3787
|
real_spec.write_text(
|
|
3657
|
-
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- sibling command must print sibling-ok.\n"
|
|
3788
|
+
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- sibling command must print sibling-ok.\n",
|
|
3789
|
+
encoding="utf-8",
|
|
3658
3790
|
)
|
|
3659
3791
|
(real_user / "spec.expected.json").write_text(json.dumps({
|
|
3660
3792
|
"verification_commands": [
|
|
3661
3793
|
{"cmd": "printf sibling-ok", "stdout_contains": ["sibling-ok"]}
|
|
3662
3794
|
]
|
|
3663
|
-
}) + "\n")
|
|
3795
|
+
}) + "\n", encoding="utf-8")
|
|
3664
3796
|
(real_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3665
3797
|
"source": {"type": "spec", "spec_path": str(real_spec)}
|
|
3666
|
-
}))
|
|
3798
|
+
}), encoding="utf-8")
|
|
3667
3799
|
sibling_run = subprocess.run(
|
|
3668
3800
|
[sys.executable, script_path],
|
|
3669
3801
|
cwd=real_user,
|
|
3670
3802
|
capture_output=True,
|
|
3671
3803
|
text=True,
|
|
3804
|
+
encoding="utf-8",
|
|
3672
3805
|
)
|
|
3673
3806
|
if sibling_run.returncode != 0:
|
|
3674
3807
|
print(sibling_run.stderr, file=sys.stderr)
|
|
3675
3808
|
return 1
|
|
3676
|
-
staged = loads_strict_json((real_devlyn / "spec-verify.json").read_text())
|
|
3809
|
+
staged = loads_strict_json((real_devlyn / "spec-verify.json").read_text(encoding="utf-8"))
|
|
3677
3810
|
if staged.get("verification_commands", [{}])[0].get("cmd") != "printf sibling-ok":
|
|
3678
3811
|
print("sibling spec.expected.json was not staged into .devlyn/spec-verify.json", file=sys.stderr)
|
|
3679
3812
|
return 1
|
|
3680
3813
|
|
|
3814
|
+
pure_prose = "# Design\n\n<!-- devlyn:verification -->\n## Verification\n\n- No runtime verification commands.\n"
|
|
3815
|
+
stale_command = {"verification_commands": [{"cmd": "printf unexpected-command"}]}
|
|
3816
|
+
pure_inline = pure_prose + "\n```json\n" + json.dumps(stale_command) + "\n```\n"
|
|
3817
|
+
for name, filename, source, contract, expected_rule in (
|
|
3818
|
+
("pure-prose", "spec.md", pure_prose, {"pure_design": True}, None),
|
|
3819
|
+
("pure-inline", "design-notes.md", pure_inline, {"pure_design": True, "verification_commands": []}, None),
|
|
3820
|
+
("empty-runtime", "design-notes.md", pure_inline, {"verification_commands": []}, "correctness.spec-verify-malformed"),
|
|
3821
|
+
("contradictory-pure", "spec.md", pure_inline, {"pure_design": True, **stale_command}, "correctness.spec-verify-malformed"),
|
|
3822
|
+
("pure-required-file", "spec.md", pure_inline, {"pure_design": True, "required_files": ["missing.md"]}, "correctness.required-file-missing"),
|
|
3823
|
+
):
|
|
3824
|
+
case_root = work / name
|
|
3825
|
+
case_root.mkdir()
|
|
3826
|
+
case_devlyn = case_root / ".devlyn"
|
|
3827
|
+
case_devlyn.mkdir()
|
|
3828
|
+
case_spec = case_root / filename
|
|
3829
|
+
case_spec.write_text(source, encoding="utf-8")
|
|
3830
|
+
(case_root / "spec.expected.json").write_text(json.dumps(contract), encoding="utf-8")
|
|
3831
|
+
(case_devlyn / "spec-verify.json").write_text(json.dumps(stale_command), encoding="utf-8")
|
|
3832
|
+
(case_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3833
|
+
"source": {"type": "spec", "spec_path": str(case_spec)},
|
|
3834
|
+
}), encoding="utf-8")
|
|
3835
|
+
case_run = subprocess.run(
|
|
3836
|
+
[sys.executable, script_path], cwd=case_root,
|
|
3837
|
+
capture_output=True, text=True, encoding="utf-8",
|
|
3838
|
+
)
|
|
3839
|
+
if case_run.returncode != (1 if expected_rule else 0):
|
|
3840
|
+
print(f"{name}: sibling execution returned {case_run.returncode}: {case_run.stderr}", file=sys.stderr)
|
|
3841
|
+
return 1
|
|
3842
|
+
case_findings = [
|
|
3843
|
+
loads_strict_json(line) for line in
|
|
3844
|
+
(case_devlyn / output_findings_name()).read_text(encoding="utf-8").splitlines()
|
|
3845
|
+
]
|
|
3846
|
+
if [finding["rule_id"] for finding in case_findings] != ([expected_rule] if expected_rule else []):
|
|
3847
|
+
print(f"{name}: unexpected sibling findings: {case_findings}", file=sys.stderr)
|
|
3848
|
+
return 1
|
|
3849
|
+
case_results = case_devlyn / "spec-verify.results.json"
|
|
3850
|
+
if expected_rule == "correctness.spec-verify-malformed":
|
|
3851
|
+
if case_results.exists():
|
|
3852
|
+
print(f"{name}: malformed sibling reached command execution", file=sys.stderr)
|
|
3853
|
+
return 1
|
|
3854
|
+
elif (
|
|
3855
|
+
loads_strict_json(case_results.read_text(encoding="utf-8"))
|
|
3856
|
+
!= {"commands": [], "process_evidence": None}
|
|
3857
|
+
or (case_devlyn / "spec-verify.json").exists()
|
|
3858
|
+
):
|
|
3859
|
+
print(f"{name}: pure-design sibling ran or retained a stale command", file=sys.stderr)
|
|
3860
|
+
return 1
|
|
3861
|
+
|
|
3681
3862
|
malformed = work / "malformed-sibling"
|
|
3682
3863
|
malformed.mkdir()
|
|
3683
3864
|
malformed_devlyn = malformed / ".devlyn"
|
|
@@ -3686,48 +3867,51 @@ def run_self_test() -> int:
|
|
|
3686
3867
|
malformed_spec.write_text(
|
|
3687
3868
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n```json\n"
|
|
3688
3869
|
"{\"verification_commands\":[{\"cmd\":\"printf inline-ok\"}]}\n"
|
|
3689
|
-
"```\n"
|
|
3870
|
+
"```\n",
|
|
3871
|
+
encoding="utf-8",
|
|
3690
3872
|
)
|
|
3691
|
-
(malformed / "spec.expected.json").write_text(json.dumps({"unknown": True}) + "\n")
|
|
3873
|
+
(malformed / "spec.expected.json").write_text(json.dumps({"unknown": True}) + "\n", encoding="utf-8")
|
|
3692
3874
|
(malformed_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3693
3875
|
"source": {"type": "spec", "spec_path": str(malformed_spec)}
|
|
3694
|
-
}))
|
|
3876
|
+
}), encoding="utf-8")
|
|
3695
3877
|
malformed_run = subprocess.run(
|
|
3696
3878
|
[sys.executable, script_path],
|
|
3697
3879
|
cwd=malformed,
|
|
3698
3880
|
capture_output=True,
|
|
3699
3881
|
text=True,
|
|
3882
|
+
encoding="utf-8",
|
|
3700
3883
|
)
|
|
3701
3884
|
if malformed_run.returncode == 0:
|
|
3702
3885
|
print("malformed sibling spec.expected.json fell back to inline carrier", file=sys.stderr)
|
|
3703
3886
|
return 1
|
|
3704
3887
|
|
|
3705
3888
|
bench_spec = work / "bench-spec.md"
|
|
3706
|
-
bench_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- benchmark pre-staged wins.\n")
|
|
3889
|
+
bench_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- benchmark pre-staged wins.\n", encoding="utf-8")
|
|
3707
3890
|
(work / "spec.expected.json").write_text(json.dumps({
|
|
3708
3891
|
"verification_commands": [
|
|
3709
3892
|
{"cmd": "printf wrong", "stdout_contains": ["wrong"]}
|
|
3710
3893
|
]
|
|
3711
|
-
}) + "\n")
|
|
3894
|
+
}) + "\n", encoding="utf-8")
|
|
3712
3895
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3713
3896
|
"source": {"type": "spec", "spec_path": str(bench_spec)}
|
|
3714
|
-
}))
|
|
3897
|
+
}), encoding="utf-8")
|
|
3715
3898
|
(devlyn / "spec-verify.json").write_text(json.dumps({
|
|
3716
3899
|
"verification_commands": [
|
|
3717
3900
|
{"cmd": "printf bench-staged", "stdout_contains": ["bench-staged"]}
|
|
3718
3901
|
]
|
|
3719
|
-
}) + "\n")
|
|
3902
|
+
}) + "\n", encoding="utf-8")
|
|
3720
3903
|
bench_pre_staged = subprocess.run(
|
|
3721
3904
|
[sys.executable, script_path],
|
|
3722
3905
|
cwd=work,
|
|
3723
3906
|
env=env,
|
|
3724
3907
|
capture_output=True,
|
|
3725
3908
|
text=True,
|
|
3909
|
+
encoding="utf-8",
|
|
3726
3910
|
)
|
|
3727
3911
|
if bench_pre_staged.returncode != 0:
|
|
3728
3912
|
print(bench_pre_staged.stderr, file=sys.stderr)
|
|
3729
3913
|
return 1
|
|
3730
|
-
staged_bench = loads_strict_json((devlyn / "spec-verify.json").read_text())
|
|
3914
|
+
staged_bench = loads_strict_json((devlyn / "spec-verify.json").read_text(encoding="utf-8"))
|
|
3731
3915
|
if staged_bench.get("verification_commands", [{}])[0].get("cmd") != "printf bench-staged":
|
|
3732
3916
|
print("benchmark pre-staged contract was overwritten", file=sys.stderr)
|
|
3733
3917
|
return 1
|
|
@@ -3737,15 +3921,15 @@ def run_self_test() -> int:
|
|
|
3737
3921
|
verify_devlyn = verify_output / ".devlyn"
|
|
3738
3922
|
verify_devlyn.mkdir()
|
|
3739
3923
|
verify_spec = verify_output / "spec.md"
|
|
3740
|
-
verify_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- verify mechanical output.\n")
|
|
3924
|
+
verify_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- verify mechanical output.\n", encoding="utf-8")
|
|
3741
3925
|
(verify_output / "spec.expected.json").write_text(json.dumps({
|
|
3742
3926
|
"verification_commands": [
|
|
3743
3927
|
{"cmd": "printf wrong", "stdout_contains": ["expected"]}
|
|
3744
3928
|
]
|
|
3745
|
-
}) + "\n")
|
|
3929
|
+
}) + "\n", encoding="utf-8")
|
|
3746
3930
|
(verify_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3747
3931
|
"source": {"type": "spec", "spec_path": str(verify_spec)}
|
|
3748
|
-
}))
|
|
3932
|
+
}), encoding="utf-8")
|
|
3749
3933
|
verify_env = os.environ.copy()
|
|
3750
3934
|
verify_env.update({
|
|
3751
3935
|
"SPEC_VERIFY_PHASE": "verify_mechanical",
|
|
@@ -3758,11 +3942,12 @@ def run_self_test() -> int:
|
|
|
3758
3942
|
env=verify_env,
|
|
3759
3943
|
capture_output=True,
|
|
3760
3944
|
text=True,
|
|
3945
|
+
encoding="utf-8",
|
|
3761
3946
|
)
|
|
3762
3947
|
if verify_output_run.returncode == 0:
|
|
3763
3948
|
print("VERIFY output-mode failing command was accepted", file=sys.stderr)
|
|
3764
3949
|
return 1
|
|
3765
|
-
verify_findings = (verify_devlyn / "verify-mechanical.findings.jsonl").read_text()
|
|
3950
|
+
verify_findings = (verify_devlyn / "verify-mechanical.findings.jsonl").read_text(encoding="utf-8")
|
|
3766
3951
|
if '"phase": "verify_mechanical"' not in verify_findings or "VERIFY-MECH-" not in verify_findings:
|
|
3767
3952
|
print("VERIFY output-mode did not route findings to verify-mechanical", file=sys.stderr)
|
|
3768
3953
|
return 1
|
|
@@ -3772,7 +3957,8 @@ def run_self_test() -> int:
|
|
|
3772
3957
|
contract_devlyn = contract_root / ".devlyn"
|
|
3773
3958
|
contract_devlyn.mkdir()
|
|
3774
3959
|
(contract_root / "package.json").write_text(
|
|
3775
|
-
'{\n "dependencies": {},\n "devDependencies": {}\n}\n'
|
|
3960
|
+
'{\n "dependencies": {},\n "devDependencies": {}\n}\n',
|
|
3961
|
+
encoding="utf-8",
|
|
3776
3962
|
)
|
|
3777
3963
|
subprocess.run(["git", "init", "-q"], cwd=contract_root, check=True)
|
|
3778
3964
|
subprocess.run(["git", "add", "-A"], cwd=contract_root, check=True)
|
|
@@ -3785,14 +3971,16 @@ def run_self_test() -> int:
|
|
|
3785
3971
|
["git", "rev-parse", "HEAD"],
|
|
3786
3972
|
cwd=contract_root,
|
|
3787
3973
|
text=True,
|
|
3974
|
+
encoding="utf-8",
|
|
3788
3975
|
).strip()
|
|
3789
3976
|
contract_spec = contract_root / "spec.md"
|
|
3790
|
-
contract_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- expected contract checks.\n")
|
|
3791
|
-
(contract_root / "app.js").write_text("try { work(); } catch { return null; }\n")
|
|
3792
|
-
(contract_root / "forbidden.txt").write_text("forbidden\n")
|
|
3977
|
+
contract_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- expected contract checks.\n", encoding="utf-8")
|
|
3978
|
+
(contract_root / "app.js").write_text("try { work(); } catch { return null; }\n", encoding="utf-8")
|
|
3979
|
+
(contract_root / "forbidden.txt").write_text("forbidden\n", encoding="utf-8")
|
|
3793
3980
|
(contract_root / "package.json").write_text(
|
|
3794
3981
|
'{\n "dependencies": {\n "left-pad": "1.3.0"\n },\n'
|
|
3795
|
-
' "devDependencies": {}\n}\n'
|
|
3982
|
+
' "devDependencies": {}\n}\n',
|
|
3983
|
+
encoding="utf-8",
|
|
3796
3984
|
)
|
|
3797
3985
|
(contract_root / "spec.expected.json").write_text(json.dumps({
|
|
3798
3986
|
"verification_commands": [{"cmd": "printf ok", "stdout_contains": ["ok"]}],
|
|
@@ -3804,22 +3992,23 @@ def run_self_test() -> int:
|
|
|
3804
3992
|
"required_files": ["required.txt"],
|
|
3805
3993
|
"forbidden_files": ["forbidden.txt"],
|
|
3806
3994
|
"max_deps_added": 0,
|
|
3807
|
-
}) + "\n")
|
|
3995
|
+
}) + "\n", encoding="utf-8")
|
|
3808
3996
|
subprocess.run(["git", "add", "-A"], cwd=contract_root, check=True)
|
|
3809
3997
|
(contract_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3810
3998
|
"source": {"type": "spec", "spec_path": str(contract_spec)},
|
|
3811
3999
|
"base_ref": {"sha": base_sha},
|
|
3812
|
-
}))
|
|
4000
|
+
}), encoding="utf-8")
|
|
3813
4001
|
contract_run = subprocess.run(
|
|
3814
4002
|
[sys.executable, script_path],
|
|
3815
4003
|
cwd=contract_root,
|
|
3816
4004
|
capture_output=True,
|
|
3817
4005
|
text=True,
|
|
4006
|
+
encoding="utf-8",
|
|
3818
4007
|
)
|
|
3819
4008
|
if contract_run.returncode == 0:
|
|
3820
4009
|
print("expected contract violations were accepted", file=sys.stderr)
|
|
3821
4010
|
return 1
|
|
3822
|
-
findings_text = (contract_devlyn / output_findings_name()).read_text()
|
|
4011
|
+
findings_text = (contract_devlyn / output_findings_name()).read_text(encoding="utf-8")
|
|
3823
4012
|
for rule_id in (
|
|
3824
4013
|
"correctness.forbidden-pattern",
|
|
3825
4014
|
"correctness.required-file-missing",
|
|
@@ -3837,13 +4026,14 @@ def run_self_test() -> int:
|
|
|
3837
4026
|
"exit_code": 0,
|
|
3838
4027
|
"tags": ["boundary_overlap"],
|
|
3839
4028
|
"tag_evidence": {"boundary_overlap": ["one_minute_overlap"]},
|
|
3840
|
-
}) + "\n")
|
|
4029
|
+
}) + "\n", encoding="utf-8")
|
|
3841
4030
|
weak = subprocess.run(
|
|
3842
4031
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3843
4032
|
cwd=work,
|
|
3844
4033
|
env=env,
|
|
3845
4034
|
capture_output=True,
|
|
3846
4035
|
text=True,
|
|
4036
|
+
encoding="utf-8",
|
|
3847
4037
|
)
|
|
3848
4038
|
if weak.returncode == 0:
|
|
3849
4039
|
print("incomplete boundary_overlap evidence was accepted", file=sys.stderr)
|
|
@@ -3857,11 +4047,12 @@ def run_self_test() -> int:
|
|
|
3857
4047
|
error_spec.write_text(
|
|
3858
4048
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
3859
4049
|
"- Invalid input must print JSON error object `{ \"error\": \"bad_input\" }` to stderr and exit 2.\n"
|
|
3860
|
-
"- Malformed input must exit 2 and print stderr JSON with keys `code` and `detail`; values are implementation-defined.\n"
|
|
4050
|
+
"- Malformed input must exit 2 and print stderr JSON with keys `code` and `detail`; values are implementation-defined.\n",
|
|
4051
|
+
encoding="utf-8",
|
|
3861
4052
|
)
|
|
3862
4053
|
(error_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3863
4054
|
"source": {"type": "spec", "spec_path": str(error_spec)}
|
|
3864
|
-
}))
|
|
4055
|
+
}), encoding="utf-8")
|
|
3865
4056
|
(error_devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
3866
4057
|
"id": "P6",
|
|
3867
4058
|
"derived_from": "Invalid input must print a JSON error object to stderr and exit 2.",
|
|
@@ -3872,12 +4063,13 @@ def run_self_test() -> int:
|
|
|
3872
4063
|
"stdout_stderr_contract": ["asserts_named_stream_output"],
|
|
3873
4064
|
"error_contract": ["asserts_error_payload_or_stderr"],
|
|
3874
4065
|
},
|
|
3875
|
-
}) + "\n")
|
|
4066
|
+
}) + "\n", encoding="utf-8")
|
|
3876
4067
|
weak_error_contract = subprocess.run(
|
|
3877
4068
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3878
4069
|
cwd=error_root,
|
|
3879
4070
|
capture_output=True,
|
|
3880
4071
|
text=True,
|
|
4072
|
+
encoding="utf-8",
|
|
3881
4073
|
)
|
|
3882
4074
|
if weak_error_contract.returncode == 0:
|
|
3883
4075
|
print("error_contract without exit-code evidence was accepted", file=sys.stderr)
|
|
@@ -3896,12 +4088,13 @@ def run_self_test() -> int:
|
|
|
3896
4088
|
"asserts_nonzero_or_exit_2",
|
|
3897
4089
|
],
|
|
3898
4090
|
},
|
|
3899
|
-
}) + "\n")
|
|
4091
|
+
}) + "\n", encoding="utf-8")
|
|
3900
4092
|
weak_stdio_contract = subprocess.run(
|
|
3901
4093
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3902
4094
|
cwd=error_root,
|
|
3903
4095
|
capture_output=True,
|
|
3904
4096
|
text=True,
|
|
4097
|
+
encoding="utf-8",
|
|
3905
4098
|
)
|
|
3906
4099
|
if weak_stdio_contract.returncode == 0:
|
|
3907
4100
|
print("stdout_stderr_contract without stream evidence was accepted", file=sys.stderr)
|
|
@@ -3926,12 +4119,13 @@ def run_self_test() -> int:
|
|
|
3926
4119
|
"visible_text_names_exact_json_error_object",
|
|
3927
4120
|
],
|
|
3928
4121
|
},
|
|
3929
|
-
}) + "\n")
|
|
4122
|
+
}) + "\n", encoding="utf-8")
|
|
3930
4123
|
missing_exact_error_object = subprocess.run(
|
|
3931
4124
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3932
4125
|
cwd=error_root,
|
|
3933
4126
|
capture_output=True,
|
|
3934
4127
|
text=True,
|
|
4128
|
+
encoding="utf-8",
|
|
3935
4129
|
)
|
|
3936
4130
|
if missing_exact_error_object.returncode == 0:
|
|
3937
4131
|
print(
|
|
@@ -3961,12 +4155,13 @@ def run_self_test() -> int:
|
|
|
3961
4155
|
"asserts_exact_error_object",
|
|
3962
4156
|
],
|
|
3963
4157
|
},
|
|
3964
|
-
}) + "\n")
|
|
4158
|
+
}) + "\n", encoding="utf-8")
|
|
3965
4159
|
strong_json_error_shape_contract = subprocess.run(
|
|
3966
4160
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3967
4161
|
cwd=error_root,
|
|
3968
4162
|
capture_output=True,
|
|
3969
4163
|
text=True,
|
|
4164
|
+
encoding="utf-8",
|
|
3970
4165
|
)
|
|
3971
4166
|
if strong_json_error_shape_contract.returncode != 0:
|
|
3972
4167
|
print("JSON error object shape_contract with exact object evidence was rejected", file=sys.stderr)
|
|
@@ -3993,12 +4188,13 @@ def run_self_test() -> int:
|
|
|
3993
4188
|
"asserts_no_unexpected_output_keys",
|
|
3994
4189
|
],
|
|
3995
4190
|
},
|
|
3996
|
-
}) + "\n")
|
|
4191
|
+
}) + "\n", encoding="utf-8")
|
|
3997
4192
|
error_exit_shape_contract = subprocess.run(
|
|
3998
4193
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3999
4194
|
cwd=error_root,
|
|
4000
4195
|
capture_output=True,
|
|
4001
4196
|
text=True,
|
|
4197
|
+
encoding="utf-8",
|
|
4002
4198
|
)
|
|
4003
4199
|
if error_exit_shape_contract.returncode != 0:
|
|
4004
4200
|
print("error-exit shape_contract without visible exact error object was rejected", file=sys.stderr)
|
|
@@ -4012,11 +4208,12 @@ def run_self_test() -> int:
|
|
|
4012
4208
|
http_error_spec = http_error_root / "spec.md"
|
|
4013
4209
|
http_error_spec.write_text(
|
|
4014
4210
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
4015
|
-
"- An invalid query returns HTTP 400 with JSON error body `{ \"error\": \"invalid_query\", \"field\": \"per_page\" }`.\n"
|
|
4211
|
+
"- An invalid query returns HTTP 400 with JSON error body `{ \"error\": \"invalid_query\", \"field\": \"per_page\" }`.\n",
|
|
4212
|
+
encoding="utf-8",
|
|
4016
4213
|
)
|
|
4017
4214
|
(http_error_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
4018
4215
|
"source": {"type": "spec", "spec_path": str(http_error_spec)}
|
|
4019
|
-
}))
|
|
4216
|
+
}), encoding="utf-8")
|
|
4020
4217
|
(http_error_devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
4021
4218
|
"id": "P8b",
|
|
4022
4219
|
"derived_from": (
|
|
@@ -4029,12 +4226,13 @@ def run_self_test() -> int:
|
|
|
4029
4226
|
"tag_evidence": {
|
|
4030
4227
|
"http_error_contract": ["asserts_http_error_status"],
|
|
4031
4228
|
},
|
|
4032
|
-
}) + "\n")
|
|
4229
|
+
}) + "\n", encoding="utf-8")
|
|
4033
4230
|
incomplete_http_error_contract = subprocess.run(
|
|
4034
4231
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4035
4232
|
cwd=http_error_root,
|
|
4036
4233
|
capture_output=True,
|
|
4037
4234
|
text=True,
|
|
4235
|
+
encoding="utf-8",
|
|
4038
4236
|
)
|
|
4039
4237
|
if incomplete_http_error_contract.returncode == 0:
|
|
4040
4238
|
print("http_error_contract without payload evidence was accepted", file=sys.stderr)
|
|
@@ -4061,12 +4259,13 @@ def run_self_test() -> int:
|
|
|
4061
4259
|
"visible_text_names_exact_json_error_object",
|
|
4062
4260
|
],
|
|
4063
4261
|
},
|
|
4064
|
-
}) + "\n")
|
|
4262
|
+
}) + "\n", encoding="utf-8")
|
|
4065
4263
|
weak_exact_error_shape_contract = subprocess.run(
|
|
4066
4264
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4067
4265
|
cwd=http_error_root,
|
|
4068
4266
|
capture_output=True,
|
|
4069
4267
|
text=True,
|
|
4268
|
+
encoding="utf-8",
|
|
4070
4269
|
)
|
|
4071
4270
|
if weak_exact_error_shape_contract.returncode == 0:
|
|
4072
4271
|
print("exact error body shape_contract without exact object evidence was accepted", file=sys.stderr)
|
|
@@ -4094,12 +4293,13 @@ def run_self_test() -> int:
|
|
|
4094
4293
|
"asserts_exact_error_object",
|
|
4095
4294
|
],
|
|
4096
4295
|
},
|
|
4097
|
-
}) + "\n")
|
|
4296
|
+
}) + "\n", encoding="utf-8")
|
|
4098
4297
|
strong_exact_error_shape_contract = subprocess.run(
|
|
4099
4298
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4100
4299
|
cwd=http_error_root,
|
|
4101
4300
|
capture_output=True,
|
|
4102
4301
|
text=True,
|
|
4302
|
+
encoding="utf-8",
|
|
4103
4303
|
)
|
|
4104
4304
|
if strong_exact_error_shape_contract.returncode != 0:
|
|
4105
4305
|
print("exact error body shape_contract with exact object evidence was rejected", file=sys.stderr)
|
|
@@ -4114,11 +4314,12 @@ def run_self_test() -> int:
|
|
|
4114
4314
|
shape_spec.write_text(
|
|
4115
4315
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
4116
4316
|
"- On success, output is one JSON object with keys `applied`, `rejected`, and `accounts`; "
|
|
4117
|
-
"`rejected` rows have keys `id` and `reason`.\n"
|
|
4317
|
+
"`rejected` rows have keys `id` and `reason`.\n",
|
|
4318
|
+
encoding="utf-8",
|
|
4118
4319
|
)
|
|
4119
4320
|
(shape_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
4120
4321
|
"source": {"type": "spec", "spec_path": str(shape_spec)}
|
|
4121
|
-
}))
|
|
4322
|
+
}), encoding="utf-8")
|
|
4122
4323
|
(shape_devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
4123
4324
|
"id": "P8e",
|
|
4124
4325
|
"derived_from": (
|
|
@@ -4129,12 +4330,13 @@ def run_self_test() -> int:
|
|
|
4129
4330
|
"exit_code": 0,
|
|
4130
4331
|
"tags": ["shape_contract"],
|
|
4131
4332
|
"tag_evidence": {},
|
|
4132
|
-
}) + "\n")
|
|
4333
|
+
}) + "\n", encoding="utf-8")
|
|
4133
4334
|
weak_shape_contract = subprocess.run(
|
|
4134
4335
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4135
4336
|
cwd=shape_root,
|
|
4136
4337
|
capture_output=True,
|
|
4137
4338
|
text=True,
|
|
4339
|
+
encoding="utf-8",
|
|
4138
4340
|
)
|
|
4139
4341
|
if weak_shape_contract.returncode == 0:
|
|
4140
4342
|
print("shape_contract without any evidence was accepted", file=sys.stderr)
|
|
@@ -4156,12 +4358,13 @@ def run_self_test() -> int:
|
|
|
4156
4358
|
"asserts_no_unexpected_output_keys",
|
|
4157
4359
|
],
|
|
4158
4360
|
},
|
|
4159
|
-
}) + "\n")
|
|
4361
|
+
}) + "\n", encoding="utf-8")
|
|
4160
4362
|
strong_shape_contract = subprocess.run(
|
|
4161
4363
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4162
4364
|
cwd=shape_root,
|
|
4163
4365
|
capture_output=True,
|
|
4164
4366
|
text=True,
|
|
4367
|
+
encoding="utf-8",
|
|
4165
4368
|
)
|
|
4166
4369
|
if strong_shape_contract.returncode != 0:
|
|
4167
4370
|
print("shape_contract with exact key evidence was rejected", file=sys.stderr)
|
|
@@ -4181,7 +4384,8 @@ def run_self_test() -> int:
|
|
|
4181
4384
|
required_spec.write_text(
|
|
4182
4385
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
4183
4386
|
"- A failed all-or-nothing operation must roll back tentative state "
|
|
4184
|
-
"so later orders can use the released stock.\n"
|
|
4387
|
+
"so later orders can use the released stock.\n",
|
|
4388
|
+
encoding="utf-8",
|
|
4185
4389
|
)
|
|
4186
4390
|
(required_root / "spec.expected.json").write_text(json.dumps({
|
|
4187
4391
|
"verification_commands": [{"cmd": "printf ok", "stdout_contains": ["ok"]}],
|
|
@@ -4194,10 +4398,10 @@ def run_self_test() -> int:
|
|
|
4194
4398
|
),
|
|
4195
4399
|
},
|
|
4196
4400
|
],
|
|
4197
|
-
}) + "\n")
|
|
4401
|
+
}) + "\n", encoding="utf-8")
|
|
4198
4402
|
(required_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
4199
4403
|
"source": {"type": "spec", "spec_path": str(required_spec)}
|
|
4200
|
-
}))
|
|
4404
|
+
}), encoding="utf-8")
|
|
4201
4405
|
(required_devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
4202
4406
|
"id": "P14",
|
|
4203
4407
|
"derived_from": (
|
|
@@ -4213,12 +4417,13 @@ def run_self_test() -> int:
|
|
|
4213
4417
|
"later_entity_fails_or_reroutes",
|
|
4214
4418
|
],
|
|
4215
4419
|
},
|
|
4216
|
-
}) + "\n")
|
|
4420
|
+
}) + "\n", encoding="utf-8")
|
|
4217
4421
|
missing_declared_requirement = subprocess.run(
|
|
4218
4422
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4219
4423
|
cwd=required_root,
|
|
4220
4424
|
capture_output=True,
|
|
4221
4425
|
text=True,
|
|
4426
|
+
encoding="utf-8",
|
|
4222
4427
|
)
|
|
4223
4428
|
if missing_declared_requirement.returncode == 0:
|
|
4224
4429
|
print(
|
|
@@ -4250,12 +4455,13 @@ def run_self_test() -> int:
|
|
|
4250
4455
|
"later_entity_uses_released_state",
|
|
4251
4456
|
],
|
|
4252
4457
|
},
|
|
4253
|
-
}) + "\n")
|
|
4458
|
+
}) + "\n", encoding="utf-8")
|
|
4254
4459
|
covered_declared_requirement = subprocess.run(
|
|
4255
4460
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4256
4461
|
cwd=required_root,
|
|
4257
4462
|
capture_output=True,
|
|
4258
4463
|
text=True,
|
|
4464
|
+
encoding="utf-8",
|
|
4259
4465
|
)
|
|
4260
4466
|
if covered_declared_requirement.returncode != 0:
|
|
4261
4467
|
print(
|
|
@@ -4271,12 +4477,13 @@ def run_self_test() -> int:
|
|
|
4271
4477
|
"required_risk_probe_requirements": [
|
|
4272
4478
|
{"tag": "not-a-real-tag", "derived_from": "irrelevant"},
|
|
4273
4479
|
],
|
|
4274
|
-
}) + "\n")
|
|
4480
|
+
}) + "\n", encoding="utf-8")
|
|
4275
4481
|
malformed_requirement_tag = subprocess.run(
|
|
4276
4482
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4277
4483
|
cwd=required_root,
|
|
4278
4484
|
capture_output=True,
|
|
4279
4485
|
text=True,
|
|
4486
|
+
encoding="utf-8",
|
|
4280
4487
|
)
|
|
4281
4488
|
if malformed_requirement_tag.returncode == 0:
|
|
4282
4489
|
print("required_risk_probe_requirements with an unknown tag was accepted", file=sys.stderr)
|
|
@@ -4287,12 +4494,13 @@ def run_self_test() -> int:
|
|
|
4287
4494
|
"required_risk_probe_requirements": [
|
|
4288
4495
|
{"tag": "rollback_state", "derived_from": "text not present in the spec"},
|
|
4289
4496
|
],
|
|
4290
|
-
}) + "\n")
|
|
4497
|
+
}) + "\n", encoding="utf-8")
|
|
4291
4498
|
malformed_requirement_derived_from = subprocess.run(
|
|
4292
4499
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4293
4500
|
cwd=required_root,
|
|
4294
4501
|
capture_output=True,
|
|
4295
4502
|
text=True,
|
|
4503
|
+
encoding="utf-8",
|
|
4296
4504
|
)
|
|
4297
4505
|
if malformed_requirement_derived_from.returncode == 0:
|
|
4298
4506
|
print(
|
|
@@ -4310,11 +4518,12 @@ def run_self_test() -> int:
|
|
|
4310
4518
|
atomic_batch_spec.write_text(
|
|
4311
4519
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
4312
4520
|
"- A POST with one valid + one invalid item returns `400`, AND a subsequent GET returns the same list as before the import.\n"
|
|
4313
|
-
"- A POST with all-valid items returns `201`, and the items appear in GET output in order with distinct ids.\n"
|
|
4521
|
+
"- A POST with all-valid items returns `201`, and the items appear in GET output in order with distinct ids.\n",
|
|
4522
|
+
encoding="utf-8",
|
|
4314
4523
|
)
|
|
4315
4524
|
(atomic_batch_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
4316
4525
|
"source": {"type": "spec", "spec_path": str(atomic_batch_spec)}
|
|
4317
|
-
}))
|
|
4526
|
+
}), encoding="utf-8")
|
|
4318
4527
|
(atomic_batch_devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
4319
4528
|
"id": "P13b",
|
|
4320
4529
|
"derived_from": (
|
|
@@ -4330,12 +4539,13 @@ def run_self_test() -> int:
|
|
|
4330
4539
|
"asserts_store_unchanged_after_failure",
|
|
4331
4540
|
],
|
|
4332
4541
|
},
|
|
4333
|
-
}) + "\n")
|
|
4542
|
+
}) + "\n", encoding="utf-8")
|
|
4334
4543
|
incomplete_atomic_batch_probe = subprocess.run(
|
|
4335
4544
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4336
4545
|
cwd=atomic_batch_root,
|
|
4337
4546
|
capture_output=True,
|
|
4338
4547
|
text=True,
|
|
4548
|
+
encoding="utf-8",
|
|
4339
4549
|
)
|
|
4340
4550
|
if incomplete_atomic_batch_probe.returncode == 0:
|
|
4341
4551
|
print("atomic_batch_state without success-order evidence was accepted", file=sys.stderr)
|
|
@@ -4350,15 +4560,15 @@ def run_self_test() -> int:
|
|
|
4350
4560
|
(scope_root / "lib").mkdir()
|
|
4351
4561
|
(scope_root / "lib2").mkdir()
|
|
4352
4562
|
(scope_root / "data").mkdir()
|
|
4353
|
-
(scope_root / "bin" / "cli.js").write_text("module.exports = {};\n")
|
|
4354
|
-
(scope_root / "lib" / "keep.js").write_text("module.exports = {};\n")
|
|
4355
|
-
(scope_root / "lib2" / "keep.js").write_text("module.exports = {};\n")
|
|
4356
|
-
(scope_root / "data" / "usage-stats.json").write_text("{}\n")
|
|
4563
|
+
(scope_root / "bin" / "cli.js").write_text("module.exports = {};\n", encoding="utf-8")
|
|
4564
|
+
(scope_root / "lib" / "keep.js").write_text("module.exports = {};\n", encoding="utf-8")
|
|
4565
|
+
(scope_root / "lib2" / "keep.js").write_text("module.exports = {};\n", encoding="utf-8")
|
|
4566
|
+
(scope_root / "data" / "usage-stats.json").write_text("{}\n", encoding="utf-8")
|
|
4357
4567
|
scope_spec = scope_root / "spec.md"
|
|
4358
|
-
scope_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- scope gate checks.\n")
|
|
4568
|
+
scope_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- scope gate checks.\n", encoding="utf-8")
|
|
4359
4569
|
(scope_root / "spec.expected.json").write_text(json.dumps({
|
|
4360
4570
|
"verification_commands": [{"cmd": "printf ok", "stdout_contains": ["ok"]}],
|
|
4361
|
-
}) + "\n")
|
|
4571
|
+
}) + "\n", encoding="utf-8")
|
|
4362
4572
|
subprocess.run(["git", "init", "-q"], cwd=scope_root, check=True)
|
|
4363
4573
|
subprocess.run(["git", "add", "-A"], cwd=scope_root, check=True)
|
|
4364
4574
|
subprocess.run(
|
|
@@ -4368,11 +4578,12 @@ def run_self_test() -> int:
|
|
|
4368
4578
|
)
|
|
4369
4579
|
scope_base_sha = subprocess.check_output(
|
|
4370
4580
|
["git", "rev-parse", "HEAD"], cwd=scope_root, text=True,
|
|
4581
|
+
encoding="utf-8",
|
|
4371
4582
|
).strip()
|
|
4372
4583
|
(scope_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
4373
4584
|
"source": {"type": "spec", "spec_path": str(scope_spec)},
|
|
4374
4585
|
"base_ref": {"sha": scope_base_sha},
|
|
4375
|
-
}))
|
|
4586
|
+
}), encoding="utf-8")
|
|
4376
4587
|
(scope_root / "preexisting.local").write_text("pre-existing untracked\n", encoding="utf-8")
|
|
4377
4588
|
# Writer parity: --write-untracked-baseline must share the reader's
|
|
4378
4589
|
# parser — untracked DIRECTORIES expand to per-file paths and
|
|
@@ -4384,6 +4595,7 @@ def run_self_test() -> int:
|
|
|
4384
4595
|
write_baseline_run = subprocess.run(
|
|
4385
4596
|
[sys.executable, script_path, "--write-untracked-baseline"],
|
|
4386
4597
|
cwd=scope_root, capture_output=True, text=True,
|
|
4598
|
+
encoding="utf-8",
|
|
4387
4599
|
)
|
|
4388
4600
|
if write_baseline_run.returncode != 0:
|
|
4389
4601
|
print("--write-untracked-baseline failed", file=sys.stderr)
|
|
@@ -4399,30 +4611,33 @@ def run_self_test() -> int:
|
|
|
4399
4611
|
scope_build_gate_env["SPEC_VERIFY_PHASE"] = "build_gate"
|
|
4400
4612
|
|
|
4401
4613
|
# Test 1: no .devlyn/plan.md at all -> fail-closed CRITICAL, not a no-op.
|
|
4402
|
-
(scope_root / "bin" / "cli.js").write_text("module.exports = { ok: true };\n")
|
|
4614
|
+
(scope_root / "bin" / "cli.js").write_text("module.exports = { ok: true };\n", encoding="utf-8")
|
|
4403
4615
|
missing_plan_run = subprocess.run(
|
|
4404
4616
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4405
4617
|
capture_output=True, text=True,
|
|
4618
|
+
encoding="utf-8",
|
|
4406
4619
|
)
|
|
4407
4620
|
if missing_plan_run.returncode == 0:
|
|
4408
4621
|
print("BUILD_GATE accepted a run with no plan.md", file=sys.stderr)
|
|
4409
4622
|
return 1
|
|
4410
|
-
if "scope.authorized-surface-malformed" not in scope_findings_path.read_text():
|
|
4623
|
+
if "scope.authorized-surface-malformed" not in scope_findings_path.read_text(encoding="utf-8"):
|
|
4411
4624
|
print("missing plan.md did not emit scope.authorized-surface-malformed", file=sys.stderr)
|
|
4412
4625
|
return 1
|
|
4413
4626
|
|
|
4414
4627
|
# Test 2: plan.md present but no authorized_surface json block -> malformed.
|
|
4415
4628
|
(scope_devlyn / "plan.md").write_text(
|
|
4416
|
-
"# PLAN\n\n<!-- devlyn:authorized-surface -->\n## 1. Files to touch\n\n- `bin/cli.js` (edit): ship the fix.\n"
|
|
4629
|
+
"# PLAN\n\n<!-- devlyn:authorized-surface -->\n## 1. Files to touch\n\n- `bin/cli.js` (edit): ship the fix.\n",
|
|
4630
|
+
encoding="utf-8",
|
|
4417
4631
|
)
|
|
4418
4632
|
malformed_block_run = subprocess.run(
|
|
4419
4633
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4420
4634
|
capture_output=True, text=True,
|
|
4635
|
+
encoding="utf-8",
|
|
4421
4636
|
)
|
|
4422
4637
|
if malformed_block_run.returncode == 0:
|
|
4423
4638
|
print("BUILD_GATE accepted plan.md with no authorized_surface block", file=sys.stderr)
|
|
4424
4639
|
return 1
|
|
4425
|
-
if "scope.authorized-surface-malformed" not in scope_findings_path.read_text():
|
|
4640
|
+
if "scope.authorized-surface-malformed" not in scope_findings_path.read_text(encoding="utf-8"):
|
|
4426
4641
|
print("missing authorized_surface block did not emit scope.authorized-surface-malformed", file=sys.stderr)
|
|
4427
4642
|
return 1
|
|
4428
4643
|
malformed_print_surface = subprocess.run(
|
|
@@ -4438,15 +4653,18 @@ def run_self_test() -> int:
|
|
|
4438
4653
|
(scope_devlyn / "plan.md").write_text(
|
|
4439
4654
|
"# PLAN\n\n<!-- devlyn:authorized-surface -->\n## 1. Files to touch\n\n"
|
|
4440
4655
|
"```json\n"
|
|
4441
|
-
+ json.dumps({"authorized_surface": [entry]}) + "\n```\n"
|
|
4656
|
+
+ json.dumps({"authorized_surface": [entry]}) + "\n```\n",
|
|
4657
|
+
encoding="utf-8",
|
|
4442
4658
|
)
|
|
4443
4659
|
brace_build_gate = subprocess.run(
|
|
4444
4660
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4445
4661
|
capture_output=True, text=True,
|
|
4662
|
+
encoding="utf-8",
|
|
4446
4663
|
)
|
|
4447
4664
|
brace_print_surface = subprocess.run(
|
|
4448
4665
|
[sys.executable, script_path, "--print-authorized-surface"], cwd=scope_root,
|
|
4449
4666
|
capture_output=True, text=True,
|
|
4667
|
+
encoding="utf-8",
|
|
4450
4668
|
)
|
|
4451
4669
|
findings_text = scope_findings_path.read_text(encoding="utf-8")
|
|
4452
4670
|
if (
|
|
@@ -4466,9 +4684,10 @@ def run_self_test() -> int:
|
|
|
4466
4684
|
"- `bin/cli.js` (edit): ship the fix.\n\n"
|
|
4467
4685
|
"```json\n"
|
|
4468
4686
|
'{"authorized_surface": ["bin/cli.js", "lib/**", "authorized-but-uncreated.txt"]}\n'
|
|
4469
|
-
"```\n"
|
|
4687
|
+
"```\n",
|
|
4688
|
+
encoding="utf-8",
|
|
4470
4689
|
)
|
|
4471
|
-
(scope_root / "lib" / "new.js").write_text("module.exports = { created: true };\n")
|
|
4690
|
+
(scope_root / "lib" / "new.js").write_text("module.exports = { created: true };\n", encoding="utf-8")
|
|
4472
4691
|
print_surface = subprocess.run(
|
|
4473
4692
|
[sys.executable, script_path, "--print-authorized-surface"],
|
|
4474
4693
|
cwd=scope_root,
|
|
@@ -4490,16 +4709,18 @@ def run_self_test() -> int:
|
|
|
4490
4709
|
missing_baseline_run = subprocess.run(
|
|
4491
4710
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4492
4711
|
capture_output=True, text=True,
|
|
4712
|
+
encoding="utf-8",
|
|
4493
4713
|
)
|
|
4494
4714
|
if missing_baseline_run.returncode == 0:
|
|
4495
4715
|
print("BUILD_GATE accepted missing .devlyn/untracked.baseline", file=sys.stderr)
|
|
4496
4716
|
return 1
|
|
4497
|
-
if "untracked.baseline" not in scope_findings_path.read_text():
|
|
4717
|
+
if "untracked.baseline" not in scope_findings_path.read_text(encoding="utf-8"):
|
|
4498
4718
|
print("missing untracked baseline did not emit a scope finding", file=sys.stderr)
|
|
4499
4719
|
return 1
|
|
4500
4720
|
rewrite_baseline_run = subprocess.run(
|
|
4501
4721
|
[sys.executable, script_path, "--write-untracked-baseline"],
|
|
4502
4722
|
cwd=scope_root, capture_output=True, text=True,
|
|
4723
|
+
encoding="utf-8",
|
|
4503
4724
|
)
|
|
4504
4725
|
if rewrite_baseline_run.returncode != 0:
|
|
4505
4726
|
print("--write-untracked-baseline re-run failed", file=sys.stderr)
|
|
@@ -4507,29 +4728,31 @@ def run_self_test() -> int:
|
|
|
4507
4728
|
in_scope_run = subprocess.run(
|
|
4508
4729
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4509
4730
|
capture_output=True, text=True,
|
|
4731
|
+
encoding="utf-8",
|
|
4510
4732
|
)
|
|
4511
4733
|
if in_scope_run.returncode != 0:
|
|
4512
4734
|
print("in-scope-only diff was rejected", file=sys.stderr)
|
|
4513
4735
|
print(in_scope_run.stderr, file=sys.stderr)
|
|
4514
4736
|
return 1
|
|
4515
|
-
if "scope." in scope_findings_path.read_text():
|
|
4737
|
+
if "scope." in scope_findings_path.read_text(encoding="utf-8"):
|
|
4516
4738
|
print("in-scope-only diff produced a spurious scope finding", file=sys.stderr)
|
|
4517
|
-
print(scope_findings_path.read_text(), file=sys.stderr)
|
|
4739
|
+
print(scope_findings_path.read_text(encoding="utf-8"), file=sys.stderr)
|
|
4518
4740
|
return 1
|
|
4519
4741
|
|
|
4520
4742
|
# Test 4: directory grant covers lib/**; an out-of-scope file must be
|
|
4521
4743
|
# flagged, and the fix_hint must never suggest self-authorization.
|
|
4522
|
-
(scope_root / "lib" / "keep.js").write_text("module.exports = { touched: true };\n")
|
|
4523
|
-
(scope_root / "data" / "usage-stats.json").write_text('{"leaked": true}\n')
|
|
4524
|
-
(scope_root / "data" / "scratch.json").write_text('{"untracked": true}\n')
|
|
4744
|
+
(scope_root / "lib" / "keep.js").write_text("module.exports = { touched: true };\n", encoding="utf-8")
|
|
4745
|
+
(scope_root / "data" / "usage-stats.json").write_text('{"leaked": true}\n', encoding="utf-8")
|
|
4746
|
+
(scope_root / "data" / "scratch.json").write_text('{"untracked": true}\n', encoding="utf-8")
|
|
4525
4747
|
out_of_scope_run = subprocess.run(
|
|
4526
4748
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4527
4749
|
capture_output=True, text=True,
|
|
4750
|
+
encoding="utf-8",
|
|
4528
4751
|
)
|
|
4529
4752
|
if out_of_scope_run.returncode == 0:
|
|
4530
4753
|
print("out-of-scope file was accepted", file=sys.stderr)
|
|
4531
4754
|
return 1
|
|
4532
|
-
out_of_scope_lines = scope_findings_path.read_text().splitlines()
|
|
4755
|
+
out_of_scope_lines = scope_findings_path.read_text(encoding="utf-8").splitlines()
|
|
4533
4756
|
out_of_scope_findings = [loads_strict_json(line) for line in out_of_scope_lines if line.strip()]
|
|
4534
4757
|
flagged_files = {f["file"] for f in out_of_scope_findings if f.get("rule_id") == "scope.out-of-scope-file"}
|
|
4535
4758
|
if "data/usage-stats.json" not in flagged_files:
|
|
@@ -4558,14 +4781,15 @@ def run_self_test() -> int:
|
|
|
4558
4781
|
|
|
4559
4782
|
# Test 5: lib2/keep.js must NOT be covered by the lib/** grant
|
|
4560
4783
|
# (directory-prefix boundary, not a bare string-prefix match).
|
|
4561
|
-
(scope_root / "lib2" / "keep.js").write_text("module.exports = { touched: true };\n")
|
|
4784
|
+
(scope_root / "lib2" / "keep.js").write_text("module.exports = { touched: true };\n", encoding="utf-8")
|
|
4562
4785
|
subprocess.run(
|
|
4563
4786
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4564
4787
|
capture_output=True, text=True,
|
|
4788
|
+
encoding="utf-8",
|
|
4565
4789
|
)
|
|
4566
4790
|
boundary_flagged = {
|
|
4567
4791
|
loads_strict_json(line)["file"]
|
|
4568
|
-
for line in scope_findings_path.read_text().splitlines() if line.strip()
|
|
4792
|
+
for line in scope_findings_path.read_text(encoding="utf-8").splitlines() if line.strip()
|
|
4569
4793
|
}
|
|
4570
4794
|
if "lib2/keep.js" not in boundary_flagged:
|
|
4571
4795
|
print("lib/** incorrectly matched lib2/keep.js (directory-prefix boundary bug)", file=sys.stderr)
|
|
@@ -4585,8 +4809,9 @@ def run_self_test() -> int:
|
|
|
4585
4809
|
subprocess.run(
|
|
4586
4810
|
[sys.executable, script_path], cwd=scope_root, env=verify_mech_env,
|
|
4587
4811
|
capture_output=True, text=True,
|
|
4812
|
+
encoding="utf-8",
|
|
4588
4813
|
)
|
|
4589
|
-
verify_mech_findings = (scope_devlyn / "verify-mechanical.findings.jsonl").read_text()
|
|
4814
|
+
verify_mech_findings = (scope_devlyn / "verify-mechanical.findings.jsonl").read_text(encoding="utf-8")
|
|
4590
4815
|
if "scope." in verify_mech_findings:
|
|
4591
4816
|
print("VERIFY MECHANICAL ran the BUILD_GATE-only authorized_surface gate", file=sys.stderr)
|
|
4592
4817
|
print(verify_mech_findings, file=sys.stderr)
|
|
@@ -4799,8 +5024,8 @@ def main() -> int:
|
|
|
4799
5024
|
print(f"[spec-verify] carrier malformed: {expected_error}", file=sys.stderr)
|
|
4800
5025
|
write_malformed_finding(devlyn_dir, expected_error, expected_path)
|
|
4801
5026
|
return 1
|
|
4802
|
-
if
|
|
4803
|
-
staged, error = (
|
|
5027
|
+
if expected_found:
|
|
5028
|
+
staged, error = (expected_staged, None)
|
|
4804
5029
|
else:
|
|
4805
5030
|
staged, error = stage_from_source(source_md, devlyn_dir)
|
|
4806
5031
|
else:
|
|
@@ -4850,7 +5075,7 @@ def main() -> int:
|
|
|
4850
5075
|
return 0
|
|
4851
5076
|
else:
|
|
4852
5077
|
try:
|
|
4853
|
-
spec = loads_strict_json(spec_path.read_text())
|
|
5078
|
+
spec = loads_strict_json(spec_path.read_text(encoding="utf-8"))
|
|
4854
5079
|
except (ValueError, OSError) as e:
|
|
4855
5080
|
print(f"[spec-verify] error: cannot parse {spec_path}: {e}", file=sys.stderr)
|
|
4856
5081
|
return 2
|
|
@@ -5145,12 +5370,12 @@ def main() -> int:
|
|
|
5145
5370
|
results_path.write_text(json.dumps({
|
|
5146
5371
|
"commands": results,
|
|
5147
5372
|
"process_evidence": evidence_carrier,
|
|
5148
|
-
}, indent=2) + "\n")
|
|
5373
|
+
}, indent=2) + "\n", encoding="utf-8")
|
|
5149
5374
|
|
|
5150
5375
|
# Append findings (jsonl). BUILD_GATE merge step concatenates this onto
|
|
5151
5376
|
# build_gate.findings.jsonl; never overwrite the orchestrator's own gate
|
|
5152
5377
|
# findings. Truncate this file each run since it is a per-round artifact.
|
|
5153
|
-
with findings_path.open("w") as fh:
|
|
5378
|
+
with findings_path.open("w", encoding="utf-8") as fh:
|
|
5154
5379
|
for f in findings:
|
|
5155
5380
|
fh.write(json.dumps(f) + "\n")
|
|
5156
5381
|
|
|
@@ -5181,4 +5406,5 @@ def main() -> int:
|
|
|
5181
5406
|
|
|
5182
5407
|
|
|
5183
5408
|
if __name__ == "__main__":
|
|
5409
|
+
runpy.run_path(str(Path(__file__).with_name("platform-support.py")))["configure_utf8"]()
|
|
5184
5410
|
sys.exit(main())
|