Skip to content

Commit acbf7b6

Browse files
authored
Add TRACE TRO export for certified bundles (#274)
* Add TRACE TRO export for certified bundles * Format TRACE TRO files * Document TRACE bundle exports * Modernize docs tooling commands
1 parent e7cb50e commit acbf7b6

10 files changed

Lines changed: 538 additions & 9 deletions

File tree

.github/workflows/push.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,8 @@ jobs:
8181
- uses: actions/setup-node@v4
8282
with:
8383
node-version: 18.x
84-
- name: Install MyST
85-
run: npm install -g mystmd
8684
- name: Build HTML Assets
87-
run: cd docs && myst build --html
85+
run: make docs
8886
- name: Upload artifact
8987
uses: actions/upload-pages-artifact@v3
9088
with:

Makefile

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
.PHONY: docs
1+
.PHONY: docs docs-serve
2+
3+
MYSTMD_VERSION ?= 1.8.3
4+
MYST_CMD = npx --yes mystmd@$(MYSTMD_VERSION)
25

36
all: build-package
47

58
docs:
6-
cd docs && jupyter book start
9+
cd docs && $(MYST_CMD) build --html
10+
11+
docs-serve:
12+
cd docs && $(MYST_CMD) start
713

814
install:
915
uv pip install -e .[dev]
@@ -27,4 +33,4 @@ build-package:
2733
python -m build
2834

2935
test:
30-
pytest tests --cov=policyengine --cov-report=term-missing
36+
pytest tests --cov=policyengine --cov-report=term-missing

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ uv pip install -e .[dev] # install with dev dependencies (pytest, ruff, m
8686
```bash
8787
make format # ruff format
8888
make test # pytest with coverage
89-
make docs # build Jupyter Book documentation
89+
make docs # build static MyST/Jupyter Book 2 HTML docs
90+
make docs-serve # preview the docs locally
9091
make clean # remove caches, build artifacts, .h5 files
9192
```
9293

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add TRACE TRO export helpers for certified runtime bundles and expose them through `policyengine.core`.

docs/dev.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ dependencies used in CI (pytest, ruff, mypy, towncrier).
2323
```bash
2424
make format # ruff format
2525
make test # pytest with coverage
26-
make docs # run the MyST docs build used in CI via npx
26+
make docs # build static MyST/Jupyter Book 2 HTML docs
27+
make docs-serve # preview the docs locally
2728
make clean # remove caches, build artifacts, .h5 files
2829
```
2930

docs/release-bundles.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,55 @@ Notes:
186186
- apps and APIs should surface this bundle, not only country package versions
187187
- a bundle may reuse a previously staged data artifact if compatibility is explicitly certified
188188

189+
## TRACE export
190+
191+
The internal build manifest and certified runtime bundle remain the operational source of
192+
truth.
193+
194+
TRACE sits on top of those manifests as a standards-based export layer.
195+
196+
### What gets exported
197+
198+
Country `*-data` repos should emit a `trace.tro.jsonld` file for each published data
199+
release. That TRO should cover:
200+
201+
- the release manifest itself
202+
- each published artifact hash listed in the release manifest
203+
- the build-time model provenance recorded in the release manifest
204+
205+
`policyengine.py` should emit a separate certified-bundle TRO. That TRO should cover:
206+
207+
- the bundled country release manifest shipped in `policyengine.py`
208+
- the country data release manifest resolved for the certified data package version
209+
- the certified dataset artifact hash
210+
- the certification basis used to allow runtime reuse
211+
212+
### What TRACE does not replace
213+
214+
TRACE is not the source of truth for compatibility policy.
215+
216+
In particular, TRACE does not decide:
217+
218+
- whether a new model version can safely reuse an existing data artifact
219+
- how `data_build_fingerprint` is computed
220+
- which staged artifact becomes a supported runtime default
221+
222+
Those decisions still belong to the country data build manifest and the
223+
`policyengine.py` certified runtime bundle.
224+
225+
### Why we still want it
226+
227+
TRACE adds three things our internal manifests do not provide by themselves:
228+
229+
- a standard declaration format for provenance exchange
230+
- a composition fingerprint over the exact artifacts in scope
231+
- a better external surface for papers, audits, and reproducibility reviews
232+
233+
That is why the recommended design is:
234+
235+
- internal manifests for build/certification control
236+
- generated TRACE TROs for standards-based export
237+
189238
## Compatibility rule
190239

191240
The architecture should avoid forcing a new data build for every harmless country model release.

src/policyengine/core/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@
3636
from .tax_benefit_model_version import (
3737
TaxBenefitModelVersion as TaxBenefitModelVersion,
3838
)
39+
from .trace_tro import (
40+
build_trace_tro_from_release_bundle as build_trace_tro_from_release_bundle,
41+
)
42+
from .trace_tro import (
43+
compute_trace_composition_fingerprint as compute_trace_composition_fingerprint,
44+
)
45+
from .trace_tro import serialize_trace_tro as serialize_trace_tro
3946
from .variable import Variable as Variable
4047

4148
# Rebuild models to resolve forward references

src/policyengine/core/tax_benefit_model_version.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@
44

55
from pydantic import BaseModel, Field
66

7-
from .release_manifest import CountryReleaseManifest, DataCertification, PackageVersion
7+
from .release_manifest import (
8+
CountryReleaseManifest,
9+
DataCertification,
10+
PackageVersion,
11+
get_data_release_manifest,
12+
)
813
from .tax_benefit_model import TaxBenefitModel
14+
from .trace_tro import build_trace_tro_from_release_bundle
915

1016
if TYPE_CHECKING:
1117
from .parameter import Parameter
@@ -201,6 +207,22 @@ def release_bundle(self) -> dict[str, str | None]:
201207
),
202208
}
203209

210+
@property
211+
def trace_tro(self) -> dict:
212+
if self.release_manifest is None:
213+
raise ValueError(
214+
"TRACE TRO export requires a bundled country release manifest."
215+
)
216+
217+
data_release_manifest = get_data_release_manifest(
218+
self.release_manifest.country_id
219+
)
220+
return build_trace_tro_from_release_bundle(
221+
self.release_manifest,
222+
data_release_manifest,
223+
certification=self.data_certification,
224+
)
225+
204226
def __repr__(self) -> str:
205227
# Give the id and version, and the number of variables, parameters, parameter nodes, parameter values
206228
return f"<TaxBenefitModelVersion id={self.id} variables={len(self.variables)} parameters={len(self.parameters)} parameter_nodes={len(self.parameter_nodes)} parameter_values={len(self.parameter_values)}>"

0 commit comments

Comments
 (0)