validibot-shared 0.9.2__tar.gz → 0.10.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.9.2 → validibot_shared-0.10.0}/PKG-INFO +3 -3
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/pyproject.toml +2 -2
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/tests/test_evidence_manifest.py +112 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/__init__.py +4 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/evidence/__init__.py +4 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/evidence/manifest.py +71 -1
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/.gitignore +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/LICENSE +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/NOTICE +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/README.md +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/tests/__init__.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/tests/test_energyplus_envelopes.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/tests/test_energyplus_models.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/tests/test_fmu_envelopes.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/tests/test_fmu_models.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/tests/test_package_init.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/tests/test_shacl_envelopes.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/tests/test_validations_envelopes.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/energyplus/__init__.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/energyplus/envelopes.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/energyplus/models.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/fmu/__init__.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/fmu/envelopes.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/fmu/models.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/py.typed +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/shacl/__init__.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/shacl/envelopes.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/validations/__init__.py +0 -0
- {validibot_shared-0.9.2 → validibot_shared-0.10.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.10.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
|
|
@@ -26,8 +26,8 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
26
26
|
Requires-Python: >=3.10
|
|
27
27
|
Requires-Dist: pydantic<3.0,>=2.13
|
|
28
28
|
Provides-Extra: dev
|
|
29
|
-
Requires-Dist: pytest==9.
|
|
30
|
-
Requires-Dist: ruff==0.15.
|
|
29
|
+
Requires-Dist: pytest==9.1.1; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff==0.15.18; extra == 'dev'
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
32
32
|
|
|
33
33
|
<div align="center">
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "validibot-shared"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.10.0"
|
|
8
8
|
description = "Shared library for data interchange between Validibot and validator containers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -37,7 +37,7 @@ classifiers = [
|
|
|
37
37
|
dependencies = ["pydantic>=2.13,<3.0"]
|
|
38
38
|
|
|
39
39
|
[project.optional-dependencies]
|
|
40
|
-
dev = ["pytest==9.
|
|
40
|
+
dev = ["pytest==9.1.1", "ruff==0.15.18"]
|
|
41
41
|
|
|
42
42
|
[project.urls]
|
|
43
43
|
Homepage = "https://validibot.com"
|
|
@@ -25,6 +25,8 @@ from pydantic import ValidationError
|
|
|
25
25
|
|
|
26
26
|
from validibot_shared.evidence import (
|
|
27
27
|
SCHEMA_VERSION,
|
|
28
|
+
ContractConstant,
|
|
29
|
+
ContractSignalMapping,
|
|
28
30
|
EvidenceManifest,
|
|
29
31
|
ManifestPayloadDigests,
|
|
30
32
|
ManifestRetentionInfo,
|
|
@@ -165,6 +167,116 @@ class TestOptionalFields:
|
|
|
165
167
|
assert manifest.schema_version == "validibot.evidence.v1"
|
|
166
168
|
|
|
167
169
|
|
|
170
|
+
# ──────────────────────────────────────────────────────────────────────
|
|
171
|
+
# WorkflowContractSnapshot — constants, signal-mapping defs, definition hash
|
|
172
|
+
# ──────────────────────────────────────────────────────────────────────
|
|
173
|
+
#
|
|
174
|
+
# ADR-2026-06-18. These three fields are additive and optional: a producer
|
|
175
|
+
# predating them (or a workflow with none) leaves them empty, and the schema
|
|
176
|
+
# stays ``validibot.evidence.v1``. Recording constants + signal-mapping
|
|
177
|
+
# *definitions* + the definition hash is the transparency half of the constants
|
|
178
|
+
# feature — it lets EVERY run (not just Pro-signed ones) carry "checked against
|
|
179
|
+
# these constants". Resolved ``s.*`` values are deliberately NOT here (they are
|
|
180
|
+
# submission-derived and retention-gated); only the definition is.
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class TestWorkflowContractPrimitives:
|
|
184
|
+
def test_new_fields_default_to_empty(self):
|
|
185
|
+
"""A producer predating this change (or a bare workflow) leaves them empty.
|
|
186
|
+
|
|
187
|
+
Back-compat is the whole reason these are additive-optional: an older
|
|
188
|
+
manifest that never set them must still validate.
|
|
189
|
+
"""
|
|
190
|
+
snap = WorkflowContractSnapshot(allowed_file_types=["json"])
|
|
191
|
+
assert snap.constants == []
|
|
192
|
+
assert snap.signal_mappings == []
|
|
193
|
+
assert snap.workflow_definition_hash == ""
|
|
194
|
+
|
|
195
|
+
def test_constant_value_is_preserved_verbatim(self):
|
|
196
|
+
"""A NUMBER constant's decimal string is stored exactly (no float coercion).
|
|
197
|
+
|
|
198
|
+
The attested precision (``"0.40"``) must survive into the snapshot, or the
|
|
199
|
+
credential's "checked against c.energy_price = 0.40" claim would drift
|
|
200
|
+
from the value actually recorded.
|
|
201
|
+
"""
|
|
202
|
+
snap = WorkflowContractSnapshot(
|
|
203
|
+
constants=[
|
|
204
|
+
ContractConstant(
|
|
205
|
+
name="energy_price",
|
|
206
|
+
data_type="NUMBER",
|
|
207
|
+
value="0.40",
|
|
208
|
+
),
|
|
209
|
+
ContractConstant(
|
|
210
|
+
name="allowed_currencies",
|
|
211
|
+
data_type="LIST",
|
|
212
|
+
value=["EUR", "GBP"],
|
|
213
|
+
),
|
|
214
|
+
],
|
|
215
|
+
)
|
|
216
|
+
assert snap.constants[0].value == "0.40"
|
|
217
|
+
assert snap.constants[1].value == ["EUR", "GBP"]
|
|
218
|
+
|
|
219
|
+
def test_signal_mapping_records_definition_not_value(self):
|
|
220
|
+
"""The snapshot carries a signal's DEFINITION, never a resolved value.
|
|
221
|
+
|
|
222
|
+
Publishing ``source_path``/``on_missing``/``default`` is safe; a resolved
|
|
223
|
+
``s.*`` value is submission-derived and must not appear in the
|
|
224
|
+
always-publishable contract.
|
|
225
|
+
"""
|
|
226
|
+
snap = WorkflowContractSnapshot(
|
|
227
|
+
signal_mappings=[
|
|
228
|
+
ContractSignalMapping(
|
|
229
|
+
name="reported_total",
|
|
230
|
+
source_path="$.total",
|
|
231
|
+
on_missing="error",
|
|
232
|
+
),
|
|
233
|
+
],
|
|
234
|
+
)
|
|
235
|
+
mapping = snap.signal_mappings[0]
|
|
236
|
+
assert mapping.name == "reported_total"
|
|
237
|
+
assert mapping.source_path == "$.total"
|
|
238
|
+
# There is no place to smuggle a resolved value in — the model has no
|
|
239
|
+
# such field.
|
|
240
|
+
assert not hasattr(mapping, "resolved_value")
|
|
241
|
+
|
|
242
|
+
def test_primitives_survive_json_round_trip(self):
|
|
243
|
+
"""The fields serialize and re-validate identically (canonical evidence).
|
|
244
|
+
|
|
245
|
+
The manifest is hashed and signed as JSON, so the new fields must
|
|
246
|
+
round-trip through ``model_dump``/``model_validate`` unchanged.
|
|
247
|
+
"""
|
|
248
|
+
snap = WorkflowContractSnapshot(
|
|
249
|
+
constants=[ContractConstant(name="p", data_type="NUMBER", value="0.40")],
|
|
250
|
+
signal_mappings=[ContractSignalMapping(name="t", source_path="$.t")],
|
|
251
|
+
workflow_definition_hash="sha256:deadbeef",
|
|
252
|
+
)
|
|
253
|
+
restored = WorkflowContractSnapshot.model_validate(
|
|
254
|
+
snap.model_dump(mode="json"),
|
|
255
|
+
)
|
|
256
|
+
assert restored == snap
|
|
257
|
+
|
|
258
|
+
def test_additive_fields_preserve_v1_schema(self):
|
|
259
|
+
"""A manifest carrying constants still reports schema_version v1.
|
|
260
|
+
|
|
261
|
+
Additive optional fields preserve v1 per the module's own policy;
|
|
262
|
+
bumping SCHEMA_VERSION here would be wrong.
|
|
263
|
+
"""
|
|
264
|
+
manifest = EvidenceManifest(
|
|
265
|
+
**{
|
|
266
|
+
**_minimal_manifest_kwargs(),
|
|
267
|
+
"workflow_contract": WorkflowContractSnapshot(
|
|
268
|
+
allowed_file_types=["json"],
|
|
269
|
+
constants=[
|
|
270
|
+
ContractConstant(name="p", data_type="NUMBER", value="0.40"),
|
|
271
|
+
],
|
|
272
|
+
workflow_definition_hash="sha256:abc",
|
|
273
|
+
),
|
|
274
|
+
},
|
|
275
|
+
)
|
|
276
|
+
assert manifest.schema_version == "validibot.evidence.v1"
|
|
277
|
+
assert manifest.workflow_contract.constants[0].name == "p"
|
|
278
|
+
|
|
279
|
+
|
|
168
280
|
# ──────────────────────────────────────────────────────────────────────
|
|
169
281
|
# StepValidatorRecord — semantic_digest can be None for legacy validators
|
|
170
282
|
# ──────────────────────────────────────────────────────────────────────
|
|
@@ -9,6 +9,8 @@ from validibot_shared.evidence import (
|
|
|
9
9
|
SCHEMA_VERSION as EVIDENCE_SCHEMA_VERSION,
|
|
10
10
|
)
|
|
11
11
|
from validibot_shared.evidence import (
|
|
12
|
+
ContractConstant,
|
|
13
|
+
ContractSignalMapping,
|
|
12
14
|
EvidenceManifest,
|
|
13
15
|
ManifestPayloadDigests,
|
|
14
16
|
ManifestRetentionInfo,
|
|
@@ -26,6 +28,8 @@ from validibot_shared.shacl.envelopes import (
|
|
|
26
28
|
|
|
27
29
|
__all__ = [
|
|
28
30
|
"EVIDENCE_SCHEMA_VERSION",
|
|
31
|
+
"ContractConstant",
|
|
32
|
+
"ContractSignalMapping",
|
|
29
33
|
"EnergyPlusSimulationLogs",
|
|
30
34
|
"EnergyPlusSimulationMetrics",
|
|
31
35
|
"EnergyPlusSimulationOutputs",
|
|
@@ -9,6 +9,8 @@ manifest without pulling in the application stack.
|
|
|
9
9
|
|
|
10
10
|
from validibot_shared.evidence.manifest import (
|
|
11
11
|
SCHEMA_VERSION,
|
|
12
|
+
ContractConstant,
|
|
13
|
+
ContractSignalMapping,
|
|
12
14
|
EvidenceManifest,
|
|
13
15
|
ManifestPayloadDigests,
|
|
14
16
|
ManifestRetentionInfo,
|
|
@@ -18,6 +20,8 @@ from validibot_shared.evidence.manifest import (
|
|
|
18
20
|
|
|
19
21
|
__all__ = [
|
|
20
22
|
"SCHEMA_VERSION",
|
|
23
|
+
"ContractConstant",
|
|
24
|
+
"ContractSignalMapping",
|
|
21
25
|
"EvidenceManifest",
|
|
22
26
|
"ManifestPayloadDigests",
|
|
23
27
|
"ManifestRetentionInfo",
|
|
@@ -49,7 +49,7 @@ file in the export bundle.
|
|
|
49
49
|
|
|
50
50
|
from __future__ import annotations
|
|
51
51
|
|
|
52
|
-
from typing import Literal
|
|
52
|
+
from typing import Any, Literal
|
|
53
53
|
|
|
54
54
|
from pydantic import BaseModel, ConfigDict, Field
|
|
55
55
|
|
|
@@ -110,6 +110,44 @@ class StepValidatorRecord(BaseModel):
|
|
|
110
110
|
)
|
|
111
111
|
|
|
112
112
|
|
|
113
|
+
class ContractConstant(BaseModel):
|
|
114
|
+
"""A workflow Constant (the ``c.*`` namespace) recorded in the contract.
|
|
115
|
+
|
|
116
|
+
A Constant (ADR-2026-06-18) is a workflow-defined *fixed literal*, so its
|
|
117
|
+
name **and value** are always safe to publish in the contract snapshot —
|
|
118
|
+
unlike a resolved ``s.*`` signal value, which is submission-derived and must
|
|
119
|
+
stay in the retention-gated run record. ``value`` is the stored form: a
|
|
120
|
+
canonical decimal *string* for NUMBER (so ``"0.40"`` keeps its precision), a
|
|
121
|
+
``str`` for STRING, a ``bool`` for BOOLEAN, and a ``list``/``dict`` for
|
|
122
|
+
LIST/OBJECT.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
model_config = ConfigDict(frozen=True)
|
|
126
|
+
|
|
127
|
+
name: str
|
|
128
|
+
data_type: str = ""
|
|
129
|
+
value: Any = None
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class ContractSignalMapping(BaseModel):
|
|
133
|
+
"""A workflow signal-mapping *definition* recorded in the contract.
|
|
134
|
+
|
|
135
|
+
The workflow-defined config for an ``s.<name>`` signal — NOT its resolved
|
|
136
|
+
runtime value. Publishing the definition (how the signal is sourced) is safe
|
|
137
|
+
and lets a verifier see the contract; the *resolved* value is
|
|
138
|
+
submission-derived and belongs only in the retention-gated run record
|
|
139
|
+
(ADR-2026-06-18).
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
model_config = ConfigDict(frozen=True)
|
|
143
|
+
|
|
144
|
+
name: str
|
|
145
|
+
source_path: str = ""
|
|
146
|
+
on_missing: str = ""
|
|
147
|
+
default_value: Any = None
|
|
148
|
+
data_type: str = ""
|
|
149
|
+
|
|
150
|
+
|
|
113
151
|
class WorkflowContractSnapshot(BaseModel):
|
|
114
152
|
"""Frozen snapshot of the workflow's launch-contract fields.
|
|
115
153
|
|
|
@@ -139,6 +177,38 @@ class WorkflowContractSnapshot(BaseModel):
|
|
|
139
177
|
agent_public_discovery: bool = False
|
|
140
178
|
agent_access_enabled: bool = False
|
|
141
179
|
|
|
180
|
+
# ── Workflow contract primitives (ADR-2026-06-18) ────────────────────────
|
|
181
|
+
# Additive, optional fields — the schema stays ``validibot.evidence.v1``
|
|
182
|
+
# (additive changes preserve v1 by the policy documented at the top of this
|
|
183
|
+
# module; a bump would be wrong here). Producers that predate this change
|
|
184
|
+
# simply leave them empty, and older consumers ignore unknown-to-them fields.
|
|
185
|
+
constants: list[ContractConstant] = Field(
|
|
186
|
+
default_factory=list,
|
|
187
|
+
description=(
|
|
188
|
+
"Workflow Constants (c.* namespace) with their fixed values — the "
|
|
189
|
+
"named thresholds this run was judged against. Empty for workflows "
|
|
190
|
+
"with no constants or for producers predating this field."
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
signal_mappings: list[ContractSignalMapping] = Field(
|
|
194
|
+
default_factory=list,
|
|
195
|
+
description=(
|
|
196
|
+
"Workflow signal-mapping DEFINITIONS (name + source_path + "
|
|
197
|
+
"on_missing + default + type) — never resolved s.* runtime values, "
|
|
198
|
+
"which are submission-derived and retention-gated."
|
|
199
|
+
),
|
|
200
|
+
)
|
|
201
|
+
workflow_definition_hash: str = Field(
|
|
202
|
+
default="",
|
|
203
|
+
description=(
|
|
204
|
+
"The workflow-definition digest (e.g. 'sha256:...') covering the "
|
|
205
|
+
"semantic contract — constants, signal-mapping definitions, and "
|
|
206
|
+
"per-step validator + effective-ruleset + assertions. Lets a "
|
|
207
|
+
"verifier confirm the run's contract matches a claimed version. "
|
|
208
|
+
"Empty for producers predating this field."
|
|
209
|
+
),
|
|
210
|
+
)
|
|
211
|
+
|
|
142
212
|
|
|
143
213
|
class ManifestRetentionInfo(BaseModel):
|
|
144
214
|
"""Retention class + applied-redaction summary.
|
|
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
|
|
File without changes
|
{validibot_shared-0.9.2 → validibot_shared-0.10.0}/validibot_shared/validations/envelopes.py
RENAMED
|
File without changes
|