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,661 @@
1
+ """Authenticated laboratory audit for the bounded Phase 14C keyword slice."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+
13
+ def _kind_dicts(value: Any, kind: str) -> list[dict[str, Any]]:
14
+ found: list[dict[str, Any]] = []
15
+ if isinstance(value, dict):
16
+ if value.get("kind") == kind:
17
+ found.append(value)
18
+ for child in value.values():
19
+ found.extend(_kind_dicts(child, kind))
20
+ elif isinstance(value, (list, tuple)):
21
+ for child in value:
22
+ found.extend(_kind_dicts(child, kind))
23
+ return found
24
+
25
+
26
+ def _table(payload: dict[str, Any], table_id: str) -> dict[str, Any]:
27
+ return next(
28
+ item
29
+ for item in payload.get("fact_tables", ())
30
+ if item.get("table_id") == table_id
31
+ )
32
+
33
+
34
+ def _facts(payload: dict[str, Any], table_id: str) -> list[dict[str, Any]]:
35
+ return [
36
+ item.get("value", {})
37
+ for item in _table(payload, table_id).get("records", ())
38
+ ]
39
+
40
+
41
+ def _lowering_proof(
42
+ payload: dict[str, Any], fact_values: list[dict[str, Any]]
43
+ ) -> tuple[bool, list[dict[str, Any]]]:
44
+ """Reconstruct source staging and formal-reference order from C IR."""
45
+
46
+ c_ir = payload.get("c_ir", {})
47
+ declarations = _kind_dicts(c_ir, "CVariableDeclaration")
48
+ calls = _kind_dicts(c_ir, "CCallExpr")
49
+ evidence: list[dict[str, Any]] = []
50
+ valid = True
51
+ for fact in fact_values:
52
+ call_id = fact.get("call_node_id")
53
+ source_ids = list(fact.get("source_argument_node_ids", ()))
54
+ staged = [
55
+ item
56
+ for item in declarations
57
+ if str(item.get("node_id", "")).startswith("c-arg-temp-")
58
+ and call_id in item.get("provenance", {}).get("source_node_ids", ())
59
+ ]
60
+ staged_source: list[str | None] = []
61
+ staged_bindings: list[str | None] = []
62
+ for declaration in staged:
63
+ matches = [
64
+ source_id
65
+ for source_id in source_ids
66
+ if source_id
67
+ in declaration.get("provenance", {}).get("source_node_ids", ())
68
+ ]
69
+ staged_source.append(matches[0] if len(matches) == 1 else None)
70
+ staged_bindings.append(
71
+ declaration.get("identifier", {}).get("binding_id")
72
+ )
73
+ target_calls = [
74
+ item
75
+ for item in calls
76
+ if call_id in item.get("provenance", {}).get("source_node_ids", ())
77
+ ]
78
+ target_call = target_calls[0] if len(target_calls) == 1 else {}
79
+ actual_formal = [
80
+ item.get("binding_id") for item in target_call.get("arguments", ())
81
+ ]
82
+ permutation = list(fact.get("parameter_to_source_ordinals", ()))
83
+ permutation_valid = (
84
+ len(permutation) == len(staged_bindings)
85
+ and all(
86
+ isinstance(index, int) and 0 <= index < len(staged_bindings)
87
+ for index in permutation
88
+ )
89
+ )
90
+ expected_formal = (
91
+ [staged_bindings[index] for index in permutation]
92
+ if permutation_valid
93
+ else []
94
+ )
95
+ item_valid = (
96
+ staged_source == source_ids
97
+ and len(staged_bindings) == len(source_ids)
98
+ and len(set(staged_bindings)) == len(staged_bindings)
99
+ and len(target_calls) == 1
100
+ and actual_formal == expected_formal
101
+ and all(
102
+ argument.get("kind") == "CIdentifierRef"
103
+ for argument in target_call.get("arguments", ())
104
+ )
105
+ )
106
+ valid = valid and item_valid
107
+ evidence.append(
108
+ {
109
+ "call_node_id": call_id,
110
+ "source_order": staged_source,
111
+ "parameter_to_source_ordinals": permutation,
112
+ "formal_reference_binding_ids": actual_formal,
113
+ "valid": item_valid,
114
+ }
115
+ )
116
+ return valid, evidence
117
+
118
+
119
+ def audit_keyword(root: Path) -> dict[str, Any]:
120
+ """Independently gate direct keyword binding without invoking C tools."""
121
+
122
+ from pycforge import (
123
+ ConversionRequest,
124
+ PythonToCConverter,
125
+ ResultStatus,
126
+ SourceBundle,
127
+ SourceDocumentInput,
128
+ )
129
+ from pycforge.converter.c_output import validate_c_text
130
+ from pycforge.converter.contracts.configuration import (
131
+ PHASE14B_RENDERER,
132
+ PHASE14B_RULE_SET,
133
+ PHASE14C_RENDERER,
134
+ PHASE14C_RULE_SET,
135
+ )
136
+ from pycforge.converter.contracts.versions import (
137
+ KEYWORD_CALL_FACT_SCHEMA,
138
+ PHASE14C_C_IR_SCHEMA,
139
+ PHASE14C_CONVERSION_PLAN_SCHEMA,
140
+ PHASE14C_CONVERSION_SUMMARY_SCHEMA,
141
+ PHASE14C_DECISION_TRACE_SCHEMA,
142
+ PHASE14C_GENERATED_C_SCHEMA,
143
+ RESULT_SCHEMA_VERSION,
144
+ )
145
+ from pycforge.converter.core.request import ObservationOptions
146
+ from pycforge.converter.core.serialization import result_to_json
147
+ from pycforge.converter.keyword_calls import (
148
+ KEYWORD_CALL_KEY_DOMAIN,
149
+ KEYWORD_CALL_LOWERING_SHAPE,
150
+ KEYWORD_CALL_OBLIGATIONS,
151
+ KEYWORD_CALL_PROVENANCE_EVIDENCE,
152
+ KEYWORD_CALL_RULE_ID,
153
+ KEYWORD_CALL_RULE_VERSION,
154
+ KEYWORD_CALL_TABLE_DEPENDENCIES,
155
+ KEYWORD_CALL_TABLE_ID,
156
+ validate_keyword_call_binding_facts,
157
+ )
158
+ from pycforge.converter.support_templates import default_helper_registry
159
+
160
+ expected_obligations = (
161
+ "direct-source-target-resolved-once",
162
+ "explicit-keywords-only-no-unpacking",
163
+ "positional-prefix-bound-in-order",
164
+ "keyword-names-bound-to-positional-or-keyword-parameters",
165
+ "parameter-coverage-exact",
166
+ "argument-representations-compatible-after-binding",
167
+ "source-arguments-evaluated-left-to-right-once",
168
+ "argument-temporaries-reordered-only-after-evaluation",
169
+ "c-call-arguments-in-formal-order",
170
+ "parameter-ownership-boundary-explicit",
171
+ "runtime-binding-failure-absent",
172
+ "allocation-and-cleanup-absent",
173
+ "structured-c-ir-only",
174
+ "source-provenance-anchored",
175
+ "cancellation-safe-points-honored",
176
+ "target-contract-exact",
177
+ )
178
+ expected_dependencies = (
179
+ "binding-facts",
180
+ "function-signature-facts",
181
+ "value-category-facts",
182
+ "call-target-facts",
183
+ "evaluation-order-facts",
184
+ )
185
+ expected_provenance = (
186
+ "direct-source-target",
187
+ "exact-explicit-keyword-names",
188
+ "complete-parameter-coverage",
189
+ "source-order-evaluation",
190
+ "formal-order-reference-permutation",
191
+ "single-evaluation",
192
+ )
193
+ source = (
194
+ "def mark_int(value: int) -> int:\n return value\n\n"
195
+ "def mark_bool(value: bool) -> bool:\n return value\n\n"
196
+ "def mark_float(value: float) -> float:\n return value\n\n"
197
+ "def choose(left: int, flag: bool, ratio: float) -> int:\n"
198
+ " return left\n\n"
199
+ "def direct(x: int, y: bool, z: float) -> int:\n"
200
+ " return choose(left=x, flag=y, ratio=z)\n\n"
201
+ "def reordered(x: int, y: bool, z: float) -> int:\n"
202
+ " return choose(ratio=mark_float(z), left=mark_int(x), flag=mark_bool(y))\n"
203
+ )
204
+ cross_primary = (
205
+ "from lib import choose\n\n"
206
+ "def run(value: int, flag: bool, ratio: float) -> int:\n"
207
+ " return choose(ratio=ratio, flag=flag, left=value)\n"
208
+ )
209
+ cross_companion = (
210
+ "def choose(left: int, flag: bool, ratio: float) -> int:\n"
211
+ " return left\n"
212
+ )
213
+ sink = "def sink(value: int, flag: bool) -> int:\n return value\n\n"
214
+ record = (
215
+ "class Sample:\n count: int\n"
216
+ " def __init__(self, count: int) -> None:\n"
217
+ " self.count = count\n\n"
218
+ "def run(value: int) -> int:\n"
219
+ " sample = Sample(count=value)\n return value\n"
220
+ )
221
+ rejections = {
222
+ "star-positional": ("PYC2910", "def sink(value: int) -> int:\n return value\n\ndef run(value: int) -> int:\n return sink(*value)\n"),
223
+ "star-keyword": ("PYC2910", "def sink(value: int) -> int:\n return value\n\ndef run(value: int) -> int:\n return sink(**value)\n"),
224
+ "positional-only-name": ("PYC2912", "def sink(value: int, /, flag: bool) -> int:\n return value\n\ndef run(value: int, flag: bool) -> int:\n return sink(value=value, flag=flag)\n"),
225
+ "unknown-name": ("PYC2912", sink + "def run(value: int, flag: bool) -> int:\n return sink(missing=value, flag=flag)\n"),
226
+ "positional-keyword-collision": ("PYC2912", sink + "def run(value: int, flag: bool) -> int:\n return sink(value, value=value, flag=flag)\n"),
227
+ "duplicate-keyword": ("PYC2912", sink + "def run(value: int, flag: bool) -> int:\n return sink(value=value, flag=flag, value=value)\n"),
228
+ "missing-parameter": ("PYC2904", sink + "def run(value: int) -> int:\n return sink(value=value)\n"),
229
+ "excess-positional": ("PYC2904", sink + "def run(value: int, flag: bool) -> int:\n return sink(value, flag, value)\n"),
230
+ "mapped-category": ("PYC2905", sink + "def run(value: int, flag: bool) -> int:\n return sink(flag=value, value=flag)\n"),
231
+ "default-target": ("PYC2911", "def sink(value: int, flag: bool = True) -> int:\n return value\n\ndef run(value: int, flag: bool) -> int:\n return sink(flag=flag, value=value)\n"),
232
+ "keyword-only-target": ("PYC2911", "def sink(value: int, *, flag: bool) -> int:\n return value\n\ndef run(value: int, flag: bool) -> int:\n return sink(flag=flag, value=value)\n"),
233
+ "variadic-target": ("PYC2911", "def sink(value: int, *rest: int) -> int:\n return value\n\ndef run(value: int) -> int:\n return sink(value=value)\n"),
234
+ "range-keyword": ("PYC2842", "def run() -> int:\n total = 0\n for item in range(stop=3):\n total = total + item\n return total\n"),
235
+ "record-constructor-keyword": ("PYC3605", record),
236
+ "dynamic-target": ("PYC2901", "def run(value: int) -> int:\n return missing(value=value)\n"),
237
+ "recursive-target": ("PYC2920", "def run(value: int) -> int:\n return run(value=value)\n"),
238
+ }
239
+
240
+ try:
241
+ converter = PythonToCConverter()
242
+ observation = ObservationOptions("Full", False)
243
+ def phase14c_request(text: str) -> ConversionRequest:
244
+ return ConversionRequest.from_source(
245
+ text,
246
+ rule_set_version=PHASE14C_RULE_SET,
247
+ renderer_version=PHASE14C_RENDERER,
248
+ )
249
+ accepted = converter.convert(
250
+ phase14c_request(source), observation=observation
251
+ )
252
+ repeated = converter.convert(
253
+ phase14c_request(source), observation=observation
254
+ )
255
+ if accepted.stage_artifact is None:
256
+ raise ValueError("keyword conversion omitted its final artifact")
257
+ payload = accepted.stage_artifact.payload
258
+ summary = accepted.conversion_summary or {}
259
+ trace = accepted.decision_trace or {}
260
+ active_contracts = {
261
+ "rule_set": PHASE14C_RULE_SET,
262
+ "renderer": PHASE14C_RENDERER,
263
+ "keyword_call_facts": KEYWORD_CALL_FACT_SCHEMA,
264
+ "conversion_plan": PHASE14C_CONVERSION_PLAN_SCHEMA,
265
+ "c_ir": PHASE14C_C_IR_SCHEMA,
266
+ "generated_c": PHASE14C_GENERATED_C_SCHEMA,
267
+ "conversion_summary": PHASE14C_CONVERSION_SUMMARY_SCHEMA,
268
+ "decision_trace": PHASE14C_DECISION_TRACE_SCHEMA,
269
+ "result_serialization": RESULT_SCHEMA_VERSION,
270
+ }
271
+ active_contracts_valid = active_contracts == {
272
+ "rule_set": "phase14-direct-keyword-calls-v0.14.2",
273
+ "renderer": "c-renderer-v0.14.2",
274
+ "keyword_call_facts": "fact-table/0.14.2",
275
+ "conversion_plan": "conversion-plan/0.14.2",
276
+ "c_ir": "c-ir/0.14.2",
277
+ "generated_c": "generated-c/0.14.2",
278
+ "conversion_summary": "pycforge.conversion-summary/0.14.2",
279
+ "decision_trace": "pycforge.decision-trace/0.14.2",
280
+ "result_serialization": "0.5",
281
+ }
282
+ identity_valid = (
283
+ active_contracts_valid
284
+ and accepted.status is ResultStatus.CONVERTED
285
+ and accepted.generated_c is not None
286
+ and accepted.stage_artifact.kind == "generated_c"
287
+ and accepted.stage_artifact.schema_version == "0.14.2"
288
+ and payload.get("schema_version") == PHASE14C_GENERATED_C_SCHEMA
289
+ and payload.get("c_ir_schema") == PHASE14C_C_IR_SCHEMA
290
+ and payload.get("c_ir", {}).get("schema_version") == PHASE14C_C_IR_SCHEMA
291
+ and payload.get("rule_set_version") == PHASE14C_RULE_SET
292
+ and payload.get("renderer_version") == PHASE14C_RENDERER
293
+ and summary.get("schema_version") == PHASE14C_CONVERSION_SUMMARY_SCHEMA
294
+ and trace.get("schema_version") == PHASE14C_DECISION_TRACE_SCHEMA
295
+ )
296
+ constants_valid = (
297
+ KEYWORD_CALL_TABLE_ID == "keyword-call-binding-facts"
298
+ and KEYWORD_CALL_KEY_DOMAIN == "keyword-call-node-id"
299
+ and KEYWORD_CALL_LOWERING_SHAPE
300
+ == "source-order-temporaries-formal-order-references-v1"
301
+ and KEYWORD_CALL_RULE_ID == "phase14.keyword_call.exact_binding"
302
+ and KEYWORD_CALL_RULE_VERSION == "0.14.2"
303
+ and KEYWORD_CALL_OBLIGATIONS == expected_obligations
304
+ and KEYWORD_CALL_TABLE_DEPENDENCIES == expected_dependencies
305
+ and KEYWORD_CALL_PROVENANCE_EVIDENCE == expected_provenance
306
+ )
307
+ fact_table = _table(payload, KEYWORD_CALL_TABLE_ID)
308
+ records = list(fact_table.get("records", ()))
309
+ fact_values = _facts(payload, KEYWORD_CALL_TABLE_ID)
310
+ table_valid = (
311
+ fact_table.get("schema_version") == KEYWORD_CALL_FACT_SCHEMA
312
+ and fact_table.get("producer_stage") == "analysis.plan"
313
+ and fact_table.get("key_domain") == KEYWORD_CALL_KEY_DOMAIN
314
+ and fact_table.get("completeness") == "complete"
315
+ and tuple(fact_table.get("invalidation_dependencies", ()))
316
+ == expected_dependencies
317
+ and len(records) == 2
318
+ and [item.get("key") for item in records]
319
+ == sorted(item.get("key") for item in records)
320
+ and len({item.get("key") for item in records}) == 2
321
+ and all(
322
+ item.get("key") == item.get("value", {}).get("call_node_id")
323
+ and tuple(item.get("provenance", {}).get("evidence", ()))
324
+ == expected_provenance
325
+ for item in records
326
+ )
327
+ )
328
+ independent_valid, independent_reason = validate_keyword_call_binding_facts(
329
+ payload
330
+ )
331
+ direct = next(
332
+ (item for item in fact_values if item.get("keyword_names") == ["left", "flag", "ratio"]),
333
+ {},
334
+ )
335
+ reordered = next(
336
+ (item for item in fact_values if item.get("keyword_names") == ["ratio", "left", "flag"]),
337
+ {},
338
+ )
339
+ facts_valid = (
340
+ direct.get("source_to_parameter_ordinals") == [0, 1, 2]
341
+ and direct.get("parameter_to_source_ordinals") == [0, 1, 2]
342
+ and reordered.get("source_to_parameter_ordinals") == [2, 0, 1]
343
+ and reordered.get("parameter_to_source_ordinals") == [1, 2, 0]
344
+ and all(
345
+ fact.get("target_name") == "choose"
346
+ and fact.get("parameter_names") == ["left", "flag", "ratio"]
347
+ and fact.get("evaluation_order")
348
+ == fact.get("source_argument_node_ids")
349
+ and fact.get("arguments_evaluated_once") is True
350
+ and fact.get("parameter_coverage_exact") is True
351
+ and fact.get("lowering_shape") == KEYWORD_CALL_LOWERING_SHAPE
352
+ and fact.get("allocation_model") == "none"
353
+ and fact.get("cleanup_model") == "none"
354
+ and fact.get("runtime_binding_failure") == "proved-absent"
355
+ and fact.get("supported") is True
356
+ and fact.get("diagnostic_code") is None
357
+ for fact in fact_values
358
+ )
359
+ )
360
+ plans = [
361
+ item
362
+ for item in payload.get("rule_plans", ())
363
+ if item.get("rule_id") == KEYWORD_CALL_RULE_ID
364
+ ]
365
+ plans_valid = (
366
+ len(plans) == 2
367
+ and {item.get("source_node_id") for item in plans}
368
+ == {item.get("call_node_id") for item in fact_values}
369
+ and all(
370
+ item.get("rule_version") == KEYWORD_CALL_RULE_VERSION
371
+ and item.get("support_state") == "SupportedDirect"
372
+ and item.get("helper_requirements") == []
373
+ and item.get("unresolved_obligations") == []
374
+ and item.get("resolved_obligations")
375
+ == item.get("semantic_obligations")
376
+ for item in plans
377
+ )
378
+ )
379
+ lowering_valid, lowering_evidence = _lowering_proof(payload, fact_values)
380
+ observer_valid = (
381
+ summary.get("rule_set_version") == PHASE14C_RULE_SET
382
+ and summary.get("renderer_version") == PHASE14C_RENDERER
383
+ and list(summary.get("keyword_calls", ())) == fact_values
384
+ and trace.get("trace_level") == "Full"
385
+ and trace.get("completeness") == "complete"
386
+ and trace.get("truncated") is False
387
+ and trace.get("observer_failed") is False
388
+ and [
389
+ item
390
+ for item in trace.get("rule_decisions", ())
391
+ if item.get("rule_id") == KEYWORD_CALL_RULE_ID
392
+ ]
393
+ == plans
394
+ and all(
395
+ any(
396
+ mapping.get("rule_plan_id") == plan.get("plan_id")
397
+ for mapping in payload.get("source_output_mappings", ())
398
+ )
399
+ for plan in plans
400
+ )
401
+ )
402
+ helper_registry = default_helper_registry()
403
+ helper_valid = (
404
+ not payload.get("helper_manifest")
405
+ and not payload.get("helper_requirements")
406
+ and helper_registry.fingerprint
407
+ == "fcb719f07984f3b79e17262e90f93823a9a0139a60529f8486ab09f6c3663d98"
408
+ )
409
+ conformance = validate_c_text(accepted.generated_c or "")
410
+ deterministic = (
411
+ result_to_json(accepted) == result_to_json(repeated)
412
+ and accepted.request_fingerprint == repeated.request_fingerprint
413
+ and accepted.output_fingerprint == repeated.output_fingerprint
414
+ and accepted.stage_artifact.artifact_fingerprint
415
+ == repeated.stage_artifact.artifact_fingerprint
416
+ )
417
+ fresh_code = (
418
+ "import hashlib,json; from pycforge import ConversionRequest,PythonToCConverter; "
419
+ "from pycforge.converter.contracts.configuration import PHASE14C_RENDERER,PHASE14C_RULE_SET; "
420
+ "from pycforge.converter.core.request import ObservationOptions; "
421
+ "from pycforge.converter.core.serialization import result_to_json; "
422
+ f"s={source!r}; "
423
+ "r=PythonToCConverter().convert(ConversionRequest.from_source(s,rule_set_version=PHASE14C_RULE_SET,renderer_version=PHASE14C_RENDERER),observation=ObservationOptions('Full',False)); "
424
+ "print(json.dumps({'serialized_sha256':hashlib.sha256(result_to_json(r).encode()).hexdigest(),"
425
+ "'generated_sha256':hashlib.sha256((r.generated_c or '').encode()).hexdigest(),"
426
+ "'artifact_fingerprint':r.stage_artifact.artifact_fingerprint.value},sort_keys=True))"
427
+ )
428
+ env = {**__import__("os").environ, "PYTHONPATH": str(root)}
429
+ fresh = json.loads(
430
+ subprocess.check_output(
431
+ [sys.executable, "-c", fresh_code], cwd=root, env=env, text=True
432
+ )
433
+ )
434
+ fresh_expected = {
435
+ "serialized_sha256": hashlib.sha256(
436
+ result_to_json(accepted).encode()
437
+ ).hexdigest(),
438
+ "generated_sha256": hashlib.sha256(
439
+ (accepted.generated_c or "").encode()
440
+ ).hexdigest(),
441
+ "artifact_fingerprint": accepted.stage_artifact.artifact_fingerprint.value,
442
+ }
443
+ fresh_process_valid = fresh == fresh_expected
444
+
445
+ cross_request = ConversionRequest(
446
+ SourceBundle(
447
+ SourceDocumentInput("app.py", cross_primary, "app"),
448
+ (SourceDocumentInput("lib.py", cross_companion, "lib"),),
449
+ ),
450
+ rule_set_version=PHASE14C_RULE_SET,
451
+ renderer_version=PHASE14C_RENDERER,
452
+ )
453
+ cross = converter.convert(cross_request, observation=observation)
454
+ cross_repeat = converter.convert(cross_request, observation=observation)
455
+ cross_payload = (
456
+ cross.stage_artifact.payload if cross.stage_artifact is not None else {}
457
+ )
458
+ cross_facts = (
459
+ _facts(cross_payload, KEYWORD_CALL_TABLE_ID) if cross_payload else []
460
+ )
461
+ cross_independent, cross_reason = (
462
+ validate_keyword_call_binding_facts(cross_payload)
463
+ if cross_payload
464
+ else (False, "cross-module conversion omitted its payload")
465
+ )
466
+ module_functions = (
467
+ {
468
+ item.get("value", {}).get("function_node_id"): item.get("value", {})
469
+ for item in _table(
470
+ cross_payload, "module-function-facts"
471
+ ).get("records", ())
472
+ }
473
+ if cross_payload
474
+ else {}
475
+ )
476
+ cross_lowering, cross_lowering_evidence = _lowering_proof(
477
+ cross_payload, cross_facts
478
+ )
479
+ cross_fact = cross_facts[0] if len(cross_facts) == 1 else {}
480
+ cross_valid = (
481
+ cross.status is ResultStatus.CONVERTED
482
+ and cross.stage_artifact is not None
483
+ and cross.stage_artifact.schema_version == "0.14.2"
484
+ and cross_payload.get("rule_set_version") == PHASE14C_RULE_SET
485
+ and cross_payload.get("renderer_version") == PHASE14C_RENDERER
486
+ and len(cross_facts) == 1
487
+ and cross_fact.get("source_to_parameter_ordinals") == [2, 1, 0]
488
+ and cross_fact.get("parameter_to_source_ordinals") == [2, 1, 0]
489
+ and module_functions.get(
490
+ cross_fact.get("target_function_node_id"), {}
491
+ ).get("module_id")
492
+ == "lib"
493
+ and (cross.conversion_summary or {})
494
+ .get("module_initialization", {})
495
+ .get("module_order")
496
+ == ["lib", "app"]
497
+ and cross_independent
498
+ and cross_lowering
499
+ and result_to_json(cross) == result_to_json(cross_repeat)
500
+ and validate_c_text(cross.generated_c or "").accepted
501
+ and not cross_payload.get("helper_manifest")
502
+ )
503
+
504
+ rejected = {
505
+ label: converter.convert(phase14c_request(text))
506
+ for label, (_, text) in rejections.items()
507
+ }
508
+ rejection_codes = {
509
+ label: [item.code for item in result.diagnostics]
510
+ for label, result in rejected.items()
511
+ }
512
+ rejections_valid = all(
513
+ result.status is ResultStatus.REJECTED
514
+ and rejection_codes[label] == [expected_code]
515
+ and result.generated_c is None
516
+ and result.output_fingerprint is None
517
+ and result.stage_artifact is not None
518
+ and "c_ir" not in result.stage_artifact.payload
519
+ and "helper_manifest" not in result.stage_artifact.payload
520
+ for label, (expected_code, _) in rejections.items()
521
+ for result in (rejected[label],)
522
+ )
523
+
524
+ historical_source = (
525
+ "def choose(left: int, flag: bool) -> int:\n return left\n\n"
526
+ "def run(value: int, flag: bool) -> int:\n"
527
+ " return choose(flag=flag, left=value)\n"
528
+ )
529
+ historical = converter.convert(
530
+ ConversionRequest.from_source(
531
+ historical_source,
532
+ rule_set_version=PHASE14B_RULE_SET,
533
+ renderer_version=PHASE14B_RENDERER,
534
+ ),
535
+ observation=observation,
536
+ )
537
+ historical_payload = (
538
+ historical.stage_artifact.payload
539
+ if historical.stage_artifact is not None
540
+ else {}
541
+ )
542
+ phase14b_exact = (
543
+ historical.status is ResultStatus.REJECTED
544
+ and [item.code for item in historical.diagnostics] == ["PYC2910"]
545
+ and [item.diagnostic_id for item in historical.diagnostics]
546
+ == ["diag-33b10f68721e38b3e960"]
547
+ and historical.request_fingerprint is not None
548
+ and historical.request_fingerprint.value
549
+ == "c447d082bb1b12228b0e7fd80ed17c438063f0591ffee2b4b240031fc6d9187f"
550
+ and historical.stage_artifact is not None
551
+ and historical.stage_artifact.kind == "conversion_plan"
552
+ and historical.stage_artifact.schema_version == "0.14.1"
553
+ and historical.stage_artifact.artifact_fingerprint.value
554
+ == "8daf5c369e7ea4e61521bebbae32efccd4ede450e375b09bc44f37ed9d0540c5"
555
+ and historical_payload.get("schema_version") == "conversion-plan/0.14.1"
556
+ and (historical.conversion_summary or {}).get("schema_version")
557
+ == "pycforge.conversion-summary/0.14.1"
558
+ and (historical.decision_trace or {}).get("schema_version")
559
+ == "pycforge.decision-trace/0.14.1"
560
+ and KEYWORD_CALL_TABLE_ID
561
+ not in {
562
+ item.get("table_id")
563
+ for item in historical_payload.get("fact_tables", ())
564
+ }
565
+ and historical.generated_c is None
566
+ and historical.output_fingerprint is None
567
+ )
568
+
569
+ passed = all(
570
+ (
571
+ identity_valid,
572
+ constants_valid,
573
+ table_valid,
574
+ independent_valid,
575
+ facts_valid,
576
+ plans_valid,
577
+ lowering_valid,
578
+ observer_valid,
579
+ helper_valid,
580
+ conformance.accepted,
581
+ deterministic,
582
+ fresh_process_valid,
583
+ cross_valid,
584
+ rejections_valid,
585
+ phase14b_exact,
586
+ )
587
+ )
588
+ return {
589
+ "audit": "keyword",
590
+ "passed": passed,
591
+ "active_contracts": active_contracts,
592
+ "active_contract_identities_valid": identity_valid,
593
+ "keyword_rule_id": KEYWORD_CALL_RULE_ID,
594
+ "keyword_rule_version": KEYWORD_CALL_RULE_VERSION,
595
+ "keyword_fact_schema": KEYWORD_CALL_FACT_SCHEMA,
596
+ "keyword_contract_constants_valid": constants_valid,
597
+ "keyword_fact_count": len(fact_values),
598
+ "keyword_fact_table_valid": table_valid,
599
+ "independent_fact_and_plan_validation": independent_valid,
600
+ "independent_validation_message": independent_reason,
601
+ "direct_source_to_parameter_ordinals": direct.get(
602
+ "source_to_parameter_ordinals"
603
+ ),
604
+ "direct_parameter_to_source_ordinals": direct.get(
605
+ "parameter_to_source_ordinals"
606
+ ),
607
+ "reordered_source_to_parameter_ordinals": reordered.get(
608
+ "source_to_parameter_ordinals"
609
+ ),
610
+ "reordered_parameter_to_source_ordinals": reordered.get(
611
+ "parameter_to_source_ordinals"
612
+ ),
613
+ "keyword_facts_valid": facts_valid,
614
+ "keyword_rule_plan_count": len(plans),
615
+ "keyword_rule_plans_valid": plans_valid,
616
+ "source_order_staging_and_formal_reference_permutation_valid": lowering_valid,
617
+ "lowering_evidence": lowering_evidence,
618
+ "observer_evidence_valid": observer_valid,
619
+ "helper_manifest_empty": not payload.get("helper_manifest"),
620
+ "helper_registry_fingerprint": helper_registry.fingerprint,
621
+ "helper_contract_unchanged": helper_valid,
622
+ "allocation": "none",
623
+ "cleanup": "none",
624
+ "runtime_binding_failure": "proved-absent",
625
+ "generated_c_conformance": conformance.accepted,
626
+ "deterministic": deterministic,
627
+ "fresh_process_deterministic": fresh_process_valid,
628
+ "serialized_sha256": fresh_expected["serialized_sha256"],
629
+ "generated_c_sha256": fresh_expected["generated_sha256"],
630
+ "cross_module_valid": cross_valid,
631
+ "cross_module_source_to_parameter_ordinals": cross_fact.get(
632
+ "source_to_parameter_ordinals"
633
+ ),
634
+ "cross_module_parameter_to_source_ordinals": cross_fact.get(
635
+ "parameter_to_source_ordinals"
636
+ ),
637
+ "cross_module_target_module": module_functions.get(
638
+ cross_fact.get("target_function_node_id"), {}
639
+ ).get("module_id"),
640
+ "cross_module_order": (cross.conversion_summary or {})
641
+ .get("module_initialization", {})
642
+ .get("module_order"),
643
+ "cross_module_independent_validation": cross_independent,
644
+ "cross_module_independent_validation_message": cross_reason,
645
+ "cross_module_lowering_evidence": cross_lowering_evidence,
646
+ "rejection_case_count": len(rejections),
647
+ "rejection_codes": rejection_codes,
648
+ "rejections_exact_and_publish_no_c": rejections_valid,
649
+ "phase14b_exact_keyword_rejection": phase14b_exact,
650
+ "python_subprocess_used_for_determinism_only": True,
651
+ "c_toolchain_invoked": False,
652
+ "generated_c_compiled_or_executed": False,
653
+ }
654
+ except Exception as exc:
655
+ return {
656
+ "audit": "keyword",
657
+ "passed": False,
658
+ "error": f"{type(exc).__name__}: {exc}",
659
+ "c_toolchain_invoked": False,
660
+ "generated_c_compiled_or_executed": False,
661
+ }