millforge 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- millforge/__init__.py +1174 -0
- millforge/_forge/LICENSE +21 -0
- millforge/_forge/PROVENANCE.json +295 -0
- millforge/_forge/UPDATE_POLICY.md +24 -0
- millforge/_forge/__init__.py +14 -0
- millforge/_forge/adapter.py +2232 -0
- millforge/_forge/base_runner.py +121 -0
- millforge/_forge/clients/__init__.py +10 -0
- millforge/_forge/clients/base.py +200 -0
- millforge/_forge/context/__init__.py +23 -0
- millforge/_forge/context/manager.py +178 -0
- millforge/_forge/context/strategies.py +335 -0
- millforge/_forge/core/__init__.py +16 -0
- millforge/_forge/core/inference.py +433 -0
- millforge/_forge/core/messages.py +119 -0
- millforge/_forge/core/runner.py +479 -0
- millforge/_forge/core/steps.py +108 -0
- millforge/_forge/core/workflow.py +400 -0
- millforge/_forge/errors.py +222 -0
- millforge/_forge/guardrails/__init__.py +21 -0
- millforge/_forge/guardrails/error_tracker.py +71 -0
- millforge/_forge/guardrails/guardrails.py +194 -0
- millforge/_forge/guardrails/nudge.py +47 -0
- millforge/_forge/guardrails/response_validator.py +119 -0
- millforge/_forge/guardrails/step_enforcer.py +183 -0
- millforge/_forge/prompts/__init__.py +16 -0
- millforge/_forge/prompts/nudges.py +95 -0
- millforge/_forge/prompts/templates.py +285 -0
- millforge/_version.py +3 -0
- millforge/artifacts.py +570 -0
- millforge/base/__init__.py +97 -0
- millforge/base/composition.py +402 -0
- millforge/base/context.py +285 -0
- millforge/base/harness.py +138 -0
- millforge/base/identity.py +465 -0
- millforge/base/options.py +34 -0
- millforge/base/platform.py +17 -0
- millforge/base/prompt.py +317 -0
- millforge/base/runner.py +546 -0
- millforge/compiled_plan.py +970 -0
- millforge/compiler/__init__.py +231 -0
- millforge/compiler/artifact_validation.py +257 -0
- millforge/compiler/canonicalization.py +169 -0
- millforge/compiler/capabilities.py +66 -0
- millforge/compiler/catalogs.py +500 -0
- millforge/compiler/diagnostics.py +491 -0
- millforge/compiler/graph.py +678 -0
- millforge/compiler/lowering.py +198 -0
- millforge/compiler/output.py +692 -0
- millforge/compiler/parsing.py +1424 -0
- millforge/compiler/requests.py +1180 -0
- millforge/compiler/schema_validation.py +272 -0
- millforge/compiler/semantic.py +490 -0
- millforge/compiler/service.py +448 -0
- millforge/compiler/source.py +375 -0
- millforge/compiler/validators.py +184 -0
- millforge/connectors/__init__.py +95 -0
- millforge/connectors/admission.py +801 -0
- millforge/connectors/broker.py +202 -0
- millforge/connectors/contracts.py +1159 -0
- millforge/connectors/diagnostics.py +189 -0
- millforge/connectors/fake.py +66 -0
- millforge/connectors/runtime.py +236 -0
- millforge/contracts.py +2860 -0
- millforge/custom_tools/__init__.py +67 -0
- millforge/custom_tools/compiler.py +724 -0
- millforge/custom_tools/contracts.py +1093 -0
- millforge/custom_tools/diagnostics.py +205 -0
- millforge/eval_artifacts.py +952 -0
- millforge/eval_boundary.py +2435 -0
- millforge/eval_fixtures/__init__.py +1 -0
- millforge/eval_fixtures/default_pack/__init__.py +1 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.bug_diagnosis.traceback.v1.json +52 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.direct_edit.import_sort.v1.json +52 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.evidence_discipline.no_source_change.v1.json +51 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.false_closure.visible_green.v1.json +52 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.multi_file.api_contract.v1.json +54 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.recovery.malformed_artifact.v1.json +54 -0
- millforge/eval_fixtures/default_pack/manifest.json +12 -0
- millforge/eval_modes.py +1282 -0
- millforge/eval_presets.py +1398 -0
- millforge/eval_reports.py +2517 -0
- millforge/eval_suite.py +2429 -0
- millforge/eval_trials.py +2632 -0
- millforge/eval_workflow.py +794 -0
- millforge/exceptions.py +122 -0
- millforge/model_backend.py +2098 -0
- millforge/protocols.py +340 -0
- millforge/py.typed +0 -0
- millforge/runtime.py +1791 -0
- millforge/testing/__init__.py +1089 -0
- millforge/tools/__init__.py +83 -0
- millforge/tools/builtin_runtime.py +1339 -0
- millforge/tools/builtins.py +773 -0
- millforge/tools/execution.py +1545 -0
- millforge/tools/path_policy.py +155 -0
- millforge/tools/pi_compat/PI_LICENSE +21 -0
- millforge/tools/pi_compat/PROVENANCE.json +55 -0
- millforge/tools/pi_compat/UPDATE_POLICY.md +36 -0
- millforge/tools/pi_compat/__init__.py +34 -0
- millforge/tools/pi_compat/contracts.py +49 -0
- millforge/tools/pi_compat/editing.py +390 -0
- millforge/tools/pi_compat/mutations.py +57 -0
- millforge/tools/pi_compat/operations.py +401 -0
- millforge/tools/pi_compat/paths.py +155 -0
- millforge/tools/pi_compat/process.py +1375 -0
- millforge/tools/pi_compat/search.py +738 -0
- millforge/tools/pi_compat/truncation.py +267 -0
- millforge/tools/pi_compat_catalog.py +396 -0
- millforge/tools/pi_compat_runtime.py +460 -0
- millforge/tools/registry.py +553 -0
- millforge/tools/results.py +533 -0
- millforge-0.1.0.dist-info/METADATA +844 -0
- millforge-0.1.0.dist-info/RECORD +116 -0
- millforge-0.1.0.dist-info/WHEEL +4 -0
- millforge-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
"""Atomic compiler output publication helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import errno
|
|
6
|
+
import hashlib
|
|
7
|
+
import os
|
|
8
|
+
import secrets
|
|
9
|
+
import stat
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path, PurePosixPath
|
|
12
|
+
from urllib.parse import quote
|
|
13
|
+
|
|
14
|
+
from millforge.compiled_plan import CompiledHarnessPlan, canonical_compiled_plan_bytes
|
|
15
|
+
from millforge.compiled_plan import canonical_json_serialize
|
|
16
|
+
from millforge.compiler.diagnostics import (
|
|
17
|
+
CompilerDiagnostic,
|
|
18
|
+
CompilerPhase,
|
|
19
|
+
DiagnosticField,
|
|
20
|
+
DiagnosticSeverity,
|
|
21
|
+
)
|
|
22
|
+
from millforge.compiler.requests import (
|
|
23
|
+
CompileStatus,
|
|
24
|
+
DiagnosticReportState,
|
|
25
|
+
HarnessCompileRequest,
|
|
26
|
+
HarnessCompileResult,
|
|
27
|
+
PlanCommitCertainty,
|
|
28
|
+
)
|
|
29
|
+
from millforge.compiler.validators import validate_utf8_size
|
|
30
|
+
from millforge.compiler.validators import validate_sha256
|
|
31
|
+
|
|
32
|
+
MAX_COMPILED_PLAN_OUTPUT_BYTES = 1024 * 1024
|
|
33
|
+
_HARNESS_ID_FILENAME_SAFE = (
|
|
34
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class _AdmittedOutputDir:
|
|
40
|
+
"""Exact admitted output-directory handle used for anchored writes."""
|
|
41
|
+
|
|
42
|
+
path: Path
|
|
43
|
+
fd: int
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def request_identity_sha256(request: HarnessCompileRequest) -> str:
|
|
47
|
+
"""Hash the request identity fields that affect output addressing."""
|
|
48
|
+
payload = {
|
|
49
|
+
"request_id": request.request_id,
|
|
50
|
+
"source_path": request.source_path,
|
|
51
|
+
"source_format": request.source_format,
|
|
52
|
+
"expected_harness_id": request.expected_harness_id,
|
|
53
|
+
"stage_kind_id": request.stage_kind_id,
|
|
54
|
+
}
|
|
55
|
+
return hashlib.sha256(canonical_json_serialize(payload).encode("utf-8")).hexdigest()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def compiled_plan_output_path(
|
|
59
|
+
request: HarnessCompileRequest, plan: CompiledHarnessPlan
|
|
60
|
+
) -> str:
|
|
61
|
+
"""Return the safe relative content-addressed compiled-plan output path."""
|
|
62
|
+
return _join_output_path(
|
|
63
|
+
request.output_dir,
|
|
64
|
+
(
|
|
65
|
+
f"{_safe_harness_identity(plan.harness_id)}@{plan.harness_version}."
|
|
66
|
+
f"{plan.compiled_sha256}.compiled.json"
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def diagnostics_output_path(
|
|
72
|
+
request: HarnessCompileRequest,
|
|
73
|
+
*,
|
|
74
|
+
harness_id: str | None = None,
|
|
75
|
+
harness_version: int | None = None,
|
|
76
|
+
source_document_sha256: str | None = None,
|
|
77
|
+
compiled_sha256: str | None = None,
|
|
78
|
+
) -> str:
|
|
79
|
+
"""Return the safe relative request-addressed diagnostics output path."""
|
|
80
|
+
request_hash = request_identity_sha256(request)
|
|
81
|
+
if compiled_sha256 is not None:
|
|
82
|
+
digest = validate_sha256(compiled_sha256, "compiled_sha256")
|
|
83
|
+
prefix = _harness_identity_prefix(
|
|
84
|
+
harness_id=harness_id,
|
|
85
|
+
harness_version=harness_version,
|
|
86
|
+
)
|
|
87
|
+
filename = f"{prefix}.{digest}.request-{request_hash}.diagnostics.json"
|
|
88
|
+
elif source_document_sha256 is not None:
|
|
89
|
+
digest = validate_sha256(source_document_sha256, "source_document_sha256")
|
|
90
|
+
prefix = _harness_identity_prefix(
|
|
91
|
+
harness_id=harness_id,
|
|
92
|
+
harness_version=harness_version,
|
|
93
|
+
)
|
|
94
|
+
filename = f"{prefix}.{digest}.request-{request_hash}.diagnostics.json"
|
|
95
|
+
else:
|
|
96
|
+
filename = f"request-{request_hash}.diagnostics.json"
|
|
97
|
+
return _join_output_path(
|
|
98
|
+
request.output_dir,
|
|
99
|
+
filename,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def persist_compile_outputs(
|
|
104
|
+
*,
|
|
105
|
+
request: HarnessCompileRequest,
|
|
106
|
+
plan: CompiledHarnessPlan,
|
|
107
|
+
diagnostics: tuple[CompilerDiagnostic, ...] = (),
|
|
108
|
+
source_document_sha256: str | None = None,
|
|
109
|
+
) -> HarnessCompileResult:
|
|
110
|
+
"""Persist prepared diagnostics, publish a compiled plan, then commit diagnostics."""
|
|
111
|
+
request_hash = request_identity_sha256(request)
|
|
112
|
+
output_dir = _admit_output_dir(request, request_hash=request_hash)
|
|
113
|
+
if isinstance(output_dir, HarnessCompileResult):
|
|
114
|
+
return output_dir
|
|
115
|
+
|
|
116
|
+
plan_relpath = compiled_plan_output_path(request, plan)
|
|
117
|
+
diagnostics_relpath = diagnostics_output_path(
|
|
118
|
+
request,
|
|
119
|
+
harness_id=plan.harness_id,
|
|
120
|
+
harness_version=plan.harness_version,
|
|
121
|
+
source_document_sha256=source_document_sha256,
|
|
122
|
+
compiled_sha256=plan.compiled_sha256,
|
|
123
|
+
)
|
|
124
|
+
plan_name = Path(plan_relpath).name
|
|
125
|
+
diagnostics_name = Path(diagnostics_relpath).name
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
if not _admitted_output_dir_matches(output_dir):
|
|
129
|
+
return _failed_output_result(
|
|
130
|
+
request=request,
|
|
131
|
+
request_hash=request_hash,
|
|
132
|
+
code="MF-O001",
|
|
133
|
+
message="Output directory could not be confirmed after admission.",
|
|
134
|
+
source_document_sha256=source_document_sha256,
|
|
135
|
+
diagnostics=diagnostics,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
prepared_result = HarnessCompileResult(
|
|
139
|
+
request_id=request.request_id,
|
|
140
|
+
status=CompileStatus.PREPARED,
|
|
141
|
+
plan_commit_certainty=PlanCommitCertainty.ABSENT,
|
|
142
|
+
diagnostic_report_state=DiagnosticReportState.PREPARED,
|
|
143
|
+
source_document_sha256=source_document_sha256,
|
|
144
|
+
source_sha256=plan.source_sha256,
|
|
145
|
+
request_identity_sha256=request_hash,
|
|
146
|
+
harness_id=plan.harness_id,
|
|
147
|
+
diagnostics_path=diagnostics_relpath,
|
|
148
|
+
diagnostics=diagnostics,
|
|
149
|
+
)
|
|
150
|
+
prepared_error = _write_diagnostics_report(
|
|
151
|
+
output_dir=output_dir, filename=diagnostics_name, result=prepared_result
|
|
152
|
+
)
|
|
153
|
+
if prepared_error is not None:
|
|
154
|
+
return _failed_output_result(
|
|
155
|
+
request=request,
|
|
156
|
+
plan=plan,
|
|
157
|
+
request_hash=request_hash,
|
|
158
|
+
code="MF-O002",
|
|
159
|
+
message="Prepared diagnostics report could not be written.",
|
|
160
|
+
source_document_sha256=source_document_sha256,
|
|
161
|
+
diagnostics=diagnostics,
|
|
162
|
+
fields=prepared_error,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
plan_bytes = canonical_compiled_plan_bytes(plan)
|
|
166
|
+
if len(plan_bytes) > MAX_COMPILED_PLAN_OUTPUT_BYTES:
|
|
167
|
+
return _failed_output_result(
|
|
168
|
+
request=request,
|
|
169
|
+
plan=plan,
|
|
170
|
+
request_hash=request_hash,
|
|
171
|
+
code="MF-O003",
|
|
172
|
+
message="Compiled plan exceeds the output size limit.",
|
|
173
|
+
source_document_sha256=source_document_sha256,
|
|
174
|
+
diagnostics=diagnostics,
|
|
175
|
+
diagnostics_path=diagnostics_relpath,
|
|
176
|
+
diagnostic_report_state=DiagnosticReportState.PREPARED,
|
|
177
|
+
fields=(_field("compiled_plan_size", len(plan_bytes)),),
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
publish = _publish_content_addressed_file(
|
|
181
|
+
output_dir=output_dir, destination_name=plan_name, data=plan_bytes
|
|
182
|
+
)
|
|
183
|
+
if publish.result == _PublishResult.CONFLICT:
|
|
184
|
+
return _failed_output_result(
|
|
185
|
+
request=request,
|
|
186
|
+
plan=plan,
|
|
187
|
+
request_hash=request_hash,
|
|
188
|
+
code="MF-O004",
|
|
189
|
+
message="Compiled plan destination already contains different bytes.",
|
|
190
|
+
source_document_sha256=source_document_sha256,
|
|
191
|
+
diagnostics=diagnostics,
|
|
192
|
+
diagnostics_path=diagnostics_relpath,
|
|
193
|
+
diagnostic_report_state=DiagnosticReportState.PREPARED,
|
|
194
|
+
plan_certainty=PlanCommitCertainty.ABSENT,
|
|
195
|
+
cleanup_error=publish.cleanup_error,
|
|
196
|
+
)
|
|
197
|
+
if publish.error is not None:
|
|
198
|
+
return _failed_output_result(
|
|
199
|
+
request=request,
|
|
200
|
+
plan=plan,
|
|
201
|
+
request_hash=request_hash,
|
|
202
|
+
code="MF-O003",
|
|
203
|
+
message="Compiled plan temporary file could not be written.",
|
|
204
|
+
source_document_sha256=source_document_sha256,
|
|
205
|
+
diagnostics=diagnostics,
|
|
206
|
+
diagnostics_path=diagnostics_relpath,
|
|
207
|
+
diagnostic_report_state=DiagnosticReportState.PREPARED,
|
|
208
|
+
fields=publish.error,
|
|
209
|
+
cleanup_error=publish.cleanup_error,
|
|
210
|
+
)
|
|
211
|
+
if publish.cleanup_error is not None:
|
|
212
|
+
return _failed_output_result(
|
|
213
|
+
request=request,
|
|
214
|
+
plan=plan,
|
|
215
|
+
request_hash=request_hash,
|
|
216
|
+
code="MF-O005",
|
|
217
|
+
message="Compiled plan temporary file could not be cleaned up.",
|
|
218
|
+
source_document_sha256=source_document_sha256,
|
|
219
|
+
diagnostics=diagnostics,
|
|
220
|
+
diagnostics_path=diagnostics_relpath,
|
|
221
|
+
diagnostic_report_state=DiagnosticReportState.PREPARED,
|
|
222
|
+
plan_relpath=plan_relpath,
|
|
223
|
+
plan_certainty=PlanCommitCertainty.UNKNOWN,
|
|
224
|
+
fields=publish.cleanup_error,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
if not _destination_matches(
|
|
228
|
+
output_dir=output_dir, destination_name=plan_name, data=plan_bytes
|
|
229
|
+
) or not _fsync_directory(output_dir.fd):
|
|
230
|
+
durability_result = _failed_output_result(
|
|
231
|
+
request=request,
|
|
232
|
+
plan=plan,
|
|
233
|
+
request_hash=request_hash,
|
|
234
|
+
code="MF-O003",
|
|
235
|
+
message="Compiled plan publication durability could not be confirmed.",
|
|
236
|
+
source_document_sha256=source_document_sha256,
|
|
237
|
+
diagnostics=diagnostics,
|
|
238
|
+
diagnostics_path=diagnostics_relpath,
|
|
239
|
+
diagnostic_report_state=DiagnosticReportState.COMMITTED,
|
|
240
|
+
plan_relpath=plan_relpath,
|
|
241
|
+
plan_certainty=PlanCommitCertainty.UNKNOWN,
|
|
242
|
+
)
|
|
243
|
+
commit_error = _write_diagnostics_report(
|
|
244
|
+
output_dir=output_dir,
|
|
245
|
+
filename=diagnostics_name,
|
|
246
|
+
result=durability_result,
|
|
247
|
+
)
|
|
248
|
+
if commit_error is None:
|
|
249
|
+
return durability_result
|
|
250
|
+
return HarnessCompileResult(
|
|
251
|
+
request_id=request.request_id,
|
|
252
|
+
status=CompileStatus.FAILED,
|
|
253
|
+
plan_commit_certainty=PlanCommitCertainty.UNKNOWN,
|
|
254
|
+
diagnostic_report_state=DiagnosticReportState.UNKNOWN,
|
|
255
|
+
failure_phase=CompilerPhase.OUTPUT,
|
|
256
|
+
source_document_sha256=source_document_sha256,
|
|
257
|
+
source_sha256=plan.source_sha256,
|
|
258
|
+
request_identity_sha256=request_hash,
|
|
259
|
+
harness_id=plan.harness_id,
|
|
260
|
+
compiled_plan_path=plan_relpath,
|
|
261
|
+
compiled_sha256=plan.compiled_sha256,
|
|
262
|
+
diagnostics_path=diagnostics_relpath,
|
|
263
|
+
diagnostics=(
|
|
264
|
+
*durability_result.diagnostics,
|
|
265
|
+
_output_diagnostic(
|
|
266
|
+
code="MF-O002",
|
|
267
|
+
message="Committed diagnostics report could not be written.",
|
|
268
|
+
fields=commit_error,
|
|
269
|
+
),
|
|
270
|
+
),
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
committed_result = HarnessCompileResult(
|
|
274
|
+
request_id=request.request_id,
|
|
275
|
+
status=CompileStatus.COMMITTED,
|
|
276
|
+
plan_commit_certainty=PlanCommitCertainty.COMMITTED,
|
|
277
|
+
diagnostic_report_state=DiagnosticReportState.COMMITTED,
|
|
278
|
+
source_document_sha256=source_document_sha256,
|
|
279
|
+
source_sha256=plan.source_sha256,
|
|
280
|
+
request_identity_sha256=request_hash,
|
|
281
|
+
harness_id=plan.harness_id,
|
|
282
|
+
compiled_plan_path=plan_relpath,
|
|
283
|
+
compiled_sha256=plan.compiled_sha256,
|
|
284
|
+
diagnostics_path=diagnostics_relpath,
|
|
285
|
+
diagnostics=diagnostics,
|
|
286
|
+
)
|
|
287
|
+
commit_error = _write_diagnostics_report(
|
|
288
|
+
output_dir=output_dir,
|
|
289
|
+
filename=diagnostics_name,
|
|
290
|
+
result=committed_result,
|
|
291
|
+
)
|
|
292
|
+
if commit_error is None:
|
|
293
|
+
return committed_result
|
|
294
|
+
return HarnessCompileResult(
|
|
295
|
+
request_id=request.request_id,
|
|
296
|
+
status=CompileStatus.PREPARED,
|
|
297
|
+
plan_commit_certainty=PlanCommitCertainty.COMMITTED,
|
|
298
|
+
diagnostic_report_state=DiagnosticReportState.UNKNOWN,
|
|
299
|
+
source_document_sha256=source_document_sha256,
|
|
300
|
+
source_sha256=plan.source_sha256,
|
|
301
|
+
request_identity_sha256=request_hash,
|
|
302
|
+
harness_id=plan.harness_id,
|
|
303
|
+
compiled_plan_path=plan_relpath,
|
|
304
|
+
compiled_sha256=plan.compiled_sha256,
|
|
305
|
+
diagnostics_path=diagnostics_relpath,
|
|
306
|
+
diagnostics=(
|
|
307
|
+
*diagnostics,
|
|
308
|
+
_output_diagnostic(
|
|
309
|
+
code="MF-O002",
|
|
310
|
+
message="Committed diagnostics report could not be written.",
|
|
311
|
+
fields=commit_error,
|
|
312
|
+
),
|
|
313
|
+
),
|
|
314
|
+
)
|
|
315
|
+
finally:
|
|
316
|
+
_close_output_dir(output_dir)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class _PublishResult:
|
|
320
|
+
PUBLISHED = "published"
|
|
321
|
+
REUSED = "reused"
|
|
322
|
+
CONFLICT = "conflict"
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@dataclass(frozen=True)
|
|
326
|
+
class _PublishOutcome:
|
|
327
|
+
result: str | None = None
|
|
328
|
+
error: tuple[DiagnosticField, ...] | None = None
|
|
329
|
+
cleanup_error: tuple[DiagnosticField, ...] | None = None
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _publish_content_addressed_file(
|
|
333
|
+
*,
|
|
334
|
+
output_dir: _AdmittedOutputDir,
|
|
335
|
+
destination_name: str,
|
|
336
|
+
data: bytes,
|
|
337
|
+
) -> _PublishOutcome:
|
|
338
|
+
temp_name = _temporary_path(destination_name)
|
|
339
|
+
result: str | None = None
|
|
340
|
+
error: tuple[DiagnosticField, ...] | None = None
|
|
341
|
+
try:
|
|
342
|
+
_write_exclusive_temp(output_dir.fd, temp_name, data)
|
|
343
|
+
try:
|
|
344
|
+
os.link(
|
|
345
|
+
temp_name,
|
|
346
|
+
destination_name,
|
|
347
|
+
src_dir_fd=output_dir.fd,
|
|
348
|
+
dst_dir_fd=output_dir.fd,
|
|
349
|
+
)
|
|
350
|
+
except FileExistsError:
|
|
351
|
+
result = (
|
|
352
|
+
_PublishResult.REUSED
|
|
353
|
+
if _destination_matches(
|
|
354
|
+
output_dir=output_dir, destination_name=destination_name, data=data
|
|
355
|
+
)
|
|
356
|
+
else _PublishResult.CONFLICT
|
|
357
|
+
)
|
|
358
|
+
except OSError as exc:
|
|
359
|
+
if exc.errno == errno.EEXIST:
|
|
360
|
+
result = (
|
|
361
|
+
_PublishResult.REUSED
|
|
362
|
+
if _destination_matches(
|
|
363
|
+
output_dir=output_dir,
|
|
364
|
+
destination_name=destination_name,
|
|
365
|
+
data=data,
|
|
366
|
+
)
|
|
367
|
+
else _PublishResult.CONFLICT
|
|
368
|
+
)
|
|
369
|
+
else:
|
|
370
|
+
error = (_field("errno", exc.errno or errno.EIO),)
|
|
371
|
+
else:
|
|
372
|
+
result = _PublishResult.PUBLISHED
|
|
373
|
+
except FileExistsError as exc:
|
|
374
|
+
return _PublishOutcome(error=(_field("errno", exc.errno or errno.EEXIST),))
|
|
375
|
+
except OSError as exc:
|
|
376
|
+
error = (_field("errno", exc.errno or errno.EIO),)
|
|
377
|
+
cleanup_error = _unlink_if_present(output_dir=output_dir, filename=temp_name)
|
|
378
|
+
return _PublishOutcome(result=result, error=error, cleanup_error=cleanup_error)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def _write_diagnostics_report(
|
|
382
|
+
*,
|
|
383
|
+
output_dir: _AdmittedOutputDir,
|
|
384
|
+
filename: str,
|
|
385
|
+
result: HarnessCompileResult,
|
|
386
|
+
) -> tuple[DiagnosticField, ...] | None:
|
|
387
|
+
data = canonical_json_serialize(result.model_dump(mode="json")).encode("utf-8")
|
|
388
|
+
temp_name = _temporary_path(filename)
|
|
389
|
+
try:
|
|
390
|
+
_write_exclusive_temp(output_dir.fd, temp_name, data)
|
|
391
|
+
os.replace(
|
|
392
|
+
temp_name,
|
|
393
|
+
filename,
|
|
394
|
+
src_dir_fd=output_dir.fd,
|
|
395
|
+
dst_dir_fd=output_dir.fd,
|
|
396
|
+
)
|
|
397
|
+
if not _fsync_directory(output_dir.fd):
|
|
398
|
+
raise OSError(errno.EIO, os.strerror(errno.EIO))
|
|
399
|
+
except FileExistsError as exc:
|
|
400
|
+
return (_field("errno", exc.errno or errno.EEXIST),)
|
|
401
|
+
except OSError as exc:
|
|
402
|
+
_unlink_if_present(output_dir=output_dir, filename=temp_name)
|
|
403
|
+
return (_field("errno", exc.errno or errno.EIO),)
|
|
404
|
+
return None
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def _write_exclusive_temp(directory_fd: int, filename: str, data: bytes) -> None:
|
|
408
|
+
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
409
|
+
if hasattr(os, "O_NOFOLLOW"):
|
|
410
|
+
flags |= os.O_NOFOLLOW
|
|
411
|
+
fd = os.open(filename, flags, 0o600, dir_fd=directory_fd)
|
|
412
|
+
with os.fdopen(fd, "wb") as handle:
|
|
413
|
+
handle.write(data)
|
|
414
|
+
handle.flush()
|
|
415
|
+
os.fsync(handle.fileno())
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _admit_output_dir(
|
|
419
|
+
request: HarnessCompileRequest,
|
|
420
|
+
*,
|
|
421
|
+
request_hash: str | None = None,
|
|
422
|
+
) -> _AdmittedOutputDir | HarnessCompileResult:
|
|
423
|
+
root = Path(request.output_root)
|
|
424
|
+
output_parts = _relative_posix_parts(request.output_dir)
|
|
425
|
+
if output_parts is None:
|
|
426
|
+
return _failed_output_result(
|
|
427
|
+
request=request,
|
|
428
|
+
code="MF-O001",
|
|
429
|
+
message="Output directory must stay within output_root.",
|
|
430
|
+
request_hash=request_hash,
|
|
431
|
+
)
|
|
432
|
+
try:
|
|
433
|
+
root_resolved = root.resolve(strict=True)
|
|
434
|
+
if not stat.S_ISDIR(root_resolved.lstat().st_mode):
|
|
435
|
+
raise NotADirectoryError(request.output_root)
|
|
436
|
+
candidate = root_resolved.joinpath(*output_parts)
|
|
437
|
+
parent = candidate.parent.resolve(strict=True)
|
|
438
|
+
if not _is_relative_to(parent, root_resolved):
|
|
439
|
+
raise PermissionError(request.output_dir)
|
|
440
|
+
resolved = parent.joinpath(candidate.name)
|
|
441
|
+
before = resolved.lstat()
|
|
442
|
+
if stat.S_ISLNK(before.st_mode) or not stat.S_ISDIR(before.st_mode):
|
|
443
|
+
raise NotADirectoryError(request.output_dir)
|
|
444
|
+
resolved = resolved.resolve(strict=True)
|
|
445
|
+
if not _is_relative_to(resolved, root_resolved):
|
|
446
|
+
raise PermissionError(request.output_dir)
|
|
447
|
+
if (
|
|
448
|
+
os.open not in os.supports_dir_fd
|
|
449
|
+
or os.link not in os.supports_dir_fd
|
|
450
|
+
or os.unlink not in os.supports_dir_fd
|
|
451
|
+
or not hasattr(os, "O_DIRECTORY")
|
|
452
|
+
or not hasattr(os, "O_NOFOLLOW")
|
|
453
|
+
):
|
|
454
|
+
raise OSError(
|
|
455
|
+
errno.EOPNOTSUPP, "anchored output-directory operations unavailable"
|
|
456
|
+
)
|
|
457
|
+
flags = os.O_RDONLY | os.O_DIRECTORY
|
|
458
|
+
flags |= os.O_NOFOLLOW
|
|
459
|
+
fd = os.open(resolved, flags)
|
|
460
|
+
try:
|
|
461
|
+
opened = os.fstat(fd)
|
|
462
|
+
current = resolved.lstat()
|
|
463
|
+
if (
|
|
464
|
+
opened.st_dev != current.st_dev
|
|
465
|
+
or opened.st_ino != current.st_ino
|
|
466
|
+
or not stat.S_ISDIR(opened.st_mode)
|
|
467
|
+
or not stat.S_ISDIR(current.st_mode)
|
|
468
|
+
):
|
|
469
|
+
raise PermissionError(request.output_dir)
|
|
470
|
+
except Exception:
|
|
471
|
+
os.close(fd)
|
|
472
|
+
raise
|
|
473
|
+
return _AdmittedOutputDir(path=resolved, fd=fd)
|
|
474
|
+
except OSError as exc:
|
|
475
|
+
return _failed_output_result(
|
|
476
|
+
request=request,
|
|
477
|
+
code="MF-O001",
|
|
478
|
+
message="Output directory could not be admitted for diagnostics.",
|
|
479
|
+
request_hash=request_hash,
|
|
480
|
+
fields=(_field("errno", exc.errno or errno.EIO),),
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def _failed_output_result(
|
|
485
|
+
*,
|
|
486
|
+
request: HarnessCompileRequest,
|
|
487
|
+
code: str,
|
|
488
|
+
message: str,
|
|
489
|
+
plan: CompiledHarnessPlan | None = None,
|
|
490
|
+
request_hash: str | None = None,
|
|
491
|
+
source_document_sha256: str | None = None,
|
|
492
|
+
diagnostics: tuple[CompilerDiagnostic, ...] = (),
|
|
493
|
+
diagnostics_path: str | None = None,
|
|
494
|
+
diagnostic_report_state: DiagnosticReportState = DiagnosticReportState.ABSENT,
|
|
495
|
+
plan_relpath: str | None = None,
|
|
496
|
+
plan_certainty: PlanCommitCertainty = PlanCommitCertainty.ABSENT,
|
|
497
|
+
fields: tuple[DiagnosticField, ...] = (),
|
|
498
|
+
cleanup_error: tuple[DiagnosticField, ...] | None = None,
|
|
499
|
+
) -> HarnessCompileResult:
|
|
500
|
+
cleanup_diagnostics = (
|
|
501
|
+
(
|
|
502
|
+
_output_diagnostic(
|
|
503
|
+
code="MF-O005",
|
|
504
|
+
message="Compiled plan temporary file could not be cleaned up.",
|
|
505
|
+
fields=cleanup_error,
|
|
506
|
+
),
|
|
507
|
+
)
|
|
508
|
+
if cleanup_error is not None
|
|
509
|
+
else ()
|
|
510
|
+
)
|
|
511
|
+
return HarnessCompileResult(
|
|
512
|
+
request_id=request.request_id,
|
|
513
|
+
status=CompileStatus.FAILED,
|
|
514
|
+
plan_commit_certainty=plan_certainty,
|
|
515
|
+
diagnostic_report_state=diagnostic_report_state,
|
|
516
|
+
failure_phase=CompilerPhase.OUTPUT,
|
|
517
|
+
source_document_sha256=source_document_sha256,
|
|
518
|
+
source_sha256=None if plan is None else plan.source_sha256,
|
|
519
|
+
request_identity_sha256=request_hash,
|
|
520
|
+
harness_id=None if plan is None else plan.harness_id,
|
|
521
|
+
compiled_plan_path=plan_relpath,
|
|
522
|
+
compiled_sha256=(
|
|
523
|
+
None
|
|
524
|
+
if plan is None or plan_certainty == PlanCommitCertainty.ABSENT
|
|
525
|
+
else plan.compiled_sha256
|
|
526
|
+
),
|
|
527
|
+
diagnostics_path=diagnostics_path,
|
|
528
|
+
diagnostics=(
|
|
529
|
+
*diagnostics,
|
|
530
|
+
_output_diagnostic(code=code, message=message, fields=fields),
|
|
531
|
+
*cleanup_diagnostics,
|
|
532
|
+
),
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
def _output_diagnostic(
|
|
537
|
+
*,
|
|
538
|
+
code: str,
|
|
539
|
+
message: str,
|
|
540
|
+
fields: tuple[DiagnosticField, ...] = (),
|
|
541
|
+
) -> CompilerDiagnostic:
|
|
542
|
+
return CompilerDiagnostic(
|
|
543
|
+
code=code,
|
|
544
|
+
severity=DiagnosticSeverity.ERROR,
|
|
545
|
+
phase=CompilerPhase.OUTPUT,
|
|
546
|
+
message=message,
|
|
547
|
+
fields=fields,
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _field(key: str, value: str | int | float | bool) -> DiagnosticField:
|
|
552
|
+
return DiagnosticField(key=key, value=value)
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def _destination_matches(
|
|
556
|
+
*,
|
|
557
|
+
output_dir: _AdmittedOutputDir,
|
|
558
|
+
destination_name: str,
|
|
559
|
+
data: bytes,
|
|
560
|
+
) -> bool:
|
|
561
|
+
try:
|
|
562
|
+
flags = os.O_RDONLY
|
|
563
|
+
if hasattr(os, "O_NOFOLLOW"):
|
|
564
|
+
flags |= os.O_NOFOLLOW
|
|
565
|
+
fd = os.open(destination_name, flags, dir_fd=output_dir.fd)
|
|
566
|
+
except OSError:
|
|
567
|
+
return False
|
|
568
|
+
try:
|
|
569
|
+
with os.fdopen(fd, "rb") as handle:
|
|
570
|
+
return handle.read() == data
|
|
571
|
+
except OSError:
|
|
572
|
+
return False
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def _fsync_directory(directory: Path | int) -> bool:
|
|
576
|
+
if isinstance(directory, int):
|
|
577
|
+
try:
|
|
578
|
+
os.fsync(directory)
|
|
579
|
+
except OSError:
|
|
580
|
+
return False
|
|
581
|
+
return True
|
|
582
|
+
if not hasattr(os, "O_DIRECTORY"):
|
|
583
|
+
return True
|
|
584
|
+
try:
|
|
585
|
+
fd = os.open(directory, os.O_RDONLY | os.O_DIRECTORY)
|
|
586
|
+
except OSError:
|
|
587
|
+
return False
|
|
588
|
+
try:
|
|
589
|
+
os.fsync(fd)
|
|
590
|
+
except OSError:
|
|
591
|
+
return False
|
|
592
|
+
finally:
|
|
593
|
+
os.close(fd)
|
|
594
|
+
return True
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
def _temporary_path(destination_name: str) -> str:
|
|
598
|
+
return f".{destination_name}.{secrets.token_hex(8)}.tmp"
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def _unlink_if_present(
|
|
602
|
+
*, output_dir: _AdmittedOutputDir, filename: str
|
|
603
|
+
) -> tuple[DiagnosticField, ...] | None:
|
|
604
|
+
try:
|
|
605
|
+
os.unlink(filename, dir_fd=output_dir.fd)
|
|
606
|
+
except FileNotFoundError:
|
|
607
|
+
return None
|
|
608
|
+
except OSError as exc:
|
|
609
|
+
return (_field("errno", exc.errno or errno.EIO),)
|
|
610
|
+
return None
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
def _join_output_path(output_dir: str, filename: str) -> str:
|
|
614
|
+
value = f"{PurePosixPath(output_dir).as_posix().rstrip('/')}/{filename}"
|
|
615
|
+
return validate_utf8_size(value, "output path", 1024)
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def _safe_harness_identity(value: str) -> str:
|
|
619
|
+
return validate_utf8_size(
|
|
620
|
+
quote(value, safe=_HARNESS_ID_FILENAME_SAFE),
|
|
621
|
+
"harness identity",
|
|
622
|
+
320,
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def _harness_identity_prefix(
|
|
627
|
+
*,
|
|
628
|
+
harness_id: str | None,
|
|
629
|
+
harness_version: int | None,
|
|
630
|
+
) -> str:
|
|
631
|
+
if harness_id is None or harness_version is None:
|
|
632
|
+
raise ValueError("harness diagnostics paths require identity and version")
|
|
633
|
+
if harness_version <= 0:
|
|
634
|
+
raise ValueError("harness_version must be positive")
|
|
635
|
+
return f"{_safe_harness_identity(harness_id)}@{harness_version}"
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
def _relative_posix_parts(value: str) -> tuple[str, ...] | None:
|
|
639
|
+
path = PurePosixPath(value)
|
|
640
|
+
if path.is_absolute() or value.startswith("/") or "\\" in value:
|
|
641
|
+
return None
|
|
642
|
+
parts = path.parts
|
|
643
|
+
if not parts or any(_is_forbidden_path_part(part) for part in parts):
|
|
644
|
+
return None
|
|
645
|
+
return parts
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def _is_forbidden_path_part(part: str) -> bool:
|
|
649
|
+
reserved_names = {
|
|
650
|
+
"con",
|
|
651
|
+
"prn",
|
|
652
|
+
"aux",
|
|
653
|
+
"nul",
|
|
654
|
+
*(f"com{index}" for index in range(1, 10)),
|
|
655
|
+
*(f"lpt{index}" for index in range(1, 10)),
|
|
656
|
+
}
|
|
657
|
+
stem = part.split(".", 1)[0].casefold()
|
|
658
|
+
return (
|
|
659
|
+
part in {"", ".", ".."}
|
|
660
|
+
or ":" in part
|
|
661
|
+
or "\x00" in part
|
|
662
|
+
or stem in reserved_names
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def _is_relative_to(path: Path, root: Path) -> bool:
|
|
667
|
+
try:
|
|
668
|
+
path.relative_to(root)
|
|
669
|
+
return True
|
|
670
|
+
except ValueError:
|
|
671
|
+
return False
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def _admitted_output_dir_matches(output_dir: _AdmittedOutputDir) -> bool:
|
|
675
|
+
try:
|
|
676
|
+
current = output_dir.path.lstat()
|
|
677
|
+
admitted = os.fstat(output_dir.fd)
|
|
678
|
+
except OSError:
|
|
679
|
+
return False
|
|
680
|
+
return (
|
|
681
|
+
stat.S_ISDIR(current.st_mode)
|
|
682
|
+
and stat.S_ISDIR(admitted.st_mode)
|
|
683
|
+
and current.st_dev == admitted.st_dev
|
|
684
|
+
and current.st_ino == admitted.st_ino
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def _close_output_dir(output_dir: _AdmittedOutputDir) -> None:
|
|
689
|
+
try:
|
|
690
|
+
os.close(output_dir.fd)
|
|
691
|
+
except OSError:
|
|
692
|
+
return
|