pgc-runtime 3.0.0__tar.gz → 5.0.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.
- {pgc_runtime-3.0.0/pgc_runtime.egg-info → pgc_runtime-5.0.0}/PKG-INFO +54 -17
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/README.md +51 -16
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0/pgc_runtime.egg-info}/PKG-INFO +54 -17
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/pgc_runtime.egg-info/SOURCES.txt +9 -4
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/pgc_runtime.egg-info/requires.txt +3 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/pyproject.toml +4 -1
- pgc_runtime-5.0.0/runtime/api.py +173 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/boot.py +37 -24
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/cli.py +136 -15
- pgc_runtime-5.0.0/runtime/conformance.py +353 -0
- pgc_runtime-5.0.0/runtime/coordinator.py +184 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/ct_execute.py +3 -1
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/ct_executor.py +127 -24
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/dispatcher.py +12 -4
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/evidence.py +62 -9
- pgc_runtime-5.0.0/runtime/examine/__init__.py +49 -0
- pgc_runtime-5.0.0/runtime/examine/parser.py +73 -0
- pgc_runtime-5.0.0/runtime/examine/reporter.py +57 -0
- pgc_runtime-5.0.0/runtime/federation/__init__.py +16 -0
- pgc_runtime-5.0.0/runtime/federation/client.py +106 -0
- pgc_runtime-5.0.0/runtime/federation/coordinator.py +106 -0
- pgc_runtime-5.0.0/runtime/federation/defaults.py +22 -0
- pgc_runtime-5.0.0/runtime/federation/store.py +85 -0
- pgc_runtime-5.0.0/runtime/federation/worker.py +72 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/loader.py +28 -33
- pgc_runtime-5.0.0/runtime/replay.py +77 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/scheduler.py +36 -27
- pgc_runtime-5.0.0/runtime/trace_viz.py +233 -0
- pgc_runtime-3.0.0/runtime/api.py +0 -71
- pgc_runtime-3.0.0/runtime/conformance.py +0 -249
- pgc_runtime-3.0.0/runtime/examine/__init__.py +0 -126
- pgc_runtime-3.0.0/runtime/examine/classifier.py +0 -350
- pgc_runtime-3.0.0/runtime/examine/hint_engine.py +0 -245
- pgc_runtime-3.0.0/runtime/examine/locator.py +0 -148
- pgc_runtime-3.0.0/runtime/examine/parser.py +0 -223
- pgc_runtime-3.0.0/runtime/examine/reporter.py +0 -110
- pgc_runtime-3.0.0/runtime/trace_viz.py +0 -276
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/LICENSE +0 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/MANIFEST.in +0 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/NOTICE +0 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/pgc_runtime.egg-info/dependency_links.txt +0 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/pgc_runtime.egg-info/entry_points.txt +0 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/pgc_runtime.egg-info/top_level.txt +0 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/__init__.py +0 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/ct_errors.py +0 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/examine/cli.py +0 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/runtime/memory.py +0 -0
- {pgc_runtime-3.0.0 → pgc_runtime-5.0.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pgc-runtime
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0
|
|
4
4
|
Summary: PGC protocol runtime — executes a sealed snapshot as governed DAG traversal and emits traces (import package: runtime)
|
|
5
5
|
Author-email: Bhash Ganti <bachipeachy@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -22,6 +22,8 @@ License-File: LICENSE
|
|
|
22
22
|
License-File: NOTICE
|
|
23
23
|
Provides-Extra: reference
|
|
24
24
|
Requires-Dist: pgc-governance>=2.0.0; extra == "reference"
|
|
25
|
+
Provides-Extra: render
|
|
26
|
+
Requires-Dist: pgc-inspector>=5.0.0; extra == "render"
|
|
25
27
|
Dynamic: license-file
|
|
26
28
|
|
|
27
29
|
# protocol_runtime
|
|
@@ -78,17 +80,17 @@ payload external input (JSON)
|
|
|
78
80
|
data-root the state storage boundary; one data root is one instance
|
|
79
81
|
```
|
|
80
82
|
|
|
81
|
-
A run writes an append-only trace
|
|
83
|
+
A run writes an append-only trace beside the state its declared side effects produce, both under
|
|
84
|
+
the data root:
|
|
82
85
|
|
|
83
86
|
```
|
|
84
|
-
traces/<TRACE_ID>/
|
|
85
|
-
<TRACE_ID>.jsonl append-only execution log
|
|
86
|
-
<TRACE_ID>.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
data
|
|
90
|
-
|
|
91
|
-
events/ append-only history
|
|
87
|
+
<data-root>/traces/<domain>/<WF>/<TRACE_ID>/
|
|
88
|
+
<TRACE_ID>.jsonl append-only execution log, one SCHEMA_TRACE_EVENT_V1 event per line
|
|
89
|
+
<TRACE_ID>.png the path the run took, drawn only on request (`run --behavior-logic`,
|
|
90
|
+
or `behavior-logic <trace>`)
|
|
91
|
+
|
|
92
|
+
<data-root>/<domain>/<subdomain>/
|
|
93
|
+
the stores the domain's runtime binding declares
|
|
92
94
|
```
|
|
93
95
|
|
|
94
96
|
## Running
|
|
@@ -100,15 +102,18 @@ data/
|
|
|
100
102
|
./run.sh examine /abs/trace.jsonl
|
|
101
103
|
```
|
|
102
104
|
|
|
103
|
-
`run.sh` wraps the CLI, also installed as the `protocol_runtime` console script
|
|
104
|
-
subcommands:
|
|
105
|
+
`run.sh` wraps the CLI, also installed as the `protocol_runtime` console script:
|
|
105
106
|
|
|
106
107
|
| command | what it does |
|
|
107
108
|
|---|---|
|
|
108
109
|
| `run` | execute a workflow against a data root |
|
|
110
|
+
| `replay` | re-execute a run from its recorded outcomes and compare it with the original |
|
|
111
|
+
| `conformance` | run a compiled domain's test vectors; report each transform proven, unproven or refused |
|
|
109
112
|
| `boot` | warm-boot the assembled snapshot — load and hash-verify every manifest domain |
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
113
|
+
| `coordinator` | serve as the coordinating node of a federated node group |
|
|
114
|
+
| `worker` | serve as a worker node of a federated node group |
|
|
115
|
+
| `examine` | read a completed trace: its path by node, contracts, results, events and errors |
|
|
116
|
+
| `behavior-logic` | render a completed trace as a PNG: the path, each route's outcome, and why each node decided as far as the trace records it (drawn from the inspector's `si.execution.explain`; needs `pgc-runtime[render]`) |
|
|
112
117
|
|
|
113
118
|
`PGC_SNAPSHOT_ROOT` overrides the snapshot location; `PGC_IMPL_ROOTS` is the colon-separated set of
|
|
114
119
|
roots on `PYTHONPATH` for domain capability implementations.
|
|
@@ -117,6 +122,12 @@ roots on `PYTHONPATH` for domain capability implementations.
|
|
|
117
122
|
establishes that the snapshot is intact and executable before any workflow runs. A surface-only
|
|
118
123
|
snapshot has no workflow to traverse, and warm reboot is exactly what proves it sound anyway.
|
|
119
124
|
|
|
125
|
+
**A process verifies a snapshot once.** `runtime.api` keeps a booted snapshot resident and runs
|
|
126
|
+
against it as often as it is asked. A rewritten manifest, or a different trust anchor or profile
|
|
127
|
+
root, is verified afresh, and a snapshot that no longer verifies is refused. `boot` itself always
|
|
128
|
+
performs the full determination. A governed run then costs a few milliseconds, not a third of a
|
|
129
|
+
second.
|
|
130
|
+
|
|
120
131
|
**A data root is an instance, not an interface.** Two data roots against the same snapshot are two
|
|
121
132
|
independent instances of the same governed behavior.
|
|
122
133
|
|
|
@@ -128,6 +139,21 @@ invoking transforms, applying side effects — and routes on the declared outcom
|
|
|
128
139
|
nothing by name at execution time: the compiler assigned integer addresses, and traversal operates
|
|
129
140
|
on those.
|
|
130
141
|
|
|
142
|
+
**A workflow may run one contract at several places.** Each place is a node key, and the contract
|
|
143
|
+
it runs is named separately. The compiler seals each place's routing on its own, and refuses a sealed
|
|
144
|
+
dispatch that does not realize every transition declared at that node. Routing is by node, and the
|
|
145
|
+
trace names the place that ran.
|
|
146
|
+
|
|
147
|
+
**Not every step is determined by its inputs.** A transform is a deterministic atom, a
|
|
148
|
+
non-deterministic atom, or a molecule, and a different constitution governs each. A molecule has no
|
|
149
|
+
implementation: the runtime runs its declared steps. A non-deterministic atom's result is recorded
|
|
150
|
+
where it is produced, and `replay` substitutes the recorded result, so a run is reproducible even
|
|
151
|
+
when a step is not.
|
|
152
|
+
|
|
153
|
+
**One snapshot can run across nodes.** Under a federated placement, a coordinator schedules units of
|
|
154
|
+
work to workers over a shared store. Under the signed federated profile, every node verifies the
|
|
155
|
+
snapshot's signature before it runs anything.
|
|
156
|
+
|
|
131
157
|
Every step emits evidence. The trace is not a log the runtime chose to write; it is the record of
|
|
132
158
|
the path actually taken through a graph that was fixed before the run began, which is what makes a
|
|
133
159
|
run reproducible and reviewable after the fact.
|
|
@@ -164,8 +190,19 @@ export PGC_PLATFORM_ROOT=$PWD/software_governance
|
|
|
164
190
|
pgc # reports what is installed and whether the anchor resolves
|
|
165
191
|
```
|
|
166
192
|
|
|
167
|
-
`
|
|
168
|
-
|
|
193
|
+
`PGC_DOMAIN_ROOTS` names an additional domain contributing its own `registry/structures` — the
|
|
194
|
+
directory that *directly contains* it, not the repository above it; pointing one level too high is a
|
|
195
|
+
silent no-op. `PGC_SNAPSHOT_PROFILES` is the directory holding snapshot profiles, required by the
|
|
196
|
+
assembler and the runtime alike.
|
|
197
|
+
|
|
198
|
+
**Where a build writes is declared, not supplied.** Each build configuration names its root in
|
|
199
|
+
`output_configuration.root`, and every layer's output consolidates there. The compiler does not read
|
|
200
|
+
`PGC_SNAPSHOT_ROOT`; the runtime does, with its own meaning — the assembled snapshot to execute.
|
|
201
|
+
|
|
202
|
+
`PGC_BUILD_ROOT` is accepted and reported and **nothing reads it**.
|
|
203
|
+
|
|
204
|
+
The full sequence, with the repositories it needs, is in
|
|
205
|
+
[`pgc_install`](https://github.com/protocol-governed-computing/pgc_install).
|
|
169
206
|
|
|
170
207
|
**Versioning.** Two schemes, and the published version follows the second.
|
|
171
208
|
|
|
@@ -175,7 +212,7 @@ pgc # reports what is installed and whether the anchor resolves
|
|
|
175
212
|
cycle is tagged `release-<N>`. This is not published.
|
|
176
213
|
- **Public** — `PUBLIC_VERSION`, tagged on every component repository. The platform is at **`v3`**.
|
|
177
214
|
|
|
178
|
-
**The published version is the public one: `
|
|
215
|
+
**The published version is the public one: `v4` is `4.0.0`.** The standard the packages implement is a
|
|
179
216
|
separate artifact on its own track and is not this number.
|
|
180
217
|
|
|
181
218
|
The standard these packages implement is published separately: https://doi.org/10.5281/zenodo.22150616
|
|
@@ -52,17 +52,17 @@ payload external input (JSON)
|
|
|
52
52
|
data-root the state storage boundary; one data root is one instance
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
A run writes an append-only trace
|
|
55
|
+
A run writes an append-only trace beside the state its declared side effects produce, both under
|
|
56
|
+
the data root:
|
|
56
57
|
|
|
57
58
|
```
|
|
58
|
-
traces/<TRACE_ID>/
|
|
59
|
-
<TRACE_ID>.jsonl append-only execution log
|
|
60
|
-
<TRACE_ID>.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
data
|
|
64
|
-
|
|
65
|
-
events/ append-only history
|
|
59
|
+
<data-root>/traces/<domain>/<WF>/<TRACE_ID>/
|
|
60
|
+
<TRACE_ID>.jsonl append-only execution log, one SCHEMA_TRACE_EVENT_V1 event per line
|
|
61
|
+
<TRACE_ID>.png the path the run took, drawn only on request (`run --behavior-logic`,
|
|
62
|
+
or `behavior-logic <trace>`)
|
|
63
|
+
|
|
64
|
+
<data-root>/<domain>/<subdomain>/
|
|
65
|
+
the stores the domain's runtime binding declares
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
## Running
|
|
@@ -74,15 +74,18 @@ data/
|
|
|
74
74
|
./run.sh examine /abs/trace.jsonl
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
`run.sh` wraps the CLI, also installed as the `protocol_runtime` console script
|
|
78
|
-
subcommands:
|
|
77
|
+
`run.sh` wraps the CLI, also installed as the `protocol_runtime` console script:
|
|
79
78
|
|
|
80
79
|
| command | what it does |
|
|
81
80
|
|---|---|
|
|
82
81
|
| `run` | execute a workflow against a data root |
|
|
82
|
+
| `replay` | re-execute a run from its recorded outcomes and compare it with the original |
|
|
83
|
+
| `conformance` | run a compiled domain's test vectors; report each transform proven, unproven or refused |
|
|
83
84
|
| `boot` | warm-boot the assembled snapshot — load and hash-verify every manifest domain |
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
85
|
+
| `coordinator` | serve as the coordinating node of a federated node group |
|
|
86
|
+
| `worker` | serve as a worker node of a federated node group |
|
|
87
|
+
| `examine` | read a completed trace: its path by node, contracts, results, events and errors |
|
|
88
|
+
| `behavior-logic` | render a completed trace as a PNG: the path, each route's outcome, and why each node decided as far as the trace records it (drawn from the inspector's `si.execution.explain`; needs `pgc-runtime[render]`) |
|
|
86
89
|
|
|
87
90
|
`PGC_SNAPSHOT_ROOT` overrides the snapshot location; `PGC_IMPL_ROOTS` is the colon-separated set of
|
|
88
91
|
roots on `PYTHONPATH` for domain capability implementations.
|
|
@@ -91,6 +94,12 @@ roots on `PYTHONPATH` for domain capability implementations.
|
|
|
91
94
|
establishes that the snapshot is intact and executable before any workflow runs. A surface-only
|
|
92
95
|
snapshot has no workflow to traverse, and warm reboot is exactly what proves it sound anyway.
|
|
93
96
|
|
|
97
|
+
**A process verifies a snapshot once.** `runtime.api` keeps a booted snapshot resident and runs
|
|
98
|
+
against it as often as it is asked. A rewritten manifest, or a different trust anchor or profile
|
|
99
|
+
root, is verified afresh, and a snapshot that no longer verifies is refused. `boot` itself always
|
|
100
|
+
performs the full determination. A governed run then costs a few milliseconds, not a third of a
|
|
101
|
+
second.
|
|
102
|
+
|
|
94
103
|
**A data root is an instance, not an interface.** Two data roots against the same snapshot are two
|
|
95
104
|
independent instances of the same governed behavior.
|
|
96
105
|
|
|
@@ -102,6 +111,21 @@ invoking transforms, applying side effects — and routes on the declared outcom
|
|
|
102
111
|
nothing by name at execution time: the compiler assigned integer addresses, and traversal operates
|
|
103
112
|
on those.
|
|
104
113
|
|
|
114
|
+
**A workflow may run one contract at several places.** Each place is a node key, and the contract
|
|
115
|
+
it runs is named separately. The compiler seals each place's routing on its own, and refuses a sealed
|
|
116
|
+
dispatch that does not realize every transition declared at that node. Routing is by node, and the
|
|
117
|
+
trace names the place that ran.
|
|
118
|
+
|
|
119
|
+
**Not every step is determined by its inputs.** A transform is a deterministic atom, a
|
|
120
|
+
non-deterministic atom, or a molecule, and a different constitution governs each. A molecule has no
|
|
121
|
+
implementation: the runtime runs its declared steps. A non-deterministic atom's result is recorded
|
|
122
|
+
where it is produced, and `replay` substitutes the recorded result, so a run is reproducible even
|
|
123
|
+
when a step is not.
|
|
124
|
+
|
|
125
|
+
**One snapshot can run across nodes.** Under a federated placement, a coordinator schedules units of
|
|
126
|
+
work to workers over a shared store. Under the signed federated profile, every node verifies the
|
|
127
|
+
snapshot's signature before it runs anything.
|
|
128
|
+
|
|
105
129
|
Every step emits evidence. The trace is not a log the runtime chose to write; it is the record of
|
|
106
130
|
the path actually taken through a graph that was fixed before the run began, which is what makes a
|
|
107
131
|
run reproducible and reviewable after the fact.
|
|
@@ -138,8 +162,19 @@ export PGC_PLATFORM_ROOT=$PWD/software_governance
|
|
|
138
162
|
pgc # reports what is installed and whether the anchor resolves
|
|
139
163
|
```
|
|
140
164
|
|
|
141
|
-
`
|
|
142
|
-
|
|
165
|
+
`PGC_DOMAIN_ROOTS` names an additional domain contributing its own `registry/structures` — the
|
|
166
|
+
directory that *directly contains* it, not the repository above it; pointing one level too high is a
|
|
167
|
+
silent no-op. `PGC_SNAPSHOT_PROFILES` is the directory holding snapshot profiles, required by the
|
|
168
|
+
assembler and the runtime alike.
|
|
169
|
+
|
|
170
|
+
**Where a build writes is declared, not supplied.** Each build configuration names its root in
|
|
171
|
+
`output_configuration.root`, and every layer's output consolidates there. The compiler does not read
|
|
172
|
+
`PGC_SNAPSHOT_ROOT`; the runtime does, with its own meaning — the assembled snapshot to execute.
|
|
173
|
+
|
|
174
|
+
`PGC_BUILD_ROOT` is accepted and reported and **nothing reads it**.
|
|
175
|
+
|
|
176
|
+
The full sequence, with the repositories it needs, is in
|
|
177
|
+
[`pgc_install`](https://github.com/protocol-governed-computing/pgc_install).
|
|
143
178
|
|
|
144
179
|
**Versioning.** Two schemes, and the published version follows the second.
|
|
145
180
|
|
|
@@ -149,7 +184,7 @@ pgc # reports what is installed and whether the anchor resolves
|
|
|
149
184
|
cycle is tagged `release-<N>`. This is not published.
|
|
150
185
|
- **Public** — `PUBLIC_VERSION`, tagged on every component repository. The platform is at **`v3`**.
|
|
151
186
|
|
|
152
|
-
**The published version is the public one: `
|
|
187
|
+
**The published version is the public one: `v4` is `4.0.0`.** The standard the packages implement is a
|
|
153
188
|
separate artifact on its own track and is not this number.
|
|
154
189
|
|
|
155
190
|
The standard these packages implement is published separately: https://doi.org/10.5281/zenodo.22150616
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pgc-runtime
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0
|
|
4
4
|
Summary: PGC protocol runtime — executes a sealed snapshot as governed DAG traversal and emits traces (import package: runtime)
|
|
5
5
|
Author-email: Bhash Ganti <bachipeachy@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -22,6 +22,8 @@ License-File: LICENSE
|
|
|
22
22
|
License-File: NOTICE
|
|
23
23
|
Provides-Extra: reference
|
|
24
24
|
Requires-Dist: pgc-governance>=2.0.0; extra == "reference"
|
|
25
|
+
Provides-Extra: render
|
|
26
|
+
Requires-Dist: pgc-inspector>=5.0.0; extra == "render"
|
|
25
27
|
Dynamic: license-file
|
|
26
28
|
|
|
27
29
|
# protocol_runtime
|
|
@@ -78,17 +80,17 @@ payload external input (JSON)
|
|
|
78
80
|
data-root the state storage boundary; one data root is one instance
|
|
79
81
|
```
|
|
80
82
|
|
|
81
|
-
A run writes an append-only trace
|
|
83
|
+
A run writes an append-only trace beside the state its declared side effects produce, both under
|
|
84
|
+
the data root:
|
|
82
85
|
|
|
83
86
|
```
|
|
84
|
-
traces/<TRACE_ID>/
|
|
85
|
-
<TRACE_ID>.jsonl append-only execution log
|
|
86
|
-
<TRACE_ID>.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
data
|
|
90
|
-
|
|
91
|
-
events/ append-only history
|
|
87
|
+
<data-root>/traces/<domain>/<WF>/<TRACE_ID>/
|
|
88
|
+
<TRACE_ID>.jsonl append-only execution log, one SCHEMA_TRACE_EVENT_V1 event per line
|
|
89
|
+
<TRACE_ID>.png the path the run took, drawn only on request (`run --behavior-logic`,
|
|
90
|
+
or `behavior-logic <trace>`)
|
|
91
|
+
|
|
92
|
+
<data-root>/<domain>/<subdomain>/
|
|
93
|
+
the stores the domain's runtime binding declares
|
|
92
94
|
```
|
|
93
95
|
|
|
94
96
|
## Running
|
|
@@ -100,15 +102,18 @@ data/
|
|
|
100
102
|
./run.sh examine /abs/trace.jsonl
|
|
101
103
|
```
|
|
102
104
|
|
|
103
|
-
`run.sh` wraps the CLI, also installed as the `protocol_runtime` console script
|
|
104
|
-
subcommands:
|
|
105
|
+
`run.sh` wraps the CLI, also installed as the `protocol_runtime` console script:
|
|
105
106
|
|
|
106
107
|
| command | what it does |
|
|
107
108
|
|---|---|
|
|
108
109
|
| `run` | execute a workflow against a data root |
|
|
110
|
+
| `replay` | re-execute a run from its recorded outcomes and compare it with the original |
|
|
111
|
+
| `conformance` | run a compiled domain's test vectors; report each transform proven, unproven or refused |
|
|
109
112
|
| `boot` | warm-boot the assembled snapshot — load and hash-verify every manifest domain |
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
113
|
+
| `coordinator` | serve as the coordinating node of a federated node group |
|
|
114
|
+
| `worker` | serve as a worker node of a federated node group |
|
|
115
|
+
| `examine` | read a completed trace: its path by node, contracts, results, events and errors |
|
|
116
|
+
| `behavior-logic` | render a completed trace as a PNG: the path, each route's outcome, and why each node decided as far as the trace records it (drawn from the inspector's `si.execution.explain`; needs `pgc-runtime[render]`) |
|
|
112
117
|
|
|
113
118
|
`PGC_SNAPSHOT_ROOT` overrides the snapshot location; `PGC_IMPL_ROOTS` is the colon-separated set of
|
|
114
119
|
roots on `PYTHONPATH` for domain capability implementations.
|
|
@@ -117,6 +122,12 @@ roots on `PYTHONPATH` for domain capability implementations.
|
|
|
117
122
|
establishes that the snapshot is intact and executable before any workflow runs. A surface-only
|
|
118
123
|
snapshot has no workflow to traverse, and warm reboot is exactly what proves it sound anyway.
|
|
119
124
|
|
|
125
|
+
**A process verifies a snapshot once.** `runtime.api` keeps a booted snapshot resident and runs
|
|
126
|
+
against it as often as it is asked. A rewritten manifest, or a different trust anchor or profile
|
|
127
|
+
root, is verified afresh, and a snapshot that no longer verifies is refused. `boot` itself always
|
|
128
|
+
performs the full determination. A governed run then costs a few milliseconds, not a third of a
|
|
129
|
+
second.
|
|
130
|
+
|
|
120
131
|
**A data root is an instance, not an interface.** Two data roots against the same snapshot are two
|
|
121
132
|
independent instances of the same governed behavior.
|
|
122
133
|
|
|
@@ -128,6 +139,21 @@ invoking transforms, applying side effects — and routes on the declared outcom
|
|
|
128
139
|
nothing by name at execution time: the compiler assigned integer addresses, and traversal operates
|
|
129
140
|
on those.
|
|
130
141
|
|
|
142
|
+
**A workflow may run one contract at several places.** Each place is a node key, and the contract
|
|
143
|
+
it runs is named separately. The compiler seals each place's routing on its own, and refuses a sealed
|
|
144
|
+
dispatch that does not realize every transition declared at that node. Routing is by node, and the
|
|
145
|
+
trace names the place that ran.
|
|
146
|
+
|
|
147
|
+
**Not every step is determined by its inputs.** A transform is a deterministic atom, a
|
|
148
|
+
non-deterministic atom, or a molecule, and a different constitution governs each. A molecule has no
|
|
149
|
+
implementation: the runtime runs its declared steps. A non-deterministic atom's result is recorded
|
|
150
|
+
where it is produced, and `replay` substitutes the recorded result, so a run is reproducible even
|
|
151
|
+
when a step is not.
|
|
152
|
+
|
|
153
|
+
**One snapshot can run across nodes.** Under a federated placement, a coordinator schedules units of
|
|
154
|
+
work to workers over a shared store. Under the signed federated profile, every node verifies the
|
|
155
|
+
snapshot's signature before it runs anything.
|
|
156
|
+
|
|
131
157
|
Every step emits evidence. The trace is not a log the runtime chose to write; it is the record of
|
|
132
158
|
the path actually taken through a graph that was fixed before the run began, which is what makes a
|
|
133
159
|
run reproducible and reviewable after the fact.
|
|
@@ -164,8 +190,19 @@ export PGC_PLATFORM_ROOT=$PWD/software_governance
|
|
|
164
190
|
pgc # reports what is installed and whether the anchor resolves
|
|
165
191
|
```
|
|
166
192
|
|
|
167
|
-
`
|
|
168
|
-
|
|
193
|
+
`PGC_DOMAIN_ROOTS` names an additional domain contributing its own `registry/structures` — the
|
|
194
|
+
directory that *directly contains* it, not the repository above it; pointing one level too high is a
|
|
195
|
+
silent no-op. `PGC_SNAPSHOT_PROFILES` is the directory holding snapshot profiles, required by the
|
|
196
|
+
assembler and the runtime alike.
|
|
197
|
+
|
|
198
|
+
**Where a build writes is declared, not supplied.** Each build configuration names its root in
|
|
199
|
+
`output_configuration.root`, and every layer's output consolidates there. The compiler does not read
|
|
200
|
+
`PGC_SNAPSHOT_ROOT`; the runtime does, with its own meaning — the assembled snapshot to execute.
|
|
201
|
+
|
|
202
|
+
`PGC_BUILD_ROOT` is accepted and reported and **nothing reads it**.
|
|
203
|
+
|
|
204
|
+
The full sequence, with the repositories it needs, is in
|
|
205
|
+
[`pgc_install`](https://github.com/protocol-governed-computing/pgc_install).
|
|
169
206
|
|
|
170
207
|
**Versioning.** Two schemes, and the published version follows the second.
|
|
171
208
|
|
|
@@ -175,7 +212,7 @@ pgc # reports what is installed and whether the anchor resolves
|
|
|
175
212
|
cycle is tagged `release-<N>`. This is not published.
|
|
176
213
|
- **Public** — `PUBLIC_VERSION`, tagged on every component repository. The platform is at **`v3`**.
|
|
177
214
|
|
|
178
|
-
**The published version is the public one: `
|
|
215
|
+
**The published version is the public one: `v4` is `4.0.0`.** The standard the packages implement is a
|
|
179
216
|
separate artifact on its own track and is not this number.
|
|
180
217
|
|
|
181
218
|
The standard these packages implement is published separately: https://doi.org/10.5281/zenodo.22150616
|
|
@@ -14,6 +14,7 @@ runtime/api.py
|
|
|
14
14
|
runtime/boot.py
|
|
15
15
|
runtime/cli.py
|
|
16
16
|
runtime/conformance.py
|
|
17
|
+
runtime/coordinator.py
|
|
17
18
|
runtime/ct_errors.py
|
|
18
19
|
runtime/ct_execute.py
|
|
19
20
|
runtime/ct_executor.py
|
|
@@ -21,12 +22,16 @@ runtime/dispatcher.py
|
|
|
21
22
|
runtime/evidence.py
|
|
22
23
|
runtime/loader.py
|
|
23
24
|
runtime/memory.py
|
|
25
|
+
runtime/replay.py
|
|
24
26
|
runtime/scheduler.py
|
|
25
27
|
runtime/trace_viz.py
|
|
26
28
|
runtime/examine/__init__.py
|
|
27
|
-
runtime/examine/classifier.py
|
|
28
29
|
runtime/examine/cli.py
|
|
29
|
-
runtime/examine/hint_engine.py
|
|
30
|
-
runtime/examine/locator.py
|
|
31
30
|
runtime/examine/parser.py
|
|
32
|
-
runtime/examine/reporter.py
|
|
31
|
+
runtime/examine/reporter.py
|
|
32
|
+
runtime/federation/__init__.py
|
|
33
|
+
runtime/federation/client.py
|
|
34
|
+
runtime/federation/coordinator.py
|
|
35
|
+
runtime/federation/defaults.py
|
|
36
|
+
runtime/federation/store.py
|
|
37
|
+
runtime/federation/worker.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pgc-runtime"
|
|
7
|
-
version = "
|
|
7
|
+
version = "5.0.0"
|
|
8
8
|
description = "PGC protocol runtime — executes a sealed snapshot as governed DAG traversal and emits traces (import package: runtime)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -32,6 +32,9 @@ dependencies = []
|
|
|
32
32
|
|
|
33
33
|
[project.optional-dependencies]
|
|
34
34
|
reference = ["pgc-governance>=2.0.0"]
|
|
35
|
+
# The PNG projection (`--behavior-logic`, `behavior-logic`) draws the inspector's explanation of a
|
|
36
|
+
# run. Execution never imports the inspector; only `runtime.trace_viz` does.
|
|
37
|
+
render = ["pgc-inspector>=5.0.0"]
|
|
35
38
|
|
|
36
39
|
[project.scripts]
|
|
37
40
|
protocol_runtime = "runtime.cli:main"
|
|
@@ -0,0 +1,173 @@
|
|
|
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
|
+
import functools
|
|
12
|
+
import os
|
|
13
|
+
import threading
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from runtime.boot import BootedSnapshot, boot, default_snapshot_root
|
|
19
|
+
from runtime.evidence import TraceWriter, make_trace_id
|
|
20
|
+
from runtime.scheduler import run_wf
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class RunResult:
|
|
25
|
+
status: str # terminal workflow outcome (e.g. "SUCCESS", "ACK", "VIOLATION")
|
|
26
|
+
surface: dict[str, Any] # workflow result surface (the observable outputs)
|
|
27
|
+
trace_id: str
|
|
28
|
+
trace_dir: Path
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def run_workflow(
|
|
32
|
+
*,
|
|
33
|
+
wf_fqdn: str,
|
|
34
|
+
payload: dict[str, Any],
|
|
35
|
+
data_root: str | Path,
|
|
36
|
+
snapshot_root: str | Path | None = None,
|
|
37
|
+
replay_trace: str | Path | None = None,
|
|
38
|
+
) -> RunResult:
|
|
39
|
+
"""Warm-boot the assembled snapshot and execute a workflow; return `(status, surface, trace)`.
|
|
40
|
+
|
|
41
|
+
The snapshot (assembled product) is read-only input, verified via its manifest (root of trust).
|
|
42
|
+
All mutable output is scoped to the instance root `data_root`: CS state and `data_root/traces/`.
|
|
43
|
+
`snapshot_root` defaults to the sibling `../snapshot` when None.
|
|
44
|
+
Raises on load/vocab errors and propagates runtime exceptions (after recording them to the trace).
|
|
45
|
+
"""
|
|
46
|
+
data_root = Path(data_root)
|
|
47
|
+
domain = wf_fqdn.split("::")[0]
|
|
48
|
+
|
|
49
|
+
booted = resident(snapshot_root)
|
|
50
|
+
pkg = booted.domains.get(domain)
|
|
51
|
+
if pkg is None:
|
|
52
|
+
raise RuntimeError(
|
|
53
|
+
f"Domain {domain!r} is not in the assembled snapshot "
|
|
54
|
+
f"(manifest domains: {sorted(booted.domains)})."
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
trace_id = make_trace_id(domain, wf_fqdn, payload)
|
|
58
|
+
wf_code = wf_fqdn.split("::")[-1]
|
|
59
|
+
trace_dir = data_root / "traces" / domain / wf_code / trace_id
|
|
60
|
+
trace_dir.mkdir(parents=True, exist_ok=True)
|
|
61
|
+
|
|
62
|
+
wf_addr = pkg.vocab.addr(wf_fqdn) # KeyError if the WF is not in the snapshot vocab
|
|
63
|
+
writer = TraceWriter(trace_dir=trace_dir, trace_id=trace_id, domain=domain,
|
|
64
|
+
wf_addr=wf_addr, wf_fqdn=wf_fqdn, snapshot_root=booted.snapshot_root,
|
|
65
|
+
snapshot_id=booted.snapshot_id)
|
|
66
|
+
if replay_trace is not None:
|
|
67
|
+
# A replay substitutes each recorded outcome of an atom declared not deterministic, so it
|
|
68
|
+
# reproduces the original determination rather than drawing a new one.
|
|
69
|
+
from runtime.replay import recorded_outcomes
|
|
70
|
+
writer.replay_from(recorded_outcomes(Path(replay_trace)))
|
|
71
|
+
try:
|
|
72
|
+
status, surface = run_wf(wf_fqdn=wf_fqdn, payload=payload, pkg=pkg,
|
|
73
|
+
writer=writer, data_root=str(data_root))
|
|
74
|
+
except Exception as exc:
|
|
75
|
+
writer.error(str(exc))
|
|
76
|
+
raise
|
|
77
|
+
finally:
|
|
78
|
+
# The JSONL is the evidence. A picture of the path is a view of it, drawn on request
|
|
79
|
+
# (`run --behavior-logic`, or `behavior-logic <trace>`), never as a cost of every run.
|
|
80
|
+
writer.close()
|
|
81
|
+
|
|
82
|
+
return RunResult(status=status, surface=surface or {}, trace_id=trace_id, trace_dir=trace_dir)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Warm boot is load once, verify once. Every call booting afresh re-verified the whole snapshot from
|
|
86
|
+
# its bytes on each run — about 60% of a run's time, repeated by every server, worker and suite that
|
|
87
|
+
# calls this in a loop. The package a boot produces is frozen, and nothing reads the snapshot again
|
|
88
|
+
# after it, so one verified boot serves every run against the same snapshot.
|
|
89
|
+
#
|
|
90
|
+
# What makes it the same snapshot is the manifest — its identity, and the file itself — together
|
|
91
|
+
# with what acceptance was judged against: the trust anchor and the profile root. A rebuild in place
|
|
92
|
+
# rewrites the manifest, so it is verified afresh. `boot()` itself is never cached: the CLI's `boot`
|
|
93
|
+
# and anything asking for acceptance get the full determination every time.
|
|
94
|
+
_RESIDENT: dict[tuple, BootedSnapshot] = {}
|
|
95
|
+
_RESIDENT_LOCK = threading.Lock()
|
|
96
|
+
_RESIDENT_LIMIT = 4
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _resident_key(root: Path) -> tuple | None:
|
|
100
|
+
manifest = root / "manifest.json"
|
|
101
|
+
try:
|
|
102
|
+
stat = manifest.stat()
|
|
103
|
+
except FileNotFoundError:
|
|
104
|
+
return None
|
|
105
|
+
return (str(root), stat.st_mtime_ns, stat.st_size,
|
|
106
|
+
os.environ.get("PGC_TRUST_ROOT_PUBKEY"), os.environ.get("PGC_SNAPSHOT_PROFILES"))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def resident(snapshot_root: str | Path | None = None) -> BootedSnapshot:
|
|
110
|
+
"""The booted snapshot at `snapshot_root`, verified once per process and per manifest."""
|
|
111
|
+
given = Path(snapshot_root) if snapshot_root is not None else default_snapshot_root()
|
|
112
|
+
key = _resident_key(given.resolve())
|
|
113
|
+
if key is None:
|
|
114
|
+
return boot(given) # no manifest: let boot refuse it, and say why
|
|
115
|
+
with _RESIDENT_LOCK:
|
|
116
|
+
booted = _RESIDENT.get(key)
|
|
117
|
+
if booted is None:
|
|
118
|
+
booted = boot(given)
|
|
119
|
+
if len(_RESIDENT) >= _RESIDENT_LIMIT:
|
|
120
|
+
_RESIDENT.pop(next(iter(_RESIDENT)))
|
|
121
|
+
_RESIDENT[key] = booted
|
|
122
|
+
return booted
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@functools.lru_cache(maxsize=8)
|
|
126
|
+
def _placement(snapshot_root: Path) -> str | None:
|
|
127
|
+
# The snapshot is immutable, so what it was permitted cannot change under a running process.
|
|
128
|
+
from runtime.coordinator import sealed_placement_mode
|
|
129
|
+
return sealed_placement_mode(snapshot_root)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def invoke_workflow(
|
|
133
|
+
*,
|
|
134
|
+
wf_fqdn: str,
|
|
135
|
+
payload: dict[str, Any],
|
|
136
|
+
data_root: str | Path,
|
|
137
|
+
snapshot_root: str | Path | None = None,
|
|
138
|
+
) -> RunResult:
|
|
139
|
+
"""Have a workflow executed wherever the sealed composition places execution.
|
|
140
|
+
|
|
141
|
+
The entry point for a party that receives work but is not itself where work is placed — the
|
|
142
|
+
interaction boundary. Under `FEDERATED_NODE` the unit goes to the coordinator named by
|
|
143
|
+
`PGC_COORDINATOR_URL` and a worker executes it; under every other placement it runs here through
|
|
144
|
+
`run_workflow`. Either way the result is the same `RunResult`, with `trace_dir` under the shared
|
|
145
|
+
`data_root`.
|
|
146
|
+
|
|
147
|
+
Under `FEDERATED_NODE` this waits for the unit's outcome without limit once it is admitted, so
|
|
148
|
+
a failure it raises is always one in which nothing was admitted (see `submit_and_wait`).
|
|
149
|
+
|
|
150
|
+
Placement is read from the snapshot, not from configuration: a boundary that chose its own
|
|
151
|
+
arrangement would be granting itself what the composition was or was not permitted. Execution
|
|
152
|
+
itself never consults placement — this decides only where `run_workflow` is called.
|
|
153
|
+
"""
|
|
154
|
+
root = Path(snapshot_root) if snapshot_root is not None else default_snapshot_root()
|
|
155
|
+
if _placement(root.resolve()) != "FEDERATED_NODE":
|
|
156
|
+
return run_workflow(wf_fqdn=wf_fqdn, payload=payload, data_root=data_root, snapshot_root=root)
|
|
157
|
+
|
|
158
|
+
from runtime.federation.client import submit_and_wait
|
|
159
|
+
|
|
160
|
+
coordinator_url = os.environ.get("PGC_COORDINATOR_URL")
|
|
161
|
+
if not coordinator_url:
|
|
162
|
+
raise RuntimeError(
|
|
163
|
+
"the snapshot places execution on FEDERATED_NODE and PGC_COORDINATOR_URL is not set: "
|
|
164
|
+
"this node does not execute, and has nowhere to send the work."
|
|
165
|
+
)
|
|
166
|
+
booted = resident(root) # a boundary acts on no snapshot it has not authenticated (OB-1)
|
|
167
|
+
outcome = submit_and_wait(coordinator_url.rstrip("/"), wf_fqdn=wf_fqdn, payload=payload,
|
|
168
|
+
snapshot_id=booted.snapshot_id)
|
|
169
|
+
if outcome.get("state") != "executed":
|
|
170
|
+
raise RuntimeError(f"unit {outcome.get('unit_id')} failed on {outcome.get('worker')}: "
|
|
171
|
+
f"{outcome.get('detail')}")
|
|
172
|
+
return RunResult(status=outcome["status"], surface=outcome.get("surface") or {},
|
|
173
|
+
trace_id=outcome["trace_id"], trace_dir=Path(data_root) / outcome["trace_dir"])
|