validibot-shared 0.17.0__tar.gz → 0.18.0__tar.gz
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.
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/PKG-INFO +1 -1
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/pyproject.toml +1 -1
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_evidence_manifest.py +100 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/__init__.py +6 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/evidence/__init__.py +6 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/evidence/manifest.py +197 -4
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/.gitignore +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/LICENSE +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/NOTICE +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/README.md +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/__init__.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_artifacts.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_callback_nonce_contract.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_canonicalization.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_energyplus_envelopes.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_energyplus_models.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_fmu_envelopes.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_fmu_models.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_package_init.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_schematron_envelopes.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_schematron_svrl.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_shacl_envelopes.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/tests/test_validations_envelopes.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/_vendor/__init__.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/_vendor/jcs.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/canonicalization.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/energyplus/__init__.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/energyplus/envelopes.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/energyplus/models.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/fmu/__init__.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/fmu/envelopes.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/fmu/models.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/py.typed +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/schematron/__init__.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/schematron/envelopes.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/schematron/svrl.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/shacl/__init__.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/shacl/envelopes.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/validations/__init__.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/validations/artifacts.py +0 -0
- {validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/validations/envelopes.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: validibot-shared
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.0
|
|
4
4
|
Summary: Shared library for data interchange between Validibot and validator containers
|
|
5
5
|
Project-URL: Homepage, https://validibot.com
|
|
6
6
|
Project-URL: Documentation, https://docs.validibot.com
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "validibot-shared"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.18.0"
|
|
8
8
|
description = "Shared library for data interchange between Validibot and validator containers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -19,6 +19,8 @@ What this file covers
|
|
|
19
19
|
5. Artifact lineage fields are additive, evidence-safe projections:
|
|
20
20
|
hashes and stable IDs are allowed; private storage URIs are not part
|
|
21
21
|
of the public schema.
|
|
22
|
+
6. Attempt-bound execution evidence records canonical envelopes, verified
|
|
23
|
+
input identities, and explicit preprocessing relationships without URIs.
|
|
22
24
|
"""
|
|
23
25
|
|
|
24
26
|
from __future__ import annotations
|
|
@@ -33,6 +35,9 @@ from validibot_shared.evidence import (
|
|
|
33
35
|
EvidenceManifest,
|
|
34
36
|
ManifestArtifactInputBinding,
|
|
35
37
|
ManifestArtifactLineageEdge,
|
|
38
|
+
ManifestExecutionAttempt,
|
|
39
|
+
ManifestExecutionInput,
|
|
40
|
+
ManifestInputRelationship,
|
|
36
41
|
ManifestPayloadDigests,
|
|
37
42
|
ManifestProducedArtifact,
|
|
38
43
|
ManifestRetentionInfo,
|
|
@@ -207,6 +212,7 @@ class TestArtifactLineageFields:
|
|
|
207
212
|
contract_key="generated_model",
|
|
208
213
|
filename="model.epjson",
|
|
209
214
|
sha256="a" * 64,
|
|
215
|
+
storage_version="42",
|
|
210
216
|
)
|
|
211
217
|
binding = ManifestArtifactInputBinding(
|
|
212
218
|
target_step_id=20,
|
|
@@ -217,6 +223,7 @@ class TestArtifactLineageFields:
|
|
|
217
223
|
source_artifact_id="artifact-1",
|
|
218
224
|
source_filename="model.epjson",
|
|
219
225
|
source_sha256="a" * 64,
|
|
226
|
+
source_storage_version="42",
|
|
220
227
|
producer_step_key="build_model",
|
|
221
228
|
producer_contract_key="generated_model",
|
|
222
229
|
resolved=True,
|
|
@@ -245,6 +252,99 @@ class TestArtifactLineageFields:
|
|
|
245
252
|
assert not hasattr(restored.artifact_input_bindings[0], "uri")
|
|
246
253
|
|
|
247
254
|
|
|
255
|
+
# ──────────────────────────────────────────────────────────────────────
|
|
256
|
+
# Attempt-bound execution evidence — immutable files and transformations
|
|
257
|
+
# ──────────────────────────────────────────────────────────────────────
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class TestExecutionAttemptEvidence:
|
|
261
|
+
"""Pin the additive strict-execution evidence contract for verifiers."""
|
|
262
|
+
|
|
263
|
+
def test_execution_attempts_default_to_empty(self):
|
|
264
|
+
"""Inline validators and older producers remain valid without attempts."""
|
|
265
|
+
manifest = EvidenceManifest(**_minimal_manifest_kwargs())
|
|
266
|
+
|
|
267
|
+
assert manifest.execution_attempts == []
|
|
268
|
+
assert manifest.schema_version == "validibot.evidence.v1"
|
|
269
|
+
|
|
270
|
+
def test_attempt_records_round_trip_without_storage_uris(self):
|
|
271
|
+
"""Portable evidence keeps immutable identities but exposes no file URI."""
|
|
272
|
+
execution_input = ManifestExecutionInput(
|
|
273
|
+
channel="input_files",
|
|
274
|
+
name="generated.epjson",
|
|
275
|
+
role="primary-model",
|
|
276
|
+
port_key="primary_model",
|
|
277
|
+
media_type="application/json",
|
|
278
|
+
size_bytes=123,
|
|
279
|
+
sha256="b" * 64,
|
|
280
|
+
storage_version="sha256:" + "b" * 64,
|
|
281
|
+
)
|
|
282
|
+
relationship = ManifestInputRelationship(
|
|
283
|
+
source_kind="submission",
|
|
284
|
+
source_id="submission-1",
|
|
285
|
+
source_name="template.epjson",
|
|
286
|
+
source_size_bytes=100,
|
|
287
|
+
source_sha256="a" * 64,
|
|
288
|
+
target_channel="input_files",
|
|
289
|
+
target_name="generated.epjson",
|
|
290
|
+
target_port_key="primary_model",
|
|
291
|
+
target_sha256="b" * 64,
|
|
292
|
+
relationship="transformed",
|
|
293
|
+
transformation="energyplus-template-substitution.v1",
|
|
294
|
+
)
|
|
295
|
+
attempt = ManifestExecutionAttempt(
|
|
296
|
+
execution_attempt_id="attempt-1",
|
|
297
|
+
step_run_id="step-run-1",
|
|
298
|
+
attempt_number=1,
|
|
299
|
+
state="COMPLETED",
|
|
300
|
+
runner_type="cloud_run_job",
|
|
301
|
+
provider_execution_id="execution-1",
|
|
302
|
+
attempt_contract_version="validibot.attempt.v2",
|
|
303
|
+
input_envelope_sha256="c" * 64,
|
|
304
|
+
output_envelope_sha256="d" * 64,
|
|
305
|
+
backend_image_digest="registry/energyplus@sha256:" + "e" * 64,
|
|
306
|
+
inputs_verified=True,
|
|
307
|
+
input_files=[execution_input],
|
|
308
|
+
input_relationships=[relationship],
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
manifest = EvidenceManifest(
|
|
312
|
+
**_minimal_manifest_kwargs(),
|
|
313
|
+
execution_attempts=[attempt],
|
|
314
|
+
)
|
|
315
|
+
restored = EvidenceManifest.model_validate(manifest.model_dump(mode="json"))
|
|
316
|
+
|
|
317
|
+
assert restored == manifest
|
|
318
|
+
assert restored.execution_attempts[0].inputs_verified is True
|
|
319
|
+
assert not hasattr(restored.execution_attempts[0].input_files[0], "uri")
|
|
320
|
+
|
|
321
|
+
def test_relationship_keeps_original_and_executed_digests_distinct(self):
|
|
322
|
+
"""Preprocessing evidence cannot imply original bytes were executed."""
|
|
323
|
+
relationship = ManifestInputRelationship(
|
|
324
|
+
source_kind="submission",
|
|
325
|
+
source_sha256="a" * 64,
|
|
326
|
+
target_channel="input_files",
|
|
327
|
+
target_name="generated.idf",
|
|
328
|
+
target_sha256="b" * 64,
|
|
329
|
+
relationship="transformed",
|
|
330
|
+
transformation="energyplus-template-substitution.v1",
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
assert relationship.source_sha256 != relationship.target_sha256
|
|
334
|
+
assert relationship.relationship == "transformed"
|
|
335
|
+
|
|
336
|
+
def test_execution_input_rejects_invalid_integrity_identity(self):
|
|
337
|
+
"""Evidence cannot accept an invalid digest or negative byte length."""
|
|
338
|
+
with pytest.raises(ValidationError):
|
|
339
|
+
ManifestExecutionInput(
|
|
340
|
+
channel="input_files",
|
|
341
|
+
name="model.idf",
|
|
342
|
+
size_bytes=-1,
|
|
343
|
+
sha256="not-a-sha256",
|
|
344
|
+
storage_version="generation-1",
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
248
348
|
# ──────────────────────────────────────────────────────────────────────
|
|
249
349
|
# WorkflowContractSnapshot — constants, signal-mapping defs, definition hash
|
|
250
350
|
# ──────────────────────────────────────────────────────────────────────
|
|
@@ -19,6 +19,9 @@ from validibot_shared.evidence import (
|
|
|
19
19
|
ContractConstant,
|
|
20
20
|
ContractSignalMapping,
|
|
21
21
|
EvidenceManifest,
|
|
22
|
+
ManifestExecutionAttempt,
|
|
23
|
+
ManifestExecutionInput,
|
|
24
|
+
ManifestInputRelationship,
|
|
22
25
|
ManifestPayloadDigests,
|
|
23
26
|
ManifestRetentionInfo,
|
|
24
27
|
StepValidatorRecord,
|
|
@@ -47,6 +50,9 @@ __all__ = [
|
|
|
47
50
|
"FMUProbeResult",
|
|
48
51
|
"FMUVariableMeta",
|
|
49
52
|
"ManifestPayloadDigests",
|
|
53
|
+
"ManifestExecutionAttempt",
|
|
54
|
+
"ManifestExecutionInput",
|
|
55
|
+
"ManifestInputRelationship",
|
|
50
56
|
"ManifestRetentionInfo",
|
|
51
57
|
"SHACLInputEnvelope",
|
|
52
58
|
"SHACLInputs",
|
|
@@ -14,6 +14,9 @@ from validibot_shared.evidence.manifest import (
|
|
|
14
14
|
EvidenceManifest,
|
|
15
15
|
ManifestArtifactInputBinding,
|
|
16
16
|
ManifestArtifactLineageEdge,
|
|
17
|
+
ManifestExecutionAttempt,
|
|
18
|
+
ManifestExecutionInput,
|
|
19
|
+
ManifestInputRelationship,
|
|
17
20
|
ManifestPayloadDigests,
|
|
18
21
|
ManifestProducedArtifact,
|
|
19
22
|
ManifestRetentionInfo,
|
|
@@ -28,6 +31,9 @@ __all__ = [
|
|
|
28
31
|
"EvidenceManifest",
|
|
29
32
|
"ManifestArtifactInputBinding",
|
|
30
33
|
"ManifestArtifactLineageEdge",
|
|
34
|
+
"ManifestExecutionAttempt",
|
|
35
|
+
"ManifestExecutionInput",
|
|
36
|
+
"ManifestInputRelationship",
|
|
31
37
|
"ManifestPayloadDigests",
|
|
32
38
|
"ManifestProducedArtifact",
|
|
33
39
|
"ManifestRetentionInfo",
|
|
@@ -252,10 +252,11 @@ class ManifestPayloadDigests(BaseModel):
|
|
|
252
252
|
input_sha256: str | None = Field(
|
|
253
253
|
default=None,
|
|
254
254
|
description=(
|
|
255
|
-
"SHA-256 hex of the submission's primary file bytes
|
|
256
|
-
"
|
|
257
|
-
"DO_NOT_STORE purges the bytes themselves
|
|
258
|
-
"the
|
|
255
|
+
"SHA-256 hex of the submission's primary file bytes accepted by "
|
|
256
|
+
"the producer. Always present in Session B, even when "
|
|
257
|
+
"DO_NOT_STORE purges the bytes themselves. This does not by "
|
|
258
|
+
"itself claim the same bytes crossed the execution boundary; "
|
|
259
|
+
"execution_attempts records that identity and any transformation."
|
|
259
260
|
),
|
|
260
261
|
)
|
|
261
262
|
output_envelope_sha256: str | None = Field(
|
|
@@ -309,6 +310,13 @@ class ManifestProducedArtifact(BaseModel):
|
|
|
309
310
|
data_format: str = Field(default="", description="Domain data format.")
|
|
310
311
|
size_bytes: int | None = Field(default=None, description="Artifact byte size.")
|
|
311
312
|
sha256: str = Field(default="", description="SHA-256 of artifact bytes.")
|
|
313
|
+
storage_version: str = Field(
|
|
314
|
+
default="",
|
|
315
|
+
description=(
|
|
316
|
+
"Provider-specific immutable storage version verified for the "
|
|
317
|
+
"artifact. Empty for producers predating strict artifact identity."
|
|
318
|
+
),
|
|
319
|
+
)
|
|
312
320
|
manifest_sha256: str = Field(
|
|
313
321
|
default="",
|
|
314
322
|
description="SHA-256 of the validator output manifest, if present.",
|
|
@@ -390,6 +398,13 @@ class ManifestArtifactInputBinding(BaseModel):
|
|
|
390
398
|
)
|
|
391
399
|
source_size_bytes: int | None = Field(default=None)
|
|
392
400
|
source_sha256: str = Field(default="", description="Resolved source SHA-256.")
|
|
401
|
+
source_storage_version: str = Field(
|
|
402
|
+
default="",
|
|
403
|
+
description=(
|
|
404
|
+
"Immutable storage version used for this binding. Empty for "
|
|
405
|
+
"unresolved inputs or producers predating strict file identity."
|
|
406
|
+
),
|
|
407
|
+
)
|
|
393
408
|
producer_step_key: str = Field(
|
|
394
409
|
default="",
|
|
395
410
|
description="Producer step key for upstream_artifact bindings.",
|
|
@@ -421,6 +436,173 @@ class ManifestArtifactLineageEdge(BaseModel):
|
|
|
421
436
|
target_step_run_id: str = Field(default="", description="Consumer step-run id.")
|
|
422
437
|
|
|
423
438
|
|
|
439
|
+
class ManifestExecutionInput(BaseModel):
|
|
440
|
+
"""URI-free identity of one file committed to an execution envelope.
|
|
441
|
+
|
|
442
|
+
The trusted producer copies these fields from the immutable input envelope
|
|
443
|
+
bound to an execution attempt. The strict runtime contract verifies the
|
|
444
|
+
exact size, digest, and storage version while streaming before domain
|
|
445
|
+
processing. ``ManifestExecutionAttempt.inputs_verified`` tells consumers
|
|
446
|
+
whether trusted completion confirmed that verification boundary was crossed.
|
|
447
|
+
"""
|
|
448
|
+
|
|
449
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
450
|
+
|
|
451
|
+
channel: Literal["input_files", "resource_files"] = Field(
|
|
452
|
+
description="Input-envelope collection that carried this file.",
|
|
453
|
+
)
|
|
454
|
+
name: str = Field(description="Safe logical leaf name from the input envelope.")
|
|
455
|
+
role: str = Field(
|
|
456
|
+
default="",
|
|
457
|
+
description="Validator-facing role for an input file.",
|
|
458
|
+
)
|
|
459
|
+
resource_type: str = Field(
|
|
460
|
+
default="",
|
|
461
|
+
description="Validator-facing resource type for a resource file.",
|
|
462
|
+
)
|
|
463
|
+
port_key: str = Field(
|
|
464
|
+
default="",
|
|
465
|
+
description="Stable Validibot file-port key, when declared.",
|
|
466
|
+
)
|
|
467
|
+
resource_id: str = Field(
|
|
468
|
+
default="",
|
|
469
|
+
description="Producer-side resource identifier, for resource files.",
|
|
470
|
+
)
|
|
471
|
+
media_type: str = Field(
|
|
472
|
+
default="",
|
|
473
|
+
description="Declared media type, when the envelope supplies one.",
|
|
474
|
+
)
|
|
475
|
+
size_bytes: int = Field(
|
|
476
|
+
ge=0,
|
|
477
|
+
description="Exact byte size enforced by the runtime's streaming read.",
|
|
478
|
+
)
|
|
479
|
+
sha256: str = Field(
|
|
480
|
+
pattern=r"^[0-9a-f]{64}$",
|
|
481
|
+
description="Lowercase SHA-256 of the exact execution-boundary bytes.",
|
|
482
|
+
)
|
|
483
|
+
storage_version: str = Field(
|
|
484
|
+
min_length=1,
|
|
485
|
+
max_length=512,
|
|
486
|
+
description="Provider-specific immutable object/version identity.",
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
class ManifestInputRelationship(BaseModel):
|
|
491
|
+
"""Relationship between source bytes and an execution-envelope file.
|
|
492
|
+
|
|
493
|
+
This makes preprocessing explicit. For example, an EnergyPlus template can
|
|
494
|
+
be recorded as the source while the generated IDF/epJSON file is the target,
|
|
495
|
+
preventing evidence from implying the original template bytes were executed.
|
|
496
|
+
"""
|
|
497
|
+
|
|
498
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
499
|
+
|
|
500
|
+
source_kind: str = Field(
|
|
501
|
+
description=(
|
|
502
|
+
"Source category, such as submission, workflow_resource, "
|
|
503
|
+
"upstream_artifact, or generated."
|
|
504
|
+
),
|
|
505
|
+
)
|
|
506
|
+
source_id: str = Field(
|
|
507
|
+
default="",
|
|
508
|
+
description="Stable producer-side source identifier, when available.",
|
|
509
|
+
)
|
|
510
|
+
source_name: str = Field(
|
|
511
|
+
default="",
|
|
512
|
+
description="Evidence-safe source filename or logical name.",
|
|
513
|
+
)
|
|
514
|
+
source_size_bytes: int | None = Field(
|
|
515
|
+
default=None,
|
|
516
|
+
ge=0,
|
|
517
|
+
description="Exact source size, when durably known.",
|
|
518
|
+
)
|
|
519
|
+
source_sha256: str = Field(
|
|
520
|
+
pattern=r"^[0-9a-f]{64}$",
|
|
521
|
+
description="Lowercase SHA-256 of the source bytes.",
|
|
522
|
+
)
|
|
523
|
+
target_channel: Literal["input_files", "resource_files"] = Field(
|
|
524
|
+
description="Input-envelope collection containing the target file.",
|
|
525
|
+
)
|
|
526
|
+
target_name: str = Field(description="Target file's safe logical name.")
|
|
527
|
+
target_port_key: str = Field(
|
|
528
|
+
default="",
|
|
529
|
+
description="Stable file-port key that disambiguates the target.",
|
|
530
|
+
)
|
|
531
|
+
target_sha256: str = Field(
|
|
532
|
+
pattern=r"^[0-9a-f]{64}$",
|
|
533
|
+
description="Lowercase SHA-256 of the execution-boundary target bytes.",
|
|
534
|
+
)
|
|
535
|
+
relationship: str = Field(
|
|
536
|
+
description="Explicit relationship, such as identical or transformed.",
|
|
537
|
+
)
|
|
538
|
+
transformation: str = Field(
|
|
539
|
+
default="",
|
|
540
|
+
description=(
|
|
541
|
+
"Stable transformation identifier when relationship is transformed."
|
|
542
|
+
),
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
class ManifestExecutionAttempt(BaseModel):
|
|
547
|
+
"""Attempt-bound execution identity projected without storage URIs.
|
|
548
|
+
|
|
549
|
+
Only attempts with a committed canonical input-envelope digest belong in
|
|
550
|
+
this list. Failed retries may therefore appear, but an attempt must not set
|
|
551
|
+
``inputs_verified`` merely because files were declared. The producer sets it
|
|
552
|
+
only after trusted completion confirms the strict backend crossed the input
|
|
553
|
+
verification boundary.
|
|
554
|
+
"""
|
|
555
|
+
|
|
556
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
557
|
+
|
|
558
|
+
execution_attempt_id: str = Field(description="ExecutionAttempt UUID.")
|
|
559
|
+
step_run_id: str = Field(description="Step-run identity executed by the attempt.")
|
|
560
|
+
attempt_number: int = Field(
|
|
561
|
+
ge=1,
|
|
562
|
+
description="Monotonic attempt number within the step run.",
|
|
563
|
+
)
|
|
564
|
+
state: str = Field(description="Durable terminal attempt state at evidence time.")
|
|
565
|
+
runner_type: str = Field(description="Execution provider/runner type.")
|
|
566
|
+
provider_execution_id: str = Field(
|
|
567
|
+
default="",
|
|
568
|
+
description="Provider-assigned execution identity, when available.",
|
|
569
|
+
)
|
|
570
|
+
attempt_contract_version: str = Field(
|
|
571
|
+
description="Strict attempt contract version used by the input envelope.",
|
|
572
|
+
)
|
|
573
|
+
input_envelope_sha256: str = Field(
|
|
574
|
+
pattern=r"^[0-9a-f]{64}$",
|
|
575
|
+
description="Canonical SHA-256 committed before provider execution.",
|
|
576
|
+
)
|
|
577
|
+
output_envelope_sha256: str | None = Field(
|
|
578
|
+
default=None,
|
|
579
|
+
pattern=r"^[0-9a-f]{64}$",
|
|
580
|
+
description=(
|
|
581
|
+
"Canonical trusted output-envelope SHA-256, when produced and "
|
|
582
|
+
"permitted by retention policy."
|
|
583
|
+
),
|
|
584
|
+
)
|
|
585
|
+
backend_image_digest: str = Field(
|
|
586
|
+
default="",
|
|
587
|
+
description="Resolved digest of the backend image used by this attempt.",
|
|
588
|
+
)
|
|
589
|
+
inputs_verified: bool = Field(
|
|
590
|
+
default=False,
|
|
591
|
+
description=(
|
|
592
|
+
"Whether trusted completion confirms strict streaming verification "
|
|
593
|
+
"completed before domain processing."
|
|
594
|
+
),
|
|
595
|
+
)
|
|
596
|
+
input_files: list[ManifestExecutionInput] = Field(
|
|
597
|
+
default_factory=list,
|
|
598
|
+
description="URI-free file identities copied from the committed envelope.",
|
|
599
|
+
)
|
|
600
|
+
input_relationships: list[ManifestInputRelationship] = Field(
|
|
601
|
+
default_factory=list,
|
|
602
|
+
description="Source-to-execution relationships, including preprocessing.",
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
|
|
424
606
|
class EvidenceManifest(BaseModel):
|
|
425
607
|
"""Top-level evidence manifest for a single validation run.
|
|
426
608
|
|
|
@@ -498,6 +680,14 @@ class EvidenceManifest(BaseModel):
|
|
|
498
680
|
"artifact_input_bindings but do not create producer-step edges."
|
|
499
681
|
),
|
|
500
682
|
)
|
|
683
|
+
execution_attempts: list[ManifestExecutionAttempt] = Field(
|
|
684
|
+
default_factory=list,
|
|
685
|
+
description=(
|
|
686
|
+
"Attempt-bound canonical envelope, provider, backend image, verified "
|
|
687
|
+
"file, and preprocessing evidence. Empty for inline validators or "
|
|
688
|
+
"producers predating strict execution evidence."
|
|
689
|
+
),
|
|
690
|
+
)
|
|
501
691
|
# The auth channel that initiated the run. Pinning it in the
|
|
502
692
|
# manifest lets verifiers answer "what surface produced this
|
|
503
693
|
# run?" without consulting the producer database (the run row
|
|
@@ -525,6 +715,9 @@ __all__ = [
|
|
|
525
715
|
"EvidenceManifest",
|
|
526
716
|
"ManifestArtifactInputBinding",
|
|
527
717
|
"ManifestArtifactLineageEdge",
|
|
718
|
+
"ManifestExecutionAttempt",
|
|
719
|
+
"ManifestExecutionInput",
|
|
720
|
+
"ManifestInputRelationship",
|
|
528
721
|
"ManifestPayloadDigests",
|
|
529
722
|
"ManifestProducedArtifact",
|
|
530
723
|
"ManifestRetentionInfo",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/energyplus/envelopes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/schematron/envelopes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/validations/__init__.py
RENAMED
|
File without changes
|
{validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/validations/artifacts.py
RENAMED
|
File without changes
|
{validibot_shared-0.17.0 → validibot_shared-0.18.0}/validibot_shared/validations/envelopes.py
RENAMED
|
File without changes
|