its-magic 0.1.3-0 → 0.1.3-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/installer.ps1 +8 -0
  2. package/installer.py +8 -0
  3. package/installer.sh +1 -0
  4. package/package.json +1 -1
  5. package/scripts/check_intake_template_parity.py +62 -0
  6. package/template/.cursor/agents/curator.mdc +1 -0
  7. package/template/.cursor/agents/po.mdc +1 -0
  8. package/template/.cursor/agents/release.mdc +1 -0
  9. package/template/.cursor/commands/auto.md +90 -0
  10. package/template/.cursor/commands/execute.md +26 -0
  11. package/template/.cursor/commands/refresh-context.md +32 -0
  12. package/template/.cursor/commands/sovereign-critic.md +104 -0
  13. package/template/.cursor/model-catalog.local.example.cursor-only.json +9 -0
  14. package/template/.cursor/model-catalog.local.example.json +9 -0
  15. package/template/.cursor/model-catalog.local.example.level-1-easy.json +9 -0
  16. package/template/.cursor/model-catalog.local.example.level-2-complex.json +9 -0
  17. package/template/.cursor/model-catalog.local.example.level-3-mega.json +9 -0
  18. package/template/.cursor/model-catalog.local.example.level-4-super.json +9 -0
  19. package/template/.cursor/model-catalog.local.example.role-based-balanced.json +18 -0
  20. package/template/.cursor/model-catalog.local.example.role-based-highend.json +18 -0
  21. package/template/.cursor/rules/sovereign-role-manifest.mdc.example +27 -0
  22. package/template/.cursor/scratchpad.local.example.md +55 -0
  23. package/template/.cursor/scratchpad.md +203 -0
  24. package/template/.cursor/sovereign-role-manifest.yaml.example +53 -0
  25. package/template/decisions/DEC-0104.md +279 -0
  26. package/template/decisions/DEC-0105.md +231 -0
  27. package/template/decisions/DEC-0107.md +246 -0
  28. package/template/docs/engineering/architecture.md +78 -0
  29. package/template/docs/engineering/auto-orchestration-reference.md +7 -0
  30. package/template/docs/engineering/context/installer-owned-paths.manifest +8 -0
  31. package/template/docs/engineering/reason_codes.md +411 -0
  32. package/template/docs/engineering/runbook.md +1119 -0
  33. package/template/docs/engineering/sovereign-memory/.gitkeep +0 -0
  34. package/template/docs/engineering/sovereign-memory/retrospectives/.gitkeep +0 -0
  35. package/template/handoffs/sovereign_decisions/.gitkeep +0 -0
  36. package/template/handoffs/sovereign_deferrals/.gitkeep +0 -0
  37. package/template/handoffs/sovereign_role_reviews.jsonl +0 -0
  38. package/template/scripts/__pycache__/decision_ledger_lib.cpython-312.pyc +0 -0
  39. package/template/scripts/check_intake_template_parity.py +181 -0
  40. package/template/scripts/decision_ledger_lib.py +732 -0
  41. package/template/scripts/ledger_validate.py +153 -0
  42. package/template/scripts/model_tier_lib.py +680 -0
  43. package/template/scripts/model_tier_validate.py +368 -0
  44. package/template/scripts/parallel_dev_arbiter.py +923 -0
  45. package/template/scripts/release_trigger_adapters.py +843 -0
  46. package/template/scripts/self_healing_deploy_lib.py +463 -0
  47. package/template/scripts/self_healing_deploy_validate.py +78 -0
  48. package/template/scripts/sovereign_convergence_lib.py +994 -0
  49. package/template/scripts/sovereign_convergence_validate.py +206 -0
  50. package/template/scripts/sovereign_critic_lib.py +629 -0
  51. package/template/scripts/sovereign_critic_validate.py +131 -0
  52. package/template/scripts/sovereign_loop_lib.py +828 -0
  53. package/template/scripts/sovereign_loop_validate.py +122 -0
  54. package/template/scripts/sovereign_memory_lib.py +869 -0
  55. package/template/scripts/sovereign_memory_validate.py +153 -0
  56. package/template/scripts/sovereign_role_manifest_lib.py +547 -0
  57. package/template/scripts/sovereign_role_manifest_validate.py +105 -0
  58. package/template/tests/us0108_contract_test.py +207 -0
  59. package/template/tests/us0109_contract_test.py +209 -0
  60. package/template/tests/us0109_us0110_compose_test.py +34 -0
  61. package/template/tests/us0111_contract_test.py +345 -0
@@ -0,0 +1,368 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Model tier validator CLI (US-0101 / DEC-0086; US-0102 / DEC-0087).
4
+
5
+ Validates:
6
+ - Tier enum values (cheap|balanced|strong)
7
+ - Catalog schema (v1 + v2)
8
+ - Direct MODEL_<PHASE> slug keys
9
+ - Phase key spelling (canonical phase IDs)
10
+ - Forbidden vendor slugs in template agents
11
+
12
+ Exit codes:
13
+ - 0: All validations passed
14
+ - 1: Validation failed (see stderr for details)
15
+ """
16
+
17
+ import argparse
18
+ import json
19
+ import re
20
+ import sys
21
+ from pathlib import Path
22
+ from typing import Dict, List, Tuple
23
+
24
+ sys.path.insert(0, str(Path(__file__).parent))
25
+ from model_tier_lib import (
26
+ CANONICAL_PHASE_IDS,
27
+ CATALOG_ROLE_KEYS,
28
+ DEFAULT_PHASE_TIER_MATRIX,
29
+ PRECEDENCE_CHAIN_STEPS,
30
+ ReasonCode,
31
+ Tier,
32
+ catalog_validation_reason_code,
33
+ phase_to_model_key,
34
+ resolve_model_for_phase,
35
+ validate_catalog_schema,
36
+ validate_direct_slug,
37
+ )
38
+
39
+ # Reason codes used for fail-closed reporting (DEC-0086 §3 + DEC-0087 §8):
40
+ # - MODEL_TIER_INVALID: unknown tier value
41
+ # - MODEL_CATALOG_INVALID: malformed catalog JSON (v1)
42
+ # - MODEL_SLUG_UNKNOWN: tier key missing from catalog
43
+ # - MODEL_RESOLVE_FALLBACK: catalog lookup failed, using fallback
44
+ # - MODEL_OVERRIDE_SLUG_UNKNOWN: direct slug validation failure
45
+ # - MODEL_ROLE_SLUG_UNKNOWN: role catalog lookup miss
46
+ # - MODEL_CATALOG_SCHEMA_V2_INVALID: v2 schema validation failure
47
+ REASON_CODES = list(ReasonCode)
48
+
49
+ FORBIDDEN_SLUG_PATTERNS = [
50
+ r"composer-",
51
+ r"claude-",
52
+ r"gpt-",
53
+ r"opus-",
54
+ r"glm-",
55
+ ]
56
+
57
+ CANONICAL_PHASE_ID_SET = set(CANONICAL_PHASE_IDS)
58
+
59
+
60
+ def validate_tier_enum(tier_value: str) -> Tuple[bool, str]:
61
+ """Validate tier enum value."""
62
+ try:
63
+ Tier(tier_value)
64
+ return True, ""
65
+ except ValueError:
66
+ return False, f"Invalid tier value: {tier_value} (expected: cheap|balanced|strong)"
67
+
68
+
69
+ def validate_phase_key(phase: str) -> Tuple[bool, str]:
70
+ """Validate phase key spelling."""
71
+ if phase not in CANONICAL_PHASE_ID_SET:
72
+ return False, (
73
+ f"Unknown phase ID: {phase} "
74
+ f"(canonical: {', '.join(sorted(CANONICAL_PHASE_ID_SET))})"
75
+ )
76
+ return True, ""
77
+
78
+
79
+ def check_forbidden_slugs_in_file(file_path: Path) -> List[str]:
80
+ """Check for forbidden vendor slugs in a file."""
81
+ violations = []
82
+ if not file_path.exists():
83
+ return violations
84
+
85
+ content = file_path.read_text(encoding="utf-8")
86
+ lines = content.split("\n")
87
+
88
+ for line_num, line in enumerate(lines, start=1):
89
+ for pattern in FORBIDDEN_SLUG_PATTERNS:
90
+ if re.search(pattern, line, re.IGNORECASE):
91
+ violations.append(
92
+ f"{file_path}:{line_num}: forbidden slug pattern '{pattern}' found: {line.strip()}"
93
+ )
94
+
95
+ return violations
96
+
97
+
98
+ def check_template_agents(repo_root: Path) -> List[str]:
99
+ """Check template/.cursor/agents/*.mdc for forbidden slugs."""
100
+ violations = []
101
+ agents_dir = repo_root / "template" / ".cursor" / "agents"
102
+
103
+ if not agents_dir.exists():
104
+ return violations
105
+
106
+ for agent_file in agents_dir.glob("*.mdc"):
107
+ violations.extend(check_forbidden_slugs_in_file(agent_file))
108
+
109
+ return violations
110
+
111
+
112
+ def parse_scratchpad_keys(scratchpad_path: Path) -> Dict[str, str]:
113
+ """Parse key=value lines from scratchpad (skip comments)."""
114
+ result: Dict[str, str] = {}
115
+ if not scratchpad_path.exists():
116
+ return result
117
+ for line in scratchpad_path.read_text(encoding="utf-8").split("\n"):
118
+ stripped = line.strip()
119
+ if not stripped or stripped.startswith("#"):
120
+ continue
121
+ if "=" in stripped:
122
+ key, value = stripped.split("=", 1)
123
+ result[key.strip()] = value.strip()
124
+ return result
125
+
126
+
127
+ def validate_catalog(catalog_path: Path) -> Tuple[bool, List[str], ReasonCode]:
128
+ """Validate catalog schema and return list of errors."""
129
+ errors: List[str] = []
130
+
131
+ if not catalog_path.exists():
132
+ errors.append(f"Catalog file not found: {catalog_path}")
133
+ return False, errors, ReasonCode.MODEL_CATALOG_INVALID
134
+
135
+ is_valid, error_msg = validate_catalog_schema(catalog_path)
136
+ if not is_valid:
137
+ schema_version = None
138
+ try:
139
+ with open(catalog_path, "r", encoding="utf-8") as f:
140
+ data = json.load(f)
141
+ schema_version = data.get("schema_version")
142
+ except (json.JSONDecodeError, OSError):
143
+ pass
144
+ code = catalog_validation_reason_code(
145
+ error_msg,
146
+ {"schema_version": schema_version} if schema_version else None,
147
+ )
148
+ errors.append(error_msg or "Catalog validation failed")
149
+ return False, errors, code
150
+
151
+ with open(catalog_path, "r", encoding="utf-8") as f:
152
+ catalog = json.load(f)
153
+
154
+ for tier_name, slug in catalog["tiers"].items():
155
+ if not slug.strip():
156
+ errors.append(f"Tier '{tier_name}' has empty slug")
157
+
158
+ if catalog.get("schema_version") == 2 and "roles" in catalog:
159
+ for role_name in CATALOG_ROLE_KEYS:
160
+ if role_name not in catalog["roles"]:
161
+ errors.append(f"Missing role key: {role_name}")
162
+ elif not catalog["roles"][role_name].strip():
163
+ errors.append(f"Role '{role_name}' has empty slug")
164
+
165
+ code = (
166
+ ReasonCode.MODEL_CATALOG_SCHEMA_V2_INVALID
167
+ if catalog.get("schema_version") == 2
168
+ else ReasonCode.MODEL_CATALOG_INVALID
169
+ )
170
+ return len(errors) == 0, errors, code
171
+
172
+
173
+ def validate_scratchpad_tiers(scratchpad_path: Path) -> Tuple[bool, List[str]]:
174
+ """Validate MODEL_TIER_* keys in scratchpad file."""
175
+ errors: List[str] = []
176
+
177
+ if not scratchpad_path.exists():
178
+ return True, errors
179
+
180
+ content = scratchpad_path.read_text(encoding="utf-8")
181
+
182
+ for line in content.split("\n"):
183
+ line = line.strip()
184
+ if line.startswith("MODEL_TIER_") and "=" in line:
185
+ key, value = line.split("=", 1)
186
+ value = value.strip()
187
+
188
+ if key.startswith("#"):
189
+ continue
190
+
191
+ if value and not value.startswith("<"):
192
+ is_valid, error = validate_tier_enum(value)
193
+ if not is_valid:
194
+ errors.append(f"{key}={value}: {error}")
195
+
196
+ return len(errors) == 0, errors
197
+
198
+
199
+ def validate_scratchpad_direct_slugs(scratchpad_path: Path, catalog: dict | None) -> Tuple[bool, List[str]]:
200
+ """Validate MODEL_<PHASE> direct override keys."""
201
+ errors: List[str] = []
202
+ pad = parse_scratchpad_keys(scratchpad_path)
203
+ model_resolve = pad.get("MODEL_RESOLVE", "alias_only")
204
+
205
+ for phase_id in CANONICAL_PHASE_ID_SET:
206
+ key = phase_to_model_key(phase_id)
207
+ if key in pad and pad[key].strip() and not pad[key].startswith("<"):
208
+ slug = pad[key].strip()
209
+ is_valid, error = validate_direct_slug(slug, model_resolve, catalog)
210
+ if not is_valid:
211
+ errors.append(f"{key}={slug}: {error} [{ReasonCode.MODEL_OVERRIDE_SLUG_UNKNOWN.value}]")
212
+
213
+ return len(errors) == 0, errors
214
+
215
+
216
+ def run_precedence_self_test() -> List[str]:
217
+ """Precedence self-test hook (DEC-0087)."""
218
+ errors: List[str] = []
219
+
220
+ if len(PRECEDENCE_CHAIN_STEPS) != 5:
221
+ errors.append("PRECEDENCE_CHAIN_STEPS must have exactly 5 steps")
222
+
223
+ # Step 1 wins over tier
224
+ pad = {"MODEL_EXECUTE": "<test-slug>", "MODEL_TIER_EXECUTE": "cheap", "MODEL_RESOLVE": "alias_only"}
225
+ result = resolve_model_for_phase("execute", pad)
226
+ if not result.success or result.slug != "<test-slug>":
227
+ errors.append("Precedence self-test: MODEL_EXECUTE should win step 1")
228
+
229
+ # Tier-only backward compat: execute → strong → omit alias
230
+ result = resolve_model_for_phase("execute", {"MODEL_RESOLVE": "alias_only"})
231
+ if not result.success or result.tier != Tier.STRONG or result.alias is not None:
232
+ errors.append("Precedence self-test: tier-only execute should resolve to strong/omit")
233
+
234
+ return errors
235
+
236
+
237
+ def main():
238
+ parser = argparse.ArgumentParser(
239
+ description="Model tier validator (US-0101 / US-0102)",
240
+ formatter_class=argparse.RawDescriptionHelpFormatter,
241
+ epilog="""
242
+ Examples:
243
+ python scripts/model_tier_validate.py --repo .
244
+ python scripts/model_tier_validate.py --catalog .cursor/model-catalog.local.json
245
+ python scripts/model_tier_validate.py --check-template-agents
246
+ python scripts/model_tier_validate.py --enforce
247
+ """,
248
+ )
249
+
250
+ parser.add_argument("--repo", type=Path, help="Repository root (default: current directory)")
251
+ parser.add_argument("--catalog", type=Path, help="Path to local catalog")
252
+ parser.add_argument("--scratchpad", type=Path, help="Path to scratchpad file")
253
+ parser.add_argument("--check-template-agents", action="store_true", help="Check template agents for forbidden slugs")
254
+ parser.add_argument("--self-test", action="store_true", help="Run self-test (validate library contract)")
255
+ parser.add_argument("--enforce", action="store_true", help="Exit non-zero on any fail-closed code")
256
+
257
+ args = parser.parse_args()
258
+
259
+ repo_root = (args.repo or Path.cwd()).resolve()
260
+ all_errors: List[str] = []
261
+
262
+ if args.self_test:
263
+ print("[SELF-TEST] Validating model_tier_lib contract...")
264
+
265
+ for tier in Tier:
266
+ is_valid, error = validate_tier_enum(tier.value)
267
+ if not is_valid:
268
+ all_errors.append(f"Self-test failed: {error}")
269
+
270
+ for phase in CANONICAL_PHASE_ID_SET:
271
+ is_valid, error = validate_phase_key(phase)
272
+ if not is_valid:
273
+ all_errors.append(f"Self-test failed: {error}")
274
+
275
+ test_content = "model: composer-1"
276
+ for pattern in FORBIDDEN_SLUG_PATTERNS:
277
+ if not re.search(pattern, test_content, re.IGNORECASE):
278
+ all_errors.append(f"Self-test failed: pattern '{pattern}' not matching test content")
279
+
280
+ all_errors.extend(run_precedence_self_test())
281
+
282
+ for code in (
283
+ ReasonCode.MODEL_OVERRIDE_SLUG_UNKNOWN,
284
+ ReasonCode.MODEL_ROLE_SLUG_UNKNOWN,
285
+ ReasonCode.MODEL_CATALOG_SCHEMA_V2_INVALID,
286
+ ):
287
+ if code not in ReasonCode:
288
+ all_errors.append(f"Self-test failed: missing reason code {code.value}")
289
+
290
+ if all_errors:
291
+ print("[SELF_TEST_FAILED]")
292
+ for error in all_errors:
293
+ print(f" {error}", file=sys.stderr)
294
+ sys.exit(1)
295
+ else:
296
+ print("[DEV_ENVIRONMENT_SELF_TEST_OK]")
297
+ sys.exit(0)
298
+
299
+ catalog_dict = None
300
+
301
+ if args.catalog:
302
+ print(f"[CATALOG] Validating {args.catalog}...")
303
+ is_valid, errors, code = validate_catalog(args.catalog)
304
+ if not is_valid:
305
+ all_errors.extend(f"[{code.value}] {e}" for e in errors)
306
+ print(f"[CATALOG_INVALID] {args.catalog}", file=sys.stderr)
307
+ for error in errors:
308
+ print(f" [{code.value}] {error}", file=sys.stderr)
309
+ else:
310
+ with open(args.catalog, "r", encoding="utf-8") as f:
311
+ catalog_dict = json.load(f)
312
+
313
+ if args.scratchpad:
314
+ print(f"[SCRATCHPAD] Validating {args.scratchpad}...")
315
+ is_valid, errors = validate_scratchpad_tiers(args.scratchpad)
316
+ if not is_valid:
317
+ all_errors.extend(errors)
318
+ is_valid, errors = validate_scratchpad_direct_slugs(args.scratchpad, catalog_dict)
319
+ if not is_valid:
320
+ all_errors.extend(errors)
321
+
322
+ if args.check_template_agents:
323
+ print(f"[TEMPLATE] Checking {repo_root / 'template' / '.cursor' / 'agents'}...")
324
+ violations = check_template_agents(repo_root)
325
+ if violations:
326
+ all_errors.extend(violations)
327
+
328
+ if not args.catalog and not args.scratchpad and not args.check_template_agents:
329
+ print(f"[REPO] Validating {repo_root}...")
330
+
331
+ for example_name in (
332
+ "model-catalog.local.example.json",
333
+ "model-catalog.local.example.role-based-balanced.json",
334
+ "model-catalog.local.example.role-based-highend.json",
335
+ ):
336
+ catalog_example = repo_root / ".cursor" / example_name
337
+ if catalog_example.exists():
338
+ print(f"[CATALOG] Validating {catalog_example}...")
339
+ is_valid, errors, code = validate_catalog(catalog_example)
340
+ if not is_valid:
341
+ all_errors.extend(f"[{code.value}] {e}" for e in errors)
342
+
343
+ scratchpad = repo_root / ".cursor" / "scratchpad.md"
344
+ if scratchpad.exists():
345
+ print(f"[SCRATCHPAD] Validating {scratchpad}...")
346
+ is_valid, errors = validate_scratchpad_tiers(scratchpad)
347
+ if not is_valid:
348
+ all_errors.extend(errors)
349
+
350
+ print(f"[TEMPLATE] Checking {repo_root / 'template' / '.cursor' / 'agents'}...")
351
+ violations = check_template_agents(repo_root)
352
+ if violations:
353
+ all_errors.extend(violations)
354
+
355
+ all_errors.extend(run_precedence_self_test())
356
+
357
+ if all_errors:
358
+ print(f"\n[MODEL_TIER_VALIDATION_FAILED] {len(all_errors)} error(s)", file=sys.stderr)
359
+ for error in all_errors:
360
+ print(f" {error}", file=sys.stderr)
361
+ sys.exit(1 if args.enforce or True else 0)
362
+ else:
363
+ print("\n[MODEL_TIER_VALIDATION_OK]")
364
+ sys.exit(0)
365
+
366
+
367
+ if __name__ == "__main__":
368
+ main()