rexecop 0.2.2a0__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.
- rexecop/__init__.py +3 -0
- rexecop/__main__.py +4 -0
- rexecop/adapters/__init__.py +1 -0
- rexecop/adapters/govengine_port/__init__.py +19 -0
- rexecop/adapters/govengine_port/adapter.py +19 -0
- rexecop/adapters/govengine_port/client.py +161 -0
- rexecop/adapters/govengine_port/contracts.py +88 -0
- rexecop/adapters/govengine_port/static_adapter.py +39 -0
- rexecop/adapters/sclite_port/__init__.py +26 -0
- rexecop/adapters/sclite_port/contracts.py +149 -0
- rexecop/adapters/sclite_port/emitter.py +408 -0
- rexecop/adapters/sclite_port/execution_receipt_metrics.py +76 -0
- rexecop/adapters/sclite_port/fixture_bundle.py +49 -0
- rexecop/adapters/sclite_port/full_bundle.py +430 -0
- rexecop/adapters/sclite_port/govengine_policy_bridge.py +31 -0
- rexecop/adapters/sclite_port/placeholder_emitter.py +83 -0
- rexecop/adapters/sclite_port/target_host.py +32 -0
- rexecop/cli.py +376 -0
- rexecop/connectors/__init__.py +17 -0
- rexecop/connectors/base.py +35 -0
- rexecop/connectors/capability.py +35 -0
- rexecop/connectors/command_shape.py +30 -0
- rexecop/connectors/composite_runtime.py +118 -0
- rexecop/connectors/errors.py +14 -0
- rexecop/connectors/fixture_loader.py +63 -0
- rexecop/connectors/http_api.py +374 -0
- rexecop/connectors/http_support.py +83 -0
- rexecop/connectors/local_shell.py +126 -0
- rexecop/connectors/mock_runtime.py +60 -0
- rexecop/connectors/mutating.py +13 -0
- rexecop/connectors/runtime.py +16 -0
- rexecop/connectors/ssh_readonly.py +162 -0
- rexecop/environment/__init__.py +4 -0
- rexecop/environment/loader.py +20 -0
- rexecop/environment/model.py +41 -0
- rexecop/environment/sanitize.py +57 -0
- rexecop/errors.py +10 -0
- rexecop/escalation/__init__.py +5 -0
- rexecop/escalation/package.py +31 -0
- rexecop/evidence/__init__.py +4 -0
- rexecop/evidence/event.py +22 -0
- rexecop/evidence/manager.py +47 -0
- rexecop/evidence/redaction.py +26 -0
- rexecop/examples/__init__.py +1 -0
- rexecop/examples/bootstrap_receipt.py +61 -0
- rexecop/execution/__init__.py +6 -0
- rexecop/execution/backend.py +29 -0
- rexecop/execution/executor.py +114 -0
- rexecop/execution/internal_registry.py +59 -0
- rexecop/operation/__init__.py +12 -0
- rexecop/operation/controller.py +554 -0
- rexecop/operation/model.py +111 -0
- rexecop/operation/plan.py +62 -0
- rexecop/operation/state.py +72 -0
- rexecop/orchestration/__init__.py +5 -0
- rexecop/orchestration/orchestrator.py +589 -0
- rexecop/profile/__init__.py +11 -0
- rexecop/profile/contract.py +42 -0
- rexecop/profile/loader.py +76 -0
- rexecop/profile/resolver.py +69 -0
- rexecop/profile/validation_rules.py +28 -0
- rexecop/runtime_ops/__init__.py +19 -0
- rexecop/runtime_ops/coordinator.py +108 -0
- rexecop/runtime_ops/maintenance.py +38 -0
- rexecop/runtime_ops/monitor.py +49 -0
- rexecop/runtime_ops/queue.py +76 -0
- rexecop/runtime_ops/rollback.py +71 -0
- rexecop/runtime_ops/target_lock.py +98 -0
- rexecop/runtime_ops/worker.py +141 -0
- rexecop/secrets/__init__.py +17 -0
- rexecop/secrets/port.py +7 -0
- rexecop/secrets/resolver.py +66 -0
- rexecop/storage/__init__.py +13 -0
- rexecop/storage/factory.py +27 -0
- rexecop/storage/file_store.py +106 -0
- rexecop/storage/memory_store.py +75 -0
- rexecop/storage/port.py +43 -0
- rexecop/storage/sqlite_store.py +158 -0
- rexecop/types.py +10 -0
- rexecop/validation/__init__.py +5 -0
- rexecop/validation/validator.py +130 -0
- rexecop/workflow/__init__.py +4 -0
- rexecop/workflow/loader.py +26 -0
- rexecop/workflow/model.py +93 -0
- rexecop/workflow/runner.py +165 -0
- rexecop-0.2.2a0.dist-info/METADATA +231 -0
- rexecop-0.2.2a0.dist-info/RECORD +90 -0
- rexecop-0.2.2a0.dist-info/WHEEL +4 -0
- rexecop-0.2.2a0.dist-info/entry_points.txt +2 -0
- rexecop-0.2.2a0.dist-info/licenses/LICENSE +21 -0
rexecop/__init__.py
ADDED
rexecop/__main__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""External system adapters (GovEngine, SCLite)."""
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from rexecop.adapters.govengine_port.adapter import default_govengine_adapter
|
|
2
|
+
from rexecop.adapters.govengine_port.client import GovEngineClient
|
|
3
|
+
from rexecop.adapters.govengine_port.contracts import (
|
|
4
|
+
GovEngineAdapter,
|
|
5
|
+
GovEngineDecision,
|
|
6
|
+
GovEngineDecisionType,
|
|
7
|
+
GovEngineRequest,
|
|
8
|
+
)
|
|
9
|
+
from rexecop.adapters.govengine_port.static_adapter import StaticGovEngineAdapter
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"GovEngineClient",
|
|
13
|
+
"GovEngineAdapter",
|
|
14
|
+
"GovEngineDecision",
|
|
15
|
+
"GovEngineDecisionType",
|
|
16
|
+
"GovEngineRequest",
|
|
17
|
+
"StaticGovEngineAdapter",
|
|
18
|
+
"default_govengine_adapter",
|
|
19
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from rexecop.adapters.govengine_port.client import GovEngineClient
|
|
4
|
+
from rexecop.adapters.govengine_port.contracts import GovEngineAdapter, GovEngineDecisionType
|
|
5
|
+
from rexecop.adapters.govengine_port.static_adapter import StaticGovEngineAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def default_govengine_adapter() -> GovEngineAdapter:
|
|
9
|
+
"""Fail-closed real GovEngine client for local development."""
|
|
10
|
+
return GovEngineClient()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def static_govengine_adapter(
|
|
14
|
+
decision_type: GovEngineDecisionType,
|
|
15
|
+
*,
|
|
16
|
+
summary: str = "",
|
|
17
|
+
) -> GovEngineAdapter:
|
|
18
|
+
"""Explicit bootstrap/test adapter."""
|
|
19
|
+
return StaticGovEngineAdapter(decision_type, summary=summary)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from govengine import compose_runtime_admission_result, runtime_admission_public_summary
|
|
6
|
+
from govengine.admission import RuntimeAdmissionResult
|
|
7
|
+
|
|
8
|
+
from rexecop.adapters.govengine_port.contracts import (
|
|
9
|
+
GovEngineDecision,
|
|
10
|
+
GovEngineDecisionType,
|
|
11
|
+
GovEngineRequest,
|
|
12
|
+
is_mutating_mode,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def map_runtime_admission_to_decision(result: RuntimeAdmissionResult) -> GovEngineDecision:
|
|
17
|
+
if result.allowed:
|
|
18
|
+
return GovEngineDecision(
|
|
19
|
+
decision_type=GovEngineDecisionType.ALLOWED,
|
|
20
|
+
summary=result.reason_code,
|
|
21
|
+
details={
|
|
22
|
+
"admission_id": result.admission_id,
|
|
23
|
+
"status": result.status,
|
|
24
|
+
"public_summary": runtime_admission_public_summary(result),
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
actions = set(result.required_next_actions)
|
|
29
|
+
blockers = set(result.blockers)
|
|
30
|
+
|
|
31
|
+
if result.status == "needs_review" or actions.intersection(
|
|
32
|
+
{"approve_execution_ticket", "obtain_policy_decision"}
|
|
33
|
+
):
|
|
34
|
+
return GovEngineDecision(
|
|
35
|
+
decision_type=GovEngineDecisionType.APPROVAL_REQUIRED,
|
|
36
|
+
summary=result.reason_code,
|
|
37
|
+
details={
|
|
38
|
+
"admission_id": result.admission_id,
|
|
39
|
+
"status": result.status,
|
|
40
|
+
"blockers": list(blockers),
|
|
41
|
+
"required_next_actions": list(actions),
|
|
42
|
+
},
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if result.status == "dry_run_only" or "live_backend_disabled" in blockers:
|
|
46
|
+
return GovEngineDecision(
|
|
47
|
+
decision_type=GovEngineDecisionType.READ_ONLY_ONLY,
|
|
48
|
+
summary=result.reason_code,
|
|
49
|
+
details={"admission_id": result.admission_id, "status": result.status},
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return GovEngineDecision(
|
|
53
|
+
decision_type=GovEngineDecisionType.BLOCKED,
|
|
54
|
+
summary=result.reason_code,
|
|
55
|
+
details={
|
|
56
|
+
"admission_id": result.admission_id,
|
|
57
|
+
"status": result.status,
|
|
58
|
+
"blockers": list(blockers),
|
|
59
|
+
"required_next_actions": list(actions),
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def build_compose_inputs(request: GovEngineRequest) -> dict[str, Any]:
|
|
65
|
+
preview = dict(request.preview)
|
|
66
|
+
override = preview.get("admission_compose")
|
|
67
|
+
if isinstance(override, dict):
|
|
68
|
+
return dict(override)
|
|
69
|
+
|
|
70
|
+
policy_decision = preview.get("policy_decision")
|
|
71
|
+
if policy_decision is None:
|
|
72
|
+
return {
|
|
73
|
+
"admission_id": f"adm-{request.operation_id}",
|
|
74
|
+
"subject_ref": f"rexecop:{request.operation_id}",
|
|
75
|
+
"prepared_execution_contract": {
|
|
76
|
+
"status": "prepared",
|
|
77
|
+
"digest": f"sha256:{request.operation_id}",
|
|
78
|
+
},
|
|
79
|
+
"policy_decision": None,
|
|
80
|
+
"execution_ticket": None,
|
|
81
|
+
"trust_decision": None,
|
|
82
|
+
"runner_profile": {
|
|
83
|
+
"name": "rexecop",
|
|
84
|
+
"allowed": False,
|
|
85
|
+
"live_backend_enabled": False,
|
|
86
|
+
},
|
|
87
|
+
"receipt_obligation": {"required": True, "binds": ["admission", "ticket"]},
|
|
88
|
+
"metadata": {"source": "rexecop", "phase": "2b"},
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
runner_profile = preview.get(
|
|
92
|
+
"runner_profile",
|
|
93
|
+
{
|
|
94
|
+
"name": "rexecop",
|
|
95
|
+
"allowed": True,
|
|
96
|
+
"live_backend_enabled": is_mutating_mode(request.mode),
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
"admission_id": f"adm-{request.operation_id}",
|
|
102
|
+
"subject_ref": f"rexecop:{request.operation_id}",
|
|
103
|
+
"prepared_execution_contract": preview.get(
|
|
104
|
+
"prepared_execution_contract",
|
|
105
|
+
{"status": "prepared", "digest": f"sha256:{request.operation_id}"},
|
|
106
|
+
),
|
|
107
|
+
"policy_decision": policy_decision,
|
|
108
|
+
"execution_ticket": preview.get("execution_ticket"),
|
|
109
|
+
"trust_decision": preview.get("trust_decision"),
|
|
110
|
+
"runner_profile": runner_profile,
|
|
111
|
+
"receipt_obligation": preview.get(
|
|
112
|
+
"receipt_obligation",
|
|
113
|
+
{"required": True, "binds": ["admission", "ticket"]},
|
|
114
|
+
),
|
|
115
|
+
"artifact_refs": preview.get("artifact_refs"),
|
|
116
|
+
"metadata": {"source": "rexecop", "operation_id": request.operation_id},
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class GovEngineClient:
|
|
121
|
+
"""Real GovEngine adapter using runtime admission composition."""
|
|
122
|
+
|
|
123
|
+
def evaluate(self, request: GovEngineRequest) -> GovEngineDecision:
|
|
124
|
+
inputs = build_compose_inputs(request)
|
|
125
|
+
try:
|
|
126
|
+
result = compose_runtime_admission_result(
|
|
127
|
+
**inputs,
|
|
128
|
+
live=is_mutating_mode(request.mode),
|
|
129
|
+
)
|
|
130
|
+
except Exception as exc:
|
|
131
|
+
return GovEngineDecision(
|
|
132
|
+
decision_type=GovEngineDecisionType.ERROR,
|
|
133
|
+
summary=str(exc),
|
|
134
|
+
details={"adapter": "govengine_client"},
|
|
135
|
+
)
|
|
136
|
+
return map_runtime_admission_to_decision(result)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def build_runner_request_preview(
|
|
140
|
+
plan_steps: list[dict[str, Any]],
|
|
141
|
+
*,
|
|
142
|
+
operation_id: str,
|
|
143
|
+
) -> dict[str, Any]:
|
|
144
|
+
"""Shape helper for future runner execution (Phase 4+)."""
|
|
145
|
+
from govengine.execution.runner_protocol import GovRunnerRequest, GovRunnerStep
|
|
146
|
+
|
|
147
|
+
steps = tuple(
|
|
148
|
+
GovRunnerStep(
|
|
149
|
+
index=index,
|
|
150
|
+
tool=str(step.get("connector") or step.get("type") or "internal"),
|
|
151
|
+
args=(str(step.get("action") or ""),),
|
|
152
|
+
)
|
|
153
|
+
for index, step in enumerate(plan_steps)
|
|
154
|
+
)
|
|
155
|
+
request = GovRunnerRequest(
|
|
156
|
+
request_id=f"req-{operation_id}",
|
|
157
|
+
source="rexecop",
|
|
158
|
+
steps=steps,
|
|
159
|
+
dry_run=True,
|
|
160
|
+
)
|
|
161
|
+
return request.as_dict()
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
from typing import Any, Protocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GovEngineDecisionType(StrEnum):
|
|
9
|
+
ALLOWED = "allowed"
|
|
10
|
+
BLOCKED = "blocked"
|
|
11
|
+
APPROVAL_REQUIRED = "approval_required"
|
|
12
|
+
MAINTENANCE_WINDOW_REQUIRED = "maintenance_window_required"
|
|
13
|
+
BACKUP_REQUIRED = "backup_required"
|
|
14
|
+
READ_ONLY_ONLY = "read_only_only"
|
|
15
|
+
HUMAN_REQUIRED = "human_required"
|
|
16
|
+
UNSUPPORTED = "unsupported"
|
|
17
|
+
ERROR = "error"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
BLOCKING_DECISIONS = frozenset(
|
|
21
|
+
{
|
|
22
|
+
GovEngineDecisionType.BLOCKED,
|
|
23
|
+
GovEngineDecisionType.READ_ONLY_ONLY,
|
|
24
|
+
GovEngineDecisionType.HUMAN_REQUIRED,
|
|
25
|
+
GovEngineDecisionType.UNSUPPORTED,
|
|
26
|
+
GovEngineDecisionType.ERROR,
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
WAITING_DECISIONS = frozenset(
|
|
31
|
+
{
|
|
32
|
+
GovEngineDecisionType.APPROVAL_REQUIRED,
|
|
33
|
+
GovEngineDecisionType.MAINTENANCE_WINDOW_REQUIRED,
|
|
34
|
+
GovEngineDecisionType.BACKUP_REQUIRED,
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
MUTATING_MODES = frozenset({"apply", "recovery"})
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def is_mutating_mode(mode: str) -> bool:
|
|
42
|
+
return mode in MUTATING_MODES
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class GovEngineRequest:
|
|
47
|
+
operation_id: str
|
|
48
|
+
profile: str
|
|
49
|
+
environment: str
|
|
50
|
+
intent: str
|
|
51
|
+
target: str
|
|
52
|
+
mode: str
|
|
53
|
+
risk: str
|
|
54
|
+
preview: dict[str, Any] = field(default_factory=dict)
|
|
55
|
+
|
|
56
|
+
def as_dict(self) -> dict[str, Any]:
|
|
57
|
+
return {
|
|
58
|
+
"operation_id": self.operation_id,
|
|
59
|
+
"profile": self.profile,
|
|
60
|
+
"environment": self.environment,
|
|
61
|
+
"intent": self.intent,
|
|
62
|
+
"target": self.target,
|
|
63
|
+
"mode": self.mode,
|
|
64
|
+
"risk": self.risk,
|
|
65
|
+
"preview": dict(self.preview),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True)
|
|
70
|
+
class GovEngineDecision:
|
|
71
|
+
decision_type: GovEngineDecisionType
|
|
72
|
+
summary: str
|
|
73
|
+
details: dict[str, Any] = field(default_factory=dict)
|
|
74
|
+
|
|
75
|
+
def as_dict(self) -> dict[str, Any]:
|
|
76
|
+
return {
|
|
77
|
+
"decision_type": self.decision_type.value,
|
|
78
|
+
"summary": self.summary,
|
|
79
|
+
"details": dict(self.details),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def allows_mutating_execution(self) -> bool:
|
|
84
|
+
return self.decision_type == GovEngineDecisionType.ALLOWED
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class GovEngineAdapter(Protocol):
|
|
88
|
+
def evaluate(self, request: GovEngineRequest) -> GovEngineDecision: ...
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from rexecop.adapters.govengine_port.contracts import (
|
|
4
|
+
GovEngineDecision,
|
|
5
|
+
GovEngineDecisionType,
|
|
6
|
+
GovEngineRequest,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
STATIC_ADAPTER_NOTICE = (
|
|
10
|
+
"StaticGovEngineAdapter is bootstrap/test only and is not production governance."
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class StaticGovEngineAdapter:
|
|
15
|
+
"""Bootstrap/test governance adapter. Not a policy engine."""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
decision_type: GovEngineDecisionType,
|
|
20
|
+
*,
|
|
21
|
+
summary: str = "",
|
|
22
|
+
details: dict[str, object] | None = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
self.decision_type = decision_type
|
|
25
|
+
self.summary = summary or f"static decision: {decision_type.value}"
|
|
26
|
+
self.details = dict(details or {})
|
|
27
|
+
self.bootstrap_only = True
|
|
28
|
+
|
|
29
|
+
def evaluate(self, request: GovEngineRequest) -> GovEngineDecision:
|
|
30
|
+
return GovEngineDecision(
|
|
31
|
+
decision_type=self.decision_type,
|
|
32
|
+
summary=self.summary,
|
|
33
|
+
details={
|
|
34
|
+
**self.details,
|
|
35
|
+
"adapter": "static",
|
|
36
|
+
"bootstrap_only": True,
|
|
37
|
+
"operation_id": request.operation_id,
|
|
38
|
+
},
|
|
39
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from rexecop.adapters.sclite_port.contracts import (
|
|
2
|
+
ARTIFACT_SLOTS,
|
|
3
|
+
EVENT_SCLITE_MAPPING,
|
|
4
|
+
PLACEHOLDER_EMITTER_NOTICE,
|
|
5
|
+
RECEIPT_EXPORT_AUTHORITY,
|
|
6
|
+
SCLITE_SCHEMA_REFS,
|
|
7
|
+
SCLiteArtifactDescriptor,
|
|
8
|
+
SCLiteEmitter,
|
|
9
|
+
SCLiteReceiptExport,
|
|
10
|
+
)
|
|
11
|
+
from rexecop.adapters.sclite_port.emitter import SCLiteArtifactEmitter
|
|
12
|
+
from rexecop.adapters.sclite_port.placeholder_emitter import PlaceholderSCLiteEmitter
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"ARTIFACT_SLOTS",
|
|
16
|
+
"EVENT_SCLITE_MAPPING",
|
|
17
|
+
"PLACEHOLDER_EMITTER_NOTICE",
|
|
18
|
+
"RECEIPT_EXPORT_AUTHORITY",
|
|
19
|
+
"SCLITE_ARTIFACT_AUTHORITY",
|
|
20
|
+
"SCLITE_SCHEMA_REFS",
|
|
21
|
+
"SCLiteArtifactDescriptor",
|
|
22
|
+
"SCLiteArtifactEmitter",
|
|
23
|
+
"SCLiteEmitter",
|
|
24
|
+
"SCLiteReceiptExport",
|
|
25
|
+
"PlaceholderSCLiteEmitter",
|
|
26
|
+
]
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, Protocol
|
|
5
|
+
|
|
6
|
+
PLACEHOLDER_EMITTER_NOTICE = (
|
|
7
|
+
"DEPRECATED: Placeholder SCLite emitter is bootstrap/offline only. "
|
|
8
|
+
"Use SCLiteArtifactEmitter for real emission (Phase 3B+). "
|
|
9
|
+
"Receipt exports under .rexecop/receipts/ are non-authoritative summaries."
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
SCLITE_ARTIFACT_AUTHORITY = "sclite_artifact"
|
|
13
|
+
|
|
14
|
+
RECEIPT_EXPORT_AUTHORITY = "non_authoritative_export"
|
|
15
|
+
|
|
16
|
+
SCLITE_SCHEMA_REFS: dict[str, str] = {
|
|
17
|
+
"intent_contract": "schemas/intent_contract.v0.2.schema.json",
|
|
18
|
+
"policy_decision": "schemas/policy_decision.v0.2.schema.json",
|
|
19
|
+
"execution_contract": "schemas/execution_contract.v0.2.schema.json",
|
|
20
|
+
"execution_ticket": "schemas/execution_ticket.v0.3.schema.json",
|
|
21
|
+
"execution_receipt": "schemas/execution_receipt.v0.2.schema.json",
|
|
22
|
+
"evidence_contract": "schemas/evidence_contract.v0.2.schema.json",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
ARTIFACT_SLOTS = (
|
|
26
|
+
"intent_contract",
|
|
27
|
+
"policy_decision",
|
|
28
|
+
"execution_contract",
|
|
29
|
+
"execution_ticket",
|
|
30
|
+
"execution_receipt",
|
|
31
|
+
"evidence_contract",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
EVENT_SCLITE_MAPPING: dict[str, dict[str, str]] = {
|
|
35
|
+
"operation_created": {
|
|
36
|
+
"future_artifact": "intent_contract",
|
|
37
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["intent_contract"],
|
|
38
|
+
},
|
|
39
|
+
"operation_triggered": {
|
|
40
|
+
"future_artifact": "intent_contract",
|
|
41
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["intent_contract"],
|
|
42
|
+
},
|
|
43
|
+
"plan_generated": {
|
|
44
|
+
"future_artifact": "execution_contract",
|
|
45
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["execution_contract"],
|
|
46
|
+
},
|
|
47
|
+
"govengine_decision_requested": {
|
|
48
|
+
"future_artifact": "policy_decision",
|
|
49
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["policy_decision"],
|
|
50
|
+
},
|
|
51
|
+
"govengine_decision_received": {
|
|
52
|
+
"future_artifact": "policy_decision",
|
|
53
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["policy_decision"],
|
|
54
|
+
},
|
|
55
|
+
"approval_received": {
|
|
56
|
+
"future_artifact": "execution_ticket",
|
|
57
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["execution_ticket"],
|
|
58
|
+
},
|
|
59
|
+
"state_transition": {
|
|
60
|
+
"future_artifact": "execution_receipt",
|
|
61
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["execution_receipt"],
|
|
62
|
+
},
|
|
63
|
+
"step_started": {
|
|
64
|
+
"future_artifact": "execution_receipt",
|
|
65
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["execution_receipt"],
|
|
66
|
+
},
|
|
67
|
+
"step_completed": {
|
|
68
|
+
"future_artifact": "execution_receipt",
|
|
69
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["execution_receipt"],
|
|
70
|
+
},
|
|
71
|
+
"step_failed": {
|
|
72
|
+
"future_artifact": "execution_receipt",
|
|
73
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["execution_receipt"],
|
|
74
|
+
},
|
|
75
|
+
"validation_started": {
|
|
76
|
+
"future_artifact": "evidence_contract",
|
|
77
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["evidence_contract"],
|
|
78
|
+
},
|
|
79
|
+
"validation_completed": {
|
|
80
|
+
"future_artifact": "evidence_contract",
|
|
81
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["evidence_contract"],
|
|
82
|
+
},
|
|
83
|
+
"receipt_generated": {
|
|
84
|
+
"future_artifact": "execution_receipt",
|
|
85
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["execution_receipt"],
|
|
86
|
+
},
|
|
87
|
+
"operation_completed": {
|
|
88
|
+
"future_artifact": "execution_receipt",
|
|
89
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["execution_receipt"],
|
|
90
|
+
},
|
|
91
|
+
"operation_failed": {
|
|
92
|
+
"future_artifact": "execution_receipt",
|
|
93
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["execution_receipt"],
|
|
94
|
+
},
|
|
95
|
+
"operation_escalated": {
|
|
96
|
+
"future_artifact": "evidence_contract",
|
|
97
|
+
"sclite_schema_ref": SCLITE_SCHEMA_REFS["evidence_contract"],
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass(frozen=True)
|
|
103
|
+
class SCLiteArtifactDescriptor:
|
|
104
|
+
artifact_role: str
|
|
105
|
+
sclite_schema_ref: str
|
|
106
|
+
descriptor_path: str | None = None
|
|
107
|
+
digest: str | None = None
|
|
108
|
+
status: str = "placeholder"
|
|
109
|
+
|
|
110
|
+
def as_dict(self) -> dict[str, Any]:
|
|
111
|
+
return {
|
|
112
|
+
"artifact_role": self.artifact_role,
|
|
113
|
+
"sclite_schema_ref": self.sclite_schema_ref,
|
|
114
|
+
"descriptor_path": self.descriptor_path,
|
|
115
|
+
"digest": self.digest,
|
|
116
|
+
"status": self.status,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass
|
|
121
|
+
class SCLiteReceiptExport:
|
|
122
|
+
operation_id: str
|
|
123
|
+
authority: str = RECEIPT_EXPORT_AUTHORITY
|
|
124
|
+
emitter: str = "placeholder"
|
|
125
|
+
migration_note: str = PLACEHOLDER_EMITTER_NOTICE
|
|
126
|
+
artifact_slots: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
127
|
+
evidence_event_mappings: list[dict[str, str]] = field(default_factory=list)
|
|
128
|
+
internal_evidence_event_ids: list[str] = field(default_factory=list)
|
|
129
|
+
|
|
130
|
+
def as_dict(self) -> dict[str, Any]:
|
|
131
|
+
return {
|
|
132
|
+
"operation_id": self.operation_id,
|
|
133
|
+
"authority": self.authority,
|
|
134
|
+
"emitter": self.emitter,
|
|
135
|
+
"migration_note": self.migration_note,
|
|
136
|
+
"artifact_slots": dict(self.artifact_slots),
|
|
137
|
+
"evidence_event_mappings": list(self.evidence_event_mappings),
|
|
138
|
+
"internal_evidence_event_ids": list(self.internal_evidence_event_ids),
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class SCLiteEmitter(Protocol):
|
|
143
|
+
def export_operation_receipt(
|
|
144
|
+
self,
|
|
145
|
+
*,
|
|
146
|
+
operation_id: str,
|
|
147
|
+
events: list[dict[str, Any]],
|
|
148
|
+
plan_summary: dict[str, Any] | None = None,
|
|
149
|
+
) -> SCLiteReceiptExport: ...
|