contractforge-ai 0.2.8__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 (112) hide show
  1. contractforge_ai/__init__.py +239 -0
  2. contractforge_ai/adapter_validation/__init__.py +14 -0
  3. contractforge_ai/adapter_validation/models.py +36 -0
  4. contractforge_ai/adapter_validation/registry.py +45 -0
  5. contractforge_ai/adapter_validation/validation.py +359 -0
  6. contractforge_ai/agentic/__init__.py +41 -0
  7. contractforge_ai/agentic/context.py +84 -0
  8. contractforge_ai/agentic/generate.py +1746 -0
  9. contractforge_ai/agentic/governance.py +331 -0
  10. contractforge_ai/agentic/intent.py +316 -0
  11. contractforge_ai/agentic/models.py +233 -0
  12. contractforge_ai/agentic/planning.py +62 -0
  13. contractforge_ai/agentic/transform.py +348 -0
  14. contractforge_ai/cli.py +43 -0
  15. contractforge_ai/cli_context_commands.py +83 -0
  16. contractforge_ai/cli_generation_helpers.py +83 -0
  17. contractforge_ai/cli_io.py +68 -0
  18. contractforge_ai/cli_onboarding_commands.py +135 -0
  19. contractforge_ai/cli_output.py +516 -0
  20. contractforge_ai/cli_parser.py +498 -0
  21. contractforge_ai/cli_payload.py +9 -0
  22. contractforge_ai/cli_project_generation.py +224 -0
  23. contractforge_ai/cli_prompt_evaluation.py +32 -0
  24. contractforge_ai/cli_validation_commands.py +236 -0
  25. contractforge_ai/cli_workflows.py +153 -0
  26. contractforge_ai/connectors.py +312 -0
  27. contractforge_ai/context/__init__.py +35 -0
  28. contractforge_ai/context/databricks.py +159 -0
  29. contractforge_ai/context/knowledge.py +309 -0
  30. contractforge_ai/context/loaders.py +30 -0
  31. contractforge_ai/context/project.py +294 -0
  32. contractforge_ai/context/redaction.py +44 -0
  33. contractforge_ai/enrichment/__init__.py +23 -0
  34. contractforge_ai/enrichment/core.py +395 -0
  35. contractforge_ai/evaluation/__init__.py +53 -0
  36. contractforge_ai/evaluation/enrichment_quality.py +354 -0
  37. contractforge_ai/evaluation/prompts.py +573 -0
  38. contractforge_ai/evaluation/provider_live.py +392 -0
  39. contractforge_ai/evaluation/structured.py +209 -0
  40. contractforge_ai/explainers/__init__.py +6 -0
  41. contractforge_ai/explainers/failure.py +267 -0
  42. contractforge_ai/generators/__init__.py +30 -0
  43. contractforge_ai/generators/contract.py +230 -0
  44. contractforge_ai/generators/environments.py +62 -0
  45. contractforge_ai/generators/metadata.py +343 -0
  46. contractforge_ai/generators/project.py +2314 -0
  47. contractforge_ai/generators/shape.py +179 -0
  48. contractforge_ai/generators/targets.py +39 -0
  49. contractforge_ai/integrations/__init__.py +1 -0
  50. contractforge_ai/integrations/contractforge_naming.py +34 -0
  51. contractforge_ai/intelligence/__init__.py +19 -0
  52. contractforge_ai/intelligence/critique.py +283 -0
  53. contractforge_ai/intelligence/routing.py +282 -0
  54. contractforge_ai/models.py +318 -0
  55. contractforge_ai/observability/__init__.py +17 -0
  56. contractforge_ai/observability/control_tables.py +783 -0
  57. contractforge_ai/onboarding/__init__.py +26 -0
  58. contractforge_ai/onboarding/agent_assets.py +274 -0
  59. contractforge_ai/onboarding/discovery.py +185 -0
  60. contractforge_ai/onboarding/init.py +234 -0
  61. contractforge_ai/onboarding/profiles.py +157 -0
  62. contractforge_ai/onboarding/provider_credentials.py +36 -0
  63. contractforge_ai/parity/__init__.py +11 -0
  64. contractforge_ai/parity/models.py +160 -0
  65. contractforge_ai/parity/platforms.py +135 -0
  66. contractforge_ai/planning/__init__.py +21 -0
  67. contractforge_ai/planning/platforms.py +24 -0
  68. contractforge_ai/planning/project.py +1018 -0
  69. contractforge_ai/planning/spec.py +519 -0
  70. contractforge_ai/project_structure/__init__.py +5 -0
  71. contractforge_ai/project_structure/findings.py +40 -0
  72. contractforge_ai/project_structure/io.py +44 -0
  73. contractforge_ai/project_structure/models.py +64 -0
  74. contractforge_ai/project_structure/validation.py +585 -0
  75. contractforge_ai/projects/__init__.py +21 -0
  76. contractforge_ai/projects/artifact_policy.py +60 -0
  77. contractforge_ai/projects/guided.py +793 -0
  78. contractforge_ai/projects/loaders.py +21 -0
  79. contractforge_ai/projects/models.py +202 -0
  80. contractforge_ai/projects/patching.py +175 -0
  81. contractforge_ai/projects/siblings.py +138 -0
  82. contractforge_ai/projects/writer.py +82 -0
  83. contractforge_ai/prompts/contract_reviewer.md +12 -0
  84. contractforge_ai/providers/__init__.py +71 -0
  85. contractforge_ai/providers/anthropic.py +141 -0
  86. contractforge_ai/providers/base.py +76 -0
  87. contractforge_ai/providers/bedrock.py +164 -0
  88. contractforge_ai/providers/capabilities.py +257 -0
  89. contractforge_ai/providers/databricks.py +153 -0
  90. contractforge_ai/providers/deepseek.py +129 -0
  91. contractforge_ai/providers/env.py +146 -0
  92. contractforge_ai/providers/factory.py +44 -0
  93. contractforge_ai/providers/gemini.py +174 -0
  94. contractforge_ai/providers/offline.py +21 -0
  95. contractforge_ai/providers/openai.py +200 -0
  96. contractforge_ai/providers/routing.py +399 -0
  97. contractforge_ai/reports.py +1982 -0
  98. contractforge_ai/reports_translation.py +278 -0
  99. contractforge_ai/reviewers/__init__.py +6 -0
  100. contractforge_ai/reviewers/architecture.py +126 -0
  101. contractforge_ai/reviewers/contract.py +413 -0
  102. contractforge_ai/reviewers/output.py +59 -0
  103. contractforge_ai/validation/__init__.py +25 -0
  104. contractforge_ai/validation/contractforge.py +176 -0
  105. contractforge_ai/validation/generated.py +307 -0
  106. contractforge_ai/validation/loop.py +522 -0
  107. contractforge_ai/write_modes.py +24 -0
  108. contractforge_ai-0.2.8.dist-info/METADATA +697 -0
  109. contractforge_ai-0.2.8.dist-info/RECORD +112 -0
  110. contractforge_ai-0.2.8.dist-info/WHEEL +5 -0
  111. contractforge_ai-0.2.8.dist-info/entry_points.txt +2 -0
  112. contractforge_ai-0.2.8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,239 @@
1
+ """AI-assisted tools for ContractForge."""
2
+
3
+ from contractforge_ai.agentic import (
4
+ ContextSnapshot,
5
+ GapPlan,
6
+ GenerationAuditEvent,
7
+ GenerationAuditTrail,
8
+ GenerationPolicyEngine,
9
+ GenerationPolicyFinding,
10
+ GenerationPolicyResult,
11
+ GenerationSignature,
12
+ IntentGenerationRequest,
13
+ IntentGenerationResult,
14
+ IntentSpec,
15
+ ProjectState,
16
+ TransformationPlan,
17
+ TransformationStep,
18
+ analyze_project_state,
19
+ generate_from_intent,
20
+ infer_transformation_plan,
21
+ interpret_intent,
22
+ plan_project_gaps,
23
+ )
24
+ from contractforge_ai.context import (
25
+ ContextFile,
26
+ KnowledgeChunk,
27
+ KnowledgeIndex,
28
+ KnowledgeSearchResult,
29
+ ProjectContextPackage,
30
+ build_knowledge_index,
31
+ build_project_context_package,
32
+ load_knowledge_index,
33
+ query_knowledge_index,
34
+ save_knowledge_index,
35
+ )
36
+ from contractforge_ai.connectors import (
37
+ ConnectorIntent,
38
+ connector_aliases,
39
+ connector_details,
40
+ connector_intent,
41
+ connector_message,
42
+ supported_connector_names,
43
+ )
44
+ from contractforge_ai.enrichment import (
45
+ EnrichmentResult,
46
+ enrich_adapter_validation,
47
+ enrich_control_table_analysis,
48
+ enrich_failure_explanation,
49
+ enrich_project_plan,
50
+ enrich_project_synthesis,
51
+ enrich_review_result,
52
+ )
53
+ from contractforge_ai.explainers.failure import explain_failure
54
+ from contractforge_ai.evaluation import (
55
+ EnrichmentQualityReport,
56
+ evaluate_enrichment_quality,
57
+ evaluate_prompt_cases,
58
+ list_prompt_templates,
59
+ render_prompt,
60
+ validate_model_output,
61
+ )
62
+ from contractforge_ai.generators.contract import generate_contract_draft
63
+ from contractforge_ai.generators.metadata import suggest_metadata
64
+ from contractforge_ai.generators.project import (
65
+ generate_aws_glue_iceberg_project,
66
+ generate_classic_pyspark_project,
67
+ generate_contractforge_python_project,
68
+ generate_contractforge_yaml_project,
69
+ generate_databricks_dab_project,
70
+ generate_dbt_project,
71
+ )
72
+ from contractforge_ai.generators.shape import suggest_shape
73
+ from contractforge_ai.intelligence import CritiqueFinding, CritiqueReport, TaskRouteRequest, TaskRoutingReport, critique_output, route_task
74
+ from contractforge_ai.models import (
75
+ Assumption,
76
+ ContractDraftResult,
77
+ EvidenceItem,
78
+ FailureExplanation,
79
+ Finding,
80
+ MetadataSuggestionResult,
81
+ RequiredDecision,
82
+ ReviewResult,
83
+ ShapeSuggestionResult,
84
+ Suggestion,
85
+ Traceability,
86
+ ValidationResult,
87
+ confidence_level,
88
+ )
89
+ from contractforge_ai.onboarding import AgentInstructionRequest, generate_agent_instruction_plan
90
+ from contractforge_ai.observability import ControlTableAnalysis, ControlTableEvidencePackage, analyze_control_tables
91
+ from contractforge_ai.planning import (
92
+ ProjectIntent,
93
+ ProjectPlannerRequest,
94
+ ProjectPlannerResult,
95
+ ProjectRecommendation,
96
+ plan_project_from_intent,
97
+ )
98
+ from contractforge_ai.projects import (
99
+ ArtifactWriteResult,
100
+ DecisionReport,
101
+ ProjectArtifact,
102
+ ProjectPlan,
103
+ load_project_plan,
104
+ write_project_plan,
105
+ )
106
+ from contractforge_ai.projects.guided import GuidedProjectRequest, GuidedProjectResult, generate_guided_project
107
+ from contractforge_ai.reports import (
108
+ RenderedReport,
109
+ render_guided_project_review,
110
+ render_markdown_report,
111
+ render_operational_analysis_review,
112
+ )
113
+ from contractforge_ai.reviewers.contract import review_contract
114
+ from contractforge_ai.reviewers.architecture import ArchitectureConceptFinding, ArchitectureReview, review_governed_architecture
115
+ from contractforge_ai.validation import (
116
+ DeterministicValidationCheck,
117
+ DeterministicValidationReport,
118
+ validate_contract_artifact,
119
+ validate_generated_contract,
120
+ validate_model_artifact,
121
+ validate_project_plan_artifact,
122
+ validate_with_contractforge,
123
+ )
124
+
125
+ __all__ = [
126
+ "Assumption",
127
+ "ArchitectureConceptFinding",
128
+ "ArchitectureReview",
129
+ "ArtifactWriteResult",
130
+ "AgentInstructionRequest",
131
+ "ContextSnapshot",
132
+ "ConnectorIntent",
133
+ "DecisionReport",
134
+ "DeterministicValidationCheck",
135
+ "DeterministicValidationReport",
136
+ "EvidenceItem",
137
+ "EnrichmentResult",
138
+ "EnrichmentQualityReport",
139
+ "FailureExplanation",
140
+ "Finding",
141
+ "GapPlan",
142
+ "GenerationAuditEvent",
143
+ "GenerationAuditTrail",
144
+ "GenerationPolicyEngine",
145
+ "GenerationPolicyFinding",
146
+ "GenerationPolicyResult",
147
+ "GenerationSignature",
148
+ "GuidedProjectRequest",
149
+ "GuidedProjectResult",
150
+ "IntentGenerationRequest",
151
+ "IntentGenerationResult",
152
+ "IntentSpec",
153
+ "KnowledgeChunk",
154
+ "KnowledgeIndex",
155
+ "KnowledgeSearchResult",
156
+ "ProjectArtifact",
157
+ "ProjectState",
158
+ "ProjectIntent",
159
+ "ProjectPlan",
160
+ "ProjectPlannerRequest",
161
+ "ProjectPlannerResult",
162
+ "ProjectRecommendation",
163
+ "RequiredDecision",
164
+ "RenderedReport",
165
+ "ContractDraftResult",
166
+ "ContextFile",
167
+ "ControlTableAnalysis",
168
+ "ControlTableEvidencePackage",
169
+ "CritiqueFinding",
170
+ "CritiqueReport",
171
+ "MetadataSuggestionResult",
172
+ "ProjectContextPackage",
173
+ "ReviewResult",
174
+ "ShapeSuggestionResult",
175
+ "Suggestion",
176
+ "TaskRouteRequest",
177
+ "TaskRoutingReport",
178
+ "Traceability",
179
+ "TransformationPlan",
180
+ "TransformationStep",
181
+ "ValidationResult",
182
+ "analyze_control_tables",
183
+ "analyze_project_state",
184
+ "build_knowledge_index",
185
+ "build_project_context_package",
186
+ "confidence_level",
187
+ "connector_aliases",
188
+ "connector_details",
189
+ "connector_intent",
190
+ "connector_message",
191
+ "critique_output",
192
+ "explain_failure",
193
+ "enrich_adapter_validation",
194
+ "enrich_control_table_analysis",
195
+ "enrich_failure_explanation",
196
+ "enrich_project_plan",
197
+ "enrich_project_synthesis",
198
+ "enrich_review_result",
199
+ "evaluate_prompt_cases",
200
+ "evaluate_enrichment_quality",
201
+ "generate_aws_glue_iceberg_project",
202
+ "generate_contract_draft",
203
+ "generate_classic_pyspark_project",
204
+ "generate_contractforge_python_project",
205
+ "generate_contractforge_yaml_project",
206
+ "generate_databricks_dab_project",
207
+ "generate_dbt_project",
208
+ "generate_guided_project",
209
+ "generate_from_intent",
210
+ "generate_agent_instruction_plan",
211
+ "infer_transformation_plan",
212
+ "interpret_intent",
213
+ "review_contract",
214
+ "review_governed_architecture",
215
+ "route_task",
216
+ "suggest_metadata",
217
+ "suggest_shape",
218
+ "supported_connector_names",
219
+ "validate_contract_artifact",
220
+ "validate_generated_contract",
221
+ "validate_model_artifact",
222
+ "validate_model_output",
223
+ "validate_project_plan_artifact",
224
+ "validate_with_contractforge",
225
+ "load_knowledge_index",
226
+ "load_project_plan",
227
+ "list_prompt_templates",
228
+ "plan_project_from_intent",
229
+ "plan_project_gaps",
230
+ "query_knowledge_index",
231
+ "render_prompt",
232
+ "render_guided_project_review",
233
+ "render_markdown_report",
234
+ "render_operational_analysis_review",
235
+ "save_knowledge_index",
236
+ "write_project_plan",
237
+ ]
238
+
239
+ __version__ = "0.2.8"
@@ -0,0 +1,14 @@
1
+ """Optional adapter-aware validation for ContractForge AI."""
2
+
3
+ from contractforge_ai.adapter_validation.models import AdapterPlanningOutcome, AdapterValidationStatus
4
+ from contractforge_ai.adapter_validation.registry import AdapterPlannerSpec, adapter_planner_spec, known_adapter_names
5
+ from contractforge_ai.adapter_validation.validation import validate_contract_with_adapter
6
+
7
+ __all__ = [
8
+ "AdapterPlannerSpec",
9
+ "AdapterPlanningOutcome",
10
+ "AdapterValidationStatus",
11
+ "adapter_planner_spec",
12
+ "known_adapter_names",
13
+ "validate_contract_with_adapter",
14
+ ]
@@ -0,0 +1,36 @@
1
+ """Models for deterministic adapter planning validation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any, Literal
7
+
8
+ from contractforge_ai.models import EvidenceItem, Finding
9
+
10
+ AdapterValidationStatus = Literal["READY", "NEEDS_DECISIONS", "INVALID"]
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class AdapterPlanningOutcome:
15
+ """Result of planning one contract through one optional platform adapter."""
16
+
17
+ adapter: str
18
+ status: AdapterValidationStatus
19
+ summary: str
20
+ findings: list[Finding] = field(default_factory=list)
21
+ evidence: list[EvidenceItem] = field(default_factory=list)
22
+ raw_status: str | None = None
23
+ artifact_names: list[str] = field(default_factory=list)
24
+ artifact_types: list[str] = field(default_factory=list)
25
+
26
+ def to_dict(self) -> dict[str, Any]:
27
+ return {
28
+ "adapter": self.adapter,
29
+ "status": self.status,
30
+ "summary": self.summary,
31
+ "findings": [finding.to_dict() for finding in self.findings],
32
+ "evidence": [item.to_dict() for item in self.evidence],
33
+ **({"raw_status": self.raw_status} if self.raw_status else {}),
34
+ **({"artifact_names": self.artifact_names} if self.artifact_names else {}),
35
+ **({"artifact_types": self.artifact_types} if self.artifact_types else {}),
36
+ }
@@ -0,0 +1,45 @@
1
+ """Registry of optional ContractForge adapter planning APIs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class AdapterPlannerSpec:
11
+ """Import target for one adapter's public planning function."""
12
+
13
+ name: str
14
+ module: str
15
+ function: str
16
+ render_function: str | None = None
17
+ kwargs: dict[str, Any] = field(default_factory=dict)
18
+
19
+
20
+ DEFAULT_ADAPTER_PLANNERS: dict[str, AdapterPlannerSpec] = {
21
+ "aws": AdapterPlannerSpec(
22
+ name="aws",
23
+ module="contractforge_aws.api",
24
+ function="plan_aws_contract",
25
+ render_function="render_aws_contract",
26
+ ),
27
+ "databricks": AdapterPlannerSpec(
28
+ name="databricks",
29
+ module="contractforge_databricks.api",
30
+ function="plan_databricks_contract",
31
+ render_function="render_databricks_contract",
32
+ ),
33
+ }
34
+
35
+
36
+ def adapter_planner_spec(adapter: str) -> AdapterPlannerSpec | None:
37
+ """Return the planner spec for a supported optional adapter name."""
38
+
39
+ return DEFAULT_ADAPTER_PLANNERS.get(adapter.strip().lower())
40
+
41
+
42
+ def known_adapter_names() -> tuple[str, ...]:
43
+ """Return adapter names known by the AI deterministic validation registry."""
44
+
45
+ return tuple(sorted(DEFAULT_ADAPTER_PLANNERS))
@@ -0,0 +1,359 @@
1
+ """Deterministic validation against optional platform adapter planners."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module
6
+ from typing import Any
7
+
8
+ from contractforge_ai.adapter_validation.models import AdapterPlanningOutcome, AdapterValidationStatus
9
+ from contractforge_ai.adapter_validation.registry import adapter_planner_spec, known_adapter_names
10
+ from contractforge_ai.models import EvidenceItem, Finding, Severity
11
+
12
+ _SUPPORTED = "SUPPORTED"
13
+ _SUPPORTED_WITH_WARNINGS = "SUPPORTED_WITH_WARNINGS"
14
+ _REVIEW_REQUIRED = "REVIEW_REQUIRED"
15
+ _UNSUPPORTED = "UNSUPPORTED"
16
+
17
+ _ARTIFACT_TYPE_SUFFIXES: tuple[tuple[str, str], ...] = (
18
+ (".databricks.yml", "databricks_asset_bundle"),
19
+ (".glue_job.py", "aws_glue_job_runtime"),
20
+ (".glue_job_definition.json", "aws_glue_job_definition"),
21
+ (".deployment_manifest.json", "deployment_manifest"),
22
+ (".cloudformation.json", "cloudformation"),
23
+ (".tf", "terraform"),
24
+ (".iam_policy.json", "iam_policy"),
25
+ (".lakeformation.json", "lake_formation"),
26
+ (".evidence_ddl.sql", "evidence_ddl_sql"),
27
+ (".state_ddl.sql", "state_ddl_sql"),
28
+ (".evidence.sql", "evidence_sql"),
29
+ (".quality.dqdl", "quality_dqdl"),
30
+ (".quality.sql", "quality_sql"),
31
+ (".governance.sql", "governance_sql"),
32
+ (".annotations.sql", "annotations_sql"),
33
+ (".operations.json", "operations_json"),
34
+ (".strategy.json", "strategy_json"),
35
+ (".md", "review_markdown"),
36
+ (".sql", "sql"),
37
+ (".json", "json"),
38
+ (".py", "python"),
39
+ (".yml", "yaml"),
40
+ (".yaml", "yaml"),
41
+ )
42
+
43
+
44
+ def validate_contract_with_adapter(
45
+ contract: dict[str, Any],
46
+ *,
47
+ adapter: str,
48
+ environment: dict[str, Any] | None = None,
49
+ ) -> AdapterPlanningOutcome:
50
+ """Plan one contract through an optional adapter public API."""
51
+
52
+ normalized_adapter = adapter.strip().lower()
53
+ spec = adapter_planner_spec(normalized_adapter)
54
+ if spec is None:
55
+ return _unknown_adapter(normalized_adapter)
56
+ try:
57
+ module = import_module(spec.module)
58
+ planner = getattr(module, spec.function)
59
+ except ModuleNotFoundError as exc:
60
+ missing = exc.name or spec.module
61
+ return _package_unavailable(normalized_adapter, missing)
62
+ except Exception as exc:
63
+ return _adapter_import_failed(normalized_adapter, exc)
64
+ try:
65
+ kwargs = dict(spec.kwargs)
66
+ if environment is not None:
67
+ kwargs["environment"] = environment
68
+ result = planner(contract, **kwargs)
69
+ except Exception as exc:
70
+ return _planning_failed(normalized_adapter, exc)
71
+
72
+ raw_status = _status_text(getattr(result, "status", "UNKNOWN"))
73
+ artifact_names, artifact_types, render_findings = _render_artifact_summary(
74
+ normalized_adapter,
75
+ spec,
76
+ module,
77
+ contract,
78
+ environment=environment,
79
+ )
80
+ findings = [
81
+ *_findings_from_planning(normalized_adapter, result, raw_status),
82
+ *render_findings,
83
+ ]
84
+ status = _validation_status(raw_status, findings)
85
+ evidence = [
86
+ EvidenceItem(
87
+ source=f"contractforge_{normalized_adapter}",
88
+ reason="Planned generated contract through the adapter public planning API.",
89
+ value={
90
+ "planning_status": raw_status,
91
+ "blocker_codes": _issue_codes(getattr(result, "blockers", ()) or ()),
92
+ "warning_codes": _issue_codes(getattr(result, "warnings", ()) or ()),
93
+ "artifact_count": len(artifact_names),
94
+ "artifact_types": artifact_types,
95
+ "artifact_names": artifact_names,
96
+ },
97
+ confidence=1.0,
98
+ )
99
+ ]
100
+ return AdapterPlanningOutcome(
101
+ adapter=normalized_adapter,
102
+ status=status,
103
+ summary=f"{normalized_adapter} adapter planning returned {raw_status}.",
104
+ findings=findings,
105
+ evidence=evidence,
106
+ raw_status=raw_status,
107
+ artifact_names=artifact_names,
108
+ artifact_types=artifact_types,
109
+ )
110
+
111
+
112
+ def _validation_status(raw_status: str, findings: list[Finding]) -> AdapterValidationStatus:
113
+ if raw_status == _UNSUPPORTED or any(item.severity in {"high", "critical"} for item in findings):
114
+ return "INVALID"
115
+ if raw_status in {_SUPPORTED_WITH_WARNINGS, _REVIEW_REQUIRED} or findings:
116
+ return "NEEDS_DECISIONS"
117
+ if raw_status == _SUPPORTED:
118
+ return "READY"
119
+ return "NEEDS_DECISIONS"
120
+
121
+
122
+ def _findings_from_planning(adapter: str, result: Any, raw_status: str) -> list[Finding]:
123
+ findings: list[Finding] = []
124
+ for blocker in tuple(getattr(result, "blockers", ()) or ()):
125
+ findings.append(
126
+ _finding(
127
+ adapter=adapter,
128
+ code=f"adapter.{adapter}.planning.blocker.{_safe_code(getattr(blocker, 'code', 'unknown'))}",
129
+ severity="high",
130
+ title=f"{adapter} adapter cannot plan this contract",
131
+ detail=str(getattr(blocker, "message", blocker)),
132
+ recommendation="Change the contract semantics or select an adapter/runtime that declares the required capability.",
133
+ )
134
+ )
135
+ for warning in tuple(getattr(result, "warnings", ()) or ()):
136
+ findings.append(
137
+ _finding(
138
+ adapter=adapter,
139
+ code=f"adapter.{adapter}.planning.warning.{_safe_code(getattr(warning, 'code', 'unknown'))}",
140
+ severity="medium",
141
+ title=f"{adapter} adapter planning warning",
142
+ detail=str(getattr(warning, "message", warning)),
143
+ recommendation="Review the adapter warning before deployment; do not let AI silently accept changed semantics.",
144
+ )
145
+ )
146
+ if raw_status == _REVIEW_REQUIRED and not findings:
147
+ findings.append(
148
+ _finding(
149
+ adapter=adapter,
150
+ code=f"adapter.{adapter}.planning.review_required",
151
+ severity="medium",
152
+ title=f"{adapter} adapter requires review",
153
+ detail="The adapter planner returned REVIEW_REQUIRED.",
154
+ recommendation="Review the generated contract and adapter plan before deployment.",
155
+ )
156
+ )
157
+ if raw_status == _UNSUPPORTED and not findings:
158
+ findings.append(
159
+ _finding(
160
+ adapter=adapter,
161
+ code=f"adapter.{adapter}.planning.unsupported",
162
+ severity="high",
163
+ title=f"{adapter} adapter does not support this contract",
164
+ detail="The adapter planner returned UNSUPPORTED.",
165
+ recommendation="Change unsupported semantics or target another adapter/runtime.",
166
+ )
167
+ )
168
+ return findings
169
+
170
+
171
+ def _render_artifact_summary(
172
+ adapter: str,
173
+ spec: Any,
174
+ module: Any,
175
+ contract: dict[str, Any],
176
+ *,
177
+ environment: dict[str, Any] | None,
178
+ ) -> tuple[list[str], list[str], list[Finding]]:
179
+ if not spec.render_function:
180
+ return [], [], []
181
+ if _contains_review_required(contract):
182
+ return [], [], [
183
+ _finding(
184
+ adapter=adapter,
185
+ code=f"adapter.{adapter}.render_review_required",
186
+ severity="medium",
187
+ title=f"{adapter} adapter rendering requires review",
188
+ detail="The contract still contains REVIEW_REQUIRED placeholders, so AI validation stopped before rendering native artifacts.",
189
+ recommendation="Resolve review placeholders deterministically before treating adapter rendering as deployable.",
190
+ )
191
+ ]
192
+ try:
193
+ renderer = getattr(module, spec.render_function)
194
+ except AttributeError:
195
+ return [], [], [
196
+ _finding(
197
+ adapter=adapter,
198
+ code=f"adapter.{adapter}.render_api_missing",
199
+ severity="medium",
200
+ title=f"{adapter} adapter render API is missing",
201
+ detail=f"{spec.module}.{spec.render_function} is not available.",
202
+ recommendation="Expose the adapter public render API so AI validation can report native artifact types.",
203
+ )
204
+ ]
205
+ try:
206
+ kwargs = dict(spec.kwargs)
207
+ if environment is not None:
208
+ kwargs["environment"] = environment
209
+ rendered = renderer(contract, **kwargs)
210
+ except Exception as exc:
211
+ return [], [], [
212
+ _finding(
213
+ adapter=adapter,
214
+ code=f"adapter.{adapter}.render_failed",
215
+ severity="high",
216
+ title=f"{adapter} adapter render failed",
217
+ detail=f"{type(exc).__name__}: {exc}",
218
+ recommendation="Fix the contract or adapter renderer before treating generated artifacts as deployable.",
219
+ )
220
+ ]
221
+ artifact_names = sorted(str(name) for name in _artifact_mapping(rendered))
222
+ artifact_types = sorted({_artifact_type(name) for name in artifact_names})
223
+ return artifact_names, artifact_types, []
224
+
225
+
226
+ def _artifact_mapping(rendered: Any) -> dict[str, Any]:
227
+ artifacts = getattr(rendered, "artifacts", None)
228
+ return artifacts if isinstance(artifacts, dict) else {}
229
+
230
+
231
+ def _artifact_type(name: str) -> str:
232
+ normalized = name.lower()
233
+ for suffix, artifact_type in _ARTIFACT_TYPE_SUFFIXES:
234
+ if normalized.endswith(suffix):
235
+ return artifact_type
236
+ return "artifact"
237
+
238
+
239
+ def _contains_review_required(value: Any) -> bool:
240
+ if isinstance(value, dict):
241
+ return any(_contains_review_required(item) for item in value.values())
242
+ if isinstance(value, list):
243
+ return any(_contains_review_required(item) for item in value)
244
+ return value == "REVIEW_REQUIRED"
245
+
246
+
247
+ def _status_text(value: Any) -> str:
248
+ text = getattr(value, "value", value)
249
+ return str(text).split(".")[-1].upper()
250
+
251
+
252
+ def _issue_codes(issues: Any) -> list[str]:
253
+ return [
254
+ _safe_code(getattr(issue, "code", "unknown"))
255
+ for issue in tuple(issues or ())
256
+ ]
257
+
258
+
259
+ def _unknown_adapter(adapter: str) -> AdapterPlanningOutcome:
260
+ return AdapterPlanningOutcome(
261
+ adapter=adapter,
262
+ status="NEEDS_DECISIONS",
263
+ summary=f"Adapter {adapter!r} is not registered for deterministic AI planning validation.",
264
+ findings=[
265
+ _finding(
266
+ adapter=adapter,
267
+ code="adapter.validation.unknown_adapter",
268
+ severity="medium",
269
+ title="Unknown adapter requested",
270
+ detail=f"Known adapters: {', '.join(known_adapter_names())}.",
271
+ recommendation="Register a public adapter planner or use one of the known adapter names.",
272
+ )
273
+ ],
274
+ evidence=[_evidence(adapter, "Adapter name was not found in the deterministic validation registry.")],
275
+ )
276
+
277
+
278
+ def _package_unavailable(adapter: str, missing: str) -> AdapterPlanningOutcome:
279
+ return AdapterPlanningOutcome(
280
+ adapter=adapter,
281
+ status="NEEDS_DECISIONS",
282
+ summary=f"{adapter} adapter package is not available, so adapter planning was skipped.",
283
+ findings=[
284
+ _finding(
285
+ adapter=adapter,
286
+ code=f"adapter.{adapter}.package_unavailable",
287
+ severity="medium",
288
+ title=f"{adapter} adapter package is not installed",
289
+ detail=f"Import failed because {missing!r} is not available.",
290
+ recommendation=f"Install the {adapter} adapter extra/package before treating adapter planning as complete.",
291
+ )
292
+ ],
293
+ evidence=[_evidence(adapter, "Adapter package was unavailable during deterministic validation.")],
294
+ )
295
+
296
+
297
+ def _adapter_import_failed(adapter: str, exc: Exception) -> AdapterPlanningOutcome:
298
+ return AdapterPlanningOutcome(
299
+ adapter=adapter,
300
+ status="NEEDS_DECISIONS",
301
+ summary=f"{adapter} adapter import failed.",
302
+ findings=[
303
+ _finding(
304
+ adapter=adapter,
305
+ code=f"adapter.{adapter}.import_failed",
306
+ severity="medium",
307
+ title=f"{adapter} adapter import failed",
308
+ detail=f"{type(exc).__name__}: {exc}",
309
+ recommendation="Repair the adapter installation before relying on adapter-aware AI validation.",
310
+ )
311
+ ],
312
+ evidence=[_evidence(adapter, "Adapter import failed during deterministic validation.")],
313
+ )
314
+
315
+
316
+ def _planning_failed(adapter: str, exc: Exception) -> AdapterPlanningOutcome:
317
+ return AdapterPlanningOutcome(
318
+ adapter=adapter,
319
+ status="INVALID",
320
+ summary=f"{adapter} adapter rejected the generated contract during planning.",
321
+ findings=[
322
+ _finding(
323
+ adapter=adapter,
324
+ code=f"adapter.{adapter}.planning_failed",
325
+ severity="high",
326
+ title=f"{adapter} adapter planning failed",
327
+ detail=f"{type(exc).__name__}: {exc}",
328
+ recommendation="Fix the contract so the adapter public planner can evaluate it deterministically.",
329
+ )
330
+ ],
331
+ evidence=[_evidence(adapter, "Adapter planner raised an exception during deterministic validation.")],
332
+ )
333
+
334
+
335
+ def _finding(
336
+ *,
337
+ adapter: str,
338
+ code: str,
339
+ severity: Severity,
340
+ title: str,
341
+ detail: str,
342
+ recommendation: str,
343
+ ) -> Finding:
344
+ return Finding(
345
+ code=code,
346
+ severity=severity,
347
+ title=title,
348
+ detail=detail,
349
+ recommendation=recommendation,
350
+ evidence=[_evidence(adapter, f"Adapter validation rule {code!r} identified this condition.")],
351
+ )
352
+
353
+
354
+ def _evidence(adapter: str, reason: str) -> EvidenceItem:
355
+ return EvidenceItem(source=f"contractforge_{adapter}", reason=reason, confidence=1.0)
356
+
357
+
358
+ def _safe_code(value: str) -> str:
359
+ return "".join(char.lower() if char.isalnum() else "_" for char in str(value)).strip("_") or "unknown"