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
@@ -0,0 +1,148 @@
1
+ """Construction determination records — evidence that a determination was made, including refusals.
2
+
3
+ `3e` §3.1: for any determination, its evidence must be sufficient to establish which closure applied,
4
+ which rules that closure supplied, what each predicate yielded, what the dominant consequence was,
5
+ and that the resulting state is what that consequence permitted.
6
+
7
+ Construction previously produced none. A refused build wrote a diagnostic to stderr and exited 1, so
8
+ the determination *this candidate set is inadmissible under this closure* — the exact subject of
9
+ `3e` §3.3 — left no checkable record anywhere. Admissions were evidenced by their output; refusals
10
+ by nothing, which is EV-3 and EN-12 breached in the direction that matters, since a system recording
11
+ what it permitted and not what it refused has a record of its work proceeding rather than of its
12
+ governance operating.
13
+
14
+ **A record is not usable output.** `4a` GC-6 forbids a refused construction producing usable output,
15
+ and `1c` AI-8 states the boundary exactly: after refusal the governed state is as though the proposal
16
+ had not been made, *"except for the evidence that it was refused."* The realization's own technique
17
+ table lists "a failed build writing nothing" as serving AI-8; that technique overshot the property it
18
+ served, and this is the correction.
19
+
20
+ Records are written outside the compiled output tree. Nothing reads them — they are evidence, and
21
+ `3e` §3.2 makes evidence output only.
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import json
26
+ import os
27
+ import time
28
+ from pathlib import Path
29
+ from typing import Any
30
+
31
+
32
+ RECORD_VERSION = "v0"
33
+
34
+
35
+ def _root() -> Path:
36
+ """Where determination records are written — outside the compiled output tree.
37
+
38
+ `PGC_SNAPSHOT_ROOT` is set per build by the runners; the compiled projections live under
39
+ `<root>/compiled`, so records sit beside them rather than within them. A record inside the output
40
+ would be output, and GC-6 is about what a refused construction leaves behind.
41
+ """
42
+ root = os.environ.get("PGC_SNAPSHOT_ROOT")
43
+ if not root:
44
+ # No fallback. A relative default would write the record wherever the process happened to be
45
+ # standing — which put it inside the assembled snapshot the first time this ran, where it
46
+ # would have become undeclared content at acceptance. An unlocatable evidence root is a
47
+ # refusal to write, reported, not a guess.
48
+ raise RuntimeError(
49
+ "PGC_SNAPSHOT_ROOT is not set — there is no declared root to write the determination "
50
+ "record to, and a relative default would place evidence by accident."
51
+ )
52
+ return Path(root) / "determinations"
53
+
54
+
55
+ def _closure(state: Any) -> dict[str, Any]:
56
+ """Which closure applied — `3e` §3.1 point 1."""
57
+ meta = dict(getattr(state, "stage_metadata", {}) or {})
58
+ closure = dict(meta.get("governance_closure") or {})
59
+ return {
60
+ # The closure enumerated. `rules_supplied` and `predicate_results` name every governing
61
+ # element that applied and by what invariant identity — that enumeration is what establishes
62
+ # point 1; the hash below is a compact identifier for the same set, present only where a
63
+ # closure was imported. The platform IS the governance surface and imports none, so it
64
+ # carries no hash and its closure is established by the enumeration alone.
65
+ "authorized_namespaces": list(meta.get("authorized_namespaces", []) or []),
66
+ "imported_governance": closure or None,
67
+ }
68
+
69
+
70
+ def _rules_and_predicates(state: Any) -> tuple[list[str], list[dict[str, Any]]]:
71
+ """Which rules the closure supplied, and what each predicate yielded — points 2 and 3.
72
+
73
+ `assertion_coverage` is what `s4_govern` already records per derived assertion. It is lifted
74
+ rather than recomputed: a second derivation of one fact is two facts.
75
+ """
76
+ coverage = list((getattr(state, "stage_metadata", {}) or {}).get("assertion_coverage", []) or [])
77
+ supplied = sorted({c.get("assert_code", "") for c in coverage if c.get("assert_code")})
78
+ yielded = [
79
+ {
80
+ "rule": c.get("assert_code"),
81
+ "invariant": c.get("fqdn_id"),
82
+ "result": c.get("status"),
83
+ "violations": c.get("violations", 0),
84
+ "imported": c.get("imported", False),
85
+ }
86
+ for c in coverage
87
+ ]
88
+ return supplied, yielded
89
+
90
+
91
+ def write(state: Any, structure: str, consequence: str, failed_at: str | None = None) -> Path | None:
92
+ """Write the determination record for one construction. Returns its path, or None if unwritable.
93
+
94
+ `consequence` is ADMITTED or REFUSED. Both are written, and written the same way: EV-3 requires
95
+ refusals evidenced as fully as admissions, and a record shape that differed between them would
96
+ make the two incomparable — which is how a system ends up able to show what it permitted and not
97
+ what it refused.
98
+ """
99
+ supplied, yielded = _rules_and_predicates(state)
100
+ errors = list(getattr(state, "errors", []) or [])
101
+ materialized = list(getattr(state, "materialized_paths", []) or [])
102
+
103
+ record = {
104
+ "record_version": RECORD_VERSION,
105
+ "record_type": "construction_determination",
106
+ "consequence": consequence,
107
+ # 1 — which closure applied
108
+ "closure": _closure(state),
109
+ # 2 — which rules that closure supplied
110
+ "rules_supplied": supplied,
111
+ # 3 — what each predicate yielded
112
+ "predicate_results": yielded,
113
+ # what was proposed
114
+ "proposed": {
115
+ "structure": structure,
116
+ "candidates": len(getattr(state, "graph", None).nodes) if getattr(state, "graph", None) else None,
117
+ },
118
+ # 4 — the dominant consequence, and its grounds
119
+ "grounds": [
120
+ {
121
+ "code": getattr(e, "code", None).value if hasattr(getattr(e, "code", None), "value") else str(getattr(e, "code", "")),
122
+ "phase": getattr(e, "phase", None),
123
+ "fqdn_id": getattr(e, "fqdn_id", None),
124
+ "message": getattr(e, "message", ""),
125
+ }
126
+ for e in errors
127
+ ],
128
+ # 5 — that the resulting state is what the consequence permitted
129
+ "resulting_state": {
130
+ "failed_at": failed_at,
131
+ "materialized_count": len(materialized),
132
+ "nothing_proceeded": consequence == "REFUSED" and not materialized,
133
+ },
134
+ # Observational content, declared as such (`3e` §5): it may differ between two determinations
135
+ # over the same inputs and no determination may depend on it.
136
+ "observational": {"determined_at_ns": time.time_ns()},
137
+ }
138
+
139
+ try:
140
+ root = _root()
141
+ root.mkdir(parents=True, exist_ok=True)
142
+ path = root / f"{structure}.{consequence.lower()}.json"
143
+ path.write_text(json.dumps(record, indent=2, sort_keys=False) + "\n", encoding="utf-8")
144
+ return path
145
+ except (OSError, RuntimeError):
146
+ # Evidence that cannot be written is a finding, not a reason to fail the determination it
147
+ # records — the determination already happened. Surfaced by the caller, never swallowed.
148
+ return None
@@ -0,0 +1,199 @@
1
+ """Machine-block health — does the compiler consume the machine-block keys artifacts declare?
2
+
3
+ A heuristic, post-successful-build diagnostic. For each compiled artifact whose kind is NOT
4
+ already closed by schema conformance, it flattens the machine block to leaf keys and checks
5
+ whether each leaf name appears as a string literal in the compiler source. A leaf with no
6
+ detected consumer is a *candidate* — not proof — of an unconsumed declaration.
7
+
8
+ Scope rule (principled, not an arbitrary list): the diagnostic applies only to artifact kinds
9
+ whose machine-block consumption is NOT already closed by `additionalProperties: false` schema
10
+ conformance. For schema-closed kinds an unconsumed key cannot silently survive, so a census
11
+ adds only noise.
12
+
13
+ Disposition (Machine Block §11): a top-level field with no detected compiler consumer is split
14
+ by its kind's disposition. On a DECLARATIVE kind (STRUCTURE, VOCAB, … — inert definitions the
15
+ topology references), such a field is *preserved* declared policy carried into the snapshot for
16
+ downstream/runtime reference; that is a legitimate disposition, not a defect.
17
+
18
+ A field on an EXECUTABLE kind may also be preserved, but only by NAMING its consumer in
19
+ `_DOWNSTREAM_CONSUMERS`. Boundary contracts (TI/TE) are the standing case: the compiler seals
20
+ them and the transport engine or the inspector interprets them, and the compiler cannot import
21
+ either — so the scan structurally cannot see those consumers. Everything else on an EXECUTABLE
22
+ kind is a genuine *candidate unconsumed*.
23
+
24
+ Severity: WARNING only, and only for genuine candidates. It never fails the build, changes
25
+ runtime behavior, or asserts a conformance rule. The authoritative rule is the compiler's actual
26
+ behavior (and, where it applies, schema closure). A candidate means exactly: "this machine-block
27
+ key has no detected compiler consumer" — the source-inspection method cannot prove a key unused.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import re
33
+ from collections import defaultdict
34
+ from collections.abc import Mapping, Sequence
35
+ from dataclasses import dataclass, field
36
+ from pathlib import Path
37
+ from typing import Any, Iterable
38
+
39
+ from compiler.governance_engine.artifact_kinds import REGISTRY, DECLARATIVE
40
+
41
+ _LITERAL = re.compile(r"""['"]([A-Za-z_][A-Za-z0-9_]*)['"]""")
42
+
43
+ # Keys on an EXECUTABLE kind whose consumer is, BY ARCHITECTURE, outside the compiler.
44
+ #
45
+ # A boundary contract is a declaration the compiler seals and another component interprets. The
46
+ # compiler cannot import that component — purity forbids it — so the source scan structurally
47
+ # cannot see the consumer, and the key reads as unconsumed however live it is. Declaring it here
48
+ # keeps the census honest: a false candidate teaches a reader to ignore the warning, and a
49
+ # genuinely dead key then hides among the ones they have learned to skip.
50
+ #
51
+ # Each entry NAMES its consumer, so the claim is checkable rather than a blanket exemption. A key
52
+ # that no component reads must not be listed — that is the finding the census exists to surface.
53
+ _DOWNSTREAM_CONSUMERS: dict[tuple[str, str], str] = {
54
+ ("TE", "evidence_policy"): "consumed by the transport resolver (resolver/resolver.py)",
55
+ ("TI", "catalog"): "consumed by snapshot_inspector (inspector/catalog.py)",
56
+ ("WF", "subdomain"): "consumed by snapshot_assembler (assembler/indexes.py)",
57
+ }
58
+
59
+ _DECLARATIVE_POLICY = "declarative policy, no compiler consumer"
60
+
61
+
62
+ @dataclass
63
+ class KindReport:
64
+ consumed: set[str] = field(default_factory=set)
65
+ unconsumed: set[str] = field(default_factory=set)
66
+ preserved: dict[str, str] = field(default_factory=dict) # key → why it is not a candidate
67
+
68
+
69
+ @dataclass
70
+ class HealthReport:
71
+ by_kind: dict[str, KindReport] = field(default_factory=dict)
72
+
73
+ @property
74
+ def examined(self) -> int:
75
+ return sum(len(k.consumed) + len(k.unconsumed) + len(k.preserved)
76
+ for k in self.by_kind.values())
77
+
78
+ @property
79
+ def candidate_unconsumed(self) -> int:
80
+ return sum(len(k.unconsumed) for k in self.by_kind.values())
81
+
82
+ @property
83
+ def preserved_total(self) -> int:
84
+ return sum(len(k.preserved) for k in self.by_kind.values())
85
+
86
+ @property
87
+ def has_candidates(self) -> bool:
88
+ return self.candidate_unconsumed > 0
89
+
90
+
91
+ def _source_literals(compiler_src: Path) -> set[str]:
92
+ """Every quoted identifier appearing anywhere in the compiler source (its consumption surface).
93
+
94
+ This module is excluded from its own scan. Its literals name keys in order to CLASSIFY them,
95
+ never to consume them — counting those would let the census answer its own question, reporting
96
+ a key as compiler-consumed on the strength of the table that says it is consumed elsewhere.
97
+ """
98
+ out: set[str] = set()
99
+ for py in compiler_src.rglob("*.py"):
100
+ if "__pycache__" in py.parts:
101
+ continue
102
+ if py.resolve() == Path(__file__).resolve():
103
+ continue
104
+ try:
105
+ out.update(_LITERAL.findall(py.read_text(encoding="utf-8", errors="ignore")))
106
+ except OSError:
107
+ continue
108
+ return out
109
+
110
+
111
+ def _declared_fields(fm: Any) -> Iterable[str]:
112
+ """The top-level declared fields of a machine block.
113
+
114
+ Deliberately top-level only: the health question is whether the compiler consumes the
115
+ *fields an artifact declares*. A field's internal shape (nested data — layer names, paths,
116
+ enum values) is that field's own business, read structurally rather than by literal key
117
+ name, and flattening into it produces heuristic noise, not signal. (frontmatter is a
118
+ MappingProxyType, so accept any Mapping.)
119
+ """
120
+ if isinstance(fm, Mapping):
121
+ for k in fm.keys():
122
+ yield str(k)
123
+
124
+
125
+ def check(nodes: Iterable[Any], schema_closed_prefixes: set[str], compiler_src: Path) -> HealthReport:
126
+ """Build the health report over compiled graph nodes."""
127
+ literals = _source_literals(compiler_src)
128
+ # Fail safe: a real compiler source has thousands of literals. If the scan finds far fewer,
129
+ # source is not introspectable (e.g. installed without .py) and every field would falsely
130
+ # read as unconsumed — so report nothing rather than a spurious alarm.
131
+ if len(literals) < 200:
132
+ return HealthReport(by_kind={})
133
+ by_kind: dict[str, KindReport] = defaultdict(KindReport)
134
+ for node in nodes:
135
+ code = getattr(node, "artifact_code", "") or ""
136
+ prefix = code.split("_", 1)[0]
137
+ if not prefix or prefix in schema_closed_prefixes:
138
+ continue # schema-closed kinds are already guaranteed; skip
139
+ # imported artifacts were authored elsewhere — not this build's declaration surface
140
+ if (getattr(node, "metadata", None) or {}).get("imported"):
141
+ continue
142
+ descriptor = REGISTRY.descriptor(prefix)
143
+ is_declarative = descriptor is not None and descriptor.disposition == DECLARATIVE
144
+ fm = getattr(node, "frontmatter", None) or {}
145
+ for leaf in set(_declared_fields(fm)):
146
+ # A DECLARED disposition outranks the heuristic scan: if the key is known to be
147
+ # consumed outside the compiler, an incidental literal match inside it proves nothing.
148
+ if (prefix, leaf) in _DOWNSTREAM_CONSUMERS:
149
+ by_kind[prefix].preserved[leaf] = _DOWNSTREAM_CONSUMERS[(prefix, leaf)]
150
+ elif leaf in literals:
151
+ by_kind[prefix].consumed.add(leaf)
152
+ elif is_declarative:
153
+ # Machine Block §11: declared policy/reference data on a declarative kind, carried
154
+ # into the snapshot but not compiler-consumed — a legitimate *preserved* disposition.
155
+ by_kind[prefix].preserved[leaf] = _DECLARATIVE_POLICY
156
+ else:
157
+ by_kind[prefix].unconsumed.add(leaf)
158
+ return HealthReport(by_kind=dict(by_kind))
159
+
160
+
161
+ def format_report(report: HealthReport, verbose: bool) -> list[str]:
162
+ """Render the diagnostic. Empty list = print nothing (clean, non-verbose)."""
163
+ if not report.by_kind:
164
+ return []
165
+ if not verbose:
166
+ if not report.has_candidates:
167
+ return []
168
+ n = report.candidate_unconsumed
169
+ return [f"⚠ Machine-block health: {n} candidate unconsumed key(s). "
170
+ f"Run with --verbose for details."]
171
+
172
+ lines = ["", "Machine-block health", "─" * 20]
173
+ consumed_total = 0
174
+ for kind in sorted(report.by_kind):
175
+ r = report.by_kind[kind]
176
+ consumed_total += len(r.consumed)
177
+ lines.append(f"Kind: {kind}")
178
+ for k in sorted(r.consumed):
179
+ lines.append(f" ✓ {k}")
180
+ for k in sorted(r.preserved):
181
+ lines.append(f" ○ {k} (preserved — {r.preserved[k]})")
182
+ for k in sorted(r.unconsumed):
183
+ lines.append(f" ⚠ {k} (no detected compiler consumer)")
184
+ lines += [
185
+ "",
186
+ "Summary:",
187
+ f" machine-block keys examined: {report.examined}",
188
+ f" consumed: {consumed_total}",
189
+ f" preserved: {report.preserved_total}",
190
+ f" candidate unconsumed: {report.candidate_unconsumed}",
191
+ "",
192
+ "NOTE: census results are heuristic diagnostics. They do not affect compilation",
193
+ " success, runtime behavior, or conformance. 'Preserved' fields either carry",
194
+ " declared policy on a declarative kind (Machine Block §11) or name a consumer",
195
+ " outside the compiler, which the source scan cannot see. A 'candidate' is a key",
196
+ " on an executable kind with no consumer detected or declared anywhere — the",
197
+ " strongest signal this census can give, though still not proof of disuse.",
198
+ ]
199
+ return lines
@@ -0,0 +1,6 @@
1
+ """
2
+ Governance integration.
3
+
4
+ Graph legality enforcement — structural rules applied to the Graph.
5
+ Used by S4 GOVERN to validate topology against constitutional constraints.
6
+ """
File without changes
@@ -0,0 +1,221 @@
1
+ """ArtifactKindRegistry — the single source of truth for artifact-kind metadata.
2
+
3
+ The compiler owns *how to process* artifact kinds; it must not scatter *what kinds exist* across a
4
+ handful of disconnected dictionaries. This module replaces five maps that had drifted apart
5
+ (`_TYPE_TO_KIND`, `_GOVERNANCE_PREFIXES`, the directory `type_map`, `INDEX_SECTION_BY_KIND`, and the
6
+ per-build `artifact_types` whitelist) with one `ArtifactKindDescriptor` per type-prefix.
7
+
8
+ Every consumer asks the registry a question instead of indexing a raw map:
9
+
10
+ registry.descriptor("ENTITY").materialization_directory # "entities"
11
+ registry.node_kind("ENTITY") # "GOVERNANCE"
12
+ registry.canonical_type("GOVERNANCE", "ENTITY_BLOCK_V0") # "ENTITY"
13
+
14
+ Disposition separates the two axes `NodeKind` was conflating:
15
+ * EXECUTABLE — participates in the execution topology (WF/CC/CT/CS/IN/RB/EV/AC/TI/TE)
16
+ * DECLARATIVE — inert definition the topology references (ENTITY/STRUCTURE/SCHEMA/CONSTITUTION/…)
17
+
18
+ `node_kind` is a *string* (the `NodeKind` value), so this governance-layer module never imports the
19
+ compiler's enum (dependency layering: compiler ← governance). The compiler maps the string to its
20
+ `NodeKind` at its own boundary.
21
+
22
+ This is `BuiltInArtifactRegistry` — the substrate bootstrap, exactly as a C compiler ships built-in
23
+ lexical grammar. Step 4 promotes it to a governed protocol artifact: a `CompiledArtifactRegistry`
24
+ loads the same `ArtifactKindDescriptor`s from the protocol surface, and *nothing downstream changes*
25
+ — consumers still ask the registry the same questions. The descriptor fields below ARE that registry's
26
+ schema, revealed by the first entity integration.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from dataclasses import dataclass
32
+
33
+ EXECUTABLE = "EXECUTABLE"
34
+ DECLARATIVE = "DECLARATIVE"
35
+
36
+ # Governance Ontology (spec fragment 03) — both axes are CLOSED per ontology version.
37
+ # §4 primary Semantic Category: exactly one per kind.
38
+ DEFINITIONAL, NORMATIVE, CONTRACTUAL = "Definitional", "Normative", "Contractual"
39
+ OPERATIONAL, PARTICIPATORY, EVIDENTIAL = "Operational", "Participatory", "Evidential"
40
+ SEMANTIC_CATEGORIES = (DEFINITIONAL, NORMATIVE, CONTRACTUAL, OPERATIONAL, PARTICIPATORY, EVIDENTIAL)
41
+ # §6 provenance: how the element came to exist. Semantic, not physical.
42
+ AUTHORED, DERIVED, PRODUCED = "authored", "derived", "produced"
43
+ PROVENANCES = (AUTHORED, DERIVED, PRODUCED)
44
+ # §7 runtime disposition is a CATEGORY CONTRACT property, never stored per kind — it is derived
45
+ # from the category so the two can never disagree. Distinct from the `disposition` field below,
46
+ # which answers a different question (topology participation).
47
+ _RUNTIME_DISPOSITION = {
48
+ DEFINITIONAL: "not executable",
49
+ NORMATIVE: "not executable",
50
+ CONTRACTUAL: "not independently executable",
51
+ OPERATIONAL: "executable",
52
+ PARTICIPATORY: "participates in execution",
53
+ EVIDENTIAL: "produced by execution",
54
+ }
55
+
56
+
57
+ @dataclass(frozen=True)
58
+ class ArtifactKindDescriptor:
59
+ """Everything the pipeline needs to know about one artifact kind. One object, not five maps."""
60
+ prefix: str # the artifact-code type token, e.g. "ENTITY", "CC", "STRUCTURE"
61
+ node_kind: str # NodeKind value (string): the topology classification
62
+ disposition: str # EXECUTABLE | DECLARATIVE — TOPOLOGY PARTICIPATION only.
63
+ # Not ontology §7 runtime disposition (see runtime_disposition).
64
+ materialization_directory: str | None = None # output dir under artifacts/, or None if not materialized here
65
+ index_section: str | None = None # kind_index section for this kind, or None if not indexed
66
+ canonical_keeps_prefix: bool = True # in canonical projection: keep this prefix vs collapse to node_kind
67
+ artifact_kind: str | None = None # canonical in-block discriminator (Kind Vocabulary); None = not yet authorized
68
+ semantic_category: str | None = None # Governance Ontology §4 primary category; None = kind not authorized
69
+ provenance: str | None = None # Governance Ontology §6 provenance; None = kind not authorized
70
+
71
+ @property
72
+ def inspectable_source(self) -> bool:
73
+ return self.index_section is not None
74
+
75
+ @property
76
+ def runtime_disposition(self) -> str | None:
77
+ """Ontology §7 runtime disposition — derived from the category, never stored.
78
+
79
+ Distinct from `disposition`: a CAPABILITY_CONTRACT participates in the execution
80
+ topology (disposition == EXECUTABLE) yet is "not independently executable" (§7).
81
+ """
82
+ return _RUNTIME_DISPOSITION.get(self.semantic_category)
83
+
84
+
85
+ def _d(prefix, node_kind, disposition, directory=None, index_section=None, keeps_prefix=True, ak=None,
86
+ category=None, provenance=None):
87
+ return ArtifactKindDescriptor(prefix, node_kind, disposition, directory, index_section, keeps_prefix, ak,
88
+ category, provenance)
89
+
90
+
91
+ # The complete built-in table. Values are the EXACT union of the five legacy maps, so the registry is
92
+ # behavior-identical. EXECUTABLE kinds keep their prefix in canonical (node_kind == prefix); DECLARATIVE
93
+ # kinds that ride NodeKind.GOVERNANCE keep their prefix only when listed (the old _GOVERNANCE_PREFIXES),
94
+ # else they collapse to "GOVERNANCE".
95
+ _DESCRIPTORS: tuple[ArtifactKindDescriptor, ...] = (
96
+ # EXECUTABLE — execution topology
97
+ _d("WF", "WF", EXECUTABLE, "workflows", "workflows", ak="WORKFLOW",
98
+ category=OPERATIONAL, provenance=AUTHORED),
99
+ _d("CC", "CC", EXECUTABLE, "capability_contracts", "capability_contracts", ak="CAPABILITY_CONTRACT",
100
+ category=CONTRACTUAL, provenance=AUTHORED),
101
+ _d("CT", "CT", EXECUTABLE, "capability_transforms", "capability_transforms", ak="CAPABILITY_TRANSFORM",
102
+ category=OPERATIONAL, provenance=AUTHORED),
103
+ _d("CS", "CS", EXECUTABLE, "capability_side_effects", "capability_side_effects", ak="CAPABILITY_SIDE_EFFECT",
104
+ category=OPERATIONAL, provenance=AUTHORED),
105
+ _d("IN", "IN", EXECUTABLE, "intents", "intents", ak="INTENT",
106
+ category=OPERATIONAL, provenance=AUTHORED),
107
+ _d("TI", "TI", EXECUTABLE, "ingress_intents", ak="TRANSPORT_INGRESS",
108
+ category=CONTRACTUAL, provenance=AUTHORED),
109
+ _d("TE", "TE", EXECUTABLE, "transport/egress", ak="TRANSPORT_EGRESS",
110
+ category=CONTRACTUAL, provenance=AUTHORED),
111
+ _d("RB", "RB", EXECUTABLE, "runtime_bindings", "runtime_bindings", ak="RUNTIME_BINDING",
112
+ category=OPERATIONAL, provenance=AUTHORED),
113
+ # EVENT: the *kind* is an authored declaration of an event type (schema + trigger). The
114
+ # produced elements — event occurrences and trace records — belong to the runtime evidence
115
+ # model, not the artifact-kind ontology, so no kind carries provenance `produced`.
116
+ _d("EV", "EV", EXECUTABLE, "events", ak="EVENT",
117
+ category=EVIDENTIAL, provenance=AUTHORED),
118
+ _d("AC", "AC", EXECUTABLE, "actors", ak="ACTOR",
119
+ category=PARTICIPATORY, provenance=AUTHORED),
120
+ # DECLARATIVE — own NodeKind
121
+ _d("ASSERT", "ASSERT", DECLARATIVE, "assertions", ak="ASSERT",
122
+ category=NORMATIVE, provenance=DERIVED),
123
+ _d("TEST_DATA", "TEST_DATA", DECLARATIVE),
124
+ # DECLARATIVE — ride NodeKind.GOVERNANCE, keep their prefix (old _GOVERNANCE_PREFIXES)
125
+ _d("INVARIANT", "GOVERNANCE", DECLARATIVE, "invariants", ak="INVARIANT",
126
+ category=NORMATIVE, provenance=AUTHORED),
127
+ _d("VOCAB", "GOVERNANCE", DECLARATIVE, "vocabulary", ak="VOCABULARY",
128
+ category=DEFINITIONAL, provenance=AUTHORED),
129
+ _d("CONSTITUTION", "GOVERNANCE", DECLARATIVE, "concerns", ak="CONSTITUTION",
130
+ category=NORMATIVE, provenance=AUTHORED),
131
+ _d("SCHEMA", "GOVERNANCE", DECLARATIVE, "schemas"),
132
+ _d("STRUCTURE", "GOVERNANCE", DECLARATIVE, "structures", ak="STRUCTURE",
133
+ category=DEFINITIONAL, provenance=AUTHORED),
134
+ _d("SURFACE", "GOVERNANCE", DECLARATIVE, "surface_contracts", ak="SURFACE_CONTRACT",
135
+ category=CONTRACTUAL, provenance=AUTHORED),
136
+ _d("ENTITY", "GOVERNANCE", DECLARATIVE, "entities", "entities", ak="ENTITY"),
137
+ # DECLARATIVE — ride NodeKind.GOVERNANCE, collapse to "GOVERNANCE" in canonical (not in old prefixes)
138
+ _d("LAYER", "GOVERNANCE", DECLARATIVE, keeps_prefix=False),
139
+ _d("GOVERNANCE", "GOVERNANCE", DECLARATIVE, keeps_prefix=False),
140
+ )
141
+
142
+
143
+ class BuiltInArtifactRegistry:
144
+ """The substrate bootstrap registry. Step 4 swaps in a CompiledArtifactRegistry with the same API."""
145
+
146
+ def __init__(self, descriptors: tuple[ArtifactKindDescriptor, ...] = _DESCRIPTORS):
147
+ self._by_prefix = {d.prefix: d for d in descriptors}
148
+ # Authoritative index: canonical artifact_kind -> descriptor (Machine Block §6).
149
+ self._by_kind = {d.artifact_kind: d for d in descriptors if d.artifact_kind}
150
+
151
+ def descriptor(self, prefix: str) -> ArtifactKindDescriptor | None:
152
+ return self._by_prefix.get(prefix)
153
+
154
+ def descriptor_for_kind(self, artifact_kind: str) -> ArtifactKindDescriptor | None:
155
+ """Resolve a descriptor by its canonical `artifact_kind` — the authoritative discriminator."""
156
+ return self._by_kind.get(artifact_kind)
157
+
158
+ def node_kind_for_kind(self, artifact_kind: str) -> str | None:
159
+ """NodeKind (string) for a canonical `artifact_kind`; None if the kind is unknown."""
160
+ d = self._by_kind.get(artifact_kind)
161
+ return d.node_kind if d else None
162
+
163
+ def known(self, prefix: str) -> bool:
164
+ return prefix in self._by_prefix
165
+
166
+ def node_kind(self, prefix: str) -> str | None:
167
+ d = self._by_prefix.get(prefix)
168
+ return d.node_kind if d else None
169
+
170
+ def directory(self, prefix: str) -> str:
171
+ """Output directory for a type prefix. Raises ValueError (legacy contract) if unmapped."""
172
+ d = self._by_prefix.get(prefix)
173
+ if d is None or d.materialization_directory is None:
174
+ valid = sorted(p for p, x in self._by_prefix.items() if x.materialization_directory)
175
+ raise ValueError(f"Unknown artifact type prefix: {prefix}\nValid prefixes: {', '.join(valid)}")
176
+ return d.materialization_directory
177
+
178
+ def semantic_category(self, artifact_kind: str) -> str | None:
179
+ """Governance Ontology §4 primary category for a canonical `artifact_kind`."""
180
+ d = self._by_kind.get(artifact_kind)
181
+ return d.semantic_category if d else None
182
+
183
+ def provenance(self, artifact_kind: str) -> str | None:
184
+ """Governance Ontology §6 provenance for a canonical `artifact_kind`."""
185
+ d = self._by_kind.get(artifact_kind)
186
+ return d.provenance if d else None
187
+
188
+ def runtime_disposition(self, artifact_kind: str) -> str | None:
189
+ """Ontology §7 runtime disposition for a canonical `artifact_kind`."""
190
+ d = self._by_kind.get(artifact_kind)
191
+ return d.runtime_disposition if d else None
192
+
193
+ def ontology_coverage(self) -> dict[str, tuple[str | None, str | None]]:
194
+ """Every authorized kind → (category, provenance). Ontology §11 conformance input."""
195
+ return {k: (d.semantic_category, d.provenance) for k, d in sorted(self._by_kind.items())}
196
+
197
+ def index_section(self, prefix: str) -> str | None:
198
+ d = self._by_prefix.get(prefix)
199
+ return d.index_section if d else None
200
+
201
+ def index_sections(self) -> dict[str, str]:
202
+ """Prefix → kind_index section, for every indexed kind."""
203
+ return {p: d.index_section for p, d in self._by_prefix.items() if d.index_section}
204
+
205
+ def canonical_type(self, node_kind: str, artifact_code: str) -> str:
206
+ """The artifact_type written into the canonical projection (replaces _resolve_artifact_type).
207
+
208
+ Non-GOVERNANCE node kinds: the node_kind IS the prefix. GOVERNANCE node kinds: keep the code's
209
+ prefix when the descriptor says so (old _GOVERNANCE_PREFIXES), else collapse to "GOVERNANCE".
210
+ """
211
+ if node_kind != "GOVERNANCE":
212
+ return node_kind
213
+ for d in self._by_prefix.values():
214
+ if d.node_kind == "GOVERNANCE" and d.canonical_keeps_prefix \
215
+ and artifact_code.startswith(d.prefix + "_"):
216
+ return d.prefix
217
+ return node_kind
218
+
219
+
220
+ # The process-wide built-in registry. Import this; never re-create raw maps.
221
+ REGISTRY = BuiltInArtifactRegistry()
File without changes