openworkproof 1.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.
- openworkproof/__init__.py +1 -0
- openworkproof/acceptance.py +3006 -0
- openworkproof/cli.py +164 -0
- openworkproof/composition.py +832 -0
- openworkproof/evidence.py +8281 -0
- openworkproof/execution_adapter.py +256 -0
- openworkproof/external_acceptor.py +211 -0
- openworkproof/mcp_server.py +2939 -0
- openworkproof/mcp_transport.py +72 -0
- openworkproof/models.py +3380 -0
- openworkproof/policy.py +2494 -0
- openworkproof/predicates.py +333 -0
- openworkproof/repo_pipeline/__init__.py +74 -0
- openworkproof/repo_pipeline/analysis.py +152 -0
- openworkproof/repo_pipeline/errors.py +64 -0
- openworkproof/repo_pipeline/models.py +80 -0
- openworkproof/repo_pipeline/output.py +68 -0
- openworkproof/repo_pipeline/reader.py +47 -0
- openworkproof/repo_pipeline/sources.py +120 -0
- openworkproof/repo_pipeline/traversal.py +253 -0
- openworkproof/repo_tools.py +8382 -0
- openworkproof/runtime_context.py +189 -0
- openworkproof/schema_registry.py +623 -0
- openworkproof/schemas/v0.1/acceptance-receipt.schema.json +1 -0
- openworkproof/schemas/v0.1/acceptance-rejection-receipt.schema.json +1 -0
- openworkproof/schemas/v0.1/action-receipt.schema.json +1 -0
- openworkproof/schemas/v0.1/capability-grant.schema.json +1 -0
- openworkproof/schemas/v0.1/schema-registry.json +1 -0
- openworkproof/schemas/v0.1/work-order.schema.json +1 -0
- openworkproof/signing.py +421 -0
- openworkproof/state.py +765 -0
- openworkproof/team_network_client.py +419 -0
- openworkproof/trusted_helper.py +269 -0
- openworkproof-1.0.0.dist-info/METADATA +578 -0
- openworkproof-1.0.0.dist-info/RECORD +39 -0
- openworkproof-1.0.0.dist-info/WHEEL +5 -0
- openworkproof-1.0.0.dist-info/entry_points.txt +2 -0
- openworkproof-1.0.0.dist-info/licenses/LICENSE +202 -0
- openworkproof-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,832 @@
|
|
|
1
|
+
"""Bounded causal replay for authorization receipt histories."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from itertools import islice
|
|
8
|
+
|
|
9
|
+
from openworkproof.models import (
|
|
10
|
+
ActionReceiptEnvelope,
|
|
11
|
+
ApprovalDecisionReceipt,
|
|
12
|
+
ApprovalRequestedReceipt,
|
|
13
|
+
ComposeProofArguments,
|
|
14
|
+
CreatePrProposalArguments,
|
|
15
|
+
GrantConsumedReceipt,
|
|
16
|
+
GrantIssuedReceipt,
|
|
17
|
+
GrantRevokedReceipt,
|
|
18
|
+
RollbackReceipt,
|
|
19
|
+
SystemEventReceipt,
|
|
20
|
+
TerminationDecisionReceipt,
|
|
21
|
+
ToolCallReceipt,
|
|
22
|
+
WorkOrder,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
MAX_AUTHORIZATION_RECEIPTS = 64
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AuthorizationCausalityError(RuntimeError):
|
|
30
|
+
"""Authorization receipt causality could not be replayed exactly."""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class AuthorizationCausalSnapshot:
|
|
35
|
+
"""Immutable causal facts visible at one signed receipt."""
|
|
36
|
+
|
|
37
|
+
active_patch_receipt_id: str | None
|
|
38
|
+
failure_receipt_id: str | None
|
|
39
|
+
rollback_receipt_id: str | None
|
|
40
|
+
latest_composition_trigger_id: str | None
|
|
41
|
+
independent_result_receipt_id: str | None
|
|
42
|
+
approval_decision_by_request: tuple[tuple[str, str], ...]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class AuthorizationCausalState(AuthorizationCausalSnapshot):
|
|
47
|
+
"""Immutable final facts and bounded per-receipt causal snapshots."""
|
|
48
|
+
|
|
49
|
+
independent_failure_terminal: bool
|
|
50
|
+
snapshots_by_receipt: tuple[
|
|
51
|
+
tuple[str, AuthorizationCausalSnapshot],
|
|
52
|
+
...,
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
def snapshot_for(self, receipt_id: str) -> AuthorizationCausalSnapshot:
|
|
56
|
+
for candidate_id, snapshot in self.snapshots_by_receipt:
|
|
57
|
+
if candidate_id == receipt_id:
|
|
58
|
+
return snapshot
|
|
59
|
+
raise _fail("receipt causal snapshot is unavailable")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True)
|
|
63
|
+
class _CausalIndex:
|
|
64
|
+
by_id: dict[str, ActionReceiptEnvelope]
|
|
65
|
+
by_digest: dict[str, ActionReceiptEnvelope]
|
|
66
|
+
by_sequence: dict[int, ActionReceiptEnvelope]
|
|
67
|
+
issuance_by_grant: dict[str, GrantIssuedReceipt]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _fail(message: str) -> AuthorizationCausalityError:
|
|
71
|
+
return AuthorizationCausalityError(message)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _bounded_receipts(
|
|
75
|
+
receipts: Iterable[ActionReceiptEnvelope],
|
|
76
|
+
) -> tuple[ActionReceiptEnvelope, ...]:
|
|
77
|
+
try:
|
|
78
|
+
bounded = tuple(
|
|
79
|
+
islice(receipts, MAX_AUTHORIZATION_RECEIPTS + 1)
|
|
80
|
+
)
|
|
81
|
+
except AuthorizationCausalityError:
|
|
82
|
+
raise
|
|
83
|
+
except Exception as error:
|
|
84
|
+
raise _fail(
|
|
85
|
+
"receipt history input is unavailable"
|
|
86
|
+
) from error
|
|
87
|
+
if len(bounded) > MAX_AUTHORIZATION_RECEIPTS:
|
|
88
|
+
raise _fail("receipt history exceeds its bounded input capacity")
|
|
89
|
+
return bounded
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _build_index(
|
|
93
|
+
receipts: tuple[ActionReceiptEnvelope, ...],
|
|
94
|
+
) -> _CausalIndex:
|
|
95
|
+
by_id: dict[str, ActionReceiptEnvelope] = {}
|
|
96
|
+
by_digest: dict[str, ActionReceiptEnvelope] = {}
|
|
97
|
+
by_sequence: dict[int, ActionReceiptEnvelope] = {}
|
|
98
|
+
issuance_by_grant: dict[str, GrantIssuedReceipt] = {}
|
|
99
|
+
for receipt in receipts:
|
|
100
|
+
if (
|
|
101
|
+
receipt.receipt_id in by_id
|
|
102
|
+
or receipt.digest in by_digest
|
|
103
|
+
or receipt.sequence in by_sequence
|
|
104
|
+
):
|
|
105
|
+
raise _fail("receipt history index is not unique")
|
|
106
|
+
by_id[receipt.receipt_id] = receipt
|
|
107
|
+
by_digest[receipt.digest] = receipt
|
|
108
|
+
by_sequence[receipt.sequence] = receipt
|
|
109
|
+
if (
|
|
110
|
+
isinstance(receipt, GrantIssuedReceipt)
|
|
111
|
+
and receipt.policy_decision == "allow"
|
|
112
|
+
and receipt.execution_status == "succeeded"
|
|
113
|
+
and receipt.issued_grant_id is not None
|
|
114
|
+
):
|
|
115
|
+
if receipt.issued_grant_id in issuance_by_grant:
|
|
116
|
+
raise _fail("Grant issuance index is not unique")
|
|
117
|
+
issuance_by_grant[receipt.issued_grant_id] = receipt
|
|
118
|
+
return _CausalIndex(
|
|
119
|
+
by_id=by_id,
|
|
120
|
+
by_digest=by_digest,
|
|
121
|
+
by_sequence=by_sequence,
|
|
122
|
+
issuance_by_grant=issuance_by_grant,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def validate_authorization_causal_bindings(
|
|
127
|
+
work_order: WorkOrder,
|
|
128
|
+
receipts: tuple[ActionReceiptEnvelope, ...],
|
|
129
|
+
causal_state: AuthorizationCausalState,
|
|
130
|
+
) -> None:
|
|
131
|
+
"""Compare caller state with canonical replay of its bounded snapshot."""
|
|
132
|
+
|
|
133
|
+
canonical = replay_authorization_causality(work_order, receipts)
|
|
134
|
+
if canonical != causal_state:
|
|
135
|
+
raise _fail(
|
|
136
|
+
"canonical causal state rejects the supplied causal binding"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def replay_authorization_causality(
|
|
141
|
+
work_order: WorkOrder,
|
|
142
|
+
receipts: Iterable[ActionReceiptEnvelope],
|
|
143
|
+
) -> AuthorizationCausalState:
|
|
144
|
+
"""Replay exact semantic parents and return the frozen causal state."""
|
|
145
|
+
|
|
146
|
+
if not isinstance(work_order, WorkOrder):
|
|
147
|
+
raise _fail("WorkOrder is unavailable for causal replay")
|
|
148
|
+
bounded = _bounded_receipts(receipts)
|
|
149
|
+
if any(
|
|
150
|
+
not isinstance(receipt, ActionReceiptEnvelope)
|
|
151
|
+
for receipt in bounded
|
|
152
|
+
):
|
|
153
|
+
raise _fail("receipt history contains a malformed receipt")
|
|
154
|
+
index = _build_index(bounded)
|
|
155
|
+
ordered = bounded
|
|
156
|
+
roles_by_key = {
|
|
157
|
+
binding.key_id: binding.role
|
|
158
|
+
for binding in work_order.key_bindings
|
|
159
|
+
}
|
|
160
|
+
roles_by_actor = {
|
|
161
|
+
(binding.subject_id, binding.key_id): binding.role
|
|
162
|
+
for binding in work_order.key_bindings
|
|
163
|
+
}
|
|
164
|
+
previous: ActionReceiptEnvelope | None = None
|
|
165
|
+
for expected_sequence, receipt in enumerate(ordered, start=1):
|
|
166
|
+
if (
|
|
167
|
+
receipt.work_order_digest != work_order.digest
|
|
168
|
+
or receipt.sequence != expected_sequence
|
|
169
|
+
or receipt.previous_receipt_digest
|
|
170
|
+
!= (previous.digest if previous is not None else None)
|
|
171
|
+
):
|
|
172
|
+
raise _fail(
|
|
173
|
+
"receipt chain origin or adjacency is invalid"
|
|
174
|
+
)
|
|
175
|
+
previous = receipt
|
|
176
|
+
if ordered:
|
|
177
|
+
first = ordered[0]
|
|
178
|
+
root_grant_id = work_order.root_grant_template.grant_id
|
|
179
|
+
root_activation = (
|
|
180
|
+
isinstance(first, GrantIssuedReceipt)
|
|
181
|
+
and first.parent_grant_id is None
|
|
182
|
+
and first.authorizing_grant_id == root_grant_id
|
|
183
|
+
and first.issued_grant_id == root_grant_id
|
|
184
|
+
and first.policy_decision == "allow"
|
|
185
|
+
and first.execution_status == "succeeded"
|
|
186
|
+
and first.state_before == "issued"
|
|
187
|
+
and first.state_after == "running"
|
|
188
|
+
and first.parent_receipt_ids == ()
|
|
189
|
+
and roles_by_actor.get(
|
|
190
|
+
(first.actor_id, first.actor_key_id)
|
|
191
|
+
)
|
|
192
|
+
== "Manager"
|
|
193
|
+
)
|
|
194
|
+
cause = (
|
|
195
|
+
first.cause
|
|
196
|
+
if isinstance(first, SystemEventReceipt)
|
|
197
|
+
else None
|
|
198
|
+
)
|
|
199
|
+
contract_expiry = (
|
|
200
|
+
isinstance(first, SystemEventReceipt)
|
|
201
|
+
and first.system_event_name == "contract_expired"
|
|
202
|
+
and first.error_code == "CONTRACT_EXPIRED"
|
|
203
|
+
and first.policy_decision == "not_applicable"
|
|
204
|
+
and first.execution_status == "succeeded"
|
|
205
|
+
and first.state_before == "issued"
|
|
206
|
+
and first.state_after == "frozen"
|
|
207
|
+
and first.parent_receipt_ids == ()
|
|
208
|
+
and roles_by_actor.get(
|
|
209
|
+
(first.actor_id, first.actor_key_id)
|
|
210
|
+
)
|
|
211
|
+
== "Sidecar"
|
|
212
|
+
and getattr(cause, "deadline", None)
|
|
213
|
+
== work_order.deadline
|
|
214
|
+
and getattr(cause, "observed_at", None)
|
|
215
|
+
== first.occurred_at
|
|
216
|
+
and getattr(cause, "tip_receipt_digest", None) is None
|
|
217
|
+
and first.occurred_at > work_order.deadline
|
|
218
|
+
)
|
|
219
|
+
maintainer_termination = (
|
|
220
|
+
isinstance(first, TerminationDecisionReceipt)
|
|
221
|
+
and first.policy_decision == "allow"
|
|
222
|
+
and first.execution_status == "succeeded"
|
|
223
|
+
and first.state_before == "issued"
|
|
224
|
+
and first.state_after == "rejected"
|
|
225
|
+
and first.parent_receipt_ids == ()
|
|
226
|
+
and first.target_work_order_digest == work_order.digest
|
|
227
|
+
and roles_by_actor.get(
|
|
228
|
+
(first.actor_id, first.actor_key_id)
|
|
229
|
+
)
|
|
230
|
+
== "Maintainer"
|
|
231
|
+
and first.decided_at >= work_order.issued_at
|
|
232
|
+
and first.occurred_at >= first.decided_at
|
|
233
|
+
and (
|
|
234
|
+
first.occurred_at - first.decided_at
|
|
235
|
+
).total_seconds()
|
|
236
|
+
<= 300
|
|
237
|
+
and first.occurred_at <= work_order.deadline
|
|
238
|
+
)
|
|
239
|
+
if sum(
|
|
240
|
+
(root_activation, contract_expiry, maintainer_termination)
|
|
241
|
+
) != 1:
|
|
242
|
+
raise _fail("receipt chain has no valid genesis event")
|
|
243
|
+
prior: list[ActionReceiptEnvelope] = []
|
|
244
|
+
active_patch: ToolCallReceipt | None = None
|
|
245
|
+
current_failure: ToolCallReceipt | None = None
|
|
246
|
+
current_rollback: RollbackReceipt | None = None
|
|
247
|
+
latest_passing_result: ToolCallReceipt | None = None
|
|
248
|
+
latest_composition_trigger: SystemEventReceipt | None = None
|
|
249
|
+
independent_result: ToolCallReceipt | None = None
|
|
250
|
+
independent_failure_terminal = False
|
|
251
|
+
last_independent_attempt: ToolCallReceipt | None = None
|
|
252
|
+
sealed_tail_appended = False
|
|
253
|
+
started_execution_context_ids: set[str] = set()
|
|
254
|
+
started_container_instance_digests: set[str] = set()
|
|
255
|
+
latest_revocation: dict[str, GrantRevokedReceipt] = {}
|
|
256
|
+
decision_by_request: dict[str, ApprovalDecisionReceipt] = {}
|
|
257
|
+
snapshots_by_receipt: list[
|
|
258
|
+
tuple[str, AuthorizationCausalSnapshot]
|
|
259
|
+
] = []
|
|
260
|
+
|
|
261
|
+
def verifier_result_passed(receipt: ToolCallReceipt) -> bool:
|
|
262
|
+
results = tuple(
|
|
263
|
+
result
|
|
264
|
+
for result in receipt.predicate_results
|
|
265
|
+
if result.name == "tests_passed"
|
|
266
|
+
)
|
|
267
|
+
return bool(results) and all(
|
|
268
|
+
result.passed for result in results
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
def prior_receipt(
|
|
272
|
+
current: ActionReceiptEnvelope,
|
|
273
|
+
receipt_id: str,
|
|
274
|
+
*,
|
|
275
|
+
digest: str | None = None,
|
|
276
|
+
expected_type=None,
|
|
277
|
+
) -> ActionReceiptEnvelope:
|
|
278
|
+
candidate = index.by_id.get(receipt_id)
|
|
279
|
+
if (
|
|
280
|
+
candidate is None
|
|
281
|
+
or candidate.sequence >= current.sequence
|
|
282
|
+
or (digest is not None and candidate.digest != digest)
|
|
283
|
+
or (
|
|
284
|
+
expected_type is not None
|
|
285
|
+
and not isinstance(candidate, expected_type)
|
|
286
|
+
)
|
|
287
|
+
):
|
|
288
|
+
raise _fail("receipt causal reference is unavailable")
|
|
289
|
+
return candidate
|
|
290
|
+
|
|
291
|
+
def prior_digest(
|
|
292
|
+
current: ActionReceiptEnvelope,
|
|
293
|
+
digest: str,
|
|
294
|
+
*,
|
|
295
|
+
expected_type=None,
|
|
296
|
+
) -> ActionReceiptEnvelope:
|
|
297
|
+
candidate = index.by_digest.get(digest)
|
|
298
|
+
if candidate is None:
|
|
299
|
+
raise _fail("receipt causal digest is unavailable")
|
|
300
|
+
return prior_receipt(
|
|
301
|
+
current,
|
|
302
|
+
candidate.receipt_id,
|
|
303
|
+
digest=digest,
|
|
304
|
+
expected_type=expected_type,
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
def grant_issuance(
|
|
308
|
+
current: ActionReceiptEnvelope,
|
|
309
|
+
grant_id: str,
|
|
310
|
+
) -> GrantIssuedReceipt:
|
|
311
|
+
issuance = index.issuance_by_grant.get(grant_id)
|
|
312
|
+
if issuance is None:
|
|
313
|
+
raise _fail("receipt authorizing Grant issuance is unavailable")
|
|
314
|
+
candidate = prior_receipt(
|
|
315
|
+
current,
|
|
316
|
+
issuance.receipt_id,
|
|
317
|
+
expected_type=GrantIssuedReceipt,
|
|
318
|
+
)
|
|
319
|
+
assert isinstance(candidate, GrantIssuedReceipt)
|
|
320
|
+
return candidate
|
|
321
|
+
|
|
322
|
+
for receipt in ordered:
|
|
323
|
+
if independent_failure_terminal:
|
|
324
|
+
termination = (
|
|
325
|
+
isinstance(receipt, TerminationDecisionReceipt)
|
|
326
|
+
and receipt.state_before == "evidence_incomplete"
|
|
327
|
+
and receipt.state_after == "rejected"
|
|
328
|
+
and receipt.policy_decision == "allow"
|
|
329
|
+
and receipt.execution_status == "succeeded"
|
|
330
|
+
)
|
|
331
|
+
contract_expiry = (
|
|
332
|
+
isinstance(receipt, SystemEventReceipt)
|
|
333
|
+
and receipt.system_event_name == "contract_expired"
|
|
334
|
+
and receipt.error_code == "CONTRACT_EXPIRED"
|
|
335
|
+
and receipt.state_before == "evidence_incomplete"
|
|
336
|
+
and receipt.state_after == "frozen"
|
|
337
|
+
)
|
|
338
|
+
if sealed_tail_appended or not (termination or contract_expiry):
|
|
339
|
+
raise _fail(
|
|
340
|
+
"independent evidence failure sealed the episode"
|
|
341
|
+
)
|
|
342
|
+
sealed_tail_appended = True
|
|
343
|
+
root_activation = (
|
|
344
|
+
isinstance(receipt, GrantIssuedReceipt)
|
|
345
|
+
and receipt.parent_grant_id is None
|
|
346
|
+
and receipt.policy_decision == "allow"
|
|
347
|
+
and receipt.execution_status == "succeeded"
|
|
348
|
+
)
|
|
349
|
+
parents: dict[str, ActionReceiptEnvelope] = {}
|
|
350
|
+
|
|
351
|
+
def add_parent(parent: ActionReceiptEnvelope) -> None:
|
|
352
|
+
parents[parent.receipt_id] = parent
|
|
353
|
+
|
|
354
|
+
if root_activation:
|
|
355
|
+
if receipt.sequence != 1:
|
|
356
|
+
raise _fail("root activation is not the genesis receipt")
|
|
357
|
+
elif receipt.actor_type == "agent":
|
|
358
|
+
grant_id_value = (
|
|
359
|
+
receipt.authorizing_grant_id
|
|
360
|
+
if isinstance(
|
|
361
|
+
receipt,
|
|
362
|
+
(GrantIssuedReceipt, GrantRevokedReceipt),
|
|
363
|
+
)
|
|
364
|
+
else receipt.grant_id
|
|
365
|
+
)
|
|
366
|
+
add_parent(grant_issuance(receipt, grant_id_value))
|
|
367
|
+
if receipt.policy_decision == "deny":
|
|
368
|
+
revocation = latest_revocation.get(grant_id_value)
|
|
369
|
+
if revocation is not None:
|
|
370
|
+
add_parent(revocation)
|
|
371
|
+
if receipt.policy_error_code == "STATE_DENIED" and prior:
|
|
372
|
+
add_parent(prior[-1])
|
|
373
|
+
|
|
374
|
+
if isinstance(receipt, GrantConsumedReceipt):
|
|
375
|
+
if current_failure is not None and current_rollback is not None:
|
|
376
|
+
add_parent(current_failure)
|
|
377
|
+
add_parent(current_rollback)
|
|
378
|
+
if (
|
|
379
|
+
receipt.policy_decision == "allow"
|
|
380
|
+
and (
|
|
381
|
+
current_failure is None
|
|
382
|
+
or current_rollback is None
|
|
383
|
+
or current_rollback.sequence
|
|
384
|
+
<= current_failure.sequence
|
|
385
|
+
)
|
|
386
|
+
):
|
|
387
|
+
raise _fail("retry causal episode is unavailable")
|
|
388
|
+
elif isinstance(receipt, GrantRevokedReceipt):
|
|
389
|
+
add_parent(
|
|
390
|
+
grant_issuance(receipt, receipt.revoked_grant_id)
|
|
391
|
+
)
|
|
392
|
+
elif isinstance(receipt, ToolCallReceipt):
|
|
393
|
+
if receipt.tool_name == "owp.repo_read":
|
|
394
|
+
if active_patch is not None:
|
|
395
|
+
add_parent(active_patch)
|
|
396
|
+
elif receipt.tool_name == "owp.apply_patch":
|
|
397
|
+
# A patch that follows a repo-read causally depends on the
|
|
398
|
+
# read: the exact historical replay must mirror the
|
|
399
|
+
# publication tip-parent rule (the latest repo-read on the
|
|
400
|
+
# same grant is a causal parent of the patch).
|
|
401
|
+
prior_read = next(
|
|
402
|
+
(
|
|
403
|
+
item
|
|
404
|
+
for item in reversed(prior)
|
|
405
|
+
if isinstance(item, ToolCallReceipt)
|
|
406
|
+
and item.tool_name == "owp.repo_read"
|
|
407
|
+
and item.grant_id == receipt.grant_id
|
|
408
|
+
),
|
|
409
|
+
None,
|
|
410
|
+
)
|
|
411
|
+
if prior_read is not None:
|
|
412
|
+
add_parent(prior_read)
|
|
413
|
+
elif receipt.tool_name == "owp.run_tests":
|
|
414
|
+
# An independent episode is identified by its state pair and
|
|
415
|
+
# role alone: both succeeded and closed infrastructure-failed
|
|
416
|
+
# (TIMEOUT / OUTPUT_LIMIT / DISK_LIMIT) receipts carry the
|
|
417
|
+
# same proof_composed parent and the same fresh execution
|
|
418
|
+
# context requirements, so causal replay must accept either.
|
|
419
|
+
is_independent_attempt = (
|
|
420
|
+
receipt.state_before == "evidence_incomplete"
|
|
421
|
+
and receipt.state_after == "evidence_incomplete"
|
|
422
|
+
and receipt.policy_decision == "allow"
|
|
423
|
+
and roles_by_key.get(receipt.actor_key_id)
|
|
424
|
+
== "Verifier"
|
|
425
|
+
)
|
|
426
|
+
if active_patch is not None:
|
|
427
|
+
add_parent(active_patch)
|
|
428
|
+
elif receipt.policy_decision == "allow":
|
|
429
|
+
raise _fail("tested patch receipt is unavailable")
|
|
430
|
+
if is_independent_attempt:
|
|
431
|
+
if latest_composition_trigger is None:
|
|
432
|
+
raise _fail(
|
|
433
|
+
"independent result episode is unavailable"
|
|
434
|
+
)
|
|
435
|
+
add_parent(latest_composition_trigger)
|
|
436
|
+
# A retry after an infrastructure failure links to the
|
|
437
|
+
# previous closed failure receipt of the same episode.
|
|
438
|
+
if last_independent_attempt is not None:
|
|
439
|
+
add_parent(last_independent_attempt)
|
|
440
|
+
factors = receipt.correlation_factors
|
|
441
|
+
if (
|
|
442
|
+
factors.execution_context_id is None
|
|
443
|
+
or factors.container_instance_id_digest is None
|
|
444
|
+
or factors.execution_context_id
|
|
445
|
+
in started_execution_context_ids
|
|
446
|
+
or factors.container_instance_id_digest
|
|
447
|
+
in started_container_instance_digests
|
|
448
|
+
):
|
|
449
|
+
raise _fail(
|
|
450
|
+
"independent result requires fresh execution context"
|
|
451
|
+
)
|
|
452
|
+
if (
|
|
453
|
+
verifier_result_passed(receipt)
|
|
454
|
+
and independent_result is not None
|
|
455
|
+
):
|
|
456
|
+
raise _fail(
|
|
457
|
+
"independent result episode is unavailable"
|
|
458
|
+
)
|
|
459
|
+
elif receipt.tool_name == "owp.create_pr_proposal":
|
|
460
|
+
arguments = receipt.request_arguments
|
|
461
|
+
if not isinstance(arguments, CreatePrProposalArguments):
|
|
462
|
+
raise _fail("PR proposal arguments are unavailable")
|
|
463
|
+
patch = prior_digest(
|
|
464
|
+
receipt,
|
|
465
|
+
arguments.target_patch_digest,
|
|
466
|
+
expected_type=ToolCallReceipt,
|
|
467
|
+
)
|
|
468
|
+
if (
|
|
469
|
+
patch.tool_name != "owp.apply_patch"
|
|
470
|
+
or patch is not active_patch
|
|
471
|
+
):
|
|
472
|
+
raise _fail("PR proposal target patch is not active")
|
|
473
|
+
decision = prior_receipt(
|
|
474
|
+
receipt,
|
|
475
|
+
receipt.approval_receipt_id,
|
|
476
|
+
digest=receipt.approval_receipt_digest,
|
|
477
|
+
expected_type=ApprovalDecisionReceipt,
|
|
478
|
+
)
|
|
479
|
+
assert isinstance(decision, ApprovalDecisionReceipt)
|
|
480
|
+
request = prior_receipt(
|
|
481
|
+
receipt,
|
|
482
|
+
decision.request_receipt_id,
|
|
483
|
+
digest=decision.request_receipt_digest,
|
|
484
|
+
expected_type=ApprovalRequestedReceipt,
|
|
485
|
+
)
|
|
486
|
+
assert isinstance(request, ApprovalRequestedReceipt)
|
|
487
|
+
scope = {
|
|
488
|
+
"work_order_digest": work_order.digest,
|
|
489
|
+
"operation": "create_local_pr_proposal",
|
|
490
|
+
"target_patch_digest": patch.digest,
|
|
491
|
+
}
|
|
492
|
+
if (
|
|
493
|
+
request.request_kind != "high_risk_action"
|
|
494
|
+
or dict(request.requested_scope) != scope
|
|
495
|
+
or dict(decision.approved_scope) != scope
|
|
496
|
+
or request.expires_at != decision.expires_at
|
|
497
|
+
or decision_by_request.get(request.receipt_id)
|
|
498
|
+
is not decision
|
|
499
|
+
):
|
|
500
|
+
raise _fail("PR proposal approval binding is invalid")
|
|
501
|
+
add_parent(patch)
|
|
502
|
+
add_parent(decision)
|
|
503
|
+
elif receipt.tool_name == "owp.compose_proof":
|
|
504
|
+
arguments = receipt.request_arguments
|
|
505
|
+
if not isinstance(arguments, ComposeProofArguments):
|
|
506
|
+
raise _fail("composition arguments are unavailable")
|
|
507
|
+
if receipt.state_before == "evidence_incomplete":
|
|
508
|
+
if (
|
|
509
|
+
latest_composition_trigger is None
|
|
510
|
+
or independent_result is None
|
|
511
|
+
):
|
|
512
|
+
raise _fail(
|
|
513
|
+
"recomposition causal inputs are unavailable"
|
|
514
|
+
)
|
|
515
|
+
add_parent(latest_composition_trigger)
|
|
516
|
+
add_parent(independent_result)
|
|
517
|
+
if (
|
|
518
|
+
arguments.previous_report_digest
|
|
519
|
+
!= latest_composition_trigger.cause.composition_report_digest
|
|
520
|
+
):
|
|
521
|
+
raise _fail(
|
|
522
|
+
"composition previous report digest is invalid"
|
|
523
|
+
)
|
|
524
|
+
else:
|
|
525
|
+
if (
|
|
526
|
+
receipt.state_before == "locally_verified"
|
|
527
|
+
and arguments.previous_report_digest is not None
|
|
528
|
+
):
|
|
529
|
+
raise _fail(
|
|
530
|
+
"composition previous report digest is invalid"
|
|
531
|
+
)
|
|
532
|
+
if active_patch is not None:
|
|
533
|
+
add_parent(active_patch)
|
|
534
|
+
if latest_passing_result is not None:
|
|
535
|
+
add_parent(latest_passing_result)
|
|
536
|
+
if (
|
|
537
|
+
receipt.policy_decision == "allow"
|
|
538
|
+
and (
|
|
539
|
+
active_patch is None
|
|
540
|
+
or latest_passing_result is None
|
|
541
|
+
)
|
|
542
|
+
):
|
|
543
|
+
raise _fail(
|
|
544
|
+
"composition causal inputs are unavailable"
|
|
545
|
+
)
|
|
546
|
+
elif isinstance(receipt, ApprovalRequestedReceipt):
|
|
547
|
+
scope = dict(receipt.requested_scope)
|
|
548
|
+
if receipt.request_kind == "high_risk_action":
|
|
549
|
+
patch = prior_digest(
|
|
550
|
+
receipt,
|
|
551
|
+
scope["target_patch_digest"],
|
|
552
|
+
expected_type=ToolCallReceipt,
|
|
553
|
+
)
|
|
554
|
+
if (
|
|
555
|
+
patch.tool_name != "owp.apply_patch"
|
|
556
|
+
or patch is not active_patch
|
|
557
|
+
):
|
|
558
|
+
raise _fail("approval target patch is not active")
|
|
559
|
+
add_parent(patch)
|
|
560
|
+
else:
|
|
561
|
+
report_digest = scope["composition_report_digest"]
|
|
562
|
+
trigger = next(
|
|
563
|
+
(
|
|
564
|
+
item
|
|
565
|
+
for item in reversed(prior)
|
|
566
|
+
if (
|
|
567
|
+
isinstance(item, SystemEventReceipt)
|
|
568
|
+
and item.system_event_name
|
|
569
|
+
== "proof_composed"
|
|
570
|
+
and getattr(
|
|
571
|
+
item.cause,
|
|
572
|
+
"composition_report_digest",
|
|
573
|
+
None,
|
|
574
|
+
)
|
|
575
|
+
== report_digest
|
|
576
|
+
)
|
|
577
|
+
),
|
|
578
|
+
None,
|
|
579
|
+
)
|
|
580
|
+
if trigger is None:
|
|
581
|
+
raise _fail(
|
|
582
|
+
"final approval trigger is unavailable"
|
|
583
|
+
)
|
|
584
|
+
add_parent(trigger)
|
|
585
|
+
elif isinstance(receipt, ApprovalDecisionReceipt):
|
|
586
|
+
request = prior_receipt(
|
|
587
|
+
receipt,
|
|
588
|
+
receipt.request_receipt_id,
|
|
589
|
+
digest=receipt.request_receipt_digest,
|
|
590
|
+
expected_type=ApprovalRequestedReceipt,
|
|
591
|
+
)
|
|
592
|
+
assert isinstance(request, ApprovalRequestedReceipt)
|
|
593
|
+
if receipt.request_receipt_id in decision_by_request:
|
|
594
|
+
raise _fail(
|
|
595
|
+
"approval request has more than one decision"
|
|
596
|
+
)
|
|
597
|
+
if (
|
|
598
|
+
receipt.approved_scope != request.requested_scope
|
|
599
|
+
or receipt.expires_at != request.expires_at
|
|
600
|
+
or receipt.decided_at < request.occurred_at
|
|
601
|
+
or receipt.decided_at > request.expires_at
|
|
602
|
+
or receipt.occurred_at < receipt.decided_at
|
|
603
|
+
or (
|
|
604
|
+
receipt.occurred_at - receipt.decided_at
|
|
605
|
+
).total_seconds()
|
|
606
|
+
> 300
|
|
607
|
+
):
|
|
608
|
+
raise _fail("approval decision is stale or mismatched")
|
|
609
|
+
decision_by_request[receipt.request_receipt_id] = receipt
|
|
610
|
+
add_parent(request)
|
|
611
|
+
elif isinstance(receipt, RollbackReceipt):
|
|
612
|
+
patch = prior_receipt(
|
|
613
|
+
receipt,
|
|
614
|
+
receipt.target_patch_receipt_id,
|
|
615
|
+
digest=receipt.target_patch_digest,
|
|
616
|
+
expected_type=ToolCallReceipt,
|
|
617
|
+
)
|
|
618
|
+
if (
|
|
619
|
+
patch.tool_name != "owp.apply_patch"
|
|
620
|
+
or patch is not active_patch
|
|
621
|
+
):
|
|
622
|
+
raise _fail("rollback target is not the active patch")
|
|
623
|
+
add_parent(patch)
|
|
624
|
+
proactive_denial = (
|
|
625
|
+
receipt.policy_decision == "deny"
|
|
626
|
+
and receipt.policy_error_code == "STATE_DENIED"
|
|
627
|
+
and receipt.execution_status == "denied"
|
|
628
|
+
and receipt.state_before == receipt.state_after
|
|
629
|
+
and receipt.state_before in {"running", "retrying"}
|
|
630
|
+
)
|
|
631
|
+
if not proactive_denial:
|
|
632
|
+
failure = current_failure
|
|
633
|
+
if (
|
|
634
|
+
failure is None
|
|
635
|
+
or failure.sequence <= patch.sequence
|
|
636
|
+
or patch.receipt_id not in failure.parent_receipt_ids
|
|
637
|
+
):
|
|
638
|
+
raise _fail(
|
|
639
|
+
"rollback failure receipt is unavailable"
|
|
640
|
+
)
|
|
641
|
+
add_parent(failure)
|
|
642
|
+
elif isinstance(receipt, SystemEventReceipt):
|
|
643
|
+
if receipt.system_event_name in {
|
|
644
|
+
"proof_composed",
|
|
645
|
+
"security_violation",
|
|
646
|
+
}:
|
|
647
|
+
initiator = prior_digest(
|
|
648
|
+
receipt,
|
|
649
|
+
receipt.cause.initiator_receipt_digest,
|
|
650
|
+
expected_type=ToolCallReceipt,
|
|
651
|
+
)
|
|
652
|
+
parents = {initiator.receipt_id: initiator}
|
|
653
|
+
elif prior:
|
|
654
|
+
parents = {prior[-1].receipt_id: prior[-1]}
|
|
655
|
+
elif isinstance(receipt, TerminationDecisionReceipt):
|
|
656
|
+
parents = (
|
|
657
|
+
{prior[-1].receipt_id: prior[-1]} if prior else {}
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
expected = tuple(
|
|
661
|
+
parent.receipt_id
|
|
662
|
+
for parent in sorted(
|
|
663
|
+
parents.values(),
|
|
664
|
+
key=lambda item: item.sequence,
|
|
665
|
+
)
|
|
666
|
+
)
|
|
667
|
+
if receipt.parent_receipt_ids != expected:
|
|
668
|
+
raise _fail(
|
|
669
|
+
"receipt causal parents failed exact historical replay"
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
if (
|
|
673
|
+
isinstance(receipt, ToolCallReceipt)
|
|
674
|
+
and receipt.tool_name == "owp.apply_patch"
|
|
675
|
+
and receipt.policy_decision == "allow"
|
|
676
|
+
and receipt.execution_status == "succeeded"
|
|
677
|
+
):
|
|
678
|
+
if active_patch is not None:
|
|
679
|
+
raise _fail("a second active patch is not allowed")
|
|
680
|
+
active_patch = receipt
|
|
681
|
+
current_failure = None
|
|
682
|
+
current_rollback = None
|
|
683
|
+
latest_passing_result = None
|
|
684
|
+
elif (
|
|
685
|
+
isinstance(receipt, RollbackReceipt)
|
|
686
|
+
and receipt.policy_decision == "allow"
|
|
687
|
+
and receipt.execution_status == "succeeded"
|
|
688
|
+
):
|
|
689
|
+
active_patch = None
|
|
690
|
+
current_rollback = receipt
|
|
691
|
+
if (
|
|
692
|
+
isinstance(receipt, ToolCallReceipt)
|
|
693
|
+
and receipt.tool_name == "owp.run_tests"
|
|
694
|
+
and receipt.policy_decision == "allow"
|
|
695
|
+
and receipt.execution_status == "succeeded"
|
|
696
|
+
):
|
|
697
|
+
if receipt.state_after == "needs_rework":
|
|
698
|
+
current_failure = receipt
|
|
699
|
+
current_rollback = None
|
|
700
|
+
elif (
|
|
701
|
+
receipt.state_before == "evidence_incomplete"
|
|
702
|
+
and receipt.state_after == "evidence_incomplete"
|
|
703
|
+
and roles_by_key.get(receipt.actor_key_id)
|
|
704
|
+
== "Verifier"
|
|
705
|
+
and verifier_result_passed(receipt)
|
|
706
|
+
):
|
|
707
|
+
independent_result = receipt
|
|
708
|
+
elif (
|
|
709
|
+
receipt.state_before == "evidence_incomplete"
|
|
710
|
+
and receipt.state_after == "evidence_incomplete"
|
|
711
|
+
and roles_by_key.get(receipt.actor_key_id)
|
|
712
|
+
== "Verifier"
|
|
713
|
+
):
|
|
714
|
+
independent_failure_terminal = True
|
|
715
|
+
elif receipt.state_after == "locally_verified":
|
|
716
|
+
latest_passing_result = receipt
|
|
717
|
+
if (
|
|
718
|
+
isinstance(receipt, ToolCallReceipt)
|
|
719
|
+
and receipt.tool_name == "owp.run_tests"
|
|
720
|
+
and receipt.state_before == "evidence_incomplete"
|
|
721
|
+
and receipt.state_after == "evidence_incomplete"
|
|
722
|
+
and roles_by_key.get(receipt.actor_key_id) == "Verifier"
|
|
723
|
+
):
|
|
724
|
+
last_independent_attempt = receipt
|
|
725
|
+
if (
|
|
726
|
+
isinstance(receipt, ToolCallReceipt)
|
|
727
|
+
and receipt.quota_charge is not None
|
|
728
|
+
and roles_by_key.get(receipt.actor_key_id)
|
|
729
|
+
in {"Developer", "Verifier"}
|
|
730
|
+
):
|
|
731
|
+
factors = receipt.correlation_factors
|
|
732
|
+
if factors.execution_context_id is not None:
|
|
733
|
+
started_execution_context_ids.add(
|
|
734
|
+
factors.execution_context_id
|
|
735
|
+
)
|
|
736
|
+
if factors.container_instance_id_digest is not None:
|
|
737
|
+
started_container_instance_digests.add(
|
|
738
|
+
factors.container_instance_id_digest
|
|
739
|
+
)
|
|
740
|
+
if (
|
|
741
|
+
isinstance(receipt, SystemEventReceipt)
|
|
742
|
+
and receipt.system_event_name == "proof_composed"
|
|
743
|
+
):
|
|
744
|
+
latest_composition_trigger = receipt
|
|
745
|
+
independent_result = None
|
|
746
|
+
if (
|
|
747
|
+
isinstance(receipt, GrantRevokedReceipt)
|
|
748
|
+
and receipt.policy_decision == "allow"
|
|
749
|
+
and receipt.execution_status == "succeeded"
|
|
750
|
+
):
|
|
751
|
+
latest_revocation[receipt.revoked_grant_id] = receipt
|
|
752
|
+
snapshots_by_receipt.append(
|
|
753
|
+
(
|
|
754
|
+
receipt.receipt_id,
|
|
755
|
+
AuthorizationCausalSnapshot(
|
|
756
|
+
active_patch_receipt_id=(
|
|
757
|
+
active_patch.receipt_id
|
|
758
|
+
if active_patch is not None
|
|
759
|
+
else None
|
|
760
|
+
),
|
|
761
|
+
failure_receipt_id=(
|
|
762
|
+
current_failure.receipt_id
|
|
763
|
+
if current_failure is not None
|
|
764
|
+
else None
|
|
765
|
+
),
|
|
766
|
+
rollback_receipt_id=(
|
|
767
|
+
current_rollback.receipt_id
|
|
768
|
+
if current_rollback is not None
|
|
769
|
+
else None
|
|
770
|
+
),
|
|
771
|
+
latest_composition_trigger_id=(
|
|
772
|
+
latest_composition_trigger.receipt_id
|
|
773
|
+
if latest_composition_trigger is not None
|
|
774
|
+
else None
|
|
775
|
+
),
|
|
776
|
+
independent_result_receipt_id=(
|
|
777
|
+
independent_result.receipt_id
|
|
778
|
+
if independent_result is not None
|
|
779
|
+
else None
|
|
780
|
+
),
|
|
781
|
+
approval_decision_by_request=tuple(
|
|
782
|
+
(request_id, decision.receipt_id)
|
|
783
|
+
for request_id, decision
|
|
784
|
+
in decision_by_request.items()
|
|
785
|
+
),
|
|
786
|
+
),
|
|
787
|
+
)
|
|
788
|
+
)
|
|
789
|
+
prior.append(receipt)
|
|
790
|
+
|
|
791
|
+
return AuthorizationCausalState(
|
|
792
|
+
active_patch_receipt_id=(
|
|
793
|
+
active_patch.receipt_id
|
|
794
|
+
if active_patch is not None
|
|
795
|
+
else None
|
|
796
|
+
),
|
|
797
|
+
failure_receipt_id=(
|
|
798
|
+
current_failure.receipt_id
|
|
799
|
+
if current_failure is not None
|
|
800
|
+
else None
|
|
801
|
+
),
|
|
802
|
+
rollback_receipt_id=(
|
|
803
|
+
current_rollback.receipt_id
|
|
804
|
+
if current_rollback is not None
|
|
805
|
+
else None
|
|
806
|
+
),
|
|
807
|
+
latest_composition_trigger_id=(
|
|
808
|
+
latest_composition_trigger.receipt_id
|
|
809
|
+
if latest_composition_trigger is not None
|
|
810
|
+
else None
|
|
811
|
+
),
|
|
812
|
+
independent_result_receipt_id=(
|
|
813
|
+
independent_result.receipt_id
|
|
814
|
+
if independent_result is not None
|
|
815
|
+
else None
|
|
816
|
+
),
|
|
817
|
+
approval_decision_by_request=tuple(
|
|
818
|
+
(request_id, decision.receipt_id)
|
|
819
|
+
for request_id, decision in decision_by_request.items()
|
|
820
|
+
),
|
|
821
|
+
independent_failure_terminal=independent_failure_terminal,
|
|
822
|
+
snapshots_by_receipt=tuple(snapshots_by_receipt),
|
|
823
|
+
)
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
__all__ = [
|
|
827
|
+
"AuthorizationCausalState",
|
|
828
|
+
"AuthorizationCausalSnapshot",
|
|
829
|
+
"AuthorizationCausalityError",
|
|
830
|
+
"replay_authorization_causality",
|
|
831
|
+
"validate_authorization_causal_bindings",
|
|
832
|
+
]
|