pgc-compiler 2.0.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 (177) hide show
  1. compiler/VERSION +1 -0
  2. compiler/__init__.py +1 -0
  3. compiler/atoms/__init__.py +54 -0
  4. compiler/atoms/error_codes.py +160 -0
  5. compiler/atoms/errors.py +125 -0
  6. compiler/atoms/fqdn.py +204 -0
  7. compiler/atoms/invariants.py +106 -0
  8. compiler/atoms/phase.py +99 -0
  9. compiler/atoms/pipeline.py +51 -0
  10. compiler/atoms/snapshot_gate.py +51 -0
  11. compiler/atoms/sorting.py +125 -0
  12. compiler/cli.py +637 -0
  13. compiler/diagnostics/__init__.py +6 -0
  14. compiler/diagnostics/determination.py +148 -0
  15. compiler/diagnostics/machine_block_health.py +199 -0
  16. compiler/governance/__init__.py +6 -0
  17. compiler/governance_engine/__init__.py +0 -0
  18. compiler/governance_engine/artifact_kinds.py +221 -0
  19. compiler/governance_engine/assertions/__init__.py +0 -0
  20. compiler/governance_engine/assertions/handlers/__init__.py +200 -0
  21. compiler/governance_engine/assertions/handlers/assert_ac_declaration_well_formed_v0.py +70 -0
  22. compiler/governance_engine/assertions/handlers/assert_actor_authority_separation_v0.py +17 -0
  23. compiler/governance_engine/assertions/handlers/assert_artifact_content_hash_declared_v0.py +60 -0
  24. compiler/governance_engine/assertions/handlers/assert_assert_not_runtime_referenced_v0.py +49 -0
  25. compiler/governance_engine/assertions/handlers/assert_assert_parity_v0.py +169 -0
  26. compiler/governance_engine/assertions/handlers/assert_atom_output_purity_v0.py +186 -0
  27. compiler/governance_engine/assertions/handlers/assert_authority_constituted_v0.py +56 -0
  28. compiler/governance_engine/assertions/handlers/assert_authority_required_for_execution_v0.py +16 -0
  29. compiler/governance_engine/assertions/handlers/assert_authority_state_well_formed_v0.py +17 -0
  30. compiler/governance_engine/assertions/handlers/assert_binding_integrity_v0.py +64 -0
  31. compiler/governance_engine/assertions/handlers/assert_binding_surface_closed_v0.py +92 -0
  32. compiler/governance_engine/assertions/handlers/assert_cc_capability_binding_valid_v0.py +85 -0
  33. compiler/governance_engine/assertions/handlers/assert_cc_inputs_satisfied_v0.py +89 -0
  34. compiler/governance_engine/assertions/handlers/assert_cc_no_implicit_chaining_v0.py +89 -0
  35. compiler/governance_engine/assertions/handlers/assert_cc_no_missing_dependencies_v0.py +89 -0
  36. compiler/governance_engine/assertions/handlers/assert_cc_no_unused_outputs_v0.py +81 -0
  37. compiler/governance_engine/assertions/handlers/assert_cc_storage_op_conformance_v0.py +74 -0
  38. compiler/governance_engine/assertions/handlers/assert_compiler_governance_declared_v0.py +65 -0
  39. compiler/governance_engine/assertions/handlers/assert_compiler_no_execution_v0.py +86 -0
  40. compiler/governance_engine/assertions/handlers/assert_concern_not_authority_v0.py +51 -0
  41. compiler/governance_engine/assertions/handlers/assert_conformance_assertion_mode_valid_v0.py +35 -0
  42. compiler/governance_engine/assertions/handlers/assert_cryptographic_trust_declared_v0.py +51 -0
  43. compiler/governance_engine/assertions/handlers/assert_cs_isolated_execution_v0.py +35 -0
  44. compiler/governance_engine/assertions/handlers/assert_cs_surface_closed_v0.py +241 -0
  45. compiler/governance_engine/assertions/handlers/assert_cs_traceable_v0.py +35 -0
  46. compiler/governance_engine/assertions/handlers/assert_ct_output_contract_match_v0.py +167 -0
  47. compiler/governance_engine/assertions/handlers/assert_ct_surface_closed_v0.py +173 -0
  48. compiler/governance_engine/assertions/handlers/assert_ct_surface_derived_closed_v0.py +108 -0
  49. compiler/governance_engine/assertions/handlers/assert_ct_test_data_outcome_declared_v0.py +112 -0
  50. compiler/governance_engine/assertions/handlers/assert_ev_append_only_v0.py +70 -0
  51. compiler/governance_engine/assertions/handlers/assert_ev_schema_required_v0.py +59 -0
  52. compiler/governance_engine/assertions/handlers/assert_execution_placement_declared_v0.py +51 -0
  53. compiler/governance_engine/assertions/handlers/assert_execution_scheduling_declared_v0.py +51 -0
  54. compiler/governance_engine/assertions/handlers/assert_fqdn_namespace_authorized_v0.py +38 -0
  55. compiler/governance_engine/assertions/handlers/assert_fqdn_only_references_v0.py +89 -0
  56. compiler/governance_engine/assertions/handlers/assert_governance_declaration_resolves_v0.py +126 -0
  57. compiler/governance_engine/assertions/handlers/assert_handler_registry_closed_v0.py +75 -0
  58. compiler/governance_engine/assertions/handlers/assert_identity_authority_separation_v0.py +19 -0
  59. compiler/governance_engine/assertions/handlers/assert_identity_fqdn_consistency.py +75 -0
  60. compiler/governance_engine/assertions/handlers/assert_implementation_admissible_v0.py +66 -0
  61. compiler/governance_engine/assertions/handlers/assert_in_no_execution_logic_v0.py +82 -0
  62. compiler/governance_engine/assertions/handlers/assert_in_schema_required_v0.py +74 -0
  63. compiler/governance_engine/assertions/handlers/assert_in_workflow_binding_v0.py +101 -0
  64. compiler/governance_engine/assertions/handlers/assert_inspection_capability_read_only_v0.py +114 -0
  65. compiler/governance_engine/assertions/handlers/assert_no_ambient_authority_v0.py +17 -0
  66. compiler/governance_engine/assertions/handlers/assert_no_runtime_authorization_v0.py +18 -0
  67. compiler/governance_engine/assertions/handlers/assert_no_runtime_topology_synthesis_v0.py +18 -0
  68. compiler/governance_engine/assertions/handlers/assert_no_smart_execution_v0.py +102 -0
  69. compiler/governance_engine/assertions/handlers/assert_no_undeclared_behavior_surface_v0.py +52 -0
  70. compiler/governance_engine/assertions/handlers/assert_no_workflow_authorization_logic_v0.py +18 -0
  71. compiler/governance_engine/assertions/handlers/assert_protocol_surface_closed_v0.py +119 -0
  72. compiler/governance_engine/assertions/handlers/assert_rb_binding_policy_conformance_v0.py +124 -0
  73. compiler/governance_engine/assertions/handlers/assert_rb_cs_only_v0.py +68 -0
  74. compiler/governance_engine/assertions/handlers/assert_rb_no_logic_v0.py +81 -0
  75. compiler/governance_engine/assertions/handlers/assert_rb_parameters_declared_v0.py +117 -0
  76. compiler/governance_engine/assertions/handlers/assert_rb_storage_subdomain_owned_v0.py +118 -0
  77. compiler/governance_engine/assertions/handlers/assert_runtime_invariant_wired_v0.py +163 -0
  78. compiler/governance_engine/assertions/handlers/assert_schema_conformance_v0.py +64 -0
  79. compiler/governance_engine/assertions/handlers/assert_schema_description_well_formed_v0.py +97 -0
  80. compiler/governance_engine/assertions/handlers/assert_security_domain_declared_v0.py +51 -0
  81. compiler/governance_engine/assertions/handlers/assert_structure_paths_well_formed_v0.py +118 -0
  82. compiler/governance_engine/assertions/handlers/assert_superseded_not_referenced_v0.py +134 -0
  83. compiler/governance_engine/assertions/handlers/assert_test_data_match_ct_output_v0.py +134 -0
  84. compiler/governance_engine/assertions/handlers/assert_topology_acyclic_v0.py +53 -0
  85. compiler/governance_engine/assertions/handlers/assert_topology_authority_orthogonal_v0.py +110 -0
  86. compiler/governance_engine/assertions/handlers/assert_topology_capability_reference_unique_v0.py +63 -0
  87. compiler/governance_engine/assertions/handlers/assert_topology_contract_closed_v0.py +113 -0
  88. compiler/governance_engine/assertions/handlers/assert_topology_immutable_after_compilation_v0.py +17 -0
  89. compiler/governance_engine/assertions/handlers/assert_topology_input_reference_declared_v0.py +112 -0
  90. compiler/governance_engine/assertions/handlers/assert_topology_routing_complete_v0.py +82 -0
  91. compiler/governance_engine/assertions/handlers/assert_topology_step_declared_v0.py +77 -0
  92. compiler/governance_engine/assertions/handlers/assert_topology_step_id_unique_v0.py +60 -0
  93. compiler/governance_engine/assertions/handlers/assert_topology_surface_canonical_v0.py +180 -0
  94. compiler/governance_engine/assertions/handlers/assert_topology_transport_orthogonal_v0.py +113 -0
  95. compiler/governance_engine/assertions/handlers/assert_trace_authority_binding_required_v0.py +18 -0
  96. compiler/governance_engine/assertions/handlers/assert_transport_canonical_normalization_v0.py +83 -0
  97. compiler/governance_engine/assertions/handlers/assert_transport_no_dynamic_routing_v0.py +93 -0
  98. compiler/governance_engine/assertions/handlers/assert_transport_no_workflow_semantics_v0.py +106 -0
  99. compiler/governance_engine/assertions/handlers/assert_transport_operation_identity_independence_v0.py +56 -0
  100. compiler/governance_engine/assertions/handlers/assert_transport_response_projection_external_v0.py +38 -0
  101. compiler/governance_engine/assertions/handlers/assert_transport_result_class_protocol_independence_v0.py +48 -0
  102. compiler/governance_engine/assertions/handlers/assert_transport_target_exists_v0.py +132 -0
  103. compiler/governance_engine/assertions/handlers/assert_unique_artifact_id_v0.py +58 -0
  104. compiler/governance_engine/assertions/handlers/assert_vocabulary_symbols_well_formed_v0.py +163 -0
  105. compiler/governance_engine/assertions/handlers/assert_wf_announcement_distinct_v0.py +85 -0
  106. compiler/governance_engine/assertions/handlers/assert_wf_cc_only_nodes_v0.py +60 -0
  107. compiler/governance_engine/assertions/handlers/assert_wf_entry_intent_v0.py +68 -0
  108. compiler/governance_engine/assertions/handlers/assert_wf_execution_path_valid_v0.py +89 -0
  109. compiler/governance_engine/assertions/handlers/assert_wf_node_key_binding_unique_v0.py +107 -0
  110. compiler/governance_engine/platform_root.py +102 -0
  111. compiler/governance_engine/structure/__init__.py +56 -0
  112. compiler/governance_engine/structure/discovery/__init__.py +28 -0
  113. compiler/governance_engine/structure/discovery/artifact_locator.py +210 -0
  114. compiler/governance_engine/structure/exceptions.py +113 -0
  115. compiler/governance_engine/structure/loading/__init__.py +88 -0
  116. compiler/governance_engine/structure/loading/fs_reader.py +225 -0
  117. compiler/governance_engine/structure/loading/markdown_parser.py +148 -0
  118. compiler/governance_engine/structure/loading/protocol_loader.py +587 -0
  119. compiler/governance_engine/structure/loading/test_case_provider.py +125 -0
  120. compiler/governance_engine/structure/loading/trace_reader.py +98 -0
  121. compiler/governance_engine/structure/loading/vocabulary_loader.py +174 -0
  122. compiler/governance_engine/structure/loading/yaml_parser.py +209 -0
  123. compiler/governance_engine/structure/resolution/__init__.py +22 -0
  124. compiler/governance_engine/structure/resolution/domain_resolver.py +124 -0
  125. compiler/governance_engine/structure/resolution/layer_resolver.py +572 -0
  126. compiler/governance_engine/structure/resolution/path_registry.py +1054 -0
  127. compiler/graph/__init__.py +38 -0
  128. compiler/graph/addressing.py +153 -0
  129. compiler/graph/edge.py +100 -0
  130. compiler/graph/evidence.py +637 -0
  131. compiler/graph/graph.py +269 -0
  132. compiler/graph/hashing.py +89 -0
  133. compiler/graph/node.py +134 -0
  134. compiler/graph/query.py +215 -0
  135. compiler/graph/state.py +237 -0
  136. compiler/graph/trace.py +97 -0
  137. compiler/graph/types.py +71 -0
  138. compiler/projections/__init__.py +207 -0
  139. compiler/projections/artifact_index.py +225 -0
  140. compiler/projections/canonical.py +173 -0
  141. compiler/projections/dispatch.py +532 -0
  142. compiler/projections/evidence.py +170 -0
  143. compiler/projections/handlers.py +224 -0
  144. compiler/projections/ipm.py +388 -0
  145. compiler/projections/reverse.py +165 -0
  146. compiler/projections/tokenized.py +135 -0
  147. compiler/projections/vocabulary.py +108 -0
  148. compiler/stages/__init__.py +28 -0
  149. compiler/stages/s1_extract.py +867 -0
  150. compiler/stages/s2_canonicalize.py +516 -0
  151. compiler/stages/s3_semantic_addressing.py +66 -0
  152. compiler/stages/s4_govern.py +754 -0
  153. compiler/stages/s5_construct.py +537 -0
  154. compiler/stages/s6_project.py +94 -0
  155. compiler/stages/s7_materialize.py +1170 -0
  156. compiler/stages/s8_verify.py +643 -0
  157. compiler/stages/s9_attest.py +184 -0
  158. compiler/structure_loader.py +132 -0
  159. compiler/visualization/__init__.py +12 -0
  160. compiler/visualization/consumers/__init__.py +28 -0
  161. compiler/visualization/consumers/evidence_projection.py +162 -0
  162. compiler/visualization/consumers/evidence_query.py +296 -0
  163. compiler/visualization/consumers/evidence_reader.py +127 -0
  164. compiler/visualization/views/__init__.py +70 -0
  165. compiler/visualization/views/_png_writer.py +45 -0
  166. compiler/visualization/views/artifact_lineage.py +180 -0
  167. compiler/visualization/views/family_view.py +164 -0
  168. compiler/visualization/views/materialization_view.py +150 -0
  169. compiler/visualization/views/structural_dag.py +158 -0
  170. compiler/visualization/wf_graph_generator.py +534 -0
  171. pgc_compiler-2.0.0.dist-info/METADATA +203 -0
  172. pgc_compiler-2.0.0.dist-info/RECORD +177 -0
  173. pgc_compiler-2.0.0.dist-info/WHEEL +5 -0
  174. pgc_compiler-2.0.0.dist-info/entry_points.txt +2 -0
  175. pgc_compiler-2.0.0.dist-info/licenses/LICENSE +67 -0
  176. pgc_compiler-2.0.0.dist-info/licenses/NOTICE +11 -0
  177. pgc_compiler-2.0.0.dist-info/top_level.txt +1 -0
compiler/VERSION ADDED
@@ -0,0 +1 @@
1
+ 14
compiler/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """compiler — Protocol Intermediate Representation compiler for PGS."""
@@ -0,0 +1,54 @@
1
+ """
2
+ Atoms: Pure functions with no side effects.
3
+
4
+ Design:
5
+ - Single responsibility
6
+ - Explicit inputs/outputs
7
+ - No I/O, no state
8
+ - Independently testable
9
+ - Reusable across phases
10
+ """
11
+
12
+ from compiler.atoms.error_codes import ErrorCode, ERROR_SUGGESTIONS
13
+ from compiler.atoms.errors import CompilerError, Severity
14
+ from compiler.atoms.fqdn import FQDN, parse_fqdn, build_fqdn, to_fqdn
15
+ from compiler.atoms.invariants import require, require_not_none, require_exists
16
+ from compiler.atoms.phase import PhaseResult, PhaseStatus, PhaseMetrics
17
+ from compiler.atoms.pipeline import strip_transient_pipeline_fields
18
+ from compiler.atoms.sorting import (
19
+ ensure_deterministic_output,
20
+ sort_artifacts_by_fqdn,
21
+ sort_by_fqdn,
22
+ sort_dict_keys,
23
+ )
24
+ from compiler.atoms.snapshot_gate import assert_snapshot_valid
25
+
26
+ __all__ = [
27
+ # Error model
28
+ "CompilerError",
29
+ "ErrorCode",
30
+ "ERROR_SUGGESTIONS",
31
+ "Severity",
32
+ # FQDN identity
33
+ "FQDN",
34
+ "parse_fqdn",
35
+ "build_fqdn",
36
+ "to_fqdn",
37
+ # Phase results
38
+ "PhaseResult",
39
+ "PhaseStatus",
40
+ "PhaseMetrics",
41
+ # Invariant enforcement
42
+ "require",
43
+ "require_not_none",
44
+ "require_exists",
45
+ # Pipeline metadata
46
+ "strip_transient_pipeline_fields",
47
+ # Deterministic ordering
48
+ "ensure_deterministic_output",
49
+ "sort_artifacts_by_fqdn",
50
+ "sort_by_fqdn",
51
+ "sort_dict_keys",
52
+ # Snapshot admission control
53
+ "assert_snapshot_valid",
54
+ ]
@@ -0,0 +1,160 @@
1
+ """
2
+ Centralized error code registry.
3
+
4
+ All error codes in one place to prevent:
5
+ - String drift
6
+ - Inconsistent reporting
7
+ - Duplicate codes
8
+ - Missing documentation
9
+
10
+ Add new codes here, never inline in phase code.
11
+ """
12
+
13
+ from enum import Enum
14
+
15
+
16
+ class ErrorCode(Enum):
17
+ """
18
+ Error codes for the PGS compiler.
19
+
20
+ Organized by stage:
21
+ - E0xx: Discovery errors (S1)
22
+ - E1xx: Parse errors (S1)
23
+ - E2xx: Validation errors (S2–S4)
24
+ - E3xx: Materialization errors (S7)
25
+ - E4xx: Verification errors (S8)
26
+ - E7xx: ASSERT errors (S4)
27
+ - E8xx: Conformance errors (S7)
28
+ - E9xx: Internal/system errors
29
+ """
30
+
31
+ # ==================
32
+ # Discovery (E0xx)
33
+ # ==================
34
+ E001_NO_ARTIFACTS = "E001_NO_ARTIFACTS"
35
+ """No artifacts found in search path."""
36
+
37
+ E002_DUPLICATE_FQDN = "E002_DUPLICATE_FQDN"
38
+ """Duplicate FQDN detected (collision)."""
39
+
40
+ E003_INVALID_FILENAME = "E003_INVALID_FILENAME"
41
+ """Invalid filename pattern."""
42
+
43
+ E004_CROSS_REPO_REF = "E004_CROSS_REPO_REF"
44
+ """Cross-repo reference detected (source_path outside input_dir)."""
45
+
46
+ E005_DEPRECATED_ARTIFACT = "E005_DEPRECATED_ARTIFACT"
47
+ """Artifact marked status=deprecated; skipped during extraction."""
48
+
49
+ # ==================
50
+ # Parse (E1xx)
51
+ # ==================
52
+ E101_INVALID_YAML = "E101_INVALID_YAML"
53
+ """Invalid YAML frontmatter."""
54
+
55
+ E102_MISSING_FIELD = "E102_MISSING_FIELD"
56
+ """Missing required field."""
57
+
58
+ E103_TYPE_MISMATCH = "E103_TYPE_MISMATCH"
59
+ """Field type mismatch."""
60
+
61
+ E104_INVALID_FQDN = "E104_INVALID_FQDN"
62
+ """Invalid FQDN format."""
63
+
64
+ # ==================
65
+ # Validation (E2xx)
66
+ # ==================
67
+ E201_MISSING_REFERENCE = "E201_MISSING_REFERENCE"
68
+ """Reference to non-existent artifact (FQDN not found)."""
69
+
70
+ E202_CIRCULAR_DEPENDENCY = "E202_CIRCULAR_DEPENDENCY"
71
+ """Circular dependency detected (A→B→A)."""
72
+
73
+ E203_SCHEMA_INVALID = "E203_SCHEMA_INVALID"
74
+ """Schema validation failed (Pydantic error)."""
75
+
76
+ E204_INVALID_RB_BINDING = "E204_INVALID_RB_BINDING"
77
+ """RB references invalid CS artifact."""
78
+
79
+ E205_CT_VALIDATION_FAILED = "E205_CT_VALIDATION_FAILED"
80
+ """CT-IR validation failed."""
81
+
82
+ # ==================
83
+ # Materialization (E3xx)
84
+ # ==================
85
+ E301_WRITE_FAILED = "E301_WRITE_FAILED"
86
+ """Failed to write output file."""
87
+
88
+ E302_JSON_SERIALIZE_FAILED = "E302_JSON_SERIALIZE_FAILED"
89
+ """JSON serialization failed."""
90
+
91
+ # ==================
92
+ # Verification (E4xx)
93
+ # ==================
94
+ E401_MISSING_OUTPUT = "E401_MISSING_OUTPUT"
95
+ """Expected output file not found."""
96
+
97
+ E402_UNDECLARED_OUTPUT = "E402_UNDECLARED_OUTPUT"
98
+ """Output file not declared in expected outputs."""
99
+
100
+ E403_OUTPUT_MISMATCH = "E403_OUTPUT_MISMATCH"
101
+ """Output doesn't match expected schema."""
102
+
103
+ # ==================
104
+ # ASSERT Phase (E701-E750)
105
+ # ==================
106
+ E701_ASSERTION_FAILURE = "E701_ASSERTION_FAILURE"
107
+ """Assertion violations detected."""
108
+
109
+ E702_UNKNOWN_ASSERT = "E702_UNKNOWN_ASSERT"
110
+ """No executor for ASSERT artifact."""
111
+
112
+ E703_MALFORMED_ASSERT = "E703_MALFORMED_ASSERT"
113
+ """ASSERT artifact schema violation."""
114
+
115
+ # ==================
116
+ # Conformance (E8xx)
117
+ # ==================
118
+ E801_TEST_DATA_MISSING = "E801_TEST_DATA_MISSING"
119
+ """Test data missing for CT."""
120
+
121
+ E802_CONFORMANCE_FAILURE = "E802_CONFORMANCE_FAILURE"
122
+ """CT conformance test failed."""
123
+
124
+ E803_TEST_DATA_INVALID = "E803_TEST_DATA_INVALID"
125
+ """TEST_DATA does not match CT contract."""
126
+
127
+ # ==================
128
+ # Internal (E9xx)
129
+ # ==================
130
+ E901_INTERNAL_ERROR = "E901_INTERNAL_ERROR"
131
+ """Internal compiler error (bug)."""
132
+
133
+ E902_CONFIG_ERROR = "E902_CONFIG_ERROR"
134
+ """Configuration error."""
135
+
136
+
137
+ # Error suggestions (actionable fixes)
138
+ ERROR_SUGGESTIONS: dict[ErrorCode, str] = {
139
+ ErrorCode.E001_NO_ARTIFACTS: "Check input directory path and artifact_types config",
140
+ ErrorCode.E002_DUPLICATE_FQDN: "Rename artifact or change namespace to make FQDN unique",
141
+ ErrorCode.E003_INVALID_FILENAME: "Ensure filename matches pattern: {TYPE}_{NAME}_V{N}.md",
142
+ ErrorCode.E004_CROSS_REPO_REF: "Move artifact into input_dir or remove reference",
143
+ ErrorCode.E005_DEPRECATED_ARTIFACT: "Remove deprecated artifact or update its status field",
144
+ ErrorCode.E101_INVALID_YAML: "Check YAML syntax, ensure proper indentation",
145
+ ErrorCode.E102_MISSING_FIELD: "Add required field to artifact frontmatter",
146
+ ErrorCode.E104_INVALID_FQDN: "FQDN format: {namespace}::{artifact_code}",
147
+ ErrorCode.E201_MISSING_REFERENCE: "Add referenced artifact or remove reference",
148
+ ErrorCode.E202_CIRCULAR_DEPENDENCY: "Break dependency cycle between artifacts",
149
+ ErrorCode.E204_INVALID_RB_BINDING: "Ensure RB references valid CS artifact",
150
+ ErrorCode.E205_CT_VALIDATION_FAILED: "Check CT atom_stream and purity constraints",
151
+ ErrorCode.E301_WRITE_FAILED: "Check output directory permissions and disk space",
152
+ ErrorCode.E401_MISSING_OUTPUT: "Check materialization phase completed successfully",
153
+ ErrorCode.E402_UNDECLARED_OUTPUT: "Remove stale file or add to expected outputs",
154
+ ErrorCode.E701_ASSERTION_FAILURE: "Fix detected violations in protocol artifacts",
155
+ ErrorCode.E702_UNKNOWN_ASSERT: "Ensure an executor is registered for this ASSERT artifact",
156
+ ErrorCode.E703_MALFORMED_ASSERT: "Check ASSERT artifact against CONSTITUTION_ASSERT_V0",
157
+ ErrorCode.E801_TEST_DATA_MISSING: "Create TEST_DATA artifact for the CT",
158
+ ErrorCode.E802_CONFORMANCE_FAILURE: "Debug CT implementation to match expected behavior",
159
+ ErrorCode.E803_TEST_DATA_INVALID: "Fix TEST_DATA bindings to match CT inputs contract",
160
+ }
@@ -0,0 +1,125 @@
1
+ """
2
+ Structured error model for the PGS compiler.
3
+
4
+ Design:
5
+ - Machine-readable + human-friendly
6
+ - Phase-tagged (S1–S8)
7
+ - Context-rich (FQDN, artifact_code, source_path)
8
+ - Actionable messages via ERROR_SUGGESTIONS
9
+ """
10
+
11
+ from dataclasses import dataclass, field
12
+ from enum import Enum
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from compiler.atoms.error_codes import ErrorCode, ERROR_SUGGESTIONS
17
+
18
+
19
+ class Severity(Enum):
20
+ """Error severity levels."""
21
+
22
+ ERROR = "error"
23
+ WARNING = "warning"
24
+ INFO = "info"
25
+
26
+
27
+ @dataclass
28
+ class CompilerError(Exception):
29
+ """
30
+ Structured error for compiler failures.
31
+
32
+ Fields:
33
+ code: Error code (e.g., E201_MISSING_REFERENCE)
34
+ message: Human-readable description
35
+ phase: Stage where error occurred (e.g., S1_EXTRACT)
36
+ fqdn_id: Fully qualified domain name (primary identity)
37
+ artifact_code: Short code for display/logging (not unique)
38
+ source_path: Path to source file if applicable
39
+ context: Additional debug information
40
+ severity: Error severity level
41
+ """
42
+
43
+ code: ErrorCode
44
+ message: str
45
+ phase: str
46
+ fqdn_id: str | None = None
47
+ artifact_code: str | None = None
48
+ source_path: Path | None = None
49
+ context: dict[str, Any] = field(default_factory=dict)
50
+ severity: Severity = Severity.ERROR
51
+
52
+ def __str__(self) -> str:
53
+ """String representation delegates to format()."""
54
+ return self.format(verbose=False)
55
+
56
+ def format(self, verbose: bool = False) -> str:
57
+ """
58
+ Format error for terminal output.
59
+
60
+ Args:
61
+ verbose: Include full context and suggestions
62
+
63
+ Returns:
64
+ Formatted error string
65
+ """
66
+ # Color codes (basic ANSI)
67
+ color = {
68
+ Severity.ERROR: "\033[91m", # Red
69
+ Severity.WARNING: "\033[93m", # Yellow
70
+ Severity.INFO: "\033[94m", # Blue
71
+ }
72
+ reset = "\033[0m"
73
+
74
+ severity_color = color.get(self.severity, "")
75
+ severity_str = self.severity.value.upper()
76
+
77
+ # Basic format: [ERROR] E201: Message
78
+ basic = f"{severity_color}[{severity_str}]{reset} {self.code.value}: {self.message}"
79
+
80
+ if not verbose:
81
+ return basic
82
+
83
+ # Verbose format includes location, context
84
+ parts = [basic]
85
+
86
+ if self.source_path:
87
+ parts.append(f" File: {self.source_path}")
88
+
89
+ if self.fqdn_id:
90
+ parts.append(f" FQDN: {self.fqdn_id}")
91
+
92
+ if self.artifact_code:
93
+ parts.append(f" Code: {self.artifact_code}")
94
+
95
+ if self.phase:
96
+ parts.append(f" Phase: {self.phase}")
97
+
98
+ if self.context:
99
+ parts.append(" Context:")
100
+ for key, value in self.context.items():
101
+ parts.append(f" {key}: {value}")
102
+
103
+ # Add suggestion if available
104
+ suggestion = self._get_suggestion()
105
+ if suggestion:
106
+ parts.append(f" 💡 Suggestion: {suggestion}")
107
+
108
+ return "\n".join(parts)
109
+
110
+ def _get_suggestion(self) -> str | None:
111
+ """Get actionable suggestion for fixing this error."""
112
+ return ERROR_SUGGESTIONS.get(self.code)
113
+
114
+ def to_dict(self) -> dict[str, Any]:
115
+ """Convert to dictionary for JSON output."""
116
+ return {
117
+ "code": self.code.value,
118
+ "message": self.message,
119
+ "phase": self.phase,
120
+ "fqdn_id": self.fqdn_id,
121
+ "artifact_code": self.artifact_code,
122
+ "source_path": str(self.source_path) if self.source_path else None,
123
+ "context": self.context,
124
+ "severity": self.severity.value,
125
+ }
compiler/atoms/fqdn.py ADDED
@@ -0,0 +1,204 @@
1
+ """
2
+ FQDN (Fully Qualified Domain Name) utilities.
3
+
4
+ Patch 1 Applied: FQDN is PRIMARY identity, never use artifact_code for uniqueness.
5
+
6
+ FQDN Format: {namespace}::{artifact_code}
7
+ Example: pgs_governance::CT_SCAN_ARTIFACTS_V0
8
+
9
+ Design:
10
+ - FQDN is PRIMARY identity (all references use FQDN)
11
+ - artifact_code is display/logging ONLY (not guaranteed unique)
12
+ - Namespace prevents collision across layers (e.g., pgs_governance, compiler)
13
+ - Parse/build functions for deterministic construction
14
+ """
15
+
16
+ import re
17
+ from dataclasses import dataclass
18
+ from pathlib import Path
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class FQDN:
23
+ """
24
+ Fully Qualified Domain Name for artifact identity.
25
+
26
+ CRITICAL: This is the PRIMARY identity.
27
+ artifact_code alone is NOT sufficient (may collide).
28
+
29
+ IMMUTABILITY GUARANTEE:
30
+ - frozen=True prevents mutation
31
+ - Always use str(fqdn) or fqdn.fqdn for identity
32
+ - NEVER access .namespace or .artifact_code separately outside atoms layer
33
+ - Prevents accidental reversion to partial identity
34
+
35
+ Fields:
36
+ namespace: Package/layer namespace (e.g., "pgs_governance") [INTERNAL]
37
+ artifact_code: Short code (e.g., "CT_SCAN_V0") [INTERNAL]
38
+ fqdn: Full identifier (namespace::artifact_code) [PUBLIC]
39
+ """
40
+
41
+ namespace: str
42
+ artifact_code: str
43
+
44
+ @property
45
+ def fqdn(self) -> str:
46
+ """
47
+ Get full FQDN string.
48
+
49
+ This is the ONLY public interface for identity.
50
+ Use this, not namespace or artifact_code separately.
51
+ """
52
+ return f"{self.namespace}::{self.artifact_code}"
53
+
54
+ def __str__(self) -> str:
55
+ """
56
+ String representation is the FQDN.
57
+
58
+ Always use this for display/logging/comparison.
59
+ """
60
+ return self.fqdn
61
+
62
+ def __hash__(self) -> int:
63
+ """Hash by FQDN (for use in dicts/sets)."""
64
+ return hash(self.fqdn)
65
+
66
+ def __repr__(self) -> str:
67
+ """Repr is also the FQDN (not internal components)."""
68
+ return f"FQDN('{self.fqdn}')"
69
+
70
+
71
+ def parse_fqdn(fqdn_str: str) -> FQDN:
72
+ """
73
+ Parse FQDN string into components.
74
+
75
+ Args:
76
+ fqdn_str: FQDN string (namespace::artifact_code)
77
+
78
+ Returns:
79
+ FQDN object
80
+
81
+ Raises:
82
+ ValueError: If FQDN format invalid
83
+
84
+ Examples:
85
+ >>> parse_fqdn("pgs_governance::CT_SCAN_V0")
86
+ FQDN(namespace='pgs_governance', artifact_code='CT_SCAN_V0')
87
+ """
88
+ if "::" not in fqdn_str:
89
+ raise ValueError(
90
+ f"Invalid FQDN format: '{fqdn_str}'\n"
91
+ f"Expected: {{namespace}}::{{artifact_code}}\n"
92
+ f"Example: pgs_governance::CT_SCAN_V0"
93
+ )
94
+
95
+ parts = fqdn_str.split("::", 1)
96
+ if len(parts) != 2:
97
+ raise ValueError(f"Invalid FQDN: '{fqdn_str}' (expected exactly one '::')")
98
+
99
+ namespace, artifact_code = parts
100
+
101
+ if not namespace:
102
+ raise ValueError(f"Invalid FQDN: '{fqdn_str}' (empty namespace)")
103
+
104
+ if not artifact_code:
105
+ raise ValueError(f"Invalid FQDN: '{fqdn_str}' (empty artifact_code)")
106
+
107
+ return FQDN(namespace=namespace, artifact_code=artifact_code)
108
+
109
+
110
+ def build_fqdn(source_path: Path, layer_root: Path, artifact_code: str) -> FQDN:
111
+ """
112
+ Build FQDN from source path and layer root.
113
+
114
+ Derives namespace from directory structure:
115
+ - Namespace = layer_root.name (e.g., pgs_governance, compiler)
116
+
117
+ Args:
118
+ source_path: Path to artifact source file
119
+ layer_root: Root directory of layer
120
+ artifact_code: Short code (e.g., CT_SCAN_V0)
121
+
122
+ Returns:
123
+ FQDN object
124
+
125
+ Examples:
126
+ >>> build_fqdn(
127
+ ... Path("/pgs/pgs_governance/registry/CT_FOO.md"),
128
+ ... Path("/pgs/pgs_governance"),
129
+ ... "CT_FOO_V0"
130
+ ... )
131
+ FQDN(namespace='pgs_governance', artifact_code='CT_FOO_V0')
132
+ """
133
+ # Namespace is derived from layer root directory name
134
+ # This ensures uniqueness across different layers/repos
135
+ namespace = layer_root.name
136
+
137
+ return FQDN(namespace=namespace, artifact_code=artifact_code)
138
+
139
+
140
+ def extract_artifact_code(filename: str) -> str:
141
+ """
142
+ Extract artifact code from filename.
143
+
144
+ Args:
145
+ filename: Artifact filename (e.g., "CT_SCAN_V0.md")
146
+
147
+ Returns:
148
+ Artifact code without extension
149
+
150
+ Examples:
151
+ >>> extract_artifact_code("CT_SCAN_ARTIFACTS_V0.md")
152
+ 'CT_SCAN_ARTIFACTS_V0'
153
+ """
154
+ # Remove extension
155
+ if filename.endswith(".md"):
156
+ return filename[:-3]
157
+ return filename
158
+
159
+
160
+ def validate_artifact_code(code: str) -> bool:
161
+ """
162
+ Validate artifact code format.
163
+
164
+ Expected pattern: {TYPE}_{NAME}_V{VERSION}
165
+ Examples: CT_SCAN_V0, WF_BUILD_V1
166
+
167
+ Args:
168
+ code: Artifact code to validate
169
+
170
+ Returns:
171
+ True if valid, False otherwise
172
+ """
173
+ # Pattern: TYPE_NAME_VN (where TYPE is 2-20 uppercase letters)
174
+ pattern = r"^[A-Z]{2,20}_[A-Z0-9_]+_V\d+$"
175
+ return bool(re.match(pattern, code))
176
+
177
+
178
+ def to_fqdn(ref: str, namespace: str) -> FQDN:
179
+ """
180
+ Normalize reference to FQDN.
181
+
182
+ CRITICAL: All references MUST be FQDN.
183
+ This helper converts partial references (artifact_code only) to full FQDN.
184
+
185
+ Args:
186
+ ref: Reference string (either "namespace::artifact_code" or "artifact_code")
187
+ namespace: Default namespace if ref is partial
188
+
189
+ Returns:
190
+ FQDN object
191
+
192
+ Examples:
193
+ >>> to_fqdn("pgs_governance::CT_SCAN_V0", "fallback")
194
+ FQDN('pgs_governance::CT_SCAN_V0')
195
+
196
+ >>> to_fqdn("CT_SCAN_V0", "pgs_governance")
197
+ FQDN('pgs_governance::CT_SCAN_V0')
198
+ """
199
+ if "::" in ref:
200
+ # Already FQDN format
201
+ return parse_fqdn(ref)
202
+ else:
203
+ # Partial reference (artifact_code only)
204
+ return FQDN(namespace=namespace, artifact_code=ref)
@@ -0,0 +1,106 @@
1
+ """
2
+ Lightweight invariant enforcement.
3
+
4
+ NOT the PGS ASSERT framework - just simple fail-fast helpers.
5
+
6
+ Design:
7
+ - Simple condition checking
8
+ - Raises CompilerError on violation
9
+ - No protocol semantics
10
+ - No registry hooks
11
+ - Just: if not condition → error
12
+ """
13
+
14
+ from .errors import CompilerError
15
+
16
+
17
+ def require(condition: bool, error: CompilerError) -> None:
18
+ """
19
+ Require condition to be true, raise error if false.
20
+
21
+ Lightweight assertion for fail-fast enforcement.
22
+ NOT PGS ASSERT framework - no protocol semantics.
23
+
24
+ Args:
25
+ condition: Boolean condition that must be true
26
+ error: Error to raise if condition is false
27
+
28
+ Raises:
29
+ CompilerError: If condition is false
30
+
31
+ Examples:
32
+ >>> require(
33
+ ... fqdn_id in artifacts,
34
+ ... CompilerError(
35
+ ... code=ErrorCode.E201_MISSING_REFERENCE,
36
+ ... message=f"Reference {fqdn_id} not found",
37
+ ... phase="VALIDATE"
38
+ ... )
39
+ ... )
40
+ """
41
+ if not condition:
42
+ raise error
43
+
44
+
45
+ def require_not_none(value: any, error: CompilerError) -> any:
46
+ """
47
+ Require value is not None, raise error if None.
48
+
49
+ Args:
50
+ value: Value to check
51
+ error: Error to raise if value is None
52
+
53
+ Returns:
54
+ value (if not None)
55
+
56
+ Raises:
57
+ CompilerError: If value is None
58
+
59
+ Examples:
60
+ >>> config = require_not_none(
61
+ ... config_dict.get("artifact_types"),
62
+ ... CompilerError(
63
+ ... code=ErrorCode.E902_CONFIG_ERROR,
64
+ ... message="Missing required config: artifact_types",
65
+ ... phase="CONFIG"
66
+ ... )
67
+ ... )
68
+ """
69
+ if value is None:
70
+ raise error
71
+ return value
72
+
73
+
74
+ def require_exists(path: "Path", error: CompilerError) -> "Path":
75
+ """
76
+ Require path exists, raise error if not.
77
+
78
+ Args:
79
+ path: Path to check
80
+ error: Error to raise if path doesn't exist
81
+
82
+ Returns:
83
+ path (if exists)
84
+
85
+ Raises:
86
+ CompilerError: If path doesn't exist
87
+
88
+ Examples:
89
+ >>> input_dir = require_exists(
90
+ ... Path(args.input_dir),
91
+ ... CompilerError(
92
+ ... code=ErrorCode.E001_NO_ARTIFACTS,
93
+ ... message=f"Input directory not found: {args.input_dir}",
94
+ ... phase="DISCOVERY"
95
+ ... )
96
+ ... )
97
+ """
98
+ from pathlib import Path
99
+
100
+ if not isinstance(path, Path):
101
+ path = Path(path)
102
+
103
+ if not path.exists():
104
+ raise error
105
+
106
+ return path