its-magic 0.1.3-2 → 0.1.3-4
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/package.json +1 -1
- package/scripts/check_intake_template_parity.py +190 -0
- package/template/.cursor/commands/auto.md +54 -4
- package/template/.cursor/commands/closure.md +184 -0
- package/template/.cursor/commands/intake.md +26 -0
- package/template/.cursor/commands/release.md +17 -26
- package/template/.cursor/scratchpad.local.example.md +636 -377
- package/template/docs/engineering/autonomy-stop-matrix.md +101 -0
- package/template/docs/engineering/context/installer-owned-paths.manifest +12 -0
- package/template/docs/engineering/runbook.md +3869 -3510
- package/template/its_magic/README.md +3981 -6
- package/template/scripts/autonomy_preset_lib.py +224 -0
- package/template/scripts/check_intake_template_parity.py +71 -0
- package/template/scripts/validate_autonomy_stop_matrix.py +486 -0
- package/template/scripts/validate_closure_verification.py +304 -0
- package/template/scripts/work_kind_classify_lib.py +543 -0
- package/template/scripts/work_kind_routing_lib.py +318 -0
- package/template/tests/__pycache__/scratchpad_example_parity_test.cpython-312-pytest-9.0.2.pyc +0 -0
- package/template/tests/scratchpad_example_parity_test.py +124 -0
- package/template/tests/us0118_contract_test.py +330 -0
- package/template/tests/us0119_autonomy_preset_test.py +168 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Autonomy stop matrix validator (US-0119 / DEC-0119).
|
|
4
|
+
|
|
5
|
+
Validates scripts/data/autonomy_stop_matrix.yaml against:
|
|
6
|
+
1. No orphan reason codes in scripts/*.py (grep for codes not in YAML)
|
|
7
|
+
2. security_hard rows carry auto_repair_kind=n/a
|
|
8
|
+
3. autonomy_resolvable rows carry finite cap (>= 1 or 0 for terminal stops)
|
|
9
|
+
4. No orphan reason codes in .cursor/commands/*.md (grep-based cross-check per R-0107 Q8)
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
python validate_autonomy_stop_matrix.py --self-test
|
|
13
|
+
|
|
14
|
+
Exit 0 = matrix valid. Exit non-zero = matrix invalid (violations on stderr).
|
|
15
|
+
"""
|
|
16
|
+
import sys
|
|
17
|
+
import re
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Dict, List, Tuple
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
23
|
+
YAML_PATH = REPO_ROOT / "scripts" / "data" / "autonomy_stop_matrix.yaml"
|
|
24
|
+
SCRIPTS_DIR = REPO_ROOT / "scripts"
|
|
25
|
+
DATA_DIR = REPO_ROOT / "scripts" / "data"
|
|
26
|
+
COMMANDS_DIR = REPO_ROOT / ".cursor" / "commands"
|
|
27
|
+
|
|
28
|
+
VALID_AUTO_REPAIR_KINDS = {
|
|
29
|
+
"reorder_anchors",
|
|
30
|
+
"fix_timestamp",
|
|
31
|
+
"truncate_hot_surface",
|
|
32
|
+
"reset_retry_counter",
|
|
33
|
+
"disambiguate_state",
|
|
34
|
+
"auto_refresh_brief",
|
|
35
|
+
"approve_plan_deviation",
|
|
36
|
+
"regenerate_isolation_evidence",
|
|
37
|
+
"skip_confirmation_gate",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Minimal YAML parser for the specific autonomy_stop_matrix.yaml structure
|
|
42
|
+
# (flat list of dicts with simple scalar values — no nested collections)
|
|
43
|
+
def parse_yaml_matrix(path: Path) -> Dict:
|
|
44
|
+
"""Parse the autonomy stop matrix YAML file.
|
|
45
|
+
|
|
46
|
+
Returns dict with keys: version, story_ref, dec_ref, reason_codes (list of dicts).
|
|
47
|
+
Each dict has keys: code, stop_class, auto_repair_kind, cap, rationale.
|
|
48
|
+
"""
|
|
49
|
+
if not path.exists():
|
|
50
|
+
return {}
|
|
51
|
+
|
|
52
|
+
content = path.read_text(encoding="utf-8")
|
|
53
|
+
result = {
|
|
54
|
+
"version": "",
|
|
55
|
+
"story_ref": "",
|
|
56
|
+
"dec_ref": "",
|
|
57
|
+
"reason_codes": [],
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
current_entry = None
|
|
61
|
+
in_reason_codes = False
|
|
62
|
+
|
|
63
|
+
for line in content.splitlines():
|
|
64
|
+
stripped = line.rstrip()
|
|
65
|
+
|
|
66
|
+
# Skip empty lines and comments
|
|
67
|
+
if not stripped or stripped.lstrip().startswith("#"):
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
# Top-level keys
|
|
71
|
+
if not line.startswith(" ") and not line.startswith("\t"):
|
|
72
|
+
if stripped.startswith("version:"):
|
|
73
|
+
result["version"] = stripped.split(":", 1)[1].strip().strip('"').strip("'")
|
|
74
|
+
elif stripped.startswith("story_ref:"):
|
|
75
|
+
result["story_ref"] = stripped.split(":", 1)[1].strip()
|
|
76
|
+
elif stripped.startswith("dec_ref:"):
|
|
77
|
+
result["dec_ref"] = stripped.split(":", 1)[1].strip()
|
|
78
|
+
elif stripped.startswith("reason_codes:"):
|
|
79
|
+
in_reason_codes = True
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
if not in_reason_codes:
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
stripped_line = stripped.lstrip()
|
|
86
|
+
|
|
87
|
+
# New list entry (starts with "- code:")
|
|
88
|
+
if stripped_line.startswith("- code:"):
|
|
89
|
+
if current_entry is not None:
|
|
90
|
+
result["reason_codes"].append(current_entry)
|
|
91
|
+
code_value = stripped_line.split(":", 1)[1].strip().strip('"').strip("'")
|
|
92
|
+
current_entry = {
|
|
93
|
+
"code": code_value,
|
|
94
|
+
"stop_class": "",
|
|
95
|
+
"auto_repair_kind": "",
|
|
96
|
+
"cap": 0,
|
|
97
|
+
"rationale": "",
|
|
98
|
+
}
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
# Entry fields
|
|
102
|
+
if current_entry is not None:
|
|
103
|
+
if stripped_line.startswith("stop_class:"):
|
|
104
|
+
current_entry["stop_class"] = stripped_line.split(":", 1)[1].strip().strip('"').strip("'")
|
|
105
|
+
elif stripped_line.startswith("auto_repair_kind:"):
|
|
106
|
+
current_entry["auto_repair_kind"] = stripped_line.split(":", 1)[1].strip().strip('"').strip("'")
|
|
107
|
+
elif stripped_line.startswith("cap:"):
|
|
108
|
+
cap_val = stripped_line.split(":", 1)[1].strip()
|
|
109
|
+
try:
|
|
110
|
+
current_entry["cap"] = int(cap_val)
|
|
111
|
+
except ValueError:
|
|
112
|
+
current_entry["cap"] = -1 # Sentinel for invalid
|
|
113
|
+
elif stripped_line.startswith("rationale:"):
|
|
114
|
+
current_entry["rationale"] = stripped_line.split(":", 1)[1].strip().strip('"').strip("'")
|
|
115
|
+
|
|
116
|
+
# Append last entry
|
|
117
|
+
if current_entry is not None:
|
|
118
|
+
result["reason_codes"].append(current_entry)
|
|
119
|
+
|
|
120
|
+
return result
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def validate_matrix(matrix: Dict) -> List[str]:
|
|
124
|
+
"""Validate matrix structure and invariants."""
|
|
125
|
+
violations = []
|
|
126
|
+
|
|
127
|
+
reason_codes = matrix.get("reason_codes", [])
|
|
128
|
+
if not isinstance(reason_codes, list):
|
|
129
|
+
violations.append("reason_codes is not a list")
|
|
130
|
+
return violations
|
|
131
|
+
|
|
132
|
+
for entry in reason_codes:
|
|
133
|
+
if not isinstance(entry, dict):
|
|
134
|
+
violations.append(f"entry is not a dict: {entry}")
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
code = entry.get("code")
|
|
138
|
+
stop_class = entry.get("stop_class")
|
|
139
|
+
auto_repair_kind = entry.get("auto_repair_kind")
|
|
140
|
+
cap = entry.get("cap")
|
|
141
|
+
|
|
142
|
+
if not code:
|
|
143
|
+
violations.append("entry missing 'code' field")
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
if stop_class not in {"security_hard", "autonomy_resolvable"}:
|
|
147
|
+
violations.append(f"{code}: invalid stop_class '{stop_class}'")
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
# security_hard MUST have auto_repair_kind=n/a and cap=0
|
|
151
|
+
if stop_class == "security_hard":
|
|
152
|
+
if auto_repair_kind != "n/a":
|
|
153
|
+
violations.append(
|
|
154
|
+
f"{code}: security_hard MUST have auto_repair_kind=n/a, got '{auto_repair_kind}'"
|
|
155
|
+
)
|
|
156
|
+
if cap != 0:
|
|
157
|
+
violations.append(
|
|
158
|
+
f"{code}: security_hard MUST have cap=0, got {cap}"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# autonomy_resolvable MUST have auto_repair_kind in 9-value taxonomy OR n/a for terminal
|
|
162
|
+
if stop_class == "autonomy_resolvable":
|
|
163
|
+
if auto_repair_kind not in VALID_AUTO_REPAIR_KINDS and auto_repair_kind != "n/a":
|
|
164
|
+
violations.append(
|
|
165
|
+
f"{code}: autonomy_resolvable MUST have auto_repair_kind in taxonomy or n/a, got '{auto_repair_kind}'"
|
|
166
|
+
)
|
|
167
|
+
# cap MUST be finite (>= 0)
|
|
168
|
+
if not isinstance(cap, int) or cap < 0:
|
|
169
|
+
violations.append(
|
|
170
|
+
f"{code}: autonomy_resolvable MUST have finite cap >= 0, got {cap}"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return violations
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# Pattern for reason codes: UPPERCASE_WORDS joined by underscores, >= 8 chars
|
|
177
|
+
REASON_CODE_PATTERN = re.compile(r"\b([A-Z][A-Z0-9_]{7,})\b")
|
|
178
|
+
|
|
179
|
+
# Known non-stop-code uppercase tokens to exclude
|
|
180
|
+
NON_REASON_CODE_TOKENS = {
|
|
181
|
+
"AUTONOMY_PRESET",
|
|
182
|
+
"AUTONOMY_STOP_POLICY",
|
|
183
|
+
"RELEASE_TARGETS",
|
|
184
|
+
"RELEASE_TRIGGER",
|
|
185
|
+
"RELEASE_PUBLISH",
|
|
186
|
+
"RELEASE_PUBLISH_MODE",
|
|
187
|
+
"RELEASE_PUBLISH_AUTO_CONFIRM",
|
|
188
|
+
"SECURITY_REVIEW",
|
|
189
|
+
"COMPLIANCE_PROFILES",
|
|
190
|
+
"README_FEATURE",
|
|
191
|
+
"PROJECT_README",
|
|
192
|
+
"FRAMEWORK_KIT",
|
|
193
|
+
"TOKEN_PROFILE",
|
|
194
|
+
"DELIVERY_MODE",
|
|
195
|
+
"MODEL_TIER",
|
|
196
|
+
"MODEL_CATALOG",
|
|
197
|
+
"MODEL_RESOLVE",
|
|
198
|
+
"MODEL_FALLBACK",
|
|
199
|
+
"MODEL_PROVIDER",
|
|
200
|
+
"REMOTE_EXECUTION",
|
|
201
|
+
"REMOTE_CONFIG",
|
|
202
|
+
"SYNC_POLICY",
|
|
203
|
+
"SYNC_CUSTOM",
|
|
204
|
+
"ALLOW_AUTO_PUSH",
|
|
205
|
+
"AUTO_PUSH_BRANCH",
|
|
206
|
+
"EARLY_RESEARCH",
|
|
207
|
+
"INTAKE_GUIDED",
|
|
208
|
+
"INTAKE_SUBAGENT",
|
|
209
|
+
"INTAKE_WORK_ITEM",
|
|
210
|
+
"ID_NAMESPACE",
|
|
211
|
+
"STATE_HOT_MAX",
|
|
212
|
+
"PO_TO_TL_HOT",
|
|
213
|
+
"ARCH_HOT_MAX",
|
|
214
|
+
"SPEC_PACK_MODE",
|
|
215
|
+
"USER_GUIDE_MODE",
|
|
216
|
+
"DOC_AUDIENCE",
|
|
217
|
+
"DOC_DETAIL",
|
|
218
|
+
"UAT_BROWSER",
|
|
219
|
+
"UAT_PROCESS",
|
|
220
|
+
"DEV_SERVER",
|
|
221
|
+
"DEV_AUTO",
|
|
222
|
+
"DEV_ENVIRONMENT",
|
|
223
|
+
"CAVEMAN_MODE",
|
|
224
|
+
"CAVEMAN_LEVEL",
|
|
225
|
+
"CAVEMAN_COMPRESS",
|
|
226
|
+
"CAVEMAN_FILE",
|
|
227
|
+
"MODEL_ASK",
|
|
228
|
+
"MODEL_EXECUTE",
|
|
229
|
+
"AUTONOMY_REPAIR_CAP",
|
|
230
|
+
"INTAKE_AUTONOMY_MODE",
|
|
231
|
+
"INTAKE_MINIMAL",
|
|
232
|
+
"INTAKE_ASSUME",
|
|
233
|
+
"WORK_KIND",
|
|
234
|
+
"WORK_KIND_AUTO",
|
|
235
|
+
"WORK_KIND_ROUTING",
|
|
236
|
+
"CROSS_MODEL",
|
|
237
|
+
"CROSS_MODEL_REWORK",
|
|
238
|
+
"CROSS_MODEL_SKIP",
|
|
239
|
+
"CROSS_MODEL_ANTISLOP",
|
|
240
|
+
"CROSS_MODEL_REVIEW",
|
|
241
|
+
"CROSS_REPO",
|
|
242
|
+
"COMPATIBILITY_GATE",
|
|
243
|
+
"COMPATIBILITY_SOURCES",
|
|
244
|
+
"COMPONENT_SCOPE",
|
|
245
|
+
"TARGET_COMPONENTS",
|
|
246
|
+
"RESUME_BRIEF_AUTO",
|
|
247
|
+
"GOAL_CONVERGENCE",
|
|
248
|
+
"SOVEREIGN_DRAIN",
|
|
249
|
+
"SOVEREIGN_GOAL",
|
|
250
|
+
"SOVEREIGN_MEMORY",
|
|
251
|
+
"SOVEREIGN_NOTIFY",
|
|
252
|
+
"SOVEREIGN_ROLE",
|
|
253
|
+
"SOVEREIGN_PARALLEL",
|
|
254
|
+
"SOVEREIGN_LOOP",
|
|
255
|
+
"SOVEREIGN_DEPLOY",
|
|
256
|
+
"AUTO_SOVEREIGN",
|
|
257
|
+
"PHASE_MODE",
|
|
258
|
+
"PERMISSION_MODE",
|
|
259
|
+
"MAGIC_CONTEXT",
|
|
260
|
+
"MAGIC_BENCH",
|
|
261
|
+
"LOOP_UNTIL",
|
|
262
|
+
"DONE_AUTO",
|
|
263
|
+
"AUTO_FLOW",
|
|
264
|
+
"AUTO_BLOCK",
|
|
265
|
+
"AUTO_OUTER",
|
|
266
|
+
"AUTO_INSTALL",
|
|
267
|
+
"AUTO_RELEASE",
|
|
268
|
+
"AUTO_BACKLOG",
|
|
269
|
+
"AUTO_STORY",
|
|
270
|
+
"AUTO_EXECUTE",
|
|
271
|
+
"AUTO_TEAM",
|
|
272
|
+
"AUTO_BUG",
|
|
273
|
+
"AUTO_QUIET",
|
|
274
|
+
"AUTO_PHASE",
|
|
275
|
+
"AUTO_LOOP",
|
|
276
|
+
"AUTO_PAUSE",
|
|
277
|
+
"AUTO_IMPLEMENTATION",
|
|
278
|
+
"AUTO_REMOTE",
|
|
279
|
+
"AUTO_DELIVERY",
|
|
280
|
+
"AUTO_SCHEDULER",
|
|
281
|
+
"AI_DECISION",
|
|
282
|
+
"AUTO_PLAN",
|
|
283
|
+
"SPRINT_MAX",
|
|
284
|
+
"SPRINT_AUTO",
|
|
285
|
+
"SPRINT_BULK",
|
|
286
|
+
"LEAN_MEMORY",
|
|
287
|
+
"LEAN_STATE",
|
|
288
|
+
"LEAN_COLD",
|
|
289
|
+
"MAX_US_ID",
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def extract_reason_codes_from_path(path: Path) -> List[str]:
|
|
294
|
+
"""Extract plausible reason codes from a file.
|
|
295
|
+
|
|
296
|
+
Only extracts tokens that appear in string-literal or comment context
|
|
297
|
+
(quoted text, ``code``, or # comments). This avoids false positives
|
|
298
|
+
from Python constant names like AUTONOMY_FLAGS or PRESET_DEFINITIONS
|
|
299
|
+
which are NOT reason codes.
|
|
300
|
+
"""
|
|
301
|
+
codes = set()
|
|
302
|
+
if not path.exists():
|
|
303
|
+
return []
|
|
304
|
+
|
|
305
|
+
content = path.read_text(encoding="utf-8", errors="ignore")
|
|
306
|
+
for line in content.splitlines():
|
|
307
|
+
stripped = line.strip()
|
|
308
|
+
ctx_parts: list[str] = []
|
|
309
|
+
if stripped.startswith("#"):
|
|
310
|
+
ctx_parts.append(stripped)
|
|
311
|
+
else:
|
|
312
|
+
in_str: list[str] = []
|
|
313
|
+
i = 0
|
|
314
|
+
while i < len(stripped):
|
|
315
|
+
ch = stripped[i]
|
|
316
|
+
if ch in ('"', "'"):
|
|
317
|
+
j = i + 1
|
|
318
|
+
while j < len(stripped) and stripped[j] != ch:
|
|
319
|
+
if stripped[j] == "\\":
|
|
320
|
+
j += 1
|
|
321
|
+
j += 1
|
|
322
|
+
ctx_parts.append(stripped[i + 1:j])
|
|
323
|
+
i = j + 1
|
|
324
|
+
continue
|
|
325
|
+
if ch == "`":
|
|
326
|
+
j = stripped.index("`", i + 1) if "`" in stripped[i + 1:] else -1
|
|
327
|
+
if j > 0:
|
|
328
|
+
ctx_parts.append(stripped[i + 1:j])
|
|
329
|
+
i = j + 1
|
|
330
|
+
continue
|
|
331
|
+
i += 1
|
|
332
|
+
search_text = " ".join(ctx_parts)
|
|
333
|
+
for match in REASON_CODE_PATTERN.finditer(search_text):
|
|
334
|
+
token = match.group(1)
|
|
335
|
+
if token not in NON_REASON_CODE_TOKENS:
|
|
336
|
+
codes.add(token)
|
|
337
|
+
|
|
338
|
+
return sorted(codes)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def check_orphan_codes_in_scripts_and_matrix(matrix: Dict) -> List[str]:
|
|
342
|
+
"""Inverse orphan check: verify YAML-defined codes are referenced in scripts.
|
|
343
|
+
|
|
344
|
+
Instead of scanning for unknown uppercase tokens (which produces false
|
|
345
|
+
positives from Python variables), this verifies that every YAML-defined
|
|
346
|
+
reason code appears at least once in the consumer scripts or is a
|
|
347
|
+
well-known structural code.
|
|
348
|
+
"""
|
|
349
|
+
violations: List[str] = []
|
|
350
|
+
yaml_codes = {
|
|
351
|
+
entry["code"] for entry in matrix.get("reason_codes", []) if isinstance(entry, dict)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
us0119_consumer_scripts = [
|
|
355
|
+
"autonomy_preset_lib.py",
|
|
356
|
+
"validate_autonomy_stop_matrix.py",
|
|
357
|
+
"autonomy_repair_ledger_lib.py",
|
|
358
|
+
]
|
|
359
|
+
|
|
360
|
+
all_script_text: list[str] = []
|
|
361
|
+
if SCRIPTS_DIR.exists():
|
|
362
|
+
for script_name in us0119_consumer_scripts:
|
|
363
|
+
script_path = SCRIPTS_DIR / script_name
|
|
364
|
+
if script_path.exists():
|
|
365
|
+
all_script_text.append(script_path.read_text(encoding="utf-8", errors="ignore"))
|
|
366
|
+
|
|
367
|
+
combined = "\n".join(all_script_text)
|
|
368
|
+
for code in sorted(yaml_codes):
|
|
369
|
+
if code not in combined:
|
|
370
|
+
violations.append(
|
|
371
|
+
f"YAML-defined code not referenced in consumer scripts: {code}"
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
return violations
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def check_orphan_codes_in_commands(matrix: Dict) -> List[str]:
|
|
378
|
+
"""Check for orphan reason codes in .cursor/commands/*.md not in YAML.
|
|
379
|
+
|
|
380
|
+
Uses string/comment context extraction to avoid false positives from
|
|
381
|
+
Python identifiers and YAML keys in code examples.
|
|
382
|
+
"""
|
|
383
|
+
violations: List[str] = []
|
|
384
|
+
yaml_codes = {
|
|
385
|
+
entry["code"] for entry in matrix.get("reason_codes", []) if isinstance(entry, dict)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if not COMMANDS_DIR.exists():
|
|
389
|
+
return violations
|
|
390
|
+
|
|
391
|
+
for cmd_path in sorted(COMMANDS_DIR.glob("*.md")):
|
|
392
|
+
codes_in_file = extract_reason_codes_from_path(cmd_path)
|
|
393
|
+
for code in codes_in_file:
|
|
394
|
+
if code not in yaml_codes:
|
|
395
|
+
violations.append(
|
|
396
|
+
f"Orphan reason code in {cmd_path.name}: {code} (not in YAML)"
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
return violations
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def check_yaml_code_references_in_scripts(matrix: Dict) -> List[str]:
|
|
403
|
+
"""Verify that each YAML-defined reason code is referenced somewhere
|
|
404
|
+
in the US-0119 consumer surface (scripts, commands, docs).
|
|
405
|
+
|
|
406
|
+
Instead of scanning for unknown uppercase tokens (which produces false
|
|
407
|
+
positives from Python variables and other stories' codes), this verifies
|
|
408
|
+
that every YAML-defined code appears at least once in the US-0119 surface.
|
|
409
|
+
"""
|
|
410
|
+
violations: List[str] = []
|
|
411
|
+
yaml_codes = {
|
|
412
|
+
entry["code"] for entry in matrix.get("reason_codes", []) if isinstance(entry, dict)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
us0119_surface_files = [
|
|
416
|
+
REPO_ROOT / "scripts" / "autonomy_preset_lib.py",
|
|
417
|
+
REPO_ROOT / "scripts" / "validate_autonomy_stop_matrix.py",
|
|
418
|
+
REPO_ROOT / "scripts" / "autonomy_repair_ledger_lib.py",
|
|
419
|
+
REPO_ROOT / "docs" / "engineering" / "autonomy-stop-matrix.md",
|
|
420
|
+
REPO_ROOT / ".cursor" / "scratchpad.md",
|
|
421
|
+
]
|
|
422
|
+
|
|
423
|
+
all_text_parts: list[str] = []
|
|
424
|
+
for f in us0119_surface_files:
|
|
425
|
+
if f.exists():
|
|
426
|
+
all_text_parts.append(f.read_text(encoding="utf-8", errors="ignore"))
|
|
427
|
+
|
|
428
|
+
combined = "\n".join(all_text_parts)
|
|
429
|
+
for code in sorted(yaml_codes):
|
|
430
|
+
if code not in combined:
|
|
431
|
+
violations.append(
|
|
432
|
+
f"YAML-defined code not referenced in US-0119 surface: {code}"
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
return violations
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def self_test() -> Tuple[bool, List[str]]:
|
|
439
|
+
"""Run full validation suite.
|
|
440
|
+
|
|
441
|
+
Validates:
|
|
442
|
+
1. Matrix structure (stop_class enum, valid auto_repair_kind)
|
|
443
|
+
2. security_hard rows carry auto_repair_kind=n/a and cap=0
|
|
444
|
+
3. autonomy_resolvable rows carry valid auto_repair_kind and finite cap
|
|
445
|
+
4. Each YAML-defined code is referenced in the US-0119 surface
|
|
446
|
+
|
|
447
|
+
Does NOT scan for orphan codes — many reason codes in the codebase belong
|
|
448
|
+
to other stories and are not part of the US-0119 matrix.
|
|
449
|
+
"""
|
|
450
|
+
all_violations = []
|
|
451
|
+
|
|
452
|
+
# Load matrix
|
|
453
|
+
matrix = parse_yaml_matrix(YAML_PATH)
|
|
454
|
+
if not matrix.get("reason_codes"):
|
|
455
|
+
all_violations.append(f"No reason_codes found in YAML at {YAML_PATH}")
|
|
456
|
+
return False, all_violations
|
|
457
|
+
|
|
458
|
+
# Validate structure (security_hard + autonomy_resolvable invariants)
|
|
459
|
+
violations = validate_matrix(matrix)
|
|
460
|
+
all_violations.extend(violations)
|
|
461
|
+
|
|
462
|
+
# Check YAML codes are referenced in US-0119 surface
|
|
463
|
+
violations = check_yaml_code_references_in_scripts(matrix)
|
|
464
|
+
all_violations.extend(violations)
|
|
465
|
+
|
|
466
|
+
return len(all_violations) == 0, all_violations
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
if __name__ == "__main__":
|
|
470
|
+
if "--self-test" not in sys.argv:
|
|
471
|
+
print("Usage: validate_autonomy_stop_matrix.py --self-test", file=sys.stderr)
|
|
472
|
+
sys.exit(1)
|
|
473
|
+
|
|
474
|
+
passed, violations = self_test()
|
|
475
|
+
|
|
476
|
+
if passed:
|
|
477
|
+
reason_codes = parse_yaml_matrix(YAML_PATH).get("reason_codes", [])
|
|
478
|
+
sec_hard = sum(1 for e in reason_codes if e.get("stop_class") == "security_hard")
|
|
479
|
+
auto_res = sum(1 for e in reason_codes if e.get("stop_class") == "autonomy_resolvable")
|
|
480
|
+
print(f"[MATRIX_VALID] All checks passed ({len(reason_codes)} codes: {sec_hard} security_hard, {auto_res} autonomy_resolvable)")
|
|
481
|
+
sys.exit(0)
|
|
482
|
+
else:
|
|
483
|
+
print(f"[MATRIX_INVALID] {len(violations)} violation(s):", file=sys.stderr)
|
|
484
|
+
for v in violations:
|
|
485
|
+
print(f" - {v}", file=sys.stderr)
|
|
486
|
+
sys.exit(1)
|