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.
- compiler/VERSION +1 -0
- compiler/__init__.py +1 -0
- compiler/atoms/__init__.py +54 -0
- compiler/atoms/error_codes.py +160 -0
- compiler/atoms/errors.py +125 -0
- compiler/atoms/fqdn.py +204 -0
- compiler/atoms/invariants.py +106 -0
- compiler/atoms/phase.py +99 -0
- compiler/atoms/pipeline.py +51 -0
- compiler/atoms/snapshot_gate.py +51 -0
- compiler/atoms/sorting.py +125 -0
- compiler/cli.py +637 -0
- compiler/diagnostics/__init__.py +6 -0
- compiler/diagnostics/determination.py +148 -0
- compiler/diagnostics/machine_block_health.py +199 -0
- compiler/governance/__init__.py +6 -0
- compiler/governance_engine/__init__.py +0 -0
- compiler/governance_engine/artifact_kinds.py +221 -0
- compiler/governance_engine/assertions/__init__.py +0 -0
- compiler/governance_engine/assertions/handlers/__init__.py +200 -0
- compiler/governance_engine/assertions/handlers/assert_ac_declaration_well_formed_v0.py +70 -0
- compiler/governance_engine/assertions/handlers/assert_actor_authority_separation_v0.py +17 -0
- compiler/governance_engine/assertions/handlers/assert_artifact_content_hash_declared_v0.py +60 -0
- compiler/governance_engine/assertions/handlers/assert_assert_not_runtime_referenced_v0.py +49 -0
- compiler/governance_engine/assertions/handlers/assert_assert_parity_v0.py +169 -0
- compiler/governance_engine/assertions/handlers/assert_atom_output_purity_v0.py +186 -0
- compiler/governance_engine/assertions/handlers/assert_authority_constituted_v0.py +56 -0
- compiler/governance_engine/assertions/handlers/assert_authority_required_for_execution_v0.py +16 -0
- compiler/governance_engine/assertions/handlers/assert_authority_state_well_formed_v0.py +17 -0
- compiler/governance_engine/assertions/handlers/assert_binding_integrity_v0.py +64 -0
- compiler/governance_engine/assertions/handlers/assert_binding_surface_closed_v0.py +92 -0
- compiler/governance_engine/assertions/handlers/assert_cc_capability_binding_valid_v0.py +85 -0
- compiler/governance_engine/assertions/handlers/assert_cc_inputs_satisfied_v0.py +89 -0
- compiler/governance_engine/assertions/handlers/assert_cc_no_implicit_chaining_v0.py +89 -0
- compiler/governance_engine/assertions/handlers/assert_cc_no_missing_dependencies_v0.py +89 -0
- compiler/governance_engine/assertions/handlers/assert_cc_no_unused_outputs_v0.py +81 -0
- compiler/governance_engine/assertions/handlers/assert_cc_storage_op_conformance_v0.py +74 -0
- compiler/governance_engine/assertions/handlers/assert_compiler_governance_declared_v0.py +65 -0
- compiler/governance_engine/assertions/handlers/assert_compiler_no_execution_v0.py +86 -0
- compiler/governance_engine/assertions/handlers/assert_concern_not_authority_v0.py +51 -0
- compiler/governance_engine/assertions/handlers/assert_conformance_assertion_mode_valid_v0.py +35 -0
- compiler/governance_engine/assertions/handlers/assert_cryptographic_trust_declared_v0.py +51 -0
- compiler/governance_engine/assertions/handlers/assert_cs_isolated_execution_v0.py +35 -0
- compiler/governance_engine/assertions/handlers/assert_cs_surface_closed_v0.py +241 -0
- compiler/governance_engine/assertions/handlers/assert_cs_traceable_v0.py +35 -0
- compiler/governance_engine/assertions/handlers/assert_ct_output_contract_match_v0.py +167 -0
- compiler/governance_engine/assertions/handlers/assert_ct_surface_closed_v0.py +173 -0
- compiler/governance_engine/assertions/handlers/assert_ct_surface_derived_closed_v0.py +108 -0
- compiler/governance_engine/assertions/handlers/assert_ct_test_data_outcome_declared_v0.py +112 -0
- compiler/governance_engine/assertions/handlers/assert_ev_append_only_v0.py +70 -0
- compiler/governance_engine/assertions/handlers/assert_ev_schema_required_v0.py +59 -0
- compiler/governance_engine/assertions/handlers/assert_execution_placement_declared_v0.py +51 -0
- compiler/governance_engine/assertions/handlers/assert_execution_scheduling_declared_v0.py +51 -0
- compiler/governance_engine/assertions/handlers/assert_fqdn_namespace_authorized_v0.py +38 -0
- compiler/governance_engine/assertions/handlers/assert_fqdn_only_references_v0.py +89 -0
- compiler/governance_engine/assertions/handlers/assert_governance_declaration_resolves_v0.py +126 -0
- compiler/governance_engine/assertions/handlers/assert_handler_registry_closed_v0.py +75 -0
- compiler/governance_engine/assertions/handlers/assert_identity_authority_separation_v0.py +19 -0
- compiler/governance_engine/assertions/handlers/assert_identity_fqdn_consistency.py +75 -0
- compiler/governance_engine/assertions/handlers/assert_implementation_admissible_v0.py +66 -0
- compiler/governance_engine/assertions/handlers/assert_in_no_execution_logic_v0.py +82 -0
- compiler/governance_engine/assertions/handlers/assert_in_schema_required_v0.py +74 -0
- compiler/governance_engine/assertions/handlers/assert_in_workflow_binding_v0.py +101 -0
- compiler/governance_engine/assertions/handlers/assert_inspection_capability_read_only_v0.py +114 -0
- compiler/governance_engine/assertions/handlers/assert_no_ambient_authority_v0.py +17 -0
- compiler/governance_engine/assertions/handlers/assert_no_runtime_authorization_v0.py +18 -0
- compiler/governance_engine/assertions/handlers/assert_no_runtime_topology_synthesis_v0.py +18 -0
- compiler/governance_engine/assertions/handlers/assert_no_smart_execution_v0.py +102 -0
- compiler/governance_engine/assertions/handlers/assert_no_undeclared_behavior_surface_v0.py +52 -0
- compiler/governance_engine/assertions/handlers/assert_no_workflow_authorization_logic_v0.py +18 -0
- compiler/governance_engine/assertions/handlers/assert_protocol_surface_closed_v0.py +119 -0
- compiler/governance_engine/assertions/handlers/assert_rb_binding_policy_conformance_v0.py +124 -0
- compiler/governance_engine/assertions/handlers/assert_rb_cs_only_v0.py +68 -0
- compiler/governance_engine/assertions/handlers/assert_rb_no_logic_v0.py +81 -0
- compiler/governance_engine/assertions/handlers/assert_rb_parameters_declared_v0.py +117 -0
- compiler/governance_engine/assertions/handlers/assert_rb_storage_subdomain_owned_v0.py +118 -0
- compiler/governance_engine/assertions/handlers/assert_runtime_invariant_wired_v0.py +163 -0
- compiler/governance_engine/assertions/handlers/assert_schema_conformance_v0.py +64 -0
- compiler/governance_engine/assertions/handlers/assert_schema_description_well_formed_v0.py +97 -0
- compiler/governance_engine/assertions/handlers/assert_security_domain_declared_v0.py +51 -0
- compiler/governance_engine/assertions/handlers/assert_structure_paths_well_formed_v0.py +118 -0
- compiler/governance_engine/assertions/handlers/assert_superseded_not_referenced_v0.py +134 -0
- compiler/governance_engine/assertions/handlers/assert_test_data_match_ct_output_v0.py +134 -0
- compiler/governance_engine/assertions/handlers/assert_topology_acyclic_v0.py +53 -0
- compiler/governance_engine/assertions/handlers/assert_topology_authority_orthogonal_v0.py +110 -0
- compiler/governance_engine/assertions/handlers/assert_topology_capability_reference_unique_v0.py +63 -0
- compiler/governance_engine/assertions/handlers/assert_topology_contract_closed_v0.py +113 -0
- compiler/governance_engine/assertions/handlers/assert_topology_immutable_after_compilation_v0.py +17 -0
- compiler/governance_engine/assertions/handlers/assert_topology_input_reference_declared_v0.py +112 -0
- compiler/governance_engine/assertions/handlers/assert_topology_routing_complete_v0.py +82 -0
- compiler/governance_engine/assertions/handlers/assert_topology_step_declared_v0.py +77 -0
- compiler/governance_engine/assertions/handlers/assert_topology_step_id_unique_v0.py +60 -0
- compiler/governance_engine/assertions/handlers/assert_topology_surface_canonical_v0.py +180 -0
- compiler/governance_engine/assertions/handlers/assert_topology_transport_orthogonal_v0.py +113 -0
- compiler/governance_engine/assertions/handlers/assert_trace_authority_binding_required_v0.py +18 -0
- compiler/governance_engine/assertions/handlers/assert_transport_canonical_normalization_v0.py +83 -0
- compiler/governance_engine/assertions/handlers/assert_transport_no_dynamic_routing_v0.py +93 -0
- compiler/governance_engine/assertions/handlers/assert_transport_no_workflow_semantics_v0.py +106 -0
- compiler/governance_engine/assertions/handlers/assert_transport_operation_identity_independence_v0.py +56 -0
- compiler/governance_engine/assertions/handlers/assert_transport_response_projection_external_v0.py +38 -0
- compiler/governance_engine/assertions/handlers/assert_transport_result_class_protocol_independence_v0.py +48 -0
- compiler/governance_engine/assertions/handlers/assert_transport_target_exists_v0.py +132 -0
- compiler/governance_engine/assertions/handlers/assert_unique_artifact_id_v0.py +58 -0
- compiler/governance_engine/assertions/handlers/assert_vocabulary_symbols_well_formed_v0.py +163 -0
- compiler/governance_engine/assertions/handlers/assert_wf_announcement_distinct_v0.py +85 -0
- compiler/governance_engine/assertions/handlers/assert_wf_cc_only_nodes_v0.py +60 -0
- compiler/governance_engine/assertions/handlers/assert_wf_entry_intent_v0.py +68 -0
- compiler/governance_engine/assertions/handlers/assert_wf_execution_path_valid_v0.py +89 -0
- compiler/governance_engine/assertions/handlers/assert_wf_node_key_binding_unique_v0.py +107 -0
- compiler/governance_engine/platform_root.py +102 -0
- compiler/governance_engine/structure/__init__.py +56 -0
- compiler/governance_engine/structure/discovery/__init__.py +28 -0
- compiler/governance_engine/structure/discovery/artifact_locator.py +210 -0
- compiler/governance_engine/structure/exceptions.py +113 -0
- compiler/governance_engine/structure/loading/__init__.py +88 -0
- compiler/governance_engine/structure/loading/fs_reader.py +225 -0
- compiler/governance_engine/structure/loading/markdown_parser.py +148 -0
- compiler/governance_engine/structure/loading/protocol_loader.py +587 -0
- compiler/governance_engine/structure/loading/test_case_provider.py +125 -0
- compiler/governance_engine/structure/loading/trace_reader.py +98 -0
- compiler/governance_engine/structure/loading/vocabulary_loader.py +174 -0
- compiler/governance_engine/structure/loading/yaml_parser.py +209 -0
- compiler/governance_engine/structure/resolution/__init__.py +22 -0
- compiler/governance_engine/structure/resolution/domain_resolver.py +124 -0
- compiler/governance_engine/structure/resolution/layer_resolver.py +572 -0
- compiler/governance_engine/structure/resolution/path_registry.py +1054 -0
- compiler/graph/__init__.py +38 -0
- compiler/graph/addressing.py +153 -0
- compiler/graph/edge.py +100 -0
- compiler/graph/evidence.py +637 -0
- compiler/graph/graph.py +269 -0
- compiler/graph/hashing.py +89 -0
- compiler/graph/node.py +134 -0
- compiler/graph/query.py +215 -0
- compiler/graph/state.py +237 -0
- compiler/graph/trace.py +97 -0
- compiler/graph/types.py +71 -0
- compiler/projections/__init__.py +207 -0
- compiler/projections/artifact_index.py +225 -0
- compiler/projections/canonical.py +173 -0
- compiler/projections/dispatch.py +532 -0
- compiler/projections/evidence.py +170 -0
- compiler/projections/handlers.py +224 -0
- compiler/projections/ipm.py +388 -0
- compiler/projections/reverse.py +165 -0
- compiler/projections/tokenized.py +135 -0
- compiler/projections/vocabulary.py +108 -0
- compiler/stages/__init__.py +28 -0
- compiler/stages/s1_extract.py +867 -0
- compiler/stages/s2_canonicalize.py +516 -0
- compiler/stages/s3_semantic_addressing.py +66 -0
- compiler/stages/s4_govern.py +754 -0
- compiler/stages/s5_construct.py +537 -0
- compiler/stages/s6_project.py +94 -0
- compiler/stages/s7_materialize.py +1170 -0
- compiler/stages/s8_verify.py +643 -0
- compiler/stages/s9_attest.py +184 -0
- compiler/structure_loader.py +132 -0
- compiler/visualization/__init__.py +12 -0
- compiler/visualization/consumers/__init__.py +28 -0
- compiler/visualization/consumers/evidence_projection.py +162 -0
- compiler/visualization/consumers/evidence_query.py +296 -0
- compiler/visualization/consumers/evidence_reader.py +127 -0
- compiler/visualization/views/__init__.py +70 -0
- compiler/visualization/views/_png_writer.py +45 -0
- compiler/visualization/views/artifact_lineage.py +180 -0
- compiler/visualization/views/family_view.py +164 -0
- compiler/visualization/views/materialization_view.py +150 -0
- compiler/visualization/views/structural_dag.py +158 -0
- compiler/visualization/wf_graph_generator.py +534 -0
- pgc_compiler-2.0.0.dist-info/METADATA +203 -0
- pgc_compiler-2.0.0.dist-info/RECORD +177 -0
- pgc_compiler-2.0.0.dist-info/WHEEL +5 -0
- pgc_compiler-2.0.0.dist-info/entry_points.txt +2 -0
- pgc_compiler-2.0.0.dist-info/licenses/LICENSE +67 -0
- pgc_compiler-2.0.0.dist-info/licenses/NOTICE +11 -0
- 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
|
+
"""
|