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,131 @@
|
|
|
1
|
+
"""Closed deterministic replay for the public validation-only contract language."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import copy
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from typing import Any, TypedDict
|
|
8
|
+
|
|
9
|
+
from core_runtime.core.canonicalization import canonical_json_hash
|
|
10
|
+
from core_runtime.core.contract_evaluator import evaluate_contract_payload, input_fingerprint
|
|
11
|
+
from core_runtime.core.contract_program_registry import execute_registry_operation
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ContractProgramExecution(TypedDict):
|
|
15
|
+
schema: str
|
|
16
|
+
status: str
|
|
17
|
+
authority: str
|
|
18
|
+
execution_authorized: bool
|
|
19
|
+
state_application_authorized: bool
|
|
20
|
+
program_fingerprint: str
|
|
21
|
+
input_fingerprint: str
|
|
22
|
+
emitted: list[dict[str, Any]]
|
|
23
|
+
staged_transitions: list[dict[str, str]]
|
|
24
|
+
errors: list[dict[str, str]]
|
|
25
|
+
execution_fingerprint: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _error(code: str, message: str, field: str) -> dict[str, str]:
|
|
29
|
+
return {"code": code, "message": message, "field": field}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def execute_contract_program(program: Mapping[str, Any], sealed_inputs: Mapping[str, Any]) -> ContractProgramExecution:
|
|
33
|
+
"""Replay one closed program against caller-supplied sealed inputs.
|
|
34
|
+
|
|
35
|
+
This function neither reads external state nor applies transitions. A transition
|
|
36
|
+
instruction only records a candidate transition in the result for a separate,
|
|
37
|
+
explicitly authorized system to evaluate.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
program_payload = copy.deepcopy(dict(program))
|
|
41
|
+
evaluation = evaluate_contract_payload(program_payload)
|
|
42
|
+
execution: dict[str, Any] = {
|
|
43
|
+
"schema": "core.contract_program_execution.v1",
|
|
44
|
+
"status": "rejected" if evaluation["status"] != "passed" else "passed",
|
|
45
|
+
"authority": "validation_only",
|
|
46
|
+
"execution_authorized": False,
|
|
47
|
+
"state_application_authorized": False,
|
|
48
|
+
"program_fingerprint": str(program_payload.get("fingerprint", "")),
|
|
49
|
+
"input_fingerprint": input_fingerprint(dict(sealed_inputs)),
|
|
50
|
+
"emitted": [],
|
|
51
|
+
"staged_transitions": [],
|
|
52
|
+
"errors": [],
|
|
53
|
+
}
|
|
54
|
+
if evaluation["status"] != "passed":
|
|
55
|
+
execution["errors"] = [
|
|
56
|
+
{"code": str(item["code"]), "message": str(item["message"]), "field": str(item["field"])}
|
|
57
|
+
for item in evaluation["errors"]
|
|
58
|
+
]
|
|
59
|
+
else:
|
|
60
|
+
values: dict[str, Any] = {}
|
|
61
|
+
max_items = int(program_payload["limits"]["max_items"])
|
|
62
|
+
for index, instruction in enumerate(program_payload["instructions"]):
|
|
63
|
+
opcode = instruction["opcode"]
|
|
64
|
+
field = f"instructions[{index}]"
|
|
65
|
+
if opcode == "load":
|
|
66
|
+
key = instruction["key"]
|
|
67
|
+
if key not in sealed_inputs:
|
|
68
|
+
execution["status"] = "insufficient_data"
|
|
69
|
+
execution["errors"].append(_error("sealed_input_missing", "Required sealed input is absent.", f"{field}.key"))
|
|
70
|
+
break
|
|
71
|
+
values[instruction["output"]] = copy.deepcopy(sealed_inputs[key])
|
|
72
|
+
elif opcode == "assert":
|
|
73
|
+
key = instruction["key"]
|
|
74
|
+
if key not in sealed_inputs:
|
|
75
|
+
execution["status"] = "insufficient_data"
|
|
76
|
+
execution["errors"].append(_error("sealed_input_missing", "Required sealed input is absent.", f"{field}.key"))
|
|
77
|
+
break
|
|
78
|
+
if sealed_inputs[key] != instruction["equals"]:
|
|
79
|
+
execution["status"] = "blocked"
|
|
80
|
+
execution["errors"].append(_error("assertion_failed", "Sealed input does not match the declared assertion.", f"{field}.equals"))
|
|
81
|
+
break
|
|
82
|
+
elif opcode == "derive":
|
|
83
|
+
keys = instruction["input_keys"]
|
|
84
|
+
if any(key not in values for key in keys):
|
|
85
|
+
execution["status"] = "blocked"
|
|
86
|
+
execution["errors"].append(_error("derived_input_missing", "Derivation requires a prior output key.", f"{field}.input_keys"))
|
|
87
|
+
break
|
|
88
|
+
if instruction["operation"] == "copy":
|
|
89
|
+
values[instruction["output"]] = copy.deepcopy(values[keys[0]])
|
|
90
|
+
elif instruction["operation"] == "registry":
|
|
91
|
+
try:
|
|
92
|
+
values[instruction["output"]] = execute_registry_operation(
|
|
93
|
+
str(instruction["registry_key"]),
|
|
94
|
+
[values[key] for key in keys],
|
|
95
|
+
)
|
|
96
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
97
|
+
execution["status"] = "blocked"
|
|
98
|
+
execution["errors"].append(
|
|
99
|
+
_error("registry_operation_rejected", str(exc), f"{field}.registry_key")
|
|
100
|
+
)
|
|
101
|
+
break
|
|
102
|
+
else:
|
|
103
|
+
values[instruction["output"]] = len(keys)
|
|
104
|
+
elif opcode == "transition":
|
|
105
|
+
execution["staged_transitions"].append(
|
|
106
|
+
{
|
|
107
|
+
"transition_id": instruction["transition_id"],
|
|
108
|
+
"before_ref": instruction["before_ref"],
|
|
109
|
+
"after_ref": instruction["after_ref"],
|
|
110
|
+
"reversibility_class": instruction["reversibility_class"],
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
elif opcode == "emit":
|
|
114
|
+
key = instruction["value_key"]
|
|
115
|
+
if key not in values:
|
|
116
|
+
execution["status"] = "blocked"
|
|
117
|
+
execution["errors"].append(_error("emitted_value_missing", "Emit requires a prior output key.", f"{field}.value_key"))
|
|
118
|
+
break
|
|
119
|
+
execution["emitted"].append({"code": instruction["code"], "value": copy.deepcopy(values[key])})
|
|
120
|
+
elif opcode == "halt":
|
|
121
|
+
execution["status"] = instruction["status"]
|
|
122
|
+
|
|
123
|
+
if len(values) > max_items:
|
|
124
|
+
execution["status"] = "blocked"
|
|
125
|
+
execution["errors"].append(_error("runtime_item_limit_exceeded", "Runtime output count exceeds max_items.", "limits.max_items"))
|
|
126
|
+
break
|
|
127
|
+
|
|
128
|
+
if execution["status"] != "passed":
|
|
129
|
+
execution["staged_transitions"] = []
|
|
130
|
+
execution["execution_fingerprint"] = f"sha256:{canonical_json_hash(execution)}"
|
|
131
|
+
return execution # type: ignore[return-value]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Finite registry-only operations for the isolated ContractProgram fork.
|
|
2
|
+
|
|
3
|
+
This module deliberately exposes a small, versioned operation surface. It is
|
|
4
|
+
not a dynamic evaluator and it never loads executable configuration from a
|
|
5
|
+
declaration.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
from collections.abc import Mapping, Sequence
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
REGISTRY_VERSION = "core.contract_program.registry.v1"
|
|
15
|
+
REGISTRY_KEYS = frozenset(
|
|
16
|
+
{
|
|
17
|
+
"rank.quantity.v1",
|
|
18
|
+
"member.scale_adapter.v1",
|
|
19
|
+
"regex.grammar_context.v1",
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
OPERATION_ALLOWLIST = frozenset(
|
|
23
|
+
{
|
|
24
|
+
"equals",
|
|
25
|
+
"distinct_count",
|
|
26
|
+
"is_nonempty_string",
|
|
27
|
+
"rank_of",
|
|
28
|
+
"min_of",
|
|
29
|
+
"compare",
|
|
30
|
+
"logical_and",
|
|
31
|
+
"logical_or",
|
|
32
|
+
"logical_not",
|
|
33
|
+
"member_of",
|
|
34
|
+
"regex_match",
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
FORBIDDEN_CONSTRUCTS = frozenset(
|
|
38
|
+
{"loop", "branch", "jump", "dynamic_import", "eval", "callback", "inline_policy"}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
_GRAMMARS = {
|
|
42
|
+
"qualified_ref": re.compile(r"^[a-z][a-z0-9_.-]{2,}:[^\s]{3,}$", re.IGNORECASE),
|
|
43
|
+
"identifier": re.compile(r"^[A-Za-z][A-Za-z0-9_.-]{0,127}$"),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class RegistryOperationError(ValueError):
|
|
48
|
+
"""Raised when a candidate requests an operation outside the registry."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _require_arity(inputs: Sequence[Any], expected: int) -> None:
|
|
52
|
+
if len(inputs) != expected:
|
|
53
|
+
raise RegistryOperationError(f"registry operation requires {expected} inputs")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def execute_registry_operation(registry_key: str, inputs: Sequence[Any]) -> Any:
|
|
57
|
+
"""Execute one deterministic, allowlisted registry operation."""
|
|
58
|
+
|
|
59
|
+
if registry_key not in REGISTRY_KEYS:
|
|
60
|
+
raise RegistryOperationError(f"unknown registry key: {registry_key!r}")
|
|
61
|
+
if not isinstance(inputs, Sequence) or isinstance(inputs, (str, bytes)):
|
|
62
|
+
raise RegistryOperationError("registry inputs must be a finite sequence")
|
|
63
|
+
|
|
64
|
+
if registry_key == "rank.quantity.v1":
|
|
65
|
+
_require_arity(inputs, 1)
|
|
66
|
+
value = inputs[0]
|
|
67
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
68
|
+
raise RegistryOperationError("quantity rank must be a non-negative integer")
|
|
69
|
+
return value
|
|
70
|
+
|
|
71
|
+
if registry_key == "member.scale_adapter.v1":
|
|
72
|
+
_require_arity(inputs, 4)
|
|
73
|
+
adapter_from, adapter_to, requested_from, requested_to = inputs
|
|
74
|
+
return adapter_from == requested_from and adapter_to == requested_to
|
|
75
|
+
|
|
76
|
+
_require_arity(inputs, 2)
|
|
77
|
+
value, grammar_name = inputs
|
|
78
|
+
if not isinstance(value, str) or not isinstance(grammar_name, str):
|
|
79
|
+
return False
|
|
80
|
+
grammar = _GRAMMARS.get(grammar_name)
|
|
81
|
+
if grammar is None:
|
|
82
|
+
raise RegistryOperationError(f"unknown grammar context: {grammar_name!r}")
|
|
83
|
+
return bool(grammar.fullmatch(value))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def validate_registry_program(program: Mapping[str, Any]) -> list[str]:
|
|
87
|
+
"""Return deterministic profile errors for a candidate program."""
|
|
88
|
+
|
|
89
|
+
errors: list[str] = []
|
|
90
|
+
instructions = program.get("instructions", [])
|
|
91
|
+
if not isinstance(instructions, list):
|
|
92
|
+
return ["instructions must be a list"]
|
|
93
|
+
for index, instruction in enumerate(instructions):
|
|
94
|
+
if not isinstance(instruction, Mapping):
|
|
95
|
+
errors.append(f"instructions[{index}] is not an object")
|
|
96
|
+
continue
|
|
97
|
+
for key in FORBIDDEN_CONSTRUCTS:
|
|
98
|
+
if key in instruction:
|
|
99
|
+
errors.append(f"forbidden construct {key!r} at instructions[{index}]")
|
|
100
|
+
if instruction.get("opcode") == "derive" and instruction.get("operation") == "registry":
|
|
101
|
+
registry_key = instruction.get("registry_key")
|
|
102
|
+
if registry_key not in REGISTRY_KEYS:
|
|
103
|
+
errors.append(f"unknown registry key at instructions[{index}]")
|
|
104
|
+
return errors
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""ContractProgram v2 — Contract-Oriented Reproducible Evaluation.
|
|
3
|
+
|
|
4
|
+
Traverses 6 eslabones (PERFIL→VOCABULARIO→QUERYSPEC→RESULTADO→VISTA→EVIDENCIA),
|
|
5
|
+
validates DSK declarations at each crossing, emits one of 9 verdicts.
|
|
6
|
+
|
|
7
|
+
Deterministic. No LLM. Same input → same verdict.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
import hashlib, json, sys
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
SCHEMA_VERSION = "core.contract_program.v2"
|
|
14
|
+
ESLABONES = ["PERFIL", "VOCABULARIO", "QUERYSPEC", "RESULTADO", "VISTA", "EVIDENCIA"]
|
|
15
|
+
VERDICTS = [
|
|
16
|
+
"pass", "incomplete", "scale_violation", "authority_violation",
|
|
17
|
+
"loss_undeclared", "temporal_violation", "translation_missing",
|
|
18
|
+
"intent_unconfirmed", "aborted"
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def evaluate_contract_v2(contract: dict[str, Any]) -> dict[str, Any]:
|
|
23
|
+
"""Evaluate a ContractProgram v2 contract. Returns verdict + findings."""
|
|
24
|
+
findings: list[dict[str, str]] = []
|
|
25
|
+
|
|
26
|
+
# Validate structure
|
|
27
|
+
eslabones = contract.get("eslabones", [])
|
|
28
|
+
if len(eslabones) != 6:
|
|
29
|
+
findings.append({"eslabon": "ALL", "code": "incomplete", "detail": f"expected 6 eslabones, got {len(eslabones)}"})
|
|
30
|
+
return _result(contract, "incomplete", findings)
|
|
31
|
+
|
|
32
|
+
# Check order and names
|
|
33
|
+
for i, expected_name in enumerate(ESLABONES):
|
|
34
|
+
eslabon = eslabones[i] if i < len(eslabones) else {}
|
|
35
|
+
name = eslabon.get("name", "")
|
|
36
|
+
if name != expected_name:
|
|
37
|
+
findings.append({"eslabon": f"order_{i}", "code": "incomplete",
|
|
38
|
+
"detail": f"position {i} expected {expected_name}, got {name}"})
|
|
39
|
+
|
|
40
|
+
if any(f["code"] == "incomplete" for f in findings):
|
|
41
|
+
return _result(contract, "incomplete", findings)
|
|
42
|
+
|
|
43
|
+
# Validate DSK declarations at each crossing
|
|
44
|
+
for eslabon in eslabones:
|
|
45
|
+
name = eslabon.get("name", "")
|
|
46
|
+
dsk = eslabon.get("dsk_declaration", {})
|
|
47
|
+
|
|
48
|
+
# Check composition_rule
|
|
49
|
+
if not dsk.get("composition_rule"):
|
|
50
|
+
findings.append({"eslabon": name, "code": "scale_violation",
|
|
51
|
+
"detail": "missing composition_rule"})
|
|
52
|
+
|
|
53
|
+
# Check declared_loss
|
|
54
|
+
loss = dsk.get("declared_loss", {})
|
|
55
|
+
if not loss.get("properties") or not loss.get("evidence_refs"):
|
|
56
|
+
findings.append({"eslabon": name, "code": "loss_undeclared",
|
|
57
|
+
"detail": "declared_loss missing properties or evidence_refs"})
|
|
58
|
+
|
|
59
|
+
# Check authority_ceiling
|
|
60
|
+
auth = dsk.get("authority_ceiling", "")
|
|
61
|
+
if auth not in ("reference_only", "advisory_only", "domain_authoritative", "externally_validated"):
|
|
62
|
+
findings.append({"eslabon": name, "code": "authority_violation",
|
|
63
|
+
"detail": f"invalid authority_ceiling: {auth}"})
|
|
64
|
+
|
|
65
|
+
# Check temporal_invariant if present
|
|
66
|
+
ti = dsk.get("temporal_invariant")
|
|
67
|
+
if ti is not None:
|
|
68
|
+
if not ti.get("captured_at") or not ti.get("valid_until"):
|
|
69
|
+
findings.append({"eslabon": name, "code": "temporal_violation",
|
|
70
|
+
"detail": "temporal_invariant missing captured_at or valid_until"})
|
|
71
|
+
|
|
72
|
+
# Check translation_map if present
|
|
73
|
+
tm = dsk.get("translation_map")
|
|
74
|
+
if tm is not None:
|
|
75
|
+
if not tm.get("source_field") or not tm.get("target_field"):
|
|
76
|
+
findings.append({"eslabon": name, "code": "translation_missing",
|
|
77
|
+
"detail": "translation_map missing source_field or target_field"})
|
|
78
|
+
|
|
79
|
+
# Check paraphrase_intent if present
|
|
80
|
+
pi = dsk.get("paraphrase_intent")
|
|
81
|
+
if pi is not None:
|
|
82
|
+
if pi.get("confirmed_by") not in ("operator", "agent"):
|
|
83
|
+
findings.append({"eslabon": name, "code": "intent_unconfirmed",
|
|
84
|
+
"detail": f"paraphrase_intent.confirmed_by={pi.get('confirmed_by')!r} invalid"})
|
|
85
|
+
elif pi.get("confirmed_by") == "agent":
|
|
86
|
+
findings.append({"eslabon": name, "code": "intent_unconfirmed",
|
|
87
|
+
"detail": "paraphrase_intent.confirmed_by=agent (not operator)"})
|
|
88
|
+
|
|
89
|
+
# Determine verdict from findings
|
|
90
|
+
if not findings:
|
|
91
|
+
verdict = "pass"
|
|
92
|
+
else:
|
|
93
|
+
# First non-pass finding determines verdict
|
|
94
|
+
verdict = findings[0]["code"]
|
|
95
|
+
if verdict not in VERDICTS:
|
|
96
|
+
verdict = "aborted"
|
|
97
|
+
|
|
98
|
+
return _result(contract, verdict, findings)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _result(contract: dict, verdict: str, findings: list[dict]) -> dict[str, Any]:
|
|
102
|
+
payload = json.dumps(contract, sort_keys=True)
|
|
103
|
+
fp = "sha256:" + hashlib.sha256(payload.encode()).hexdigest()
|
|
104
|
+
return {
|
|
105
|
+
"schema_version": SCHEMA_VERSION,
|
|
106
|
+
"contract_id": contract.get("contract_id", ""),
|
|
107
|
+
"verdict": verdict,
|
|
108
|
+
"findings": findings,
|
|
109
|
+
"fingerprint": fp,
|
|
110
|
+
"deterministic": True,
|
|
111
|
+
"llm_used": False,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def main(argv: list[str]) -> int:
|
|
116
|
+
if len(argv) < 2:
|
|
117
|
+
print("Usage: contract_program_v2.py <contract.json>", file=sys.stderr)
|
|
118
|
+
return 1
|
|
119
|
+
contract = json.load(open(argv[1]))
|
|
120
|
+
result = evaluate_contract_v2(contract)
|
|
121
|
+
print(json.dumps(result, indent=2, sort_keys=True))
|
|
122
|
+
return 0 if result["verdict"] == "pass" else 1
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if __name__ == "__main__":
|
|
126
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Deterministic Scale Kernel v3 evaluator.
|
|
2
|
+
|
|
3
|
+
The kernel validates one typed scale crossing. It never calls a provider,
|
|
4
|
+
consults a domain or increases the authority declared by the input.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from decimal import Decimal, InvalidOperation
|
|
11
|
+
from importlib.resources import files
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from jsonschema import Draft7Validator
|
|
16
|
+
|
|
17
|
+
from core_runtime.core.rule_anchor import canonical_fingerprint
|
|
18
|
+
|
|
19
|
+
SCHEMA_VERSION = "core.dsk.v3"
|
|
20
|
+
SCHEMA_PATH = files("core_runtime").joinpath("data", "schemas", "core", "dsk.v3.json")
|
|
21
|
+
STATUSES = ("pass", "invalid", "insufficient_data", "blocked")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _error(code: str, message: str, field: str | None = None) -> dict[str, str]:
|
|
25
|
+
result = {"code": code, "message": message}
|
|
26
|
+
if field is not None:
|
|
27
|
+
result["field"] = field
|
|
28
|
+
return result
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _decimal(value: Any) -> Decimal:
|
|
32
|
+
if isinstance(value, bool):
|
|
33
|
+
raise InvalidOperation
|
|
34
|
+
return Decimal(str(value))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _decimal_text(value: Decimal) -> str:
|
|
38
|
+
if value == 0:
|
|
39
|
+
return "0"
|
|
40
|
+
return format(value.normalize(), "f")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _decimal_places(value: Decimal) -> int:
|
|
44
|
+
text = _decimal_text(value)
|
|
45
|
+
return len(text.partition(".")[2])
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _envelope(payload: dict[str, Any], status: str, errors: list[dict[str, str]], **extra: Any) -> dict[str, Any]:
|
|
49
|
+
result: dict[str, Any] = {
|
|
50
|
+
"schema": SCHEMA_VERSION,
|
|
51
|
+
"status": status,
|
|
52
|
+
"errors": errors,
|
|
53
|
+
"fingerprint": canonical_fingerprint(payload),
|
|
54
|
+
"deterministic": True,
|
|
55
|
+
"llm_used": False,
|
|
56
|
+
}
|
|
57
|
+
result.update(extra)
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _schema_errors(payload: Any) -> list[dict[str, str]]:
|
|
62
|
+
schema = json.loads(SCHEMA_PATH.read_text(encoding="utf-8"))
|
|
63
|
+
return [
|
|
64
|
+
_error("schema_validation_error", item.message, ".".join(map(str, item.absolute_path)) or "$")
|
|
65
|
+
for item in sorted(Draft7Validator(schema).iter_errors(payload), key=lambda item: list(item.absolute_path))
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def evaluate_dsk_v3(payload: dict[str, Any]) -> dict[str, Any]:
|
|
70
|
+
"""Evaluate a DSK v3 declaration and return the standard CORE envelope."""
|
|
71
|
+
|
|
72
|
+
if not isinstance(payload, dict):
|
|
73
|
+
return _envelope({}, "invalid", [_error("payload_type_invalid", "DSK input must be an object.")])
|
|
74
|
+
schema_errors = _schema_errors(payload)
|
|
75
|
+
if schema_errors:
|
|
76
|
+
return _envelope(payload, "invalid", schema_errors)
|
|
77
|
+
|
|
78
|
+
crossing = payload["crossing"]
|
|
79
|
+
source = crossing["source"]
|
|
80
|
+
target = crossing["target"]
|
|
81
|
+
conversion = crossing["conversion"]
|
|
82
|
+
authority = payload["authority"]
|
|
83
|
+
if "value" not in source:
|
|
84
|
+
return _envelope(payload, "insufficient_data", [_error("source_value_missing", "source.value is required to evaluate the crossing.", "crossing.source.value")])
|
|
85
|
+
|
|
86
|
+
errors: list[dict[str, str]] = []
|
|
87
|
+
if source["unit"] != conversion["source_unit"] or target["unit"] != conversion["target_unit"]:
|
|
88
|
+
errors.append(_error("scale_violation", "Declared conversion units do not match the crossing endpoints.", "crossing.conversion"))
|
|
89
|
+
|
|
90
|
+
ceiling = authority["ceiling"]
|
|
91
|
+
if ceiling in {"domain_authoritative", "externally_validated"} and authority["source"] != "external_validator":
|
|
92
|
+
errors.append(_error("authority_non_amplification", "A scale crossing cannot create authority without an external validator.", "authority"))
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
value = _decimal(source["value"])
|
|
96
|
+
converted = value * Decimal(conversion["numerator"]) / Decimal(conversion["denominator"])
|
|
97
|
+
except (InvalidOperation, ZeroDivisionError):
|
|
98
|
+
return _envelope(payload, "invalid", [_error("numeric_value_invalid", "Numeric values must be finite and deterministic.", "crossing.source.value")])
|
|
99
|
+
|
|
100
|
+
policies = payload.get("policies", {})
|
|
101
|
+
discrete = policies.get("discrete_multiple")
|
|
102
|
+
if discrete is not None and converted % Decimal(discrete["multiple"]) != 0:
|
|
103
|
+
errors.append(_error("discrete_multiple_violation", "Converted value is not an allowed discrete multiple.", "policies.discrete_multiple"))
|
|
104
|
+
|
|
105
|
+
resolution = policies.get("resolution")
|
|
106
|
+
if resolution is not None and _decimal_places(converted) > resolution["max_decimal_places"]:
|
|
107
|
+
errors.append(_error("resolution_violation", "Converted value exceeds the declared resolution.", "policies.resolution"))
|
|
108
|
+
|
|
109
|
+
threshold = policies.get("threshold")
|
|
110
|
+
if threshold is not None and converted < Decimal(str(threshold["minimum_value"])):
|
|
111
|
+
errors.append(_error("threshold_ineligible", "Threshold eligibility was not met; no authority is created.", "policies.threshold"))
|
|
112
|
+
|
|
113
|
+
if errors:
|
|
114
|
+
return _envelope(payload, "blocked", errors, authority_ceiling=ceiling)
|
|
115
|
+
|
|
116
|
+
return _envelope(
|
|
117
|
+
payload,
|
|
118
|
+
"pass",
|
|
119
|
+
[],
|
|
120
|
+
result={
|
|
121
|
+
"resource": target["resource"],
|
|
122
|
+
"unit": target["unit"],
|
|
123
|
+
"value": _decimal_text(converted),
|
|
124
|
+
"authority_ceiling": ceiling,
|
|
125
|
+
"composition_rule": crossing["composition_rule"],
|
|
126
|
+
"declared_loss": crossing["declared_loss"],
|
|
127
|
+
},
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def main(argv: list[str]) -> int:
|
|
132
|
+
if len(argv) != 2:
|
|
133
|
+
print("Usage: dsk_v3.py <declaration.json>", file=__import__("sys").stderr)
|
|
134
|
+
return 2
|
|
135
|
+
payload = json.loads(Path(argv[1]).read_text(encoding="utf-8"))
|
|
136
|
+
result = evaluate_dsk_v3(payload)
|
|
137
|
+
print(json.dumps(result, indent=2, ensure_ascii=False, sort_keys=True))
|
|
138
|
+
return 0 if result["status"] == "pass" else 1
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if __name__ == "__main__":
|
|
142
|
+
raise SystemExit(main(__import__("sys").argv))
|