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.
- package/installer.ps1 +8 -0
- package/installer.py +8 -0
- package/installer.sh +1 -0
- package/package.json +1 -1
- package/scripts/check_intake_template_parity.py +62 -0
- package/template/.cursor/agents/curator.mdc +1 -0
- package/template/.cursor/agents/po.mdc +1 -0
- package/template/.cursor/agents/release.mdc +1 -0
- package/template/.cursor/commands/auto.md +90 -0
- package/template/.cursor/commands/execute.md +26 -0
- package/template/.cursor/commands/refresh-context.md +32 -0
- package/template/.cursor/commands/sovereign-critic.md +104 -0
- package/template/.cursor/model-catalog.local.example.cursor-only.json +9 -0
- package/template/.cursor/model-catalog.local.example.json +9 -0
- package/template/.cursor/model-catalog.local.example.level-1-easy.json +9 -0
- package/template/.cursor/model-catalog.local.example.level-2-complex.json +9 -0
- package/template/.cursor/model-catalog.local.example.level-3-mega.json +9 -0
- package/template/.cursor/model-catalog.local.example.level-4-super.json +9 -0
- package/template/.cursor/model-catalog.local.example.role-based-balanced.json +18 -0
- package/template/.cursor/model-catalog.local.example.role-based-highend.json +18 -0
- package/template/.cursor/rules/sovereign-role-manifest.mdc.example +27 -0
- package/template/.cursor/scratchpad.local.example.md +55 -0
- package/template/.cursor/scratchpad.md +203 -0
- package/template/.cursor/sovereign-role-manifest.yaml.example +53 -0
- package/template/decisions/DEC-0104.md +279 -0
- package/template/decisions/DEC-0105.md +231 -0
- package/template/decisions/DEC-0107.md +246 -0
- package/template/docs/engineering/architecture.md +78 -0
- package/template/docs/engineering/auto-orchestration-reference.md +7 -0
- package/template/docs/engineering/context/installer-owned-paths.manifest +8 -0
- package/template/docs/engineering/reason_codes.md +411 -0
- package/template/docs/engineering/runbook.md +1119 -0
- package/template/docs/engineering/sovereign-memory/.gitkeep +0 -0
- package/template/docs/engineering/sovereign-memory/retrospectives/.gitkeep +0 -0
- package/template/handoffs/sovereign_decisions/.gitkeep +0 -0
- package/template/handoffs/sovereign_deferrals/.gitkeep +0 -0
- package/template/handoffs/sovereign_role_reviews.jsonl +0 -0
- package/template/scripts/__pycache__/decision_ledger_lib.cpython-312.pyc +0 -0
- package/template/scripts/check_intake_template_parity.py +181 -0
- package/template/scripts/decision_ledger_lib.py +732 -0
- package/template/scripts/ledger_validate.py +153 -0
- package/template/scripts/model_tier_lib.py +680 -0
- package/template/scripts/model_tier_validate.py +368 -0
- package/template/scripts/parallel_dev_arbiter.py +923 -0
- package/template/scripts/release_trigger_adapters.py +843 -0
- package/template/scripts/self_healing_deploy_lib.py +463 -0
- package/template/scripts/self_healing_deploy_validate.py +78 -0
- package/template/scripts/sovereign_convergence_lib.py +994 -0
- package/template/scripts/sovereign_convergence_validate.py +206 -0
- package/template/scripts/sovereign_critic_lib.py +629 -0
- package/template/scripts/sovereign_critic_validate.py +131 -0
- package/template/scripts/sovereign_loop_lib.py +828 -0
- package/template/scripts/sovereign_loop_validate.py +122 -0
- package/template/scripts/sovereign_memory_lib.py +869 -0
- package/template/scripts/sovereign_memory_validate.py +153 -0
- package/template/scripts/sovereign_role_manifest_lib.py +547 -0
- package/template/scripts/sovereign_role_manifest_validate.py +105 -0
- package/template/tests/us0108_contract_test.py +207 -0
- package/template/tests/us0109_contract_test.py +209 -0
- package/template/tests/us0109_us0110_compose_test.py +34 -0
- package/template/tests/us0111_contract_test.py +345 -0
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Cross-model adversarial critic helper library (US-0104 / DEC-0104).
|
|
4
|
+
|
|
5
|
+
Reason codes (DEC-0104 §11):
|
|
6
|
+
CROSS_MODEL_REVIEW_DISABLED, CROSS_MODEL_CRITIC_SPAWN_FAILED,
|
|
7
|
+
CROSS_MODEL_MODEL_COLLISION, CROSS_MODEL_ANTISLOP_FAIL,
|
|
8
|
+
CROSS_MODEL_REWORK_CAP_EXHAUSTED, CROSS_MODEL_FINDINGS_INVALID,
|
|
9
|
+
CROSS_MODEL_RECONCILE_FAILED, CROSS_MODEL_DEGRADED_MODE,
|
|
10
|
+
CROSS_MODEL_CRITIC_MODEL_UNAVAILABLE, ISOLATION_EVIDENCE_MODEL_ID_MISSING
|
|
11
|
+
|
|
12
|
+
Default-off: CROSS_MODEL_REVIEW=0 → zero overhead.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import hashlib
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
import string
|
|
22
|
+
import sys
|
|
23
|
+
import uuid
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from datetime import datetime, timezone
|
|
26
|
+
from enum import Enum
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
29
|
+
|
|
30
|
+
_SCRIPT_DIR = Path(__file__).resolve().parent
|
|
31
|
+
if str(_SCRIPT_DIR) not in sys.path:
|
|
32
|
+
sys.path.insert(0, str(_SCRIPT_DIR))
|
|
33
|
+
|
|
34
|
+
from model_tier_lib import Tier, resolve_model_for_phase # noqa: E402
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# --- Scratchpad key contracts (DEC-0104 §1) ------------------------------------
|
|
38
|
+
|
|
39
|
+
CROSS_MODEL_REVIEW_KEY = "CROSS_MODEL_REVIEW"
|
|
40
|
+
CROSS_MODEL_ANTISLOP_THRESHOLD_KEY = "CROSS_MODEL_ANTISLOP_THRESHOLD"
|
|
41
|
+
CROSS_MODEL_REWORK_MAX_KEY = "CROSS_MODEL_REWORK_MAX"
|
|
42
|
+
|
|
43
|
+
CROSS_MODEL_REVIEW_VALUES = frozenset({"0", "1"})
|
|
44
|
+
CROSS_MODEL_REVIEW_DEFAULT = "0"
|
|
45
|
+
CROSS_MODEL_ANTISLOP_THRESHOLD_DEFAULT = 6
|
|
46
|
+
CROSS_MODEL_REWORK_MAX_DEFAULT = 2
|
|
47
|
+
|
|
48
|
+
LENS_VALUES = frozenset({"challenger", "architect", "subtractor"})
|
|
49
|
+
SEVERITY_VALUES = frozenset({"low", "medium", "high", "critical"})
|
|
50
|
+
CONFIDENCE_VALUES = frozenset({"low", "medium", "high"})
|
|
51
|
+
STATUS_VALUES = frozenset({"open", "resolved", "waived"})
|
|
52
|
+
|
|
53
|
+
FINDINGS_REL = "handoffs/sovereign_critic_findings.jsonl"
|
|
54
|
+
FINDINGS_PATH = Path(FINDINGS_REL)
|
|
55
|
+
SCHEMA_VERSION = 1
|
|
56
|
+
ISSUE_KEY_MAX_CHARS = 80
|
|
57
|
+
ISSUE_KEY_HEX_LEN = 16
|
|
58
|
+
|
|
59
|
+
FINDING_REQUIRED_FIELDS = frozenset({
|
|
60
|
+
"ts",
|
|
61
|
+
"orchestrator_run_id",
|
|
62
|
+
"phase_id",
|
|
63
|
+
"role",
|
|
64
|
+
"producer_model_id",
|
|
65
|
+
"critic_model_id",
|
|
66
|
+
"lens",
|
|
67
|
+
"finding_id",
|
|
68
|
+
"severity",
|
|
69
|
+
"confidence",
|
|
70
|
+
"anti_slop_score",
|
|
71
|
+
"finding_text",
|
|
72
|
+
"status",
|
|
73
|
+
"blocking",
|
|
74
|
+
"degraded_mode",
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
ISOLATION_EVIDENCE_BASE_FIELDS = frozenset({
|
|
78
|
+
"phase_id",
|
|
79
|
+
"role",
|
|
80
|
+
"fresh_context_marker",
|
|
81
|
+
"timestamp",
|
|
82
|
+
"evidence_ref",
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
LENS_CHECKLIST_KEYS = {
|
|
86
|
+
"challenger": (
|
|
87
|
+
"edge_case_cited",
|
|
88
|
+
"failure_mode_named",
|
|
89
|
+
"concurrency_considered",
|
|
90
|
+
"input_boundary_tested",
|
|
91
|
+
),
|
|
92
|
+
"architect": (
|
|
93
|
+
"coupling_named",
|
|
94
|
+
"layer_boundary_stated",
|
|
95
|
+
"dependency_direction_explicit",
|
|
96
|
+
"interface_contract_mentioned",
|
|
97
|
+
),
|
|
98
|
+
"subtractor": (
|
|
99
|
+
"unnecessary_abstraction_flagged",
|
|
100
|
+
"yagni_applied",
|
|
101
|
+
"premature_generalization_challenged",
|
|
102
|
+
"scope_creep_identified",
|
|
103
|
+
),
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
CRITIC_TIER_OPPOSITION = {
|
|
107
|
+
Tier.STRONG: Tier.CHEAP,
|
|
108
|
+
Tier.BALANCED: Tier.CHEAP,
|
|
109
|
+
Tier.CHEAP: Tier.STRONG,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ReasonCode(str, Enum):
|
|
114
|
+
CROSS_MODEL_REVIEW_DISABLED = "CROSS_MODEL_REVIEW_DISABLED"
|
|
115
|
+
CROSS_MODEL_CRITIC_SPAWN_FAILED = "CROSS_MODEL_CRITIC_SPAWN_FAILED"
|
|
116
|
+
CROSS_MODEL_MODEL_COLLISION = "CROSS_MODEL_MODEL_COLLISION"
|
|
117
|
+
CROSS_MODEL_ANTISLOP_FAIL = "CROSS_MODEL_ANTISLOP_FAIL"
|
|
118
|
+
CROSS_MODEL_REWORK_CAP_EXHAUSTED = "CROSS_MODEL_REWORK_CAP_EXHAUSTED"
|
|
119
|
+
CROSS_MODEL_FINDINGS_INVALID = "CROSS_MODEL_FINDINGS_INVALID"
|
|
120
|
+
CROSS_MODEL_RECONCILE_FAILED = "CROSS_MODEL_RECONCILE_FAILED"
|
|
121
|
+
CROSS_MODEL_DEGRADED_MODE = "CROSS_MODEL_DEGRADED_MODE"
|
|
122
|
+
CROSS_MODEL_CRITIC_MODEL_UNAVAILABLE = "CROSS_MODEL_CRITIC_MODEL_UNAVAILABLE"
|
|
123
|
+
ISOLATION_EVIDENCE_MODEL_ID_MISSING = "ISOLATION_EVIDENCE_MODEL_ID_MISSING"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
REASON_CODES = frozenset(code.value for code in ReasonCode)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@dataclass
|
|
130
|
+
class SelectCriticResult:
|
|
131
|
+
critic_model_id: str
|
|
132
|
+
degraded: bool
|
|
133
|
+
reason_code: Optional[ReasonCode] = None
|
|
134
|
+
producer_model_id: str = ""
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclass
|
|
138
|
+
class ReconciliationResult:
|
|
139
|
+
findings: List[dict] = field(default_factory=list)
|
|
140
|
+
agreement_groups: List[List[str]] = field(default_factory=list)
|
|
141
|
+
single_finder_flags: List[str] = field(default_factory=list)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _utc_now_iso() -> str:
|
|
145
|
+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _fsync_file(path: Path) -> None:
|
|
149
|
+
"""fsync a file; best-effort on Windows where some handles reject it."""
|
|
150
|
+
try:
|
|
151
|
+
fd = os.open(str(path), os.O_RDONLY)
|
|
152
|
+
try:
|
|
153
|
+
os.fsync(fd)
|
|
154
|
+
except OSError:
|
|
155
|
+
pass
|
|
156
|
+
finally:
|
|
157
|
+
os.close(fd)
|
|
158
|
+
except OSError:
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def parse_scratchpad_threshold(scratchpad: Optional[Dict[str, str]]) -> int:
|
|
163
|
+
pad = scratchpad or {}
|
|
164
|
+
raw = pad.get(CROSS_MODEL_ANTISLOP_THRESHOLD_KEY, str(CROSS_MODEL_ANTISLOP_THRESHOLD_DEFAULT))
|
|
165
|
+
try:
|
|
166
|
+
value = int(str(raw).strip())
|
|
167
|
+
except (TypeError, ValueError):
|
|
168
|
+
return CROSS_MODEL_ANTISLOP_THRESHOLD_DEFAULT
|
|
169
|
+
return max(0, min(10, value))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def parse_scratchpad_rework_max(scratchpad: Optional[Dict[str, str]]) -> int:
|
|
173
|
+
pad = scratchpad or {}
|
|
174
|
+
raw = pad.get(CROSS_MODEL_REWORK_MAX_KEY, str(CROSS_MODEL_REWORK_MAX_DEFAULT))
|
|
175
|
+
try:
|
|
176
|
+
value = int(str(raw).strip())
|
|
177
|
+
except (TypeError, ValueError):
|
|
178
|
+
return CROSS_MODEL_REWORK_MAX_DEFAULT
|
|
179
|
+
return max(0, value)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def check_isolation_model_id(
|
|
183
|
+
evidence: dict,
|
|
184
|
+
scratchpad: Optional[Dict[str, str]] = None,
|
|
185
|
+
) -> Tuple[bool, Optional[ReasonCode]]:
|
|
186
|
+
"""Fail-closed when critic enabled and model_id absent on isolation evidence."""
|
|
187
|
+
if not is_cross_model_review_enabled(scratchpad):
|
|
188
|
+
return True, None
|
|
189
|
+
model_id = evidence.get("model_id") if isinstance(evidence, dict) else None
|
|
190
|
+
if model_id is not None and str(model_id).strip():
|
|
191
|
+
return True, None
|
|
192
|
+
return False, ReasonCode.ISOLATION_EVIDENCE_MODEL_ID_MISSING
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def is_cross_model_review_enabled(scratchpad: Optional[Dict[str, str]]) -> bool:
|
|
196
|
+
if not scratchpad:
|
|
197
|
+
return False
|
|
198
|
+
return scratchpad.get(CROSS_MODEL_REVIEW_KEY, CROSS_MODEL_REVIEW_DEFAULT).strip() == "1"
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _normalize_model_token(model_id: str) -> str:
|
|
202
|
+
token = (model_id or "").strip().lower()
|
|
203
|
+
if token in ("", "inherit", "none", "omit"):
|
|
204
|
+
return "inherit"
|
|
205
|
+
if token == "fast":
|
|
206
|
+
return "fast"
|
|
207
|
+
return token
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _infer_producer_tier(producer_model_id: str) -> Tier:
|
|
211
|
+
norm = _normalize_model_token(producer_model_id)
|
|
212
|
+
if norm == "fast":
|
|
213
|
+
return Tier.CHEAP
|
|
214
|
+
if norm == "inherit":
|
|
215
|
+
return Tier.BALANCED
|
|
216
|
+
return Tier.STRONG
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _resolve_slug_for_tier(phase_id: str, tier: Tier, scratchpad: Dict[str, str]) -> str:
|
|
220
|
+
tier_key = f"MODEL_TIER_{phase_id.upper().replace('-', '_')}"
|
|
221
|
+
pad = dict(scratchpad)
|
|
222
|
+
pad[tier_key] = tier.value
|
|
223
|
+
pad.setdefault("MODEL_TIER_DEFAULT", tier.value)
|
|
224
|
+
result = resolve_model_for_phase(phase_id, pad)
|
|
225
|
+
if result.success:
|
|
226
|
+
if result.slug:
|
|
227
|
+
return result.slug
|
|
228
|
+
if result.alias:
|
|
229
|
+
return result.alias
|
|
230
|
+
return "inherit"
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def select_critic_model(
|
|
234
|
+
producer_model_id: str,
|
|
235
|
+
scratchpad: Optional[Dict[str, str]],
|
|
236
|
+
phase_id: str,
|
|
237
|
+
) -> SelectCriticResult:
|
|
238
|
+
pad = scratchpad or {}
|
|
239
|
+
producer = producer_model_id.strip()
|
|
240
|
+
if not producer:
|
|
241
|
+
resolved = resolve_model_for_phase(phase_id, pad)
|
|
242
|
+
if resolved.success:
|
|
243
|
+
producer = resolved.slug or resolved.alias or "inherit"
|
|
244
|
+
else:
|
|
245
|
+
producer = "inherit"
|
|
246
|
+
|
|
247
|
+
producer_tier = _infer_producer_tier(producer)
|
|
248
|
+
critic_tier = CRITIC_TIER_OPPOSITION.get(producer_tier, Tier.CHEAP)
|
|
249
|
+
critic = _resolve_slug_for_tier("sovereign-critic", critic_tier, pad)
|
|
250
|
+
|
|
251
|
+
if _normalize_model_token(critic) == _normalize_model_token(producer):
|
|
252
|
+
return SelectCriticResult(
|
|
253
|
+
critic_model_id=producer,
|
|
254
|
+
degraded=True,
|
|
255
|
+
reason_code=ReasonCode.CROSS_MODEL_DEGRADED_MODE,
|
|
256
|
+
producer_model_id=producer,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return SelectCriticResult(
|
|
260
|
+
critic_model_id=critic,
|
|
261
|
+
degraded=False,
|
|
262
|
+
reason_code=None,
|
|
263
|
+
producer_model_id=producer,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def compute_issue_key(finding_text: str) -> str:
|
|
268
|
+
lowered = finding_text.lower()
|
|
269
|
+
table = str.maketrans("", "", string.punctuation)
|
|
270
|
+
cleaned = lowered.translate(table)
|
|
271
|
+
collapsed = re.sub(r"\s+", " ", cleaned).strip()
|
|
272
|
+
if len(collapsed) > ISSUE_KEY_MAX_CHARS:
|
|
273
|
+
truncated = collapsed[:ISSUE_KEY_MAX_CHARS]
|
|
274
|
+
if " " in truncated:
|
|
275
|
+
truncated = truncated.rsplit(" ", 1)[0]
|
|
276
|
+
collapsed = truncated
|
|
277
|
+
digest = hashlib.sha256(collapsed.encode("utf-8")).hexdigest()[:ISSUE_KEY_HEX_LEN]
|
|
278
|
+
return f"ik_{digest}"
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def score_lens_antislop(lens: str, checklist_hits: Dict[str, bool]) -> int:
|
|
282
|
+
if lens not in LENS_CHECKLIST_KEYS:
|
|
283
|
+
return 0
|
|
284
|
+
keys = LENS_CHECKLIST_KEYS[lens]
|
|
285
|
+
hits = sum(1 for key in keys if checklist_hits.get(key))
|
|
286
|
+
score = int(round(hits * 2.5))
|
|
287
|
+
return max(0, min(10, score))
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def compute_anti_slop_aggregate(lens_scores: List[int]) -> int:
|
|
291
|
+
if not lens_scores:
|
|
292
|
+
return 0
|
|
293
|
+
return min(int(s) for s in lens_scores)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def reconcile_findings(raw_findings: List[dict]) -> ReconciliationResult:
|
|
297
|
+
if not raw_findings:
|
|
298
|
+
return ReconciliationResult()
|
|
299
|
+
|
|
300
|
+
keyed: Dict[str, List[dict]] = {}
|
|
301
|
+
for item in raw_findings:
|
|
302
|
+
text = str(item.get("finding_text", ""))
|
|
303
|
+
key = item.get("issue_key") or compute_issue_key(text)
|
|
304
|
+
keyed.setdefault(key, []).append({**item, "issue_key": key})
|
|
305
|
+
|
|
306
|
+
merged: List[dict] = []
|
|
307
|
+
agreement_groups: List[List[str]] = []
|
|
308
|
+
single_finder_flags: List[str] = []
|
|
309
|
+
|
|
310
|
+
for key, group in keyed.items():
|
|
311
|
+
lenses = {str(g.get("lens", "")) for g in group}
|
|
312
|
+
finding_ids = [str(g.get("finding_id", "")) for g in group if g.get("finding_id")]
|
|
313
|
+
if len(lenses) >= 2:
|
|
314
|
+
confidence = "high"
|
|
315
|
+
single_finder = False
|
|
316
|
+
agreement_groups.append(finding_ids)
|
|
317
|
+
else:
|
|
318
|
+
confidence = "medium"
|
|
319
|
+
single_finder = True
|
|
320
|
+
single_finder_flags.extend(finding_ids)
|
|
321
|
+
|
|
322
|
+
base = dict(group[0])
|
|
323
|
+
base["confidence"] = confidence
|
|
324
|
+
base["single_finder"] = single_finder
|
|
325
|
+
base["issue_key"] = key
|
|
326
|
+
merged.append(base)
|
|
327
|
+
|
|
328
|
+
return ReconciliationResult(
|
|
329
|
+
findings=merged,
|
|
330
|
+
agreement_groups=agreement_groups,
|
|
331
|
+
single_finder_flags=single_finder_flags,
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def schema_check(entry: dict) -> Tuple[bool, Optional[str]]:
|
|
336
|
+
if not isinstance(entry, dict):
|
|
337
|
+
return False, "entry must be object"
|
|
338
|
+
missing = FINDING_REQUIRED_FIELDS - set(entry.keys())
|
|
339
|
+
if missing:
|
|
340
|
+
return False, f"missing fields: {sorted(missing)}"
|
|
341
|
+
extra = set(entry.keys()) - FINDING_REQUIRED_FIELDS - {"issue_key", "single_finder", "rework_generation", "schema_version"}
|
|
342
|
+
if extra:
|
|
343
|
+
return False, f"unknown fields: {sorted(extra)}"
|
|
344
|
+
if entry["lens"] not in LENS_VALUES:
|
|
345
|
+
return False, f"invalid lens: {entry['lens']}"
|
|
346
|
+
if entry["severity"] not in SEVERITY_VALUES:
|
|
347
|
+
return False, f"invalid severity: {entry['severity']}"
|
|
348
|
+
if entry["confidence"] not in CONFIDENCE_VALUES:
|
|
349
|
+
return False, f"invalid confidence: {entry['confidence']}"
|
|
350
|
+
if entry["status"] not in STATUS_VALUES:
|
|
351
|
+
return False, f"invalid status: {entry['status']}"
|
|
352
|
+
score = entry["anti_slop_score"]
|
|
353
|
+
if not isinstance(score, int) or score < 0 or score > 10:
|
|
354
|
+
return False, "anti_slop_score must be int 0-10"
|
|
355
|
+
for bool_field in ("blocking", "degraded_mode"):
|
|
356
|
+
if not isinstance(entry[bool_field], bool):
|
|
357
|
+
return False, f"{bool_field} must be bool"
|
|
358
|
+
if not str(entry["finding_text"]).strip():
|
|
359
|
+
return False, "finding_text must be non-empty"
|
|
360
|
+
return True, None
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def append_finding(
|
|
364
|
+
path: Path,
|
|
365
|
+
entry: dict,
|
|
366
|
+
*,
|
|
367
|
+
scratchpad: Optional[Dict[str, str]] = None,
|
|
368
|
+
) -> Tuple[bool, Optional[ReasonCode]]:
|
|
369
|
+
if not is_cross_model_review_enabled(scratchpad):
|
|
370
|
+
return False, ReasonCode.CROSS_MODEL_REVIEW_DISABLED
|
|
371
|
+
ok, err = schema_check(entry)
|
|
372
|
+
if not ok:
|
|
373
|
+
return False, ReasonCode.CROSS_MODEL_FINDINGS_INVALID
|
|
374
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
375
|
+
line = json.dumps(entry, ensure_ascii=False) + "\n"
|
|
376
|
+
with path.open("a", encoding="utf-8", newline="\n") as handle:
|
|
377
|
+
handle.write(line)
|
|
378
|
+
handle.flush()
|
|
379
|
+
try:
|
|
380
|
+
os.fsync(handle.fileno())
|
|
381
|
+
except OSError:
|
|
382
|
+
_fsync_file(path)
|
|
383
|
+
return True, None
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def read_open_blocking(repo: Path) -> List[dict]:
|
|
387
|
+
path = repo / FINDINGS_PATH
|
|
388
|
+
if not path.is_file():
|
|
389
|
+
return []
|
|
390
|
+
open_rows: List[dict] = []
|
|
391
|
+
for raw in path.read_text(encoding="utf-8", errors="replace").splitlines():
|
|
392
|
+
if not raw.strip():
|
|
393
|
+
continue
|
|
394
|
+
try:
|
|
395
|
+
obj = json.loads(raw)
|
|
396
|
+
except json.JSONDecodeError:
|
|
397
|
+
continue
|
|
398
|
+
if obj.get("blocking") and obj.get("status") == "open":
|
|
399
|
+
open_rows.append(obj)
|
|
400
|
+
return open_rows
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def resolve_finding(path: Path, finding_id: str, status: str) -> bool:
|
|
404
|
+
if status not in ("resolved", "waived"):
|
|
405
|
+
return False
|
|
406
|
+
if not path.is_file():
|
|
407
|
+
return False
|
|
408
|
+
lines = path.read_text(encoding="utf-8", errors="replace").splitlines(keepends=True)
|
|
409
|
+
changed = False
|
|
410
|
+
out: List[str] = []
|
|
411
|
+
for raw in lines:
|
|
412
|
+
if not raw.strip():
|
|
413
|
+
out.append(raw)
|
|
414
|
+
continue
|
|
415
|
+
try:
|
|
416
|
+
obj = json.loads(raw)
|
|
417
|
+
except json.JSONDecodeError:
|
|
418
|
+
out.append(raw)
|
|
419
|
+
continue
|
|
420
|
+
if str(obj.get("finding_id")) == finding_id:
|
|
421
|
+
obj["status"] = status
|
|
422
|
+
changed = True
|
|
423
|
+
out.append(json.dumps(obj, ensure_ascii=False) + "\n")
|
|
424
|
+
else:
|
|
425
|
+
out.append(raw)
|
|
426
|
+
if changed:
|
|
427
|
+
path.write_text("".join(out), encoding="utf-8")
|
|
428
|
+
return changed
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def build_qa_cross_reviewer_block(repo: Path) -> dict:
|
|
432
|
+
rows = read_open_blocking(repo)
|
|
433
|
+
return {
|
|
434
|
+
"cross_reviewer_findings": [
|
|
435
|
+
{
|
|
436
|
+
"finding_id": row.get("finding_id"),
|
|
437
|
+
"lens": row.get("lens"),
|
|
438
|
+
"severity": row.get("severity"),
|
|
439
|
+
"confidence": row.get("confidence"),
|
|
440
|
+
"finding_text": str(row.get("finding_text", ""))[:200],
|
|
441
|
+
"blocking": row.get("blocking", False),
|
|
442
|
+
"degraded_mode": row.get("degraded_mode", False),
|
|
443
|
+
}
|
|
444
|
+
for row in rows
|
|
445
|
+
],
|
|
446
|
+
"open_blocking_count": len(rows),
|
|
447
|
+
"findings_path": FINDINGS_REL,
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def build_critic_evidence_block(
|
|
452
|
+
*,
|
|
453
|
+
scratchpad: Optional[Dict[str, str]] = None,
|
|
454
|
+
producer_model_id: str,
|
|
455
|
+
critic_model_id: str,
|
|
456
|
+
anti_slop_aggregate: int,
|
|
457
|
+
rework_generation: int = 0,
|
|
458
|
+
degraded_mode: bool = False,
|
|
459
|
+
) -> Optional[dict]:
|
|
460
|
+
"""Additive dev_to_qa evidence tuple when CROSS_MODEL_REVIEW=1."""
|
|
461
|
+
if not is_cross_model_review_enabled(scratchpad):
|
|
462
|
+
return None
|
|
463
|
+
return {
|
|
464
|
+
"producer_model_id": producer_model_id,
|
|
465
|
+
"critic_model_id": critic_model_id,
|
|
466
|
+
"anti_slop_aggregate": int(anti_slop_aggregate),
|
|
467
|
+
"rework_generation": int(rework_generation),
|
|
468
|
+
"degraded_mode": bool(degraded_mode),
|
|
469
|
+
"findings_path": FINDINGS_REL,
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def patch_ledger_cross_model_reviewed(
|
|
474
|
+
repo: Path,
|
|
475
|
+
orchestrator_run_id: str,
|
|
476
|
+
phase_id: str,
|
|
477
|
+
role: str,
|
|
478
|
+
*,
|
|
479
|
+
scratchpad: Optional[Dict[str, str]] = None,
|
|
480
|
+
producer_model_id: str = "",
|
|
481
|
+
critic_model_id: str = "",
|
|
482
|
+
) -> Tuple[bool, Optional[ReasonCode]]:
|
|
483
|
+
"""Append ledger entry with cross_model_reviewed=True when ledger enabled."""
|
|
484
|
+
from decision_ledger_lib import ( # noqa: WPS433
|
|
485
|
+
append_entry,
|
|
486
|
+
build_new_entry,
|
|
487
|
+
is_ledger_enabled,
|
|
488
|
+
resolve_ledger_path,
|
|
489
|
+
resolve_plan_fidelity,
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
if not is_ledger_enabled(scratchpad):
|
|
493
|
+
return True, None
|
|
494
|
+
|
|
495
|
+
ledger_path = resolve_ledger_path(orchestrator_run_id, repo)
|
|
496
|
+
fidelity = resolve_plan_fidelity(scratchpad).value
|
|
497
|
+
rationale = (
|
|
498
|
+
f"Cross-model critic review completed for {phase_id}/{role} "
|
|
499
|
+
f"(producer={producer_model_id or 'inherit'}, critic={critic_model_id or 'inherit'})."
|
|
500
|
+
)
|
|
501
|
+
entry = build_new_entry(
|
|
502
|
+
orchestrator_run_id=orchestrator_run_id,
|
|
503
|
+
phase_id=phase_id,
|
|
504
|
+
role=role,
|
|
505
|
+
decision_type="CROSS_MODEL_REVIEW",
|
|
506
|
+
rationale=rationale,
|
|
507
|
+
plan_fidelity=fidelity,
|
|
508
|
+
cross_model_reviewed=True,
|
|
509
|
+
risk_tier="medium",
|
|
510
|
+
)
|
|
511
|
+
result = append_entry(ledger_path, entry, scratchpad=scratchpad)
|
|
512
|
+
if not result.success:
|
|
513
|
+
return False, ReasonCode.CROSS_MODEL_FINDINGS_INVALID
|
|
514
|
+
return True, None
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def build_sample_finding(**overrides: Any) -> dict:
|
|
518
|
+
base = {
|
|
519
|
+
"ts": _utc_now_iso(),
|
|
520
|
+
"orchestrator_run_id": "self-test-run",
|
|
521
|
+
"phase_id": "research",
|
|
522
|
+
"role": "tech-lead",
|
|
523
|
+
"producer_model_id": "inherit",
|
|
524
|
+
"critic_model_id": "fast",
|
|
525
|
+
"lens": "challenger",
|
|
526
|
+
"finding_id": str(uuid.uuid4()),
|
|
527
|
+
"severity": "medium",
|
|
528
|
+
"confidence": "medium",
|
|
529
|
+
"anti_slop_score": 8,
|
|
530
|
+
"finding_text": "Missing null guard on boundary input path.",
|
|
531
|
+
"status": "open",
|
|
532
|
+
"blocking": False,
|
|
533
|
+
"degraded_mode": False,
|
|
534
|
+
}
|
|
535
|
+
base.update(overrides)
|
|
536
|
+
return base
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def self_test() -> bool:
|
|
540
|
+
errors: List[str] = []
|
|
541
|
+
|
|
542
|
+
if len(REASON_CODES) != 10:
|
|
543
|
+
errors.append(f"expected 10 reason codes, got {len(REASON_CODES)}")
|
|
544
|
+
|
|
545
|
+
if CROSS_MODEL_REVIEW_DEFAULT != "0":
|
|
546
|
+
errors.append("CROSS_MODEL_REVIEW_DEFAULT must be 0")
|
|
547
|
+
|
|
548
|
+
if is_cross_model_review_enabled({}) or is_cross_model_review_enabled(None):
|
|
549
|
+
errors.append("default scratchpad must disable critic")
|
|
550
|
+
|
|
551
|
+
if not is_cross_model_review_enabled({CROSS_MODEL_REVIEW_KEY: "1"}):
|
|
552
|
+
errors.append("CROSS_MODEL_REVIEW=1 must enable critic")
|
|
553
|
+
|
|
554
|
+
key_a = compute_issue_key("Race on concurrent append!")
|
|
555
|
+
key_b = compute_issue_key("race on concurrent append")
|
|
556
|
+
if key_a != key_b:
|
|
557
|
+
errors.append("issue_key normalization mismatch")
|
|
558
|
+
|
|
559
|
+
scores = [7, 9, 6]
|
|
560
|
+
if compute_anti_slop_aggregate(scores) != 6:
|
|
561
|
+
errors.append("aggregate must be min(lens_scores)")
|
|
562
|
+
|
|
563
|
+
rubric = score_lens_antislop(
|
|
564
|
+
"challenger",
|
|
565
|
+
{
|
|
566
|
+
"edge_case_cited": True,
|
|
567
|
+
"failure_mode_named": True,
|
|
568
|
+
"concurrency_considered": False,
|
|
569
|
+
"input_boundary_tested": False,
|
|
570
|
+
},
|
|
571
|
+
)
|
|
572
|
+
if rubric != 5:
|
|
573
|
+
errors.append(f"challenger rubric expected 5, got {rubric}")
|
|
574
|
+
|
|
575
|
+
raw = [
|
|
576
|
+
build_sample_finding(lens="challenger", finding_text="Shared coupling risk in module boundary"),
|
|
577
|
+
build_sample_finding(lens="architect", finding_text="Shared coupling risk in module boundary"),
|
|
578
|
+
build_sample_finding(lens="subtractor", finding_text="Unique over abstraction in helper layer"),
|
|
579
|
+
]
|
|
580
|
+
reconciled = reconcile_findings(raw)
|
|
581
|
+
if len(reconciled.agreement_groups) != 1:
|
|
582
|
+
errors.append("expected one agreement group")
|
|
583
|
+
if len(reconciled.single_finder_flags) != 1:
|
|
584
|
+
errors.append("expected one single-finder flag")
|
|
585
|
+
|
|
586
|
+
sample = build_sample_finding()
|
|
587
|
+
ok, err = schema_check(sample)
|
|
588
|
+
if not ok:
|
|
589
|
+
errors.append(f"schema_check valid sample failed: {err}")
|
|
590
|
+
|
|
591
|
+
bad, err = schema_check({"lens": "challenger"})
|
|
592
|
+
if bad:
|
|
593
|
+
errors.append("schema_check should reject incomplete entry")
|
|
594
|
+
|
|
595
|
+
select = select_critic_model("inherit", {CROSS_MODEL_REVIEW_KEY: "1"}, "execute")
|
|
596
|
+
if not select.critic_model_id:
|
|
597
|
+
errors.append("select_critic_model must return critic_model_id")
|
|
598
|
+
|
|
599
|
+
ok_model, code = check_isolation_model_id({"phase_id": "execute"}, {CROSS_MODEL_REVIEW_KEY: "0"})
|
|
600
|
+
if not ok_model or code is not None:
|
|
601
|
+
errors.append("model_id check must pass when critic disabled")
|
|
602
|
+
|
|
603
|
+
bad_model, bad_code = check_isolation_model_id({}, {CROSS_MODEL_REVIEW_KEY: "1"})
|
|
604
|
+
if bad_model or bad_code != ReasonCode.ISOLATION_EVIDENCE_MODEL_ID_MISSING:
|
|
605
|
+
errors.append("model_id check must fail-closed when critic enabled and model_id missing")
|
|
606
|
+
|
|
607
|
+
if len(ISOLATION_EVIDENCE_BASE_FIELDS) != 5:
|
|
608
|
+
errors.append("ISOLATION_EVIDENCE_BASE_FIELDS must have 5 base fields")
|
|
609
|
+
|
|
610
|
+
if errors:
|
|
611
|
+
for item in errors:
|
|
612
|
+
print(f" {item}", file=sys.stderr)
|
|
613
|
+
print("[SELF_TEST_FAILED]", file=sys.stderr)
|
|
614
|
+
return False
|
|
615
|
+
|
|
616
|
+
print("[SOVEREIGN_CRITIC_SELF_TEST_OK]")
|
|
617
|
+
return True
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
if __name__ == "__main__":
|
|
621
|
+
import argparse
|
|
622
|
+
|
|
623
|
+
parser = argparse.ArgumentParser(description="Sovereign critic library (US-0104 / DEC-0104)")
|
|
624
|
+
parser.add_argument("--self-test", action="store_true", help="Run self-test")
|
|
625
|
+
args = parser.parse_args()
|
|
626
|
+
if args.self_test:
|
|
627
|
+
sys.exit(0 if self_test() else 1)
|
|
628
|
+
parser.print_help()
|
|
629
|
+
sys.exit(2)
|