zeromodel-artifacts 1.1.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.
- zeromodel_artifacts-1.1.0/PKG-INFO +118 -0
- zeromodel_artifacts-1.1.0/README.md +92 -0
- zeromodel_artifacts-1.1.0/pyproject.toml +44 -0
- zeromodel_artifacts-1.1.0/setup.cfg +4 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/__init__.py +120 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/adapted_report_persistence.py +91 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/adapter.py +30 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/aggregate.py +795 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/canonicalization.py +13 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/compatibility_schema.py +93 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/compiled_artifact.py +518 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/core_artifact_persistence.py +95 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/ref.py +44 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/report_adapter_contract_persistence.py +78 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/report_compiler.py +254 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/report_decode.py +82 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/report_dto.py +493 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/report_errors.py +13 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/report_loading.py +94 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/score_semantics.py +16 -0
- zeromodel_artifacts-1.1.0/src/zeromodel/artifacts/store.py +156 -0
- zeromodel_artifacts-1.1.0/src/zeromodel_artifacts.egg-info/PKG-INFO +118 -0
- zeromodel_artifacts-1.1.0/src/zeromodel_artifacts.egg-info/SOURCES.txt +35 -0
- zeromodel_artifacts-1.1.0/src/zeromodel_artifacts.egg-info/dependency_links.txt +1 -0
- zeromodel_artifacts-1.1.0/src/zeromodel_artifacts.egg-info/requires.txt +2 -0
- zeromodel_artifacts-1.1.0/src/zeromodel_artifacts.egg-info/top_level.txt +1 -0
- zeromodel_artifacts-1.1.0/tests/test_adapted_report_identity.py +430 -0
- zeromodel_artifacts-1.1.0/tests/test_adapted_report_persistence.py +209 -0
- zeromodel_artifacts-1.1.0/tests/test_artifact_ref_and_store.py +146 -0
- zeromodel_artifacts-1.1.0/tests/test_artifacts_api_isolation.py +98 -0
- zeromodel_artifacts-1.1.0/tests/test_compiled_artifact_validation.py +364 -0
- zeromodel_artifacts-1.1.0/tests/test_compiled_report_aggregate.py +321 -0
- zeromodel_artifacts-1.1.0/tests/test_compiled_report_aggregate_adversarial.py +761 -0
- zeromodel_artifacts-1.1.0/tests/test_reference_kind_and_receipt_invariants.py +242 -0
- zeromodel_artifacts-1.1.0/tests/test_report_adapter_contract.py +63 -0
- zeromodel_artifacts-1.1.0/tests/test_report_compilation.py +409 -0
- zeromodel_artifacts-1.1.0/tests/test_report_families.py +184 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zeromodel-artifacts
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: ZeroModel canonical artifact reference, resolution, and content-addressed storage
|
|
5
|
+
Author-email: Ernan Hughes <ernanhughes@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ernanhughes/zeromodel
|
|
8
|
+
Project-URL: Repository, https://github.com/ernanhughes/zeromodel
|
|
9
|
+
Project-URL: Documentation, https://ernanhughes.github.io/zeromodel/
|
|
10
|
+
Keywords: visual-policy-map,vpm,artifact-identity,content-addressed-storage
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: numpy>=1.23
|
|
25
|
+
Requires-Dist: zeromodel==1.1.0
|
|
26
|
+
|
|
27
|
+
# zeromodel-artifacts
|
|
28
|
+
|
|
29
|
+
Canonical artifact reference, resolution, and content-addressed storage for
|
|
30
|
+
the ZeroModel workspace.
|
|
31
|
+
|
|
32
|
+
This package defines the stable, cross-package `ArtifactRef` identity and the
|
|
33
|
+
`ArtifactResolver` / `ArtifactStore` protocols other packages (such as
|
|
34
|
+
`zeromodel-trust` and `zeromodel-navigation`) use to persist and resolve their
|
|
35
|
+
own artifacts, without each package inventing its own storage layer.
|
|
36
|
+
|
|
37
|
+
It reuses `zeromodel.core`'s existing canonicalization and digest primitives
|
|
38
|
+
(`canonical_json_bytes`, `sha256_digest`) rather than redefining them.
|
|
39
|
+
|
|
40
|
+
## Report compilation and the compiled-report aggregate
|
|
41
|
+
|
|
42
|
+
On top of the storage kernel, this package also compiles typed external
|
|
43
|
+
reports into deterministic, source-bound VPM artifacts (`ReportAdapter`,
|
|
44
|
+
`AdaptedReportDTO`, `compile_report()`) and resolves the complete result as
|
|
45
|
+
one coherent aggregate:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
AdaptedReportDTO (persisted, resolvable)
|
|
49
|
+
ReportAdapterContractDTO (persisted, resolvable)
|
|
50
|
+
↓
|
|
51
|
+
CompiledReportArtifactDTO (aggregate root)
|
|
52
|
+
├── adapted_report_ref -> AdaptedReportDTO
|
|
53
|
+
├── adapter_contract_ref -> ReportAdapterContractDTO
|
|
54
|
+
├── score_table_ref -> ScoreTable
|
|
55
|
+
├── layout_recipe_ref -> LayoutRecipe
|
|
56
|
+
└── vpm_artifact_ref -> VPMArtifact
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`load_compiled_report_aggregate()` resolves all five referenced artifacts
|
|
60
|
+
and runs `validate_compiled_report_aggregate()`, which proves the
|
|
61
|
+
collection is semantically closed - not merely that each object's own
|
|
62
|
+
digest is valid, and not merely that the resolved objects agree with each
|
|
63
|
+
other. A compiled report can reference a `ScoreTable` from one report, a
|
|
64
|
+
`LayoutRecipe` from another, and a `VPMArtifact` from a third, each
|
|
65
|
+
individually digest-valid; a VPM can even embed the *correct* `ScoreTable`
|
|
66
|
+
and `LayoutRecipe` while its normalized pixels or row/column ordering are
|
|
67
|
+
fabricated. Aggregate validation proves: every resolved object's
|
|
68
|
+
store-level digest actually matches the ref the compiled report declares
|
|
69
|
+
for it (not merely assumed from how the caller obtained the object); the
|
|
70
|
+
resolved adapter contract's declared fields agree with what the compiled
|
|
71
|
+
and adapted reports copied from it; the VPM's `normalized_values`/
|
|
72
|
+
`row_order`/`column_order`/`provenance` are exactly what `build_vpm()`
|
|
73
|
+
would deterministically reproduce from the resolved `ScoreTable`/
|
|
74
|
+
`LayoutRecipe`; and every VPM view coordinate, raw value, and source
|
|
75
|
+
binding is exactly what the compiled report's cell bindings claim.
|
|
76
|
+
`build_compiled_report_closure_receipt()` produces an auditable,
|
|
77
|
+
content-addressed receipt only after every check passes - never a partial
|
|
78
|
+
result.
|
|
79
|
+
|
|
80
|
+
Compatibility between two compiled reports requires three independent
|
|
81
|
+
identities to agree: `compatibility_id` (a human label), and two content
|
|
82
|
+
digests: `compatibility_schema_id` (dimension ids, order, score semantics,
|
|
83
|
+
and value/target ranges) and `report_semantics_id` (report kind, subject
|
|
84
|
+
kind, dimension namespace, and duplicate-value policy - the layer that
|
|
85
|
+
distinguishes, for example, a report over sentences from a structurally
|
|
86
|
+
identical report over claims).
|
|
87
|
+
|
|
88
|
+
`load_compiled_report_vpm(*, ref, resolver)` is the one safe public path
|
|
89
|
+
to a compiled report's rendered `VPMArtifact` - it is a thin wrapper
|
|
90
|
+
around `load_compiled_report_aggregate(...).vpm_artifact`, so it always
|
|
91
|
+
runs the full closure (including the deterministic-reconstruction check
|
|
92
|
+
above) before returning anything. `core_artifact_persistence.load_vpm_artifact`
|
|
93
|
+
remains available as an explicitly low-level Core loader (digest-only, no
|
|
94
|
+
compiled-report context to close an aggregate against) for callers who
|
|
95
|
+
genuinely have no compiled report to check against - it is not itself a
|
|
96
|
+
safe rendering path for a compiled report's VPM.
|
|
97
|
+
|
|
98
|
+
A reference is the pair `(artifact_kind, artifact_id)`, not the digest
|
|
99
|
+
alone: `CompiledReportArtifactDTO` rejects a nested ref whose declared
|
|
100
|
+
kind doesn't match its expected artifact kind even when the `artifact_id`
|
|
101
|
+
genuinely matches stored content, and `validate_compiled_report_aggregate`
|
|
102
|
+
re-asserts the same check as defense in depth.
|
|
103
|
+
`CompiledReportClosureReceiptDTO` similarly cannot be constructed with an
|
|
104
|
+
incomplete `checks` tuple or non-empty `failure_codes`, even with a
|
|
105
|
+
`receipt_id` that correctly hashes that content - the type's promise that
|
|
106
|
+
"every check passed" is enforced structurally, not left to its one
|
|
107
|
+
legitimate builder.
|
|
108
|
+
|
|
109
|
+
**Claims boundary:** ZeroModel can persist and reload a complete adapted
|
|
110
|
+
report, its governing adapter contract, and its compiled `ScoreTable`,
|
|
111
|
+
`LayoutRecipe`, and `VPMArtifact` as one content-addressed aggregate, and
|
|
112
|
+
verify that every subject, dimension, value, coordinate, source binding,
|
|
113
|
+
declared contract semantic, and rendered pixel agrees across all five
|
|
114
|
+
representations - including that the VPM's visible pixels were actually
|
|
115
|
+
generated by its own embedded recipe, not merely digest-consistent with
|
|
116
|
+
themselves. This does not include Trust-signed compiled reports (Trust
|
|
117
|
+
integration is a call-site composition, not implemented here),
|
|
118
|
+
cross-schema conversion, or automatic report repair.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# zeromodel-artifacts
|
|
2
|
+
|
|
3
|
+
Canonical artifact reference, resolution, and content-addressed storage for
|
|
4
|
+
the ZeroModel workspace.
|
|
5
|
+
|
|
6
|
+
This package defines the stable, cross-package `ArtifactRef` identity and the
|
|
7
|
+
`ArtifactResolver` / `ArtifactStore` protocols other packages (such as
|
|
8
|
+
`zeromodel-trust` and `zeromodel-navigation`) use to persist and resolve their
|
|
9
|
+
own artifacts, without each package inventing its own storage layer.
|
|
10
|
+
|
|
11
|
+
It reuses `zeromodel.core`'s existing canonicalization and digest primitives
|
|
12
|
+
(`canonical_json_bytes`, `sha256_digest`) rather than redefining them.
|
|
13
|
+
|
|
14
|
+
## Report compilation and the compiled-report aggregate
|
|
15
|
+
|
|
16
|
+
On top of the storage kernel, this package also compiles typed external
|
|
17
|
+
reports into deterministic, source-bound VPM artifacts (`ReportAdapter`,
|
|
18
|
+
`AdaptedReportDTO`, `compile_report()`) and resolves the complete result as
|
|
19
|
+
one coherent aggregate:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
AdaptedReportDTO (persisted, resolvable)
|
|
23
|
+
ReportAdapterContractDTO (persisted, resolvable)
|
|
24
|
+
↓
|
|
25
|
+
CompiledReportArtifactDTO (aggregate root)
|
|
26
|
+
├── adapted_report_ref -> AdaptedReportDTO
|
|
27
|
+
├── adapter_contract_ref -> ReportAdapterContractDTO
|
|
28
|
+
├── score_table_ref -> ScoreTable
|
|
29
|
+
├── layout_recipe_ref -> LayoutRecipe
|
|
30
|
+
└── vpm_artifact_ref -> VPMArtifact
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`load_compiled_report_aggregate()` resolves all five referenced artifacts
|
|
34
|
+
and runs `validate_compiled_report_aggregate()`, which proves the
|
|
35
|
+
collection is semantically closed - not merely that each object's own
|
|
36
|
+
digest is valid, and not merely that the resolved objects agree with each
|
|
37
|
+
other. A compiled report can reference a `ScoreTable` from one report, a
|
|
38
|
+
`LayoutRecipe` from another, and a `VPMArtifact` from a third, each
|
|
39
|
+
individually digest-valid; a VPM can even embed the *correct* `ScoreTable`
|
|
40
|
+
and `LayoutRecipe` while its normalized pixels or row/column ordering are
|
|
41
|
+
fabricated. Aggregate validation proves: every resolved object's
|
|
42
|
+
store-level digest actually matches the ref the compiled report declares
|
|
43
|
+
for it (not merely assumed from how the caller obtained the object); the
|
|
44
|
+
resolved adapter contract's declared fields agree with what the compiled
|
|
45
|
+
and adapted reports copied from it; the VPM's `normalized_values`/
|
|
46
|
+
`row_order`/`column_order`/`provenance` are exactly what `build_vpm()`
|
|
47
|
+
would deterministically reproduce from the resolved `ScoreTable`/
|
|
48
|
+
`LayoutRecipe`; and every VPM view coordinate, raw value, and source
|
|
49
|
+
binding is exactly what the compiled report's cell bindings claim.
|
|
50
|
+
`build_compiled_report_closure_receipt()` produces an auditable,
|
|
51
|
+
content-addressed receipt only after every check passes - never a partial
|
|
52
|
+
result.
|
|
53
|
+
|
|
54
|
+
Compatibility between two compiled reports requires three independent
|
|
55
|
+
identities to agree: `compatibility_id` (a human label), and two content
|
|
56
|
+
digests: `compatibility_schema_id` (dimension ids, order, score semantics,
|
|
57
|
+
and value/target ranges) and `report_semantics_id` (report kind, subject
|
|
58
|
+
kind, dimension namespace, and duplicate-value policy - the layer that
|
|
59
|
+
distinguishes, for example, a report over sentences from a structurally
|
|
60
|
+
identical report over claims).
|
|
61
|
+
|
|
62
|
+
`load_compiled_report_vpm(*, ref, resolver)` is the one safe public path
|
|
63
|
+
to a compiled report's rendered `VPMArtifact` - it is a thin wrapper
|
|
64
|
+
around `load_compiled_report_aggregate(...).vpm_artifact`, so it always
|
|
65
|
+
runs the full closure (including the deterministic-reconstruction check
|
|
66
|
+
above) before returning anything. `core_artifact_persistence.load_vpm_artifact`
|
|
67
|
+
remains available as an explicitly low-level Core loader (digest-only, no
|
|
68
|
+
compiled-report context to close an aggregate against) for callers who
|
|
69
|
+
genuinely have no compiled report to check against - it is not itself a
|
|
70
|
+
safe rendering path for a compiled report's VPM.
|
|
71
|
+
|
|
72
|
+
A reference is the pair `(artifact_kind, artifact_id)`, not the digest
|
|
73
|
+
alone: `CompiledReportArtifactDTO` rejects a nested ref whose declared
|
|
74
|
+
kind doesn't match its expected artifact kind even when the `artifact_id`
|
|
75
|
+
genuinely matches stored content, and `validate_compiled_report_aggregate`
|
|
76
|
+
re-asserts the same check as defense in depth.
|
|
77
|
+
`CompiledReportClosureReceiptDTO` similarly cannot be constructed with an
|
|
78
|
+
incomplete `checks` tuple or non-empty `failure_codes`, even with a
|
|
79
|
+
`receipt_id` that correctly hashes that content - the type's promise that
|
|
80
|
+
"every check passed" is enforced structurally, not left to its one
|
|
81
|
+
legitimate builder.
|
|
82
|
+
|
|
83
|
+
**Claims boundary:** ZeroModel can persist and reload a complete adapted
|
|
84
|
+
report, its governing adapter contract, and its compiled `ScoreTable`,
|
|
85
|
+
`LayoutRecipe`, and `VPMArtifact` as one content-addressed aggregate, and
|
|
86
|
+
verify that every subject, dimension, value, coordinate, source binding,
|
|
87
|
+
declared contract semantic, and rendered pixel agrees across all five
|
|
88
|
+
representations - including that the VPM's visible pixels were actually
|
|
89
|
+
generated by its own embedded recipe, not merely digest-consistent with
|
|
90
|
+
themselves. This does not include Trust-signed compiled reports (Trust
|
|
91
|
+
integration is a call-site composition, not implemented here),
|
|
92
|
+
cross-schema conversion, or automatic report repair.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "zeromodel-artifacts"
|
|
7
|
+
version = "1.1.0"
|
|
8
|
+
description = "ZeroModel canonical artifact reference, resolution, and content-addressed storage"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [{name = "Ernan Hughes", email = "ernanhughes@gmail.com"}]
|
|
13
|
+
keywords = [
|
|
14
|
+
"visual-policy-map",
|
|
15
|
+
"vpm",
|
|
16
|
+
"artifact-identity",
|
|
17
|
+
"content-addressed-storage",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Intended Audience :: Science/Research",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
"Programming Language :: Python :: 3",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
30
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
31
|
+
]
|
|
32
|
+
dependencies = [
|
|
33
|
+
"numpy>=1.23",
|
|
34
|
+
"zeromodel==1.1.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/ernanhughes/zeromodel"
|
|
39
|
+
Repository = "https://github.com/ernanhughes/zeromodel"
|
|
40
|
+
Documentation = "https://ernanhughes.github.io/zeromodel/"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["src"]
|
|
44
|
+
include = ["zeromodel.artifacts*"]
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from zeromodel.artifacts.adapted_report_persistence import (
|
|
4
|
+
ADAPTED_REPORT_ARTIFACT_KIND,
|
|
5
|
+
load_adapted_report,
|
|
6
|
+
store_adapted_report,
|
|
7
|
+
)
|
|
8
|
+
from zeromodel.artifacts.adapter import ReportAdapter
|
|
9
|
+
from zeromodel.artifacts.aggregate import (
|
|
10
|
+
CompiledReportClosureReceiptDTO,
|
|
11
|
+
ResolvedCompiledReportAggregateDTO,
|
|
12
|
+
build_compiled_report_closure_receipt,
|
|
13
|
+
load_compiled_report_aggregate,
|
|
14
|
+
load_compiled_report_vpm,
|
|
15
|
+
validate_compiled_report_aggregate,
|
|
16
|
+
)
|
|
17
|
+
from zeromodel.artifacts.canonicalization import canonical_json_bytes, sha256_digest
|
|
18
|
+
from zeromodel.artifacts.compatibility_schema import (
|
|
19
|
+
compute_compatibility_schema_id,
|
|
20
|
+
compute_report_semantics_id,
|
|
21
|
+
)
|
|
22
|
+
from zeromodel.artifacts.compiled_artifact import (
|
|
23
|
+
CellBindingDTO,
|
|
24
|
+
CompiledReportArtifactDTO,
|
|
25
|
+
CoreArtifactRefs,
|
|
26
|
+
)
|
|
27
|
+
from zeromodel.artifacts.core_artifact_persistence import (
|
|
28
|
+
LAYOUT_RECIPE_ARTIFACT_KIND,
|
|
29
|
+
SCORE_TABLE_ARTIFACT_KIND,
|
|
30
|
+
VPM_ARTIFACT_ARTIFACT_KIND,
|
|
31
|
+
load_layout_recipe,
|
|
32
|
+
load_score_table,
|
|
33
|
+
load_vpm_artifact,
|
|
34
|
+
store_layout_recipe,
|
|
35
|
+
store_score_table,
|
|
36
|
+
store_vpm_artifact,
|
|
37
|
+
)
|
|
38
|
+
from zeromodel.artifacts.ref import ARTIFACT_REF_VERSION, ArtifactRef, is_sha256_digest
|
|
39
|
+
from zeromodel.artifacts.report_adapter_contract_persistence import (
|
|
40
|
+
REPORT_ADAPTER_CONTRACT_ARTIFACT_KIND,
|
|
41
|
+
load_report_adapter_contract,
|
|
42
|
+
store_report_adapter_contract,
|
|
43
|
+
)
|
|
44
|
+
from zeromodel.artifacts.report_compiler import compile_report
|
|
45
|
+
from zeromodel.artifacts.report_dto import (
|
|
46
|
+
AdaptedDimensionDTO,
|
|
47
|
+
AdaptedReportDTO,
|
|
48
|
+
AdaptedSubjectDTO,
|
|
49
|
+
AdaptedValueDTO,
|
|
50
|
+
ReportAdapterContractDTO,
|
|
51
|
+
ReportFindingRefDTO,
|
|
52
|
+
SourceBindingDTO,
|
|
53
|
+
)
|
|
54
|
+
from zeromodel.artifacts.report_errors import (
|
|
55
|
+
ReportAdaptationError,
|
|
56
|
+
ReportCompilationError,
|
|
57
|
+
)
|
|
58
|
+
from zeromodel.artifacts.report_loading import load_compiled_report_artifact
|
|
59
|
+
from zeromodel.artifacts.score_semantics import ScoreSemantics
|
|
60
|
+
from zeromodel.artifacts.store import (
|
|
61
|
+
ArtifactIntegrityError,
|
|
62
|
+
ArtifactManifestConflictError,
|
|
63
|
+
ArtifactNotFoundError,
|
|
64
|
+
ArtifactResolver,
|
|
65
|
+
ArtifactStore,
|
|
66
|
+
InMemoryArtifactStore,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
__all__ = [
|
|
70
|
+
"ADAPTED_REPORT_ARTIFACT_KIND",
|
|
71
|
+
"ARTIFACT_REF_VERSION",
|
|
72
|
+
"LAYOUT_RECIPE_ARTIFACT_KIND",
|
|
73
|
+
"REPORT_ADAPTER_CONTRACT_ARTIFACT_KIND",
|
|
74
|
+
"SCORE_TABLE_ARTIFACT_KIND",
|
|
75
|
+
"VPM_ARTIFACT_ARTIFACT_KIND",
|
|
76
|
+
"AdaptedDimensionDTO",
|
|
77
|
+
"AdaptedReportDTO",
|
|
78
|
+
"AdaptedSubjectDTO",
|
|
79
|
+
"AdaptedValueDTO",
|
|
80
|
+
"ArtifactIntegrityError",
|
|
81
|
+
"ArtifactManifestConflictError",
|
|
82
|
+
"ArtifactNotFoundError",
|
|
83
|
+
"ArtifactRef",
|
|
84
|
+
"ArtifactResolver",
|
|
85
|
+
"ArtifactStore",
|
|
86
|
+
"CellBindingDTO",
|
|
87
|
+
"CompiledReportArtifactDTO",
|
|
88
|
+
"CompiledReportClosureReceiptDTO",
|
|
89
|
+
"CoreArtifactRefs",
|
|
90
|
+
"InMemoryArtifactStore",
|
|
91
|
+
"ReportAdaptationError",
|
|
92
|
+
"ReportAdapter",
|
|
93
|
+
"ReportAdapterContractDTO",
|
|
94
|
+
"ReportCompilationError",
|
|
95
|
+
"ReportFindingRefDTO",
|
|
96
|
+
"ResolvedCompiledReportAggregateDTO",
|
|
97
|
+
"ScoreSemantics",
|
|
98
|
+
"SourceBindingDTO",
|
|
99
|
+
"build_compiled_report_closure_receipt",
|
|
100
|
+
"canonical_json_bytes",
|
|
101
|
+
"compile_report",
|
|
102
|
+
"compute_compatibility_schema_id",
|
|
103
|
+
"compute_report_semantics_id",
|
|
104
|
+
"is_sha256_digest",
|
|
105
|
+
"load_adapted_report",
|
|
106
|
+
"load_compiled_report_aggregate",
|
|
107
|
+
"load_compiled_report_artifact",
|
|
108
|
+
"load_compiled_report_vpm",
|
|
109
|
+
"load_layout_recipe",
|
|
110
|
+
"load_report_adapter_contract",
|
|
111
|
+
"load_score_table",
|
|
112
|
+
"load_vpm_artifact",
|
|
113
|
+
"sha256_digest",
|
|
114
|
+
"store_adapted_report",
|
|
115
|
+
"store_layout_recipe",
|
|
116
|
+
"store_report_adapter_contract",
|
|
117
|
+
"store_score_table",
|
|
118
|
+
"store_vpm_artifact",
|
|
119
|
+
"validate_compiled_report_aggregate",
|
|
120
|
+
]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Persist and reload `AdaptedReportDTO` as a first-class artifact.
|
|
2
|
+
|
|
3
|
+
Before this module, `AdaptedReportDTO` only ever existed as a discarded
|
|
4
|
+
local Python object inside `compile_report()`: the compiled report named it
|
|
5
|
+
by digest (`adapted_report_id`) but nothing made that digest resolvable
|
|
6
|
+
after the process exited. Raw values, per-value confidence, per-value
|
|
7
|
+
importance, source bindings, and parent-report lineage - none of which the
|
|
8
|
+
VPM matrix preserves completely - were unrecoverable once compilation
|
|
9
|
+
finished. This module closes that gap using the same decode-and-verify
|
|
10
|
+
pattern as `core_artifact_persistence.py`: resolve canonical bytes,
|
|
11
|
+
recompute the digest, require it to equal the requested ref's
|
|
12
|
+
`artifact_id`, decode, and reconstruct via `AdaptedReportDTO.__post_init__`
|
|
13
|
+
- never trusting a store's manifest as authoritative.
|
|
14
|
+
|
|
15
|
+
Canonical format: the stored payload is exactly
|
|
16
|
+
`adapted_report_signing_payload(adapted_report)` - the same payload
|
|
17
|
+
`AdaptedReportDTO.adapted_report_id` is computed over, with the
|
|
18
|
+
self-referential id field excluded. Because `ArtifactStore.put()` computes
|
|
19
|
+
`ref.artifact_id` as `sha256_digest(canonical_bytes)` over that same
|
|
20
|
+
payload, `ref.artifact_id == adapted_report.adapted_report_id` always
|
|
21
|
+
holds - the artifact-store identity and the DTO's own content identity are
|
|
22
|
+
the same digest, not two independent layers that happen to agree.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import json
|
|
28
|
+
|
|
29
|
+
from zeromodel.artifacts.canonicalization import canonical_json_bytes, sha256_digest
|
|
30
|
+
from zeromodel.artifacts.ref import ArtifactRef
|
|
31
|
+
from zeromodel.artifacts.report_decode import (
|
|
32
|
+
decode_attributes,
|
|
33
|
+
decode_dimension,
|
|
34
|
+
decode_subject,
|
|
35
|
+
decode_value,
|
|
36
|
+
)
|
|
37
|
+
from zeromodel.artifacts.report_dto import (
|
|
38
|
+
AdaptedReportDTO,
|
|
39
|
+
adapted_report_signing_payload,
|
|
40
|
+
)
|
|
41
|
+
from zeromodel.artifacts.report_errors import ReportCompilationError
|
|
42
|
+
from zeromodel.artifacts.store import ArtifactResolver, ArtifactStore
|
|
43
|
+
|
|
44
|
+
ADAPTED_REPORT_ARTIFACT_KIND = "zeromodel.artifacts.adapted-report/v1"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def store_adapted_report(
|
|
48
|
+
adapted_report: AdaptedReportDTO, *, store: ArtifactStore
|
|
49
|
+
) -> ArtifactRef:
|
|
50
|
+
canonical_bytes = canonical_json_bytes(
|
|
51
|
+
adapted_report_signing_payload(adapted_report)
|
|
52
|
+
)
|
|
53
|
+
ref = store.put(ADAPTED_REPORT_ARTIFACT_KIND, canonical_bytes, manifest=None)
|
|
54
|
+
if ref.artifact_id != adapted_report.adapted_report_id:
|
|
55
|
+
raise ReportCompilationError(
|
|
56
|
+
"stored adapted-report digest does not match adapted_report_id "
|
|
57
|
+
f"(stored={ref.artifact_id}, adapted_report_id={adapted_report.adapted_report_id})"
|
|
58
|
+
)
|
|
59
|
+
return ref
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def load_adapted_report(
|
|
63
|
+
ref: ArtifactRef, *, resolver: ArtifactResolver
|
|
64
|
+
) -> AdaptedReportDTO:
|
|
65
|
+
if ref.artifact_kind != ADAPTED_REPORT_ARTIFACT_KIND:
|
|
66
|
+
raise ReportCompilationError(
|
|
67
|
+
f"load_adapted_report requires artifact_kind={ADAPTED_REPORT_ARTIFACT_KIND!r}, "
|
|
68
|
+
f"got {ref.artifact_kind!r}"
|
|
69
|
+
)
|
|
70
|
+
canonical_bytes = resolver.resolve_canonical_bytes(ref)
|
|
71
|
+
actual_digest = sha256_digest(canonical_bytes)
|
|
72
|
+
if actual_digest != ref.artifact_id:
|
|
73
|
+
raise ReportCompilationError(
|
|
74
|
+
f"resolved canonical bytes for {ref.artifact_id} do not hash to the requested id "
|
|
75
|
+
f"(got {actual_digest})"
|
|
76
|
+
)
|
|
77
|
+
payload = json.loads(canonical_bytes)
|
|
78
|
+
|
|
79
|
+
return AdaptedReportDTO(
|
|
80
|
+
adapted_report_id=ref.artifact_id,
|
|
81
|
+
report_id=payload["report_id"],
|
|
82
|
+
report_kind=payload["report_kind"],
|
|
83
|
+
adapter_contract_id=payload["adapter_contract_id"],
|
|
84
|
+
compatibility_id=payload["compatibility_id"],
|
|
85
|
+
subjects=tuple(decode_subject(item) for item in payload["subjects"]),
|
|
86
|
+
dimensions=tuple(decode_dimension(item) for item in payload["dimensions"]),
|
|
87
|
+
values=tuple(decode_value(item) for item in payload["values"]),
|
|
88
|
+
parent_report_ids=tuple(payload["parent_report_ids"]),
|
|
89
|
+
attributes=decode_attributes(payload["attributes"]),
|
|
90
|
+
spec_version=payload["spec_version"],
|
|
91
|
+
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""The adapter protocol - the ingress boundary for domain reports.
|
|
2
|
+
|
|
3
|
+
Concrete adapters live in the external application (e.g.
|
|
4
|
+
`writer.integrations.zeromodel.AIArtifactReportAdapter`), never in this
|
|
5
|
+
package. An adapter must not write directly to an `ArtifactStore`,
|
|
6
|
+
construct rendered images, or calculate artifact identity itself - it only
|
|
7
|
+
translates one typed domain report into a neutral `AdaptedReportDTO`;
|
|
8
|
+
`compile_report` (see `report_compiler.py`) owns validation and identity.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Generic, Protocol, TypeVar, runtime_checkable
|
|
14
|
+
|
|
15
|
+
from zeromodel.artifacts.report_dto import AdaptedReportDTO, ReportAdapterContractDTO
|
|
16
|
+
|
|
17
|
+
ReportT = TypeVar("ReportT", contravariant=True)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@runtime_checkable
|
|
21
|
+
class ReportAdapter(Protocol, Generic[ReportT]):
|
|
22
|
+
"""Domain-owned translation from one typed report into neutral ZeroModel form."""
|
|
23
|
+
|
|
24
|
+
def contract(self) -> ReportAdapterContractDTO:
|
|
25
|
+
"""Return the stable contract governing this adapter's translation."""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
def adapt(self, report: ReportT) -> AdaptedReportDTO:
|
|
29
|
+
"""Convert one domain report into the neutral ZeroModel report form."""
|
|
30
|
+
...
|