HumboldtJoker commited on
Commit
9994a58
·
verified ·
1 Parent(s): 879085f

Clean up nested duplicate: v2/v2/skills/discipline_gate.py

Browse files
Files changed (1) hide show
  1. v2/v2/skills/discipline_gate.py +0 -280
v2/v2/skills/discipline_gate.py DELETED
@@ -1,280 +0,0 @@
1
- """Discipline gate — a Kintsugi skill, not a regex filter.
2
-
3
- Last node of every plan. Nothing reaches the user without passing here.
4
-
5
- Three layers, in order of authority:
6
-
7
- 1. BELIEF CHECKS — the gate holds beliefs (from the BDI store) about
8
- the shared-db constraint, the auth architecture, and the audit
9
- findings. The draft is checked against each. A belief with
10
- confidence 1.0 (operator constraint) produces a BLOCK on violation;
11
- lower-confidence beliefs produce WARNs citing their source.
12
- 2. PATTERN CHECKS — the code-level regex layer (destructive SQL,
13
- signature bypass shapes, transaction gaps). Code, so it cannot be
14
- argued with.
15
- 3. CONFIDENCE GRADING — the gate grades how much of the answer is
16
- actually evidenced: source files read this session → HIGH; packs/
17
- architecture map only → MEDIUM; neither → LOW. Failed verification
18
- caps at LOW; no verification of generated code caps at MEDIUM.
19
-
20
- The gate's output is the final artifact: the (possibly blocked) answer
21
- plus machine-readable flags for the API response.
22
- """
23
-
24
- import re
25
- from dataclasses import dataclass, field
26
- from enum import Enum
27
-
28
- from kintsugi_core import (
29
- BaseSkillChip,
30
- BDIStore,
31
- EFEWeights,
32
- SkillContext,
33
- SkillDomain,
34
- SkillRequest,
35
- SkillResponse,
36
- )
37
- from skills.migration_safety import classify_sql, extract_sql
38
- from skills.security_review import (
39
- findings_relevant_to,
40
- recurring_pattern_hits,
41
- touches_auth,
42
- )
43
-
44
-
45
- class Confidence(str, Enum):
46
- HIGH = "HIGH" # claims backed by source read this session, verified
47
- MEDIUM = "MEDIUM" # backed by architecture map / audit / packs
48
- LOW = "LOW" # reasoning from architecture alone, or verification failed
49
-
50
-
51
- class Severity(str, Enum):
52
- BLOCK = "BLOCK"
53
- WARN = "WARN"
54
- INFO = "INFO"
55
-
56
-
57
- @dataclass
58
- class GateVerdict:
59
- passed: bool = True
60
- confidence: Confidence = Confidence.LOW
61
- flags: list = field(default_factory=list)
62
- requires_review: list = field(default_factory=list)
63
- beliefs_consulted: list = field(default_factory=list)
64
-
65
- def add(self, severity: Severity, message: str, belief_id: str = ""):
66
- self.flags.append({
67
- "severity": severity.value, "message": message,
68
- "belief": belief_id,
69
- })
70
- if severity == Severity.BLOCK:
71
- self.passed = False
72
-
73
- def format_annotations(self) -> str:
74
- lines = []
75
- if self.flags:
76
- lines.append("**Discipline Gate:**")
77
- icons = {"BLOCK": "[BLOCK]", "WARN": "[WARN]", "INFO": "[info]"}
78
- for f in self.flags:
79
- src = f" (belief: {f['belief']})" if f["belief"] else ""
80
- lines.append(f"- {icons[f['severity']]} {f['message']}{src}")
81
- if self.requires_review:
82
- lines.append(f"- Review required from: "
83
- f"{', '.join(sorted(set(self.requires_review)))}")
84
- lines.append(f"- Confidence: **{self.confidence.value}**")
85
- return "\n".join(lines)
86
-
87
-
88
- class DisciplineGateChip(BaseSkillChip):
89
- name = "discipline_gate"
90
- description = "Belief-checked final gate with earned confidence"
91
- version = "2.0.0"
92
- domain = SkillDomain.SECURITY
93
- efe_weights = EFEWeights(
94
- mission_alignment=0.20, stakeholder_benefit=0.30,
95
- resource_efficiency=0.05, transparency=0.30, equity=0.15,
96
- )
97
- consensus_actions = ["release_blocked_answer"]
98
-
99
- def __init__(self, bdi: BDIStore):
100
- super().__init__()
101
- self.bdi = bdi
102
-
103
- async def handle(self, request: SkillRequest,
104
- context: SkillContext) -> SkillResponse:
105
- question = context.metadata.get("question", request.raw_input)
106
- session = context.metadata.get("session")
107
-
108
- draft_artifact = request.parameters.get("draft") or {}
109
- draft = draft_artifact.get("text", "") if isinstance(draft_artifact, dict) else str(draft_artifact)
110
- analysis = request.parameters.get("analysis") or {}
111
- migration_report = request.parameters.get("migration_report") or {}
112
- security_report = request.parameters.get("security_report") or {}
113
- verification = request.parameters.get("verification") or {}
114
-
115
- verdict = GateVerdict()
116
-
117
- if not draft:
118
- verdict.add(Severity.BLOCK, "synthesis produced no draft")
119
- return self._respond(verdict, draft, question)
120
-
121
- self._check_shared_db(verdict, draft)
122
- self._check_auth(verdict, draft, question, security_report)
123
- self._check_audit_patterns(verdict, draft)
124
- self._grade_confidence(
125
- verdict, draft, session, analysis, verification, draft_artifact,
126
- )
127
-
128
- return self._respond(verdict, draft, question)
129
-
130
- # ------------------------------------------------------------------
131
- # Layer 1+2: belief-backed checks (patterns cited to beliefs)
132
- # ------------------------------------------------------------------
133
-
134
- def _check_shared_db(self, verdict: GateVerdict, draft: str) -> None:
135
- belief = self.bdi.get_belief("belief_constraint_shared_db")
136
- belief_id = belief.id if belief else ""
137
- if belief:
138
- verdict.beliefs_consulted.append(belief.id)
139
-
140
- for sql in extract_sql(draft):
141
- klass = classify_sql(sql)
142
- if klass["destructive"]:
143
- severity = (Severity.BLOCK
144
- if belief and belief.confidence >= 1.0
145
- else Severity.WARN)
146
- verdict.add(
147
- severity,
148
- f"Draft contains destructive SQL "
149
- f"({', '.join(klass['violations'])}). "
150
- f"{belief.content if belief else 'Shared-db rule.'}",
151
- belief_id,
152
- )
153
-
154
- def _check_auth(self, verdict: GateVerdict, draft: str, question: str,
155
- security_report: dict) -> None:
156
- auth_hit = (touches_auth(draft) or touches_auth(question)
157
- or security_report.get("auth_touched", False))
158
- if not auth_hit:
159
- return
160
- belief = self.bdi.get_belief("belief_arch_auth_flow")
161
- if belief:
162
- verdict.beliefs_consulted.append(belief.id)
163
- verdict.add(
164
- Severity.WARN,
165
- "This touches authentication. Review with security before "
166
- "merging.",
167
- belief.id if belief else "",
168
- )
169
- verdict.requires_review.append("security")
170
-
171
- def _check_audit_patterns(self, verdict: GateVerdict, draft: str) -> None:
172
- for hit in findings_relevant_to(draft):
173
- if not hit["pattern_matched"]:
174
- continue
175
- belief_id = f"belief_audit_{hit['id'].lower()}"
176
- belief = self.bdi.get_belief(belief_id)
177
- if belief:
178
- verdict.beliefs_consulted.append(belief_id)
179
- severity = (Severity.WARN
180
- if hit["severity"] in ("CRITICAL", "HIGH")
181
- else Severity.INFO)
182
- verdict.add(
183
- severity,
184
- f"Draft walks into audit finding {hit['id']} "
185
- f"({hit['title']}). {hit['advice']}",
186
- belief_id,
187
- )
188
- for hit in recurring_pattern_hits(draft):
189
- verdict.add(Severity.INFO, hit["message"])
190
-
191
- # ------------------------------------------------------------------
192
- # Layer 3: earned confidence
193
- # ------------------------------------------------------------------
194
-
195
- def _grade_confidence(self, verdict: GateVerdict, draft: str, session,
196
- analysis: dict, verification: dict,
197
- draft_artifact: dict) -> None:
198
- files_read = set(session.files_read) if session else set()
199
- files_cited = set(re.findall(
200
- r"\b((?:server|client|shared|design-system)/[\w./-]+\.\w+)\b",
201
- draft,
202
- ))
203
-
204
- has_source = bool(files_read)
205
- cites_unread = files_cited - files_read
206
- has_knowledge = bool(
207
- draft_artifact.get("packs_used")
208
- or analysis.get("repo_available") is False
209
- or self.bdi.list_beliefs()
210
- )
211
-
212
- checks = verification.get("checks", []) if verification else []
213
- ran = [c for c in checks if c.get("available")]
214
- failed = [c for c in ran if not c.get("passed")]
215
- has_code = bool(re.search(r"```\w*\n.{10,}?```", draft, re.DOTALL))
216
-
217
- if failed:
218
- confidence = Confidence.LOW
219
- verdict.add(
220
- Severity.WARN,
221
- f"Generated code FAILED verification "
222
- f"({len(failed)}/{len(ran)} checks): "
223
- f"{failed[0]['output'][:200]}",
224
- )
225
- elif has_source and (not has_code or ran):
226
- confidence = Confidence.HIGH
227
- elif has_source or has_knowledge:
228
- confidence = Confidence.MEDIUM
229
- if has_code and not ran:
230
- verdict.add(
231
- Severity.INFO,
232
- "Code in this answer was not verified (tsc/tests "
233
- "unavailable here) — treat as unchecked.",
234
- )
235
- else:
236
- confidence = Confidence.LOW
237
-
238
- if cites_unread:
239
- if confidence == Confidence.HIGH:
240
- confidence = Confidence.MEDIUM
241
- verdict.add(
242
- Severity.INFO,
243
- f"Answer references files not read this session: "
244
- f"{', '.join(sorted(cites_unread)[:5])} — reasoning from "
245
- f"architecture for those.",
246
- )
247
-
248
- verdict.confidence = confidence
249
-
250
- # ------------------------------------------------------------------
251
-
252
- def _respond(self, verdict: GateVerdict, draft: str,
253
- question: str) -> SkillResponse:
254
- annotations = verdict.format_annotations()
255
- if verdict.passed:
256
- final_text = f"{draft}\n\n---\n{annotations}"
257
- else:
258
- final_text = (
259
- "**BLOCKED by Discipline Gate.**\n\n"
260
- "The generated answer violated a hard constraint and was "
261
- "withheld.\n\n" + annotations +
262
- "\n\nAsk for the safe alternative (e.g. an additive "
263
- "multi-step migration) and Rivet will produce it."
264
- )
265
- return SkillResponse(
266
- content=final_text,
267
- success=True,
268
- data={
269
- "text": final_text,
270
- "passed": verdict.passed,
271
- "confidence": verdict.confidence.value,
272
- "flags": verdict.flags,
273
- "requires_review": sorted(set(verdict.requires_review)),
274
- "beliefs_consulted": sorted(set(verdict.beliefs_consulted)),
275
- },
276
- requires_consensus=not verdict.passed,
277
- consensus_action=(
278
- "release_blocked_answer" if not verdict.passed else None
279
- ),
280
- )