core-runtime-engine 11.5.1__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.
- core_runtime/__init__.py +10 -0
- core_runtime/__version__.py +17 -0
- core_runtime/cli/__init__.py +7 -0
- core_runtime/cli/__main__.py +22 -0
- core_runtime/cli/bump_version.py +176 -0
- core_runtime/cli/contract_preflight.py +69 -0
- core_runtime/cli/create_domain.py +66 -0
- core_runtime/cli/doctor.py +44 -0
- core_runtime/cli/inventory.py +85 -0
- core_runtime/cli/lint.py +8 -0
- core_runtime/cli/main.py +579 -0
- core_runtime/cli/release_check.py +65 -0
- core_runtime/cli/repair_artifact_paths.py +48 -0
- core_runtime/cli/sync_template.py +67 -0
- core_runtime/cli/validate.py +73 -0
- core_runtime/core/__init__.py +34 -0
- core_runtime/core/audit_event.py +760 -0
- core_runtime/core/audit_trail_index.py +100 -0
- core_runtime/core/canonicalization.py +55 -0
- core_runtime/core/contract_evaluator.py +945 -0
- core_runtime/core/contract_executability.py +307 -0
- core_runtime/core/contract_loader.py +57 -0
- core_runtime/core/contract_probes.py +630 -0
- core_runtime/core/contract_program.py +131 -0
- core_runtime/core/contract_program_registry.py +104 -0
- core_runtime/core/contract_program_v2.py +126 -0
- core_runtime/core/dsk_v3.py +142 -0
- core_runtime/core/explainability.py +2115 -0
- core_runtime/core/numeric_normalization.py +120 -0
- core_runtime/core/rule_anchor.py +1388 -0
- core_runtime/core/schema_fingerprint.py +69 -0
- core_runtime/core/sensor_evidence.py +548 -0
- core_runtime/data/contracts/CoreAnchor.sol +109 -0
- core_runtime/data/contracts/CoreRuleAnchor.abi.json +111 -0
- core_runtime/data/contracts/CoreRuleAnchor.bin +1 -0
- core_runtime/data/contracts/CoreRuleAnchor.build.json +20 -0
- core_runtime/data/contracts/CoreRuleAnchor.runtime.bin +1 -0
- core_runtime/data/contracts/CoreRuleAnchor.sol +74 -0
- core_runtime/data/package_data_manifest.v1.json +173 -0
- core_runtime/data/schemas/core/causal_trace.v1.json +141 -0
- core_runtime/data/schemas/core/context_gate.v1.json +38 -0
- core_runtime/data/schemas/core/context_threshold.v1.json +46 -0
- core_runtime/data/schemas/core/contract_program.v1.json +186 -0
- core_runtime/data/schemas/core/contract_program.v2.json +187 -0
- core_runtime/data/schemas/core/control_decision.v1.json +114 -0
- core_runtime/data/schemas/core/dsk.v3.json +105 -0
- core_runtime/data/schemas/core/effect_result.v1.json +39 -0
- core_runtime/data/schemas/core/entropy_signal.v1.json +108 -0
- core_runtime/data/schemas/core/execution_receipt.v1.json +93 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v1.json +87 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v2.json +72 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v3.json +38 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v4.json +72 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v5.json +38 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v6.json +116 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v7.json +37 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v8.json +114 -0
- core_runtime/data/schemas/core/frozen_rule_set.v1.json +282 -0
- core_runtime/data/schemas/core/memory_artifact.v1.json +120 -0
- core_runtime/data/schemas/core/memory_generation_result.v1.json +37 -0
- core_runtime/data/schemas/core/operational_learning_event.v1.json +63 -0
- core_runtime/data/schemas/core/pattern_candidate.v1.json +114 -0
- core_runtime/data/schemas/core/physical_safety_assurance_case.v1.json +676 -0
- core_runtime/data/schemas/core/policy_lifecycle.v1.json +99 -0
- core_runtime/data/schemas/core/retention_manifest.v1.json +53 -0
- core_runtime/data/schemas/core/reversibility_policy.v1.json +107 -0
- core_runtime/data/schemas/core/rule_anchor_batch.v1.json +93 -0
- core_runtime/data/schemas/core/rule_anchor_chain_evidence.v1.json +56 -0
- core_runtime/data/schemas/core/rule_approval.v1.json +49 -0
- core_runtime/data/schemas/core/rule_approval_request.v1.json +42 -0
- core_runtime/data/schemas/core/state_transition.v1.json +115 -0
- core_runtime/data/schemas/core/task_closeout.v1.json +47 -0
- core_runtime/data/schemas/core/template_promotion_candidate.v1.json +83 -0
- core_runtime/data/schemas/core/unsigned_rule_anchor_deployment.v1.json +106 -0
- core_runtime/data/schemas/core/unsigned_rule_anchor_transaction.v1.json +116 -0
- core_runtime/tooling/__init__.py +48 -0
- core_runtime/tooling/bump_version.py +900 -0
- core_runtime/tooling/contract_preflight.py +293 -0
- core_runtime/tooling/create_domain.py +254 -0
- core_runtime/tooling/diagnostics.py +129 -0
- core_runtime/tooling/doctor.py +482 -0
- core_runtime/tooling/file_inventory.py +182 -0
- core_runtime/tooling/json_checks.py +100 -0
- core_runtime/tooling/release_check.py +1017 -0
- core_runtime/tooling/repair_artifact_paths.py +458 -0
- core_runtime/tooling/report_writer.py +176 -0
- core_runtime/tooling/repository_inventory.py +399 -0
- core_runtime/tooling/safety_checks.py +172 -0
- core_runtime/tooling/sync_template.py +303 -0
- core_runtime/tooling/validation.py +507 -0
- core_runtime/tooling/version_inventory.py +256 -0
- core_runtime_engine-11.5.1.dist-info/METADATA +35 -0
- core_runtime_engine-11.5.1.dist-info/RECORD +96 -0
- core_runtime_engine-11.5.1.dist-info/WHEEL +5 -0
- core_runtime_engine-11.5.1.dist-info/entry_points.txt +2 -0
- core_runtime_engine-11.5.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
"""Deterministic audit trail primitives.
|
|
2
|
+
|
|
3
|
+
This module is intentionally separate from the operational EventLog.
|
|
4
|
+
It defines a read-only audit schema, canonical fingerprints, and
|
|
5
|
+
derivation helpers for audit artifacts produced from already-existing
|
|
6
|
+
reports.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from enum import Enum
|
|
15
|
+
from typing import Any, Mapping
|
|
16
|
+
|
|
17
|
+
from core_runtime.core.numeric_normalization import quantize_float
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
AUDIT_SCHEMA_VERSION = "4.9.0"
|
|
21
|
+
ALLOWED_CORRELATION_PREFIXES = (
|
|
22
|
+
"proposal::",
|
|
23
|
+
"profile::",
|
|
24
|
+
"pair::",
|
|
25
|
+
"report::",
|
|
26
|
+
"dataset::",
|
|
27
|
+
"graph::",
|
|
28
|
+
"router::",
|
|
29
|
+
"gaia_pipeline::",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class EventAuthority(Enum):
|
|
34
|
+
OBSERVATION = "observation"
|
|
35
|
+
DERIVED = "derived"
|
|
36
|
+
AUTHORITATIVE = "authoritative"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _normalize_value(value: Any) -> Any:
|
|
40
|
+
if isinstance(value, Enum):
|
|
41
|
+
return value.value
|
|
42
|
+
if isinstance(value, float):
|
|
43
|
+
return quantize_float(value)
|
|
44
|
+
if isinstance(value, dict):
|
|
45
|
+
return {str(key): _normalize_value(item) for key, item in sorted(value.items(), key=lambda item: str(item[0]))}
|
|
46
|
+
if isinstance(value, Mapping):
|
|
47
|
+
return {str(key): _normalize_value(item) for key, item in sorted(value.items(), key=lambda item: str(item[0]))}
|
|
48
|
+
if isinstance(value, list):
|
|
49
|
+
return [_normalize_value(item) for item in value]
|
|
50
|
+
if isinstance(value, tuple):
|
|
51
|
+
return [_normalize_value(item) for item in value]
|
|
52
|
+
if isinstance(value, set):
|
|
53
|
+
return sorted((_normalize_value(item) for item in value), key=canonical_json)
|
|
54
|
+
return value
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def canonical_json(data: Any) -> str:
|
|
58
|
+
return json.dumps(
|
|
59
|
+
_normalize_value(data),
|
|
60
|
+
sort_keys=True,
|
|
61
|
+
separators=(",", ":"),
|
|
62
|
+
ensure_ascii=False,
|
|
63
|
+
default=str,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def compute_operational_fingerprint(payload: Any) -> str:
|
|
68
|
+
canonical = canonical_json(payload)
|
|
69
|
+
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def compute_audit_fingerprint(payload: Any) -> str:
|
|
73
|
+
canonical = canonical_json(payload)
|
|
74
|
+
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def compute_graph_fingerprint(payload: Any) -> str:
|
|
78
|
+
canonical = canonical_json(payload)
|
|
79
|
+
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _validate_correlation_id(correlation_id: str) -> str:
|
|
83
|
+
if not isinstance(correlation_id, str):
|
|
84
|
+
raise TypeError(f"Expected string correlation_id, got {type(correlation_id).__name__}")
|
|
85
|
+
normalized = correlation_id.strip()
|
|
86
|
+
if not normalized:
|
|
87
|
+
raise ValueError("correlation_id must not be empty")
|
|
88
|
+
if not normalized.startswith(ALLOWED_CORRELATION_PREFIXES):
|
|
89
|
+
raise ValueError(
|
|
90
|
+
"correlation_id must use a semantic namespace such as "
|
|
91
|
+
"'proposal::<id>' or 'report::<id>'"
|
|
92
|
+
)
|
|
93
|
+
namespace, _, suffix = normalized.partition("::")
|
|
94
|
+
if not namespace or not suffix.strip():
|
|
95
|
+
raise ValueError("correlation_id must include a non-empty semantic identifier")
|
|
96
|
+
return normalized
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def proposal_correlation_id(proposal_id: str) -> str:
|
|
100
|
+
return _validate_correlation_id(f"proposal::{proposal_id.strip()}")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def profile_correlation_id(profile_id: str) -> str:
|
|
104
|
+
return _validate_correlation_id(f"profile::{profile_id.strip()}")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def pair_correlation_id(pair_id: str) -> str:
|
|
108
|
+
return _validate_correlation_id(f"pair::{pair_id.strip()}")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def report_correlation_id(report_id: str) -> str:
|
|
112
|
+
return _validate_correlation_id(f"report::{report_id.strip()}")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def dataset_correlation_id(dataset_id: str) -> str:
|
|
116
|
+
return _validate_correlation_id(f"dataset::{dataset_id.strip()}")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def graph_correlation_id(graph_id: str) -> str:
|
|
120
|
+
return _validate_correlation_id(f"graph::{graph_id.strip()}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def router_correlation_id(router_id: str) -> str:
|
|
124
|
+
return _validate_correlation_id(f"router::{router_id.strip()}")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def gaia_pipeline_correlation_id(run_id: str) -> str:
|
|
128
|
+
return _validate_correlation_id(f"gaia_pipeline::{run_id.strip()}")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def pair_canonical_key(pair: Mapping[str, Any]) -> tuple[str, str, str, str]:
|
|
132
|
+
return (
|
|
133
|
+
str(pair.get("pair_id", "")),
|
|
134
|
+
str(pair.get("profile_id", "")),
|
|
135
|
+
str(pair.get("proposal_id", "")),
|
|
136
|
+
str(pair.get("actual_status", "")),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def compute_audit_event_id(
|
|
141
|
+
event_type: str,
|
|
142
|
+
correlation_id: str,
|
|
143
|
+
logical_tick: int,
|
|
144
|
+
payload_hash: str,
|
|
145
|
+
) -> str:
|
|
146
|
+
canonical = canonical_json(
|
|
147
|
+
{
|
|
148
|
+
"correlation_id": _validate_correlation_id(correlation_id),
|
|
149
|
+
"event_type": str(event_type),
|
|
150
|
+
"logical_tick": int(logical_tick),
|
|
151
|
+
"payload_hash": str(payload_hash),
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _event_body(
|
|
158
|
+
*,
|
|
159
|
+
event_id: str,
|
|
160
|
+
event_type: str,
|
|
161
|
+
authority: EventAuthority,
|
|
162
|
+
logical_tick: int,
|
|
163
|
+
correlation_id: str,
|
|
164
|
+
payload: dict[str, Any],
|
|
165
|
+
) -> dict[str, Any]:
|
|
166
|
+
return {
|
|
167
|
+
"schema_version": AUDIT_SCHEMA_VERSION,
|
|
168
|
+
"event_id": event_id,
|
|
169
|
+
"event_type": event_type,
|
|
170
|
+
"authority": authority.value,
|
|
171
|
+
"logical_tick": int(logical_tick),
|
|
172
|
+
"correlation_id": correlation_id,
|
|
173
|
+
"payload": payload,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@dataclass(frozen=True)
|
|
178
|
+
class AuditEvent:
|
|
179
|
+
event_id: str
|
|
180
|
+
event_type: str
|
|
181
|
+
authority: EventAuthority
|
|
182
|
+
logical_tick: int
|
|
183
|
+
correlation_id: str
|
|
184
|
+
payload: dict[str, Any] = field(default_factory=dict)
|
|
185
|
+
fingerprint: str = ""
|
|
186
|
+
schema_version: str = AUDIT_SCHEMA_VERSION
|
|
187
|
+
|
|
188
|
+
def __post_init__(self) -> None:
|
|
189
|
+
if not isinstance(self.logical_tick, int):
|
|
190
|
+
raise TypeError(f"Expected int logical_tick, got {type(self.logical_tick).__name__}")
|
|
191
|
+
if self.logical_tick < 0:
|
|
192
|
+
raise ValueError("logical_tick must be non-negative")
|
|
193
|
+
|
|
194
|
+
normalized_authority = self.authority
|
|
195
|
+
if not isinstance(normalized_authority, EventAuthority):
|
|
196
|
+
normalized_authority = EventAuthority(str(normalized_authority))
|
|
197
|
+
|
|
198
|
+
normalized_correlation_id = _validate_correlation_id(self.correlation_id)
|
|
199
|
+
normalized_payload = _normalize_value(dict(self.payload))
|
|
200
|
+
payload_hash = compute_audit_fingerprint(normalized_payload)
|
|
201
|
+
expected_event_id = compute_audit_event_id(
|
|
202
|
+
self.event_type,
|
|
203
|
+
normalized_correlation_id,
|
|
204
|
+
self.logical_tick,
|
|
205
|
+
payload_hash,
|
|
206
|
+
)
|
|
207
|
+
body = _event_body(
|
|
208
|
+
event_id=expected_event_id,
|
|
209
|
+
event_type=self.event_type,
|
|
210
|
+
authority=normalized_authority,
|
|
211
|
+
logical_tick=self.logical_tick,
|
|
212
|
+
correlation_id=normalized_correlation_id,
|
|
213
|
+
payload=normalized_payload,
|
|
214
|
+
)
|
|
215
|
+
expected_fingerprint = compute_audit_fingerprint(body)
|
|
216
|
+
|
|
217
|
+
if self.event_id and self.event_id != expected_event_id:
|
|
218
|
+
raise ValueError("event_id does not match deterministic audit fingerprint")
|
|
219
|
+
if self.fingerprint and self.fingerprint != expected_fingerprint:
|
|
220
|
+
raise ValueError("fingerprint does not match deterministic audit fingerprint")
|
|
221
|
+
|
|
222
|
+
object.__setattr__(self, "authority", normalized_authority)
|
|
223
|
+
object.__setattr__(self, "correlation_id", normalized_correlation_id)
|
|
224
|
+
object.__setattr__(self, "payload", normalized_payload)
|
|
225
|
+
object.__setattr__(self, "event_id", expected_event_id)
|
|
226
|
+
object.__setattr__(self, "fingerprint", expected_fingerprint)
|
|
227
|
+
|
|
228
|
+
def to_dict(self) -> dict[str, Any]:
|
|
229
|
+
return {
|
|
230
|
+
"schema_version": self.schema_version,
|
|
231
|
+
"event_id": self.event_id,
|
|
232
|
+
"event_type": self.event_type,
|
|
233
|
+
"authority": self.authority.value,
|
|
234
|
+
"logical_tick": self.logical_tick,
|
|
235
|
+
"correlation_id": self.correlation_id,
|
|
236
|
+
"payload": _normalize_value(self.payload),
|
|
237
|
+
"fingerprint": self.fingerprint,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@classmethod
|
|
241
|
+
def from_dict(cls, data: Mapping[str, Any]) -> "AuditEvent":
|
|
242
|
+
return cls(
|
|
243
|
+
event_id=str(data.get("event_id", "")),
|
|
244
|
+
event_type=str(data.get("event_type", "")),
|
|
245
|
+
authority=EventAuthority(str(data.get("authority", EventAuthority.DERIVED.value))),
|
|
246
|
+
logical_tick=int(data.get("logical_tick", 0)),
|
|
247
|
+
correlation_id=str(data.get("correlation_id", "")),
|
|
248
|
+
payload=dict(data.get("payload", {})),
|
|
249
|
+
fingerprint=str(data.get("fingerprint", "")),
|
|
250
|
+
schema_version=str(data.get("schema_version", AUDIT_SCHEMA_VERSION)),
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def canonical_event_sort_key(event: AuditEvent) -> tuple[int, str, str, str]:
|
|
255
|
+
return (
|
|
256
|
+
int(event.logical_tick),
|
|
257
|
+
event.correlation_id,
|
|
258
|
+
event.event_type,
|
|
259
|
+
event.event_id,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _compatibility_terminal_event_type(pair: Mapping[str, Any]) -> str:
|
|
264
|
+
status = str(pair.get("actual_status", "")).lower()
|
|
265
|
+
profile_id = str(pair.get("profile_id", "")).lower()
|
|
266
|
+
pair_id = str(pair.get("pair_id", "")).lower()
|
|
267
|
+
if status != "compatible":
|
|
268
|
+
return "CompatibilityRejected"
|
|
269
|
+
if "certified" in pair_id or "certified" in profile_id:
|
|
270
|
+
return "CompatibilityCertified"
|
|
271
|
+
return "CompatibilityAccepted"
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def derive_compatibility_audit_events(
|
|
275
|
+
report: Mapping[str, Any],
|
|
276
|
+
*,
|
|
277
|
+
source_fingerprint: str | None = None,
|
|
278
|
+
) -> list[AuditEvent]:
|
|
279
|
+
report_fingerprint = source_fingerprint or compute_operational_fingerprint(report)
|
|
280
|
+
report_id = report_fingerprint
|
|
281
|
+
correlation = report_correlation_id(report_id)
|
|
282
|
+
pairs = list(report.get("matrix", []))
|
|
283
|
+
if not pairs:
|
|
284
|
+
pairs = list(report.get("pairs", []))
|
|
285
|
+
pairs = sorted(
|
|
286
|
+
[pair for pair in pairs if isinstance(pair, Mapping)],
|
|
287
|
+
key=pair_canonical_key,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
events: list[AuditEvent] = []
|
|
291
|
+
for index, pair in enumerate(pairs):
|
|
292
|
+
pair_id = str(pair.get("pair_id", "")).strip()
|
|
293
|
+
if not pair_id:
|
|
294
|
+
continue
|
|
295
|
+
pair_correlation = pair_correlation_id(pair_id)
|
|
296
|
+
payload = {
|
|
297
|
+
"pair_id": pair_id,
|
|
298
|
+
"profile_id": str(pair.get("profile_id", "")),
|
|
299
|
+
"proposal_id": str(pair.get("proposal_id", "")),
|
|
300
|
+
"expected_status": str(pair.get("expected_status", "")),
|
|
301
|
+
"actual_status": str(pair.get("actual_status", "")),
|
|
302
|
+
"incompatible_reason": pair.get("incompatible_reason"),
|
|
303
|
+
"report_id": report_id,
|
|
304
|
+
}
|
|
305
|
+
events.append(
|
|
306
|
+
AuditEvent(
|
|
307
|
+
event_id="",
|
|
308
|
+
event_type="CompatibilityChecked",
|
|
309
|
+
authority=EventAuthority.DERIVED,
|
|
310
|
+
logical_tick=index * 2,
|
|
311
|
+
correlation_id=pair_correlation,
|
|
312
|
+
payload=payload,
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
terminal_type = _compatibility_terminal_event_type(pair)
|
|
316
|
+
if terminal_type == "CompatibilityRejected":
|
|
317
|
+
terminal_payload = {
|
|
318
|
+
**payload,
|
|
319
|
+
"decision": "rejected",
|
|
320
|
+
}
|
|
321
|
+
elif terminal_type == "CompatibilityCertified":
|
|
322
|
+
terminal_payload = {
|
|
323
|
+
**payload,
|
|
324
|
+
"decision": "certified",
|
|
325
|
+
}
|
|
326
|
+
else:
|
|
327
|
+
terminal_payload = {
|
|
328
|
+
**payload,
|
|
329
|
+
"decision": "accepted",
|
|
330
|
+
}
|
|
331
|
+
events.append(
|
|
332
|
+
AuditEvent(
|
|
333
|
+
event_id="",
|
|
334
|
+
event_type=terminal_type,
|
|
335
|
+
authority=EventAuthority.DERIVED,
|
|
336
|
+
logical_tick=index * 2 + 1,
|
|
337
|
+
correlation_id=pair_correlation,
|
|
338
|
+
payload=terminal_payload,
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
report_payload = {
|
|
343
|
+
"report_id": report_id,
|
|
344
|
+
"report_schema": str(report.get("schema", "")),
|
|
345
|
+
"status": str(report.get("status", "")),
|
|
346
|
+
"summary": dict(report.get("summary", {})) if isinstance(report.get("summary", {}), Mapping) else report.get("summary", {}),
|
|
347
|
+
"pair_count": len(pairs),
|
|
348
|
+
}
|
|
349
|
+
events.append(
|
|
350
|
+
AuditEvent(
|
|
351
|
+
event_id="",
|
|
352
|
+
event_type="CompatibilityMatrixReported",
|
|
353
|
+
authority=EventAuthority.DERIVED,
|
|
354
|
+
logical_tick=len(events),
|
|
355
|
+
correlation_id=correlation,
|
|
356
|
+
payload=report_payload,
|
|
357
|
+
)
|
|
358
|
+
)
|
|
359
|
+
return events
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _decision_event_type(decision: str) -> str:
|
|
363
|
+
normalized = decision.lower().strip()
|
|
364
|
+
if normalized == "certified":
|
|
365
|
+
return "ProposalCertified"
|
|
366
|
+
if normalized == "accepted":
|
|
367
|
+
return "ProposalAccepted"
|
|
368
|
+
if normalized == "unsafe":
|
|
369
|
+
return "ProposalUnsafe"
|
|
370
|
+
if normalized == "unsupported":
|
|
371
|
+
return "ProposalUnsupported"
|
|
372
|
+
if normalized == "missing_evidence":
|
|
373
|
+
return "ProposalMissingEvidence"
|
|
374
|
+
return "ProposalRejected"
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def derive_expert_report_audit_events(
|
|
378
|
+
report: Mapping[str, Any],
|
|
379
|
+
*,
|
|
380
|
+
source_fingerprint: str | None = None,
|
|
381
|
+
) -> list[AuditEvent]:
|
|
382
|
+
report_fingerprint = source_fingerprint or compute_operational_fingerprint(report)
|
|
383
|
+
report_id = report_fingerprint
|
|
384
|
+
correlation = report_correlation_id(report_id)
|
|
385
|
+
proposals = list(report.get("proposals", []))
|
|
386
|
+
proposals = sorted(
|
|
387
|
+
[proposal for proposal in proposals if isinstance(proposal, Mapping)],
|
|
388
|
+
key=lambda proposal: (
|
|
389
|
+
str(proposal.get("proposal_id", "")),
|
|
390
|
+
str(proposal.get("file", "")),
|
|
391
|
+
str(proposal.get("decision", "")),
|
|
392
|
+
),
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
events: list[AuditEvent] = []
|
|
396
|
+
for index, proposal in enumerate(proposals):
|
|
397
|
+
proposal_id = str(proposal.get("proposal_id", "")).strip()
|
|
398
|
+
if not proposal_id:
|
|
399
|
+
continue
|
|
400
|
+
proposal_correlation = proposal_correlation_id(proposal_id)
|
|
401
|
+
payload = {
|
|
402
|
+
"proposal_id": proposal_id,
|
|
403
|
+
"decision": str(proposal.get("decision", "")),
|
|
404
|
+
"reason_code": str(proposal.get("reason_code", "")),
|
|
405
|
+
"status": str(proposal.get("status", "")),
|
|
406
|
+
"file": str(proposal.get("file", "")),
|
|
407
|
+
"report_id": report_id,
|
|
408
|
+
}
|
|
409
|
+
events.append(
|
|
410
|
+
AuditEvent(
|
|
411
|
+
event_id="",
|
|
412
|
+
event_type="ProposalEvaluated",
|
|
413
|
+
authority=EventAuthority.DERIVED,
|
|
414
|
+
logical_tick=index * 2,
|
|
415
|
+
correlation_id=proposal_correlation,
|
|
416
|
+
payload=payload,
|
|
417
|
+
)
|
|
418
|
+
)
|
|
419
|
+
decision_event_type = _decision_event_type(payload["decision"])
|
|
420
|
+
events.append(
|
|
421
|
+
AuditEvent(
|
|
422
|
+
event_id="",
|
|
423
|
+
event_type=decision_event_type,
|
|
424
|
+
authority=EventAuthority.DERIVED,
|
|
425
|
+
logical_tick=index * 2 + 1,
|
|
426
|
+
correlation_id=proposal_correlation,
|
|
427
|
+
payload=payload,
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
report_payload = {
|
|
432
|
+
"report_id": report_id,
|
|
433
|
+
"report_schema": str(report.get("schema", "")),
|
|
434
|
+
"status": str(report.get("status", "")),
|
|
435
|
+
"summary": dict(report.get("summary", {})) if isinstance(report.get("summary", {}), Mapping) else report.get("summary", {}),
|
|
436
|
+
"proposal_count": len(proposals),
|
|
437
|
+
}
|
|
438
|
+
events.append(
|
|
439
|
+
AuditEvent(
|
|
440
|
+
event_id="",
|
|
441
|
+
event_type="ExpertProposalBatchReported",
|
|
442
|
+
authority=EventAuthority.DERIVED,
|
|
443
|
+
logical_tick=len(events),
|
|
444
|
+
correlation_id=correlation,
|
|
445
|
+
payload=report_payload,
|
|
446
|
+
)
|
|
447
|
+
)
|
|
448
|
+
return events
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _router_validation_audit_events(
|
|
452
|
+
payload: Mapping[str, Any],
|
|
453
|
+
*,
|
|
454
|
+
source_fingerprint: str | None = None,
|
|
455
|
+
) -> list[AuditEvent]:
|
|
456
|
+
routing_id = str(payload.get("routing_id", "")).strip() or source_fingerprint or "router"
|
|
457
|
+
warnings = [item for item in payload.get("warnings", []) if isinstance(item, Mapping)]
|
|
458
|
+
errors = [item for item in payload.get("errors", []) if isinstance(item, Mapping)]
|
|
459
|
+
event_payload = {
|
|
460
|
+
"routing_id": routing_id,
|
|
461
|
+
"validation_schema": str(payload.get("schema", "")),
|
|
462
|
+
"status": str(payload.get("status", "")),
|
|
463
|
+
"error_count": len(errors),
|
|
464
|
+
"warning_count": len(warnings),
|
|
465
|
+
"error_codes": [
|
|
466
|
+
str(item.get("code", "")).strip()
|
|
467
|
+
for item in errors
|
|
468
|
+
if str(item.get("code", "")).strip()
|
|
469
|
+
],
|
|
470
|
+
"warning_codes": [
|
|
471
|
+
str(item.get("code", "")).strip()
|
|
472
|
+
for item in warnings
|
|
473
|
+
if str(item.get("code", "")).strip()
|
|
474
|
+
],
|
|
475
|
+
"source_fingerprint": source_fingerprint or compute_operational_fingerprint(payload),
|
|
476
|
+
}
|
|
477
|
+
return [
|
|
478
|
+
AuditEvent(
|
|
479
|
+
event_id="",
|
|
480
|
+
event_type="RouterFixtureValidated",
|
|
481
|
+
authority=EventAuthority.DERIVED,
|
|
482
|
+
logical_tick=0,
|
|
483
|
+
correlation_id=router_correlation_id(routing_id),
|
|
484
|
+
payload=event_payload,
|
|
485
|
+
)
|
|
486
|
+
]
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def _router_evaluation_audit_events(
|
|
490
|
+
payload: Mapping[str, Any],
|
|
491
|
+
*,
|
|
492
|
+
source_fingerprint: str | None = None,
|
|
493
|
+
) -> list[AuditEvent]:
|
|
494
|
+
routing_id = str(payload.get("routing_id", "")).strip() or source_fingerprint or "router"
|
|
495
|
+
profile_id = str(payload.get("profile_id", "")).strip()
|
|
496
|
+
summary = payload.get("evaluation_summary", {})
|
|
497
|
+
summary_payload = dict(summary) if isinstance(summary, Mapping) else {}
|
|
498
|
+
selected_experts = [item for item in payload.get("selected_experts", []) if isinstance(item, Mapping)]
|
|
499
|
+
rejected_experts = [item for item in payload.get("rejected_experts", []) if isinstance(item, Mapping)]
|
|
500
|
+
selected_expert_records = [
|
|
501
|
+
{
|
|
502
|
+
"decision": "selected",
|
|
503
|
+
"expert_id": str(item.get("expert_id", "")).strip(),
|
|
504
|
+
"proposal_id": str(item.get("proposal_id", "")).strip(),
|
|
505
|
+
"reason": str(item.get("reason", "")).strip(),
|
|
506
|
+
}
|
|
507
|
+
for item in selected_experts
|
|
508
|
+
if str(item.get("expert_id", "")).strip() and str(item.get("proposal_id", "")).strip()
|
|
509
|
+
]
|
|
510
|
+
rejected_expert_records = [
|
|
511
|
+
{
|
|
512
|
+
"decision": "rejected",
|
|
513
|
+
"expert_id": str(item.get("expert_id", "")).strip(),
|
|
514
|
+
"proposal_id": str(item.get("proposal_id", "")).strip(),
|
|
515
|
+
"reason": str(item.get("reason", "")).strip(),
|
|
516
|
+
}
|
|
517
|
+
for item in rejected_experts
|
|
518
|
+
if str(item.get("expert_id", "")).strip() and str(item.get("proposal_id", "")).strip()
|
|
519
|
+
]
|
|
520
|
+
event_payload = {
|
|
521
|
+
"routing_id": routing_id,
|
|
522
|
+
"profile_id": profile_id,
|
|
523
|
+
"evaluation_schema": str(payload.get("schema", "")),
|
|
524
|
+
"status": str(payload.get("status", "")),
|
|
525
|
+
"selected_count": int(summary_payload.get("selected_count", len(selected_experts))),
|
|
526
|
+
"rejected_count": int(summary_payload.get("rejected_count", len(rejected_experts))),
|
|
527
|
+
"total_proposals": int(summary_payload.get("total_proposals", len(selected_experts) + len(rejected_experts))),
|
|
528
|
+
"reason_codes": [
|
|
529
|
+
str(code).strip()
|
|
530
|
+
for code in payload.get("reason_codes", [])
|
|
531
|
+
if str(code).strip()
|
|
532
|
+
],
|
|
533
|
+
"selected_expert_ids": [
|
|
534
|
+
str(item.get("expert_id", "")).strip()
|
|
535
|
+
for item in selected_experts
|
|
536
|
+
if str(item.get("expert_id", "")).strip()
|
|
537
|
+
],
|
|
538
|
+
"rejected_expert_ids": [
|
|
539
|
+
str(item.get("expert_id", "")).strip()
|
|
540
|
+
for item in rejected_experts
|
|
541
|
+
if str(item.get("expert_id", "")).strip()
|
|
542
|
+
],
|
|
543
|
+
"selected_experts": selected_expert_records,
|
|
544
|
+
"rejected_experts": rejected_expert_records,
|
|
545
|
+
"source_fingerprint": source_fingerprint or compute_operational_fingerprint(payload),
|
|
546
|
+
}
|
|
547
|
+
return [
|
|
548
|
+
AuditEvent(
|
|
549
|
+
event_id="",
|
|
550
|
+
event_type="RouterEligibilityEvaluated",
|
|
551
|
+
authority=EventAuthority.DERIVED,
|
|
552
|
+
logical_tick=0,
|
|
553
|
+
correlation_id=router_correlation_id(routing_id),
|
|
554
|
+
payload=event_payload,
|
|
555
|
+
)
|
|
556
|
+
]
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def _router_report_audit_events(
|
|
560
|
+
payload: Mapping[str, Any],
|
|
561
|
+
*,
|
|
562
|
+
source_fingerprint: str | None = None,
|
|
563
|
+
) -> list[AuditEvent]:
|
|
564
|
+
report_id = source_fingerprint or compute_operational_fingerprint(payload)
|
|
565
|
+
summary = payload.get("summary", {})
|
|
566
|
+
summary_payload = dict(summary) if isinstance(summary, Mapping) else {}
|
|
567
|
+
fixtures = [item for item in payload.get("fixtures", []) if isinstance(item, Mapping)]
|
|
568
|
+
event_payload = {
|
|
569
|
+
"report_id": report_id,
|
|
570
|
+
"report_schema": str(payload.get("schema", "")),
|
|
571
|
+
"status": str(payload.get("status", "")),
|
|
572
|
+
"summary": summary_payload,
|
|
573
|
+
"fixture_count": len(fixtures),
|
|
574
|
+
"fixture_files": [
|
|
575
|
+
str(item.get("file", "")).strip()
|
|
576
|
+
for item in fixtures
|
|
577
|
+
if str(item.get("file", "")).strip()
|
|
578
|
+
],
|
|
579
|
+
"source_fingerprint": source_fingerprint or compute_operational_fingerprint(payload),
|
|
580
|
+
}
|
|
581
|
+
return [
|
|
582
|
+
AuditEvent(
|
|
583
|
+
event_id="",
|
|
584
|
+
event_type="RouterBatchReportGenerated",
|
|
585
|
+
authority=EventAuthority.DERIVED,
|
|
586
|
+
logical_tick=0,
|
|
587
|
+
correlation_id=report_correlation_id(report_id),
|
|
588
|
+
payload=event_payload,
|
|
589
|
+
)
|
|
590
|
+
]
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def _router_replay_audit_events(
|
|
594
|
+
payload: Mapping[str, Any],
|
|
595
|
+
*,
|
|
596
|
+
source_fingerprint: str | None = None,
|
|
597
|
+
) -> list[AuditEvent]:
|
|
598
|
+
routing_id = str(payload.get("routing_id", "")).strip()
|
|
599
|
+
event_type = "RouterReplayCertified" if str(payload.get("status", "")).strip() == "certified" else "RouterReplayDiverged"
|
|
600
|
+
run_1 = payload.get("run_1", {})
|
|
601
|
+
run_2 = payload.get("run_2", {})
|
|
602
|
+
event_payload = {
|
|
603
|
+
"replay_schema": str(payload.get("replay_schema", payload.get("schema", ""))),
|
|
604
|
+
"status": str(payload.get("status", "")),
|
|
605
|
+
"fixture": str(payload.get("fixture", "")).strip(),
|
|
606
|
+
"routing_id": routing_id,
|
|
607
|
+
"fingerprint": payload.get("fingerprint"),
|
|
608
|
+
"diff": payload.get("diff"),
|
|
609
|
+
"run_1_output_hash": run_1.get("output_hash") if isinstance(run_1, Mapping) else None,
|
|
610
|
+
"run_2_output_hash": run_2.get("output_hash") if isinstance(run_2, Mapping) else None,
|
|
611
|
+
"source_fingerprint": source_fingerprint or compute_operational_fingerprint(payload),
|
|
612
|
+
}
|
|
613
|
+
correlation_seed = routing_id or source_fingerprint or compute_operational_fingerprint(payload)
|
|
614
|
+
return [
|
|
615
|
+
AuditEvent(
|
|
616
|
+
event_id="",
|
|
617
|
+
event_type=event_type,
|
|
618
|
+
authority=EventAuthority.DERIVED,
|
|
619
|
+
logical_tick=0,
|
|
620
|
+
correlation_id=router_correlation_id(correlation_seed),
|
|
621
|
+
payload=event_payload,
|
|
622
|
+
)
|
|
623
|
+
]
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def _gaia_surrogate_sort_key(entry: Mapping[str, Any]) -> tuple[int, str, int, str]:
|
|
627
|
+
source_id = str(entry.get("source_id", "")).strip()
|
|
628
|
+
row_index = entry.get("row_index", 0)
|
|
629
|
+
try:
|
|
630
|
+
numeric_source_id = int(source_id)
|
|
631
|
+
except ValueError:
|
|
632
|
+
numeric_source_id = 0
|
|
633
|
+
try:
|
|
634
|
+
numeric_row_index = int(row_index)
|
|
635
|
+
except (TypeError, ValueError):
|
|
636
|
+
numeric_row_index = 0
|
|
637
|
+
return (
|
|
638
|
+
numeric_row_index,
|
|
639
|
+
source_id,
|
|
640
|
+
numeric_source_id,
|
|
641
|
+
str(entry.get("proposal_id", "")),
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def derive_gaia_surrogate_audit_events(
|
|
646
|
+
evaluation: Mapping[str, Any],
|
|
647
|
+
*,
|
|
648
|
+
source_fingerprint: str | None = None,
|
|
649
|
+
) -> list[AuditEvent]:
|
|
650
|
+
evaluation_fingerprint = source_fingerprint or compute_operational_fingerprint(evaluation)
|
|
651
|
+
run_id = str(evaluation.get("run_id", "")).strip() or evaluation_fingerprint
|
|
652
|
+
correlation = gaia_pipeline_correlation_id(run_id)
|
|
653
|
+
proposal_id = str(evaluation.get("proposal_id", "")).strip()
|
|
654
|
+
proposal_hash = str(evaluation.get("proposal_hash", "")).strip()
|
|
655
|
+
weights_hash = str(evaluation.get("weights_hash", "")).strip()
|
|
656
|
+
mismatches = list(evaluation.get("mismatches", []))
|
|
657
|
+
mismatches = sorted(
|
|
658
|
+
[entry for entry in mismatches if isinstance(entry, Mapping)],
|
|
659
|
+
key=_gaia_surrogate_sort_key,
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
total_evaluated = int(evaluation.get("total_evaluated", len(mismatches)))
|
|
663
|
+
correct_predictions = int(evaluation.get("correct_predictions", max(total_evaluated - len(mismatches), 0)))
|
|
664
|
+
agreement_rate = float(evaluation.get("agreement_rate", 0.0))
|
|
665
|
+
decision_threshold = float(evaluation.get("decision_threshold", 0.5))
|
|
666
|
+
|
|
667
|
+
summary_payload = {
|
|
668
|
+
"run_id": run_id,
|
|
669
|
+
"proposal_id": proposal_id,
|
|
670
|
+
"proposal_hash": proposal_hash,
|
|
671
|
+
"evaluation_hash": evaluation_fingerprint,
|
|
672
|
+
"weights_hash": weights_hash,
|
|
673
|
+
"feature_columns": list(evaluation.get("feature_columns", [])),
|
|
674
|
+
"decision_threshold": decision_threshold,
|
|
675
|
+
"total_evaluated": total_evaluated,
|
|
676
|
+
"correct_predictions": correct_predictions,
|
|
677
|
+
"mismatch_count": len(mismatches),
|
|
678
|
+
"agreement_rate": agreement_rate,
|
|
679
|
+
}
|
|
680
|
+
events: list[AuditEvent] = [
|
|
681
|
+
AuditEvent(
|
|
682
|
+
event_id="",
|
|
683
|
+
event_type="GaiaSurrogateProposalEvaluated",
|
|
684
|
+
authority=EventAuthority.DERIVED,
|
|
685
|
+
logical_tick=0,
|
|
686
|
+
correlation_id=correlation,
|
|
687
|
+
payload=summary_payload,
|
|
688
|
+
)
|
|
689
|
+
]
|
|
690
|
+
|
|
691
|
+
for index, mismatch in enumerate(mismatches, start=1):
|
|
692
|
+
mismatch_payload = {
|
|
693
|
+
"run_id": run_id,
|
|
694
|
+
"proposal_id": proposal_id,
|
|
695
|
+
"source_id": str(mismatch.get("source_id", "")).strip(),
|
|
696
|
+
"row_index": int(mismatch.get("row_index", index)),
|
|
697
|
+
"predicted_nearby": bool(mismatch.get("predicted_nearby", False)),
|
|
698
|
+
"actual_nearby": bool(mismatch.get("actual_nearby", False)),
|
|
699
|
+
"probability": mismatch.get("probability"),
|
|
700
|
+
"distance_pc": mismatch.get("distance_pc"),
|
|
701
|
+
"decision_threshold": decision_threshold,
|
|
702
|
+
"evaluation_hash": evaluation_fingerprint,
|
|
703
|
+
}
|
|
704
|
+
events.append(
|
|
705
|
+
AuditEvent(
|
|
706
|
+
event_id="",
|
|
707
|
+
event_type="GaiaSurrogatePredictionMismatch",
|
|
708
|
+
authority=EventAuthority.DERIVED,
|
|
709
|
+
logical_tick=index,
|
|
710
|
+
correlation_id=correlation,
|
|
711
|
+
payload=mismatch_payload,
|
|
712
|
+
)
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
completion_payload = {
|
|
716
|
+
"run_id": run_id,
|
|
717
|
+
"proposal_id": proposal_id,
|
|
718
|
+
"evaluation_hash": evaluation_fingerprint,
|
|
719
|
+
"proposal_hash": proposal_hash,
|
|
720
|
+
"weights_hash": weights_hash,
|
|
721
|
+
"total_evaluated": total_evaluated,
|
|
722
|
+
"mismatch_count": len(mismatches),
|
|
723
|
+
"agreement_rate": agreement_rate,
|
|
724
|
+
"status": str(evaluation.get("status", "passed")),
|
|
725
|
+
}
|
|
726
|
+
events.append(
|
|
727
|
+
AuditEvent(
|
|
728
|
+
event_id="",
|
|
729
|
+
event_type="GaiaSurrogateEvaluationCompleted",
|
|
730
|
+
authority=EventAuthority.DERIVED,
|
|
731
|
+
logical_tick=len(events),
|
|
732
|
+
correlation_id=correlation,
|
|
733
|
+
payload=completion_payload,
|
|
734
|
+
)
|
|
735
|
+
)
|
|
736
|
+
return events
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def derive_events_for_source(
|
|
740
|
+
*,
|
|
741
|
+
artifact_type: str,
|
|
742
|
+
artifact_payload: Mapping[str, Any],
|
|
743
|
+
source_fingerprint: str | None = None,
|
|
744
|
+
) -> list[AuditEvent]:
|
|
745
|
+
normalized_type = artifact_type.strip().lower()
|
|
746
|
+
if normalized_type == "compatibility_matrix":
|
|
747
|
+
return derive_compatibility_audit_events(artifact_payload, source_fingerprint=source_fingerprint)
|
|
748
|
+
if normalized_type == "expert_report":
|
|
749
|
+
return derive_expert_report_audit_events(artifact_payload, source_fingerprint=source_fingerprint)
|
|
750
|
+
if normalized_type == "expert_router_validation":
|
|
751
|
+
return _router_validation_audit_events(artifact_payload, source_fingerprint=source_fingerprint)
|
|
752
|
+
if normalized_type == "expert_router_evaluation":
|
|
753
|
+
return _router_evaluation_audit_events(artifact_payload, source_fingerprint=source_fingerprint)
|
|
754
|
+
if normalized_type in {"expert_router_report", "expert_router_batch_report"}:
|
|
755
|
+
return _router_report_audit_events(artifact_payload, source_fingerprint=source_fingerprint)
|
|
756
|
+
if normalized_type in {"expert_router_replay_certification", "expert_router_replay_certification_batch"}:
|
|
757
|
+
return _router_replay_audit_events(artifact_payload, source_fingerprint=source_fingerprint)
|
|
758
|
+
if normalized_type == "gaia_surrogate":
|
|
759
|
+
return derive_gaia_surrogate_audit_events(artifact_payload, source_fingerprint=source_fingerprint)
|
|
760
|
+
raise ValueError(f"Unsupported audit source type: {artifact_type}")
|