devlyn-cli 3.0.1 → 3.1.1
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/AGENTS.md +1 -1
- package/CLAUDE.md +3 -3
- package/README.md +20 -3
- 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 +4 -2
- package/config/skills/_shared/resolve-bootstrap.py +102 -59
- 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 +274 -157
- package/config/skills/_shared/state-phase-write.py +26 -9
- package/config/skills/_shared/task-complete.py +1173 -0
- package/config/skills/_shared/verify-merge-findings.py +9 -8
- package/config/skills/devlyn:queue/SKILL.md +3 -3
- package/config/skills/devlyn:resolve/SKILL.md +14 -7
- package/config/skills/devlyn:resolve/references/outer-loop.md +10 -2
- package/config/skills/devlyn:resolve/references/phases/verify.md +5 -3
- package/config/skills/devlyn:resolve/references/task-completion.md +117 -0
- package/package.json +1 -1
- package/scripts/lint-skills.sh +10 -6
|
@@ -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
|
|
@@ -1012,7 +1013,7 @@ def resolve_required_risk_probe_requirements(
|
|
|
1012
1013
|
return ([], err)
|
|
1013
1014
|
reqs = (data or {}).get("required_risk_probe_requirements", [])
|
|
1014
1015
|
else:
|
|
1015
|
-
_section_found, block = extract_verification_block(source_md.read_text())
|
|
1016
|
+
_section_found, block = extract_verification_block(source_md.read_text(encoding="utf-8"))
|
|
1016
1017
|
if block is None:
|
|
1017
1018
|
return ([], None)
|
|
1018
1019
|
try:
|
|
@@ -1022,7 +1023,7 @@ def resolve_required_risk_probe_requirements(
|
|
|
1022
1023
|
reqs = parsed.get("required_risk_probe_requirements", []) if isinstance(parsed, dict) else []
|
|
1023
1024
|
if not isinstance(reqs, list):
|
|
1024
1025
|
return ([], "required_risk_probe_requirements must be a list")
|
|
1025
|
-
verification_text = extract_verification_text(source_md.read_text())
|
|
1026
|
+
verification_text = extract_verification_text(source_md.read_text(encoding="utf-8"))
|
|
1026
1027
|
for i, req in enumerate(reqs):
|
|
1027
1028
|
err = validate_required_risk_probe_requirement(req, i, verification_text)
|
|
1028
1029
|
if err:
|
|
@@ -1044,12 +1045,12 @@ def load_risk_probes(
|
|
|
1044
1045
|
if source_md is None or not source_md.is_file():
|
|
1045
1046
|
return ([], "risk-probes.jsonl exists but source markdown is unavailable")
|
|
1046
1047
|
|
|
1047
|
-
verification_text = extract_verification_text(source_md.read_text())
|
|
1048
|
+
verification_text = extract_verification_text(source_md.read_text(encoding="utf-8"))
|
|
1048
1049
|
if not verification_text:
|
|
1049
1050
|
return ([], "risk-probes.jsonl exists but source has no <!-- devlyn:verification --> section")
|
|
1050
1051
|
|
|
1051
1052
|
probes: list[dict] = []
|
|
1052
|
-
for index, line in enumerate(probes_path.read_text().splitlines()):
|
|
1053
|
+
for index, line in enumerate(probes_path.read_text(encoding="utf-8").splitlines()):
|
|
1053
1054
|
if not line.strip():
|
|
1054
1055
|
continue
|
|
1055
1056
|
try:
|
|
@@ -1099,7 +1100,7 @@ def read_source(work: Path, devlyn_dir: Path) -> tuple[str | None, Path | None]:
|
|
|
1099
1100
|
if not state_path.is_file():
|
|
1100
1101
|
return (None, None)
|
|
1101
1102
|
try:
|
|
1102
|
-
state = loads_strict_json(state_path.read_text())
|
|
1103
|
+
state = loads_strict_json(state_path.read_text(encoding="utf-8"))
|
|
1103
1104
|
except (ValueError, OSError):
|
|
1104
1105
|
return (None, None)
|
|
1105
1106
|
src = state.get("source") or {}
|
|
@@ -1123,7 +1124,7 @@ def read_state(devlyn_dir: Path) -> dict:
|
|
|
1123
1124
|
if not state_path.is_file():
|
|
1124
1125
|
return {}
|
|
1125
1126
|
try:
|
|
1126
|
-
data = loads_strict_json(state_path.read_text())
|
|
1127
|
+
data = loads_strict_json(state_path.read_text(encoding="utf-8"))
|
|
1127
1128
|
except (ValueError, OSError):
|
|
1128
1129
|
return {}
|
|
1129
1130
|
return data if isinstance(data, dict) else {}
|
|
@@ -1176,7 +1177,7 @@ def source_integrity_error(src_type: str | None, state: dict, source_md: Path |
|
|
|
1176
1177
|
|
|
1177
1178
|
def load_expected_contract(expected_path: Path) -> tuple[dict | None, str | None]:
|
|
1178
1179
|
try:
|
|
1179
|
-
data = loads_strict_json(expected_path.read_text())
|
|
1180
|
+
data = loads_strict_json(expected_path.read_text(encoding="utf-8"))
|
|
1180
1181
|
except ValueError as e:
|
|
1181
1182
|
return (None, f"{expected_path} has invalid JSON: {e}")
|
|
1182
1183
|
except OSError as e:
|
|
@@ -1198,7 +1199,7 @@ def stage_from_source(md: Path, devlyn_dir: Path) -> tuple[bool, str | None]:
|
|
|
1198
1199
|
error=None → the sentinel is absent entirely (handwritten spec or
|
|
1199
1200
|
generated source missing the contract).
|
|
1200
1201
|
"""
|
|
1201
|
-
section_found, block = extract_verification_block(md.read_text())
|
|
1202
|
+
section_found, block = extract_verification_block(md.read_text(encoding="utf-8"))
|
|
1202
1203
|
if not section_found:
|
|
1203
1204
|
return (False, None)
|
|
1204
1205
|
if block is None:
|
|
@@ -1212,7 +1213,7 @@ def stage_from_source(md: Path, devlyn_dir: Path) -> tuple[bool, str | None]:
|
|
|
1212
1213
|
return (False, f"`<!-- devlyn:verification -->` ```json``` block in {md}: {err}")
|
|
1213
1214
|
normalized = {"verification_commands": data["verification_commands"]}
|
|
1214
1215
|
devlyn_dir.mkdir(parents=True, exist_ok=True)
|
|
1215
|
-
(devlyn_dir / "spec-verify.json").write_text(json.dumps(normalized, indent=2) + "\n")
|
|
1216
|
+
(devlyn_dir / "spec-verify.json").write_text(json.dumps(normalized, indent=2) + "\n", encoding="utf-8")
|
|
1216
1217
|
return (True, None)
|
|
1217
1218
|
|
|
1218
1219
|
|
|
@@ -1243,7 +1244,7 @@ def stage_from_expected(
|
|
|
1243
1244
|
return (True, False, None, expected_path, data)
|
|
1244
1245
|
normalized = {"verification_commands": commands}
|
|
1245
1246
|
devlyn_dir.mkdir(parents=True, exist_ok=True)
|
|
1246
|
-
(devlyn_dir / "spec-verify.json").write_text(json.dumps(normalized, indent=2) + "\n")
|
|
1247
|
+
(devlyn_dir / "spec-verify.json").write_text(json.dumps(normalized, indent=2) + "\n", encoding="utf-8")
|
|
1247
1248
|
return (True, True, None, expected_path, data)
|
|
1248
1249
|
|
|
1249
1250
|
|
|
@@ -1278,7 +1279,7 @@ def write_malformed_finding(
|
|
|
1278
1279
|
"blocking": True,
|
|
1279
1280
|
"status": "open",
|
|
1280
1281
|
}
|
|
1281
|
-
with findings_path.open("w") as fh:
|
|
1282
|
+
with findings_path.open("w", encoding="utf-8") as fh:
|
|
1282
1283
|
fh.write(json.dumps(finding) + "\n")
|
|
1283
1284
|
|
|
1284
1285
|
|
|
@@ -1300,7 +1301,7 @@ def write_risk_probe_integrity_finding(devlyn_dir: Path, error: str) -> None:
|
|
|
1300
1301
|
"blocking": True,
|
|
1301
1302
|
"status": "open",
|
|
1302
1303
|
}
|
|
1303
|
-
with findings_path.open("w") as fh:
|
|
1304
|
+
with findings_path.open("w", encoding="utf-8") as fh:
|
|
1304
1305
|
fh.write(json.dumps(finding) + "\n")
|
|
1305
1306
|
|
|
1306
1307
|
|
|
@@ -1321,14 +1322,14 @@ def diff_text_for_expected(work: Path, devlyn_dir: Path, state: dict) -> tuple[s
|
|
|
1321
1322
|
external_diff = devlyn_dir / "external-diff.patch"
|
|
1322
1323
|
if external_diff.is_file():
|
|
1323
1324
|
try:
|
|
1324
|
-
return (external_diff.read_text(), None)
|
|
1325
|
+
return (external_diff.read_text(encoding="utf-8"), None)
|
|
1325
1326
|
except OSError as e:
|
|
1326
1327
|
return ("", f"cannot read {external_diff}: {e}")
|
|
1327
1328
|
base_sha = ((state.get("base_ref") or {}).get("sha") or "").strip()
|
|
1328
1329
|
cmd = ["git", "diff"]
|
|
1329
1330
|
if base_sha:
|
|
1330
1331
|
cmd.append(base_sha)
|
|
1331
|
-
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True)
|
|
1332
|
+
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True, encoding="utf-8")
|
|
1332
1333
|
if proc.returncode != 0:
|
|
1333
1334
|
return ("", (proc.stderr or proc.stdout or "git diff failed").strip())
|
|
1334
1335
|
return (proc.stdout or "", None)
|
|
@@ -1340,7 +1341,7 @@ def count_deps_added(work: Path, state: dict) -> int:
|
|
|
1340
1341
|
if base_sha:
|
|
1341
1342
|
cmd.append(base_sha)
|
|
1342
1343
|
cmd.extend(["--", "package.json"])
|
|
1343
|
-
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True)
|
|
1344
|
+
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True, encoding="utf-8")
|
|
1344
1345
|
if proc.returncode != 0:
|
|
1345
1346
|
return 0
|
|
1346
1347
|
in_deps = False
|
|
@@ -1365,7 +1366,7 @@ def changed_files(work: Path, state: dict, devlyn_dir: Path) -> list[str]:
|
|
|
1365
1366
|
if external_diff.is_file():
|
|
1366
1367
|
names: list[str] = []
|
|
1367
1368
|
try:
|
|
1368
|
-
external_text = external_diff.read_text()
|
|
1369
|
+
external_text = external_diff.read_text(encoding="utf-8")
|
|
1369
1370
|
except OSError:
|
|
1370
1371
|
return []
|
|
1371
1372
|
for line in external_text.splitlines():
|
|
@@ -1378,7 +1379,7 @@ def changed_files(work: Path, state: dict, devlyn_dir: Path) -> list[str]:
|
|
|
1378
1379
|
cmd = ["git", "diff", "--name-only"]
|
|
1379
1380
|
if base_sha:
|
|
1380
1381
|
cmd.append(base_sha)
|
|
1381
|
-
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True)
|
|
1382
|
+
proc = subprocess.run(cmd, cwd=str(work), capture_output=True, text=True, encoding="utf-8")
|
|
1382
1383
|
if proc.returncode != 0:
|
|
1383
1384
|
return []
|
|
1384
1385
|
return [line.strip() for line in (proc.stdout or "").splitlines() if line.strip()]
|
|
@@ -1856,7 +1857,7 @@ def run_check_mode(md_path: Path) -> int:
|
|
|
1856
1857
|
if not md_path.is_file():
|
|
1857
1858
|
print(f"[spec-verify --check] error: {md_path} not found", file=sys.stderr)
|
|
1858
1859
|
return 2
|
|
1859
|
-
text = md_path.read_text()
|
|
1860
|
+
text = md_path.read_text(encoding="utf-8")
|
|
1860
1861
|
frontmatter_err = validate_present_spec_complexity(text)
|
|
1861
1862
|
if frontmatter_err:
|
|
1862
1863
|
print(f"[spec-verify --check] {md_path}: {frontmatter_err}", file=sys.stderr)
|
|
@@ -1947,6 +1948,7 @@ def run_self_test() -> int:
|
|
|
1947
1948
|
cwd=help_tmp,
|
|
1948
1949
|
capture_output=True,
|
|
1949
1950
|
text=True,
|
|
1951
|
+
encoding="utf-8",
|
|
1950
1952
|
)
|
|
1951
1953
|
if help_result.returncode != 0 or "usage:" not in help_result.stdout:
|
|
1952
1954
|
print(f"{help_flag} did not return usage successfully", file=sys.stderr)
|
|
@@ -1959,6 +1961,7 @@ def run_self_test() -> int:
|
|
|
1959
1961
|
cwd=help_tmp,
|
|
1960
1962
|
capture_output=True,
|
|
1961
1963
|
text=True,
|
|
1964
|
+
encoding="utf-8",
|
|
1962
1965
|
)
|
|
1963
1966
|
if unknown_result.returncode != 2 or "unknown argument" not in unknown_result.stderr:
|
|
1964
1967
|
print("unknown option did not fail closed", file=sys.stderr)
|
|
@@ -1988,15 +1991,15 @@ def run_self_test() -> int:
|
|
|
1988
1991
|
else:
|
|
1989
1992
|
raise AssertionError(f"malformed brace glob accepted: {entry}")
|
|
1990
1993
|
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")
|
|
1994
|
+
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n", encoding="utf-8")
|
|
1992
1995
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
1993
1996
|
"source": {"type": "spec", "spec_path": str(spec_md)}
|
|
1994
|
-
}))
|
|
1997
|
+
}), encoding="utf-8")
|
|
1995
1998
|
(devlyn / "spec-verify.json").write_text(json.dumps({
|
|
1996
1999
|
"verification_commands": [
|
|
1997
2000
|
{"cmd": "printf ok", "exit_code": 0, "stdout_contains": ["ok"]}
|
|
1998
2001
|
]
|
|
1999
|
-
}) + "\n")
|
|
2002
|
+
}) + "\n", encoding="utf-8")
|
|
2000
2003
|
probes_dir = devlyn / "probes"
|
|
2001
2004
|
probes_dir.mkdir()
|
|
2002
2005
|
probe_script = probes_dir / "P1.py"
|
|
@@ -2018,7 +2021,7 @@ def run_self_test() -> int:
|
|
|
2018
2021
|
],
|
|
2019
2022
|
},
|
|
2020
2023
|
}
|
|
2021
|
-
(devlyn / "risk-probes.jsonl").write_text(json.dumps(risk_probe_payload) + "\n")
|
|
2024
|
+
(devlyn / "risk-probes.jsonl").write_text(json.dumps(risk_probe_payload) + "\n", encoding="utf-8")
|
|
2022
2025
|
loaded_probes, loaded_probe_error = load_risk_probes(
|
|
2023
2026
|
devlyn, spec_md, require_present=True
|
|
2024
2027
|
)
|
|
@@ -2068,7 +2071,7 @@ def run_self_test() -> int:
|
|
|
2068
2071
|
inline_timeout_spec, inline_timeout_devlyn
|
|
2069
2072
|
)
|
|
2070
2073
|
inline_staged = loads_strict_json(
|
|
2071
|
-
(inline_timeout_devlyn / "spec-verify.json").read_text()
|
|
2074
|
+
(inline_timeout_devlyn / "spec-verify.json").read_text(encoding="utf-8")
|
|
2072
2075
|
)
|
|
2073
2076
|
if (
|
|
2074
2077
|
not staged
|
|
@@ -2089,7 +2092,7 @@ def run_self_test() -> int:
|
|
|
2089
2092
|
"verification_commands": [
|
|
2090
2093
|
{"cmd": "printf ok", "timeout_sec": invalid_timeout}
|
|
2091
2094
|
]
|
|
2092
|
-
}) + "\n")
|
|
2095
|
+
}) + "\n", encoding="utf-8")
|
|
2093
2096
|
found, staged, sibling_error, _path, _data = stage_from_expected(
|
|
2094
2097
|
sibling_timeout_spec, sibling_timeout_devlyn
|
|
2095
2098
|
)
|
|
@@ -2104,12 +2107,12 @@ def run_self_test() -> int:
|
|
|
2104
2107
|
"verification_commands": [
|
|
2105
2108
|
{"cmd": "printf ok", "timeout_sec": 5}
|
|
2106
2109
|
]
|
|
2107
|
-
}) + "\n")
|
|
2110
|
+
}) + "\n", encoding="utf-8")
|
|
2108
2111
|
found, staged, sibling_error, _path, _data = stage_from_expected(
|
|
2109
2112
|
sibling_timeout_spec, sibling_timeout_devlyn
|
|
2110
2113
|
)
|
|
2111
2114
|
sibling_staged = loads_strict_json(
|
|
2112
|
-
(sibling_timeout_devlyn / "spec-verify.json").read_text()
|
|
2115
|
+
(sibling_timeout_devlyn / "spec-verify.json").read_text(encoding="utf-8")
|
|
2113
2116
|
)
|
|
2114
2117
|
if (
|
|
2115
2118
|
not found
|
|
@@ -2149,23 +2152,24 @@ def run_self_test() -> int:
|
|
|
2149
2152
|
"run_id": "rs-timeout-run",
|
|
2150
2153
|
"source": {"type": "spec", "spec_path": str(timeout_run_spec)},
|
|
2151
2154
|
"phases": {"build_gate": {"round": 2}},
|
|
2152
|
-
}))
|
|
2155
|
+
}), encoding="utf-8")
|
|
2153
2156
|
timeout_run = subprocess.run(
|
|
2154
2157
|
[sys.executable, script_path],
|
|
2155
2158
|
cwd=timeout_run_root,
|
|
2156
2159
|
capture_output=True,
|
|
2157
2160
|
text=True,
|
|
2161
|
+
encoding="utf-8",
|
|
2158
2162
|
)
|
|
2159
2163
|
if timeout_run.returncode == 0:
|
|
2160
2164
|
print("declared one-second timeout did not fail", file=sys.stderr)
|
|
2161
2165
|
return 1
|
|
2162
2166
|
timeout_document = loads_strict_json(
|
|
2163
|
-
(timeout_run_devlyn / "spec-verify.results.json").read_text()
|
|
2167
|
+
(timeout_run_devlyn / "spec-verify.results.json").read_text(encoding="utf-8")
|
|
2164
2168
|
)
|
|
2165
2169
|
timeout_results = timeout_document["commands"]
|
|
2166
2170
|
timeout_findings = [
|
|
2167
2171
|
loads_strict_json(line)
|
|
2168
|
-
for line in (timeout_run_devlyn / output_findings_name()).read_text().splitlines()
|
|
2172
|
+
for line in (timeout_run_devlyn / output_findings_name()).read_text(encoding="utf-8").splitlines()
|
|
2169
2173
|
if line.strip()
|
|
2170
2174
|
]
|
|
2171
2175
|
if (
|
|
@@ -2185,7 +2189,7 @@ def run_self_test() -> int:
|
|
|
2185
2189
|
".devlyn/process-evidence/rs-timeout-run/build_gate/round-2/manifest.json"
|
|
2186
2190
|
)
|
|
2187
2191
|
timeout_manifest = loads_strict_json(
|
|
2188
|
-
(timeout_run_root / timeout_manifest_path).read_text()
|
|
2192
|
+
(timeout_run_root / timeout_manifest_path).read_text(encoding="utf-8")
|
|
2189
2193
|
)
|
|
2190
2194
|
if (
|
|
2191
2195
|
timeout_carrier.get("manifest", {}).get("path") != timeout_manifest_path
|
|
@@ -2249,7 +2253,7 @@ def run_self_test() -> int:
|
|
|
2249
2253
|
"run_id": "rs-runner-env",
|
|
2250
2254
|
"source": {"type": "spec", "spec_path": str(runner_env_spec)},
|
|
2251
2255
|
"phases": {"verify": {"round": 3}},
|
|
2252
|
-
}))
|
|
2256
|
+
}), encoding="utf-8")
|
|
2253
2257
|
runner_env = os.environ.copy()
|
|
2254
2258
|
runner_env.update({
|
|
2255
2259
|
"SPEC_VERIFY_PHASE": "verify_mechanical",
|
|
@@ -2262,9 +2266,10 @@ def run_self_test() -> int:
|
|
|
2262
2266
|
env=runner_env,
|
|
2263
2267
|
capture_output=True,
|
|
2264
2268
|
text=True,
|
|
2269
|
+
encoding="utf-8",
|
|
2265
2270
|
)
|
|
2266
2271
|
runner_env_document = loads_strict_json(
|
|
2267
|
-
(runner_env_devlyn / "spec-verify.results.json").read_text()
|
|
2272
|
+
(runner_env_devlyn / "spec-verify.results.json").read_text(encoding="utf-8")
|
|
2268
2273
|
)
|
|
2269
2274
|
runner_env_results = runner_env_document["commands"]
|
|
2270
2275
|
if runner_env_run.returncode != 0 or not runner_env_results[0].get("pass"):
|
|
@@ -2285,6 +2290,7 @@ def run_self_test() -> int:
|
|
|
2285
2290
|
env=env,
|
|
2286
2291
|
capture_output=True,
|
|
2287
2292
|
text=True,
|
|
2293
|
+
encoding="utf-8",
|
|
2288
2294
|
)
|
|
2289
2295
|
if validate_without_digest.returncode != 0:
|
|
2290
2296
|
print("--validate-risk-probes rejected valid probes without digest", file=sys.stderr)
|
|
@@ -2296,6 +2302,7 @@ def run_self_test() -> int:
|
|
|
2296
2302
|
env=env,
|
|
2297
2303
|
capture_output=True,
|
|
2298
2304
|
text=True,
|
|
2305
|
+
encoding="utf-8",
|
|
2299
2306
|
)
|
|
2300
2307
|
if digest_run.returncode != 0:
|
|
2301
2308
|
print("--print-risk-probes-digest rejected valid probes", file=sys.stderr)
|
|
@@ -2312,23 +2319,24 @@ def run_self_test() -> int:
|
|
|
2312
2319
|
"risk_profile": {"risk_probes_enabled": True},
|
|
2313
2320
|
"risk_probes_digest": risk_digest,
|
|
2314
2321
|
"phases": {"build_gate": {"round": 4}},
|
|
2315
|
-
}))
|
|
2322
|
+
}), encoding="utf-8")
|
|
2316
2323
|
good = subprocess.run(
|
|
2317
2324
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2318
2325
|
cwd=work,
|
|
2319
2326
|
env=env,
|
|
2320
2327
|
capture_output=True,
|
|
2321
2328
|
text=True,
|
|
2329
|
+
encoding="utf-8",
|
|
2322
2330
|
)
|
|
2323
2331
|
if good.returncode != 0:
|
|
2324
2332
|
print(good.stderr, file=sys.stderr)
|
|
2325
2333
|
return 1
|
|
2326
2334
|
good_document = loads_strict_json(
|
|
2327
|
-
(devlyn / "spec-verify.results.json").read_text()
|
|
2335
|
+
(devlyn / "spec-verify.results.json").read_text(encoding="utf-8")
|
|
2328
2336
|
)
|
|
2329
2337
|
good_manifest_path = good_document.get("process_evidence", {}).get("manifest", {}).get("path")
|
|
2330
2338
|
good_manifest = (
|
|
2331
|
-
loads_strict_json((work / good_manifest_path).read_text())
|
|
2339
|
+
loads_strict_json((work / good_manifest_path).read_text(encoding="utf-8"))
|
|
2332
2340
|
if isinstance(good_manifest_path, str)
|
|
2333
2341
|
else {}
|
|
2334
2342
|
)
|
|
@@ -2349,6 +2357,7 @@ def run_self_test() -> int:
|
|
|
2349
2357
|
env=env,
|
|
2350
2358
|
capture_output=True,
|
|
2351
2359
|
text=True,
|
|
2360
|
+
encoding="utf-8",
|
|
2352
2361
|
)
|
|
2353
2362
|
if mutated_script.returncode == 0:
|
|
2354
2363
|
print("--include-risk-probes accepted mutated probe script bytes", file=sys.stderr)
|
|
@@ -2362,13 +2371,14 @@ def run_self_test() -> int:
|
|
|
2362
2371
|
|
|
2363
2372
|
mutated_payload = dict(risk_probe_payload)
|
|
2364
2373
|
mutated_payload["id"] = "P1-mutated"
|
|
2365
|
-
(devlyn / "risk-probes.jsonl").write_text(json.dumps(mutated_payload) + "\n")
|
|
2374
|
+
(devlyn / "risk-probes.jsonl").write_text(json.dumps(mutated_payload) + "\n", encoding="utf-8")
|
|
2366
2375
|
mutated_jsonl = subprocess.run(
|
|
2367
2376
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2368
2377
|
cwd=work,
|
|
2369
2378
|
env=env,
|
|
2370
2379
|
capture_output=True,
|
|
2371
2380
|
text=True,
|
|
2381
|
+
encoding="utf-8",
|
|
2372
2382
|
)
|
|
2373
2383
|
if mutated_jsonl.returncode == 0:
|
|
2374
2384
|
print("--include-risk-probes accepted mutated risk-probes.jsonl bytes", file=sys.stderr)
|
|
@@ -2378,18 +2388,19 @@ def run_self_test() -> int:
|
|
|
2378
2388
|
print("mutated risk-probes.jsonl did not emit correctness.risk-probe-integrity", file=sys.stderr)
|
|
2379
2389
|
print(integrity_findings, file=sys.stderr)
|
|
2380
2390
|
return 1
|
|
2381
|
-
(devlyn / "risk-probes.jsonl").write_text(json.dumps(risk_probe_payload) + "\n")
|
|
2391
|
+
(devlyn / "risk-probes.jsonl").write_text(json.dumps(risk_probe_payload) + "\n", encoding="utf-8")
|
|
2382
2392
|
|
|
2383
2393
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
2384
2394
|
"source": {"type": "spec", "spec_path": str(spec_md)},
|
|
2385
2395
|
"risk_profile": {"risk_probes_enabled": True},
|
|
2386
|
-
}))
|
|
2396
|
+
}), encoding="utf-8")
|
|
2387
2397
|
missing_digest = subprocess.run(
|
|
2388
2398
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2389
2399
|
cwd=work,
|
|
2390
2400
|
env=env,
|
|
2391
2401
|
capture_output=True,
|
|
2392
2402
|
text=True,
|
|
2403
|
+
encoding="utf-8",
|
|
2393
2404
|
)
|
|
2394
2405
|
if missing_digest.returncode == 0:
|
|
2395
2406
|
print("--include-risk-probes accepted enabled risk probes with missing digest", file=sys.stderr)
|
|
@@ -2407,6 +2418,7 @@ def run_self_test() -> int:
|
|
|
2407
2418
|
env=env,
|
|
2408
2419
|
capture_output=True,
|
|
2409
2420
|
text=True,
|
|
2421
|
+
encoding="utf-8",
|
|
2410
2422
|
)
|
|
2411
2423
|
if missing_jsonl_digest.returncode != 2:
|
|
2412
2424
|
print("--print-risk-probes-digest accepted missing risk-probes.jsonl", file=sys.stderr)
|
|
@@ -2420,13 +2432,14 @@ def run_self_test() -> int:
|
|
|
2420
2432
|
"source": {"type": "spec", "spec_path": str(spec_md)},
|
|
2421
2433
|
"risk_profile": {"risk_probes_enabled": True},
|
|
2422
2434
|
"risk_probes_digest": risk_digest,
|
|
2423
|
-
}))
|
|
2435
|
+
}), encoding="utf-8")
|
|
2424
2436
|
missing_required_probe = subprocess.run(
|
|
2425
2437
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2426
2438
|
cwd=work,
|
|
2427
2439
|
env=env,
|
|
2428
2440
|
capture_output=True,
|
|
2429
2441
|
text=True,
|
|
2442
|
+
encoding="utf-8",
|
|
2430
2443
|
)
|
|
2431
2444
|
if missing_required_probe.returncode == 0:
|
|
2432
2445
|
print("--include-risk-probes accepted missing required risk-probes.jsonl", file=sys.stderr)
|
|
@@ -2439,13 +2452,14 @@ def run_self_test() -> int:
|
|
|
2439
2452
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
2440
2453
|
"source": {"type": "spec", "spec_path": str(spec_md)},
|
|
2441
2454
|
"risk_profile": {"risk_probes_enabled": False},
|
|
2442
|
-
}))
|
|
2455
|
+
}), encoding="utf-8")
|
|
2443
2456
|
missing_optional_probe = subprocess.run(
|
|
2444
2457
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2445
2458
|
cwd=work,
|
|
2446
2459
|
env=env,
|
|
2447
2460
|
capture_output=True,
|
|
2448
2461
|
text=True,
|
|
2462
|
+
encoding="utf-8",
|
|
2449
2463
|
)
|
|
2450
2464
|
if missing_optional_probe.returncode != 0:
|
|
2451
2465
|
print("--include-risk-probes rejected optional missing risk-probes.jsonl", file=sys.stderr)
|
|
@@ -2455,13 +2469,14 @@ def run_self_test() -> int:
|
|
|
2455
2469
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
2456
2470
|
"source": {"type": "spec", "spec_path": str(spec_md)},
|
|
2457
2471
|
"risk_profile": {"risk_probes_enabled": "true"},
|
|
2458
|
-
}))
|
|
2472
|
+
}), encoding="utf-8")
|
|
2459
2473
|
malformed_risk_probe_state = subprocess.run(
|
|
2460
2474
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2461
2475
|
cwd=work,
|
|
2462
2476
|
env=env,
|
|
2463
2477
|
capture_output=True,
|
|
2464
2478
|
text=True,
|
|
2479
|
+
encoding="utf-8",
|
|
2465
2480
|
)
|
|
2466
2481
|
if malformed_risk_probe_state.returncode == 0:
|
|
2467
2482
|
print("--include-risk-probes accepted non-boolean risk_probes_enabled", file=sys.stderr)
|
|
@@ -2474,13 +2489,14 @@ def run_self_test() -> int:
|
|
|
2474
2489
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
2475
2490
|
"source": {"type": "spec", "spec_path": str(spec_md)},
|
|
2476
2491
|
"risk_profile": "enabled",
|
|
2477
|
-
}))
|
|
2492
|
+
}), encoding="utf-8")
|
|
2478
2493
|
malformed_risk_profile = subprocess.run(
|
|
2479
2494
|
[sys.executable, script_path, "--include-risk-probes"],
|
|
2480
2495
|
cwd=work,
|
|
2481
2496
|
env=env,
|
|
2482
2497
|
capture_output=True,
|
|
2483
2498
|
text=True,
|
|
2499
|
+
encoding="utf-8",
|
|
2484
2500
|
)
|
|
2485
2501
|
if malformed_risk_profile.returncode == 0:
|
|
2486
2502
|
print("--include-risk-probes accepted non-object risk_profile", file=sys.stderr)
|
|
@@ -2492,7 +2508,7 @@ def run_self_test() -> int:
|
|
|
2492
2508
|
|
|
2493
2509
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
2494
2510
|
"source": {"type": "spec", "spec_path": str(spec_md)}
|
|
2495
|
-
}))
|
|
2511
|
+
}), encoding="utf-8")
|
|
2496
2512
|
(devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
2497
2513
|
"id": "P1",
|
|
2498
2514
|
"derived_from": "probe must pass visible marker.",
|
|
@@ -2508,7 +2524,7 @@ def run_self_test() -> int:
|
|
|
2508
2524
|
"asserts_no_unexpected_output_keys",
|
|
2509
2525
|
],
|
|
2510
2526
|
},
|
|
2511
|
-
}) + "\n")
|
|
2527
|
+
}) + "\n", encoding="utf-8")
|
|
2512
2528
|
|
|
2513
2529
|
good_complexity = work / "good-complexity.md"
|
|
2514
2530
|
good_complexity.write_text(
|
|
@@ -2520,6 +2536,7 @@ def run_self_test() -> int:
|
|
|
2520
2536
|
cwd=work,
|
|
2521
2537
|
capture_output=True,
|
|
2522
2538
|
text=True,
|
|
2539
|
+
encoding="utf-8",
|
|
2523
2540
|
)
|
|
2524
2541
|
if good_complexity_check.returncode != 0:
|
|
2525
2542
|
print(good_complexity_check.stderr, file=sys.stderr)
|
|
@@ -2535,6 +2552,7 @@ def run_self_test() -> int:
|
|
|
2535
2552
|
cwd=work,
|
|
2536
2553
|
capture_output=True,
|
|
2537
2554
|
text=True,
|
|
2555
|
+
encoding="utf-8",
|
|
2538
2556
|
)
|
|
2539
2557
|
if bad_complexity_check.returncode == 0:
|
|
2540
2558
|
print("unsupported spec complexity was accepted", file=sys.stderr)
|
|
@@ -2556,6 +2574,7 @@ def run_self_test() -> int:
|
|
|
2556
2574
|
cwd=work,
|
|
2557
2575
|
capture_output=True,
|
|
2558
2576
|
text=True,
|
|
2577
|
+
encoding="utf-8",
|
|
2559
2578
|
)
|
|
2560
2579
|
if weak_solo_check.returncode == 0:
|
|
2561
2580
|
print("weak solo-headroom hypothesis was accepted by --check", file=sys.stderr)
|
|
@@ -2576,6 +2595,7 @@ def run_self_test() -> int:
|
|
|
2576
2595
|
cwd=work,
|
|
2577
2596
|
capture_output=True,
|
|
2578
2597
|
text=True,
|
|
2598
|
+
encoding="utf-8",
|
|
2579
2599
|
)
|
|
2580
2600
|
if weak_descriptive_check.returncode == 0:
|
|
2581
2601
|
print("descriptive backtick solo-headroom hypothesis was accepted by --check", file=sys.stderr)
|
|
@@ -2592,6 +2612,7 @@ def run_self_test() -> int:
|
|
|
2592
2612
|
cwd=work,
|
|
2593
2613
|
capture_output=True,
|
|
2594
2614
|
text=True,
|
|
2615
|
+
encoding="utf-8",
|
|
2595
2616
|
)
|
|
2596
2617
|
if strong_solo_check.returncode != 0:
|
|
2597
2618
|
print("actionable solo-headroom hypothesis was rejected by --check", file=sys.stderr)
|
|
@@ -2610,6 +2631,7 @@ def run_self_test() -> int:
|
|
|
2610
2631
|
cwd=work,
|
|
2611
2632
|
capture_output=True,
|
|
2612
2633
|
text=True,
|
|
2634
|
+
encoding="utf-8",
|
|
2613
2635
|
)
|
|
2614
2636
|
if docs_style_solo_check.returncode != 0:
|
|
2615
2637
|
print("docs-style solo-headroom hypothesis was rejected by --check", file=sys.stderr)
|
|
@@ -2627,6 +2649,7 @@ def run_self_test() -> int:
|
|
|
2627
2649
|
cwd=work,
|
|
2628
2650
|
capture_output=True,
|
|
2629
2651
|
text=True,
|
|
2652
|
+
encoding="utf-8",
|
|
2630
2653
|
)
|
|
2631
2654
|
if weak_solo_ceiling_check.returncode == 0:
|
|
2632
2655
|
print("weak solo ceiling avoidance was accepted by --check", file=sys.stderr)
|
|
@@ -2648,6 +2671,7 @@ def run_self_test() -> int:
|
|
|
2648
2671
|
cwd=work,
|
|
2649
2672
|
capture_output=True,
|
|
2650
2673
|
text=True,
|
|
2674
|
+
encoding="utf-8",
|
|
2651
2675
|
)
|
|
2652
2676
|
if strong_solo_ceiling_check.returncode != 0:
|
|
2653
2677
|
print("actionable solo ceiling avoidance was rejected by --check", file=sys.stderr)
|
|
@@ -2669,6 +2693,7 @@ def run_self_test() -> int:
|
|
|
2669
2693
|
cwd=work,
|
|
2670
2694
|
capture_output=True,
|
|
2671
2695
|
text=True,
|
|
2696
|
+
encoding="utf-8",
|
|
2672
2697
|
)
|
|
2673
2698
|
if inline_mismatched_check.returncode == 0:
|
|
2674
2699
|
print("mismatched inline solo-headroom command was accepted by --check", file=sys.stderr)
|
|
@@ -2693,6 +2718,7 @@ def run_self_test() -> int:
|
|
|
2693
2718
|
cwd=work,
|
|
2694
2719
|
capture_output=True,
|
|
2695
2720
|
text=True,
|
|
2721
|
+
encoding="utf-8",
|
|
2696
2722
|
)
|
|
2697
2723
|
if inline_matched_check.returncode != 0:
|
|
2698
2724
|
print("matched inline solo-headroom command was rejected by --check", file=sys.stderr)
|
|
@@ -2704,13 +2730,14 @@ def run_self_test() -> int:
|
|
|
2704
2730
|
"derived_from": "probe must pass visible marker.",
|
|
2705
2731
|
"cmd": "node $BENCH_FIXTURE_DIR/verifiers/hidden.js",
|
|
2706
2732
|
"exit_code": 0,
|
|
2707
|
-
}) + "\n")
|
|
2733
|
+
}) + "\n", encoding="utf-8")
|
|
2708
2734
|
bad = subprocess.run(
|
|
2709
2735
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2710
2736
|
cwd=work,
|
|
2711
2737
|
env=env,
|
|
2712
2738
|
capture_output=True,
|
|
2713
2739
|
text=True,
|
|
2740
|
+
encoding="utf-8",
|
|
2714
2741
|
)
|
|
2715
2742
|
if bad.returncode == 0:
|
|
2716
2743
|
print("hidden verifier path was accepted", file=sys.stderr)
|
|
@@ -2725,13 +2752,14 @@ def run_self_test() -> int:
|
|
|
2725
2752
|
"cmd": "python3 .devlyn/probes/Pscript.py",
|
|
2726
2753
|
"stdout_contains": ["script-ok"],
|
|
2727
2754
|
}
|
|
2728
|
-
(devlyn / "risk-probes.jsonl").write_text(json.dumps(script_probe_payload) + "\n")
|
|
2755
|
+
(devlyn / "risk-probes.jsonl").write_text(json.dumps(script_probe_payload) + "\n", encoding="utf-8")
|
|
2729
2756
|
good_script_probe = subprocess.run(
|
|
2730
2757
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2731
2758
|
cwd=work,
|
|
2732
2759
|
env=env,
|
|
2733
2760
|
capture_output=True,
|
|
2734
2761
|
text=True,
|
|
2762
|
+
encoding="utf-8",
|
|
2735
2763
|
)
|
|
2736
2764
|
if good_script_probe.returncode != 0:
|
|
2737
2765
|
print("risk probe script file was rejected", file=sys.stderr)
|
|
@@ -2743,13 +2771,14 @@ def run_self_test() -> int:
|
|
|
2743
2771
|
"id": "Pmixed",
|
|
2744
2772
|
"cmd": "python3 .devlyn/probes/Pscript.py && python3 ./.devlyn/probes/P1.py",
|
|
2745
2773
|
}
|
|
2746
|
-
(devlyn / "risk-probes.jsonl").write_text(json.dumps(mixed_script_payload) + "\n")
|
|
2774
|
+
(devlyn / "risk-probes.jsonl").write_text(json.dumps(mixed_script_payload) + "\n", encoding="utf-8")
|
|
2747
2775
|
mixed_script_probe = subprocess.run(
|
|
2748
2776
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2749
2777
|
cwd=work,
|
|
2750
2778
|
env=env,
|
|
2751
2779
|
capture_output=True,
|
|
2752
2780
|
text=True,
|
|
2781
|
+
encoding="utf-8",
|
|
2753
2782
|
)
|
|
2754
2783
|
if mixed_script_probe.returncode != 0:
|
|
2755
2784
|
print("canonical and ./-alias probe script references were rejected", file=sys.stderr)
|
|
@@ -2780,13 +2809,14 @@ def run_self_test() -> int:
|
|
|
2780
2809
|
"derived_from": "probe must pass visible marker.",
|
|
2781
2810
|
"cmd": "python3 ./.devlyn/probes/missing.py",
|
|
2782
2811
|
"exit_code": 0,
|
|
2783
|
-
}) + "\n")
|
|
2812
|
+
}) + "\n", encoding="utf-8")
|
|
2784
2813
|
missing_script_probe = subprocess.run(
|
|
2785
2814
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2786
2815
|
cwd=work,
|
|
2787
2816
|
env=env,
|
|
2788
2817
|
capture_output=True,
|
|
2789
2818
|
text=True,
|
|
2819
|
+
encoding="utf-8",
|
|
2790
2820
|
)
|
|
2791
2821
|
if missing_script_probe.returncode == 0:
|
|
2792
2822
|
print("risk probe missing script file was accepted", file=sys.stderr)
|
|
@@ -2801,6 +2831,7 @@ def run_self_test() -> int:
|
|
|
2801
2831
|
env=env,
|
|
2802
2832
|
capture_output=True,
|
|
2803
2833
|
text=True,
|
|
2834
|
+
encoding="utf-8",
|
|
2804
2835
|
)
|
|
2805
2836
|
if missing_script_digest.returncode != 2:
|
|
2806
2837
|
print("--print-risk-probes-digest accepted a missing referenced script", file=sys.stderr)
|
|
@@ -2822,13 +2853,14 @@ def run_self_test() -> int:
|
|
|
2822
2853
|
"derived_from": "probe must pass visible marker.",
|
|
2823
2854
|
"cmd": f"python3 {bad_form}",
|
|
2824
2855
|
"exit_code": 0,
|
|
2825
|
-
}) + "\n")
|
|
2856
|
+
}) + "\n", encoding="utf-8")
|
|
2826
2857
|
bad_ref_probe = subprocess.run(
|
|
2827
2858
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2828
2859
|
cwd=work,
|
|
2829
2860
|
env=env,
|
|
2830
2861
|
capture_output=True,
|
|
2831
2862
|
text=True,
|
|
2863
|
+
encoding="utf-8",
|
|
2832
2864
|
)
|
|
2833
2865
|
bad_ref_digest = subprocess.run(
|
|
2834
2866
|
[sys.executable, script_path, "--print-risk-probes-digest"],
|
|
@@ -2836,6 +2868,7 @@ def run_self_test() -> int:
|
|
|
2836
2868
|
env=env,
|
|
2837
2869
|
capture_output=True,
|
|
2838
2870
|
text=True,
|
|
2871
|
+
encoding="utf-8",
|
|
2839
2872
|
)
|
|
2840
2873
|
unrecognized = unrecognized_risk_probe_reference(f"python3 {bad_form}")
|
|
2841
2874
|
expected_error = f"risk-probes[0].cmd has {unrecognized}"
|
|
@@ -2856,13 +2889,14 @@ def run_self_test() -> int:
|
|
|
2856
2889
|
"derived_from": "probe must pass visible marker.",
|
|
2857
2890
|
"cmd": "python3 .devlyn/probes/Phidden.py",
|
|
2858
2891
|
"exit_code": 0,
|
|
2859
|
-
}) + "\n")
|
|
2892
|
+
}) + "\n", encoding="utf-8")
|
|
2860
2893
|
hidden_script_probe = subprocess.run(
|
|
2861
2894
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2862
2895
|
cwd=work,
|
|
2863
2896
|
env=env,
|
|
2864
2897
|
capture_output=True,
|
|
2865
2898
|
text=True,
|
|
2899
|
+
encoding="utf-8",
|
|
2866
2900
|
)
|
|
2867
2901
|
if hidden_script_probe.returncode == 0:
|
|
2868
2902
|
print("risk probe script containing a hidden fixture path was accepted", file=sys.stderr)
|
|
@@ -2878,13 +2912,14 @@ def run_self_test() -> int:
|
|
|
2878
2912
|
"derived_from": "probe must pass visible marker.",
|
|
2879
2913
|
"cmd": "python3 .devlyn/probes/Pexternal.py",
|
|
2880
2914
|
"exit_code": 0,
|
|
2881
|
-
}) + "\n")
|
|
2915
|
+
}) + "\n", encoding="utf-8")
|
|
2882
2916
|
external_script_probe = subprocess.run(
|
|
2883
2917
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2884
2918
|
cwd=work,
|
|
2885
2919
|
env=env,
|
|
2886
2920
|
capture_output=True,
|
|
2887
2921
|
text=True,
|
|
2922
|
+
encoding="utf-8",
|
|
2888
2923
|
)
|
|
2889
2924
|
if external_script_probe.returncode == 0:
|
|
2890
2925
|
print("risk probe script containing an external URL was accepted", file=sys.stderr)
|
|
@@ -2894,13 +2929,14 @@ def run_self_test() -> int:
|
|
|
2894
2929
|
print(external_script_probe.stderr, file=sys.stderr)
|
|
2895
2930
|
return 1
|
|
2896
2931
|
|
|
2897
|
-
(devlyn / "risk-probes.jsonl").write_text('{"id":NaN}\n')
|
|
2932
|
+
(devlyn / "risk-probes.jsonl").write_text('{"id":NaN}\n', encoding="utf-8")
|
|
2898
2933
|
bad_probe_nan = subprocess.run(
|
|
2899
2934
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2900
2935
|
cwd=work,
|
|
2901
2936
|
env=env,
|
|
2902
2937
|
capture_output=True,
|
|
2903
2938
|
text=True,
|
|
2939
|
+
encoding="utf-8",
|
|
2904
2940
|
)
|
|
2905
2941
|
if bad_probe_nan.returncode == 0:
|
|
2906
2942
|
print("NaN risk-probes JSONL was accepted", file=sys.stderr)
|
|
@@ -2917,13 +2953,14 @@ def run_self_test() -> int:
|
|
|
2917
2953
|
"exit_code": 0,
|
|
2918
2954
|
"tags": ["error_contract"],
|
|
2919
2955
|
"tag_evidence": {"error_contract": []},
|
|
2920
|
-
}) + "\n")
|
|
2956
|
+
}) + "\n", encoding="utf-8")
|
|
2921
2957
|
bad_error_ref = subprocess.run(
|
|
2922
2958
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2923
2959
|
cwd=work,
|
|
2924
2960
|
env=env,
|
|
2925
2961
|
capture_output=True,
|
|
2926
2962
|
text=True,
|
|
2963
|
+
encoding="utf-8",
|
|
2927
2964
|
)
|
|
2928
2965
|
if bad_error_ref.returncode == 0:
|
|
2929
2966
|
print("error_contract with unrelated derived_from was accepted", file=sys.stderr)
|
|
@@ -2951,13 +2988,14 @@ def run_self_test() -> int:
|
|
|
2951
2988
|
"asserts_no_unexpected_output_keys",
|
|
2952
2989
|
],
|
|
2953
2990
|
},
|
|
2954
|
-
}) + "\n")
|
|
2991
|
+
}) + "\n", encoding="utf-8")
|
|
2955
2992
|
bad_solo_headroom_probe = subprocess.run(
|
|
2956
2993
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2957
2994
|
cwd=work,
|
|
2958
2995
|
env=env,
|
|
2959
2996
|
capture_output=True,
|
|
2960
2997
|
text=True,
|
|
2998
|
+
encoding="utf-8",
|
|
2961
2999
|
)
|
|
2962
3000
|
if bad_solo_headroom_probe.returncode == 0:
|
|
2963
3001
|
print("risk probe missing solo-headroom command coverage was accepted", file=sys.stderr)
|
|
@@ -2982,13 +3020,14 @@ def run_self_test() -> int:
|
|
|
2982
3020
|
"asserts_no_unexpected_output_keys",
|
|
2983
3021
|
],
|
|
2984
3022
|
},
|
|
2985
|
-
}) + "\n")
|
|
3023
|
+
}) + "\n", encoding="utf-8")
|
|
2986
3024
|
bad_solo_headroom_derived_from = subprocess.run(
|
|
2987
3025
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
2988
3026
|
cwd=work,
|
|
2989
3027
|
env=env,
|
|
2990
3028
|
capture_output=True,
|
|
2991
3029
|
text=True,
|
|
3030
|
+
encoding="utf-8",
|
|
2992
3031
|
)
|
|
2993
3032
|
if bad_solo_headroom_derived_from.returncode == 0:
|
|
2994
3033
|
print("risk probe with unrelated solo-headroom derived_from was accepted", file=sys.stderr)
|
|
@@ -3029,7 +3068,8 @@ def run_self_test() -> int:
|
|
|
3029
3068
|
"asserts_no_unexpected_output_keys",
|
|
3030
3069
|
],
|
|
3031
3070
|
},
|
|
3032
|
-
}) + "\n"
|
|
3071
|
+
}) + "\n",
|
|
3072
|
+
encoding="utf-8",
|
|
3033
3073
|
)
|
|
3034
3074
|
late_solo_headroom_probe = subprocess.run(
|
|
3035
3075
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
@@ -3037,6 +3077,7 @@ def run_self_test() -> int:
|
|
|
3037
3077
|
env=env,
|
|
3038
3078
|
capture_output=True,
|
|
3039
3079
|
text=True,
|
|
3080
|
+
encoding="utf-8",
|
|
3040
3081
|
)
|
|
3041
3082
|
if late_solo_headroom_probe.returncode == 0:
|
|
3042
3083
|
print("solo-headroom command in a later risk probe was accepted", file=sys.stderr)
|
|
@@ -3057,13 +3098,14 @@ def run_self_test() -> int:
|
|
|
3057
3098
|
"asserts_no_unexpected_output_keys",
|
|
3058
3099
|
],
|
|
3059
3100
|
},
|
|
3060
|
-
}) + "\n")
|
|
3101
|
+
}) + "\n", encoding="utf-8")
|
|
3061
3102
|
prefix_solo_headroom_probe = subprocess.run(
|
|
3062
3103
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3063
3104
|
cwd=work,
|
|
3064
3105
|
env=env,
|
|
3065
3106
|
capture_output=True,
|
|
3066
3107
|
text=True,
|
|
3108
|
+
encoding="utf-8",
|
|
3067
3109
|
)
|
|
3068
3110
|
if prefix_solo_headroom_probe.returncode == 0:
|
|
3069
3111
|
print("solo-headroom command prefix match was accepted", file=sys.stderr)
|
|
@@ -3084,13 +3126,14 @@ def run_self_test() -> int:
|
|
|
3084
3126
|
"asserts_no_unexpected_output_keys",
|
|
3085
3127
|
],
|
|
3086
3128
|
},
|
|
3087
|
-
}) + "\n")
|
|
3129
|
+
}) + "\n", encoding="utf-8")
|
|
3088
3130
|
good_solo_headroom_probe = subprocess.run(
|
|
3089
3131
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3090
3132
|
cwd=work,
|
|
3091
3133
|
env=env,
|
|
3092
3134
|
capture_output=True,
|
|
3093
3135
|
text=True,
|
|
3136
|
+
encoding="utf-8",
|
|
3094
3137
|
)
|
|
3095
3138
|
if good_solo_headroom_probe.returncode != 0:
|
|
3096
3139
|
print("risk probe covering solo-headroom command was rejected", file=sys.stderr)
|
|
@@ -3112,17 +3155,19 @@ def run_self_test() -> int:
|
|
|
3112
3155
|
"required_files": ["bin/cli.js"],
|
|
3113
3156
|
"forbidden_files": [],
|
|
3114
3157
|
"max_deps_added": 0,
|
|
3115
|
-
}) + "\n")
|
|
3158
|
+
}) + "\n", encoding="utf-8")
|
|
3116
3159
|
spec_md.write_text(
|
|
3117
3160
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
3118
3161
|
"- solo-headroom hypothesis: solo_claude should miss duplicate handling.\n"
|
|
3119
|
-
"- Observable command: `node check.js` exposes behavior.\n"
|
|
3162
|
+
"- Observable command: `node check.js` exposes behavior.\n",
|
|
3163
|
+
encoding="utf-8",
|
|
3120
3164
|
)
|
|
3121
3165
|
weak_sibling_solo = subprocess.run(
|
|
3122
3166
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3123
3167
|
cwd=work,
|
|
3124
3168
|
capture_output=True,
|
|
3125
3169
|
text=True,
|
|
3170
|
+
encoding="utf-8",
|
|
3126
3171
|
)
|
|
3127
3172
|
if weak_sibling_solo.returncode == 0:
|
|
3128
3173
|
print("weak sibling solo-headroom hypothesis was accepted by --check-expected", file=sys.stderr)
|
|
@@ -3143,6 +3188,7 @@ def run_self_test() -> int:
|
|
|
3143
3188
|
cwd=work,
|
|
3144
3189
|
capture_output=True,
|
|
3145
3190
|
text=True,
|
|
3191
|
+
encoding="utf-8",
|
|
3146
3192
|
)
|
|
3147
3193
|
if mismatched_sibling_solo.returncode == 0:
|
|
3148
3194
|
print("mismatched sibling solo-headroom command was accepted by --check-expected", file=sys.stderr)
|
|
@@ -3163,6 +3209,7 @@ def run_self_test() -> int:
|
|
|
3163
3209
|
cwd=work,
|
|
3164
3210
|
capture_output=True,
|
|
3165
3211
|
text=True,
|
|
3212
|
+
encoding="utf-8",
|
|
3166
3213
|
)
|
|
3167
3214
|
if matched_sibling_solo.returncode != 0:
|
|
3168
3215
|
print("matched sibling solo-headroom command was rejected by --check-expected", file=sys.stderr)
|
|
@@ -3180,6 +3227,7 @@ def run_self_test() -> int:
|
|
|
3180
3227
|
cwd=work,
|
|
3181
3228
|
capture_output=True,
|
|
3182
3229
|
text=True,
|
|
3230
|
+
encoding="utf-8",
|
|
3183
3231
|
)
|
|
3184
3232
|
if docs_style_sibling_solo.returncode != 0:
|
|
3185
3233
|
print("docs-style sibling solo-headroom command was rejected by --check-expected", file=sys.stderr)
|
|
@@ -3196,6 +3244,7 @@ def run_self_test() -> int:
|
|
|
3196
3244
|
cwd=work,
|
|
3197
3245
|
capture_output=True,
|
|
3198
3246
|
text=True,
|
|
3247
|
+
encoding="utf-8",
|
|
3199
3248
|
)
|
|
3200
3249
|
if weak_sibling_solo_ceiling.returncode == 0:
|
|
3201
3250
|
print("weak sibling solo ceiling avoidance was accepted by --check-expected", file=sys.stderr)
|
|
@@ -3216,18 +3265,20 @@ def run_self_test() -> int:
|
|
|
3216
3265
|
cwd=work,
|
|
3217
3266
|
capture_output=True,
|
|
3218
3267
|
text=True,
|
|
3268
|
+
encoding="utf-8",
|
|
3219
3269
|
)
|
|
3220
3270
|
if strong_sibling_solo_ceiling.returncode != 0:
|
|
3221
3271
|
print("actionable sibling solo ceiling avoidance was rejected by --check-expected", file=sys.stderr)
|
|
3222
3272
|
print(strong_sibling_solo_ceiling.stderr, file=sys.stderr)
|
|
3223
3273
|
return 1
|
|
3224
3274
|
|
|
3225
|
-
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n")
|
|
3275
|
+
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n", encoding="utf-8")
|
|
3226
3276
|
expected_good = subprocess.run(
|
|
3227
3277
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3228
3278
|
cwd=work,
|
|
3229
3279
|
capture_output=True,
|
|
3230
3280
|
text=True,
|
|
3281
|
+
encoding="utf-8",
|
|
3231
3282
|
)
|
|
3232
3283
|
if expected_good.returncode != 0:
|
|
3233
3284
|
print(expected_good.stderr, file=sys.stderr)
|
|
@@ -3242,6 +3293,7 @@ def run_self_test() -> int:
|
|
|
3242
3293
|
cwd=work,
|
|
3243
3294
|
capture_output=True,
|
|
3244
3295
|
text=True,
|
|
3296
|
+
encoding="utf-8",
|
|
3245
3297
|
)
|
|
3246
3298
|
if expected_bad_sibling_complexity.returncode == 0:
|
|
3247
3299
|
print("unsupported sibling spec complexity was accepted by --check-expected", file=sys.stderr)
|
|
@@ -3250,14 +3302,15 @@ def run_self_test() -> int:
|
|
|
3250
3302
|
print("--check-expected did not report unsupported sibling spec complexity", file=sys.stderr)
|
|
3251
3303
|
print(expected_bad_sibling_complexity.stderr, file=sys.stderr)
|
|
3252
3304
|
return 1
|
|
3253
|
-
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n")
|
|
3305
|
+
spec_md.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- probe must pass visible marker.\n", encoding="utf-8")
|
|
3254
3306
|
|
|
3255
|
-
expected_json.write_text(json.dumps({"verification_commands": []}) + "\n")
|
|
3307
|
+
expected_json.write_text(json.dumps({"verification_commands": []}) + "\n", encoding="utf-8")
|
|
3256
3308
|
expected_empty_runtime = subprocess.run(
|
|
3257
3309
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3258
3310
|
cwd=work,
|
|
3259
3311
|
capture_output=True,
|
|
3260
3312
|
text=True,
|
|
3313
|
+
encoding="utf-8",
|
|
3261
3314
|
)
|
|
3262
3315
|
if expected_empty_runtime.returncode == 0:
|
|
3263
3316
|
print("empty verification_commands should fail for runtime specs", file=sys.stderr)
|
|
@@ -3280,6 +3333,7 @@ def run_self_test() -> int:
|
|
|
3280
3333
|
cwd=work,
|
|
3281
3334
|
capture_output=True,
|
|
3282
3335
|
text=True,
|
|
3336
|
+
encoding="utf-8",
|
|
3283
3337
|
)
|
|
3284
3338
|
if expected_empty_design.returncode != 0:
|
|
3285
3339
|
print("empty verification_commands should be valid for pure-design specs", file=sys.stderr)
|
|
@@ -3298,6 +3352,7 @@ def run_self_test() -> int:
|
|
|
3298
3352
|
cwd=work,
|
|
3299
3353
|
capture_output=True,
|
|
3300
3354
|
text=True,
|
|
3355
|
+
encoding="utf-8",
|
|
3301
3356
|
)
|
|
3302
3357
|
if pure_design_contradiction.returncode == 0:
|
|
3303
3358
|
print("pure_design: true with non-empty verification_commands was accepted", file=sys.stderr)
|
|
@@ -3316,6 +3371,7 @@ def run_self_test() -> int:
|
|
|
3316
3371
|
cwd=work,
|
|
3317
3372
|
capture_output=True,
|
|
3318
3373
|
text=True,
|
|
3374
|
+
encoding="utf-8",
|
|
3319
3375
|
)
|
|
3320
3376
|
if pure_design_not_boolean.returncode == 0:
|
|
3321
3377
|
print("non-boolean pure_design was accepted", file=sys.stderr)
|
|
@@ -3325,12 +3381,13 @@ def run_self_test() -> int:
|
|
|
3325
3381
|
print(pure_design_not_boolean.stderr, file=sys.stderr)
|
|
3326
3382
|
return 1
|
|
3327
3383
|
|
|
3328
|
-
expected_json.write_text(json.dumps({"unknown": True}) + "\n")
|
|
3384
|
+
expected_json.write_text(json.dumps({"unknown": True}) + "\n", encoding="utf-8")
|
|
3329
3385
|
expected_bad = subprocess.run(
|
|
3330
3386
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3331
3387
|
cwd=work,
|
|
3332
3388
|
capture_output=True,
|
|
3333
3389
|
text=True,
|
|
3390
|
+
encoding="utf-8",
|
|
3334
3391
|
)
|
|
3335
3392
|
if expected_bad.returncode == 0:
|
|
3336
3393
|
print("spec.expected.json with unknown key was accepted", file=sys.stderr)
|
|
@@ -3338,23 +3395,25 @@ def run_self_test() -> int:
|
|
|
3338
3395
|
|
|
3339
3396
|
expected_json.write_text(json.dumps({
|
|
3340
3397
|
"verification_commands": [{"cmd": "printf ok", "stdout_contians": ["ok"]}]
|
|
3341
|
-
}) + "\n")
|
|
3398
|
+
}) + "\n", encoding="utf-8")
|
|
3342
3399
|
expected_bad_command = subprocess.run(
|
|
3343
3400
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3344
3401
|
cwd=work,
|
|
3345
3402
|
capture_output=True,
|
|
3346
3403
|
text=True,
|
|
3404
|
+
encoding="utf-8",
|
|
3347
3405
|
)
|
|
3348
3406
|
if expected_bad_command.returncode == 0:
|
|
3349
3407
|
print("spec.expected.json command with unknown key was accepted", file=sys.stderr)
|
|
3350
3408
|
return 1
|
|
3351
3409
|
|
|
3352
|
-
expected_json.write_text("[1]\n")
|
|
3410
|
+
expected_json.write_text("[1]\n", encoding="utf-8")
|
|
3353
3411
|
expected_non_object = subprocess.run(
|
|
3354
3412
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3355
3413
|
cwd=work,
|
|
3356
3414
|
capture_output=True,
|
|
3357
3415
|
text=True,
|
|
3416
|
+
encoding="utf-8",
|
|
3358
3417
|
)
|
|
3359
3418
|
if expected_non_object.returncode == 0:
|
|
3360
3419
|
print("spec.expected.json top-level array was accepted", file=sys.stderr)
|
|
@@ -3367,12 +3426,13 @@ def run_self_test() -> int:
|
|
|
3367
3426
|
print("spec.expected.json top-level array produced a traceback", file=sys.stderr)
|
|
3368
3427
|
return 1
|
|
3369
3428
|
|
|
3370
|
-
expected_json.write_text("{broken\n")
|
|
3429
|
+
expected_json.write_text("{broken\n", encoding="utf-8")
|
|
3371
3430
|
expected_invalid_json = subprocess.run(
|
|
3372
3431
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3373
3432
|
cwd=work,
|
|
3374
3433
|
capture_output=True,
|
|
3375
3434
|
text=True,
|
|
3435
|
+
encoding="utf-8",
|
|
3376
3436
|
)
|
|
3377
3437
|
if expected_invalid_json.returncode == 0:
|
|
3378
3438
|
print("invalid spec.expected.json was accepted", file=sys.stderr)
|
|
@@ -3385,12 +3445,13 @@ def run_self_test() -> int:
|
|
|
3385
3445
|
print("invalid spec.expected.json produced a traceback", file=sys.stderr)
|
|
3386
3446
|
return 1
|
|
3387
3447
|
|
|
3388
|
-
expected_json.write_text('{"verification_commands": NaN}\n')
|
|
3448
|
+
expected_json.write_text('{"verification_commands": NaN}\n', encoding="utf-8")
|
|
3389
3449
|
expected_nan_json = subprocess.run(
|
|
3390
3450
|
[sys.executable, script_path, "--check-expected", str(expected_json)],
|
|
3391
3451
|
cwd=work,
|
|
3392
3452
|
capture_output=True,
|
|
3393
3453
|
text=True,
|
|
3454
|
+
encoding="utf-8",
|
|
3394
3455
|
)
|
|
3395
3456
|
if expected_nan_json.returncode == 0:
|
|
3396
3457
|
print("NaN spec.expected.json was accepted", file=sys.stderr)
|
|
@@ -3426,6 +3487,7 @@ def run_self_test() -> int:
|
|
|
3426
3487
|
)
|
|
3427
3488
|
external_diff_base_sha = subprocess.check_output(
|
|
3428
3489
|
["git", "rev-parse", "HEAD"], cwd=external_diff_root, text=True,
|
|
3490
|
+
encoding="utf-8",
|
|
3429
3491
|
).strip()
|
|
3430
3492
|
(external_diff_root / "outside.txt").write_text("worktree-only\n", encoding="utf-8")
|
|
3431
3493
|
(external_diff_devlyn / "external-diff.patch").write_text(
|
|
@@ -3449,26 +3511,28 @@ def run_self_test() -> int:
|
|
|
3449
3511
|
"base_ref": {"sha": external_diff_base_sha},
|
|
3450
3512
|
}
|
|
3451
3513
|
external_diff_state_path = external_diff_devlyn / "pipeline.state.json"
|
|
3452
|
-
external_diff_state_path.write_text(json.dumps(external_diff_state) + "\n")
|
|
3514
|
+
external_diff_state_path.write_text(json.dumps(external_diff_state) + "\n", encoding="utf-8")
|
|
3453
3515
|
external_diff_free_form = subprocess.run(
|
|
3454
3516
|
[sys.executable, script_path],
|
|
3455
3517
|
cwd=external_diff_root,
|
|
3456
3518
|
capture_output=True,
|
|
3457
3519
|
text=True,
|
|
3520
|
+
encoding="utf-8",
|
|
3458
3521
|
)
|
|
3459
3522
|
external_diff_findings_path = external_diff_devlyn / output_findings_name()
|
|
3460
3523
|
external_diff_free_form_findings = (
|
|
3461
|
-
external_diff_findings_path.read_text()
|
|
3524
|
+
external_diff_findings_path.read_text(encoding="utf-8")
|
|
3462
3525
|
if external_diff_findings_path.is_file()
|
|
3463
3526
|
else ""
|
|
3464
3527
|
)
|
|
3465
3528
|
external_diff_state["mode"] = "verify-only"
|
|
3466
|
-
external_diff_state_path.write_text(json.dumps(external_diff_state) + "\n")
|
|
3529
|
+
external_diff_state_path.write_text(json.dumps(external_diff_state) + "\n", encoding="utf-8")
|
|
3467
3530
|
external_diff_verify_only = subprocess.run(
|
|
3468
3531
|
[sys.executable, script_path],
|
|
3469
3532
|
cwd=external_diff_root,
|
|
3470
3533
|
capture_output=True,
|
|
3471
3534
|
text=True,
|
|
3535
|
+
encoding="utf-8",
|
|
3472
3536
|
)
|
|
3473
3537
|
if external_diff_free_form.returncode != 1:
|
|
3474
3538
|
print("non-verify-only mode accepted .devlyn/external-diff.patch", file=sys.stderr)
|
|
@@ -3514,12 +3578,13 @@ def run_self_test() -> int:
|
|
|
3514
3578
|
"spec_path": str(integrity_spec),
|
|
3515
3579
|
"spec_sha256": "0" * 64,
|
|
3516
3580
|
}
|
|
3517
|
-
}))
|
|
3581
|
+
}), encoding="utf-8")
|
|
3518
3582
|
spec_bad_hash_run = subprocess.run(
|
|
3519
3583
|
[sys.executable, script_path],
|
|
3520
3584
|
cwd=spec_integrity,
|
|
3521
3585
|
capture_output=True,
|
|
3522
3586
|
text=True,
|
|
3587
|
+
encoding="utf-8",
|
|
3523
3588
|
)
|
|
3524
3589
|
if spec_bad_hash_run.returncode == 0:
|
|
3525
3590
|
print("spec source with mismatched source.spec_sha256 was accepted", file=sys.stderr)
|
|
@@ -3536,17 +3601,18 @@ def run_self_test() -> int:
|
|
|
3536
3601
|
"spec_path": str(integrity_spec),
|
|
3537
3602
|
"spec_sha256": spec_hash,
|
|
3538
3603
|
}
|
|
3539
|
-
}))
|
|
3604
|
+
}), encoding="utf-8")
|
|
3540
3605
|
spec_hash_run = subprocess.run(
|
|
3541
3606
|
[sys.executable, script_path],
|
|
3542
3607
|
cwd=spec_integrity,
|
|
3543
3608
|
capture_output=True,
|
|
3544
3609
|
text=True,
|
|
3610
|
+
encoding="utf-8",
|
|
3545
3611
|
)
|
|
3546
3612
|
if spec_hash_run.returncode != 0:
|
|
3547
3613
|
print(spec_hash_run.stderr, file=sys.stderr)
|
|
3548
3614
|
return 1
|
|
3549
|
-
staged_spec_hash = loads_strict_json((spec_integrity_devlyn / "spec-verify.json").read_text())
|
|
3615
|
+
staged_spec_hash = loads_strict_json((spec_integrity_devlyn / "spec-verify.json").read_text(encoding="utf-8"))
|
|
3550
3616
|
if staged_spec_hash.get("verification_commands", [{}])[0].get("cmd") != "printf spec-hash-ok":
|
|
3551
3617
|
print("spec source with matching source.spec_sha256 was not staged", file=sys.stderr)
|
|
3552
3618
|
return 1
|
|
@@ -3564,12 +3630,13 @@ def run_self_test() -> int:
|
|
|
3564
3630
|
)
|
|
3565
3631
|
(generated_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3566
3632
|
"source": {"type": "generated", "criteria_path": str(generated_criteria)}
|
|
3567
|
-
}))
|
|
3633
|
+
}), encoding="utf-8")
|
|
3568
3634
|
generated_missing_hash_run = subprocess.run(
|
|
3569
3635
|
[sys.executable, script_path],
|
|
3570
3636
|
cwd=generated_user,
|
|
3571
3637
|
capture_output=True,
|
|
3572
3638
|
text=True,
|
|
3639
|
+
encoding="utf-8",
|
|
3573
3640
|
)
|
|
3574
3641
|
if generated_missing_hash_run.returncode == 0:
|
|
3575
3642
|
print("generated criteria without source.criteria_sha256 was accepted", file=sys.stderr)
|
|
@@ -3585,12 +3652,13 @@ def run_self_test() -> int:
|
|
|
3585
3652
|
"criteria_path": str(generated_criteria),
|
|
3586
3653
|
"criteria_sha256": "0" * 64,
|
|
3587
3654
|
}
|
|
3588
|
-
}))
|
|
3655
|
+
}), encoding="utf-8")
|
|
3589
3656
|
generated_bad_hash_run = subprocess.run(
|
|
3590
3657
|
[sys.executable, script_path],
|
|
3591
3658
|
cwd=generated_user,
|
|
3592
3659
|
capture_output=True,
|
|
3593
3660
|
text=True,
|
|
3661
|
+
encoding="utf-8",
|
|
3594
3662
|
)
|
|
3595
3663
|
if generated_bad_hash_run.returncode == 0:
|
|
3596
3664
|
print("generated criteria with mismatched source.criteria_sha256 was accepted", file=sys.stderr)
|
|
@@ -3607,17 +3675,18 @@ def run_self_test() -> int:
|
|
|
3607
3675
|
"criteria_path": str(generated_criteria),
|
|
3608
3676
|
"criteria_sha256": generated_hash,
|
|
3609
3677
|
}
|
|
3610
|
-
}))
|
|
3678
|
+
}), encoding="utf-8")
|
|
3611
3679
|
generated_run = subprocess.run(
|
|
3612
3680
|
[sys.executable, script_path],
|
|
3613
3681
|
cwd=generated_user,
|
|
3614
3682
|
capture_output=True,
|
|
3615
3683
|
text=True,
|
|
3684
|
+
encoding="utf-8",
|
|
3616
3685
|
)
|
|
3617
3686
|
if generated_run.returncode != 0:
|
|
3618
3687
|
print(generated_run.stderr, file=sys.stderr)
|
|
3619
3688
|
return 1
|
|
3620
|
-
staged_generated = loads_strict_json((generated_devlyn / "spec-verify.json").read_text())
|
|
3689
|
+
staged_generated = loads_strict_json((generated_devlyn / "spec-verify.json").read_text(encoding="utf-8"))
|
|
3621
3690
|
if staged_generated.get("verification_commands", [{}])[0].get("cmd") != "printf generated-ok":
|
|
3622
3691
|
print("generated criteria carrier was not staged into .devlyn/spec-verify.json", file=sys.stderr)
|
|
3623
3692
|
return 1
|
|
@@ -3633,12 +3702,13 @@ def run_self_test() -> int:
|
|
|
3633
3702
|
"criteria_path": str(generated_criteria),
|
|
3634
3703
|
"criteria_sha256": malformed_generated_hash,
|
|
3635
3704
|
}
|
|
3636
|
-
}))
|
|
3705
|
+
}), encoding="utf-8")
|
|
3637
3706
|
malformed_generated_run = subprocess.run(
|
|
3638
3707
|
[sys.executable, script_path],
|
|
3639
3708
|
cwd=generated_user,
|
|
3640
3709
|
capture_output=True,
|
|
3641
3710
|
text=True,
|
|
3711
|
+
encoding="utf-8",
|
|
3642
3712
|
)
|
|
3643
3713
|
if malformed_generated_run.returncode == 0:
|
|
3644
3714
|
print("generated criteria without a JSON carrier was accepted", file=sys.stderr)
|
|
@@ -3654,26 +3724,28 @@ def run_self_test() -> int:
|
|
|
3654
3724
|
real_devlyn.mkdir()
|
|
3655
3725
|
real_spec = real_user / "spec.md"
|
|
3656
3726
|
real_spec.write_text(
|
|
3657
|
-
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- sibling command must print sibling-ok.\n"
|
|
3727
|
+
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- sibling command must print sibling-ok.\n",
|
|
3728
|
+
encoding="utf-8",
|
|
3658
3729
|
)
|
|
3659
3730
|
(real_user / "spec.expected.json").write_text(json.dumps({
|
|
3660
3731
|
"verification_commands": [
|
|
3661
3732
|
{"cmd": "printf sibling-ok", "stdout_contains": ["sibling-ok"]}
|
|
3662
3733
|
]
|
|
3663
|
-
}) + "\n")
|
|
3734
|
+
}) + "\n", encoding="utf-8")
|
|
3664
3735
|
(real_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3665
3736
|
"source": {"type": "spec", "spec_path": str(real_spec)}
|
|
3666
|
-
}))
|
|
3737
|
+
}), encoding="utf-8")
|
|
3667
3738
|
sibling_run = subprocess.run(
|
|
3668
3739
|
[sys.executable, script_path],
|
|
3669
3740
|
cwd=real_user,
|
|
3670
3741
|
capture_output=True,
|
|
3671
3742
|
text=True,
|
|
3743
|
+
encoding="utf-8",
|
|
3672
3744
|
)
|
|
3673
3745
|
if sibling_run.returncode != 0:
|
|
3674
3746
|
print(sibling_run.stderr, file=sys.stderr)
|
|
3675
3747
|
return 1
|
|
3676
|
-
staged = loads_strict_json((real_devlyn / "spec-verify.json").read_text())
|
|
3748
|
+
staged = loads_strict_json((real_devlyn / "spec-verify.json").read_text(encoding="utf-8"))
|
|
3677
3749
|
if staged.get("verification_commands", [{}])[0].get("cmd") != "printf sibling-ok":
|
|
3678
3750
|
print("sibling spec.expected.json was not staged into .devlyn/spec-verify.json", file=sys.stderr)
|
|
3679
3751
|
return 1
|
|
@@ -3686,48 +3758,51 @@ def run_self_test() -> int:
|
|
|
3686
3758
|
malformed_spec.write_text(
|
|
3687
3759
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n```json\n"
|
|
3688
3760
|
"{\"verification_commands\":[{\"cmd\":\"printf inline-ok\"}]}\n"
|
|
3689
|
-
"```\n"
|
|
3761
|
+
"```\n",
|
|
3762
|
+
encoding="utf-8",
|
|
3690
3763
|
)
|
|
3691
|
-
(malformed / "spec.expected.json").write_text(json.dumps({"unknown": True}) + "\n")
|
|
3764
|
+
(malformed / "spec.expected.json").write_text(json.dumps({"unknown": True}) + "\n", encoding="utf-8")
|
|
3692
3765
|
(malformed_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3693
3766
|
"source": {"type": "spec", "spec_path": str(malformed_spec)}
|
|
3694
|
-
}))
|
|
3767
|
+
}), encoding="utf-8")
|
|
3695
3768
|
malformed_run = subprocess.run(
|
|
3696
3769
|
[sys.executable, script_path],
|
|
3697
3770
|
cwd=malformed,
|
|
3698
3771
|
capture_output=True,
|
|
3699
3772
|
text=True,
|
|
3773
|
+
encoding="utf-8",
|
|
3700
3774
|
)
|
|
3701
3775
|
if malformed_run.returncode == 0:
|
|
3702
3776
|
print("malformed sibling spec.expected.json fell back to inline carrier", file=sys.stderr)
|
|
3703
3777
|
return 1
|
|
3704
3778
|
|
|
3705
3779
|
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")
|
|
3780
|
+
bench_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- benchmark pre-staged wins.\n", encoding="utf-8")
|
|
3707
3781
|
(work / "spec.expected.json").write_text(json.dumps({
|
|
3708
3782
|
"verification_commands": [
|
|
3709
3783
|
{"cmd": "printf wrong", "stdout_contains": ["wrong"]}
|
|
3710
3784
|
]
|
|
3711
|
-
}) + "\n")
|
|
3785
|
+
}) + "\n", encoding="utf-8")
|
|
3712
3786
|
(devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3713
3787
|
"source": {"type": "spec", "spec_path": str(bench_spec)}
|
|
3714
|
-
}))
|
|
3788
|
+
}), encoding="utf-8")
|
|
3715
3789
|
(devlyn / "spec-verify.json").write_text(json.dumps({
|
|
3716
3790
|
"verification_commands": [
|
|
3717
3791
|
{"cmd": "printf bench-staged", "stdout_contains": ["bench-staged"]}
|
|
3718
3792
|
]
|
|
3719
|
-
}) + "\n")
|
|
3793
|
+
}) + "\n", encoding="utf-8")
|
|
3720
3794
|
bench_pre_staged = subprocess.run(
|
|
3721
3795
|
[sys.executable, script_path],
|
|
3722
3796
|
cwd=work,
|
|
3723
3797
|
env=env,
|
|
3724
3798
|
capture_output=True,
|
|
3725
3799
|
text=True,
|
|
3800
|
+
encoding="utf-8",
|
|
3726
3801
|
)
|
|
3727
3802
|
if bench_pre_staged.returncode != 0:
|
|
3728
3803
|
print(bench_pre_staged.stderr, file=sys.stderr)
|
|
3729
3804
|
return 1
|
|
3730
|
-
staged_bench = loads_strict_json((devlyn / "spec-verify.json").read_text())
|
|
3805
|
+
staged_bench = loads_strict_json((devlyn / "spec-verify.json").read_text(encoding="utf-8"))
|
|
3731
3806
|
if staged_bench.get("verification_commands", [{}])[0].get("cmd") != "printf bench-staged":
|
|
3732
3807
|
print("benchmark pre-staged contract was overwritten", file=sys.stderr)
|
|
3733
3808
|
return 1
|
|
@@ -3737,15 +3812,15 @@ def run_self_test() -> int:
|
|
|
3737
3812
|
verify_devlyn = verify_output / ".devlyn"
|
|
3738
3813
|
verify_devlyn.mkdir()
|
|
3739
3814
|
verify_spec = verify_output / "spec.md"
|
|
3740
|
-
verify_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- verify mechanical output.\n")
|
|
3815
|
+
verify_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- verify mechanical output.\n", encoding="utf-8")
|
|
3741
3816
|
(verify_output / "spec.expected.json").write_text(json.dumps({
|
|
3742
3817
|
"verification_commands": [
|
|
3743
3818
|
{"cmd": "printf wrong", "stdout_contains": ["expected"]}
|
|
3744
3819
|
]
|
|
3745
|
-
}) + "\n")
|
|
3820
|
+
}) + "\n", encoding="utf-8")
|
|
3746
3821
|
(verify_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3747
3822
|
"source": {"type": "spec", "spec_path": str(verify_spec)}
|
|
3748
|
-
}))
|
|
3823
|
+
}), encoding="utf-8")
|
|
3749
3824
|
verify_env = os.environ.copy()
|
|
3750
3825
|
verify_env.update({
|
|
3751
3826
|
"SPEC_VERIFY_PHASE": "verify_mechanical",
|
|
@@ -3758,11 +3833,12 @@ def run_self_test() -> int:
|
|
|
3758
3833
|
env=verify_env,
|
|
3759
3834
|
capture_output=True,
|
|
3760
3835
|
text=True,
|
|
3836
|
+
encoding="utf-8",
|
|
3761
3837
|
)
|
|
3762
3838
|
if verify_output_run.returncode == 0:
|
|
3763
3839
|
print("VERIFY output-mode failing command was accepted", file=sys.stderr)
|
|
3764
3840
|
return 1
|
|
3765
|
-
verify_findings = (verify_devlyn / "verify-mechanical.findings.jsonl").read_text()
|
|
3841
|
+
verify_findings = (verify_devlyn / "verify-mechanical.findings.jsonl").read_text(encoding="utf-8")
|
|
3766
3842
|
if '"phase": "verify_mechanical"' not in verify_findings or "VERIFY-MECH-" not in verify_findings:
|
|
3767
3843
|
print("VERIFY output-mode did not route findings to verify-mechanical", file=sys.stderr)
|
|
3768
3844
|
return 1
|
|
@@ -3772,7 +3848,8 @@ def run_self_test() -> int:
|
|
|
3772
3848
|
contract_devlyn = contract_root / ".devlyn"
|
|
3773
3849
|
contract_devlyn.mkdir()
|
|
3774
3850
|
(contract_root / "package.json").write_text(
|
|
3775
|
-
'{\n "dependencies": {},\n "devDependencies": {}\n}\n'
|
|
3851
|
+
'{\n "dependencies": {},\n "devDependencies": {}\n}\n',
|
|
3852
|
+
encoding="utf-8",
|
|
3776
3853
|
)
|
|
3777
3854
|
subprocess.run(["git", "init", "-q"], cwd=contract_root, check=True)
|
|
3778
3855
|
subprocess.run(["git", "add", "-A"], cwd=contract_root, check=True)
|
|
@@ -3785,14 +3862,16 @@ def run_self_test() -> int:
|
|
|
3785
3862
|
["git", "rev-parse", "HEAD"],
|
|
3786
3863
|
cwd=contract_root,
|
|
3787
3864
|
text=True,
|
|
3865
|
+
encoding="utf-8",
|
|
3788
3866
|
).strip()
|
|
3789
3867
|
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")
|
|
3868
|
+
contract_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- expected contract checks.\n", encoding="utf-8")
|
|
3869
|
+
(contract_root / "app.js").write_text("try { work(); } catch { return null; }\n", encoding="utf-8")
|
|
3870
|
+
(contract_root / "forbidden.txt").write_text("forbidden\n", encoding="utf-8")
|
|
3793
3871
|
(contract_root / "package.json").write_text(
|
|
3794
3872
|
'{\n "dependencies": {\n "left-pad": "1.3.0"\n },\n'
|
|
3795
|
-
' "devDependencies": {}\n}\n'
|
|
3873
|
+
' "devDependencies": {}\n}\n',
|
|
3874
|
+
encoding="utf-8",
|
|
3796
3875
|
)
|
|
3797
3876
|
(contract_root / "spec.expected.json").write_text(json.dumps({
|
|
3798
3877
|
"verification_commands": [{"cmd": "printf ok", "stdout_contains": ["ok"]}],
|
|
@@ -3804,22 +3883,23 @@ def run_self_test() -> int:
|
|
|
3804
3883
|
"required_files": ["required.txt"],
|
|
3805
3884
|
"forbidden_files": ["forbidden.txt"],
|
|
3806
3885
|
"max_deps_added": 0,
|
|
3807
|
-
}) + "\n")
|
|
3886
|
+
}) + "\n", encoding="utf-8")
|
|
3808
3887
|
subprocess.run(["git", "add", "-A"], cwd=contract_root, check=True)
|
|
3809
3888
|
(contract_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3810
3889
|
"source": {"type": "spec", "spec_path": str(contract_spec)},
|
|
3811
3890
|
"base_ref": {"sha": base_sha},
|
|
3812
|
-
}))
|
|
3891
|
+
}), encoding="utf-8")
|
|
3813
3892
|
contract_run = subprocess.run(
|
|
3814
3893
|
[sys.executable, script_path],
|
|
3815
3894
|
cwd=contract_root,
|
|
3816
3895
|
capture_output=True,
|
|
3817
3896
|
text=True,
|
|
3897
|
+
encoding="utf-8",
|
|
3818
3898
|
)
|
|
3819
3899
|
if contract_run.returncode == 0:
|
|
3820
3900
|
print("expected contract violations were accepted", file=sys.stderr)
|
|
3821
3901
|
return 1
|
|
3822
|
-
findings_text = (contract_devlyn / output_findings_name()).read_text()
|
|
3902
|
+
findings_text = (contract_devlyn / output_findings_name()).read_text(encoding="utf-8")
|
|
3823
3903
|
for rule_id in (
|
|
3824
3904
|
"correctness.forbidden-pattern",
|
|
3825
3905
|
"correctness.required-file-missing",
|
|
@@ -3837,13 +3917,14 @@ def run_self_test() -> int:
|
|
|
3837
3917
|
"exit_code": 0,
|
|
3838
3918
|
"tags": ["boundary_overlap"],
|
|
3839
3919
|
"tag_evidence": {"boundary_overlap": ["one_minute_overlap"]},
|
|
3840
|
-
}) + "\n")
|
|
3920
|
+
}) + "\n", encoding="utf-8")
|
|
3841
3921
|
weak = subprocess.run(
|
|
3842
3922
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3843
3923
|
cwd=work,
|
|
3844
3924
|
env=env,
|
|
3845
3925
|
capture_output=True,
|
|
3846
3926
|
text=True,
|
|
3927
|
+
encoding="utf-8",
|
|
3847
3928
|
)
|
|
3848
3929
|
if weak.returncode == 0:
|
|
3849
3930
|
print("incomplete boundary_overlap evidence was accepted", file=sys.stderr)
|
|
@@ -3857,11 +3938,12 @@ def run_self_test() -> int:
|
|
|
3857
3938
|
error_spec.write_text(
|
|
3858
3939
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
3859
3940
|
"- 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"
|
|
3941
|
+
"- Malformed input must exit 2 and print stderr JSON with keys `code` and `detail`; values are implementation-defined.\n",
|
|
3942
|
+
encoding="utf-8",
|
|
3861
3943
|
)
|
|
3862
3944
|
(error_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
3863
3945
|
"source": {"type": "spec", "spec_path": str(error_spec)}
|
|
3864
|
-
}))
|
|
3946
|
+
}), encoding="utf-8")
|
|
3865
3947
|
(error_devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
3866
3948
|
"id": "P6",
|
|
3867
3949
|
"derived_from": "Invalid input must print a JSON error object to stderr and exit 2.",
|
|
@@ -3872,12 +3954,13 @@ def run_self_test() -> int:
|
|
|
3872
3954
|
"stdout_stderr_contract": ["asserts_named_stream_output"],
|
|
3873
3955
|
"error_contract": ["asserts_error_payload_or_stderr"],
|
|
3874
3956
|
},
|
|
3875
|
-
}) + "\n")
|
|
3957
|
+
}) + "\n", encoding="utf-8")
|
|
3876
3958
|
weak_error_contract = subprocess.run(
|
|
3877
3959
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3878
3960
|
cwd=error_root,
|
|
3879
3961
|
capture_output=True,
|
|
3880
3962
|
text=True,
|
|
3963
|
+
encoding="utf-8",
|
|
3881
3964
|
)
|
|
3882
3965
|
if weak_error_contract.returncode == 0:
|
|
3883
3966
|
print("error_contract without exit-code evidence was accepted", file=sys.stderr)
|
|
@@ -3896,12 +3979,13 @@ def run_self_test() -> int:
|
|
|
3896
3979
|
"asserts_nonzero_or_exit_2",
|
|
3897
3980
|
],
|
|
3898
3981
|
},
|
|
3899
|
-
}) + "\n")
|
|
3982
|
+
}) + "\n", encoding="utf-8")
|
|
3900
3983
|
weak_stdio_contract = subprocess.run(
|
|
3901
3984
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3902
3985
|
cwd=error_root,
|
|
3903
3986
|
capture_output=True,
|
|
3904
3987
|
text=True,
|
|
3988
|
+
encoding="utf-8",
|
|
3905
3989
|
)
|
|
3906
3990
|
if weak_stdio_contract.returncode == 0:
|
|
3907
3991
|
print("stdout_stderr_contract without stream evidence was accepted", file=sys.stderr)
|
|
@@ -3926,12 +4010,13 @@ def run_self_test() -> int:
|
|
|
3926
4010
|
"visible_text_names_exact_json_error_object",
|
|
3927
4011
|
],
|
|
3928
4012
|
},
|
|
3929
|
-
}) + "\n")
|
|
4013
|
+
}) + "\n", encoding="utf-8")
|
|
3930
4014
|
missing_exact_error_object = subprocess.run(
|
|
3931
4015
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3932
4016
|
cwd=error_root,
|
|
3933
4017
|
capture_output=True,
|
|
3934
4018
|
text=True,
|
|
4019
|
+
encoding="utf-8",
|
|
3935
4020
|
)
|
|
3936
4021
|
if missing_exact_error_object.returncode == 0:
|
|
3937
4022
|
print(
|
|
@@ -3961,12 +4046,13 @@ def run_self_test() -> int:
|
|
|
3961
4046
|
"asserts_exact_error_object",
|
|
3962
4047
|
],
|
|
3963
4048
|
},
|
|
3964
|
-
}) + "\n")
|
|
4049
|
+
}) + "\n", encoding="utf-8")
|
|
3965
4050
|
strong_json_error_shape_contract = subprocess.run(
|
|
3966
4051
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3967
4052
|
cwd=error_root,
|
|
3968
4053
|
capture_output=True,
|
|
3969
4054
|
text=True,
|
|
4055
|
+
encoding="utf-8",
|
|
3970
4056
|
)
|
|
3971
4057
|
if strong_json_error_shape_contract.returncode != 0:
|
|
3972
4058
|
print("JSON error object shape_contract with exact object evidence was rejected", file=sys.stderr)
|
|
@@ -3993,12 +4079,13 @@ def run_self_test() -> int:
|
|
|
3993
4079
|
"asserts_no_unexpected_output_keys",
|
|
3994
4080
|
],
|
|
3995
4081
|
},
|
|
3996
|
-
}) + "\n")
|
|
4082
|
+
}) + "\n", encoding="utf-8")
|
|
3997
4083
|
error_exit_shape_contract = subprocess.run(
|
|
3998
4084
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
3999
4085
|
cwd=error_root,
|
|
4000
4086
|
capture_output=True,
|
|
4001
4087
|
text=True,
|
|
4088
|
+
encoding="utf-8",
|
|
4002
4089
|
)
|
|
4003
4090
|
if error_exit_shape_contract.returncode != 0:
|
|
4004
4091
|
print("error-exit shape_contract without visible exact error object was rejected", file=sys.stderr)
|
|
@@ -4012,11 +4099,12 @@ def run_self_test() -> int:
|
|
|
4012
4099
|
http_error_spec = http_error_root / "spec.md"
|
|
4013
4100
|
http_error_spec.write_text(
|
|
4014
4101
|
"# 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"
|
|
4102
|
+
"- An invalid query returns HTTP 400 with JSON error body `{ \"error\": \"invalid_query\", \"field\": \"per_page\" }`.\n",
|
|
4103
|
+
encoding="utf-8",
|
|
4016
4104
|
)
|
|
4017
4105
|
(http_error_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
4018
4106
|
"source": {"type": "spec", "spec_path": str(http_error_spec)}
|
|
4019
|
-
}))
|
|
4107
|
+
}), encoding="utf-8")
|
|
4020
4108
|
(http_error_devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
4021
4109
|
"id": "P8b",
|
|
4022
4110
|
"derived_from": (
|
|
@@ -4029,12 +4117,13 @@ def run_self_test() -> int:
|
|
|
4029
4117
|
"tag_evidence": {
|
|
4030
4118
|
"http_error_contract": ["asserts_http_error_status"],
|
|
4031
4119
|
},
|
|
4032
|
-
}) + "\n")
|
|
4120
|
+
}) + "\n", encoding="utf-8")
|
|
4033
4121
|
incomplete_http_error_contract = subprocess.run(
|
|
4034
4122
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4035
4123
|
cwd=http_error_root,
|
|
4036
4124
|
capture_output=True,
|
|
4037
4125
|
text=True,
|
|
4126
|
+
encoding="utf-8",
|
|
4038
4127
|
)
|
|
4039
4128
|
if incomplete_http_error_contract.returncode == 0:
|
|
4040
4129
|
print("http_error_contract without payload evidence was accepted", file=sys.stderr)
|
|
@@ -4061,12 +4150,13 @@ def run_self_test() -> int:
|
|
|
4061
4150
|
"visible_text_names_exact_json_error_object",
|
|
4062
4151
|
],
|
|
4063
4152
|
},
|
|
4064
|
-
}) + "\n")
|
|
4153
|
+
}) + "\n", encoding="utf-8")
|
|
4065
4154
|
weak_exact_error_shape_contract = subprocess.run(
|
|
4066
4155
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4067
4156
|
cwd=http_error_root,
|
|
4068
4157
|
capture_output=True,
|
|
4069
4158
|
text=True,
|
|
4159
|
+
encoding="utf-8",
|
|
4070
4160
|
)
|
|
4071
4161
|
if weak_exact_error_shape_contract.returncode == 0:
|
|
4072
4162
|
print("exact error body shape_contract without exact object evidence was accepted", file=sys.stderr)
|
|
@@ -4094,12 +4184,13 @@ def run_self_test() -> int:
|
|
|
4094
4184
|
"asserts_exact_error_object",
|
|
4095
4185
|
],
|
|
4096
4186
|
},
|
|
4097
|
-
}) + "\n")
|
|
4187
|
+
}) + "\n", encoding="utf-8")
|
|
4098
4188
|
strong_exact_error_shape_contract = subprocess.run(
|
|
4099
4189
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4100
4190
|
cwd=http_error_root,
|
|
4101
4191
|
capture_output=True,
|
|
4102
4192
|
text=True,
|
|
4193
|
+
encoding="utf-8",
|
|
4103
4194
|
)
|
|
4104
4195
|
if strong_exact_error_shape_contract.returncode != 0:
|
|
4105
4196
|
print("exact error body shape_contract with exact object evidence was rejected", file=sys.stderr)
|
|
@@ -4114,11 +4205,12 @@ def run_self_test() -> int:
|
|
|
4114
4205
|
shape_spec.write_text(
|
|
4115
4206
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
4116
4207
|
"- On success, output is one JSON object with keys `applied`, `rejected`, and `accounts`; "
|
|
4117
|
-
"`rejected` rows have keys `id` and `reason`.\n"
|
|
4208
|
+
"`rejected` rows have keys `id` and `reason`.\n",
|
|
4209
|
+
encoding="utf-8",
|
|
4118
4210
|
)
|
|
4119
4211
|
(shape_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
4120
4212
|
"source": {"type": "spec", "spec_path": str(shape_spec)}
|
|
4121
|
-
}))
|
|
4213
|
+
}), encoding="utf-8")
|
|
4122
4214
|
(shape_devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
4123
4215
|
"id": "P8e",
|
|
4124
4216
|
"derived_from": (
|
|
@@ -4129,12 +4221,13 @@ def run_self_test() -> int:
|
|
|
4129
4221
|
"exit_code": 0,
|
|
4130
4222
|
"tags": ["shape_contract"],
|
|
4131
4223
|
"tag_evidence": {},
|
|
4132
|
-
}) + "\n")
|
|
4224
|
+
}) + "\n", encoding="utf-8")
|
|
4133
4225
|
weak_shape_contract = subprocess.run(
|
|
4134
4226
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4135
4227
|
cwd=shape_root,
|
|
4136
4228
|
capture_output=True,
|
|
4137
4229
|
text=True,
|
|
4230
|
+
encoding="utf-8",
|
|
4138
4231
|
)
|
|
4139
4232
|
if weak_shape_contract.returncode == 0:
|
|
4140
4233
|
print("shape_contract without any evidence was accepted", file=sys.stderr)
|
|
@@ -4156,12 +4249,13 @@ def run_self_test() -> int:
|
|
|
4156
4249
|
"asserts_no_unexpected_output_keys",
|
|
4157
4250
|
],
|
|
4158
4251
|
},
|
|
4159
|
-
}) + "\n")
|
|
4252
|
+
}) + "\n", encoding="utf-8")
|
|
4160
4253
|
strong_shape_contract = subprocess.run(
|
|
4161
4254
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4162
4255
|
cwd=shape_root,
|
|
4163
4256
|
capture_output=True,
|
|
4164
4257
|
text=True,
|
|
4258
|
+
encoding="utf-8",
|
|
4165
4259
|
)
|
|
4166
4260
|
if strong_shape_contract.returncode != 0:
|
|
4167
4261
|
print("shape_contract with exact key evidence was rejected", file=sys.stderr)
|
|
@@ -4181,7 +4275,8 @@ def run_self_test() -> int:
|
|
|
4181
4275
|
required_spec.write_text(
|
|
4182
4276
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
4183
4277
|
"- A failed all-or-nothing operation must roll back tentative state "
|
|
4184
|
-
"so later orders can use the released stock.\n"
|
|
4278
|
+
"so later orders can use the released stock.\n",
|
|
4279
|
+
encoding="utf-8",
|
|
4185
4280
|
)
|
|
4186
4281
|
(required_root / "spec.expected.json").write_text(json.dumps({
|
|
4187
4282
|
"verification_commands": [{"cmd": "printf ok", "stdout_contains": ["ok"]}],
|
|
@@ -4194,10 +4289,10 @@ def run_self_test() -> int:
|
|
|
4194
4289
|
),
|
|
4195
4290
|
},
|
|
4196
4291
|
],
|
|
4197
|
-
}) + "\n")
|
|
4292
|
+
}) + "\n", encoding="utf-8")
|
|
4198
4293
|
(required_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
4199
4294
|
"source": {"type": "spec", "spec_path": str(required_spec)}
|
|
4200
|
-
}))
|
|
4295
|
+
}), encoding="utf-8")
|
|
4201
4296
|
(required_devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
4202
4297
|
"id": "P14",
|
|
4203
4298
|
"derived_from": (
|
|
@@ -4213,12 +4308,13 @@ def run_self_test() -> int:
|
|
|
4213
4308
|
"later_entity_fails_or_reroutes",
|
|
4214
4309
|
],
|
|
4215
4310
|
},
|
|
4216
|
-
}) + "\n")
|
|
4311
|
+
}) + "\n", encoding="utf-8")
|
|
4217
4312
|
missing_declared_requirement = subprocess.run(
|
|
4218
4313
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4219
4314
|
cwd=required_root,
|
|
4220
4315
|
capture_output=True,
|
|
4221
4316
|
text=True,
|
|
4317
|
+
encoding="utf-8",
|
|
4222
4318
|
)
|
|
4223
4319
|
if missing_declared_requirement.returncode == 0:
|
|
4224
4320
|
print(
|
|
@@ -4250,12 +4346,13 @@ def run_self_test() -> int:
|
|
|
4250
4346
|
"later_entity_uses_released_state",
|
|
4251
4347
|
],
|
|
4252
4348
|
},
|
|
4253
|
-
}) + "\n")
|
|
4349
|
+
}) + "\n", encoding="utf-8")
|
|
4254
4350
|
covered_declared_requirement = subprocess.run(
|
|
4255
4351
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4256
4352
|
cwd=required_root,
|
|
4257
4353
|
capture_output=True,
|
|
4258
4354
|
text=True,
|
|
4355
|
+
encoding="utf-8",
|
|
4259
4356
|
)
|
|
4260
4357
|
if covered_declared_requirement.returncode != 0:
|
|
4261
4358
|
print(
|
|
@@ -4271,12 +4368,13 @@ def run_self_test() -> int:
|
|
|
4271
4368
|
"required_risk_probe_requirements": [
|
|
4272
4369
|
{"tag": "not-a-real-tag", "derived_from": "irrelevant"},
|
|
4273
4370
|
],
|
|
4274
|
-
}) + "\n")
|
|
4371
|
+
}) + "\n", encoding="utf-8")
|
|
4275
4372
|
malformed_requirement_tag = subprocess.run(
|
|
4276
4373
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4277
4374
|
cwd=required_root,
|
|
4278
4375
|
capture_output=True,
|
|
4279
4376
|
text=True,
|
|
4377
|
+
encoding="utf-8",
|
|
4280
4378
|
)
|
|
4281
4379
|
if malformed_requirement_tag.returncode == 0:
|
|
4282
4380
|
print("required_risk_probe_requirements with an unknown tag was accepted", file=sys.stderr)
|
|
@@ -4287,12 +4385,13 @@ def run_self_test() -> int:
|
|
|
4287
4385
|
"required_risk_probe_requirements": [
|
|
4288
4386
|
{"tag": "rollback_state", "derived_from": "text not present in the spec"},
|
|
4289
4387
|
],
|
|
4290
|
-
}) + "\n")
|
|
4388
|
+
}) + "\n", encoding="utf-8")
|
|
4291
4389
|
malformed_requirement_derived_from = subprocess.run(
|
|
4292
4390
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4293
4391
|
cwd=required_root,
|
|
4294
4392
|
capture_output=True,
|
|
4295
4393
|
text=True,
|
|
4394
|
+
encoding="utf-8",
|
|
4296
4395
|
)
|
|
4297
4396
|
if malformed_requirement_derived_from.returncode == 0:
|
|
4298
4397
|
print(
|
|
@@ -4310,11 +4409,12 @@ def run_self_test() -> int:
|
|
|
4310
4409
|
atomic_batch_spec.write_text(
|
|
4311
4410
|
"# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n"
|
|
4312
4411
|
"- 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"
|
|
4412
|
+
"- A POST with all-valid items returns `201`, and the items appear in GET output in order with distinct ids.\n",
|
|
4413
|
+
encoding="utf-8",
|
|
4314
4414
|
)
|
|
4315
4415
|
(atomic_batch_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
4316
4416
|
"source": {"type": "spec", "spec_path": str(atomic_batch_spec)}
|
|
4317
|
-
}))
|
|
4417
|
+
}), encoding="utf-8")
|
|
4318
4418
|
(atomic_batch_devlyn / "risk-probes.jsonl").write_text(json.dumps({
|
|
4319
4419
|
"id": "P13b",
|
|
4320
4420
|
"derived_from": (
|
|
@@ -4330,12 +4430,13 @@ def run_self_test() -> int:
|
|
|
4330
4430
|
"asserts_store_unchanged_after_failure",
|
|
4331
4431
|
],
|
|
4332
4432
|
},
|
|
4333
|
-
}) + "\n")
|
|
4433
|
+
}) + "\n", encoding="utf-8")
|
|
4334
4434
|
incomplete_atomic_batch_probe = subprocess.run(
|
|
4335
4435
|
[sys.executable, script_path, "--validate-risk-probes"],
|
|
4336
4436
|
cwd=atomic_batch_root,
|
|
4337
4437
|
capture_output=True,
|
|
4338
4438
|
text=True,
|
|
4439
|
+
encoding="utf-8",
|
|
4339
4440
|
)
|
|
4340
4441
|
if incomplete_atomic_batch_probe.returncode == 0:
|
|
4341
4442
|
print("atomic_batch_state without success-order evidence was accepted", file=sys.stderr)
|
|
@@ -4350,15 +4451,15 @@ def run_self_test() -> int:
|
|
|
4350
4451
|
(scope_root / "lib").mkdir()
|
|
4351
4452
|
(scope_root / "lib2").mkdir()
|
|
4352
4453
|
(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")
|
|
4454
|
+
(scope_root / "bin" / "cli.js").write_text("module.exports = {};\n", encoding="utf-8")
|
|
4455
|
+
(scope_root / "lib" / "keep.js").write_text("module.exports = {};\n", encoding="utf-8")
|
|
4456
|
+
(scope_root / "lib2" / "keep.js").write_text("module.exports = {};\n", encoding="utf-8")
|
|
4457
|
+
(scope_root / "data" / "usage-stats.json").write_text("{}\n", encoding="utf-8")
|
|
4357
4458
|
scope_spec = scope_root / "spec.md"
|
|
4358
|
-
scope_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- scope gate checks.\n")
|
|
4459
|
+
scope_spec.write_text("# Spec\n\n<!-- devlyn:verification -->\n## Verification\n\n- scope gate checks.\n", encoding="utf-8")
|
|
4359
4460
|
(scope_root / "spec.expected.json").write_text(json.dumps({
|
|
4360
4461
|
"verification_commands": [{"cmd": "printf ok", "stdout_contains": ["ok"]}],
|
|
4361
|
-
}) + "\n")
|
|
4462
|
+
}) + "\n", encoding="utf-8")
|
|
4362
4463
|
subprocess.run(["git", "init", "-q"], cwd=scope_root, check=True)
|
|
4363
4464
|
subprocess.run(["git", "add", "-A"], cwd=scope_root, check=True)
|
|
4364
4465
|
subprocess.run(
|
|
@@ -4368,11 +4469,12 @@ def run_self_test() -> int:
|
|
|
4368
4469
|
)
|
|
4369
4470
|
scope_base_sha = subprocess.check_output(
|
|
4370
4471
|
["git", "rev-parse", "HEAD"], cwd=scope_root, text=True,
|
|
4472
|
+
encoding="utf-8",
|
|
4371
4473
|
).strip()
|
|
4372
4474
|
(scope_devlyn / "pipeline.state.json").write_text(json.dumps({
|
|
4373
4475
|
"source": {"type": "spec", "spec_path": str(scope_spec)},
|
|
4374
4476
|
"base_ref": {"sha": scope_base_sha},
|
|
4375
|
-
}))
|
|
4477
|
+
}), encoding="utf-8")
|
|
4376
4478
|
(scope_root / "preexisting.local").write_text("pre-existing untracked\n", encoding="utf-8")
|
|
4377
4479
|
# Writer parity: --write-untracked-baseline must share the reader's
|
|
4378
4480
|
# parser — untracked DIRECTORIES expand to per-file paths and
|
|
@@ -4384,6 +4486,7 @@ def run_self_test() -> int:
|
|
|
4384
4486
|
write_baseline_run = subprocess.run(
|
|
4385
4487
|
[sys.executable, script_path, "--write-untracked-baseline"],
|
|
4386
4488
|
cwd=scope_root, capture_output=True, text=True,
|
|
4489
|
+
encoding="utf-8",
|
|
4387
4490
|
)
|
|
4388
4491
|
if write_baseline_run.returncode != 0:
|
|
4389
4492
|
print("--write-untracked-baseline failed", file=sys.stderr)
|
|
@@ -4399,30 +4502,33 @@ def run_self_test() -> int:
|
|
|
4399
4502
|
scope_build_gate_env["SPEC_VERIFY_PHASE"] = "build_gate"
|
|
4400
4503
|
|
|
4401
4504
|
# 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")
|
|
4505
|
+
(scope_root / "bin" / "cli.js").write_text("module.exports = { ok: true };\n", encoding="utf-8")
|
|
4403
4506
|
missing_plan_run = subprocess.run(
|
|
4404
4507
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4405
4508
|
capture_output=True, text=True,
|
|
4509
|
+
encoding="utf-8",
|
|
4406
4510
|
)
|
|
4407
4511
|
if missing_plan_run.returncode == 0:
|
|
4408
4512
|
print("BUILD_GATE accepted a run with no plan.md", file=sys.stderr)
|
|
4409
4513
|
return 1
|
|
4410
|
-
if "scope.authorized-surface-malformed" not in scope_findings_path.read_text():
|
|
4514
|
+
if "scope.authorized-surface-malformed" not in scope_findings_path.read_text(encoding="utf-8"):
|
|
4411
4515
|
print("missing plan.md did not emit scope.authorized-surface-malformed", file=sys.stderr)
|
|
4412
4516
|
return 1
|
|
4413
4517
|
|
|
4414
4518
|
# Test 2: plan.md present but no authorized_surface json block -> malformed.
|
|
4415
4519
|
(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"
|
|
4520
|
+
"# PLAN\n\n<!-- devlyn:authorized-surface -->\n## 1. Files to touch\n\n- `bin/cli.js` (edit): ship the fix.\n",
|
|
4521
|
+
encoding="utf-8",
|
|
4417
4522
|
)
|
|
4418
4523
|
malformed_block_run = subprocess.run(
|
|
4419
4524
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4420
4525
|
capture_output=True, text=True,
|
|
4526
|
+
encoding="utf-8",
|
|
4421
4527
|
)
|
|
4422
4528
|
if malformed_block_run.returncode == 0:
|
|
4423
4529
|
print("BUILD_GATE accepted plan.md with no authorized_surface block", file=sys.stderr)
|
|
4424
4530
|
return 1
|
|
4425
|
-
if "scope.authorized-surface-malformed" not in scope_findings_path.read_text():
|
|
4531
|
+
if "scope.authorized-surface-malformed" not in scope_findings_path.read_text(encoding="utf-8"):
|
|
4426
4532
|
print("missing authorized_surface block did not emit scope.authorized-surface-malformed", file=sys.stderr)
|
|
4427
4533
|
return 1
|
|
4428
4534
|
malformed_print_surface = subprocess.run(
|
|
@@ -4438,15 +4544,18 @@ def run_self_test() -> int:
|
|
|
4438
4544
|
(scope_devlyn / "plan.md").write_text(
|
|
4439
4545
|
"# PLAN\n\n<!-- devlyn:authorized-surface -->\n## 1. Files to touch\n\n"
|
|
4440
4546
|
"```json\n"
|
|
4441
|
-
+ json.dumps({"authorized_surface": [entry]}) + "\n```\n"
|
|
4547
|
+
+ json.dumps({"authorized_surface": [entry]}) + "\n```\n",
|
|
4548
|
+
encoding="utf-8",
|
|
4442
4549
|
)
|
|
4443
4550
|
brace_build_gate = subprocess.run(
|
|
4444
4551
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4445
4552
|
capture_output=True, text=True,
|
|
4553
|
+
encoding="utf-8",
|
|
4446
4554
|
)
|
|
4447
4555
|
brace_print_surface = subprocess.run(
|
|
4448
4556
|
[sys.executable, script_path, "--print-authorized-surface"], cwd=scope_root,
|
|
4449
4557
|
capture_output=True, text=True,
|
|
4558
|
+
encoding="utf-8",
|
|
4450
4559
|
)
|
|
4451
4560
|
findings_text = scope_findings_path.read_text(encoding="utf-8")
|
|
4452
4561
|
if (
|
|
@@ -4466,9 +4575,10 @@ def run_self_test() -> int:
|
|
|
4466
4575
|
"- `bin/cli.js` (edit): ship the fix.\n\n"
|
|
4467
4576
|
"```json\n"
|
|
4468
4577
|
'{"authorized_surface": ["bin/cli.js", "lib/**", "authorized-but-uncreated.txt"]}\n'
|
|
4469
|
-
"```\n"
|
|
4578
|
+
"```\n",
|
|
4579
|
+
encoding="utf-8",
|
|
4470
4580
|
)
|
|
4471
|
-
(scope_root / "lib" / "new.js").write_text("module.exports = { created: true };\n")
|
|
4581
|
+
(scope_root / "lib" / "new.js").write_text("module.exports = { created: true };\n", encoding="utf-8")
|
|
4472
4582
|
print_surface = subprocess.run(
|
|
4473
4583
|
[sys.executable, script_path, "--print-authorized-surface"],
|
|
4474
4584
|
cwd=scope_root,
|
|
@@ -4490,16 +4600,18 @@ def run_self_test() -> int:
|
|
|
4490
4600
|
missing_baseline_run = subprocess.run(
|
|
4491
4601
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4492
4602
|
capture_output=True, text=True,
|
|
4603
|
+
encoding="utf-8",
|
|
4493
4604
|
)
|
|
4494
4605
|
if missing_baseline_run.returncode == 0:
|
|
4495
4606
|
print("BUILD_GATE accepted missing .devlyn/untracked.baseline", file=sys.stderr)
|
|
4496
4607
|
return 1
|
|
4497
|
-
if "untracked.baseline" not in scope_findings_path.read_text():
|
|
4608
|
+
if "untracked.baseline" not in scope_findings_path.read_text(encoding="utf-8"):
|
|
4498
4609
|
print("missing untracked baseline did not emit a scope finding", file=sys.stderr)
|
|
4499
4610
|
return 1
|
|
4500
4611
|
rewrite_baseline_run = subprocess.run(
|
|
4501
4612
|
[sys.executable, script_path, "--write-untracked-baseline"],
|
|
4502
4613
|
cwd=scope_root, capture_output=True, text=True,
|
|
4614
|
+
encoding="utf-8",
|
|
4503
4615
|
)
|
|
4504
4616
|
if rewrite_baseline_run.returncode != 0:
|
|
4505
4617
|
print("--write-untracked-baseline re-run failed", file=sys.stderr)
|
|
@@ -4507,29 +4619,31 @@ def run_self_test() -> int:
|
|
|
4507
4619
|
in_scope_run = subprocess.run(
|
|
4508
4620
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4509
4621
|
capture_output=True, text=True,
|
|
4622
|
+
encoding="utf-8",
|
|
4510
4623
|
)
|
|
4511
4624
|
if in_scope_run.returncode != 0:
|
|
4512
4625
|
print("in-scope-only diff was rejected", file=sys.stderr)
|
|
4513
4626
|
print(in_scope_run.stderr, file=sys.stderr)
|
|
4514
4627
|
return 1
|
|
4515
|
-
if "scope." in scope_findings_path.read_text():
|
|
4628
|
+
if "scope." in scope_findings_path.read_text(encoding="utf-8"):
|
|
4516
4629
|
print("in-scope-only diff produced a spurious scope finding", file=sys.stderr)
|
|
4517
|
-
print(scope_findings_path.read_text(), file=sys.stderr)
|
|
4630
|
+
print(scope_findings_path.read_text(encoding="utf-8"), file=sys.stderr)
|
|
4518
4631
|
return 1
|
|
4519
4632
|
|
|
4520
4633
|
# Test 4: directory grant covers lib/**; an out-of-scope file must be
|
|
4521
4634
|
# 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')
|
|
4635
|
+
(scope_root / "lib" / "keep.js").write_text("module.exports = { touched: true };\n", encoding="utf-8")
|
|
4636
|
+
(scope_root / "data" / "usage-stats.json").write_text('{"leaked": true}\n', encoding="utf-8")
|
|
4637
|
+
(scope_root / "data" / "scratch.json").write_text('{"untracked": true}\n', encoding="utf-8")
|
|
4525
4638
|
out_of_scope_run = subprocess.run(
|
|
4526
4639
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4527
4640
|
capture_output=True, text=True,
|
|
4641
|
+
encoding="utf-8",
|
|
4528
4642
|
)
|
|
4529
4643
|
if out_of_scope_run.returncode == 0:
|
|
4530
4644
|
print("out-of-scope file was accepted", file=sys.stderr)
|
|
4531
4645
|
return 1
|
|
4532
|
-
out_of_scope_lines = scope_findings_path.read_text().splitlines()
|
|
4646
|
+
out_of_scope_lines = scope_findings_path.read_text(encoding="utf-8").splitlines()
|
|
4533
4647
|
out_of_scope_findings = [loads_strict_json(line) for line in out_of_scope_lines if line.strip()]
|
|
4534
4648
|
flagged_files = {f["file"] for f in out_of_scope_findings if f.get("rule_id") == "scope.out-of-scope-file"}
|
|
4535
4649
|
if "data/usage-stats.json" not in flagged_files:
|
|
@@ -4558,14 +4672,15 @@ def run_self_test() -> int:
|
|
|
4558
4672
|
|
|
4559
4673
|
# Test 5: lib2/keep.js must NOT be covered by the lib/** grant
|
|
4560
4674
|
# (directory-prefix boundary, not a bare string-prefix match).
|
|
4561
|
-
(scope_root / "lib2" / "keep.js").write_text("module.exports = { touched: true };\n")
|
|
4675
|
+
(scope_root / "lib2" / "keep.js").write_text("module.exports = { touched: true };\n", encoding="utf-8")
|
|
4562
4676
|
subprocess.run(
|
|
4563
4677
|
[sys.executable, script_path], cwd=scope_root, env=scope_build_gate_env,
|
|
4564
4678
|
capture_output=True, text=True,
|
|
4679
|
+
encoding="utf-8",
|
|
4565
4680
|
)
|
|
4566
4681
|
boundary_flagged = {
|
|
4567
4682
|
loads_strict_json(line)["file"]
|
|
4568
|
-
for line in scope_findings_path.read_text().splitlines() if line.strip()
|
|
4683
|
+
for line in scope_findings_path.read_text(encoding="utf-8").splitlines() if line.strip()
|
|
4569
4684
|
}
|
|
4570
4685
|
if "lib2/keep.js" not in boundary_flagged:
|
|
4571
4686
|
print("lib/** incorrectly matched lib2/keep.js (directory-prefix boundary bug)", file=sys.stderr)
|
|
@@ -4585,8 +4700,9 @@ def run_self_test() -> int:
|
|
|
4585
4700
|
subprocess.run(
|
|
4586
4701
|
[sys.executable, script_path], cwd=scope_root, env=verify_mech_env,
|
|
4587
4702
|
capture_output=True, text=True,
|
|
4703
|
+
encoding="utf-8",
|
|
4588
4704
|
)
|
|
4589
|
-
verify_mech_findings = (scope_devlyn / "verify-mechanical.findings.jsonl").read_text()
|
|
4705
|
+
verify_mech_findings = (scope_devlyn / "verify-mechanical.findings.jsonl").read_text(encoding="utf-8")
|
|
4590
4706
|
if "scope." in verify_mech_findings:
|
|
4591
4707
|
print("VERIFY MECHANICAL ran the BUILD_GATE-only authorized_surface gate", file=sys.stderr)
|
|
4592
4708
|
print(verify_mech_findings, file=sys.stderr)
|
|
@@ -4850,7 +4966,7 @@ def main() -> int:
|
|
|
4850
4966
|
return 0
|
|
4851
4967
|
else:
|
|
4852
4968
|
try:
|
|
4853
|
-
spec = loads_strict_json(spec_path.read_text())
|
|
4969
|
+
spec = loads_strict_json(spec_path.read_text(encoding="utf-8"))
|
|
4854
4970
|
except (ValueError, OSError) as e:
|
|
4855
4971
|
print(f"[spec-verify] error: cannot parse {spec_path}: {e}", file=sys.stderr)
|
|
4856
4972
|
return 2
|
|
@@ -5145,12 +5261,12 @@ def main() -> int:
|
|
|
5145
5261
|
results_path.write_text(json.dumps({
|
|
5146
5262
|
"commands": results,
|
|
5147
5263
|
"process_evidence": evidence_carrier,
|
|
5148
|
-
}, indent=2) + "\n")
|
|
5264
|
+
}, indent=2) + "\n", encoding="utf-8")
|
|
5149
5265
|
|
|
5150
5266
|
# Append findings (jsonl). BUILD_GATE merge step concatenates this onto
|
|
5151
5267
|
# build_gate.findings.jsonl; never overwrite the orchestrator's own gate
|
|
5152
5268
|
# findings. Truncate this file each run since it is a per-round artifact.
|
|
5153
|
-
with findings_path.open("w") as fh:
|
|
5269
|
+
with findings_path.open("w", encoding="utf-8") as fh:
|
|
5154
5270
|
for f in findings:
|
|
5155
5271
|
fh.write(json.dumps(f) + "\n")
|
|
5156
5272
|
|
|
@@ -5181,4 +5297,5 @@ def main() -> int:
|
|
|
5181
5297
|
|
|
5182
5298
|
|
|
5183
5299
|
if __name__ == "__main__":
|
|
5300
|
+
runpy.run_path(str(Path(__file__).with_name("platform-support.py")))["configure_utf8"]()
|
|
5184
5301
|
sys.exit(main())
|