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/cli.py ADDED
@@ -0,0 +1,637 @@
1
+ """
2
+ CLI entry point for PGC compiler.
3
+
4
+ Subcommands:
5
+ compile — compile one or more STRUCTURE artifacts (S1–S9 pipeline)
6
+ inspect — query evidence_graph.json for a compiled structure
7
+
8
+ Pipeline: S1 EXTRACT → S2 CANONICALIZE → S3 SEMANTIC_ADDRESSING →
9
+ S4 GOVERN → S5 CONSTRUCT → S6 PROJECT → S7 MATERIALIZE → S8 VERIFY → S9 ATTEST
10
+
11
+ Each stage is a pure function: State → State.
12
+
13
+ ARCHITECTURAL INVARIANT:
14
+ - Compiler MUST NOT import: execution, runtime, pgs_runtime
15
+ - Compiler is PURE: extract, canonicalize, address, govern, construct, project, materialize, verify, attest
16
+ - Compiler outputs: JSON artifacts ONLY
17
+ """
18
+
19
+ import os
20
+ import sys
21
+ from typing import Any
22
+
23
+ import click
24
+
25
+ from compiler.graph.state import State
26
+ from compiler.stages import (
27
+ s1_extract,
28
+ s2_canonicalize,
29
+ s3_semantic_addressing,
30
+ s4_govern,
31
+ s5_construct,
32
+ s6_project,
33
+ s7_materialize,
34
+ s8_verify,
35
+ s9_attest,
36
+ )
37
+ from compiler.atoms.errors import CompilerError
38
+ from compiler.structure_loader import FORBIDDEN_STRUCTURE
39
+
40
+
41
+ def assert_structure_integrity(structure_code: str) -> None:
42
+ """
43
+ Verify build anchor is not using legacy fragmented structure.
44
+ """
45
+ if structure_code in FORBIDDEN_STRUCTURE:
46
+ raise RuntimeError(
47
+ f"LEGACY BUILD ATTEMPTED: '{structure_code}' is forbidden.\n"
48
+ f"The compiler now requires consolidated master artifacts."
49
+ )
50
+
51
+
52
+ @click.group()
53
+ def cli() -> None:
54
+ """PGC protocol compiler — topology-native governance compilation."""
55
+ pass
56
+
57
+
58
+ @cli.command()
59
+ @click.option(
60
+ "--structure",
61
+ type=str,
62
+ multiple=True,
63
+ help="STRUCTURE artifact code(s) (e.g. STRUCTURE_BUILD_PLATFORM_CONFIG_V0)",
64
+ )
65
+ @click.option(
66
+ "--all-structures",
67
+ is_flag=True,
68
+ help="Compile all standard structures (Platform, Blockchain, AI Governance)",
69
+ )
70
+ @click.option(
71
+ "--verbose",
72
+ "-v",
73
+ is_flag=True,
74
+ help="Verbose output (full error context, stage metadata)",
75
+ )
76
+ def compile(
77
+ structure: tuple,
78
+ all_structures: bool,
79
+ verbose: bool,
80
+ ) -> None:
81
+ """
82
+ Compile STRUCTURE artifacts through the S1–S8 pipeline.
83
+
84
+ Chains 8 pure stages: EXTRACT → CANONICALIZE → SEMANTIC_ADDRESSING →
85
+ GOVERN → CONSTRUCT → PROJECT → MATERIALIZE → VERIFY.
86
+ """
87
+ structures = list(structure)
88
+
89
+ if all_structures:
90
+ structures = [
91
+ "STRUCTURE_BUILD_PLATFORM_CONFIG_V0",
92
+ "STRUCTURE_BUILD_BLOCKCHAIN_CONFIG_V0",
93
+ "STRUCTURE_BUILD_AI_GOVERNANCE_CONFIG_V0",
94
+ ]
95
+ elif not structures:
96
+ structures = ["STRUCTURE_BUILD_PLATFORM_CONFIG_V0"]
97
+
98
+ click.echo(f"Starting PGC build for {len(structures)} structure(s)")
99
+ click.echo()
100
+
101
+ success_count = 0
102
+ fail_count = 0
103
+
104
+ for struct_code in structures:
105
+ try:
106
+ agg_type = _get_aggregation_type(struct_code)
107
+ if agg_type:
108
+ raise ValueError(
109
+ f"'{struct_code}' declares aggregation_type '{agg_type}' — "
110
+ "the Phase B aggregation compile path is retired. Per-domain "
111
+ "vocabulary is materialized in Phase A (S7); cross-structure "
112
+ "query metadata (protocol_snapshot/artifact_index/) is emitted "
113
+ "by the compile pipeline (S7 MATERIALIZE). Aggregation STRUCTUREs are not "
114
+ "compilable."
115
+ )
116
+ _run_compile(struct_code, verbose=verbose)
117
+ success_count += 1
118
+ except CompilerError as e:
119
+ click.echo(f"Build failed for {struct_code}: {e.format(verbose=verbose)}", err=True)
120
+ fail_count += 1
121
+ except Exception as e:
122
+ click.echo(f"Build failed for {struct_code}: {e}", err=True)
123
+ fail_count += 1
124
+
125
+ click.echo("=" * 40)
126
+ click.echo(f"Build Summary: {success_count} succeeded, {fail_count} failed")
127
+
128
+ if fail_count > 0:
129
+ sys.exit(1)
130
+
131
+
132
+ @cli.command()
133
+ @click.option(
134
+ "--structure",
135
+ type=str,
136
+ required=True,
137
+ help="STRUCTURE artifact code (e.g. STRUCTURE_BUILD_BLOCKCHAIN_CONFIG_V0)",
138
+ )
139
+ @click.option(
140
+ "--artifact",
141
+ type=str,
142
+ default=None,
143
+ help="FQDN — show artifact identity, events, and causality chain",
144
+ )
145
+ @click.option(
146
+ "--upstream",
147
+ type=str,
148
+ default=None,
149
+ help="FQDN — walk causality chain upstream from this artifact",
150
+ )
151
+ @click.option(
152
+ "--family",
153
+ type=str,
154
+ default=None,
155
+ help="EventFamily name (e.g. CONSTRUCTION) — list all events in this family",
156
+ )
157
+ @click.option(
158
+ "--downstream",
159
+ type=str,
160
+ default=None,
161
+ help="FQDN — walk causality chain downstream from this artifact",
162
+ )
163
+ def inspect(
164
+ structure: str,
165
+ artifact: str | None,
166
+ upstream: str | None,
167
+ family: str | None,
168
+ downstream: str | None,
169
+ ) -> None:
170
+ """
171
+ Query evidence_graph.json for a compiled structure.
172
+
173
+ Exactly one of --artifact, --upstream, --downstream, or --family is required.
174
+ Reads from the materialized evidence_graph.json — does not recompile.
175
+ """
176
+ # Validate: exactly one query flag required
177
+ flags = [f for f in (artifact, upstream, downstream, family) if f is not None]
178
+ if len(flags) == 0:
179
+ click.echo(
180
+ "Error: provide exactly one of --artifact, --upstream, --downstream, or --family",
181
+ err=True,
182
+ )
183
+ sys.exit(1)
184
+ if len(flags) > 1:
185
+ click.echo(
186
+ "Error: --artifact, --upstream, --downstream, and --family are mutually exclusive",
187
+ err=True,
188
+ )
189
+ sys.exit(1)
190
+
191
+ # Resolve evidence_graph.json path
192
+ try:
193
+ evidence_graph_path = _resolve_evidence_graph_path(structure)
194
+ except Exception as e:
195
+ click.echo(f"Error: {e}", err=True)
196
+ sys.exit(1)
197
+
198
+ # Load via consumer contract
199
+ from compiler.visualization.consumers.evidence_reader import load_evidence_graph
200
+ from compiler.visualization.consumers.evidence_projection import EvidenceProjection
201
+
202
+ try:
203
+ query = load_evidence_graph(evidence_graph_path)
204
+ except (FileNotFoundError, ValueError) as e:
205
+ click.echo(f"Error loading evidence_graph.json: {e}", err=True)
206
+ sys.exit(1)
207
+
208
+ projection = EvidenceProjection(query)
209
+
210
+ # Dispatch
211
+ if artifact is not None:
212
+ _inspect_artifact(query, projection, artifact)
213
+ elif upstream is not None:
214
+ _inspect_upstream(query, projection, upstream)
215
+ elif downstream is not None:
216
+ _inspect_downstream(query, downstream)
217
+ elif family is not None:
218
+ _inspect_family(query, family)
219
+
220
+
221
+ # ---------------------------------------------------------------------------
222
+ # Build pipeline helpers
223
+ # ---------------------------------------------------------------------------
224
+
225
+ def _run_compile(structure: str, verbose: bool) -> None:
226
+ """
227
+ Run PGC compilation pipeline (S1-S8) for a single structure.
228
+
229
+ Each stage is a pure function: State -> State.
230
+ Pipeline halts on first stage with errors.
231
+ """
232
+ if not structure.startswith("STRUCTURE_BUILD_"):
233
+ click.echo(f"Invalid STRUCTURE artifact: {structure}", err=True)
234
+ raise ValueError(f"Invalid structure code: {structure}")
235
+
236
+ assert_structure_integrity(structure)
237
+
238
+ from compiler.structure_loader import load_structure_artifact, get_bootstrap_search_roots
239
+
240
+ try:
241
+ structure_config = load_structure_artifact(structure, get_bootstrap_search_roots())
242
+ except Exception as e:
243
+ raise RuntimeError(f"Failed to load STRUCTURE artifact {structure}: {e}") from e
244
+
245
+ structure_config["structure_artifact_code"] = structure
246
+
247
+ # Derive display label from structure code: STRUCTURE_BUILD_BLOCKCHAIN_CONFIG_V0 → "blockchain"
248
+ build_scope = structure.removeprefix("STRUCTURE_BUILD_").split("_CONFIG_")[0].lower()
249
+
250
+ click.echo(f"PGC Compiling {build_scope} ..")
251
+ click.echo(f" STRUCTURE: {structure}")
252
+ click.echo()
253
+
254
+ state = State.initial(structure_config)
255
+
256
+ stages = [
257
+ ("S1_EXTRACT", s1_extract),
258
+ ("S2_CANONICALIZE", s2_canonicalize),
259
+ ("S3_SEMANTIC_ADDRESSING", s3_semantic_addressing),
260
+ ("S4_GOVERN", s4_govern),
261
+ ("S5_CONSTRUCT", s5_construct),
262
+ ("S6_PROJECT", s6_project),
263
+ ("S7_MATERIALIZE", s7_materialize),
264
+ ("S8_VERIFY", s8_verify),
265
+ ("S9_ATTEST", s9_attest),
266
+ ]
267
+
268
+ for stage_name, stage_fn in stages:
269
+ click.echo(f" {stage_name}...")
270
+ state = stage_fn(state)
271
+
272
+ if verbose:
273
+ for key, value in state.stage_metadata.items():
274
+ click.echo(f" {key}: {value}")
275
+
276
+ if state.has_errors:
277
+ click.echo(f" {stage_name} failed with {len(state.errors)} error(s):", err=True)
278
+ for error in state.errors:
279
+ click.echo(f" {error.format(verbose=verbose)}", err=True)
280
+ # A refusal is a determination and is evidenced as fully as an admission (EV-3, EN-12).
281
+ # AI-8 permits exactly this: after refusal the governed state is as though the proposal
282
+ # had not been made, "except for the evidence that it was refused".
283
+ from compiler.diagnostics import determination
284
+ rec = determination.write(state, structure, "REFUSED", failed_at=stage_name)
285
+ if rec:
286
+ click.echo(f" determination recorded: {rec}", err=True)
287
+ else:
288
+ click.echo(" WARNING: determination record could not be written", err=True)
289
+ raise RuntimeError(f"PGC build failed at {stage_name} for {structure}")
290
+
291
+ if state.warnings:
292
+ click.echo(f" {len(state.warnings)} warning(s)")
293
+ if verbose:
294
+ for w in state.warnings:
295
+ click.echo(f" {w.message}")
296
+
297
+ click.echo()
298
+ click.echo(f" Trace events: {len(state.trace_events)}")
299
+ click.echo(f" Materialized: {len(state.materialized_paths)} artifacts")
300
+ verified = state.stage_metadata.get("verified", False)
301
+ attested = state.stage_metadata.get("attested", False)
302
+ click.echo(f" Verified: {verified}")
303
+ click.echo(f" Attested: {attested}")
304
+ click.echo()
305
+ click.echo(f"PGC build complete for {structure}!")
306
+
307
+ # The admission is evidenced by the same record shape as a refusal, so the two are comparable.
308
+ from compiler.diagnostics import determination
309
+ rec = determination.write(state, structure, "ADMITTED")
310
+ if rec:
311
+ click.echo(f" determination recorded: {rec}")
312
+
313
+ _machine_block_health(state, structure, verbose)
314
+
315
+ click.echo(f"\n{60*'='}\n")
316
+
317
+
318
+ def _machine_block_health(state: Any, structure: str, verbose: bool) -> None:
319
+ """Post-successful-build diagnostic: do artifacts declare machine-block keys the compiler
320
+ consumes? Heuristic, warning-only, scoped to kinds not already closed by schema conformance.
321
+ Never affects build success. See compiler.diagnostics.machine_block_health.
322
+ """
323
+ try:
324
+ from pathlib import Path
325
+ from compiler.diagnostics import machine_block_health as mbh
326
+ from compiler.structure_loader import load_structure_artifact, get_bootstrap_search_roots
327
+ dispatch = load_structure_artifact("STRUCTURE_SCHEMA_DISPATCH_V0", get_bootstrap_search_roots())
328
+ closed = set((dispatch.get("core", {}) or {}).get("schema_dispatch", {}) or {})
329
+ compiler_src = Path(mbh.__file__).resolve().parent.parent # compiler/ package root
330
+ report = mbh.check(state.graph.nodes.values(), closed, compiler_src)
331
+ for line in mbh.format_report(report, verbose):
332
+ click.echo(line)
333
+ except Exception as e: # diagnostics must never break a successful build
334
+ if verbose:
335
+ click.echo(f" (machine-block health diagnostic skipped: {e})")
336
+
337
+
338
+ def _get_aggregation_type(structure_code: str) -> str | None:
339
+ """
340
+ Return the aggregation_type declared in a STRUCTURE artifact, or None.
341
+
342
+ Aggregation structures (Phase Type B) have an `aggregation_type` field
343
+ and must NOT be routed through _run_compile().
344
+ """
345
+ from compiler.structure_loader import load_structure_artifact, get_bootstrap_search_roots
346
+ try:
347
+ config = load_structure_artifact(structure_code, get_bootstrap_search_roots())
348
+ return config.get("aggregation_type") or None
349
+ except Exception:
350
+ return None
351
+
352
+
353
+ # ---------------------------------------------------------------------------
354
+ # Inspect helpers
355
+ # ---------------------------------------------------------------------------
356
+
357
+ def _resolve_evidence_graph_path(structure_code: str) -> "Path":
358
+ """
359
+ Resolve the evidence_graph.json path for a given STRUCTURE artifact code.
360
+
361
+ Uses the same path resolution contract as S7_MATERIALIZE:
362
+ <evidence_projection_path>/<structure_id>/evidence_graph.json
363
+ """
364
+ from pathlib import Path
365
+ from compiler.structure_loader import load_structure_artifact, get_bootstrap_search_roots
366
+ from compiler.governance_engine.structure.resolution.layer_resolver import LayerResolver
367
+ from compiler.projections import get_structure_scope
368
+
369
+ try:
370
+ structure_config = load_structure_artifact(structure_code, get_bootstrap_search_roots())
371
+ except Exception as e:
372
+ raise RuntimeError(f"Failed to load STRUCTURE artifact {structure_code}: {e}") from e
373
+
374
+ structure_config["structure_artifact_code"] = structure_code
375
+
376
+ structure_id = get_structure_scope(structure_config)
377
+ if not structure_id:
378
+ raise RuntimeError(f"Unknown structure_id for {structure_code}")
379
+
380
+ resolver = LayerResolver()
381
+ try:
382
+ evi_root = resolver.resolve_output_path("evidence_projection_path", "", structure_config)
383
+ except Exception as e:
384
+ raise RuntimeError(f"Failed to resolve evidence_projection_path: {e}") from e
385
+
386
+ return Path(evi_root) / structure_id / "evidence_graph.json"
387
+
388
+
389
+ def _inspect_artifact(query: Any, projection: Any, fqdn: str) -> None:
390
+ """
391
+ Display artifact identity, compilation events, and causality chain.
392
+
393
+ Output priority:
394
+ 1. Artifact identity
395
+ 2. Stage/family events for this artifact
396
+ 3. Upstream causality chain (root-first)
397
+ 4. Materialization lineage
398
+ """
399
+ # 1 — Identity
400
+ click.echo()
401
+ click.echo(click.style("Artifact", bold=True) + f": {click.style(fqdn, fg='cyan')}")
402
+ click.echo(f"Structure: {query.structure_id}")
403
+ click.echo()
404
+
405
+ # 2 — Direct events for this artifact
406
+ direct_events = [
407
+ ev for ev in query.by_family("DISCOVERY")
408
+ + query.by_family("TOPOLOGY")
409
+ + query.by_family("CONSTRUCTION")
410
+ + query.by_family("PROJECTION")
411
+ + query.by_family("MATERIALIZATION")
412
+ + query.by_family("VERIFICATION")
413
+ + query.by_family("ADDRESSING")
414
+ + query.by_family("GOVERNANCE")
415
+ if ev.subject_fqdn == fqdn
416
+ ]
417
+ # Deduplicate by event_id (set() won't work — TraceEventDTO has a mutable dict field)
418
+ seen_ids: set[int] = set()
419
+ deduped: list[Any] = []
420
+ for ev in sorted(direct_events, key=lambda e: e.event_id):
421
+ if ev.event_id not in seen_ids:
422
+ seen_ids.add(ev.event_id)
423
+ deduped.append(ev)
424
+ direct_events = deduped
425
+
426
+ if direct_events:
427
+ click.echo(click.style(f"Events ({len(direct_events)}):", bold=True))
428
+ for ev in direct_events:
429
+ _print_event_line(ev, indent=2)
430
+ click.echo()
431
+ else:
432
+ click.echo(click.style("Events:", bold=True) + " (none — FQDN not found in trace)")
433
+ click.echo()
434
+
435
+ # 3 — Causality chain
436
+ chain = projection.artifact_provenance(fqdn)
437
+ if chain:
438
+ click.echo(click.style(f"Causality chain ({len(chain)} events, root-first):", bold=True))
439
+ _print_event_chain(chain, highlight_fqdn=fqdn, indent=2)
440
+ click.echo()
441
+ else:
442
+ click.echo(click.style("Causality chain:", bold=True) + " (no causal ancestors)")
443
+ click.echo()
444
+
445
+ # 4 — Materialization
446
+ written = [
447
+ ev for ev in direct_events
448
+ if ev.operation == "artifact_written" and "output_path" in ev.detail
449
+ ]
450
+ if written:
451
+ click.echo(click.style("Materialized to:", bold=True))
452
+ for ev in written:
453
+ click.echo(f" {ev.detail['output_path']}")
454
+ click.echo()
455
+
456
+
457
+ def _inspect_upstream(query: Any, projection: Any, fqdn: str) -> None:
458
+ """
459
+ Walk and display the causality chain upstream from an artifact.
460
+
461
+ Shows causal ancestors root-first, grouped by stage, with indentation.
462
+ """
463
+ click.echo()
464
+ click.echo(click.style("Upstream causality for", bold=True) + f": {click.style(fqdn, fg='cyan')}")
465
+ click.echo(f"Structure: {query.structure_id}")
466
+ click.echo()
467
+
468
+ chain = projection.artifact_provenance(fqdn)
469
+
470
+ if not chain:
471
+ click.echo(" (no causal ancestors — FQDN not found in trace or causality not wired)")
472
+ return
473
+
474
+ click.echo(click.style(f"Causal chain ({len(chain)} events, root-first):", bold=True))
475
+ _print_event_chain_by_stage(chain, highlight_fqdn=fqdn)
476
+ click.echo()
477
+
478
+
479
+ def _inspect_downstream(query: Any, fqdn: str) -> None:
480
+ """
481
+ Walk and display the causality chain downstream from an artifact.
482
+
483
+ Finds all seed events where subject_fqdn == fqdn, then BFS-walks forward
484
+ through CAUSALITY edges using query.downstream(), collecting all descendants.
485
+ Output is grouped by stage, sorted by event_id.
486
+ """
487
+ click.echo()
488
+ click.echo(click.style("Downstream causality for", bold=True) + f": {click.style(fqdn, fg='cyan')}")
489
+ click.echo(f"Structure: {query.structure_id}")
490
+ click.echo()
491
+
492
+ # Find seed events — events whose subject is this artifact
493
+ seed_events = [
494
+ ev for ev in (
495
+ query.by_family("DISCOVERY")
496
+ + query.by_family("TOPOLOGY")
497
+ + query.by_family("CONSTRUCTION")
498
+ + query.by_family("PROJECTION")
499
+ + query.by_family("MATERIALIZATION")
500
+ + query.by_family("VERIFICATION")
501
+ + query.by_family("ADDRESSING")
502
+ + query.by_family("GOVERNANCE")
503
+ )
504
+ if ev.subject_fqdn == fqdn
505
+ ]
506
+ # Deduplicate seeds by event_id
507
+ seen_ids: set[int] = set()
508
+ seeds: list[Any] = []
509
+ for ev in sorted(seed_events, key=lambda e: e.event_id):
510
+ if ev.event_id not in seen_ids:
511
+ seen_ids.add(ev.event_id)
512
+ seeds.append(ev)
513
+
514
+ if not seeds:
515
+ click.echo(" (no events found — FQDN not in trace or causality not wired)")
516
+ return
517
+
518
+ # BFS forward through CAUSALITY edges
519
+ visited: set[int] = set()
520
+ frontier: list[Any] = list(seeds)
521
+ all_descendants: list[Any] = []
522
+
523
+ for seed in seeds:
524
+ visited.add(seed.event_id)
525
+ all_descendants.append(seed)
526
+
527
+ while frontier:
528
+ next_frontier: list[Any] = []
529
+ for ev in frontier:
530
+ children = query.downstream(ev.event_id)
531
+ for child in children:
532
+ if child.event_id not in visited:
533
+ visited.add(child.event_id)
534
+ all_descendants.append(child)
535
+ next_frontier.append(child)
536
+ frontier = next_frontier
537
+
538
+ all_descendants.sort(key=lambda e: e.event_id)
539
+
540
+ click.echo(
541
+ click.style(f"Downstream events ({len(all_descendants)} total, seed-first):", bold=True)
542
+ )
543
+ _print_event_chain_by_stage(all_descendants, highlight_fqdn=fqdn)
544
+ click.echo()
545
+
546
+
547
+ def _inspect_family(query: Any, family_name: str) -> None:
548
+ """
549
+ List all events in an EventFamily, grouped by stage.
550
+ """
551
+ available = query.families()
552
+ events = query.by_family(family_name)
553
+
554
+ click.echo()
555
+ if not events:
556
+ click.echo(
557
+ f"Family '{family_name}' not found. Available families: "
558
+ + ", ".join(available)
559
+ )
560
+ return
561
+
562
+ click.echo(
563
+ click.style("Family", bold=True)
564
+ + f": {click.style(family_name, fg='yellow')} "
565
+ + click.style(f"({len(events)} events)", dim=True)
566
+ )
567
+ click.echo(f"Structure: {query.structure_id}")
568
+ click.echo()
569
+
570
+ # Group by stage in first-seen order
571
+ by_stage: dict[str, list[Any]] = {}
572
+ for ev in sorted(events, key=lambda e: e.event_id):
573
+ by_stage.setdefault(ev.stage, []).append(ev)
574
+
575
+ for stage, stage_events in by_stage.items():
576
+ click.echo(click.style(f" {stage}", bold=True) + f" ({len(stage_events)} events)")
577
+ for ev in stage_events:
578
+ _print_event_line(ev, indent=4)
579
+ click.echo()
580
+
581
+
582
+ # ---------------------------------------------------------------------------
583
+ # Formatting helpers
584
+ # ---------------------------------------------------------------------------
585
+
586
+ def _print_event_line(ev: Any, indent: int = 2) -> None:
587
+ """Print a single event as one terminal line."""
588
+ prefix = " " * indent
589
+ op = click.style(ev.operation, fg="green")
590
+ stage_tag = click.style(f"[{ev.stage}/{ev.family}]", dim=True)
591
+ subject = f" {click.style(ev.subject_fqdn, fg='cyan')}" if ev.subject_fqdn else ""
592
+ click.echo(f"{prefix}#{ev.event_id:<4} {op} {stage_tag}{subject}")
593
+
594
+
595
+ def _print_event_chain(events: list[Any], highlight_fqdn: str | None, indent: int = 2) -> None:
596
+ """Print a flat causality chain, root-first, one event per line."""
597
+ prefix = " " * indent
598
+ for ev in events:
599
+ is_subject = ev.subject_fqdn == highlight_fqdn
600
+ op = click.style(ev.operation, fg="green" if not is_subject else "white", bold=is_subject)
601
+ stage_tag = click.style(f"[{ev.stage}]", dim=True)
602
+ family_tag = click.style(f"[{ev.family}]", dim=True)
603
+ subject = (
604
+ f" {click.style(ev.subject_fqdn, fg='cyan', bold=is_subject)}"
605
+ if ev.subject_fqdn else ""
606
+ )
607
+ marker = click.style(" ◀", fg="yellow") if is_subject else ""
608
+ click.echo(f"{prefix}#{ev.event_id:<4} {op} {stage_tag} {family_tag}{subject}{marker}")
609
+
610
+
611
+ def _print_event_chain_by_stage(events: list[Any], highlight_fqdn: str | None = None) -> None:
612
+ """Print a causality chain grouped by stage, with indentation per stage."""
613
+ by_stage: dict[str, list[Any]] = {}
614
+ stage_order: list[str] = []
615
+ for ev in events:
616
+ if ev.stage not in by_stage:
617
+ stage_order.append(ev.stage)
618
+ by_stage.setdefault(ev.stage, []).append(ev)
619
+
620
+ for stage in stage_order:
621
+ stage_events = by_stage[stage]
622
+ click.echo(f" {click.style(stage, bold=True)} ({len(stage_events)} events)")
623
+ for ev in stage_events:
624
+ is_subject = ev.subject_fqdn == highlight_fqdn
625
+ op = click.style(ev.operation, fg="green", bold=is_subject)
626
+ family_tag = click.style(f"[{ev.family}]", dim=True)
627
+ subject = (
628
+ f" {click.style(ev.subject_fqdn, fg='cyan', bold=is_subject)}"
629
+ if ev.subject_fqdn else ""
630
+ )
631
+ marker = click.style(" ◀", fg="yellow") if is_subject else ""
632
+ click.echo(f" #{ev.event_id:<4} {op} {family_tag}{subject}{marker}")
633
+ click.echo()
634
+
635
+
636
+ if __name__ == "__main__":
637
+ cli()
@@ -0,0 +1,6 @@
1
+ """Post-compile diagnostics — observations about a successful build, never gates.
2
+
3
+ Diagnostics run AFTER the S1–S9 pipeline. They do not participate in success/failure and
4
+ never change compiled output. Their job is to let the compiler observe itself — e.g. detect
5
+ when the declaration language has advanced beyond the compiler's consumption surface.
6
+ """