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.
- pgc_runtime-2.0.0.dist-info/METADATA +181 -0
- pgc_runtime-2.0.0.dist-info/RECORD +28 -0
- pgc_runtime-2.0.0.dist-info/WHEEL +5 -0
- pgc_runtime-2.0.0.dist-info/entry_points.txt +2 -0
- pgc_runtime-2.0.0.dist-info/licenses/LICENSE +67 -0
- pgc_runtime-2.0.0.dist-info/licenses/NOTICE +11 -0
- pgc_runtime-2.0.0.dist-info/top_level.txt +1 -0
- runtime/__init__.py +0 -0
- runtime/api.py +71 -0
- runtime/boot.py +147 -0
- runtime/cli.py +387 -0
- runtime/conformance.py +249 -0
- runtime/ct_errors.py +33 -0
- runtime/ct_execute.py +84 -0
- runtime/ct_executor.py +312 -0
- runtime/dispatcher.py +448 -0
- runtime/evidence.py +245 -0
- runtime/examine/__init__.py +126 -0
- runtime/examine/classifier.py +350 -0
- runtime/examine/cli.py +35 -0
- runtime/examine/hint_engine.py +245 -0
- runtime/examine/locator.py +148 -0
- runtime/examine/parser.py +223 -0
- runtime/examine/reporter.py +110 -0
- runtime/loader.py +360 -0
- runtime/memory.py +125 -0
- runtime/scheduler.py +264 -0
- runtime/trace_viz.py +276 -0
|
@@ -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,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
|
+
)
|