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.
Files changed (98) hide show
  1. cfa/__init__.py +39 -0
  2. cfa/_lazy.py +39 -0
  3. cfa/adapters/__init__.py +104 -0
  4. cfa/adapters/autogen.py +19 -0
  5. cfa/adapters/crewai.py +19 -0
  6. cfa/adapters/dspy.py +19 -0
  7. cfa/adapters/langgraph.py +19 -0
  8. cfa/adapters/openai_agents.py +19 -0
  9. cfa/audit/__init__.py +15 -0
  10. cfa/audit/context.py +205 -0
  11. cfa/audit/hashing.py +41 -0
  12. cfa/audit/trail.py +194 -0
  13. cfa/backends/__init__.py +132 -0
  14. cfa/backends/dbt.py +338 -0
  15. cfa/backends/pyspark.py +240 -0
  16. cfa/backends/sql.py +270 -0
  17. cfa/behavior/__init__.py +49 -0
  18. cfa/behavior/llm.py +244 -0
  19. cfa/behavior/spec.py +235 -0
  20. cfa/behavior/systematizer.py +222 -0
  21. cfa/cli/__init__.py +296 -0
  22. cfa/cli/__main__.py +6 -0
  23. cfa/cli/_helpers.py +109 -0
  24. cfa/cli/core/__init__.py +0 -0
  25. cfa/cli/core/evaluate.py +72 -0
  26. cfa/cli/core/validate.py +29 -0
  27. cfa/cli/formatters.py +280 -0
  28. cfa/cli/governance/__init__.py +0 -0
  29. cfa/cli/governance/audit.py +65 -0
  30. cfa/cli/governance/catalog.py +28 -0
  31. cfa/cli/governance/policy.py +119 -0
  32. cfa/cli/governance/rules.py +42 -0
  33. cfa/cli/governance/signature.py +31 -0
  34. cfa/cli/infrastructure/__init__.py +0 -0
  35. cfa/cli/infrastructure/backend_list.py +24 -0
  36. cfa/cli/infrastructure/storage.py +87 -0
  37. cfa/cli/project/__init__.py +0 -0
  38. cfa/cli/project/init.py +73 -0
  39. cfa/cli/project/lifecycle.py +92 -0
  40. cfa/cli/project/status.py +75 -0
  41. cfa/cli/project/taxonomy.py +38 -0
  42. cfa/cli/reporting/__init__.py +0 -0
  43. cfa/cli/reporting/report.py +109 -0
  44. cfa/cli/reporting/serve.py +43 -0
  45. cfa/config.py +103 -0
  46. cfa/core/__init__.py +19 -0
  47. cfa/core/codegen.py +65 -0
  48. cfa/core/conditions.py +129 -0
  49. cfa/core/kernel.py +224 -0
  50. cfa/core/phases/__init__.py +0 -0
  51. cfa/core/phases/runner.py +477 -0
  52. cfa/core/planner.py +290 -0
  53. cfa/execution/__init__.py +12 -0
  54. cfa/execution/partial.py +339 -0
  55. cfa/execution/state_projection.py +216 -0
  56. cfa/governance/__init__.py +76 -0
  57. cfa/lifecycle/__init__.py +51 -0
  58. cfa/mcp/__init__.py +347 -0
  59. cfa/mcp/__main__.py +4 -0
  60. cfa/normalizer/__init__.py +15 -0
  61. cfa/normalizer/base.py +441 -0
  62. cfa/normalizer/llm.py +426 -0
  63. cfa/observability/__init__.py +14 -0
  64. cfa/observability/indices.py +177 -0
  65. cfa/observability/metrics.py +91 -0
  66. cfa/observability/notify.py +79 -0
  67. cfa/observability/otel.py +81 -0
  68. cfa/observability/promotion.py +367 -0
  69. cfa/policy/__init__.py +12 -0
  70. cfa/policy/bundle.py +317 -0
  71. cfa/policy/catalog.py +117 -0
  72. cfa/policy/engine.py +306 -0
  73. cfa/reporting/__init__.py +42 -0
  74. cfa/reporting/charts.py +223 -0
  75. cfa/reporting/engine.py +456 -0
  76. cfa/resolution/__init__.py +62 -0
  77. cfa/runtime/__init__.py +13 -0
  78. cfa/runtime/gate.py +287 -0
  79. cfa/sandbox/__init__.py +189 -0
  80. cfa/sandbox/executor.py +92 -0
  81. cfa/sandbox/mock.py +89 -0
  82. cfa/sandbox/panic.py +52 -0
  83. cfa/storage/__init__.py +591 -0
  84. cfa/testing/__init__.py +60 -0
  85. cfa/testing/asserts.py +77 -0
  86. cfa/testing/evaluate.py +168 -0
  87. cfa/testing/fixtures.py +89 -0
  88. cfa/testing/markers.py +36 -0
  89. cfa/types.py +489 -0
  90. cfa/validation/__init__.py +14 -0
  91. cfa/validation/runtime.py +285 -0
  92. cfa/validation/signature.py +146 -0
  93. cfa/validation/static.py +252 -0
  94. cfa_kernel-0.1.0.dist-info/METADATA +32 -0
  95. cfa_kernel-0.1.0.dist-info/RECORD +98 -0
  96. cfa_kernel-0.1.0.dist-info/WHEEL +4 -0
  97. cfa_kernel-0.1.0.dist-info/entry_points.txt +3 -0
  98. 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
+ })