pgc-runtime 2.0.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.
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: pgc-runtime
3
+ Version: 2.0.0
4
+ Summary: PGC protocol runtime — executes a sealed snapshot as governed DAG traversal and emits traces (import package: runtime)
5
+ Author-email: Bhash Ganti <bachipeachy@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://omnibachi.org/
8
+ Project-URL: Repository, https://github.com/protocol-governed-computing/protocol_runtime
9
+ Project-URL: Standard, https://doi.org/10.5281/zenodo.22150616
10
+ Keywords: protocol-governed-computing,pgc,protocol,runtime,workflow,execution
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Topic :: System :: Distributed Computing
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ License-File: NOTICE
23
+ Provides-Extra: reference
24
+ Requires-Dist: pgc-governance>=2.0.0; extra == "reference"
25
+ Dynamic: license-file
26
+
27
+ # protocol_runtime
28
+
29
+ **Deterministic execution engine for Protocol-Governed Computing** (import package: `runtime`).
30
+
31
+ The runtime traverses a precompiled execution graph and produces traceable, governed outcomes. It
32
+ does not discover behavior, interpret intent, or contain business logic. Everything it will do was
33
+ decided at compile time; execution is a traversal of what the snapshot already says.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install pgc-runtime
39
+ ```
40
+
41
+ Once installed:
42
+
43
+ ```bash
44
+ protocol_runtime --help
45
+ ```
46
+
47
+ ## Where it fits
48
+
49
+ ```
50
+ software_governance the normative surface every composition rests on
51
+ conformance_workloads workloads that prove conformance
52
+ business_domains domains built on the surface
53
+
54
+ protocol_compiler source → compiled projections
55
+ snapshot_assembler projections → assembled snapshot
56
+ protocol_runtime snapshot → execution (this repo)
57
+ snapshot_inspector snapshot → inspection
58
+ ```
59
+
60
+ `protocol_transport` governs the boundary at either end of execution — ingress and egress as
61
+ first-class contracts. The runtime consumes only the **assembled** snapshot, never an individual
62
+ repo's compiled layout.
63
+
64
+ ## What it is, and is not
65
+
66
+ **It is** a deterministic graph traverser, a trace generator, and a host for the capability
67
+ implementations a snapshot names.
68
+
69
+ **It is not** a workflow authoring system, a rules engine, a business-logic container, or a
70
+ framework with pluggable behavior. There is no extension point, because an extension point is a
71
+ place where ungoverned behavior could enter.
72
+
73
+ ## Inputs and outputs
74
+
75
+ ```
76
+ snapshot root the assembled snapshot — the sole source of behavior
77
+ payload external input (JSON)
78
+ data-root the state storage boundary; one data root is one instance
79
+ ```
80
+
81
+ A run writes an append-only trace alongside the state its declared side effects produce:
82
+
83
+ ```
84
+ traces/<TRACE_ID>/
85
+ <TRACE_ID>.jsonl append-only execution log
86
+ <TRACE_ID>.md human-readable summary
87
+ <TRACE_ID>.png the execution path, rendered
88
+
89
+ data/
90
+ registry/ idempotent state
91
+ events/ append-only history
92
+ ```
93
+
94
+ ## Running
95
+
96
+ ```bash
97
+ ./run.sh # warm-boot the sibling assembled snapshot
98
+ ./run.sh boot --snapshot /abs/snapshot # explicit boot
99
+ ./run.sh run --wf <domain>::WF_… --data-root /abs/instance
100
+ ./run.sh examine /abs/trace.jsonl
101
+ ```
102
+
103
+ `run.sh` wraps the CLI, also installed as the `protocol_runtime` console script, with four
104
+ subcommands:
105
+
106
+ | command | what it does |
107
+ |---|---|
108
+ | `run` | execute a workflow against a data root |
109
+ | `boot` | warm-boot the assembled snapshot — load and hash-verify every manifest domain |
110
+ | `examine` | analyze a completed trace file |
111
+ | `behavior-logic` | render the execution path from a completed trace as a PNG |
112
+
113
+ `PGC_SNAPSHOT_ROOT` overrides the snapshot location; `PGC_IMPL_ROOTS` is the colon-separated set of
114
+ roots on `PYTHONPATH` for domain capability implementations.
115
+
116
+ **Warm reboot is its own proof.** Bringing every manifest domain resident and hash-verified
117
+ establishes that the snapshot is intact and executable before any workflow runs. A surface-only
118
+ snapshot has no workflow to traverse, and warm reboot is exactly what proves it sound anyway.
119
+
120
+ **A data root is an instance, not an interface.** Two data roots against the same snapshot are two
121
+ independent instances of the same governed behavior.
122
+
123
+ ## How execution works
124
+
125
+ The runtime loads the compiled graph, admits the request against the intent that declares it, and
126
+ walks the workflow node by node. At each node it executes the capability contract's steps —
127
+ invoking transforms, applying side effects — and routes on the declared outcome. It resolves
128
+ nothing by name at execution time: the compiler assigned integer addresses, and traversal operates
129
+ on those.
130
+
131
+ Every step emits evidence. The trace is not a log the runtime chose to write; it is the record of
132
+ the path actually taken through a graph that was fixed before the run began, which is what makes a
133
+ run reproducible and reviewable after the fact.
134
+
135
+ ## License
136
+
137
+ Apache-2.0. See `LICENSE` and `NOTICE`.
138
+
139
+ ---
140
+
141
+ ## The package family
142
+
143
+ | Package | Repository | Role |
144
+ |---|---|---|
145
+ | `pgc-compiler` | `protocol_compiler` | declarations → compiled projections |
146
+ | `pgc-assembler` | `snapshot_assembler` | projections → sealed snapshot |
147
+ | `pgc-runtime` | `protocol_runtime` | snapshot → governed execution |
148
+ | `pgc-inspector` | `snapshot_inspector` | snapshot → read-only inspection |
149
+ | `pgc-transformation` | `transformation` | change request → protocol artifacts |
150
+ | `pgc-governance` | `software_governance` | the governance surface and its capability implementations |
151
+ | `pgc-workloads` | `conformance_workloads` | the workloads that make conformance observable |
152
+ | `pgc-domains` | `business_domains` | the business domain implementations the composed snapshot binds |
153
+
154
+ `pip install pgc` brings in the whole family.
155
+
156
+ **Installing the toolchain is one of two steps.** The compiler resolves the governance surface from
157
+ `PGC_PLATFORM_ROOT` — fail-hard, cwd-independent, zero inference — so the *declarations* come from a
158
+ repository you point at, never from a wheel. A registry inside a package would be a second governance
159
+ surface competing with the repository's, and a build could then be governed by a stale copy.
160
+
161
+ ```bash
162
+ git clone https://github.com/protocol-governed-computing/software_governance
163
+ export PGC_PLATFORM_ROOT=$PWD/software_governance
164
+ pgc # reports what is installed and whether the anchor resolves
165
+ ```
166
+
167
+ `PGC_BUILD_ROOT` (compiled output, keeping the governance repo read-only) and `PGC_DOMAIN_ROOTS`
168
+ (additional domains contributing their own `registry/structures`) are optional.
169
+
170
+ **Versioning.** Two schemes, and the published version follows the second.
171
+
172
+ - **Internal** — each repository's `VERSION` file, a monotonic composition ordinal. PGC versions the
173
+ composition rather than each repo: they release together and the governance closure forces lockstep,
174
+ so the ordinal names which composition a repo belongs to. Development happens on `dev/<N>` and each
175
+ cycle is tagged `release-<N>`. This is not published.
176
+ - **Public** — `PUBLIC_VERSION`, tagged on every component repository. The platform is at **`v2`**.
177
+
178
+ **The published version is the public one: `v2` is `2.0.0`.** The standard the packages implement is a
179
+ separate artifact on its own track and is not this number.
180
+
181
+ The standard these packages implement is published separately: https://doi.org/10.5281/zenodo.22150616
@@ -0,0 +1,28 @@
1
+ pgc_runtime-2.0.0.dist-info/licenses/LICENSE,sha256=wf4s-Px76q2zHB2b8Gbvuu4ULua8AMds2IY01MkBn0A,2544
2
+ pgc_runtime-2.0.0.dist-info/licenses/NOTICE,sha256=nE4--l6r3rMCz0nVaFyFohZtY4NYADnrhcS9K2pdNr0,407
3
+ runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ runtime/api.py,sha256=zMBTeMHYL2WlcIeZZXT9v3hakt_2jk6nywt_rDlTjxM,2927
5
+ runtime/boot.py,sha256=sVdQMXSDs8ncmaDjORwxArO8SyJMtiDm-q4bvrdYFrE,6384
6
+ runtime/cli.py,sha256=y1Vurnirxqg1p5WNZpdiEAZ4-YWMlOXlsSS0JspRMSE,14478
7
+ runtime/conformance.py,sha256=h_50oqS0A6AZhwAf_CGcP372kQogRz_KRXiDdV1IMsA,9835
8
+ runtime/ct_errors.py,sha256=Uywmhi_-Zr94fUW7IpLx7RnVrKhjn7UaLHswv9HXEVI,974
9
+ runtime/ct_execute.py,sha256=BqeYskwggWo9J7Jm1qZoWk5bBdhA5d_3fGbw-SwQpA0,2821
10
+ runtime/ct_executor.py,sha256=ETxYAaHk1sEePVinsOShHFgH62VRGi6Zeqltg02UU4Q,11104
11
+ runtime/dispatcher.py,sha256=bJL15rFtNFacHv4urWRaz8TtOxp7-fRHHBFb8EbTqNA,18506
12
+ runtime/evidence.py,sha256=yHVLpkFam-hv_RZogQ1cmHYltllmdaEQJxCHy2FNAr4,9944
13
+ runtime/loader.py,sha256=Nponv5HHhspGfVdWvQl9P5Cvld6L0vh5rfevAWpub1w,14262
14
+ runtime/memory.py,sha256=3relq5_mA_kBK0SF_j2pqRt3e8D-7WoZY5H3mxN1twI,4597
15
+ runtime/scheduler.py,sha256=g0SsWi1sFKU1RZyVGYK42aq3_X5AACjvzJYJaAl8FLQ,12299
16
+ runtime/trace_viz.py,sha256=ZNTxKLI-T9m6Hma94cbwR980Hih_1f9qFcpK73vMSq4,9335
17
+ runtime/examine/__init__.py,sha256=3k-QGphzMSqgHsIRLAfs_x_D6ZcMtnCekZw3qCLXAcM,4057
18
+ runtime/examine/classifier.py,sha256=w_aemZJSouYgZh-k4-yKcNekZnct9QgCHY99TJ7jhN4,12530
19
+ runtime/examine/cli.py,sha256=xSxwr3GdwNgy7KU_toRo5GIsw1_VmSsak5-gKeopHSc,731
20
+ runtime/examine/hint_engine.py,sha256=c1ZwwSUxDhMmT4IUKHdstuG_gBicG6JvyNRJI9oP318,8700
21
+ runtime/examine/locator.py,sha256=MPo6U8McsBeLjt_7PTqK8qdZTRaSrMnApmNjZM6ftfE,5682
22
+ runtime/examine/parser.py,sha256=csUt_iHHaygrWHhuNAgTX___dK8Q72GRSlpLyuj75hs,8469
23
+ runtime/examine/reporter.py,sha256=eG47mj4KwYGzwi_WvHEw8W58B5YaiWcjE6AX5jcPgqE,3344
24
+ pgc_runtime-2.0.0.dist-info/METADATA,sha256=KlAEUKC0Q7jox_Ta1NupfqqqxUDvlwJ-0kErHMwW9JM,7706
25
+ pgc_runtime-2.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
26
+ pgc_runtime-2.0.0.dist-info/entry_points.txt,sha256=Braa-8c9Zk3ONAJmtDK982Yra7JVfqbPHOm8HCC6UQ0,54
27
+ pgc_runtime-2.0.0.dist-info/top_level.txt,sha256=-unY84bWeVaGfe3vfIlHYZmkol7p_-E1YKa_rnrmxAc,8
28
+ pgc_runtime-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ protocol_runtime = runtime.cli:main
@@ -0,0 +1,67 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 Bhash Ganti aka Bachi
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+
19
+
20
+ ------------------------
21
+ FULL LICENSE TEXT BELOW
22
+ ------------------------
23
+
24
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
25
+
26
+ 1. Definitions.
27
+ "License" shall mean the terms and conditions for use, reproduction,
28
+ and distribution as defined by Sections 1 through 9 of this document.
29
+ "Licensor" shall mean the copyright owner.
30
+ "Legal Entity" shall mean the union of the acting entity and all other
31
+ entities that control, are controlled by, or are under common control.
32
+ "You" shall mean an individual or Legal Entity exercising permissions.
33
+ "Source" form shall mean the preferred form for making modifications.
34
+ "Object" form shall mean any form resulting from mechanical transformation.
35
+ "Work" shall mean the work of authorship.
36
+ "Derivative Works" shall mean any work based on the Work.
37
+ "Contribution" shall mean any work intentionally submitted.
38
+ "Contributor" shall mean Licensor and any individual submitting Contributions.
39
+
40
+ 2. Grant of Copyright License.
41
+ Each Contributor grants You a perpetual, worldwide, non-exclusive,
42
+ no-charge, royalty-free copyright license.
43
+
44
+ 3. Grant of Patent License.
45
+ Each Contributor grants a patent license to make, use, sell, etc.
46
+
47
+ 4. Redistribution.
48
+ You may reproduce and distribute copies provided that:
49
+ - You include a copy of this License
50
+ - You retain notices
51
+ - You state modifications
52
+ - You include NOTICE file if present
53
+
54
+ 5. Submission of Contributions.
55
+ Contributions are under this License unless stated otherwise.
56
+
57
+ 6. Trademarks.
58
+ This License does not grant trademark rights.
59
+
60
+ 7. Disclaimer of Warranty.
61
+ Provided "AS IS", without warranties.
62
+
63
+ 8. Limitation of Liability.
64
+ No liability for damages.
65
+
66
+ 9. Accepting Warranty or Additional Liability.
67
+ You may offer support/warranty on your own behalf only.
@@ -0,0 +1,11 @@
1
+ PGS — Protocol-Governed Systems
2
+ Copyright 2026 Bhash Ganti aka Bachi
3
+
4
+ This project introduces a protocol-first execution model in which:
5
+
6
+ - Behavior is declared in protocol artifacts
7
+ - Execution is performed by a deterministic runtime
8
+ - Capability implementations are bound at compile time
9
+ - Governance is enforced through invariants and assertions
10
+
11
+ Extensibility is achieved by declaration, not refactor.
@@ -0,0 +1 @@
1
+ runtime
runtime/__init__.py ADDED
File without changes
runtime/api.py ADDED
@@ -0,0 +1,71 @@
1
+ """Programmatic runtime entry — run a workflow against a snapshot and return its result surface.
2
+
3
+ This is the in-process core behind `runtime run`: it loads the domain snapshot, opens a trace, drives
4
+ the workflow topology via `scheduler.run_wf`, and returns the terminal status **and result surface**. The
5
+ CLI is a thin wrapper over it, and out-of-process consumers that need the workflow surface programmatically
6
+ (e.g. the change-management validation pipeline) call this directly instead of shelling out and parsing
7
+ stdout or data files.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ from runtime.boot import boot
16
+ from runtime.evidence import TraceWriter, make_trace_id
17
+ from runtime.scheduler import run_wf
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class RunResult:
22
+ status: str # terminal workflow outcome (e.g. "SUCCESS", "ACK", "VIOLATION")
23
+ surface: dict[str, Any] # workflow result surface (the observable outputs)
24
+ trace_id: str
25
+ trace_dir: Path
26
+
27
+
28
+ def run_workflow(
29
+ *,
30
+ wf_fqdn: str,
31
+ payload: dict[str, Any],
32
+ data_root: str | Path,
33
+ snapshot_root: str | Path | None = None,
34
+ ) -> RunResult:
35
+ """Warm-boot the assembled snapshot and execute a workflow; return `(status, surface, trace)`.
36
+
37
+ The snapshot (assembled product) is read-only input, verified via its manifest (root of trust).
38
+ All mutable output is scoped to the instance root `data_root`: CS state and `data_root/traces/`.
39
+ `snapshot_root` defaults to the sibling `../snapshot` when None.
40
+ Raises on load/vocab errors and propagates runtime exceptions (after recording them to the trace).
41
+ """
42
+ data_root = Path(data_root)
43
+ domain = wf_fqdn.split("::")[0]
44
+
45
+ booted = boot(snapshot_root)
46
+ pkg = booted.domains.get(domain)
47
+ if pkg is None:
48
+ raise RuntimeError(
49
+ f"Domain {domain!r} is not in the assembled snapshot "
50
+ f"(manifest domains: {sorted(booted.domains)})."
51
+ )
52
+
53
+ trace_id = make_trace_id(domain, wf_fqdn, payload)
54
+ wf_code = wf_fqdn.split("::")[-1]
55
+ trace_dir = data_root / "traces" / domain / wf_code / trace_id
56
+ trace_dir.mkdir(parents=True, exist_ok=True)
57
+
58
+ wf_addr = pkg.vocab.addr(wf_fqdn) # KeyError if the WF is not in the snapshot vocab
59
+ writer = TraceWriter(trace_dir=trace_dir, trace_id=trace_id, domain=domain,
60
+ wf_addr=wf_addr, wf_fqdn=wf_fqdn, snapshot_root=booted.snapshot_root,
61
+ snapshot_id=booted.snapshot_id)
62
+ try:
63
+ status, surface = run_wf(wf_fqdn=wf_fqdn, payload=payload, pkg=pkg,
64
+ writer=writer, data_root=str(data_root))
65
+ except Exception as exc:
66
+ writer.error(str(exc))
67
+ raise
68
+ finally:
69
+ writer.close()
70
+
71
+ return RunResult(status=status, surface=surface or {}, trace_id=trace_id, trace_dir=trace_dir)
runtime/boot.py ADDED
@@ -0,0 +1,147 @@
1
+ """
2
+ boot.py — warm reboot: bring the assembled snapshot resident and hash-verified.
3
+
4
+ The manifest is the ROOT OF TRUST. The runtime boots *through* it, never by scanning the
5
+ filesystem. Contract: snapshot_assembler/doc/SNAPSHOT_ASSEMBLY_CONTRACT.md
6
+
7
+ Boot sequence:
8
+ 1. load manifest.json
9
+ 2. recompute composite_hash from the identity view of domains[] → MUST equal manifest's
10
+ (verifier INDEPENDENCE: the runtime recomputes per the contract; it does not import the
11
+ assembler's code to verify the assembler's output)
12
+ 3. per domain: load_domain(..., expected_tokenized_hash=<manifest>) — anchors on-disk to manifest
13
+ 4. build one RuntimePackage per domain
14
+ 5. warm reboot complete = all manifest domains resident + hash-verified
15
+
16
+ There is no WF to traverse in a surface-only snapshot (e.g. platform); warm reboot proves the
17
+ substrate loads and verifies. Execution is a separate step against an already-booted universe.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import hashlib
23
+ import json
24
+ import os
25
+ from dataclasses import dataclass
26
+ from pathlib import Path
27
+ from typing import Any
28
+
29
+ from runtime.loader import RuntimePackage, load_domain
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class BootedSnapshot:
34
+ """The resident, verified executable universe."""
35
+ snapshot_id: str
36
+ snapshot_root: Path
37
+ manifest: dict[str, Any]
38
+ domains: dict[str, RuntimePackage]
39
+
40
+ # The nine execution concerns, in flow order (shown when present in a domain).
41
+ _CONCERNS = ("TI", "AC", "IN", "WF", "CC", "CT", "CS", "EV", "TE")
42
+ _SYSTEM_NS = ("node_kind", "edge_kind", "outcome", "transition")
43
+
44
+ def summary(self) -> str:
45
+ from collections import Counter
46
+
47
+ lines = [
48
+ f"snapshot_id: {self.snapshot_id}",
49
+ f"domains: {len(self.domains)}",
50
+ ]
51
+ for name, pkg in self.domains.items():
52
+ # Concern breakdown straight from the domain vocabulary (counts every declared artifact,
53
+ # including AC_/EV_ which are not in the executable dispatch/handlers tables).
54
+ counts: Counter = Counter()
55
+ for fqdn in pkg.vocab.forward.values():
56
+ if "::" not in fqdn:
57
+ continue
58
+ ns, code = fqdn.split("::", 1)
59
+ if ns in self._SYSTEM_NS:
60
+ continue
61
+ counts[code.split("_")[0]] += 1
62
+ concerns = " ".join(f"{counts[c]} {c}" for c in self._CONCERNS if counts.get(c))
63
+ lines.append(f" - {name}: {concerns or '(no execution concerns)'} · {len(pkg.vocab.forward)} addr")
64
+ return "\n".join(lines)
65
+
66
+
67
+ def default_snapshot_root() -> Path:
68
+ """PGC_SNAPSHOT_ROOT, or the sibling `../snapshot` of this repo (umbrella product dir)."""
69
+ env = os.environ.get("PGC_SNAPSHOT_ROOT")
70
+ if env:
71
+ return Path(env)
72
+ # runtime/boot.py → runtime/ (pkg) → protocol_runtime/ (repo) → protocol-governed-computing/
73
+ return Path(__file__).resolve().parents[2] / "snapshot"
74
+
75
+
76
+ # --- composite hash: independent reimplementation of the assembly contract -----------------
77
+
78
+ def _identity_view(domains: list[dict]) -> list[dict]:
79
+ view = [
80
+ {
81
+ "domain": d["domain"],
82
+ "tokenized_projection_hash": d["projections"]["tokenized"]["projection_hash"],
83
+ "vocabulary_projection_hash": d["projections"]["vocabulary"]["projection_hash"],
84
+ # Canonical joins the view because every other member is graph-derived, and STRUCTURE
85
+ # artifacts never enter the semantic graph — without it a STRUCTURE artifact could
86
+ # change inside a sealed snapshot and boot would still attest the same identity.
87
+ "canonical_projection_hash": d["projections"]["canonical"]["projection_hash"],
88
+ "attestation_hash": d["projections"]["trust"]["attestation_hash"],
89
+ "graph_address_hash": d["graph_address_hash"],
90
+ }
91
+ for d in domains
92
+ ]
93
+ return sorted(view, key=lambda e: e["domain"])
94
+
95
+
96
+ def _composite_hash(domains: list[dict]) -> str:
97
+ canonical = json.dumps(_identity_view(domains), sort_keys=True, separators=(",", ":"))
98
+ return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
99
+
100
+
101
+ def _load_manifest(snapshot_root: Path) -> dict:
102
+ path = snapshot_root / "manifest.json"
103
+ if not path.exists():
104
+ raise FileNotFoundError(f"Assembled snapshot manifest missing: {path}")
105
+ with path.open(encoding="utf-8") as f:
106
+ return json.load(f)
107
+
108
+
109
+ def boot(snapshot_root: str | Path | None = None) -> BootedSnapshot:
110
+ """Warm-boot the assembled snapshot: verify the manifest, load + anchor every domain."""
111
+ root = Path(snapshot_root) if snapshot_root is not None else default_snapshot_root()
112
+ manifest = _load_manifest(root)
113
+ domains_meta = manifest.get("domains", [])
114
+
115
+ # 2. ACCEPTANCE — all four conditions of `3b` §7, established from content.
116
+ #
117
+ # The runtime does not carry its own weaker copy of this. It previously recomputed the composite
118
+ # over the manifest's RECORDED per-domain hashes, which detects a tampered manifest and not a
119
+ # tampered constituent — and a snapshot with an edited projection booted and reported healthy.
120
+ # `assembler.core.verify_snapshot` recomputes every constituent from its bytes; there is one
121
+ # acceptance determination and both the assembler and the runtime reach it.
122
+ #
123
+ # Importing it is not a layering breach: acceptance is a determination ABOUT a snapshot (`3b` §7),
124
+ # not part of assembling one, and a second implementation of one determination is two things that
125
+ # can disagree.
126
+ from assembler.core import AssemblyError, verify_snapshot
127
+ try:
128
+ verify_snapshot(root)
129
+ except AssemblyError as exc:
130
+ raise RuntimeError(f"Snapshot refused at acceptance: {exc}") from exc
131
+ recomputed = manifest.get("snapshot_id")
132
+
133
+ # 3-4. per-domain load, anchored to the manifest's tokenized hash
134
+ domains: dict[str, RuntimePackage] = {}
135
+ for d in domains_meta:
136
+ name = d["domain"]
137
+ domains[name] = load_domain(
138
+ root, name,
139
+ expected_tokenized_hash=d["projections"]["tokenized"]["projection_hash"],
140
+ )
141
+
142
+ return BootedSnapshot(
143
+ snapshot_id=manifest.get("snapshot_id", recomputed),
144
+ snapshot_root=root,
145
+ manifest=manifest,
146
+ domains=domains,
147
+ )