cfa-kernel 0.1.0__py3-none-any.whl
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.
- cfa/__init__.py +39 -0
- cfa/_lazy.py +39 -0
- cfa/adapters/__init__.py +104 -0
- cfa/adapters/autogen.py +19 -0
- cfa/adapters/crewai.py +19 -0
- cfa/adapters/dspy.py +19 -0
- cfa/adapters/langgraph.py +19 -0
- cfa/adapters/openai_agents.py +19 -0
- cfa/audit/__init__.py +15 -0
- cfa/audit/context.py +205 -0
- cfa/audit/hashing.py +41 -0
- cfa/audit/trail.py +194 -0
- cfa/backends/__init__.py +132 -0
- cfa/backends/dbt.py +338 -0
- cfa/backends/pyspark.py +240 -0
- cfa/backends/sql.py +270 -0
- cfa/behavior/__init__.py +49 -0
- cfa/behavior/llm.py +244 -0
- cfa/behavior/spec.py +235 -0
- cfa/behavior/systematizer.py +222 -0
- cfa/cli/__init__.py +296 -0
- cfa/cli/__main__.py +6 -0
- cfa/cli/_helpers.py +109 -0
- cfa/cli/core/__init__.py +0 -0
- cfa/cli/core/evaluate.py +72 -0
- cfa/cli/core/validate.py +29 -0
- cfa/cli/formatters.py +280 -0
- cfa/cli/governance/__init__.py +0 -0
- cfa/cli/governance/audit.py +65 -0
- cfa/cli/governance/catalog.py +28 -0
- cfa/cli/governance/policy.py +119 -0
- cfa/cli/governance/rules.py +42 -0
- cfa/cli/governance/signature.py +31 -0
- cfa/cli/infrastructure/__init__.py +0 -0
- cfa/cli/infrastructure/backend_list.py +24 -0
- cfa/cli/infrastructure/storage.py +87 -0
- cfa/cli/project/__init__.py +0 -0
- cfa/cli/project/init.py +73 -0
- cfa/cli/project/lifecycle.py +92 -0
- cfa/cli/project/status.py +75 -0
- cfa/cli/project/taxonomy.py +38 -0
- cfa/cli/reporting/__init__.py +0 -0
- cfa/cli/reporting/report.py +109 -0
- cfa/cli/reporting/serve.py +43 -0
- cfa/config.py +103 -0
- cfa/core/__init__.py +19 -0
- cfa/core/codegen.py +65 -0
- cfa/core/conditions.py +129 -0
- cfa/core/kernel.py +224 -0
- cfa/core/phases/__init__.py +0 -0
- cfa/core/phases/runner.py +477 -0
- cfa/core/planner.py +290 -0
- cfa/execution/__init__.py +12 -0
- cfa/execution/partial.py +339 -0
- cfa/execution/state_projection.py +216 -0
- cfa/governance/__init__.py +76 -0
- cfa/lifecycle/__init__.py +51 -0
- cfa/mcp/__init__.py +347 -0
- cfa/mcp/__main__.py +4 -0
- cfa/normalizer/__init__.py +15 -0
- cfa/normalizer/base.py +441 -0
- cfa/normalizer/llm.py +426 -0
- cfa/observability/__init__.py +14 -0
- cfa/observability/indices.py +177 -0
- cfa/observability/metrics.py +91 -0
- cfa/observability/notify.py +79 -0
- cfa/observability/otel.py +81 -0
- cfa/observability/promotion.py +367 -0
- cfa/policy/__init__.py +12 -0
- cfa/policy/bundle.py +317 -0
- cfa/policy/catalog.py +117 -0
- cfa/policy/engine.py +306 -0
- cfa/reporting/__init__.py +42 -0
- cfa/reporting/charts.py +223 -0
- cfa/reporting/engine.py +456 -0
- cfa/resolution/__init__.py +62 -0
- cfa/runtime/__init__.py +13 -0
- cfa/runtime/gate.py +287 -0
- cfa/sandbox/__init__.py +189 -0
- cfa/sandbox/executor.py +92 -0
- cfa/sandbox/mock.py +89 -0
- cfa/sandbox/panic.py +52 -0
- cfa/storage/__init__.py +591 -0
- cfa/testing/__init__.py +60 -0
- cfa/testing/asserts.py +77 -0
- cfa/testing/evaluate.py +168 -0
- cfa/testing/fixtures.py +89 -0
- cfa/testing/markers.py +36 -0
- cfa/types.py +489 -0
- cfa/validation/__init__.py +14 -0
- cfa/validation/runtime.py +285 -0
- cfa/validation/signature.py +146 -0
- cfa/validation/static.py +252 -0
- cfa_kernel-0.1.0.dist-info/METADATA +32 -0
- cfa_kernel-0.1.0.dist-info/RECORD +98 -0
- cfa_kernel-0.1.0.dist-info/WHEEL +4 -0
- cfa_kernel-0.1.0.dist-info/entry_points.txt +3 -0
- cfa_kernel-0.1.0.dist-info/licenses/LICENSE +21 -0
cfa/types.py
ADDED
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CFA Core Types
|
|
3
|
+
==============
|
|
4
|
+
Every intent, signature, decision and fault flows through these types.
|
|
5
|
+
Immutable after creation — any replanning generates new instances.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
import uuid
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from datetime import UTC, datetime
|
|
15
|
+
from enum import StrEnum
|
|
16
|
+
from typing import TYPE_CHECKING, Any
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from cfa.core.codegen import GeneratedCode
|
|
20
|
+
from cfa.core.planner import ExecutionPlan
|
|
21
|
+
from cfa.execution.partial import PartialExecutionState
|
|
22
|
+
from cfa.sandbox import SandboxResult
|
|
23
|
+
from cfa.validation.runtime import RuntimeValidationResult
|
|
24
|
+
from cfa.validation.static import StaticValidationResult
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _utcnow() -> datetime:
|
|
28
|
+
return datetime.now(UTC)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ── Enums ────────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DecisionState(StrEnum):
|
|
35
|
+
APPROVED = "approved"
|
|
36
|
+
APPROVED_WITH_WARNINGS = "approved_with_warnings"
|
|
37
|
+
REPLANNED = "replanned"
|
|
38
|
+
BLOCKED = "blocked"
|
|
39
|
+
PARTIALLY_COMMITTED = "partially_committed"
|
|
40
|
+
QUARANTINED = "quarantined"
|
|
41
|
+
ROLLED_BACK = "rolled_back"
|
|
42
|
+
PROMOTION_CANDIDATE = "promotion_candidate"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ConfirmationMode(StrEnum):
|
|
46
|
+
AUTO = "auto"
|
|
47
|
+
SOFT = "soft"
|
|
48
|
+
HARD = "hard"
|
|
49
|
+
HUMAN_ESCALATION = "human_escalation"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class AmbiguityLevel(StrEnum):
|
|
53
|
+
LOW = "low"
|
|
54
|
+
MEDIUM = "medium"
|
|
55
|
+
HIGH = "high"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class FaultFamily(StrEnum):
|
|
59
|
+
SEMANTIC = "semantic_faults"
|
|
60
|
+
STATIC = "static_safety_faults"
|
|
61
|
+
RUNTIME = "runtime_behavioral_faults"
|
|
62
|
+
ENVIRONMENT = "environmental_faults"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class FaultSeverity(StrEnum):
|
|
66
|
+
INFO = "info"
|
|
67
|
+
WARNING = "warning"
|
|
68
|
+
HIGH = "high"
|
|
69
|
+
CRITICAL = "critical"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class PolicyAction(StrEnum):
|
|
73
|
+
APPROVE = "approve"
|
|
74
|
+
REPLAN = "replan"
|
|
75
|
+
BLOCK = "block"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class DatasetClassification(StrEnum):
|
|
79
|
+
PUBLIC = "public"
|
|
80
|
+
INTERNAL = "internal"
|
|
81
|
+
SENSITIVE = "sensitive"
|
|
82
|
+
HIGH_VOLUME = "high_volume"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class TargetLayer(StrEnum):
|
|
86
|
+
BRONZE = "bronze"
|
|
87
|
+
SILVER = "silver"
|
|
88
|
+
GOLD = "gold"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ── Data types ───────────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass(frozen=True)
|
|
95
|
+
class DatasetRef:
|
|
96
|
+
"""Reference to a dataset with governance metadata."""
|
|
97
|
+
|
|
98
|
+
name: str
|
|
99
|
+
classification: DatasetClassification = DatasetClassification.INTERNAL
|
|
100
|
+
size_gb: float = 0.0
|
|
101
|
+
pii_columns: tuple[str, ...] = ()
|
|
102
|
+
partition_column: str | None = None
|
|
103
|
+
merge_keys: tuple[str, ...] = ()
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def contains_pii(self) -> bool:
|
|
107
|
+
return len(self.pii_columns) > 0
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def join_key(self) -> str:
|
|
111
|
+
return self.merge_keys[0] if self.merge_keys else "id"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass(frozen=True)
|
|
115
|
+
class ExecutionContext:
|
|
116
|
+
"""
|
|
117
|
+
Normative context of the execution.
|
|
118
|
+
Part of the Signature — guarantees reproducibility (Invariant I8).
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
policy_bundle_version: str
|
|
122
|
+
catalog_snapshot_version: str
|
|
123
|
+
context_registry_version_id: str
|
|
124
|
+
timestamp: datetime = field(default_factory=_utcnow)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass(frozen=True)
|
|
128
|
+
class SignatureConstraints:
|
|
129
|
+
"""Constraints declared in the State Signature."""
|
|
130
|
+
|
|
131
|
+
no_pii_raw: bool = True
|
|
132
|
+
merge_key_required: bool = True
|
|
133
|
+
enforce_types: bool = True
|
|
134
|
+
partition_by: tuple[str, ...] = ()
|
|
135
|
+
max_cost_dbu: float | None = None
|
|
136
|
+
custom: dict[str, Any] = field(default_factory=dict)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ── State Signature ──────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass(frozen=True)
|
|
143
|
+
class StateSignature:
|
|
144
|
+
"""
|
|
145
|
+
Formal contract of the intent.
|
|
146
|
+
Immutable after generation — any replanning generates a new Signature.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
domain: str
|
|
150
|
+
intent: str
|
|
151
|
+
target_layer: TargetLayer
|
|
152
|
+
datasets: tuple[DatasetRef, ...]
|
|
153
|
+
constraints: SignatureConstraints
|
|
154
|
+
execution_context: ExecutionContext
|
|
155
|
+
intent_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
156
|
+
created_at: datetime = field(default_factory=_utcnow)
|
|
157
|
+
source_intent_raw: str = ""
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def signature_hash(self) -> str:
|
|
161
|
+
payload = {
|
|
162
|
+
"domain": self.domain,
|
|
163
|
+
"intent": self.intent,
|
|
164
|
+
"target_layer": self.target_layer.value,
|
|
165
|
+
"datasets": sorted(d.name for d in self.datasets),
|
|
166
|
+
"constraints": {
|
|
167
|
+
"no_pii_raw": self.constraints.no_pii_raw,
|
|
168
|
+
"merge_key_required": self.constraints.merge_key_required,
|
|
169
|
+
"partition_by": sorted(self.constraints.partition_by),
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
content = json.dumps(payload, sort_keys=True)
|
|
173
|
+
return hashlib.sha256(content.encode()).hexdigest()
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def contains_pii(self) -> bool:
|
|
177
|
+
return any(d.contains_pii for d in self.datasets)
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def writes_to_protected_layer(self) -> bool:
|
|
181
|
+
return self.target_layer in (TargetLayer.SILVER, TargetLayer.GOLD)
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def target_dataset_name(self) -> str:
|
|
185
|
+
"""Deterministic target scope derived from the approved signature."""
|
|
186
|
+
layer = self.target_layer.value
|
|
187
|
+
if len(self.datasets) == 1:
|
|
188
|
+
return f"{layer}_{self.datasets[0].name}"
|
|
189
|
+
return f"{layer}_{self.domain}"
|
|
190
|
+
|
|
191
|
+
def with_constraints(self, **overrides: Any) -> StateSignature:
|
|
192
|
+
"""Return a new Signature with updated constraints (immutable update)."""
|
|
193
|
+
current = {
|
|
194
|
+
"no_pii_raw": self.constraints.no_pii_raw,
|
|
195
|
+
"merge_key_required": self.constraints.merge_key_required,
|
|
196
|
+
"enforce_types": self.constraints.enforce_types,
|
|
197
|
+
"partition_by": self.constraints.partition_by,
|
|
198
|
+
"max_cost_dbu": self.constraints.max_cost_dbu,
|
|
199
|
+
"custom": self.constraints.custom,
|
|
200
|
+
}
|
|
201
|
+
current.update(overrides)
|
|
202
|
+
new_constraints = SignatureConstraints(**current)
|
|
203
|
+
new_ctx = ExecutionContext(
|
|
204
|
+
policy_bundle_version=self.execution_context.policy_bundle_version,
|
|
205
|
+
catalog_snapshot_version=self.execution_context.catalog_snapshot_version,
|
|
206
|
+
context_registry_version_id=self.execution_context.context_registry_version_id,
|
|
207
|
+
)
|
|
208
|
+
return StateSignature(
|
|
209
|
+
domain=self.domain,
|
|
210
|
+
intent=self.intent,
|
|
211
|
+
target_layer=self.target_layer,
|
|
212
|
+
datasets=self.datasets,
|
|
213
|
+
constraints=new_constraints,
|
|
214
|
+
execution_context=new_ctx,
|
|
215
|
+
intent_id=self.intent_id,
|
|
216
|
+
source_intent_raw=self.source_intent_raw,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
def to_dict(self) -> dict[str, Any]:
|
|
220
|
+
return {
|
|
221
|
+
"domain": self.domain,
|
|
222
|
+
"intent": self.intent,
|
|
223
|
+
"target_layer": self.target_layer.value,
|
|
224
|
+
"datasets": [
|
|
225
|
+
{
|
|
226
|
+
"name": d.name,
|
|
227
|
+
"classification": d.classification.value,
|
|
228
|
+
"size_gb": d.size_gb,
|
|
229
|
+
"pii_columns": list(d.pii_columns),
|
|
230
|
+
"partition_column": d.partition_column,
|
|
231
|
+
"merge_keys": list(d.merge_keys),
|
|
232
|
+
}
|
|
233
|
+
for d in self.datasets
|
|
234
|
+
],
|
|
235
|
+
"constraints": {
|
|
236
|
+
"no_pii_raw": self.constraints.no_pii_raw,
|
|
237
|
+
"merge_key_required": self.constraints.merge_key_required,
|
|
238
|
+
"enforce_types": self.constraints.enforce_types,
|
|
239
|
+
"partition_by": list(self.constraints.partition_by),
|
|
240
|
+
"max_cost_dbu": self.constraints.max_cost_dbu,
|
|
241
|
+
"custom": self.constraints.custom,
|
|
242
|
+
},
|
|
243
|
+
"execution_context": {
|
|
244
|
+
"policy_bundle_version": self.execution_context.policy_bundle_version,
|
|
245
|
+
"catalog_snapshot_version": self.execution_context.catalog_snapshot_version,
|
|
246
|
+
"context_registry_version_id": self.execution_context.context_registry_version_id,
|
|
247
|
+
},
|
|
248
|
+
"intent_id": self.intent_id,
|
|
249
|
+
"source_intent_raw": self.source_intent_raw,
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
def to_json(self, indent: int = 2) -> str:
|
|
253
|
+
return json.dumps(self.to_dict(), indent=indent, ensure_ascii=False, default=str)
|
|
254
|
+
|
|
255
|
+
@classmethod
|
|
256
|
+
def from_dict(cls, data: dict[str, Any]) -> StateSignature:
|
|
257
|
+
layer_map = {"bronze": TargetLayer.BRONZE, "silver": TargetLayer.SILVER, "gold": TargetLayer.GOLD}
|
|
258
|
+
cls_map = {
|
|
259
|
+
"public": DatasetClassification.PUBLIC, "internal": DatasetClassification.INTERNAL,
|
|
260
|
+
"sensitive": DatasetClassification.SENSITIVE, "high_volume": DatasetClassification.HIGH_VOLUME,
|
|
261
|
+
}
|
|
262
|
+
datasets = tuple(
|
|
263
|
+
DatasetRef(
|
|
264
|
+
name=d["name"],
|
|
265
|
+
classification=cls_map.get(d.get("classification", "internal"), DatasetClassification.INTERNAL),
|
|
266
|
+
size_gb=d.get("size_gb", 0.0),
|
|
267
|
+
pii_columns=tuple(d.get("pii_columns", [])),
|
|
268
|
+
partition_column=d.get("partition_column"),
|
|
269
|
+
merge_keys=tuple(d.get("merge_keys", [])),
|
|
270
|
+
)
|
|
271
|
+
for d in data.get("datasets", [])
|
|
272
|
+
)
|
|
273
|
+
c = data.get("constraints", {})
|
|
274
|
+
constraints = SignatureConstraints(
|
|
275
|
+
no_pii_raw=c.get("no_pii_raw", True),
|
|
276
|
+
merge_key_required=c.get("merge_key_required", True),
|
|
277
|
+
enforce_types=c.get("enforce_types", True),
|
|
278
|
+
partition_by=tuple(c.get("partition_by", [])),
|
|
279
|
+
max_cost_dbu=c.get("max_cost_dbu"),
|
|
280
|
+
custom=c.get("custom", {}),
|
|
281
|
+
)
|
|
282
|
+
ctx = data.get("execution_context", {})
|
|
283
|
+
execution_context = ExecutionContext(
|
|
284
|
+
policy_bundle_version=ctx.get("policy_bundle_version", "unknown"),
|
|
285
|
+
catalog_snapshot_version=ctx.get("catalog_snapshot_version", "unknown"),
|
|
286
|
+
context_registry_version_id=ctx.get("context_registry_version_id", "unknown"),
|
|
287
|
+
)
|
|
288
|
+
return cls(
|
|
289
|
+
domain=data.get("domain", ""),
|
|
290
|
+
intent=data.get("intent", ""),
|
|
291
|
+
target_layer=layer_map.get(data.get("target_layer", "silver"), TargetLayer.SILVER),
|
|
292
|
+
datasets=datasets,
|
|
293
|
+
constraints=constraints,
|
|
294
|
+
execution_context=execution_context,
|
|
295
|
+
intent_id=data.get("intent_id", str(uuid.uuid4())),
|
|
296
|
+
source_intent_raw=data.get("source_intent_raw", ""),
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
@classmethod
|
|
300
|
+
def from_json(cls, json_str: str) -> StateSignature:
|
|
301
|
+
return cls.from_dict(json.loads(json_str))
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
# ── Semantic Resolution ──────────────────────────────────────────────────────
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
@dataclass
|
|
308
|
+
class SemanticResolution:
|
|
309
|
+
"""
|
|
310
|
+
Result of the Intent Normalizer's semantic resolution.
|
|
311
|
+
Feeds the Confirmation Orchestrator.
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
signature: StateSignature
|
|
315
|
+
confidence_score: float
|
|
316
|
+
ambiguity_level: AmbiguityLevel
|
|
317
|
+
competing_interpretations: list[str] = field(default_factory=list)
|
|
318
|
+
confirmation_mode: ConfirmationMode = ConfirmationMode.AUTO
|
|
319
|
+
environment_constraints_injected: list[str] = field(default_factory=list)
|
|
320
|
+
reasoning: str = ""
|
|
321
|
+
|
|
322
|
+
def __post_init__(self) -> None:
|
|
323
|
+
if self.confirmation_mode == ConfirmationMode.AUTO:
|
|
324
|
+
self.confirmation_mode = self._derive_confirmation_mode()
|
|
325
|
+
|
|
326
|
+
def _derive_confirmation_mode(self) -> ConfirmationMode:
|
|
327
|
+
sig = self.signature
|
|
328
|
+
if sig.target_layer == TargetLayer.GOLD:
|
|
329
|
+
return ConfirmationMode.HUMAN_ESCALATION
|
|
330
|
+
if self.confidence_score < 0.65 and sig.contains_pii:
|
|
331
|
+
return ConfirmationMode.HUMAN_ESCALATION
|
|
332
|
+
if len(self.competing_interpretations) > 1:
|
|
333
|
+
return ConfirmationMode.HUMAN_ESCALATION
|
|
334
|
+
if sig.writes_to_protected_layer and sig.contains_pii:
|
|
335
|
+
return ConfirmationMode.HARD
|
|
336
|
+
if self.confidence_score < 0.80 or self.ambiguity_level == AmbiguityLevel.MEDIUM:
|
|
337
|
+
return ConfirmationMode.SOFT
|
|
338
|
+
return ConfirmationMode.AUTO
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# ── Fault ────────────────────────────────────────────────────────────────────
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@dataclass(frozen=True)
|
|
345
|
+
class Fault:
|
|
346
|
+
"""
|
|
347
|
+
Governed failure event.
|
|
348
|
+
Errors in CFA are not exceptions — they are typed Faults.
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
code: str
|
|
352
|
+
family: FaultFamily
|
|
353
|
+
severity: FaultSeverity
|
|
354
|
+
stage: str
|
|
355
|
+
message: str
|
|
356
|
+
mandatory_action: PolicyAction
|
|
357
|
+
remediation: tuple[str, ...] = ()
|
|
358
|
+
detected_before_execution: bool = True
|
|
359
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
360
|
+
timestamp: datetime = field(default_factory=_utcnow)
|
|
361
|
+
|
|
362
|
+
def __str__(self) -> str:
|
|
363
|
+
return f"[{self.severity.value.upper()}] {self.code}: {self.message}"
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
# ── Policy Result ────────────────────────────────────────────────────────────
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
@dataclass
|
|
370
|
+
class PolicyResult:
|
|
371
|
+
"""Result of a Policy Engine evaluation."""
|
|
372
|
+
|
|
373
|
+
action: PolicyAction
|
|
374
|
+
faults: list[Fault] = field(default_factory=list)
|
|
375
|
+
interventions: list[str] = field(default_factory=list)
|
|
376
|
+
replan_count: int = 0
|
|
377
|
+
reasoning: str = ""
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def is_blocked(self) -> bool:
|
|
381
|
+
return self.action == PolicyAction.BLOCK
|
|
382
|
+
|
|
383
|
+
@property
|
|
384
|
+
def needs_replan(self) -> bool:
|
|
385
|
+
return self.action == PolicyAction.REPLAN
|
|
386
|
+
|
|
387
|
+
@property
|
|
388
|
+
def critical_faults(self) -> list[Fault]:
|
|
389
|
+
return [f for f in self.faults if f.severity == FaultSeverity.CRITICAL]
|
|
390
|
+
|
|
391
|
+
def to_dict(self) -> dict[str, Any]:
|
|
392
|
+
return {
|
|
393
|
+
"action": self.action.value,
|
|
394
|
+
"faults": [
|
|
395
|
+
{
|
|
396
|
+
"code": f.code, "family": f.family.value,
|
|
397
|
+
"severity": f.severity.value, "stage": f.stage,
|
|
398
|
+
"message": f.message, "mandatory_action": f.mandatory_action.value,
|
|
399
|
+
"remediation": list(f.remediation),
|
|
400
|
+
"detected_before_execution": f.detected_before_execution,
|
|
401
|
+
}
|
|
402
|
+
for f in self.faults
|
|
403
|
+
],
|
|
404
|
+
"interventions": self.interventions,
|
|
405
|
+
"replan_count": self.replan_count,
|
|
406
|
+
"reasoning": self.reasoning,
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
def to_json(self, indent: int = 2) -> str:
|
|
410
|
+
return json.dumps(self.to_dict(), indent=indent, ensure_ascii=False)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
# ── Kernel Result ────────────────────────────────────────────────────────────
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
@dataclass
|
|
417
|
+
class KernelResult:
|
|
418
|
+
"""
|
|
419
|
+
Final result of the Kernel Orchestrator for an intent.
|
|
420
|
+
Contains the decision state, the approved signature (if any),
|
|
421
|
+
and the complete trail of events for the Audit Trail.
|
|
422
|
+
"""
|
|
423
|
+
|
|
424
|
+
intent_id: str
|
|
425
|
+
state: DecisionState
|
|
426
|
+
signature: StateSignature | None = None
|
|
427
|
+
policy_result: PolicyResult | None = None
|
|
428
|
+
resolution: SemanticResolution | None = None
|
|
429
|
+
execution_plan: ExecutionPlan | None = None # type: ignore[name-defined]
|
|
430
|
+
generated_code: GeneratedCode | None = None # type: ignore[name-defined]
|
|
431
|
+
static_validation: StaticValidationResult | None = None # type: ignore[name-defined]
|
|
432
|
+
sandbox_result: SandboxResult | None = None # type: ignore[name-defined]
|
|
433
|
+
runtime_validation: RuntimeValidationResult | None = None # type: ignore[name-defined]
|
|
434
|
+
execution_state: PartialExecutionState | None = None # type: ignore[name-defined]
|
|
435
|
+
audit_events: list[dict[str, Any]] = field(default_factory=list)
|
|
436
|
+
blocked_reason: str = ""
|
|
437
|
+
replan_history: list[PolicyResult] = field(default_factory=list)
|
|
438
|
+
created_at: datetime = field(default_factory=_utcnow)
|
|
439
|
+
|
|
440
|
+
@property
|
|
441
|
+
def is_executable(self) -> bool:
|
|
442
|
+
return (
|
|
443
|
+
self.state in (DecisionState.APPROVED, DecisionState.APPROVED_WITH_WARNINGS)
|
|
444
|
+
and self.signature is not None
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
def add_event(self, stage: str, event_type: str, outcome: str, **kwargs: Any) -> None:
|
|
448
|
+
self.audit_events.append(
|
|
449
|
+
{
|
|
450
|
+
"stage": stage,
|
|
451
|
+
"event_type": event_type,
|
|
452
|
+
"outcome": outcome,
|
|
453
|
+
"timestamp": _utcnow().isoformat(),
|
|
454
|
+
"intent_id": self.intent_id,
|
|
455
|
+
**kwargs,
|
|
456
|
+
}
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
def summary(self) -> str:
|
|
460
|
+
lines = [
|
|
461
|
+
f"KernelResult [{self.intent_id[:8]}]",
|
|
462
|
+
f" state : {self.state.value}",
|
|
463
|
+
]
|
|
464
|
+
if self.signature:
|
|
465
|
+
lines.append(f" sig_hash : {self.signature.signature_hash}")
|
|
466
|
+
lines.append(f" domain : {self.signature.domain}")
|
|
467
|
+
if self.blocked_reason:
|
|
468
|
+
lines.append(f" blocked : {self.blocked_reason}")
|
|
469
|
+
if self.policy_result and self.policy_result.faults:
|
|
470
|
+
lines.append(f" faults : {[str(f) for f in self.policy_result.faults]}")
|
|
471
|
+
return "\n".join(lines)
|
|
472
|
+
|
|
473
|
+
def to_dict(self) -> dict[str, Any]:
|
|
474
|
+
d: dict[str, Any] = {
|
|
475
|
+
"intent_id": self.intent_id,
|
|
476
|
+
"state": self.state.value,
|
|
477
|
+
"blocked_reason": self.blocked_reason,
|
|
478
|
+
"replan_count": len(self.replan_history),
|
|
479
|
+
"audit_events": self.audit_events,
|
|
480
|
+
"created_at": self.created_at.isoformat(),
|
|
481
|
+
}
|
|
482
|
+
if self.signature:
|
|
483
|
+
d["signature"] = self.signature.to_dict()
|
|
484
|
+
if self.policy_result:
|
|
485
|
+
d["policy_result"] = self.policy_result.to_dict()
|
|
486
|
+
return d
|
|
487
|
+
|
|
488
|
+
def to_json(self, indent: int = 2) -> str:
|
|
489
|
+
return json.dumps(self.to_dict(), indent=indent, ensure_ascii=False, default=str)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""CFA Validation — static, runtime, signature."""
|
|
2
|
+
from cfa._lazy import LazyLoader
|
|
3
|
+
|
|
4
|
+
__getattr__ = LazyLoader({
|
|
5
|
+
"StaticValidator": ("cfa.validation.static", "StaticValidator"),
|
|
6
|
+
"StaticValidationResult": ("cfa.validation.static", "StaticValidationResult"),
|
|
7
|
+
"ForbiddenToken": ("cfa.validation.static", "ForbiddenToken"),
|
|
8
|
+
"RequiredPattern": ("cfa.validation.static", "RequiredPattern"),
|
|
9
|
+
"RuntimeValidator": ("cfa.validation.runtime", "RuntimeValidator"),
|
|
10
|
+
"RuntimeThresholds": ("cfa.validation.runtime", "RuntimeThresholds"),
|
|
11
|
+
"RuntimeValidationResult": ("cfa.validation.runtime", "RuntimeValidationResult"),
|
|
12
|
+
"validate_signature_data": ("cfa.validation.signature", "validate_signature_data"),
|
|
13
|
+
"unwrap_signature_data": ("cfa.validation.signature", "unwrap_signature_data"),
|
|
14
|
+
})
|