pycforge 0.15.2__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 (221) hide show
  1. pycforge/__init__.py +8 -0
  2. pycforge/__main__.py +2 -0
  3. pycforge/_version.py +1 -0
  4. pycforge/converter/__init__.py +0 -0
  5. pycforge/converter/analysis/__init__.py +5 -0
  6. pycforge/converter/analysis/functions.py +723 -0
  7. pycforge/converter/analysis/model.py +158 -0
  8. pycforge/converter/analysis/planning.py +464 -0
  9. pycforge/converter/analysis/stage.py +1976 -0
  10. pycforge/converter/analysis/symbols.py +237 -0
  11. pycforge/converter/analysis/validation.py +2166 -0
  12. pycforge/converter/c_output/__init__.py +2 -0
  13. pycforge/converter/c_output/conformance.py +297 -0
  14. pycforge/converter/c_output/renderer.py +220 -0
  15. pycforge/converter/conditional_regions/__init__.py +71 -0
  16. pycforge/converter/conditional_regions/analysis.py +481 -0
  17. pycforge/converter/conditional_regions/lowering.py +579 -0
  18. pycforge/converter/conditional_regions/model.py +243 -0
  19. pycforge/converter/conditional_regions/validation.py +690 -0
  20. pycforge/converter/containers/__init__.py +19 -0
  21. pycforge/converter/containers/analysis.py +542 -0
  22. pycforge/converter/containers/lowering.py +290 -0
  23. pycforge/converter/containers/model.py +146 -0
  24. pycforge/converter/contracts/__init__.py +0 -0
  25. pycforge/converter/contracts/configuration.py +178 -0
  26. pycforge/converter/contracts/identifiers.py +68 -0
  27. pycforge/converter/contracts/versions.py +71 -0
  28. pycforge/converter/core/__init__.py +0 -0
  29. pycforge/converter/core/artifact_io.py +87 -0
  30. pycforge/converter/core/cancellation.py +7 -0
  31. pycforge/converter/core/canonicalization.py +254 -0
  32. pycforge/converter/core/context.py +13 -0
  33. pycforge/converter/core/diagnostics.py +95 -0
  34. pycforge/converter/core/enums.py +23 -0
  35. pycforge/converter/core/fingerprint.py +22 -0
  36. pycforge/converter/core/pipeline.py +8 -0
  37. pycforge/converter/core/progress.py +25 -0
  38. pycforge/converter/core/request.py +50 -0
  39. pycforge/converter/core/resource_policy.py +31 -0
  40. pycforge/converter/core/result.py +23 -0
  41. pycforge/converter/core/serialization.py +36 -0
  42. pycforge/converter/core/stage.py +10 -0
  43. pycforge/converter/core/stage_artifact.py +80 -0
  44. pycforge/converter/core/stage_outcome.py +15 -0
  45. pycforge/converter/decision_trace/__init__.py +0 -0
  46. pycforge/converter/decision_trace/recorder.py +52 -0
  47. pycforge/converter/facade.py +459 -0
  48. pycforge/converter/frontend/__init__.py +4 -0
  49. pycforge/converter/frontend/normalizer.py +59 -0
  50. pycforge/converter/frontend/parser.py +20 -0
  51. pycforge/converter/frontend/source_document.py +93 -0
  52. pycforge/converter/frontend/stages.py +518 -0
  53. pycforge/converter/frontend/validation.py +76 -0
  54. pycforge/converter/io/__init__.py +0 -0
  55. pycforge/converter/io/atomic_writer.py +33 -0
  56. pycforge/converter/ir/__init__.py +1 -0
  57. pycforge/converter/ir/c_ir/__init__.py +4 -0
  58. pycforge/converter/ir/c_ir/builder.py +26 -0
  59. pycforge/converter/ir/c_ir/model.py +318 -0
  60. pycforge/converter/ir/c_ir/serialization.py +88 -0
  61. pycforge/converter/ir/c_ir/validation.py +834 -0
  62. pycforge/converter/ir/python_ir/__init__.py +17 -0
  63. pycforge/converter/ir/python_ir/nodes.py +86 -0
  64. pycforge/converter/keyword_calls/__init__.py +68 -0
  65. pycforge/converter/keyword_calls/analysis.py +458 -0
  66. pycforge/converter/keyword_calls/lowering.py +271 -0
  67. pycforge/converter/keyword_calls/model.py +304 -0
  68. pycforge/converter/keyword_calls/validation.py +802 -0
  69. pycforge/converter/keyword_only_calls/__init__.py +68 -0
  70. pycforge/converter/keyword_only_calls/analysis.py +640 -0
  71. pycforge/converter/keyword_only_calls/lowering.py +376 -0
  72. pycforge/converter/keyword_only_calls/model.py +328 -0
  73. pycforge/converter/keyword_only_calls/validation.py +1357 -0
  74. pycforge/converter/lowering.py +1000 -0
  75. pycforge/converter/modules/__init__.py +13 -0
  76. pycforge/converter/modules/analysis.py +1250 -0
  77. pycforge/converter/modules/lowering.py +200 -0
  78. pycforge/converter/modules/model.py +73 -0
  79. pycforge/converter/modules/stage.py +192 -0
  80. pycforge/converter/numeric_semantics/__init__.py +23 -0
  81. pycforge/converter/numeric_semantics/analysis.py +420 -0
  82. pycforge/converter/numeric_semantics/lowering.py +153 -0
  83. pycforge/converter/numeric_semantics/model.py +123 -0
  84. pycforge/converter/records/__init__.py +34 -0
  85. pycforge/converter/records/analysis.py +1394 -0
  86. pycforge/converter/records/lowering.py +393 -0
  87. pycforge/converter/records/model.py +319 -0
  88. pycforge/converter/rules/__init__.py +3 -0
  89. pycforge/converter/support_templates/__init__.py +49 -0
  90. pycforge/converter/support_templates/assembly.py +106 -0
  91. pycforge/converter/support_templates/factories.py +348 -0
  92. pycforge/converter/support_templates/model.py +159 -0
  93. pycforge/converter/support_templates/registry.py +388 -0
  94. pycforge/converter/telemetry/__init__.py +0 -0
  95. pycforge/converter/telemetry/sink.py +39 -0
  96. pycforge/ide/__init__.py +19 -0
  97. pycforge/ide/__main__.py +5 -0
  98. pycforge/ide/_worker_event_codec.py +276 -0
  99. pycforge/ide/_worker_protocol_json.py +197 -0
  100. pycforge/ide/_worker_protocol_types.py +90 -0
  101. pycforge/ide/_worker_request_codec.py +200 -0
  102. pycforge/ide/_worker_request_data.py +260 -0
  103. pycforge/ide/_worker_result_data.py +343 -0
  104. pycforge/ide/action_contract.py +469 -0
  105. pycforge/ide/action_validation.py +229 -0
  106. pycforge/ide/command_palette.py +223 -0
  107. pycforge/ide/controller.py +904 -0
  108. pycforge/ide/controller_conversion.py +546 -0
  109. pycforge/ide/controller_io.py +205 -0
  110. pycforge/ide/editor.py +951 -0
  111. pycforge/ide/editor_commands_qt.py +274 -0
  112. pycforge/ide/editor_lexical.py +43 -0
  113. pycforge/ide/editor_sidebars.py +236 -0
  114. pycforge/ide/editor_syntax.py +243 -0
  115. pycforge/ide/find_replace.py +535 -0
  116. pycforge/ide/icons.py +91 -0
  117. pycforge/ide/io_service.py +196 -0
  118. pycforge/ide/model.py +164 -0
  119. pycforge/ide/panels.py +903 -0
  120. pycforge/ide/positions.py +220 -0
  121. pycforge/ide/process_worker.py +130 -0
  122. pycforge/ide/qt.py +230 -0
  123. pycforge/ide/qt_actions.py +418 -0
  124. pycforge/ide/qt_close.py +169 -0
  125. pycforge/ide/qt_command_palette.py +297 -0
  126. pycforge/ide/qt_contract.py +220 -0
  127. pycforge/ide/qt_documents.py +590 -0
  128. pycforge/ide/qt_editor_buffers.py +113 -0
  129. pycforge/ide/qt_editor_surfaces.py +112 -0
  130. pycforge/ide/qt_menus.py +324 -0
  131. pycforge/ide/qt_projection.py +500 -0
  132. pycforge/ide/qt_shell.py +348 -0
  133. pycforge/ide/qt_shell_interactions.py +320 -0
  134. pycforge/ide/qt_state.py +393 -0
  135. pycforge/ide/qt_workspace_features.py +398 -0
  136. pycforge/ide/qt_workspace_navigation.py +208 -0
  137. pycforge/ide/qt_workspace_observers.py +173 -0
  138. pycforge/ide/qt_workspace_panels.py +545 -0
  139. pycforge/ide/qt_workspace_widgets.py +228 -0
  140. pycforge/ide/resources/icons/about.svg +1 -0
  141. pycforge/ide/resources/icons/add-document.svg +1 -0
  142. pycforge/ide/resources/icons/brand-mark.svg +1 -0
  143. pycforge/ide/resources/icons/bundle-search.svg +1 -0
  144. pycforge/ide/resources/icons/cancel.svg +1 -0
  145. pycforge/ide/resources/icons/check.svg +1 -0
  146. pycforge/ide/resources/icons/chevron-right.svg +1 -0
  147. pycforge/ide/resources/icons/close.svg +1 -0
  148. pycforge/ide/resources/icons/collapse-all.svg +1 -0
  149. pycforge/ide/resources/icons/command-palette.svg +1 -0
  150. pycforge/ide/resources/icons/convert.svg +1 -0
  151. pycforge/ide/resources/icons/copy.svg +1 -0
  152. pycforge/ide/resources/icons/cut.svg +1 -0
  153. pycforge/ide/resources/icons/decision-trace.svg +1 -0
  154. pycforge/ide/resources/icons/details.svg +1 -0
  155. pycforge/ide/resources/icons/diagnostics.svg +1 -0
  156. pycforge/ide/resources/icons/duplicate-line.svg +1 -0
  157. pycforge/ide/resources/icons/exit.svg +1 -0
  158. pycforge/ide/resources/icons/expand-all.svg +1 -0
  159. pycforge/ide/resources/icons/export.svg +1 -0
  160. pycforge/ide/resources/icons/find.svg +1 -0
  161. pycforge/ide/resources/icons/go-to-line.svg +1 -0
  162. pycforge/ide/resources/icons/go-to-output.svg +1 -0
  163. pycforge/ide/resources/icons/go-to-source.svg +1 -0
  164. pycforge/ide/resources/icons/history.svg +1 -0
  165. pycforge/ide/resources/icons/indent.svg +1 -0
  166. pycforge/ide/resources/icons/link-c.svg +1 -0
  167. pycforge/ide/resources/icons/mappings.svg +1 -0
  168. pycforge/ide/resources/icons/module.svg +1 -0
  169. pycforge/ide/resources/icons/move-down.svg +1 -0
  170. pycforge/ide/resources/icons/move-line-down.svg +1 -0
  171. pycforge/ide/resources/icons/move-line-up.svg +1 -0
  172. pycforge/ide/resources/icons/move-up.svg +1 -0
  173. pycforge/ide/resources/icons/next.svg +1 -0
  174. pycforge/ide/resources/icons/open.svg +1 -0
  175. pycforge/ide/resources/icons/outdent.svg +1 -0
  176. pycforge/ide/resources/icons/outline.svg +1 -0
  177. pycforge/ide/resources/icons/paste.svg +1 -0
  178. pycforge/ide/resources/icons/previous.svg +1 -0
  179. pycforge/ide/resources/icons/primary-module.svg +1 -0
  180. pycforge/ide/resources/icons/redo.svg +1 -0
  181. pycforge/ide/resources/icons/remove-document.svg +1 -0
  182. pycforge/ide/resources/icons/replace.svg +1 -0
  183. pycforge/ide/resources/icons/save-as.svg +1 -0
  184. pycforge/ide/resources/icons/save.svg +1 -0
  185. pycforge/ide/resources/icons/select-all.svg +1 -0
  186. pycforge/ide/resources/icons/settings.svg +1 -0
  187. pycforge/ide/resources/icons/show-c.svg +1 -0
  188. pycforge/ide/resources/icons/split-view.svg +1 -0
  189. pycforge/ide/resources/icons/summary.svg +1 -0
  190. pycforge/ide/resources/icons/telemetry.svg +1 -0
  191. pycforge/ide/resources/icons/toggle-comment.svg +1 -0
  192. pycforge/ide/resources/icons/toggle-fold.svg +1 -0
  193. pycforge/ide/resources/icons/undo.svg +1 -0
  194. pycforge/ide/resources/icons/whitespace.svg +1 -0
  195. pycforge/ide/revisions.py +400 -0
  196. pycforge/ide/search_service.py +280 -0
  197. pycforge/ide/session_history.py +150 -0
  198. pycforge/ide/source_editing.py +417 -0
  199. pycforge/ide/source_structure.py +558 -0
  200. pycforge/ide/source_structure_async.py +191 -0
  201. pycforge/ide/supervisor.py +503 -0
  202. pycforge/ide/theme.py +119 -0
  203. pycforge/ide/theme_stylesheet.py +598 -0
  204. pycforge/ide/theme_workspace_stylesheet.py +155 -0
  205. pycforge/ide/visual_tokens.py +142 -0
  206. pycforge/ide/worker_protocol.py +80 -0
  207. pycforge/ide/workspace_search.py +462 -0
  208. pycforge/ide/workspace_session.py +256 -0
  209. pycforge/laboratory/__init__.py +6 -0
  210. pycforge/laboratory/audits.py +2244 -0
  211. pycforge/laboratory/checkpoint_e.py +2685 -0
  212. pycforge/laboratory/cli.py +119 -0
  213. pycforge/laboratory/exit_codes.py +11 -0
  214. pycforge/laboratory/keyword_audit.py +661 -0
  215. pycforge/laboratory/keyword_only_audit.py +481 -0
  216. pycforge-0.15.2.dist-info/METADATA +209 -0
  217. pycforge-0.15.2.dist-info/RECORD +221 -0
  218. pycforge-0.15.2.dist-info/WHEEL +5 -0
  219. pycforge-0.15.2.dist-info/entry_points.txt +5 -0
  220. pycforge-0.15.2.dist-info/licenses/LICENSE +674 -0
  221. pycforge-0.15.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2244 @@
1
+ from __future__ import annotations
2
+ import ast, hashlib, json, re, subprocess, sys
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from .keyword_audit import audit_keyword
7
+ from .keyword_only_audit import audit_keyword_only
8
+
9
+ FORBIDDEN_IMPORTS = {"PyQt5", "PySide6", "subprocess"}
10
+ FORBIDDEN_TOOLCHAIN_TOKENS = {"gcc", "clang", "cl.exe", "msvc", "cc "}
11
+
12
+ def audit_architecture(root: Path) -> dict[str, Any]:
13
+ from pycforge.converter.contracts.configuration import DEFAULT_RENDERER, DEFAULT_RULE_SET
14
+ from pycforge.converter.contracts.versions import (
15
+ C_IR_SCHEMA,
16
+ CONVERSION_PLAN_SCHEMA,
17
+ GENERATED_C_SCHEMA,
18
+ KEYWORD_CALL_FACT_SCHEMA,
19
+ KEYWORD_ONLY_CALL_FACT_SCHEMA,
20
+ )
21
+ from pycforge.converter.keyword_calls import KEYWORD_CALL_RULE_ID
22
+ from pycforge.converter.keyword_only_calls import KEYWORD_ONLY_CALL_RULE_ID
23
+
24
+ violations=[]
25
+ converter_root=root/"pycforge"/"converter"
26
+ for path in sorted(converter_root.rglob("*.py")):
27
+ tree=ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
28
+ for node in ast.walk(tree):
29
+ if isinstance(node, ast.Import):
30
+ for alias in node.names:
31
+ if alias.name.split(".")[0] in FORBIDDEN_IMPORTS: violations.append(f"{path.relative_to(root)} imports {alias.name}")
32
+ elif isinstance(node, ast.ImportFrom) and node.module and node.module.split(".")[0] in FORBIDDEN_IMPORTS:
33
+ violations.append(f"{path.relative_to(root)} imports {node.module}")
34
+ # Future rules and helpers must not bypass structured C IR with final-text ownership.
35
+ for relative in (Path("pycforge/converter/rules"),Path("pycforge/converter/support_templates")):
36
+ base=root/relative
37
+ if base.exists():
38
+ for path in sorted(base.rglob("*.py")):
39
+ source=path.read_text(encoding="utf-8")
40
+ for token in ("generated_c =", "return_c_text", "append_c_text"):
41
+ if token in source: violations.append(f"{path.relative_to(root)} bypasses C IR via {token}")
42
+ renderer=root/"pycforge/converter/c_output/renderer.py"
43
+ cir=root/"pycforge/converter/ir/c_ir/model.py"
44
+ if not renderer.exists(): violations.append("missing sole deterministic C renderer")
45
+ if not cir.exists(): violations.append("missing versioned C IR model")
46
+ for relative in (Path("pycforge/converter/analysis"), Path("pycforge/converter/rules")):
47
+ base=root/relative
48
+ if base.exists():
49
+ for path in sorted(base.rglob("*.py")):
50
+ source=path.read_text(encoding="utf-8")
51
+ if "CRenderer" in source or "CTranslationUnitBuilder" in source:
52
+ violations.append(f"{path.relative_to(root)} crosses Phase 5 lowering boundary")
53
+ container_analysis=root/"pycforge"/"converter"/"containers"/"analysis.py"
54
+ container_lowering=root/"pycforge"/"converter"/"containers"/"lowering.py"
55
+ if not container_analysis.exists() or not container_lowering.exists():
56
+ violations.append("missing separated Phase 11 container analysis/lowering boundary")
57
+ elif any(token in container_analysis.read_text(encoding="utf-8") for token in ("ir.c_ir", "CRenderer", "CTranslationUnitBuilder")):
58
+ violations.append("container analysis depends on C IR or rendering")
59
+ cumulative_lowerer=root/"pycforge"/"converter"/"lowering.py"
60
+ if cumulative_lowerer.exists() and len(cumulative_lowerer.read_text(encoding="utf-8").splitlines()) > 1000:
61
+ violations.append("cumulative lowering hotspot exceeds the Phase 11 structural ceiling")
62
+ module_analysis=root/"pycforge"/"converter"/"modules"/"analysis.py"
63
+ module_lowering=root/"pycforge"/"converter"/"modules"/"lowering.py"
64
+ if not module_analysis.exists() or not module_lowering.exists():
65
+ violations.append("missing separated Phase 12 module analysis/lowering boundary")
66
+ elif any(token in module_analysis.read_text(encoding="utf-8") for token in ("ir.c_ir", "CRenderer", "CTranslationUnitBuilder")):
67
+ violations.append("module analysis depends on C IR or rendering")
68
+ if module_analysis.exists():
69
+ tree=ast.parse(module_analysis.read_text(encoding="utf-8"),filename=str(module_analysis))
70
+ forbidden_discovery={"importlib","pkgutil","site","sysconfig","os","pathlib","socket","urllib","requests","http","subprocess"}
71
+ for node in ast.walk(tree):
72
+ if isinstance(node,ast.Import):
73
+ for alias in node.names:
74
+ if alias.name.split(".")[0] in forbidden_discovery:
75
+ violations.append(f"module analysis imports discovery-capable {alias.name}")
76
+ elif isinstance(node,ast.ImportFrom) and node.module and node.module.split(".")[0] in forbidden_discovery:
77
+ violations.append(f"module analysis imports discovery-capable {node.module}")
78
+ record_analysis=root/"pycforge"/"converter"/"records"/"analysis.py"
79
+ record_lowering=root/"pycforge"/"converter"/"records"/"lowering.py"
80
+ if not record_analysis.exists() or not record_lowering.exists():
81
+ violations.append("missing separated Phase 13 record analysis/lowering boundary")
82
+ elif any(
83
+ token in record_analysis.read_text(encoding="utf-8")
84
+ for token in ("ir.c_ir", "CRenderer", "CTranslationUnitBuilder", "records.lowering")
85
+ ):
86
+ violations.append("record analysis depends on C IR, rendering, or record lowering")
87
+ numeric_analysis=root/"pycforge"/"converter"/"numeric_semantics"/"analysis.py"
88
+ numeric_lowering=root/"pycforge"/"converter"/"numeric_semantics"/"lowering.py"
89
+ if not numeric_analysis.exists() or not numeric_lowering.exists():
90
+ violations.append("missing separated Phase 14A numeric analysis/lowering boundary")
91
+ elif any(
92
+ token in numeric_analysis.read_text(encoding="utf-8")
93
+ for token in ("ir.c_ir", "CRenderer", "CTranslationUnitBuilder", "numeric_semantics.lowering")
94
+ ):
95
+ violations.append("numeric analysis depends on C IR, rendering, or numeric lowering")
96
+ conditional_analysis=root/"pycforge"/"converter"/"conditional_regions"/"analysis.py"
97
+ conditional_validation=root/"pycforge"/"converter"/"conditional_regions"/"validation.py"
98
+ conditional_lowering=root/"pycforge"/"converter"/"conditional_regions"/"lowering.py"
99
+ conditional_analysis_forbidden=(
100
+ "ir.c_ir",
101
+ "CRenderer",
102
+ "CTranslationUnitBuilder",
103
+ "conditional_regions.lowering",
104
+ "from .lowering",
105
+ )
106
+ conditional_validation_forbidden=(
107
+ "ir.c_ir",
108
+ "CRenderer",
109
+ "CTranslationUnitBuilder",
110
+ "ConditionalRegionAnalyzer",
111
+ "ConditionalRegionCIRLowerer",
112
+ "from .analysis",
113
+ "from .lowering",
114
+ )
115
+ conditional_analysis_depends_on_c_ir=bool(
116
+ conditional_analysis.exists()
117
+ and any(
118
+ token in conditional_analysis.read_text(encoding="utf-8")
119
+ for token in conditional_analysis_forbidden
120
+ )
121
+ )
122
+ conditional_validation_depends_on_producer_or_lowerer=bool(
123
+ conditional_validation.exists()
124
+ and any(
125
+ token in conditional_validation.read_text(encoding="utf-8")
126
+ for token in conditional_validation_forbidden
127
+ )
128
+ )
129
+ if not all(
130
+ path.exists()
131
+ for path in (conditional_analysis,conditional_validation,conditional_lowering)
132
+ ):
133
+ violations.append("missing separated Phase 14B conditional analysis/validation/lowering boundary")
134
+ if conditional_analysis_depends_on_c_ir:
135
+ violations.append("conditional analysis depends on C IR, rendering, or conditional lowering")
136
+ if conditional_validation_depends_on_producer_or_lowerer:
137
+ violations.append("conditional validation depends on C IR, producer analysis, or lowering")
138
+ keyword_analysis=root/"pycforge"/"converter"/"keyword_calls"/"analysis.py"
139
+ keyword_validation=root/"pycforge"/"converter"/"keyword_calls"/"validation.py"
140
+ keyword_lowering=root/"pycforge"/"converter"/"keyword_calls"/"lowering.py"
141
+ keyword_analysis_forbidden=(
142
+ "ir.c_ir",
143
+ "CRenderer",
144
+ "CTranslationUnitBuilder",
145
+ "keyword_calls.lowering",
146
+ "from .lowering",
147
+ )
148
+ keyword_validation_forbidden=(
149
+ "ir.c_ir",
150
+ "CRenderer",
151
+ "CTranslationUnitBuilder",
152
+ "KeywordCallAnalyzer",
153
+ "KeywordCallCIRLowerer",
154
+ "from .analysis",
155
+ "from .lowering",
156
+ )
157
+ keyword_analysis_depends_on_c_ir=bool(
158
+ keyword_analysis.exists()
159
+ and any(
160
+ token in keyword_analysis.read_text(encoding="utf-8")
161
+ for token in keyword_analysis_forbidden
162
+ )
163
+ )
164
+ keyword_validation_depends_on_producer_or_lowerer=bool(
165
+ keyword_validation.exists()
166
+ and any(
167
+ token in keyword_validation.read_text(encoding="utf-8")
168
+ for token in keyword_validation_forbidden
169
+ )
170
+ )
171
+ if not all(
172
+ path.exists() for path in (keyword_analysis,keyword_validation,keyword_lowering)
173
+ ):
174
+ violations.append("missing separated Phase 14C keyword analysis/validation/lowering boundary")
175
+ if keyword_analysis_depends_on_c_ir:
176
+ violations.append("keyword-call analysis depends on C IR, rendering, or keyword lowering")
177
+ if keyword_validation_depends_on_producer_or_lowerer:
178
+ violations.append("keyword-call validation depends on C IR, producer analysis, or lowering")
179
+ keyword_only_analysis=root/"pycforge"/"converter"/"keyword_only_calls"/"analysis.py"
180
+ keyword_only_validation=root/"pycforge"/"converter"/"keyword_only_calls"/"validation.py"
181
+ keyword_only_lowering=root/"pycforge"/"converter"/"keyword_only_calls"/"lowering.py"
182
+ keyword_only_analysis_depends_on_c_ir=bool(
183
+ keyword_only_analysis.exists()
184
+ and any(
185
+ token in keyword_only_analysis.read_text(encoding="utf-8")
186
+ for token in ("ir.c_ir","CRenderer","CTranslationUnitBuilder","keyword_only_calls.lowering","from .lowering")
187
+ )
188
+ )
189
+ keyword_only_validation_depends_on_producer_or_lowerer=bool(
190
+ keyword_only_validation.exists()
191
+ and any(
192
+ token in keyword_only_validation.read_text(encoding="utf-8")
193
+ for token in ("ir.c_ir","CRenderer","CTranslationUnitBuilder","KeywordOnlyCallAnalyzer","KeywordOnlyCallCIRLowerer","from .analysis","from .lowering")
194
+ )
195
+ )
196
+ if not all(path.exists() for path in (keyword_only_analysis,keyword_only_validation,keyword_only_lowering)):
197
+ violations.append("missing separated Phase 14D keyword-only analysis/validation/lowering boundary")
198
+ if keyword_only_analysis_depends_on_c_ir:
199
+ violations.append("keyword-only analysis depends on C IR, rendering, or lowering")
200
+ if keyword_only_validation_depends_on_producer_or_lowerer:
201
+ violations.append("keyword-only validation depends on C IR, producer analysis, or lowering")
202
+ active_contracts={
203
+ "rule_set":DEFAULT_RULE_SET,
204
+ "renderer":DEFAULT_RENDERER,
205
+ "keyword_call_facts":KEYWORD_CALL_FACT_SCHEMA,
206
+ "keyword_only_call_facts":KEYWORD_ONLY_CALL_FACT_SCHEMA,
207
+ "conversion_plan":CONVERSION_PLAN_SCHEMA,
208
+ "c_ir":C_IR_SCHEMA,
209
+ "generated_c":GENERATED_C_SCHEMA,
210
+ }
211
+ active_contracts_valid=active_contracts=={
212
+ "rule_set":"phase14-required-keyword-only-calls-v0.14.3",
213
+ "renderer":"c-renderer-v0.14.3",
214
+ "keyword_call_facts":"fact-table/0.14.2",
215
+ "keyword_only_call_facts":"fact-table/0.14.3",
216
+ "conversion_plan":"conversion-plan/0.14.3",
217
+ "c_ir":"c-ir/0.14.3",
218
+ "generated_c":"generated-c/0.14.3",
219
+ }
220
+ keyword_contract_valid=(
221
+ KEYWORD_CALL_RULE_ID=="phase14.keyword_call.exact_binding"
222
+ and KEYWORD_CALL_FACT_SCHEMA=="fact-table/0.14.2"
223
+ and KEYWORD_ONLY_CALL_RULE_ID=="phase14.keyword_only_call.exact_binding"
224
+ and KEYWORD_ONLY_CALL_FACT_SCHEMA=="fact-table/0.14.3"
225
+ )
226
+ if not active_contracts_valid:
227
+ violations.append("active Phase 14D architecture identities are inconsistent")
228
+ if not keyword_contract_valid:
229
+ violations.append("Phase 14C keyword rule or fact identity is inconsistent")
230
+ return {
231
+ "audit":"architecture",
232
+ "passed":not violations,
233
+ "violations":violations,
234
+ "record_analysis_present":record_analysis.is_file(),
235
+ "record_lowering_present":record_lowering.is_file(),
236
+ "record_analysis_depends_on_c_ir":bool(
237
+ record_analysis.exists()
238
+ and any(
239
+ token in record_analysis.read_text(encoding="utf-8")
240
+ for token in ("ir.c_ir", "CRenderer", "CTranslationUnitBuilder")
241
+ )
242
+ ),
243
+ "numeric_analysis_present":numeric_analysis.is_file(),
244
+ "numeric_lowering_present":numeric_lowering.is_file(),
245
+ "numeric_analysis_depends_on_c_ir":bool(
246
+ numeric_analysis.exists()
247
+ and any(
248
+ token in numeric_analysis.read_text(encoding="utf-8")
249
+ for token in ("ir.c_ir", "CRenderer", "CTranslationUnitBuilder")
250
+ )
251
+ ),
252
+ "conditional_analysis_present":conditional_analysis.is_file(),
253
+ "conditional_validation_present":conditional_validation.is_file(),
254
+ "conditional_lowering_present":conditional_lowering.is_file(),
255
+ "conditional_analysis_depends_on_c_ir":conditional_analysis_depends_on_c_ir,
256
+ "conditional_validation_depends_on_producer_or_lowerer":conditional_validation_depends_on_producer_or_lowerer,
257
+ "keyword_analysis_present":keyword_analysis.is_file(),
258
+ "keyword_validation_present":keyword_validation.is_file(),
259
+ "keyword_lowering_present":keyword_lowering.is_file(),
260
+ "keyword_analysis_depends_on_c_ir":keyword_analysis_depends_on_c_ir,
261
+ "keyword_validation_depends_on_producer_or_lowerer":keyword_validation_depends_on_producer_or_lowerer,
262
+ "keyword_only_analysis_present":keyword_only_analysis.is_file(),
263
+ "keyword_only_validation_present":keyword_only_validation.is_file(),
264
+ "keyword_only_lowering_present":keyword_only_lowering.is_file(),
265
+ "keyword_only_analysis_depends_on_c_ir":keyword_only_analysis_depends_on_c_ir,
266
+ "keyword_only_validation_depends_on_producer_or_lowerer":keyword_only_validation_depends_on_producer_or_lowerer,
267
+ "keyword_rule_id":KEYWORD_CALL_RULE_ID,
268
+ "keyword_fact_schema":KEYWORD_CALL_FACT_SCHEMA,
269
+ "keyword_only_rule_id":KEYWORD_ONLY_CALL_RULE_ID,
270
+ "keyword_only_fact_schema":KEYWORD_ONLY_CALL_FACT_SCHEMA,
271
+ "keyword_contract_valid":keyword_contract_valid,
272
+ "active_contracts":active_contracts,
273
+ "active_contract_identities_valid":active_contracts_valid,
274
+ "cumulative_lowerer_lines":len(cumulative_lowerer.read_text(encoding="utf-8").splitlines()) if cumulative_lowerer.exists() else None,
275
+ }
276
+
277
+ def audit_rules(root: Path) -> dict[str, Any]:
278
+ from pycforge.converter.contracts.configuration import (
279
+ DEFAULT_RENDERER,
280
+ DEFAULT_RULE_SET,
281
+ supports_conditional_regions,
282
+ supports_keyword_calls,
283
+ supports_keyword_only_calls,
284
+ supports_numeric,
285
+ supports_records,
286
+ )
287
+ from pycforge.converter.contracts.versions import (
288
+ C_IR_SCHEMA,
289
+ CONDITIONAL_FACT_SCHEMA,
290
+ CONVERSION_PLAN_SCHEMA,
291
+ CONVERSION_SUMMARY_SCHEMA,
292
+ DECISION_TRACE_SCHEMA,
293
+ GENERATED_C_SCHEMA,
294
+ KEYWORD_CALL_FACT_SCHEMA,
295
+ KEYWORD_ONLY_CALL_FACT_SCHEMA,
296
+ )
297
+ from pycforge.converter.analysis.planning import default_registry
298
+ try:
299
+ active_records=supports_records(DEFAULT_RULE_SET)
300
+ active_numeric=supports_numeric(DEFAULT_RULE_SET)
301
+ active_conditional=supports_conditional_regions(DEFAULT_RULE_SET)
302
+ active_keyword_calls=supports_keyword_calls(DEFAULT_RULE_SET)
303
+ active_keyword_only_calls=supports_keyword_only_calls(DEFAULT_RULE_SET)
304
+ manifest=default_registry(
305
+ include_records=active_records,
306
+ include_numeric=active_numeric,
307
+ include_conditional_regions=active_conditional,
308
+ include_keyword_calls=active_keyword_calls,
309
+ include_keyword_only_calls=active_keyword_only_calls,
310
+ ).manifest
311
+ identities=[(item["rule_id"],item["rule_version"]) for item in manifest]
312
+ phase9={"phase9.call.understood_target"}
313
+ phase11={
314
+ "phase11.container.list_literal",
315
+ "phase11.container.tuple_literal",
316
+ "phase11.container.dict_literal",
317
+ "phase11.container.index.proved",
318
+ "phase11.container.for.bounded",
319
+ "phase11.container.assignment",
320
+ "phase11.container.name",
321
+ }
322
+ phase12={
323
+ "phase12.module.document",
324
+ "phase12.module.import_from",
325
+ "phase12.module.imported_binding",
326
+ "phase12.module.function_namespace",
327
+ "phase12.module.cross_call",
328
+ "phase12.module.initialization",
329
+ "phase12.module.bundle_assembly",
330
+ }
331
+ phase13={
332
+ "phase13.record.class",
333
+ "phase13.record.field",
334
+ "phase13.record.initializer",
335
+ "phase13.record.construction",
336
+ "phase13.record.binding",
337
+ "phase13.record.name",
338
+ "phase13.record.attribute_read",
339
+ }
340
+ phase14={"phase14.numeric.floor_arithmetic"}
341
+ phase14b={
342
+ "phase14.conditional.boolean_region",
343
+ "phase14.conditional.comparison_region",
344
+ }
345
+ phase14c={"phase14.keyword_call.exact_binding"}
346
+ phase14d={"phase14.keyword_only_call.exact_binding"}
347
+ declared={item["rule_id"] for item in manifest}
348
+ active_identities={
349
+ "rule_set":DEFAULT_RULE_SET,
350
+ "renderer":DEFAULT_RENDERER,
351
+ "conditional_facts":CONDITIONAL_FACT_SCHEMA,
352
+ "keyword_call_facts":KEYWORD_CALL_FACT_SCHEMA,
353
+ "keyword_only_call_facts":KEYWORD_ONLY_CALL_FACT_SCHEMA,
354
+ "conversion_plan":CONVERSION_PLAN_SCHEMA,
355
+ "c_ir":C_IR_SCHEMA,
356
+ "generated_c":GENERATED_C_SCHEMA,
357
+ "conversion_summary":CONVERSION_SUMMARY_SCHEMA,
358
+ "decision_trace":DECISION_TRACE_SCHEMA,
359
+ }
360
+ identities_valid=active_identities=={
361
+ "rule_set":"phase14-required-keyword-only-calls-v0.14.3",
362
+ "renderer":"c-renderer-v0.14.3",
363
+ "conditional_facts":"fact-table/0.14.1",
364
+ "keyword_call_facts":"fact-table/0.14.2",
365
+ "keyword_only_call_facts":"fact-table/0.14.3",
366
+ "conversion_plan":"conversion-plan/0.14.3",
367
+ "c_ir":"c-ir/0.14.3",
368
+ "generated_c":"generated-c/0.14.3",
369
+ "conversion_summary":"pycforge.conversion-summary/0.14.3",
370
+ "decision_trace":"pycforge.decision-trace/0.14.3",
371
+ }
372
+ passed=(
373
+ len(identities)==len(set(identities))
374
+ and len(manifest)>0
375
+ and active_records
376
+ and phase9.issubset(declared)
377
+ and phase11.issubset(declared)
378
+ and phase12.issubset(declared)
379
+ and phase13.issubset(declared)
380
+ and active_numeric
381
+ and phase14.issubset(declared)
382
+ and active_conditional
383
+ and phase14b.issubset(declared)
384
+ and active_keyword_calls
385
+ and phase14c.issubset(declared)
386
+ and active_keyword_only_calls
387
+ and phase14d.issubset(declared)
388
+ and identities_valid
389
+ )
390
+ return {
391
+ "audit":"rules",
392
+ "passed":passed,
393
+ "active_rule_set":DEFAULT_RULE_SET,
394
+ "active_registry_includes_records":active_records,
395
+ "active_registry_includes_numeric":active_numeric,
396
+ "active_registry_includes_conditional_regions":active_conditional,
397
+ "active_registry_includes_keyword_calls":active_keyword_calls,
398
+ "active_registry_includes_keyword_only_calls":active_keyword_only_calls,
399
+ "active_contracts":active_identities,
400
+ "active_contract_identities_valid":identities_valid,
401
+ "rule_count":len(manifest),
402
+ "manifest":list(manifest),
403
+ "phase9_required_rules":sorted(phase9),
404
+ "missing_phase9_rules":sorted(phase9-declared),
405
+ "phase11_required_rules":sorted(phase11),
406
+ "missing_phase11_rules":sorted(phase11-declared),
407
+ "phase12_required_rules":sorted(phase12),
408
+ "missing_phase12_rules":sorted(phase12-declared),
409
+ "phase13_required_rules":sorted(phase13),
410
+ "missing_phase13_rules":sorted(phase13-declared),
411
+ "phase14_required_rules":sorted(phase14),
412
+ "missing_phase14_rules":sorted(phase14-declared),
413
+ "phase14b_required_rules":sorted(phase14b),
414
+ "missing_phase14b_rules":sorted(phase14b-declared),
415
+ "phase14c_required_rules":sorted(phase14c),
416
+ "missing_phase14c_rules":sorted(phase14c-declared),
417
+ "phase14d_required_rules":sorted(phase14d),
418
+ "missing_phase14d_rules":sorted(phase14d-declared),
419
+ "notes":[
420
+ "Registry is frozen and sorted.",
421
+ "Duplicate identities and equal-specificity overlaps reject at registry construction.",
422
+ "The active cumulative registry includes the sealed Phase 14C keyword rule and one bounded Phase 14D required-keyword-only rule.",
423
+ ],
424
+ }
425
+ except Exception as exc:
426
+ return {"audit":"rules","passed":False,"rule_count":0,"error":str(exc)}
427
+
428
+ def audit_helpers(root: Path) -> dict[str, Any]:
429
+ from pycforge.converter.support_templates import (
430
+ FLOOR_DIV_REFERENCE,
431
+ FLOOR_MOD_REFERENCE,
432
+ FrozenHelperRegistry,
433
+ builtin_definitions,
434
+ default_helper_registry,
435
+ )
436
+ try:
437
+ registry=default_helper_registry()
438
+ reversed_registry=FrozenHelperRegistry(reversed(builtin_definitions()))
439
+ references=[item["reference"] for item in registry.manifest]
440
+ required=[FLOOR_DIV_REFERENCE.canonical,FLOOR_MOD_REFERENCE.canonical]
441
+ plan=registry.resolve(required,target_contract="c11-portable-fixed-v1")
442
+ fixture_names={
443
+ FLOOR_DIV_REFERENCE.canonical:"pycf_i64_floor_div_v1.c",
444
+ FLOOR_MOD_REFERENCE.canonical:"pycf_i64_floor_mod_v1.c",
445
+ }
446
+ golden_sha256={
447
+ FLOOR_DIV_REFERENCE.canonical:"7d65353f363a713545180525d42d81f12beba69bf2a39654c6d1645089f62803",
448
+ FLOOR_MOD_REFERENCE.canonical:"f8ee68c91b353542dd856ea2af72b0523a103a717eb35de808ec811ef4663b21",
449
+ }
450
+ golden_mismatches=[]
451
+ golden_fixture_files_present=True
452
+ for reference,name in fixture_names.items():
453
+ path=root/"fixtures"/"support_templates"/name
454
+ rendered=registry.rendered_asset(reference)
455
+ if hashlib.sha256(rendered.encode("utf-8")).hexdigest() != golden_sha256[reference]:
456
+ golden_mismatches.append(name)
457
+ elif path.is_file() and path.read_text(encoding="utf-8") != rendered:
458
+ golden_mismatches.append(name)
459
+ elif not path.is_file():
460
+ golden_fixture_files_present=False
461
+ passed=(
462
+ references==required
463
+ and registry.manifest==reversed_registry.manifest
464
+ and registry.fingerprint==reversed_registry.fingerprint
465
+ and [item.reference.canonical for item in plan.manifest]==required
466
+ and not golden_mismatches
467
+ )
468
+ return {
469
+ "audit":"helpers",
470
+ "passed":passed,
471
+ "registry_version":registry.registry_version,
472
+ "registry_fingerprint":registry.fingerprint,
473
+ "helper_count":len(references),
474
+ "helpers":references,
475
+ "resolved_manifest_fingerprint":plan.manifest_fingerprint,
476
+ "golden_mismatches":golden_mismatches,
477
+ "golden_fixture_files_present":golden_fixture_files_present,
478
+ "raw_text_ingestion_supported":False,
479
+ "generated_c_compiled_or_executed":False,
480
+ }
481
+ except Exception as exc:
482
+ return {"audit":"helpers","passed":False,"helper_count":0,"error":f"{type(exc).__name__}: {exc}"}
483
+
484
+ def audit_transition(root: Path, phase: str) -> dict[str, Any]:
485
+ base=root/"transition"/phase
486
+ promoted={"phase_9","phase_10","phase_11","phase_12","phase_13","phase_14","phase_14d"}
487
+ if phase == "phase_14b":
488
+ required={
489
+ "baseline_fingerprint.json",
490
+ "breadth_and_change_budgets.md",
491
+ "conditional_temporary_regions_decision.md",
492
+ "entry_criteria.md",
493
+ "opening_evidence.md",
494
+ "rollback_conditions.md",
495
+ }
496
+ else:
497
+ required={"manifest.json","baseline_fingerprint.json","entry_criteria.md","gate_evidence.md","rollback_conditions.md"} if phase in promoted else {"manifest.json","baseline_fingerprint.json"}
498
+ if phase == "phase_13":
499
+ required.update(
500
+ {
501
+ "candidate_reseed.md",
502
+ "record_representation_decisions.md",
503
+ "release_fingerprint.json",
504
+ }
505
+ )
506
+ if phase == "phase_14":
507
+ required.update(
508
+ {
509
+ "breadth_and_change_budgets.md",
510
+ "integer_divmod_decision.md",
511
+ "opening_evidence.md",
512
+ "release_fingerprint.json",
513
+ }
514
+ )
515
+ if phase == "phase_14d":
516
+ required.update(
517
+ {
518
+ "breadth_and_change_budgets.md",
519
+ "opening_evidence.md",
520
+ "release_fingerprint.json",
521
+ "required_keyword_only_calls_decision.md",
522
+ }
523
+ )
524
+ missing=sorted(name for name in required if not (base/name).exists())
525
+ manifest_error=None
526
+ opening_error=None
527
+ authenticated_predecessor_tests=None
528
+ required_tests=None
529
+ if not missing and phase == "phase_14b":
530
+ try:
531
+ baseline=json.loads((base/"baseline_fingerprint.json").read_text(encoding="utf-8"))
532
+ predecessor=baseline.get("predecessor")
533
+ sealed=baseline.get("sealed_release_evidence")
534
+ authentication=baseline.get("authentication")
535
+ authenticated_predecessor_tests=sealed.get("tests_discovered") if isinstance(sealed,dict) else None
536
+ if (
537
+ baseline.get("schema_version")!="pycforge.phase14b-opening-baseline/0.14.1"
538
+ or baseline.get("phase")!=14
539
+ or baseline.get("mini_phase")!="14B"
540
+ or not isinstance(predecessor,dict)
541
+ or predecessor.get("version")!="0.14.0"
542
+ or predecessor.get("source_archive_size")!=1016512
543
+ or predecessor.get("source_archive_sha256")!="d4fe065d168241b4371901e19eda346c38835c1d2ac07e3870f27abb5a7b3917"
544
+ or predecessor.get("canonical_release_tree_sha256")!="6eb034b63d4f08b8ea6de08fd38e507d12d4fc2436f0d3a68443624fc4c05d76"
545
+ or predecessor.get("converter_subtree_sha256")!="ccb92a82741202569e4639342e6ae711c246e2122a689f7831715ee182596c2d"
546
+ or authenticated_predecessor_tests!=365
547
+ or not isinstance(authentication,dict)
548
+ or authentication.get("source_archive_digest_matched") is not True
549
+ or authentication.get("archive_canonical_tree_recomputed") is not True
550
+ or authentication.get("archive_tree_matched_promoted_release_fingerprint") is not True
551
+ or authentication.get("archive_converter_subtree_recomputed") is not True
552
+ or authentication.get("compiler_linker_loader_or_execution_invoked") is not False
553
+ ):
554
+ opening_error="Phase 14B opening baseline or predecessor authentication is invalid"
555
+ except (OSError,json.JSONDecodeError,AttributeError,TypeError) as exc:
556
+ opening_error=f"unreadable opening baseline: {type(exc).__name__}"
557
+ if not missing and phase in promoted:
558
+ try:
559
+ manifest=json.loads((base/"manifest.json").read_text(encoding="utf-8"))
560
+ expected_phase={"phase_9":9,"phase_10":10,"phase_11":11,"phase_12":12,"phase_13":13,"phase_14":14,"phase_14d":14}[phase]
561
+ expected_version={"phase_9":"0.9.0","phase_10":"0.10.0","phase_11":"0.11.0","phase_12":"0.12.0","phase_13":"0.13.0","phase_14":"0.14.0","phase_14d":"0.14.3"}[phase]
562
+ minimum_tests={"phase_9":92,"phase_10":154,"phase_11":169,"phase_12":189,"phase_13":224,"phase_14":335,"phase_14d":528}[phase]
563
+ required_tests=manifest.get("required_tests")
564
+ if manifest.get("phase") != expected_phase or manifest.get("version") != expected_version or type(required_tests) is not int or required_tests < minimum_tests:
565
+ manifest_error=f"Phase {expected_phase} manifest identity or regression count is invalid"
566
+ if phase == "phase_13" and manifest_error is None:
567
+ required_schemas={
568
+ "record_facts":"fact-table/0.13",
569
+ "conversion_plan":"conversion-plan/0.13",
570
+ "c_ir":"c-ir/0.13",
571
+ "generated_c":"generated-c/0.13",
572
+ "rule_set":"phase13-static-records-v0.13",
573
+ "renderer":"c-renderer-v0.13",
574
+ "module_policy":"phase13-explicit-record-modules-v0.13",
575
+ "record_policy":"phase13-immutable-automatic-records-v0.13",
576
+ }
577
+ schemas=manifest.get("schemas")
578
+ if not isinstance(schemas,dict) or any(schemas.get(key) != value for key,value in required_schemas.items()):
579
+ manifest_error="Phase 13 manifest contract identities are invalid"
580
+ if phase == "phase_14" and manifest_error is None:
581
+ required_schemas={
582
+ "numeric_facts":"fact-table/0.14",
583
+ "conversion_plan":"conversion-plan/0.14",
584
+ "c_ir":"c-ir/0.14",
585
+ "generated_c":"generated-c/0.14",
586
+ "conversion_summary":"pycforge.conversion-summary/0.14",
587
+ "decision_trace":"pycforge.decision-trace/0.14",
588
+ "rule_set":"phase14-bounded-numeric-v0.14",
589
+ "renderer":"c-renderer-v0.14",
590
+ "numeric_policy":"phase14-proved-floor-arithmetic-v0.14",
591
+ }
592
+ schemas=manifest.get("schemas")
593
+ if not isinstance(schemas,dict) or any(schemas.get(key) != value for key,value in required_schemas.items()):
594
+ manifest_error="Phase 14A manifest contract identities are invalid"
595
+ if phase == "phase_14d" and manifest_error is None:
596
+ required_schemas={
597
+ "keyword_call_facts":"fact-table/0.14.2",
598
+ "keyword_only_call_facts":"fact-table/0.14.3",
599
+ "conversion_plan":"conversion-plan/0.14.3",
600
+ "c_ir":"c-ir/0.14.3",
601
+ "generated_c":"generated-c/0.14.3",
602
+ "conversion_summary":"pycforge.conversion-summary/0.14.3",
603
+ "decision_trace":"pycforge.decision-trace/0.14.3",
604
+ "rule_set":"phase14-required-keyword-only-calls-v0.14.3",
605
+ "renderer":"c-renderer-v0.14.3",
606
+ }
607
+ schemas=manifest.get("schemas")
608
+ if (
609
+ manifest.get("schema_version")!="pycforge.phase14d-manifest/0.14.3"
610
+ or manifest.get("mini_phase")!="14D"
611
+ or not isinstance(schemas,dict)
612
+ or any(schemas.get(key) != value for key,value in required_schemas.items())
613
+ ):
614
+ manifest_error="Phase 14D manifest contract identities are invalid"
615
+ except (OSError,json.JSONDecodeError,AttributeError,TypeError) as exc:
616
+ manifest_error=f"unreadable manifest: {type(exc).__name__}"
617
+ result={
618
+ "audit":"transition",
619
+ "phase":phase,
620
+ "passed":not missing and manifest_error is None and opening_error is None,
621
+ "required_files":sorted(required),
622
+ "missing":missing,
623
+ "required_tests":required_tests,
624
+ "minimum_tests":528 if phase == "phase_14d" else 335 if phase == "phase_14" else 224 if phase == "phase_13" else None,
625
+ "manifest_error":manifest_error,
626
+ }
627
+ if phase == "phase_14b":
628
+ result.update(
629
+ {
630
+ "opening_status":"entry-feasibility-only",
631
+ "opening_error":opening_error,
632
+ "authenticated_predecessor_tests":authenticated_predecessor_tests,
633
+ "manifest_required":False,
634
+ "promotion_claimed":False,
635
+ }
636
+ )
637
+ return result
638
+
639
+ def audit_containers(root: Path) -> dict[str, Any]:
640
+ from pycforge import ConversionRequest, PythonToCConverter, ResultStatus
641
+ from pycforge.converter.c_output import validate_c_text
642
+ from pycforge.converter.contracts.configuration import DEFAULT_RENDERER, DEFAULT_RULE_SET
643
+ from pycforge.converter.contracts.versions import (
644
+ C_IR_SCHEMA,
645
+ CONVERSION_PLAN_SCHEMA,
646
+ GENERATED_C_SCHEMA,
647
+ KEYWORD_CALL_FACT_SCHEMA,
648
+ KEYWORD_ONLY_CALL_FACT_SCHEMA,
649
+ )
650
+ from pycforge.converter.support_templates import default_helper_registry
651
+
652
+ converter=PythonToCConverter()
653
+ accepted_sources={
654
+ "list-index":"def f() -> int:\n values = [1, 2]\n return values[-1]\n",
655
+ "tuple-loop":"def f() -> int:\n values = (1, 2)\n total = 0\n for value in values:\n total = total + value\n return total\n",
656
+ "dict-lookup":"def f() -> int:\n values = {\"a\": 1, \"b\": 2}\n return values[\"b\"]\n",
657
+ }
658
+ rejection_sources={
659
+ "PYC3401":"def f() -> int:\n values = []\n return 1\n",
660
+ "PYC3402":"def f() -> int:\n values = [1, 2.0]\n return 1\n",
661
+ "PYC3403":"def f() -> int:\n values = [1]\n alias = values\n return 1\n",
662
+ "PYC3404":"def f(i: int) -> int:\n values = [1]\n return values[i]\n",
663
+ "PYC3405":"def f() -> int:\n values = [1]\n return values[2]\n",
664
+ "PYC3406":"def f() -> int:\n values = [1]\n values[0] = 2\n return 1\n",
665
+ "PYC3407":"def f() -> int:\n total = 0\n for value in [1]:\n total = total + value\n return total\n",
666
+ }
667
+ accepted={name:converter.convert(ConversionRequest.from_source(source)) for name,source in accepted_sources.items()}
668
+ rejected={code:converter.convert(ConversionRequest.from_source(source)) for code,source in rejection_sources.items()}
669
+ required_tables={"container-shape-facts","container-binding-facts","container-access-facts","container-iteration-facts"}
670
+ active_contracts={
671
+ "rule_set":DEFAULT_RULE_SET,
672
+ "renderer":DEFAULT_RENDERER,
673
+ "keyword_call_facts":KEYWORD_CALL_FACT_SCHEMA,
674
+ "keyword_only_call_facts":KEYWORD_ONLY_CALL_FACT_SCHEMA,
675
+ "conversion_plan":CONVERSION_PLAN_SCHEMA,
676
+ "c_ir":C_IR_SCHEMA,
677
+ "generated_c":GENERATED_C_SCHEMA,
678
+ }
679
+ active_contracts_valid=active_contracts=={
680
+ "rule_set":"phase14-required-keyword-only-calls-v0.14.3",
681
+ "renderer":"c-renderer-v0.14.3",
682
+ "keyword_call_facts":"fact-table/0.14.2",
683
+ "keyword_only_call_facts":"fact-table/0.14.3",
684
+ "conversion_plan":"conversion-plan/0.14.3",
685
+ "c_ir":"c-ir/0.14.3",
686
+ "generated_c":"generated-c/0.14.3",
687
+ }
688
+ accepted_identity_ok=all(
689
+ result.stage_artifact is not None
690
+ and result.stage_artifact.schema_version=="0.14.3"
691
+ and result.stage_artifact.payload.get("schema_version")==GENERATED_C_SCHEMA
692
+ and result.stage_artifact.payload.get("c_ir_schema")==C_IR_SCHEMA
693
+ and result.stage_artifact.payload.get("rule_set_version")==DEFAULT_RULE_SET
694
+ and result.stage_artifact.payload.get("renderer_version")==DEFAULT_RENDERER
695
+ and len([
696
+ table for table in result.stage_artifact.payload.get("fact_tables",())
697
+ if table.get("table_id")=="keyword-call-binding-facts"
698
+ and table.get("schema_version")==KEYWORD_CALL_FACT_SCHEMA
699
+ and table.get("records")==[]
700
+ ])==1
701
+ and len([
702
+ table for table in result.stage_artifact.payload.get("fact_tables",())
703
+ if table.get("table_id")=="keyword-only-call-binding-facts"
704
+ and table.get("schema_version")==KEYWORD_ONLY_CALL_FACT_SCHEMA
705
+ and table.get("records")==[]
706
+ ])==1
707
+ for result in accepted.values()
708
+ )
709
+ accepted_ok=all(
710
+ result.status is ResultStatus.CONVERTED
711
+ and result.generated_c is not None
712
+ and validate_c_text(result.generated_c).accepted
713
+ and result.stage_artifact.payload.get("c_ir_schema") == C_IR_SCHEMA
714
+ and required_tables.issubset({item["table_id"] for item in result.stage_artifact.payload.get("fact_tables",())})
715
+ and not result.stage_artifact.payload.get("helper_manifest")
716
+ for result in accepted.values()
717
+ )
718
+ rejected_ok=all(
719
+ result.status is ResultStatus.REJECTED
720
+ and result.generated_c is None
721
+ and [item.code for item in result.diagnostics]==[code]
722
+ and not result.stage_artifact.payload.get("helper_manifest")
723
+ for code,result in rejected.items()
724
+ )
725
+ registry=default_helper_registry()
726
+ helper_stable=(
727
+ registry.fingerprint=="fcb719f07984f3b79e17262e90f93823a9a0139a60529f8486ab09f6c3663d98"
728
+ )
729
+ return {
730
+ "audit":"containers",
731
+ "passed":accepted_ok and rejected_ok and helper_stable and active_contracts_valid and accepted_identity_ok,
732
+ "active_contracts":active_contracts,
733
+ "active_contract_identities_valid":active_contracts_valid and accepted_identity_ok,
734
+ "active_c_ir_schema":C_IR_SCHEMA,
735
+ "historical_c_ir_schemas":["c-ir/0.11","c-ir/0.12"],
736
+ "accepted_forms":sorted(accepted),
737
+ "rejection_codes":sorted(rejected),
738
+ "capacity_limit":64,
739
+ "allocation":"none",
740
+ "cleanup":"not-required",
741
+ "aliasing":"rejected",
742
+ "runtime_bounds_or_hash_failure_channel":"none; statically proved",
743
+ "helper_manifest_empty":all(not item.stage_artifact.payload.get("helper_manifest") for item in (*accepted.values(),*rejected.values())),
744
+ "phase10_helper_registry_fingerprint_preserved":helper_stable,
745
+ "generated_c_compiled_or_executed":False,
746
+ }
747
+
748
+
749
+ def audit_modules(root: Path) -> dict[str, Any]:
750
+ from pycforge import ConversionRequest, PythonToCConverter, ResultStatus, SourceBundle, SourceDocumentInput
751
+ from pycforge.converter.contracts.configuration import DEFAULT_RENDERER, DEFAULT_RULE_SET
752
+ from pycforge.converter.contracts.versions import (
753
+ C_IR_SCHEMA,
754
+ CONVERSION_PLAN_SCHEMA,
755
+ GENERATED_C_SCHEMA,
756
+ KEYWORD_CALL_FACT_SCHEMA,
757
+ KEYWORD_ONLY_CALL_FACT_SCHEMA,
758
+ )
759
+ from pycforge.converter.core.resource_policy import ResourcePolicy
760
+
761
+ def request(primary_text: str, companions: tuple[tuple[str, str, str], ...] = (), *, primary_module: str = "app", policy: ResourcePolicy | None = None) -> ConversionRequest:
762
+ return ConversionRequest(
763
+ SourceBundle(
764
+ SourceDocumentInput("app.py", primary_text, primary_module),
765
+ tuple(SourceDocumentInput(logical, text, module_id) for module_id, logical, text in companions),
766
+ ),
767
+ resource_policy=policy or ResourcePolicy(),
768
+ )
769
+
770
+ converter=PythonToCConverter()
771
+ accepted=converter.convert(request(
772
+ "from lib.math import increment as inc\n\ndef run(value: int) -> int:\n return inc(value)\n",
773
+ (("lib.math","lib/math.py","def increment(value: int) -> int:\n return value + 1\n"),),
774
+ ))
775
+
776
+ def kind_count(value: Any, kind: str) -> int:
777
+ if isinstance(value,dict):
778
+ return (1 if value.get("kind")==kind else 0)+sum(kind_count(item,kind) for item in value.values())
779
+ if isinstance(value,(list,tuple)):
780
+ return sum(kind_count(item,kind) for item in value)
781
+ return 0
782
+
783
+ payload=accepted.stage_artifact.payload if accepted.stage_artifact else {}
784
+ active_contracts={
785
+ "rule_set":DEFAULT_RULE_SET,
786
+ "renderer":DEFAULT_RENDERER,
787
+ "keyword_call_facts":KEYWORD_CALL_FACT_SCHEMA,
788
+ "keyword_only_call_facts":KEYWORD_ONLY_CALL_FACT_SCHEMA,
789
+ "conversion_plan":CONVERSION_PLAN_SCHEMA,
790
+ "c_ir":C_IR_SCHEMA,
791
+ "generated_c":GENERATED_C_SCHEMA,
792
+ }
793
+ active_contracts_valid=active_contracts=={
794
+ "rule_set":"phase14-required-keyword-only-calls-v0.14.3",
795
+ "renderer":"c-renderer-v0.14.3",
796
+ "keyword_call_facts":"fact-table/0.14.2",
797
+ "keyword_only_call_facts":"fact-table/0.14.3",
798
+ "conversion_plan":"conversion-plan/0.14.3",
799
+ "c_ir":"c-ir/0.14.3",
800
+ "generated_c":"generated-c/0.14.3",
801
+ }
802
+ accepted_identity_ok=bool(
803
+ accepted.stage_artifact is not None
804
+ and accepted.stage_artifact.schema_version=="0.14.3"
805
+ and payload.get("schema_version")==GENERATED_C_SCHEMA
806
+ and payload.get("c_ir_schema")==C_IR_SCHEMA
807
+ and payload.get("rule_set_version")==DEFAULT_RULE_SET
808
+ and payload.get("renderer_version")==DEFAULT_RENDERER
809
+ and len([
810
+ table for table in payload.get("fact_tables",())
811
+ if table.get("table_id")=="keyword-call-binding-facts"
812
+ and table.get("schema_version")==KEYWORD_CALL_FACT_SCHEMA
813
+ and table.get("records")==[]
814
+ ])==1
815
+ and len([
816
+ table for table in payload.get("fact_tables",())
817
+ if table.get("table_id")=="keyword-only-call-binding-facts"
818
+ and table.get("schema_version")==KEYWORD_ONLY_CALL_FACT_SCHEMA
819
+ and table.get("records")==[]
820
+ ])==1
821
+ )
822
+ c_ir=payload.get("c_ir",{})
823
+ c_ir_manifest=c_ir.get("module_manifest",())
824
+ table_ids={item.get("table_id") for item in payload.get("fact_tables",())}
825
+ required_tables={
826
+ "module-identity-facts","module-import-facts","module-function-facts",
827
+ "module-initialization-facts","module-namespace-facts","module-source-facts",
828
+ }
829
+ rule_ids={item.get("rule_id") for item in payload.get("rule_plans",())}
830
+ mappings=payload.get("source_output_mappings",())
831
+ accepted_ok=bool(
832
+ accepted.status is ResultStatus.CONVERTED
833
+ and accepted.generated_c
834
+ and payload.get("c_ir_schema")==C_IR_SCHEMA
835
+ and kind_count(c_ir,"CTranslationUnit")==1
836
+ and c_ir.get("module_order")==["lib.math","app"]
837
+ and c_ir.get("module_dependencies")==[["app","lib.math"]]
838
+ and [item.get("module_id") for item in c_ir_manifest]==["lib.math","app"]
839
+ and sorted(item.get("bundle_ordinal") for item in c_ir_manifest)==[0,1]
840
+ and sum(item.get("is_primary") is True for item in c_ir_manifest)==1
841
+ and required_tables.issubset(table_ids)
842
+ and "phase12.module.import_from" in rule_ids
843
+ and "phase12.module.cross_call" in rule_ids
844
+ and not payload.get("helper_manifest")
845
+ and all(
846
+ item.get("storage")=="none"
847
+ and item.get("owner_module_id") in {"app","lib.math"}
848
+ and item.get("owner_document_id")
849
+ and isinstance(item.get("bundle_function_ordinal"),int)
850
+ for item in _kind_dicts(c_ir,{"CFunctionPrototype","CFunctionDefinition"})
851
+ if item.get("provenance",{}).get("origin_kind")!="support-template"
852
+ )
853
+ and {item.get("module_id") for item in mappings if item.get("source_document_id")} >= {"app","lib.math"}
854
+ )
855
+
856
+ negative_requests={
857
+ "PYC3501":ConversionRequest(SourceBundle(SourceDocumentInput("app.py","def f() -> int:\n return 1\n","Bad"))),
858
+ "PYC3502":request("def f() -> int:\n return 1\n",(("app","other.py","def g() -> int:\n return 2\n"),)),
859
+ "PYC3503":request("from absent import f\n\ndef run() -> int:\n return f()\n"),
860
+ "PYC3504":request("import lib\n\ndef run() -> int:\n return 1\n",(("lib","lib.py","def f() -> int:\n return 1\n"),)),
861
+ "PYC3505":request("from lib import absent\n\ndef run() -> int:\n return 1\n",(("lib","lib.py","def present() -> int:\n return 1\n"),)),
862
+ "PYC3506":request("from lib import f as run\n\ndef run() -> int:\n return 1\n",(("lib","lib.py","def f() -> int:\n return 1\n"),)),
863
+ "PYC3507":request("from lib import f\n\ndef run() -> int:\n return f()\n",(("lib","lib.py","from app import run\n\ndef f() -> int:\n return run()\n"),)),
864
+ "PYC3508":request("from pkg import child\n\ndef run() -> int:\n return 1\n",(("pkg.child","pkg/child.py","def child() -> int:\n return 1\n"),)),
865
+ "PYC3509":request("value = 1\n\ndef run() -> int:\n return value\n"),
866
+ "PYC3510":request("from lib import f\n\ndef run() -> int:\n return f()\n",(("lib","lib.py","def f() -> int:\n return 1\n"),),policy=ResourcePolicy(max_import_edges=0)),
867
+ }
868
+ rejected={code:converter.convert(item) for code,item in negative_requests.items()}
869
+ rejected_ok=all(
870
+ result.status is ResultStatus.REJECTED
871
+ and result.generated_c is None
872
+ and [item.code for item in result.diagnostics]==[code]
873
+ and not (result.stage_artifact and result.stage_artifact.payload.get("c_ir"))
874
+ and not (result.stage_artifact and result.stage_artifact.payload.get("helper_manifest"))
875
+ for code,result in rejected.items()
876
+ )
877
+ return {
878
+ "audit":"modules",
879
+ "passed":accepted_ok and rejected_ok and active_contracts_valid and accepted_identity_ok,
880
+ "active_contracts":active_contracts,
881
+ "active_contract_identities_valid":active_contracts_valid and accepted_identity_ok,
882
+ "active_c_ir_schema":C_IR_SCHEMA,
883
+ "historical_c_ir_schema":"c-ir/0.12",
884
+ "accepted_forms":["absolute-from-import","aliased-from-import","single-translation-unit"],
885
+ "rejection_codes":sorted(rejected),
886
+ "document_limit":64,
887
+ "import_edge_limit":4096,
888
+ "resolution":"explicit-sourcebundle-only",
889
+ "runtime_initialization":"none",
890
+ "translation_unit_count":1 if accepted_ok else None,
891
+ "generated_c_compiled_or_executed":False,
892
+ }
893
+
894
+
895
+ def audit_records(root: Path) -> dict[str, Any]:
896
+ """Audit the complete Phase 13 record slice without invoking a C toolchain."""
897
+ from pycforge import (
898
+ ConversionRequest,
899
+ PythonToCConverter,
900
+ ResultStatus,
901
+ SourceBundle,
902
+ SourceDocumentInput,
903
+ )
904
+ from pycforge.converter.c_output import validate_c_text
905
+ from pycforge.converter.contracts.configuration import DEFAULT_RENDERER, DEFAULT_RULE_SET
906
+ from pycforge.converter.contracts.versions import (
907
+ C_IR_SCHEMA,
908
+ CONVERSION_PLAN_SCHEMA,
909
+ GENERATED_C_SCHEMA,
910
+ KEYWORD_CALL_FACT_SCHEMA,
911
+ KEYWORD_ONLY_CALL_FACT_SCHEMA,
912
+ RECORD_FACT_SCHEMA,
913
+ )
914
+
915
+ accepted_source=(
916
+ "class Sample:\n"
917
+ " count: int\n"
918
+ " ratio: float\n"
919
+ " enabled: bool\n"
920
+ " def __init__(self, count: int, ratio: float, enabled: bool) -> None:\n"
921
+ " self.count = count\n"
922
+ " self.ratio = ratio\n"
923
+ " self.enabled = enabled\n"
924
+ "\n"
925
+ "def read_count(count: int, ratio: float, enabled: bool) -> int:\n"
926
+ " sample = Sample(count, ratio, enabled)\n"
927
+ " return sample.count\n"
928
+ "\n"
929
+ "def read_ratio(count: int, ratio: float, enabled: bool) -> float:\n"
930
+ " sample = Sample(count, ratio, enabled)\n"
931
+ " return sample.ratio\n"
932
+ "\n"
933
+ "def read_enabled(count: int, ratio: float, enabled: bool) -> bool:\n"
934
+ " sample = Sample(count, ratio, enabled)\n"
935
+ " return sample.enabled\n"
936
+ )
937
+ base=(
938
+ "class Point:\n"
939
+ " x: int\n"
940
+ " y: int\n"
941
+ " def __init__(self, x: int, y: int) -> None:\n"
942
+ " self.x = x\n"
943
+ " self.y = y\n"
944
+ )
945
+ rejection_sources={
946
+ "PYC3601":(
947
+ "class Point(Base):\n"
948
+ " x: int\n"
949
+ " def __init__(self, x: int) -> None:\n"
950
+ " self.x = x\n"
951
+ "\ndef run() -> int:\n return 1\n"
952
+ ),
953
+ "PYC3602":(
954
+ "class Point:\n"
955
+ " x: str\n"
956
+ " def __init__(self, x: str) -> None:\n"
957
+ " self.x = x\n"
958
+ "\ndef run() -> int:\n return 1\n"
959
+ ),
960
+ "PYC3603":base.replace(
961
+ " self.x = x\n self.y = y\n",
962
+ " self.y = y\n self.x = x\n",
963
+ )+"\ndef run() -> int:\n return 1\n",
964
+ "PYC3604":base+(
965
+ " def total(self) -> int:\n"
966
+ " return self.x + self.y\n"
967
+ "\ndef run() -> int:\n return 1\n"
968
+ ),
969
+ "PYC3605":base+(
970
+ "\ndef run() -> int:\n"
971
+ " point = Point(x=1, y=2)\n"
972
+ " return point.x\n"
973
+ ),
974
+ "PYC3606":base+(
975
+ "\ndef run() -> int:\n"
976
+ " point = Point(1, 2)\n"
977
+ " alias = point\n"
978
+ " return point.x\n"
979
+ ),
980
+ "PYC3607":base+(
981
+ "\ndef run() -> int:\n"
982
+ " point = Point(1, 2)\n"
983
+ " point.x = 3\n"
984
+ " return point.x\n"
985
+ ),
986
+ }
987
+ record_tables_required={
988
+ "record-definition-facts",
989
+ "record-field-facts",
990
+ "record-initializer-facts",
991
+ "record-instance-facts",
992
+ "record-binding-facts",
993
+ "record-access-facts",
994
+ }
995
+ record_rules_required={
996
+ "phase13.record.class",
997
+ "phase13.record.field",
998
+ "phase13.record.initializer",
999
+ "phase13.record.construction",
1000
+ "phase13.record.binding",
1001
+ "phase13.record.name",
1002
+ "phase13.record.attribute_read",
1003
+ }
1004
+
1005
+ try:
1006
+ converter=PythonToCConverter()
1007
+ first=converter.convert(ConversionRequest.from_source(accepted_source))
1008
+ second=PythonToCConverter().convert(ConversionRequest.from_source(accepted_source))
1009
+ payload=first.stage_artifact.payload if first.stage_artifact else {}
1010
+ active_contracts={
1011
+ "rule_set":DEFAULT_RULE_SET,
1012
+ "renderer":DEFAULT_RENDERER,
1013
+ "keyword_call_facts":KEYWORD_CALL_FACT_SCHEMA,
1014
+ "keyword_only_call_facts":KEYWORD_ONLY_CALL_FACT_SCHEMA,
1015
+ "conversion_plan":CONVERSION_PLAN_SCHEMA,
1016
+ "c_ir":C_IR_SCHEMA,
1017
+ "generated_c":GENERATED_C_SCHEMA,
1018
+ }
1019
+ active_contracts_valid=active_contracts=={
1020
+ "rule_set":"phase14-required-keyword-only-calls-v0.14.3",
1021
+ "renderer":"c-renderer-v0.14.3",
1022
+ "keyword_call_facts":"fact-table/0.14.2",
1023
+ "keyword_only_call_facts":"fact-table/0.14.3",
1024
+ "conversion_plan":"conversion-plan/0.14.3",
1025
+ "c_ir":"c-ir/0.14.3",
1026
+ "generated_c":"generated-c/0.14.3",
1027
+ }
1028
+ accepted_identity_ok=bool(
1029
+ first.stage_artifact is not None
1030
+ and first.stage_artifact.schema_version=="0.14.3"
1031
+ and payload.get("schema_version")==GENERATED_C_SCHEMA
1032
+ and payload.get("c_ir_schema")==C_IR_SCHEMA
1033
+ and payload.get("rule_set_version")==DEFAULT_RULE_SET
1034
+ and payload.get("renderer_version")==DEFAULT_RENDERER
1035
+ and len([
1036
+ table for table in payload.get("fact_tables",())
1037
+ if table.get("table_id")=="keyword-call-binding-facts"
1038
+ and table.get("schema_version")==KEYWORD_CALL_FACT_SCHEMA
1039
+ and table.get("records")==[]
1040
+ ])==1
1041
+ and len([
1042
+ table for table in payload.get("fact_tables",())
1043
+ if table.get("table_id")=="keyword-only-call-binding-facts"
1044
+ and table.get("schema_version")==KEYWORD_ONLY_CALL_FACT_SCHEMA
1045
+ and table.get("records")==[]
1046
+ ])==1
1047
+ )
1048
+ c_ir=payload.get("c_ir",{})
1049
+ generated=first.generated_c or ""
1050
+ conformance=validate_c_text(generated)
1051
+ tables={
1052
+ item.get("table_id"):item
1053
+ for item in payload.get("fact_tables",())
1054
+ if str(item.get("table_id","")).startswith("record-")
1055
+ }
1056
+ values={
1057
+ table_id:[item.get("value",{}) for item in table.get("records",())]
1058
+ for table_id,table in tables.items()
1059
+ }
1060
+ definitions=values.get("record-definition-facts",[])
1061
+ fields=values.get("record-field-facts",[])
1062
+ instances=values.get("record-instance-facts",[])
1063
+ bindings=values.get("record-binding-facts",[])
1064
+ accesses=values.get("record-access-facts",[])
1065
+ facts_ok=bool(
1066
+ set(tables)==record_tables_required
1067
+ and all(
1068
+ table.get("schema_version")==RECORD_FACT_SCHEMA
1069
+ and table.get("producer_stage")=="analysis.plan"
1070
+ and table.get("completeness")=="complete"
1071
+ for table in tables.values()
1072
+ )
1073
+ and len(definitions)==1
1074
+ and sorted(item.get("category") for item in fields)==[
1075
+ "boolean-like","floating-like","integer-like"
1076
+ ]
1077
+ and len(instances)==3
1078
+ and len(bindings)==3
1079
+ and len(accesses)==3
1080
+ and all(item.get("mutable") is False for item in (*definitions,*fields,*instances))
1081
+ and all(
1082
+ item.get("storage_model")=="automatic-inline-record"
1083
+ and item.get("ownership_model")=="unique-lexical-owner"
1084
+ and item.get("cleanup_model")=="none"
1085
+ and item.get("nullability_model")=="non-null-by-construction"
1086
+ for item in (*definitions,*instances)
1087
+ )
1088
+ and all(
1089
+ item.get("allocation_model")=="none"
1090
+ and item.get("aliasing_model")=="forbidden"
1091
+ for item in instances
1092
+ )
1093
+ and all(
1094
+ item.get("single_assignment") is True
1095
+ and item.get("noalias") is True
1096
+ and item.get("escapes") is False
1097
+ for item in bindings
1098
+ )
1099
+ and all(
1100
+ item.get("access_mode")=="read"
1101
+ and item.get("statically_bound") is True
1102
+ for item in accesses
1103
+ )
1104
+ )
1105
+ rule_ids={item.get("rule_id") for item in payload.get("rule_plans",())}
1106
+ rules_ok=record_rules_required.issubset(rule_ids)
1107
+ record_defs=_kind_dicts(c_ir,{"CRecordDefinition"})
1108
+ initializers=_kind_dicts(c_ir,{"CRecordInitializer"})
1109
+ members=_kind_dicts(c_ir,{"CMemberAccessExpr"})
1110
+ record_objects=[
1111
+ item for item in _kind_dicts(c_ir,{"CVariableDeclaration"})
1112
+ if item.get("initializer",{}).get("kind")=="CRecordInitializer"
1113
+ ]
1114
+ rendered_typedef=bool(re.search(
1115
+ r"typedef\s+struct\s+[A-Za-z_][A-Za-z0-9_]*\s*\{[^}]+\}\s*[A-Za-z_][A-Za-z0-9_]*\s*;",
1116
+ generated,
1117
+ re.DOTALL,
1118
+ ))
1119
+ rendered_aggregate=bool(re.search(
1120
+ r"const\s+[A-Za-z_][A-Za-z0-9_]*\s+[A-Za-z_][A-Za-z0-9_]*\s*=\s*\{[^}]+\}\s*;",
1121
+ generated,
1122
+ ))
1123
+ rendered_member_access=bool(re.search(
1124
+ r"[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*",
1125
+ generated,
1126
+ ))
1127
+ cir_ok=bool(
1128
+ payload.get("c_ir_schema")==C_IR_SCHEMA
1129
+ and len(record_defs)==1
1130
+ and [field.get("type_ref",{}).get("base") for field in record_defs[0].get("fields",())]
1131
+ == ["int64_t","double","bool"]
1132
+ and len(initializers)==3
1133
+ and len(record_objects)==3
1134
+ and all(item.get("type_ref",{}).get("qualifiers")==["const"] for item in record_objects)
1135
+ and len(members)==3
1136
+ and all(item.get("mode")=="direct" for item in members)
1137
+ and rendered_typedef
1138
+ and rendered_aggregate
1139
+ and rendered_member_access
1140
+ )
1141
+ forbidden_c_identifiers={"malloc","calloc","realloc","free","NULL","nullptr"}
1142
+ c_identifiers=set(re.findall(r"[A-Za-z_][A-Za-z0-9_]*",generated))
1143
+ no_runtime=bool(
1144
+ not payload.get("helper_manifest")
1145
+ and forbidden_c_identifiers.isdisjoint(c_identifiers)
1146
+ and all(
1147
+ item.get("provenance",{}).get("origin_kind")!="support-template"
1148
+ for item in _kind_dicts(c_ir,{"CRecordDefinition","CVariableDeclaration","CMemberAccessExpr"})
1149
+ )
1150
+ )
1151
+ in_process_deterministic=bool(
1152
+ generated
1153
+ and generated==second.generated_c
1154
+ and first.output_fingerprint==second.output_fingerprint
1155
+ and first.stage_artifact is not None
1156
+ and second.stage_artifact is not None
1157
+ and first.stage_artifact.artifact_fingerprint==second.stage_artifact.artifact_fingerprint
1158
+ )
1159
+ fresh_process_determinism=audit_determinism(root,accepted_source)
1160
+ deterministic=in_process_deterministic and fresh_process_determinism["passed"]
1161
+ accepted_ok=bool(
1162
+ first.status is ResultStatus.CONVERTED
1163
+ and second.status is ResultStatus.CONVERTED
1164
+ and not first.diagnostics
1165
+ and conformance.accepted
1166
+ and facts_ok
1167
+ and rules_ok
1168
+ and cir_ok
1169
+ and no_runtime
1170
+ and deterministic
1171
+ and active_contracts_valid
1172
+ and accepted_identity_ok
1173
+ )
1174
+
1175
+ rejected={
1176
+ code:converter.convert(ConversionRequest.from_source(source))
1177
+ for code,source in rejection_sources.items()
1178
+ }
1179
+ rejected_ok=all(
1180
+ result.status is ResultStatus.REJECTED
1181
+ and result.generated_c is None
1182
+ and [item.code for item in result.diagnostics]==[code]
1183
+ and not (result.stage_artifact and result.stage_artifact.payload.get("c_ir"))
1184
+ and not (result.stage_artifact and result.stage_artifact.payload.get("helper_manifest"))
1185
+ for code,result in rejected.items()
1186
+ )
1187
+
1188
+ imported_record=converter.convert(
1189
+ ConversionRequest(
1190
+ SourceBundle(
1191
+ SourceDocumentInput(
1192
+ "app.py",
1193
+ "from lib import Point\n\ndef run() -> int:\n"
1194
+ " point = Point(1, 2)\n return point.x\n",
1195
+ "app",
1196
+ ),
1197
+ (
1198
+ SourceDocumentInput(
1199
+ "lib.py",
1200
+ base+"\ndef keep() -> int:\n return 1\n",
1201
+ "lib",
1202
+ ),
1203
+ ),
1204
+ )
1205
+ )
1206
+ )
1207
+ cross_module_ok=bool(
1208
+ imported_record.status is ResultStatus.REJECTED
1209
+ and imported_record.generated_c is None
1210
+ and [item.code for item in imported_record.diagnostics]==["PYC3610"]
1211
+ and not (imported_record.stage_artifact and imported_record.stage_artifact.payload.get("c_ir"))
1212
+ )
1213
+ return {
1214
+ "audit":"records",
1215
+ "passed":accepted_ok and rejected_ok and cross_module_ok,
1216
+ "active_contracts":active_contracts,
1217
+ "active_contract_identities_valid":active_contracts_valid and accepted_identity_ok,
1218
+ "accepted_field_categories":["integer-like","floating-like","boolean-like"],
1219
+ "record_fact_schema":RECORD_FACT_SCHEMA,
1220
+ "record_fact_tables":sorted(tables),
1221
+ "record_rule_ids":sorted(record_rules_required & rule_ids),
1222
+ "c_ir_schema":payload.get("c_ir_schema"),
1223
+ "c_ir_record_definitions":len(record_defs),
1224
+ "c_ir_record_initializers":len(initializers),
1225
+ "c_ir_direct_member_accesses":sum(item.get("mode")=="direct" for item in members),
1226
+ "rendered_typedef_struct":rendered_typedef,
1227
+ "rendered_const_aggregate":rendered_aggregate,
1228
+ "rendered_direct_member_access":rendered_member_access,
1229
+ "generated_c_textually_valid":conformance.accepted,
1230
+ "generated_c_validation_message":conformance.message,
1231
+ "deterministic":deterministic,
1232
+ "fresh_process_determinism_sha256":fresh_process_determinism["sha256"],
1233
+ "helper_manifest_empty":not payload.get("helper_manifest"),
1234
+ "allocation":"none",
1235
+ "cleanup":"none",
1236
+ "nullability":"non-null-by-construction",
1237
+ "ownership":"unique automatic local",
1238
+ "rejection_codes":sorted(rejected),
1239
+ "cross_module_record_rejection_code":"PYC3610",
1240
+ "generated_c_validation":"structured C IR and independent textual conformance only",
1241
+ "c_toolchain_invoked":False,
1242
+ "generated_c_compiled_or_executed":False,
1243
+ }
1244
+ except Exception as exc:
1245
+ return {
1246
+ "audit":"records",
1247
+ "passed":False,
1248
+ "error":f"{type(exc).__name__}: {exc}",
1249
+ "c_toolchain_invoked":False,
1250
+ "generated_c_compiled_or_executed":False,
1251
+ }
1252
+
1253
+
1254
+ def _phase14_floor_reference(dividend: int, divisor: int) -> tuple[int, int]:
1255
+ """Model the frozen helpers without compiling or executing generated C."""
1256
+ magnitude=abs(dividend)//abs(divisor)
1257
+ quotient=-magnitude if (dividend < 0) != (divisor < 0) else magnitude
1258
+ remainder=dividend-(quotient*divisor)
1259
+ if remainder != 0 and (remainder < 0) != (divisor < 0):
1260
+ quotient-=1
1261
+ remainder+=divisor
1262
+ return quotient,remainder
1263
+
1264
+
1265
+ def audit_numeric(root: Path) -> dict[str, Any]:
1266
+ """Independently gate the bounded Phase 14A integer ``//``/``%`` slice."""
1267
+ from pycforge import ConversionRequest, PythonToCConverter, ResultStatus
1268
+ from pycforge.converter.c_output import validate_c_text
1269
+ from pycforge.converter.contracts.configuration import (
1270
+ DEFAULT_NUMERIC_POLICY,
1271
+ DEFAULT_RENDERER,
1272
+ DEFAULT_RULE_SET,
1273
+ PHASE13_RENDERER,
1274
+ PHASE13_RULE_SET,
1275
+ )
1276
+ from pycforge.converter.contracts.versions import (
1277
+ C_IR_SCHEMA,
1278
+ CONVERSION_PLAN_SCHEMA,
1279
+ CONVERSION_SUMMARY_SCHEMA,
1280
+ DECISION_TRACE_SCHEMA,
1281
+ GENERATED_C_SCHEMA,
1282
+ KEYWORD_CALL_FACT_SCHEMA,
1283
+ KEYWORD_ONLY_CALL_FACT_SCHEMA,
1284
+ NUMERIC_FACT_SCHEMA,
1285
+ )
1286
+ from pycforge.converter.core.serialization import result_to_json
1287
+ from pycforge.converter.lowering import FirstSliceLoweringStage
1288
+ from pycforge.converter.support_templates import (
1289
+ FLOOR_DIV_REFERENCE,
1290
+ FLOOR_MOD_REFERENCE,
1291
+ default_helper_registry,
1292
+ )
1293
+
1294
+ expected_helpers=(FLOOR_DIV_REFERENCE.canonical,FLOOR_MOD_REFERENCE.canonical)
1295
+ expected_assets={
1296
+ FLOOR_DIV_REFERENCE.canonical:"23fa88ff57ffe15bc20845c6a7359f6d35648ecffd3a30ea23fe43f24e1dd869",
1297
+ FLOOR_MOD_REFERENCE.canonical:"cc2e29f5823a119009df78ed20dc410c6eef4d72c57ada115790bd1120dc663e",
1298
+ }
1299
+ source=(
1300
+ "def left(value: int) -> int:\n"
1301
+ " return value + 1\n\n"
1302
+ "def run(value: int) -> int:\n"
1303
+ " return left(value) // 3 + value % -2\n"
1304
+ )
1305
+ rejection_sources={
1306
+ "float-right":("PYC3701","def f(value: int) -> int:\n return value // 2.0\n"),
1307
+ "bool-right":("PYC3701","def f(value: int) -> int:\n return value % True\n"),
1308
+ "prohibited-context":("PYC3701","def f(value: int) -> int:\n inner = lambda: value // 2\n return value\n"),
1309
+ "zero":("PYC3702","def f(value: int) -> int:\n return value // 0\n"),
1310
+ "negative-one":("PYC3702","def f(value: int) -> int:\n return value % -1\n"),
1311
+ "variable":("PYC3702","def f(value: int, divisor: int) -> int:\n return value // divisor\n"),
1312
+ "folded-expression":("PYC3702","def f(value: int) -> int:\n return value % (1 + 1)\n"),
1313
+ "positive-out-of-range":("PYC3702","def f(value: int) -> int:\n return value // 9223372036854775808\n"),
1314
+ "int64-minimum":("PYC3702","def f(value: int) -> int:\n return value % -9223372036854775808\n"),
1315
+ }
1316
+ try:
1317
+ converter=PythonToCConverter()
1318
+ accepted=converter.convert(ConversionRequest.from_source(source))
1319
+ repeated=converter.convert(ConversionRequest.from_source(source))
1320
+ if accepted.stage_artifact is None:
1321
+ raise ValueError("accepted conversion omitted its final stage artifact")
1322
+ payload=accepted.stage_artifact.payload
1323
+ fact_tables=[table for table in payload.get("fact_tables",()) if table.get("table_id")=="numeric-operation-facts"]
1324
+ facts=[] if len(fact_tables)!=1 else [record["value"] for record in fact_tables[0].get("records",())]
1325
+ plans=[plan for plan in payload.get("rule_plans",()) if plan.get("rule_id")=="phase14.numeric.floor_arithmetic"]
1326
+ manifest=list(payload.get("helper_manifest",()))
1327
+ manifest_references=tuple(item.get("reference") for item in manifest)
1328
+ manifest_assets={item.get("reference"):item.get("asset_fingerprint") for item in manifest}
1329
+ generated=accepted.generated_c or ""
1330
+
1331
+ accepted_identity_ok=(
1332
+ accepted.status is ResultStatus.CONVERTED
1333
+ and bool(generated)
1334
+ and accepted.stage_artifact.kind=="generated_c"
1335
+ and accepted.stage_artifact.schema_version=="0.14.3"
1336
+ and payload.get("schema_version")==GENERATED_C_SCHEMA
1337
+ and FirstSliceLoweringStage.input_schema==CONVERSION_PLAN_SCHEMA
1338
+ and payload.get("c_ir_schema")==C_IR_SCHEMA
1339
+ and payload.get("c_ir",{}).get("schema_version")==C_IR_SCHEMA
1340
+ and payload.get("rule_set_version")==DEFAULT_RULE_SET
1341
+ and payload.get("renderer_version")==DEFAULT_RENDERER
1342
+ and payload.get("numeric_policy_version")==DEFAULT_NUMERIC_POLICY
1343
+ and accepted.conversion_summary is not None
1344
+ and accepted.conversion_summary.get("schema_version")==CONVERSION_SUMMARY_SCHEMA
1345
+ and accepted.decision_trace is not None
1346
+ and accepted.decision_trace.get("schema_version")==DECISION_TRACE_SCHEMA
1347
+ and accepted.decision_trace.get("numeric_policy_version")==DEFAULT_NUMERIC_POLICY
1348
+ and len([
1349
+ table for table in payload.get("fact_tables",())
1350
+ if table.get("table_id")=="keyword-call-binding-facts"
1351
+ and table.get("schema_version")==KEYWORD_CALL_FACT_SCHEMA
1352
+ and table.get("records")==[]
1353
+ ])==1
1354
+ and len([
1355
+ table for table in payload.get("fact_tables",())
1356
+ if table.get("table_id")=="keyword-only-call-binding-facts"
1357
+ and table.get("schema_version")==KEYWORD_ONLY_CALL_FACT_SCHEMA
1358
+ and table.get("records")==[]
1359
+ ])==1
1360
+ )
1361
+ fact_keys={
1362
+ "operation_id","binop_node_id","function_node_id","module_id","document_id","logical_name",
1363
+ "operator_node_id","operator_kind","left_node_id","right_node_id","left_category","right_category",
1364
+ "result_category","left_c_type","right_c_type","result_c_type","divisor_value",
1365
+ "divisor_literal_node_ids","literal_shape","divisor_in_admitted_domain","divisor_nonzero_proved",
1366
+ "negative_one_divisor_excluded","minimum_signed_divisor_excluded","helper_requirement",
1367
+ "evaluation_order","operands_evaluated_once","c_type","failure_policy","support_state",
1368
+ "parameter_ownership","result_ownership","allocation_model","cleanup_model",
1369
+ "runtime_failure_channel","target_contract",
1370
+ }
1371
+ facts_ok=(
1372
+ len(fact_tables)==1
1373
+ and fact_tables[0].get("schema_version")==NUMERIC_FACT_SCHEMA
1374
+ and len(facts)==2
1375
+ and {fact.get("operator_kind") for fact in facts}=={"floor-divide","floor-modulo"}
1376
+ and {fact.get("divisor_value") for fact in facts}=={3,-2}
1377
+ and {fact.get("literal_shape") for fact in facts}=={"constant","unary-minus"}
1378
+ and {fact.get("helper_requirement") for fact in facts}==set(expected_helpers)
1379
+ and all(fact_keys.issubset(fact) for fact in facts)
1380
+ and all(
1381
+ fact.get("left_category")=="integer-like"
1382
+ and fact.get("right_category")=="integer-like"
1383
+ and fact.get("result_category")=="integer-like"
1384
+ and fact.get("left_c_type")==fact.get("right_c_type")==fact.get("result_c_type")==fact.get("c_type")=="int64_t"
1385
+ and fact.get("divisor_in_admitted_domain") is True
1386
+ and fact.get("divisor_nonzero_proved") is True
1387
+ and fact.get("negative_one_divisor_excluded") is True
1388
+ and fact.get("minimum_signed_divisor_excluded") is True
1389
+ and fact.get("evaluation_order")==[fact.get("left_node_id"),fact.get("right_node_id")]
1390
+ and fact.get("operands_evaluated_once") is True
1391
+ and fact.get("support_state")=="SupportedWithHelper"
1392
+ and fact.get("parameter_ownership")=="scalar-values-by-value"
1393
+ and fact.get("result_ownership")=="scalar-value-by-value"
1394
+ and fact.get("allocation_model")==fact.get("cleanup_model")=="none"
1395
+ and fact.get("runtime_failure_channel")=="none"
1396
+ and fact.get("target_contract")=="c11-portable-fixed-v1"
1397
+ and all(isinstance(fact.get(key),str) and fact.get(key) for key in ("module_id","document_id","logical_name","function_node_id"))
1398
+ for fact in facts
1399
+ )
1400
+ )
1401
+ plans_ok=(
1402
+ len(plans)==2
1403
+ and {item for plan in plans for item in plan.get("helper_requirements",())}==set(expected_helpers)
1404
+ and all(
1405
+ plan.get("rule_version")=="0.14"
1406
+ and plan.get("support_state")=="SupportedWithHelper"
1407
+ and not plan.get("unresolved_obligations")
1408
+ and set(plan.get("resolved_obligations",()))==set(plan.get("semantic_obligations",()))
1409
+ for plan in plans
1410
+ )
1411
+ )
1412
+ registry=default_helper_registry()
1413
+ helpers_ok=(
1414
+ manifest_references==expected_helpers
1415
+ and manifest_assets==expected_assets
1416
+ and tuple(payload.get("helper_requirements",()))==expected_helpers
1417
+ and payload.get("helper_registry_fingerprint")==registry.fingerprint
1418
+ and registry.fingerprint=="fcb719f07984f3b79e17262e90f93823a9a0139a60529f8486ab09f6c3663d98"
1419
+ and all(item.get("factory_kind")=="structured-c-ir" for item in manifest)
1420
+ )
1421
+ c_calls=_kind_dicts(payload.get("c_ir",{}),{"CCallExpr"})
1422
+ helper_calls=[
1423
+ call for call in c_calls
1424
+ if isinstance(call.get("callee"),dict)
1425
+ and str(call["callee"].get("binding_id","")).startswith("helper-binding:pycf.i64.floor_")
1426
+ ]
1427
+ temporary_names=re.findall(r"^\s*int64_t (pycf_numeric_(?:left|right|result)_[0-9a-f]+) =",generated,re.MULTILINE)
1428
+ temporary_kinds=[next(kind for kind in ("left","right","result") if name.startswith(f"pycf_numeric_{kind}_")) for name in temporary_names]
1429
+ staging_ok=(
1430
+ len(helper_calls)==2
1431
+ and temporary_kinds==["left","right","result","left","right","result"]
1432
+ and generated.find("int64_t pycf_call_") < generated.find("int64_t pycf_numeric_left_")
1433
+ and generated.count("pycf_i64_floor_div_v1(")==3
1434
+ and generated.count("pycf_i64_floor_mod_v1(")==3
1435
+ )
1436
+ conformance=validate_c_text(generated)
1437
+ deterministic=(
1438
+ result_to_json(accepted)==result_to_json(repeated)
1439
+ and accepted.output_fingerprint==repeated.output_fingerprint
1440
+ and accepted.request_fingerprint==repeated.request_fingerprint
1441
+ )
1442
+ fresh_code=(
1443
+ "import hashlib,json; from pycforge import ConversionRequest,PythonToCConverter; "
1444
+ f"r=PythonToCConverter().convert(ConversionRequest.from_source({source!r})); "
1445
+ "print(json.dumps({'generated_sha256':hashlib.sha256((r.generated_c or '').encode()).hexdigest(),"
1446
+ "'output_fingerprint':None if r.output_fingerprint is None else r.output_fingerprint.value,"
1447
+ "'request_fingerprint':None if r.request_fingerprint is None else r.request_fingerprint.value},sort_keys=True))"
1448
+ )
1449
+ env={**__import__("os").environ,"PYTHONPATH":str(root)}
1450
+ fresh=json.loads(subprocess.check_output([sys.executable,"-c",fresh_code],cwd=root,env=env,text=True))
1451
+ fresh_process_ok=fresh=={
1452
+ "generated_sha256":hashlib.sha256(generated.encode()).hexdigest(),
1453
+ "output_fingerprint":accepted.output_fingerprint.value if accepted.output_fingerprint else None,
1454
+ "request_fingerprint":accepted.request_fingerprint.value if accepted.request_fingerprint else None,
1455
+ }
1456
+
1457
+ rejected={label:converter.convert(ConversionRequest.from_source(text)) for label,(_,text) in rejection_sources.items()}
1458
+ rejection_codes={label:[item.code for item in result.diagnostics] for label,result in rejected.items()}
1459
+ rejections_ok=all(
1460
+ result.status is ResultStatus.REJECTED
1461
+ and result.generated_c is None
1462
+ and result.output_fingerprint is None
1463
+ and rejection_codes[label]==[expected_code]
1464
+ for label,(expected_code,_) in rejection_sources.items()
1465
+ for result in (rejected[label],)
1466
+ )
1467
+
1468
+ dividends=(-(2**63),-(2**63)+1,-7,-1,0,1,7,2**63-1)
1469
+ divisors=(-(2**63)+1,-7,-2,1,2,7,2**63-1)
1470
+ reference_cases=[(a,b) for a in dividends for b in divisors]
1471
+ reference_ok=all(_phase14_floor_reference(a,b)==(a//b,a%b) for a,b in reference_cases)
1472
+
1473
+ phase13_source=(
1474
+ "def plus(value: int) -> int:\n"
1475
+ " return value + 1\n\n"
1476
+ "def run() -> int:\n"
1477
+ " return plus(2)\n"
1478
+ )
1479
+ phase13_request=ConversionRequest.from_source(
1480
+ phase13_source,
1481
+ rule_set_version=PHASE13_RULE_SET,
1482
+ renderer_version=PHASE13_RENDERER,
1483
+ )
1484
+ phase13=converter.convert(phase13_request)
1485
+ phase13_payload={} if phase13.stage_artifact is None else phase13.stage_artifact.payload
1486
+ historical_ok=(
1487
+ phase13.status is ResultStatus.CONVERTED
1488
+ and phase13.stage_artifact is not None
1489
+ and phase13.stage_artifact.schema_version=="0.13"
1490
+ and phase13_payload.get("schema_version")=="generated-c/0.13"
1491
+ and phase13_payload.get("c_ir_schema")=="c-ir/0.13"
1492
+ and "numeric_policy_version" not in phase13_payload
1493
+ and phase13.conversion_summary is not None
1494
+ and phase13.conversion_summary.get("schema_version")=="pycforge.conversion-summary/0.13"
1495
+ and "numeric_policy_version" not in phase13.conversion_summary
1496
+ and phase13.decision_trace is not None
1497
+ and phase13.decision_trace.get("schema_version")=="pycforge.decision-trace/0.13"
1498
+ and "numeric_policy_version" not in phase13.decision_trace
1499
+ and hashlib.sha256((phase13.generated_c or "").encode()).hexdigest()=="d54ec54f5d9b0553d73c77179c3429928eb2c2deaa4963776429b628918cf257"
1500
+ and phase13.output_fingerprint is not None
1501
+ and phase13.output_fingerprint.value=="da9e27bd909e2ddf9154b072d668c98576a925aa7a342244db566d254ec0e556"
1502
+ and phase13.request_fingerprint is not None
1503
+ and phase13.request_fingerprint.value=="a8cb25e7596427d78a9b6560e833786920216631f3179da4abc5a8a12fabe3fb"
1504
+ )
1505
+ legacy_active=converter.convert(ConversionRequest.from_source(phase13_source,target_contract="pycforge-c11-int64-v0.1"))
1506
+ legacy_historical=converter.convert(ConversionRequest.from_source(
1507
+ phase13_source,
1508
+ target_contract="pycforge-c11-int64-v0.1",
1509
+ rule_set_version=PHASE13_RULE_SET,
1510
+ renderer_version=PHASE13_RENDERER,
1511
+ ))
1512
+ target_gate_ok=(
1513
+ legacy_active.status is ResultStatus.REJECTED
1514
+ and [item.code for item in legacy_active.diagnostics]==["PYC1008"]
1515
+ and legacy_historical.status is ResultStatus.CONVERTED
1516
+ )
1517
+ passed=all((
1518
+ accepted_identity_ok,facts_ok,plans_ok,helpers_ok,staging_ok,conformance.accepted,
1519
+ deterministic,fresh_process_ok,rejections_ok,reference_ok,historical_ok,target_gate_ok,
1520
+ ))
1521
+ return {
1522
+ "audit":"numeric",
1523
+ "passed":passed,
1524
+ "active_contracts":{
1525
+ "rule_set":DEFAULT_RULE_SET,
1526
+ "renderer":DEFAULT_RENDERER,
1527
+ "numeric_policy":DEFAULT_NUMERIC_POLICY,
1528
+ "numeric_facts":NUMERIC_FACT_SCHEMA,
1529
+ "keyword_call_facts":KEYWORD_CALL_FACT_SCHEMA,
1530
+ "keyword_only_call_facts":KEYWORD_ONLY_CALL_FACT_SCHEMA,
1531
+ "conversion_plan":CONVERSION_PLAN_SCHEMA,
1532
+ "c_ir":C_IR_SCHEMA,
1533
+ "generated_c":GENERATED_C_SCHEMA,
1534
+ "conversion_summary":CONVERSION_SUMMARY_SCHEMA,
1535
+ "decision_trace":DECISION_TRACE_SCHEMA,
1536
+ },
1537
+ "active_contract_identities_valid":accepted_identity_ok,
1538
+ "accepted_identity_valid":accepted_identity_ok,
1539
+ "numeric_fact_count":len(facts),
1540
+ "numeric_facts_valid":facts_ok,
1541
+ "numeric_rule_plan_count":len(plans),
1542
+ "numeric_rule_plans_valid":plans_ok,
1543
+ "helper_registry_fingerprint":registry.fingerprint,
1544
+ "helper_references":list(manifest_references),
1545
+ "helpers_valid":helpers_ok,
1546
+ "c_ir_helper_call_count":len(helper_calls),
1547
+ "temporary_order":temporary_kinds,
1548
+ "evaluation_staging_valid":staging_ok,
1549
+ "generated_c_conformance":conformance.accepted,
1550
+ "deterministic":deterministic,
1551
+ "fresh_process_deterministic":fresh_process_ok,
1552
+ "generated_c_sha256":hashlib.sha256(generated.encode()).hexdigest(),
1553
+ "rejection_codes":rejection_codes,
1554
+ "rejections_publish_no_c":rejections_ok,
1555
+ "reference_model_case_count":len(reference_cases),
1556
+ "reference_model_matches_python":reference_ok,
1557
+ "phase13_exact_compatibility":historical_ok,
1558
+ "target_contract_gate_valid":target_gate_ok,
1559
+ "c_toolchain_invoked":False,
1560
+ "generated_c_compiled_or_executed":False,
1561
+ }
1562
+ except Exception as exc:
1563
+ return {
1564
+ "audit":"numeric",
1565
+ "passed":False,
1566
+ "error":f"{type(exc).__name__}: {exc}",
1567
+ "c_toolchain_invoked":False,
1568
+ "generated_c_compiled_or_executed":False,
1569
+ }
1570
+
1571
+
1572
+ def _kind_dicts(value: Any, kinds: set[str]) -> list[dict[str, Any]]:
1573
+ result: list[dict[str, Any]]=[]
1574
+ if isinstance(value,dict):
1575
+ if value.get("kind") in kinds:
1576
+ result.append(value)
1577
+ for item in value.values():
1578
+ result.extend(_kind_dicts(item,kinds))
1579
+ elif isinstance(value,(list,tuple)):
1580
+ for item in value:
1581
+ result.extend(_kind_dicts(item,kinds))
1582
+ return result
1583
+
1584
+
1585
+ def audit_conditional(root: Path) -> dict[str, Any]:
1586
+ """Independently gate the bounded Phase 14B conditional-region slice."""
1587
+ from pycforge import ConversionRequest, PythonToCConverter, ResultStatus
1588
+ from pycforge.converter.c_output import validate_c_text
1589
+ from pycforge.converter.conditional_regions import (
1590
+ CONDITIONAL_REGION_KEY_DOMAIN,
1591
+ CONDITIONAL_REGION_LOWERING_SHAPE,
1592
+ CONDITIONAL_REGION_OBLIGATIONS,
1593
+ CONDITIONAL_REGION_PROVENANCE_EVIDENCE,
1594
+ CONDITIONAL_REGION_TABLE_DEPENDENCIES,
1595
+ CONDITIONAL_REGION_TABLE_ID,
1596
+ )
1597
+ from pycforge.converter.contracts.configuration import (
1598
+ DEFAULT_NUMERIC_POLICY,
1599
+ DEFAULT_RENDERER,
1600
+ DEFAULT_RULE_SET,
1601
+ PHASE14A_RENDERER,
1602
+ PHASE14A_RULE_SET,
1603
+ PHASE14B_RENDERER,
1604
+ PHASE14B_RULE_SET,
1605
+ )
1606
+ from pycforge.converter.contracts.versions import (
1607
+ C_IR_SCHEMA,
1608
+ CONDITIONAL_FACT_SCHEMA,
1609
+ CONVERSION_PLAN_SCHEMA,
1610
+ CONVERSION_SUMMARY_SCHEMA,
1611
+ DECISION_TRACE_SCHEMA,
1612
+ GENERATED_C_SCHEMA,
1613
+ KEYWORD_CALL_FACT_SCHEMA,
1614
+ KEYWORD_ONLY_CALL_FACT_SCHEMA,
1615
+ RESULT_SCHEMA_VERSION,
1616
+ )
1617
+ from pycforge.converter.core.request import ObservationOptions
1618
+ from pycforge.converter.core.serialization import result_to_json
1619
+ from pycforge.converter.support_templates import (
1620
+ FLOOR_DIV_REFERENCE,
1621
+ default_helper_registry,
1622
+ )
1623
+
1624
+ expected_obligations=(
1625
+ "scalar-operand-representations-proved",
1626
+ "unconditional-prefix-proved",
1627
+ "guard-polarity-proved",
1628
+ "short-circuit-order-preserved",
1629
+ "operands-evaluated-left-to-right-once",
1630
+ "prerequisite-statements-branch-contained",
1631
+ "intermediate-values-reused-without-reevaluation",
1632
+ "structured-c-ir-only",
1633
+ "result-materialized-once",
1634
+ "allocation-and-cleanup-absent",
1635
+ "runtime-failure-channel-unchanged",
1636
+ "source-provenance-anchored",
1637
+ "cancellation-safe-points-honored",
1638
+ "target-contract-exact",
1639
+ )
1640
+ expected_dependencies=(
1641
+ "value-category-facts",
1642
+ "evaluation-order-facts",
1643
+ "call-target-facts",
1644
+ "container-access-facts",
1645
+ "record-access-facts",
1646
+ "numeric-operation-facts",
1647
+ )
1648
+ expected_provenance=(
1649
+ "exact-scalar-operands",
1650
+ "unconditional-prefix",
1651
+ "accumulated-result-guard",
1652
+ "left-to-right-once",
1653
+ "branch-contained-prerequisites",
1654
+ "flat-structured-c-ir",
1655
+ )
1656
+ source=(
1657
+ "def flag(value: bool) -> bool:\n"
1658
+ " return value\n\n"
1659
+ "def value(item: int) -> int:\n"
1660
+ " return item\n\n"
1661
+ "def both(a: bool, b: bool, c: bool) -> bool:\n"
1662
+ " return flag(a) and b and flag(c)\n\n"
1663
+ "def either(a: bool, b: bool) -> bool:\n"
1664
+ " return a or flag(b)\n\n"
1665
+ "def ordered(a: int, b: int, c: int, d: int) -> bool:\n"
1666
+ " return a < b < (value(c) // 2) < d + 1\n"
1667
+ )
1668
+ boolean_region_source=(
1669
+ "def flag(value: bool) -> bool:\n"
1670
+ " return value\n\n"
1671
+ "def run(a: bool, b: bool) -> bool:\n"
1672
+ " return a and flag(b)\n"
1673
+ )
1674
+ comparison_region_source=(
1675
+ "def value(item: int) -> int:\n"
1676
+ " return item\n\n"
1677
+ "def run(a: int, b: int, c: int) -> bool:\n"
1678
+ " return a < b < value(c)\n"
1679
+ )
1680
+ try:
1681
+ converter=PythonToCConverter()
1682
+ observation=ObservationOptions("Full",False)
1683
+ accepted=converter.convert(
1684
+ ConversionRequest.from_source(source),observation=observation
1685
+ )
1686
+ repeated=converter.convert(
1687
+ ConversionRequest.from_source(source),observation=observation
1688
+ )
1689
+ if accepted.stage_artifact is None:
1690
+ raise ValueError("conditional audit conversion omitted its final artifact")
1691
+ payload=accepted.stage_artifact.payload
1692
+ summary=accepted.conversion_summary or {}
1693
+ trace=accepted.decision_trace or {}
1694
+
1695
+ constants_valid=(
1696
+ CONDITIONAL_REGION_TABLE_ID=="conditional-region-facts"
1697
+ and CONDITIONAL_REGION_KEY_DOMAIN=="conditional-region-node-id"
1698
+ and CONDITIONAL_REGION_LOWERING_SHAPE=="flat-guarded-assignment-v1"
1699
+ and CONDITIONAL_REGION_OBLIGATIONS==expected_obligations
1700
+ and CONDITIONAL_REGION_TABLE_DEPENDENCIES==expected_dependencies
1701
+ and CONDITIONAL_REGION_PROVENANCE_EVIDENCE==expected_provenance
1702
+ )
1703
+ identity_valid=(
1704
+ accepted.status is ResultStatus.CONVERTED
1705
+ and accepted.generated_c is not None
1706
+ and accepted.stage_artifact.kind=="generated_c"
1707
+ and accepted.stage_artifact.schema_version=="0.14.3"
1708
+ and payload.get("schema_version")==GENERATED_C_SCHEMA=="generated-c/0.14.3"
1709
+ and payload.get("c_ir_schema")==C_IR_SCHEMA=="c-ir/0.14.3"
1710
+ and payload.get("rule_set_version")==DEFAULT_RULE_SET=="phase14-required-keyword-only-calls-v0.14.3"
1711
+ and payload.get("renderer_version")==DEFAULT_RENDERER=="c-renderer-v0.14.3"
1712
+ and payload.get("numeric_policy_version")==DEFAULT_NUMERIC_POLICY
1713
+ and CONVERSION_PLAN_SCHEMA=="conversion-plan/0.14.3"
1714
+ and CONDITIONAL_FACT_SCHEMA=="fact-table/0.14.1"
1715
+ and KEYWORD_CALL_FACT_SCHEMA=="fact-table/0.14.2"
1716
+ and KEYWORD_ONLY_CALL_FACT_SCHEMA=="fact-table/0.14.3"
1717
+ and CONVERSION_SUMMARY_SCHEMA=="pycforge.conversion-summary/0.14.3"
1718
+ and DECISION_TRACE_SCHEMA=="pycforge.decision-trace/0.14.3"
1719
+ and RESULT_SCHEMA_VERSION=="0.5"
1720
+ )
1721
+
1722
+ tables=[
1723
+ item for item in payload.get("fact_tables",())
1724
+ if item.get("table_id")==CONDITIONAL_REGION_TABLE_ID
1725
+ ]
1726
+ table=tables[0] if len(tables)==1 else {}
1727
+ records=list(table.get("records",()))
1728
+ facts=[record.get("value",{}) for record in records]
1729
+ table_valid=(
1730
+ len(tables)==1
1731
+ and table.get("schema_version")==CONDITIONAL_FACT_SCHEMA
1732
+ and table.get("producer_stage")=="analysis.plan"
1733
+ and table.get("key_domain")==CONDITIONAL_REGION_KEY_DOMAIN
1734
+ and table.get("completeness")=="complete"
1735
+ and tuple(table.get("invalidation_dependencies",()))==expected_dependencies
1736
+ and len(records)==3
1737
+ and len({record.get("key") for record in records})==3
1738
+ and all(
1739
+ record.get("key")==record.get("value",{}).get("region_node_id")
1740
+ and tuple(record.get("provenance",{}).get("evidence",()))==expected_provenance
1741
+ and set(record.get("value",{}).get("prerequisite_node_ids",())).issubset(
1742
+ set(record.get("provenance",{}).get("source_node_ids",()))
1743
+ )
1744
+ for record in records
1745
+ )
1746
+ )
1747
+
1748
+ def fact_valid(fact: dict[str, Any]) -> bool:
1749
+ kind=fact.get("region_kind")
1750
+ operands=list(fact.get("operand_node_ids",()))
1751
+ categories=list(fact.get("operand_categories",()))
1752
+ placements=list(fact.get("placements",()))
1753
+ prefix=1 if kind=="boolean-short-circuit" else 2 if kind=="chained-comparison" else -1
1754
+ polarity=(
1755
+ "when-result-false"
1756
+ if kind=="boolean-short-circuit" and fact.get("operator_kinds")==["Or"]
1757
+ else "when-result-true"
1758
+ )
1759
+ if (
1760
+ prefix < 0
1761
+ or not re.fullmatch(r"conditional-region-[0-9a-f]{20}",str(fact.get("region_id","")))
1762
+ or len(operands) < prefix
1763
+ or len(categories)!=len(operands)
1764
+ or len(placements)!=len(operands)
1765
+ or fact.get("evaluation_order")!=operands
1766
+ or fact.get("guarded_operand_node_ids")!=operands[prefix:]
1767
+ or fact.get("unconditional_prefix_count")!=prefix
1768
+ or fact.get("operands_evaluated_once") is not True
1769
+ or fact.get("lowering_shape")!="flat-guarded-assignment-v1"
1770
+ or fact.get("result_category")!="boolean-like"
1771
+ or fact.get("result_c_type")!="bool"
1772
+ or fact.get("allocation_model")!="none"
1773
+ or fact.get("cleanup_model")!="none"
1774
+ or fact.get("runtime_failure_channel")!="unchanged"
1775
+ or fact.get("target_contract")!="c11-portable-fixed-v1"
1776
+ or not all(
1777
+ isinstance(fact.get(key),str) and fact.get(key)
1778
+ for key in ("region_node_id","function_node_id","module_id","document_id","logical_name")
1779
+ )
1780
+ ):
1781
+ return False
1782
+ for ordinal,(operand,category,placement) in enumerate(
1783
+ zip(operands,categories,placements)
1784
+ ):
1785
+ unconditional=ordinal < prefix
1786
+ if (
1787
+ placement.get("operand_node_id")!=operand
1788
+ or placement.get("ordinal")!=ordinal
1789
+ or placement.get("category")!=category
1790
+ or placement.get("evaluation_mode")!=("unconditional" if unconditional else "guarded")
1791
+ or placement.get("guard_polarity")!=("none" if unconditional else polarity)
1792
+ or placement.get("guard_after_operand_ordinal")!=(None if unconditional else ordinal-1)
1793
+ or placement.get("requires_statement_prelude") is not bool(placement.get("prerequisite_node_ids"))
1794
+ ):
1795
+ return False
1796
+ prerequisites=list(dict.fromkeys(
1797
+ item
1798
+ for placement in placements
1799
+ for item in placement.get("prerequisite_node_ids",())
1800
+ ))
1801
+ if fact.get("prerequisite_node_ids")!=prerequisites:
1802
+ return False
1803
+ if kind=="boolean-short-circuit":
1804
+ return (
1805
+ fact.get("operator_kinds") in (["And"],["Or"])
1806
+ and all(category=="boolean-like" for category in categories)
1807
+ )
1808
+ return (
1809
+ len(fact.get("operator_kinds",()))==len(operands)-1
1810
+ and all(item in {"Eq","NotEq","Lt","LtE","Gt","GtE"} for item in fact.get("operator_kinds",()))
1811
+ and bool(categories)
1812
+ and categories[0] in {"integer-like","floating-like","boolean-like"}
1813
+ and all(category==categories[0] for category in categories)
1814
+ )
1815
+
1816
+ fact_kinds=[fact.get("region_kind") for fact in facts]
1817
+ facts_valid=(
1818
+ fact_kinds.count("boolean-short-circuit")==2
1819
+ and fact_kinds.count("chained-comparison")==1
1820
+ and all(fact_valid(fact) for fact in facts)
1821
+ )
1822
+
1823
+ plans=[
1824
+ plan for plan in payload.get("rule_plans",())
1825
+ if str(plan.get("rule_id","")).startswith("phase14.conditional.")
1826
+ ]
1827
+ plans_by_source={plan.get("source_node_id"):plan for plan in plans}
1828
+
1829
+ def plan_valid(fact: dict[str, Any]) -> bool:
1830
+ plan=plans_by_source.get(fact.get("region_node_id"),{})
1831
+ kind=fact.get("region_kind")
1832
+ rule=(
1833
+ "phase14.conditional.boolean_region"
1834
+ if kind=="boolean-short-circuit"
1835
+ else "phase14.conditional.comparison_region"
1836
+ )
1837
+ node_kind="BoolOp" if kind=="boolean-short-circuit" else "Compare"
1838
+ guarded_count=len(fact.get("guarded_operand_node_ids",()))
1839
+ expected_facts={
1840
+ f"conditional-region:{fact.get('region_node_id')}",
1841
+ f"conditional-region-kind:{kind}",
1842
+ f"conditional-unconditional-prefix:{fact.get('unconditional_prefix_count')}",
1843
+ f"conditional-guarded-operand-count:{guarded_count}",
1844
+ "conditional-lowering-shape:flat-guarded-assignment-v1",
1845
+ "conditional-target:c11-portable-fixed-v1",
1846
+ "value-category:boolean-like",
1847
+ }
1848
+ expected_explanation=[
1849
+ "selected",rule,"for",node_kind,"conditional-region",kind,
1850
+ "unconditional-prefix",str(fact.get("unconditional_prefix_count")),
1851
+ "guarded-operands",str(guarded_count),"lowered-as",
1852
+ "flat-guarded-assignment-v1",
1853
+ ]
1854
+ return (
1855
+ plan.get("rule_id")==rule
1856
+ and plan.get("rule_version")=="0.14.1"
1857
+ and plan.get("support_state")=="SupportedDirect"
1858
+ and set(plan.get("facts_used",()))==expected_facts
1859
+ and len(plan.get("facts_used",()))==len(expected_facts)
1860
+ and tuple(plan.get("semantic_obligations",()))==expected_obligations
1861
+ and tuple(plan.get("resolved_obligations",()))==expected_obligations
1862
+ and plan.get("unresolved_obligations")==[]
1863
+ and plan.get("helper_requirements")==[]
1864
+ and plan.get("explanation_tokens")==expected_explanation
1865
+ )
1866
+
1867
+ plans_valid=(
1868
+ len(plans)==3
1869
+ and len(plans_by_source)==3
1870
+ and {plan.get("rule_id") for plan in plans}=={
1871
+ "phase14.conditional.boolean_region",
1872
+ "phase14.conditional.comparison_region",
1873
+ }
1874
+ and all(plan_valid(fact) for fact in facts)
1875
+ )
1876
+
1877
+ helper_reference=FLOOR_DIV_REFERENCE.canonical
1878
+ numeric_plans=[
1879
+ plan for plan in payload.get("rule_plans",())
1880
+ if plan.get("rule_id")=="phase14.numeric.floor_arithmetic"
1881
+ ]
1882
+ helper_registry=default_helper_registry()
1883
+ helper_ownership_valid=(
1884
+ len(numeric_plans)==1
1885
+ and numeric_plans[0].get("helper_requirements")==[helper_reference]
1886
+ and all(not plan.get("helper_requirements") for plan in plans)
1887
+ and payload.get("helper_requirements")==[helper_reference]
1888
+ and [item.get("reference") for item in payload.get("helper_manifest",())]==[helper_reference]
1889
+ and payload.get("helper_registry_fingerprint")==helper_registry.fingerprint
1890
+ and helper_registry.fingerprint=="fcb719f07984f3b79e17262e90f93823a9a0139a60529f8486ab09f6c3663d98"
1891
+ )
1892
+
1893
+ c_ir=payload.get("c_ir",{})
1894
+ region_prefixes=("c-bool-region-if-","c-chain-region-if-")
1895
+ region_ifs=[
1896
+ item for item in _kind_dicts(c_ir,{"CIfStatement"})
1897
+ if str(item.get("node_id","")).startswith(region_prefixes)
1898
+ ]
1899
+
1900
+ def source_ids(value: Any) -> set[str]:
1901
+ found: set[str]=set()
1902
+ if isinstance(value,dict):
1903
+ provenance=value.get("provenance")
1904
+ if isinstance(provenance,dict):
1905
+ found.update(
1906
+ item for item in provenance.get("source_node_ids",())
1907
+ if isinstance(item,str)
1908
+ )
1909
+ for child in value.values():
1910
+ found.update(source_ids(child))
1911
+ elif isinstance(value,(list,tuple)):
1912
+ for child in value:
1913
+ found.update(source_ids(child))
1914
+ return found
1915
+
1916
+ guard_by_placement: dict[tuple[str,int],dict[str,Any]]={}
1917
+ guard_checks: list[bool]=[]
1918
+ for fact in facts:
1919
+ region_node_id=fact.get("region_node_id")
1920
+ for placement in fact.get("placements",()):
1921
+ if placement.get("evaluation_mode")!="guarded":
1922
+ continue
1923
+ operand_id=placement.get("operand_node_id")
1924
+ candidates=[
1925
+ item for item in region_ifs
1926
+ if {region_node_id,operand_id}.issubset(
1927
+ set(item.get("provenance",{}).get("source_node_ids",()))
1928
+ )
1929
+ ]
1930
+ if len(candidates)!=1:
1931
+ guard_checks.append(False)
1932
+ continue
1933
+ guard=candidates[0]
1934
+ guard_by_placement[(str(region_node_id),int(placement.get("ordinal")))]=guard
1935
+ condition=guard.get("condition",{})
1936
+ expected_false=placement.get("guard_polarity")=="when-result-false"
1937
+ polarity_ok=(
1938
+ condition.get("kind")=="CUnaryExpr"
1939
+ and condition.get("op")=="!"
1940
+ ) if expected_false else condition.get("kind")=="CIdentifierRef"
1941
+ then_block=guard.get("then_block",{})
1942
+ nested_region_ifs=[
1943
+ item for item in _kind_dicts(then_block,{"CIfStatement"})
1944
+ if str(item.get("node_id","")).startswith(region_prefixes)
1945
+ ]
1946
+ prerequisites=set(placement.get("prerequisite_node_ids",()))
1947
+ guard_checks.append(
1948
+ polarity_ok
1949
+ and guard.get("else_block") is None
1950
+ and not nested_region_ifs
1951
+ and prerequisites.issubset(source_ids(then_block))
1952
+ and bool(_kind_dicts(then_block,{"CAssignmentStatement"}))
1953
+ )
1954
+ expected_guard_count=sum(
1955
+ len(fact.get("guarded_operand_node_ids",())) for fact in facts
1956
+ )
1957
+ flat_branch_containment_valid=(
1958
+ len(region_ifs)==expected_guard_count==5
1959
+ and len(guard_checks)==expected_guard_count
1960
+ and all(guard_checks)
1961
+ )
1962
+
1963
+ accumulator_valid=True
1964
+ rolling_reuse_valid=True
1965
+ for fact in facts:
1966
+ region_node_id=str(fact.get("region_node_id"))
1967
+ guarded=[
1968
+ placement for placement in fact.get("placements",())
1969
+ if placement.get("evaluation_mode")=="guarded"
1970
+ ]
1971
+ if fact.get("region_kind")=="boolean-short-circuit":
1972
+ for placement in guarded:
1973
+ guard=guard_by_placement.get((region_node_id,placement["ordinal"]),{})
1974
+ condition=guard.get("condition",{})
1975
+ if condition.get("kind")=="CUnaryExpr":
1976
+ condition=condition.get("operand",{})
1977
+ assignments=[
1978
+ item for item in _kind_dicts(guard.get("then_block",{}),{"CAssignmentStatement"})
1979
+ if str(item.get("node_id","")).startswith("c-bool-region-assign-")
1980
+ ]
1981
+ accumulator_valid=accumulator_valid and (
1982
+ len(assignments)==1
1983
+ and condition.get("binding_id")==assignments[0].get("target",{}).get("binding_id")
1984
+ )
1985
+ else:
1986
+ pairs=[]
1987
+ for placement in guarded:
1988
+ guard=guard_by_placement.get((region_node_id,placement["ordinal"]),{})
1989
+ assignments=[
1990
+ item for item in _kind_dicts(guard.get("then_block",{}),{"CAssignmentStatement"})
1991
+ if str(item.get("node_id","")).startswith("c-chain-region-result-assign-")
1992
+ ]
1993
+ if len(assignments)!=1:
1994
+ rolling_reuse_valid=False
1995
+ continue
1996
+ expression=assignments[0].get("value",{})
1997
+ pairs.append((
1998
+ expression.get("left",{}).get("binding_id"),
1999
+ expression.get("right",{}).get("binding_id"),
2000
+ ))
2001
+ rolling_reuse_valid=rolling_reuse_valid and all(
2002
+ pairs[index][1]==pairs[index+1][0]
2003
+ for index in range(len(pairs)-1)
2004
+ )
2005
+
2006
+ c_ir_node_ids=[]
2007
+ def collect_c_ir_node_ids(value: Any) -> None:
2008
+ if isinstance(value,dict):
2009
+ if isinstance(value.get("node_id"),str):
2010
+ c_ir_node_ids.append(value["node_id"])
2011
+ for child in value.values():
2012
+ collect_c_ir_node_ids(child)
2013
+ elif isinstance(value,(list,tuple)):
2014
+ for child in value:
2015
+ collect_c_ir_node_ids(child)
2016
+ collect_c_ir_node_ids(c_ir)
2017
+ c_ir_ids_unique=len(c_ir_node_ids)==len(set(c_ir_node_ids))
2018
+ conformance=validate_c_text(accepted.generated_c or "")
2019
+
2020
+ def canonical_dicts(values: Any) -> list[str]:
2021
+ return sorted(json.dumps(item,sort_keys=True,separators=(",",":")) for item in values)
2022
+
2023
+ trace_plans=[
2024
+ plan for plan in trace.get("rule_decisions",())
2025
+ if str(plan.get("rule_id","")).startswith("phase14.conditional.")
2026
+ ]
2027
+ observer_valid=(
2028
+ summary.get("schema_version")==CONVERSION_SUMMARY_SCHEMA
2029
+ and summary.get("rule_set_version")==DEFAULT_RULE_SET
2030
+ and summary.get("renderer_version")==DEFAULT_RENDERER
2031
+ and canonical_dicts(summary.get("conditional_regions",()))==canonical_dicts(facts)
2032
+ and trace.get("schema_version")==DECISION_TRACE_SCHEMA
2033
+ and trace.get("trace_level")=="Full"
2034
+ and trace.get("completeness")=="complete"
2035
+ and trace.get("truncated") is False
2036
+ and trace.get("observer_failed") is False
2037
+ and canonical_dicts(trace_plans)==canonical_dicts(plans)
2038
+ and bool(trace.get("source_output_mappings"))
2039
+ and bool(trace.get("stage_summaries"))
2040
+ )
2041
+
2042
+ deterministic=(
2043
+ result_to_json(accepted)==result_to_json(repeated)
2044
+ and accepted.request_fingerprint==repeated.request_fingerprint
2045
+ and accepted.output_fingerprint==repeated.output_fingerprint
2046
+ and accepted.stage_artifact.artifact_fingerprint==repeated.stage_artifact.artifact_fingerprint
2047
+ )
2048
+ fresh_code=(
2049
+ "import hashlib,json; "
2050
+ "from pycforge import ConversionRequest,PythonToCConverter; "
2051
+ "from pycforge.converter.core.request import ObservationOptions; "
2052
+ "from pycforge.converter.core.serialization import result_to_json; "
2053
+ f"r=PythonToCConverter().convert(ConversionRequest.from_source({source!r}),"
2054
+ "observation=ObservationOptions('Full',False)); "
2055
+ "print(json.dumps({'serialized_sha256':hashlib.sha256(result_to_json(r).encode()).hexdigest(),"
2056
+ "'generated_sha256':hashlib.sha256((r.generated_c or '').encode()).hexdigest(),"
2057
+ "'request_fingerprint':None if r.request_fingerprint is None else r.request_fingerprint.value,"
2058
+ "'output_fingerprint':None if r.output_fingerprint is None else r.output_fingerprint.value,"
2059
+ "'artifact_fingerprint':None if r.stage_artifact is None else r.stage_artifact.artifact_fingerprint.value},sort_keys=True))"
2060
+ )
2061
+ env={**__import__("os").environ,"PYTHONPATH":str(root)}
2062
+ fresh=json.loads(subprocess.check_output(
2063
+ [sys.executable,"-c",fresh_code],cwd=root,env=env,text=True
2064
+ ))
2065
+ fresh_process_valid=fresh=={
2066
+ "serialized_sha256":hashlib.sha256(result_to_json(accepted).encode()).hexdigest(),
2067
+ "generated_sha256":hashlib.sha256((accepted.generated_c or "").encode()).hexdigest(),
2068
+ "request_fingerprint":accepted.request_fingerprint.value if accepted.request_fingerprint else None,
2069
+ "output_fingerprint":accepted.output_fingerprint.value if accepted.output_fingerprint else None,
2070
+ "artifact_fingerprint":accepted.stage_artifact.artifact_fingerprint.value,
2071
+ }
2072
+
2073
+ historical_witness="def run() -> int:\n return 1\n"
2074
+ historical=converter.convert(
2075
+ ConversionRequest.from_source(
2076
+ historical_witness,
2077
+ rule_set_version=PHASE14A_RULE_SET,
2078
+ renderer_version=PHASE14A_RENDERER,
2079
+ ),
2080
+ observation=observation,
2081
+ )
2082
+ active_no_region=converter.convert(
2083
+ ConversionRequest.from_source(historical_witness),observation=observation
2084
+ )
2085
+ historical_payload=(
2086
+ historical.stage_artifact.payload
2087
+ if historical.stage_artifact is not None else {}
2088
+ )
2089
+ historical_exact=(
2090
+ historical.status is ResultStatus.CONVERTED
2091
+ and historical.stage_artifact is not None
2092
+ and historical.stage_artifact.schema_version=="0.14"
2093
+ and historical_payload.get("schema_version")=="generated-c/0.14"
2094
+ and historical_payload.get("c_ir_schema")=="c-ir/0.14"
2095
+ and historical_payload.get("rule_set_version")==PHASE14A_RULE_SET=="phase14-bounded-numeric-v0.14"
2096
+ and historical_payload.get("renderer_version")==PHASE14A_RENDERER=="c-renderer-v0.14"
2097
+ and historical.conversion_summary is not None
2098
+ and historical.conversion_summary.get("schema_version")=="pycforge.conversion-summary/0.14"
2099
+ and historical.decision_trace is not None
2100
+ and historical.decision_trace.get("schema_version")=="pycforge.decision-trace/0.14"
2101
+ and CONDITIONAL_REGION_TABLE_ID not in {
2102
+ item.get("table_id") for item in historical_payload.get("fact_tables",())
2103
+ }
2104
+ and historical.request_fingerprint is not None
2105
+ and historical.request_fingerprint.value=="f3bdc058becb0854692235850037797872afc00a18a132c88b7bb2950a2d4360"
2106
+ and hashlib.sha256((historical.generated_c or "").encode()).hexdigest()=="0ba73812646f4113b99bbe72661d7a7eef129901439422cc2d47bbc6ddaa64c5"
2107
+ and historical.output_fingerprint is not None
2108
+ and historical.output_fingerprint.value=="27f2abb910f41170714de587158e2eacc66ef81d8535b28a754f5f960e9b6f0d"
2109
+ )
2110
+ active_no_region_compatible=(
2111
+ active_no_region.status is ResultStatus.CONVERTED
2112
+ and active_no_region.stage_artifact is not None
2113
+ and active_no_region.stage_artifact.schema_version=="0.14.3"
2114
+ and active_no_region.generated_c==historical.generated_c
2115
+ and active_no_region.output_fingerprint==historical.output_fingerprint
2116
+ and len([
2117
+ table for table in active_no_region.stage_artifact.payload.get("fact_tables",())
2118
+ if table.get("table_id")==CONDITIONAL_REGION_TABLE_ID
2119
+ and table.get("records")==[]
2120
+ ])==1
2121
+ )
2122
+
2123
+ historical_cases={
2124
+ "PYC2950":boolean_region_source,
2125
+ "PYC2951":comparison_region_source,
2126
+ }
2127
+ historical_rejections={}
2128
+ historical_rejections_valid=True
2129
+ for code,text in historical_cases.items():
2130
+ result=converter.convert(ConversionRequest.from_source(
2131
+ text,
2132
+ rule_set_version=PHASE14A_RULE_SET,
2133
+ renderer_version=PHASE14A_RENDERER,
2134
+ ))
2135
+ historical_rejections[code]=[item.code for item in result.diagnostics]
2136
+ result_payload={} if result.stage_artifact is None else result.stage_artifact.payload
2137
+ historical_rejections_valid=historical_rejections_valid and (
2138
+ result.status is ResultStatus.REJECTED
2139
+ and historical_rejections[code]==[code]
2140
+ and result.generated_c is None
2141
+ and result.output_fingerprint is None
2142
+ and result.stage_artifact is not None
2143
+ and result.stage_artifact.kind=="conversion_plan"
2144
+ and result.stage_artifact.schema_version=="0.14"
2145
+ and CONDITIONAL_REGION_TABLE_ID not in {
2146
+ item.get("table_id") for item in result_payload.get("fact_tables",())
2147
+ }
2148
+ )
2149
+ keyword_source=(
2150
+ "def flag(value: bool) -> bool:\n"
2151
+ " return value\n\n"
2152
+ "def run(a: bool, b: bool) -> bool:\n"
2153
+ " return a and flag(value=b)\n"
2154
+ )
2155
+ keyword_rejection=converter.convert(ConversionRequest.from_source(
2156
+ keyword_source,
2157
+ rule_set_version=PHASE14B_RULE_SET,
2158
+ renderer_version=PHASE14B_RENDERER,
2159
+ ))
2160
+ root_cause_precedence_valid=(
2161
+ keyword_rejection.status is ResultStatus.REJECTED
2162
+ and [item.code for item in keyword_rejection.diagnostics]==["PYC2910"]
2163
+ and keyword_rejection.generated_c is None
2164
+ and keyword_rejection.output_fingerprint is None
2165
+ )
2166
+
2167
+ passed=all((
2168
+ constants_valid,identity_valid,table_valid,facts_valid,plans_valid,
2169
+ helper_ownership_valid,flat_branch_containment_valid,accumulator_valid,
2170
+ rolling_reuse_valid,c_ir_ids_unique,conformance.accepted,observer_valid,
2171
+ deterministic,fresh_process_valid,historical_exact,
2172
+ active_no_region_compatible,historical_rejections_valid,
2173
+ root_cause_precedence_valid,
2174
+ ))
2175
+ return {
2176
+ "audit":"conditional",
2177
+ "passed":passed,
2178
+ "active_contracts":{
2179
+ "rule_set":DEFAULT_RULE_SET,
2180
+ "renderer":DEFAULT_RENDERER,
2181
+ "conditional_facts":CONDITIONAL_FACT_SCHEMA,
2182
+ "keyword_call_facts":KEYWORD_CALL_FACT_SCHEMA,
2183
+ "keyword_only_call_facts":KEYWORD_ONLY_CALL_FACT_SCHEMA,
2184
+ "conversion_plan":CONVERSION_PLAN_SCHEMA,
2185
+ "c_ir":C_IR_SCHEMA,
2186
+ "generated_c":GENERATED_C_SCHEMA,
2187
+ "conversion_summary":CONVERSION_SUMMARY_SCHEMA,
2188
+ "decision_trace":DECISION_TRACE_SCHEMA,
2189
+ "result_serialization":RESULT_SCHEMA_VERSION,
2190
+ },
2191
+ "active_contract_identities_valid":identity_valid,
2192
+ "contract_constants_valid":constants_valid,
2193
+ "accepted_identity_valid":identity_valid,
2194
+ "conditional_fact_count":len(facts),
2195
+ "conditional_fact_kinds":sorted(fact_kinds),
2196
+ "conditional_fact_table_valid":table_valid,
2197
+ "conditional_facts_valid":facts_valid,
2198
+ "conditional_rule_plan_count":len(plans),
2199
+ "conditional_rule_plans_valid":plans_valid,
2200
+ "region_owned_helper_requirements_empty":all(
2201
+ not plan.get("helper_requirements") for plan in plans
2202
+ ),
2203
+ "composed_helper_references":[
2204
+ item.get("reference") for item in payload.get("helper_manifest",())
2205
+ ],
2206
+ "helper_ownership_valid":helper_ownership_valid,
2207
+ "expected_region_guard_count":expected_guard_count,
2208
+ "c_ir_region_guard_count":len(region_ifs),
2209
+ "flat_branch_containment_valid":flat_branch_containment_valid,
2210
+ "boolean_accumulator_reuse_valid":accumulator_valid,
2211
+ "chained_middle_reuse_valid":rolling_reuse_valid,
2212
+ "c_ir_node_ids_unique":c_ir_ids_unique,
2213
+ "generated_c_conformance":conformance.accepted,
2214
+ "observer_evidence_valid":observer_valid,
2215
+ "deterministic":deterministic,
2216
+ "fresh_process_deterministic":fresh_process_valid,
2217
+ "generated_c_sha256":hashlib.sha256((accepted.generated_c or "").encode()).hexdigest(),
2218
+ "phase14a_exact_compatibility":historical_exact,
2219
+ "active_no_region_output_compatible":active_no_region_compatible,
2220
+ "historical_placement_rejections":historical_rejections,
2221
+ "historical_placement_rejections_valid":historical_rejections_valid,
2222
+ "specific_root_cause_precedence_valid":root_cause_precedence_valid,
2223
+ "phase14b_keyword_rejection_precedence_valid":root_cause_precedence_valid,
2224
+ "python_subprocess_used_for_determinism_only":True,
2225
+ "c_toolchain_invoked":False,
2226
+ "generated_c_compiled_or_executed":False,
2227
+ }
2228
+ except Exception as exc:
2229
+ return {
2230
+ "audit":"conditional",
2231
+ "passed":False,
2232
+ "error":f"{type(exc).__name__}: {exc}",
2233
+ "c_toolchain_invoked":False,
2234
+ "generated_c_compiled_or_executed":False,
2235
+ }
2236
+
2237
+ def audit_determinism(root: Path, source: str="def add(a: int, b: int) -> int:\n return a + b\n\ndef twice(x: int) -> int:\n return add(x, x)\n") -> dict[str, Any]:
2238
+ code=("from pycforge import PythonToCConverter,ConversionRequest; "
2239
+ "from pycforge.converter.core.serialization import result_to_json; "
2240
+ f"print(result_to_json(PythonToCConverter().convert(ConversionRequest.from_source({source!r}))),end='')")
2241
+ env={**__import__('os').environ,"PYTHONPATH":str(root)}
2242
+ a=subprocess.check_output([sys.executable,"-c",code],cwd=root,env=env)
2243
+ b=subprocess.check_output([sys.executable,"-c",code],cwd=root,env=env)
2244
+ return {"audit":"determinism","passed":a==b,"sha256":hashlib.sha256(a).hexdigest()}