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.
- pycforge/__init__.py +8 -0
- pycforge/__main__.py +2 -0
- pycforge/_version.py +1 -0
- pycforge/converter/__init__.py +0 -0
- pycforge/converter/analysis/__init__.py +5 -0
- pycforge/converter/analysis/functions.py +723 -0
- pycforge/converter/analysis/model.py +158 -0
- pycforge/converter/analysis/planning.py +464 -0
- pycforge/converter/analysis/stage.py +1976 -0
- pycforge/converter/analysis/symbols.py +237 -0
- pycforge/converter/analysis/validation.py +2166 -0
- pycforge/converter/c_output/__init__.py +2 -0
- pycforge/converter/c_output/conformance.py +297 -0
- pycforge/converter/c_output/renderer.py +220 -0
- pycforge/converter/conditional_regions/__init__.py +71 -0
- pycforge/converter/conditional_regions/analysis.py +481 -0
- pycforge/converter/conditional_regions/lowering.py +579 -0
- pycforge/converter/conditional_regions/model.py +243 -0
- pycforge/converter/conditional_regions/validation.py +690 -0
- pycforge/converter/containers/__init__.py +19 -0
- pycforge/converter/containers/analysis.py +542 -0
- pycforge/converter/containers/lowering.py +290 -0
- pycforge/converter/containers/model.py +146 -0
- pycforge/converter/contracts/__init__.py +0 -0
- pycforge/converter/contracts/configuration.py +178 -0
- pycforge/converter/contracts/identifiers.py +68 -0
- pycforge/converter/contracts/versions.py +71 -0
- pycforge/converter/core/__init__.py +0 -0
- pycforge/converter/core/artifact_io.py +87 -0
- pycforge/converter/core/cancellation.py +7 -0
- pycforge/converter/core/canonicalization.py +254 -0
- pycforge/converter/core/context.py +13 -0
- pycforge/converter/core/diagnostics.py +95 -0
- pycforge/converter/core/enums.py +23 -0
- pycforge/converter/core/fingerprint.py +22 -0
- pycforge/converter/core/pipeline.py +8 -0
- pycforge/converter/core/progress.py +25 -0
- pycforge/converter/core/request.py +50 -0
- pycforge/converter/core/resource_policy.py +31 -0
- pycforge/converter/core/result.py +23 -0
- pycforge/converter/core/serialization.py +36 -0
- pycforge/converter/core/stage.py +10 -0
- pycforge/converter/core/stage_artifact.py +80 -0
- pycforge/converter/core/stage_outcome.py +15 -0
- pycforge/converter/decision_trace/__init__.py +0 -0
- pycforge/converter/decision_trace/recorder.py +52 -0
- pycforge/converter/facade.py +459 -0
- pycforge/converter/frontend/__init__.py +4 -0
- pycforge/converter/frontend/normalizer.py +59 -0
- pycforge/converter/frontend/parser.py +20 -0
- pycforge/converter/frontend/source_document.py +93 -0
- pycforge/converter/frontend/stages.py +518 -0
- pycforge/converter/frontend/validation.py +76 -0
- pycforge/converter/io/__init__.py +0 -0
- pycforge/converter/io/atomic_writer.py +33 -0
- pycforge/converter/ir/__init__.py +1 -0
- pycforge/converter/ir/c_ir/__init__.py +4 -0
- pycforge/converter/ir/c_ir/builder.py +26 -0
- pycforge/converter/ir/c_ir/model.py +318 -0
- pycforge/converter/ir/c_ir/serialization.py +88 -0
- pycforge/converter/ir/c_ir/validation.py +834 -0
- pycforge/converter/ir/python_ir/__init__.py +17 -0
- pycforge/converter/ir/python_ir/nodes.py +86 -0
- pycforge/converter/keyword_calls/__init__.py +68 -0
- pycforge/converter/keyword_calls/analysis.py +458 -0
- pycforge/converter/keyword_calls/lowering.py +271 -0
- pycforge/converter/keyword_calls/model.py +304 -0
- pycforge/converter/keyword_calls/validation.py +802 -0
- pycforge/converter/keyword_only_calls/__init__.py +68 -0
- pycforge/converter/keyword_only_calls/analysis.py +640 -0
- pycforge/converter/keyword_only_calls/lowering.py +376 -0
- pycforge/converter/keyword_only_calls/model.py +328 -0
- pycforge/converter/keyword_only_calls/validation.py +1357 -0
- pycforge/converter/lowering.py +1000 -0
- pycforge/converter/modules/__init__.py +13 -0
- pycforge/converter/modules/analysis.py +1250 -0
- pycforge/converter/modules/lowering.py +200 -0
- pycforge/converter/modules/model.py +73 -0
- pycforge/converter/modules/stage.py +192 -0
- pycforge/converter/numeric_semantics/__init__.py +23 -0
- pycforge/converter/numeric_semantics/analysis.py +420 -0
- pycforge/converter/numeric_semantics/lowering.py +153 -0
- pycforge/converter/numeric_semantics/model.py +123 -0
- pycforge/converter/records/__init__.py +34 -0
- pycforge/converter/records/analysis.py +1394 -0
- pycforge/converter/records/lowering.py +393 -0
- pycforge/converter/records/model.py +319 -0
- pycforge/converter/rules/__init__.py +3 -0
- pycforge/converter/support_templates/__init__.py +49 -0
- pycforge/converter/support_templates/assembly.py +106 -0
- pycforge/converter/support_templates/factories.py +348 -0
- pycforge/converter/support_templates/model.py +159 -0
- pycforge/converter/support_templates/registry.py +388 -0
- pycforge/converter/telemetry/__init__.py +0 -0
- pycforge/converter/telemetry/sink.py +39 -0
- pycforge/ide/__init__.py +19 -0
- pycforge/ide/__main__.py +5 -0
- pycforge/ide/_worker_event_codec.py +276 -0
- pycforge/ide/_worker_protocol_json.py +197 -0
- pycforge/ide/_worker_protocol_types.py +90 -0
- pycforge/ide/_worker_request_codec.py +200 -0
- pycforge/ide/_worker_request_data.py +260 -0
- pycforge/ide/_worker_result_data.py +343 -0
- pycforge/ide/action_contract.py +469 -0
- pycforge/ide/action_validation.py +229 -0
- pycforge/ide/command_palette.py +223 -0
- pycforge/ide/controller.py +904 -0
- pycforge/ide/controller_conversion.py +546 -0
- pycforge/ide/controller_io.py +205 -0
- pycforge/ide/editor.py +951 -0
- pycforge/ide/editor_commands_qt.py +274 -0
- pycforge/ide/editor_lexical.py +43 -0
- pycforge/ide/editor_sidebars.py +236 -0
- pycforge/ide/editor_syntax.py +243 -0
- pycforge/ide/find_replace.py +535 -0
- pycforge/ide/icons.py +91 -0
- pycforge/ide/io_service.py +196 -0
- pycforge/ide/model.py +164 -0
- pycforge/ide/panels.py +903 -0
- pycforge/ide/positions.py +220 -0
- pycforge/ide/process_worker.py +130 -0
- pycforge/ide/qt.py +230 -0
- pycforge/ide/qt_actions.py +418 -0
- pycforge/ide/qt_close.py +169 -0
- pycforge/ide/qt_command_palette.py +297 -0
- pycforge/ide/qt_contract.py +220 -0
- pycforge/ide/qt_documents.py +590 -0
- pycforge/ide/qt_editor_buffers.py +113 -0
- pycforge/ide/qt_editor_surfaces.py +112 -0
- pycforge/ide/qt_menus.py +324 -0
- pycforge/ide/qt_projection.py +500 -0
- pycforge/ide/qt_shell.py +348 -0
- pycforge/ide/qt_shell_interactions.py +320 -0
- pycforge/ide/qt_state.py +393 -0
- pycforge/ide/qt_workspace_features.py +398 -0
- pycforge/ide/qt_workspace_navigation.py +208 -0
- pycforge/ide/qt_workspace_observers.py +173 -0
- pycforge/ide/qt_workspace_panels.py +545 -0
- pycforge/ide/qt_workspace_widgets.py +228 -0
- pycforge/ide/resources/icons/about.svg +1 -0
- pycforge/ide/resources/icons/add-document.svg +1 -0
- pycforge/ide/resources/icons/brand-mark.svg +1 -0
- pycforge/ide/resources/icons/bundle-search.svg +1 -0
- pycforge/ide/resources/icons/cancel.svg +1 -0
- pycforge/ide/resources/icons/check.svg +1 -0
- pycforge/ide/resources/icons/chevron-right.svg +1 -0
- pycforge/ide/resources/icons/close.svg +1 -0
- pycforge/ide/resources/icons/collapse-all.svg +1 -0
- pycforge/ide/resources/icons/command-palette.svg +1 -0
- pycforge/ide/resources/icons/convert.svg +1 -0
- pycforge/ide/resources/icons/copy.svg +1 -0
- pycforge/ide/resources/icons/cut.svg +1 -0
- pycforge/ide/resources/icons/decision-trace.svg +1 -0
- pycforge/ide/resources/icons/details.svg +1 -0
- pycforge/ide/resources/icons/diagnostics.svg +1 -0
- pycforge/ide/resources/icons/duplicate-line.svg +1 -0
- pycforge/ide/resources/icons/exit.svg +1 -0
- pycforge/ide/resources/icons/expand-all.svg +1 -0
- pycforge/ide/resources/icons/export.svg +1 -0
- pycforge/ide/resources/icons/find.svg +1 -0
- pycforge/ide/resources/icons/go-to-line.svg +1 -0
- pycforge/ide/resources/icons/go-to-output.svg +1 -0
- pycforge/ide/resources/icons/go-to-source.svg +1 -0
- pycforge/ide/resources/icons/history.svg +1 -0
- pycforge/ide/resources/icons/indent.svg +1 -0
- pycforge/ide/resources/icons/link-c.svg +1 -0
- pycforge/ide/resources/icons/mappings.svg +1 -0
- pycforge/ide/resources/icons/module.svg +1 -0
- pycforge/ide/resources/icons/move-down.svg +1 -0
- pycforge/ide/resources/icons/move-line-down.svg +1 -0
- pycforge/ide/resources/icons/move-line-up.svg +1 -0
- pycforge/ide/resources/icons/move-up.svg +1 -0
- pycforge/ide/resources/icons/next.svg +1 -0
- pycforge/ide/resources/icons/open.svg +1 -0
- pycforge/ide/resources/icons/outdent.svg +1 -0
- pycforge/ide/resources/icons/outline.svg +1 -0
- pycforge/ide/resources/icons/paste.svg +1 -0
- pycforge/ide/resources/icons/previous.svg +1 -0
- pycforge/ide/resources/icons/primary-module.svg +1 -0
- pycforge/ide/resources/icons/redo.svg +1 -0
- pycforge/ide/resources/icons/remove-document.svg +1 -0
- pycforge/ide/resources/icons/replace.svg +1 -0
- pycforge/ide/resources/icons/save-as.svg +1 -0
- pycforge/ide/resources/icons/save.svg +1 -0
- pycforge/ide/resources/icons/select-all.svg +1 -0
- pycforge/ide/resources/icons/settings.svg +1 -0
- pycforge/ide/resources/icons/show-c.svg +1 -0
- pycforge/ide/resources/icons/split-view.svg +1 -0
- pycforge/ide/resources/icons/summary.svg +1 -0
- pycforge/ide/resources/icons/telemetry.svg +1 -0
- pycforge/ide/resources/icons/toggle-comment.svg +1 -0
- pycforge/ide/resources/icons/toggle-fold.svg +1 -0
- pycforge/ide/resources/icons/undo.svg +1 -0
- pycforge/ide/resources/icons/whitespace.svg +1 -0
- pycforge/ide/revisions.py +400 -0
- pycforge/ide/search_service.py +280 -0
- pycforge/ide/session_history.py +150 -0
- pycforge/ide/source_editing.py +417 -0
- pycforge/ide/source_structure.py +558 -0
- pycforge/ide/source_structure_async.py +191 -0
- pycforge/ide/supervisor.py +503 -0
- pycforge/ide/theme.py +119 -0
- pycforge/ide/theme_stylesheet.py +598 -0
- pycforge/ide/theme_workspace_stylesheet.py +155 -0
- pycforge/ide/visual_tokens.py +142 -0
- pycforge/ide/worker_protocol.py +80 -0
- pycforge/ide/workspace_search.py +462 -0
- pycforge/ide/workspace_session.py +256 -0
- pycforge/laboratory/__init__.py +6 -0
- pycforge/laboratory/audits.py +2244 -0
- pycforge/laboratory/checkpoint_e.py +2685 -0
- pycforge/laboratory/cli.py +119 -0
- pycforge/laboratory/exit_codes.py +11 -0
- pycforge/laboratory/keyword_audit.py +661 -0
- pycforge/laboratory/keyword_only_audit.py +481 -0
- pycforge-0.15.2.dist-info/METADATA +209 -0
- pycforge-0.15.2.dist-info/RECORD +221 -0
- pycforge-0.15.2.dist-info/WHEEL +5 -0
- pycforge-0.15.2.dist-info/entry_points.txt +5 -0
- pycforge-0.15.2.dist-info/licenses/LICENSE +674 -0
- 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()}
|