technical-debt-engine-runtime 0.1.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.
- tde_cli/__init__.py +5 -0
- tde_cli/main.py +400 -0
- tde_runtime/__init__.py +6 -0
- tde_runtime/baseline.py +196 -0
- tde_runtime/code_size.py +49 -0
- tde_runtime/complexity.py +92 -0
- tde_runtime/configuration.py +112 -0
- tde_runtime/dependency_health.py +23 -0
- tde_runtime/docker_artifact.py +47 -0
- tde_runtime/evidence_store.py +69 -0
- tde_runtime/execution.py +213 -0
- tde_runtime/maintainability.py +11 -0
- tde_runtime/models.py +70 -0
- tde_runtime/policies/__init__.py +1 -0
- tde_runtime/policies/generation-1.json +18 -0
- tde_runtime/policy.py +202 -0
- tde_runtime/query.py +36 -0
- tde_runtime/registries.py +21 -0
- tde_runtime/release_bundle.py +34 -0
- tde_runtime/release_candidate.py +75 -0
- tde_runtime/release_certification.py +87 -0
- tde_runtime/release_publication.py +114 -0
- tde_runtime/release_qualification.py +115 -0
- tde_runtime/runtime.py +193 -0
- tde_runtime/runtime_qualification.py +63 -0
- tde_runtime/software_assurance.py +183 -0
- tde_runtime/trend.py +73 -0
- tde_runtime/trusted_delivery.py +137 -0
- technical_debt_engine_runtime-0.1.0.dist-info/METADATA +5 -0
- technical_debt_engine_runtime-0.1.0.dist-info/RECORD +33 -0
- technical_debt_engine_runtime-0.1.0.dist-info/WHEEL +5 -0
- technical_debt_engine_runtime-0.1.0.dist-info/entry_points.txt +2 -0
- technical_debt_engine_runtime-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Read-only validation for publication of a certified release bundle."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from hashlib import sha256
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Mapping
|
|
8
|
+
|
|
9
|
+
from .release_bundle import digest, verify
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
PUBLICATION_TARGETS = frozenset({"github_release", "pypi", "docker_hub"})
|
|
13
|
+
REQUIRED_CONTENT_KINDS = frozenset({
|
|
14
|
+
"wheel", "source_distribution", "oci_archive", "docker_provenance",
|
|
15
|
+
"release_manifest", "release_qualification", "release_certification", "release_evidence",
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def canonical(value: Mapping[str, Any]) -> bytes:
|
|
20
|
+
return (json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + "\n").encode("utf-8")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def validate_authorization(value: object, candidate_sha: str, release_version: str,
|
|
24
|
+
bundle_id: str, bundle_checksum: str) -> list[str]:
|
|
25
|
+
"""Validate the shape and binding of an authorization assertion, not its approval."""
|
|
26
|
+
if not isinstance(value, dict):
|
|
27
|
+
return ["authorization must be a JSON object"]
|
|
28
|
+
errors = []
|
|
29
|
+
for key in ("authorizedBy", "authorizedAt", "candidateSha", "releaseVersion", "bundleId", "bundleChecksum", "targets"):
|
|
30
|
+
if key not in value: errors.append(f"authorization is missing {key}")
|
|
31
|
+
if errors: return errors
|
|
32
|
+
if not isinstance(value["authorizedBy"], str) or not value["authorizedBy"].strip(): errors.append("authorization authorizedBy must be non-empty")
|
|
33
|
+
if not isinstance(value["authorizedAt"], str) or not value["authorizedAt"].strip(): errors.append("authorization authorizedAt must be non-empty")
|
|
34
|
+
expected = {"candidateSha": candidate_sha, "releaseVersion": release_version,
|
|
35
|
+
"bundleId": bundle_id, "bundleChecksum": bundle_checksum}
|
|
36
|
+
for key, expected_value in expected.items():
|
|
37
|
+
if value[key] != expected_value: errors.append(f"authorization {key} does not bind the selected bundle")
|
|
38
|
+
if not isinstance(value["targets"], list) or set(value["targets"]) != PUBLICATION_TARGETS or len(value["targets"]) != len(PUBLICATION_TARGETS):
|
|
39
|
+
errors.append("authorization targets must be exactly github_release, pypi, and docker_hub")
|
|
40
|
+
return errors
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def validate_authorization_record(value: object, candidate_sha: str, release_version: str,
|
|
44
|
+
bundle_id: str, bundle_checksum: str) -> list[str]:
|
|
45
|
+
"""Validate an immutable, target-specific human authorization record."""
|
|
46
|
+
if not isinstance(value, dict): return ["authorization record must be a JSON object"]
|
|
47
|
+
errors = validate_authorization(value, candidate_sha, release_version, bundle_id, bundle_checksum)
|
|
48
|
+
if value.get("schemaId") != "tde.internal-release-authorization": errors.append("authorization record has an invalid schema")
|
|
49
|
+
for key in ("authorizationId", "approvedGitTag", "approvedGitHubRelease", "approvedPyPI", "approvedDockerHub", "protectedEnvironment", "publicationWorkflow"):
|
|
50
|
+
if key not in value: errors.append(f"authorization record is missing {key}")
|
|
51
|
+
approvals = value.get("targetApprovals")
|
|
52
|
+
if not isinstance(approvals, dict):
|
|
53
|
+
errors.append("authorization record must contain targetApprovals")
|
|
54
|
+
elif set(approvals) != PUBLICATION_TARGETS or any(item is not True for item in approvals.values()):
|
|
55
|
+
errors.append("every publication target requires an explicit approved state")
|
|
56
|
+
expected = {"approvedGitTag": release_version, "approvedGitHubRelease": release_version,
|
|
57
|
+
"approvedPyPI": release_version, "approvedDockerHub": f"docker.io/pcvantol/technical-debt-engine:{release_version}"}
|
|
58
|
+
for key, expected_value in expected.items():
|
|
59
|
+
if value.get(key) != expected_value: errors.append(f"authorization record {key} does not match the approved release")
|
|
60
|
+
if value.get("protectedEnvironment") != "internal-release": errors.append("authorization record does not bind internal-release")
|
|
61
|
+
if value.get("publicationWorkflow") != ".github/workflows/internal-release-publish.yml": errors.append("authorization record does not bind the publication workflow")
|
|
62
|
+
unsigned = {key: item for key, item in value.items() if key != "authorizationId"}
|
|
63
|
+
expected_id = "authorization.sha256." + sha256(canonical(unsigned)).hexdigest()
|
|
64
|
+
if value.get("authorizationId") != expected_id: errors.append("authorization record identity does not match its immutable content")
|
|
65
|
+
return errors
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _report(value: object) -> Mapping[str, Any]:
|
|
69
|
+
if not isinstance(value, dict): raise ValueError("evidence must be a JSON object")
|
|
70
|
+
return value.get("releaseQualificationEvidence", value) if isinstance(value.get("releaseQualificationEvidence", value), dict) else value
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def verify_publication_bundle(root: str | Path, candidate_sha: str, release_version: str,
|
|
74
|
+
authorization: object) -> dict[str, Any]:
|
|
75
|
+
"""Validate existing bundle/evidence and return deterministic preflight evidence."""
|
|
76
|
+
directory = Path(root)
|
|
77
|
+
verified = verify(directory)
|
|
78
|
+
bundle = verified["bundle"]
|
|
79
|
+
errors: list[str] = []
|
|
80
|
+
if not verified["integrity"]: errors.append("bundle checksum or required bundle files are invalid")
|
|
81
|
+
if not verified["complete"]: errors.append("bundle is incomplete")
|
|
82
|
+
if bundle.get("candidateSha") != candidate_sha: errors.append("bundle candidate SHA does not match workflow input")
|
|
83
|
+
if bundle.get("bundleVersion") != release_version: errors.append("bundle version does not match workflow input")
|
|
84
|
+
contents = bundle.get("contents", [])
|
|
85
|
+
by_kind = {item.get("kind"): item for item in contents if isinstance(item, dict)} if isinstance(contents, list) else {}
|
|
86
|
+
missing = sorted(REQUIRED_CONTENT_KINDS - set(by_kind))
|
|
87
|
+
if missing: errors.append("bundle is missing required identities: " + ", ".join(missing))
|
|
88
|
+
for kind, item in by_kind.items():
|
|
89
|
+
filename, expected = item.get("filename"), item.get("digest")
|
|
90
|
+
if not isinstance(filename, str) or not isinstance(expected, str) or not (directory / filename).is_file() or digest(directory / filename) != expected:
|
|
91
|
+
errors.append(f"artifact identity is invalid: {kind}")
|
|
92
|
+
qualification = certification = {}
|
|
93
|
+
try:
|
|
94
|
+
qualification = _report(json.loads((directory / by_kind["release_qualification"]["filename"]).read_text(encoding="utf-8")))
|
|
95
|
+
certification = _report(json.loads((directory / by_kind["release_certification"]["filename"]).read_text(encoding="utf-8")))
|
|
96
|
+
if qualification.get("decision") != "RELEASE_QUALIFIED" or qualification.get("releaseDecision") != "READY": errors.append("release qualification is not READY")
|
|
97
|
+
qualified_sha = qualification.get("releaseCandidate", {}).get("sha")
|
|
98
|
+
certified_sha = certification.get("candidate", {}).get("sha")
|
|
99
|
+
if qualified_sha != candidate_sha or certified_sha != candidate_sha: errors.append("qualification or certification candidate identity does not match")
|
|
100
|
+
if certification.get("decision") != "RELEASE_CERTIFIED": errors.append("release certification is not certified")
|
|
101
|
+
except (OSError, KeyError, TypeError, ValueError, json.JSONDecodeError):
|
|
102
|
+
errors.append("release qualification or certification evidence is unreadable")
|
|
103
|
+
errors.extend(validate_authorization(authorization, candidate_sha, release_version,
|
|
104
|
+
str(bundle.get("bundleId", "")), str(bundle.get("bundleChecksum", ""))))
|
|
105
|
+
evidence = {"schemaId": "tde.internal-release-publication-preflight", "schemaVersion": "1.0.0",
|
|
106
|
+
"candidateSha": candidate_sha, "releaseVersion": release_version,
|
|
107
|
+
"bundleId": bundle.get("bundleId"), "bundleChecksum": bundle.get("bundleChecksum"),
|
|
108
|
+
"bundleIntegrity": verified["integrity"], "bundleComplete": verified["complete"],
|
|
109
|
+
"publicationTargets": sorted(PUBLICATION_TARGETS), "dryRun": True,
|
|
110
|
+
"authorizationStructureValid": not validate_authorization(authorization, candidate_sha, release_version, str(bundle.get("bundleId", "")), str(bundle.get("bundleChecksum", ""))),
|
|
111
|
+
"decision": "PUBLICATION_PREFLIGHT_READY" if not errors else "PUBLICATION_PREFLIGHT_BLOCKED", "errors": errors}
|
|
112
|
+
unsigned = dict(evidence)
|
|
113
|
+
evidence["publicationEvidenceId"] = "publication-preflight.sha256." + sha256(canonical(unsigned)).hexdigest()
|
|
114
|
+
return evidence
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Evidence-only release qualification with explicit capability selection."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from hashlib import sha256
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import subprocess
|
|
9
|
+
from typing import Any, Mapping, Sequence
|
|
10
|
+
|
|
11
|
+
from .runtime import EVIDENCE_SCHEMA_VERSION, RUNTIME_VERSION
|
|
12
|
+
from .software_assurance import SoftwareAssurance
|
|
13
|
+
from .trusted_delivery import TrustedDelivery
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _git(root: Path, *args: str) -> str:
|
|
17
|
+
return subprocess.run(["git", "-C", str(root), *args], text=True, capture_output=True, check=False).stdout.strip()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _candidate_branch(root: Path) -> str:
|
|
21
|
+
"""Preserve the validated mainline branch when an exact SHA is detached."""
|
|
22
|
+
return _git(root, "branch", "--show-current") or os.environ.get("TDE_CANDIDATE_SOURCE_BRANCH", "")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _canonical(value: Mapping[str, Any]) -> bytes:
|
|
26
|
+
return (json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + "\n").encode()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _write(path: Path, value: Mapping[str, Any]) -> str:
|
|
30
|
+
raw = _canonical(value)
|
|
31
|
+
if path.exists() and path.read_bytes() != raw:
|
|
32
|
+
raise ValueError(f"refusing to overwrite immutable release evidence: {path}")
|
|
33
|
+
path.write_bytes(raw)
|
|
34
|
+
return "sha256:" + sha256(raw).hexdigest()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ReleaseQualification:
|
|
38
|
+
"""Compose already-generated evidence into a qualified, immutable release record."""
|
|
39
|
+
|
|
40
|
+
def qualify(self, root: str | Path, runtime_evidence: Mapping[str, Any], artifact_directories: Sequence[str | Path],
|
|
41
|
+
manifest_output: str | Path, selected_capabilities: Sequence[str] = (), docker_artifact_directory: str | Path | None = None) -> dict[str, Any]:
|
|
42
|
+
root = Path(root).resolve()
|
|
43
|
+
output = Path(manifest_output).resolve()
|
|
44
|
+
output.parent.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
selection = sorted(set(selected_capabilities))
|
|
46
|
+
candidate = {
|
|
47
|
+
"sha": _git(root, "rev-parse", "HEAD"),
|
|
48
|
+
"repository": _git(root, "config", "--get", "remote.origin.url") or "local",
|
|
49
|
+
"branch": _candidate_branch(root),
|
|
50
|
+
"runtimeVersion": RUNTIME_VERSION,
|
|
51
|
+
"schemaVersion": EVIDENCE_SCHEMA_VERSION,
|
|
52
|
+
"selectedCapabilities": selection,
|
|
53
|
+
"policyVersion": runtime_evidence.get("policyEvidence", {}).get("policy", {}).get("version"),
|
|
54
|
+
}
|
|
55
|
+
assurance = SoftwareAssurance().assure(root, list(artifact_directories), docker_artifact_directory)
|
|
56
|
+
artifacts = [item for record in assurance["artifacts"]["records"] for item in record["artifacts"]]
|
|
57
|
+
delivery_manifest = {"schemaId": "tde.trusted-delivery-manifest", "schemaVersion": "1.0.0",
|
|
58
|
+
"candidate": {key: candidate[key] for key in ("sha", "repository", "branch")}, "artifacts": artifacts}
|
|
59
|
+
delivery_path = output.with_name(output.stem + ".trusted-delivery.json")
|
|
60
|
+
_write(delivery_path, delivery_manifest)
|
|
61
|
+
delivery = TrustedDelivery().validate(root, runtime_evidence, assurance, delivery_path)
|
|
62
|
+
qualification = runtime_evidence.get("runtimeQualification", {})
|
|
63
|
+
policy = runtime_evidence.get("policyEvidence", {})
|
|
64
|
+
executed = set(runtime_evidence.get("executionEvidence", {}).get("executedCapabilities", []))
|
|
65
|
+
required_executed = bool(selection) and set(selection).issubset(executed)
|
|
66
|
+
checks = {
|
|
67
|
+
"candidateIdentity": bool(candidate["sha"]),
|
|
68
|
+
"capabilitySelection": bool(selection),
|
|
69
|
+
"artifactIntegrity": assurance["checks"]["artifactIntegrity"],
|
|
70
|
+
"buildReproducibility": assurance["checks"]["buildProvenanceVerification"],
|
|
71
|
+
"softwareAssurance": assurance["decision"] == "PASS",
|
|
72
|
+
"trustedDelivery": delivery["decision"] == "PASS",
|
|
73
|
+
"runtimeEvidence": runtime_evidence.get("validation", {}).get("status") == "VALID",
|
|
74
|
+
"requiredCapabilitiesExecuted": required_executed,
|
|
75
|
+
"runtimeQualification": qualification.get("level") == "QUALIFIED",
|
|
76
|
+
"policyEvidence": policy.get("decision") in {"PASS", "PASS_WITH_WARNINGS"},
|
|
77
|
+
"dockerArtifact": docker_artifact_directory is None or bool(assurance["artifacts"].get("docker", {}).get("verified")),
|
|
78
|
+
}
|
|
79
|
+
ready = all(checks.values())
|
|
80
|
+
decision = "RELEASE_QUALIFIED" if ready else "RELEASE_BLOCKED"
|
|
81
|
+
runtime_record = {
|
|
82
|
+
"selection": selection,
|
|
83
|
+
"evidenceId": runtime_evidence.get("integrity", {}).get("contentDigest"),
|
|
84
|
+
"validation": runtime_evidence.get("validation", {}),
|
|
85
|
+
"execution": runtime_evidence.get("executionEvidence", {}),
|
|
86
|
+
"qualification": qualification,
|
|
87
|
+
}
|
|
88
|
+
policy_record = {"decision": policy.get("decision"), "policy": policy.get("policy"),
|
|
89
|
+
"triggeredRules": policy.get("triggeredRules", []), "evidenceId": runtime_record["evidenceId"]}
|
|
90
|
+
release_evidence = {
|
|
91
|
+
"schemaId": "tde.release-evidence", "schemaVersion": "1.0.0", "candidate": candidate,
|
|
92
|
+
"artifacts": artifacts, "runtimeQualification": runtime_record, "policyEvidence": policy_record,
|
|
93
|
+
"softwareAssurance": {"assuranceId": assurance["assuranceId"], "decision": assurance["decision"]},
|
|
94
|
+
"trustedDelivery": {"trustedDeliveryId": delivery["trustedDeliveryId"], "decision": delivery["decision"]},
|
|
95
|
+
"releaseQualification": {"decision": decision, "releaseDecision": "READY" if ready else "NOT_READY", "checks": checks},
|
|
96
|
+
}
|
|
97
|
+
release_evidence["releaseEvidenceId"] = "release-evidence.sha256." + sha256(_canonical(release_evidence)).hexdigest()
|
|
98
|
+
evidence_path = output.with_name(output.stem + ".release-evidence.json")
|
|
99
|
+
evidence_digest = _write(evidence_path, release_evidence)
|
|
100
|
+
manifest = {
|
|
101
|
+
"schemaId": "tde.release-qualification-manifest", "schemaVersion": "1.0.0", "candidate": candidate,
|
|
102
|
+
"artifacts": artifacts, "softwareAssuranceId": assurance["assuranceId"], "trustedDeliveryId": delivery["trustedDeliveryId"],
|
|
103
|
+
"releaseEvidence": {"path": str(evidence_path), "digest": evidence_digest, "id": release_evidence["releaseEvidenceId"], "integrity": True},
|
|
104
|
+
"qualification": {"decision": decision, "checks": checks},
|
|
105
|
+
}
|
|
106
|
+
digest = _write(output, manifest)
|
|
107
|
+
return {
|
|
108
|
+
"schemaId": "tde.release-qualification", "schemaVersion": "1.0.0", "releaseCandidate": candidate,
|
|
109
|
+
"manifest": {"path": str(output), "digest": digest, "integrity": True}, "artifacts": artifacts,
|
|
110
|
+
"softwareAssurance": release_evidence["softwareAssurance"], "trustedDelivery": release_evidence["trustedDelivery"],
|
|
111
|
+
"runtimeEvidence": {"validation": runtime_record["validation"], "policyDecision": policy_record["decision"],
|
|
112
|
+
"runtimeQualification": qualification.get("level"), "identity": runtime_record["evidenceId"]},
|
|
113
|
+
"releaseEvidence": manifest["releaseEvidence"], "checks": checks,
|
|
114
|
+
"releaseDecision": "READY" if ready else "NOT_READY", "decision": decision,
|
|
115
|
+
}
|
tde_runtime/runtime.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Generic orchestration pipeline for the Technical Debt Engine runtime foundation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from hashlib import sha256
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import subprocess
|
|
9
|
+
from tempfile import TemporaryDirectory
|
|
10
|
+
from uuid import uuid4
|
|
11
|
+
from typing import Any, Callable
|
|
12
|
+
|
|
13
|
+
from .configuration import RuntimeConfiguration
|
|
14
|
+
from .models import RuntimeContext, RuntimeQualification, RuntimeResult, StageResult, StageStatus, utc_now
|
|
15
|
+
from .registries import AdapterRegistry, CapabilityRegistry, PolicyRegistry
|
|
16
|
+
from .policy import PolicyEngine, PolicyError
|
|
17
|
+
from .query import QueryEngine
|
|
18
|
+
from .execution import CapabilityExecutionEngine
|
|
19
|
+
from .runtime_qualification import RuntimeQualificationEngine
|
|
20
|
+
|
|
21
|
+
RUNTIME_VERSION = "0.1.0"
|
|
22
|
+
EVIDENCE_SCHEMA_VERSION = "1.0.0"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Runtime:
|
|
26
|
+
"""Stable public API. A future CLI is a consumer of this class, not its owner."""
|
|
27
|
+
|
|
28
|
+
_stage_names = (
|
|
29
|
+
"repository-discovery", "repository-inspection", "language-detection",
|
|
30
|
+
"capability-planning", "adapter-planning", "execution-planning",
|
|
31
|
+
"execution-context", "pipeline-execution", "normalization", "validation",
|
|
32
|
+
"policy-evaluation", "qualification", "evidence", "reporting",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def __init__(self, capability_registry: CapabilityRegistry | None = None,
|
|
36
|
+
adapter_registry: AdapterRegistry | None = None,
|
|
37
|
+
policy_engine: PolicyEngine | None = None) -> None:
|
|
38
|
+
self._capability_registry = capability_registry or CapabilityRegistry()
|
|
39
|
+
self._adapter_registry = adapter_registry or AdapterRegistry()
|
|
40
|
+
self._policy_engine = policy_engine or PolicyEngine()
|
|
41
|
+
self._execution_engine = CapabilityExecutionEngine(self._capability_registry, self._adapter_registry)
|
|
42
|
+
|
|
43
|
+
def execute(self, repository_root: str | Path,
|
|
44
|
+
configuration: RuntimeConfiguration | dict[str, Any] | None = None) -> RuntimeResult:
|
|
45
|
+
config = configuration if isinstance(configuration, RuntimeConfiguration) else RuntimeConfiguration.load(configuration)
|
|
46
|
+
root = Path(repository_root).resolve()
|
|
47
|
+
if not root.is_dir():
|
|
48
|
+
raise ValueError("repository root must be an existing directory")
|
|
49
|
+
with TemporaryDirectory(prefix="tde-runtime-") as temporary:
|
|
50
|
+
context = self._context(root, config, Path(temporary))
|
|
51
|
+
stages: list[StageResult] = []
|
|
52
|
+
values: dict[str, Any] = {"capabilities": (), "adapters": ()}
|
|
53
|
+
for identifier in self._stage_names:
|
|
54
|
+
result = self._run_stage(identifier, context, values)
|
|
55
|
+
stages.append(result)
|
|
56
|
+
values[identifier] = result.outputs
|
|
57
|
+
validation = values["validation"]
|
|
58
|
+
evidence = values["evidence"]
|
|
59
|
+
report = values["reporting"]
|
|
60
|
+
qualification = RuntimeQualification.READY if values["qualification"]["status"] != "BLOCKED" else RuntimeQualification.FAILED
|
|
61
|
+
return RuntimeResult(context, tuple(stages), evidence, validation, qualification, report)
|
|
62
|
+
|
|
63
|
+
def query(self, evidence: dict[str, Any], query: dict[str, Any]) -> dict[str, Any]:
|
|
64
|
+
"""Public read-only canonical-evidence query entrypoint."""
|
|
65
|
+
return QueryEngine().execute(evidence, query)
|
|
66
|
+
|
|
67
|
+
def _context(self, root: Path, config: RuntimeConfiguration, temporary: Path) -> RuntimeContext:
|
|
68
|
+
root_digest = self._repository_digest(root)
|
|
69
|
+
return RuntimeContext(
|
|
70
|
+
# Repository identity identifies the checkout; candidate identity identifies
|
|
71
|
+
# its source content. Conflating the two makes every source revision an
|
|
72
|
+
# incompatible baseline and prevents repository-evolution comparisons.
|
|
73
|
+
repository_root=root, repository_id=self._repository_identity(root),
|
|
74
|
+
candidate={"id": f"candidate.content.{root_digest}", "identityType": "content_digest",
|
|
75
|
+
"value": f"sha256:{root_digest}", "validationStatus": "VALID"},
|
|
76
|
+
configuration=config.as_dict(), runtime_version=RUNTIME_VERSION,
|
|
77
|
+
schema_version=EVIDENCE_SCHEMA_VERSION, execution_id=f"execution.{uuid4().hex}",
|
|
78
|
+
working_directory=root, temporary_directory=temporary,
|
|
79
|
+
execution_options=config.execution_options or {},
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _repository_digest(root: Path) -> str:
|
|
84
|
+
"""Identify source content independently of an absolute checkout path."""
|
|
85
|
+
digest = sha256()
|
|
86
|
+
excluded = {".git", ".tde", "__pycache__", ".venv", "venv", "build", "dist"}
|
|
87
|
+
for path in sorted(item for item in root.rglob("*") if item.is_file() and not any(part in excluded for part in item.relative_to(root).parts)):
|
|
88
|
+
relative = path.relative_to(root).as_posix()
|
|
89
|
+
digest.update(relative.encode("utf-8"))
|
|
90
|
+
digest.update(b"\0")
|
|
91
|
+
# Git may materialize text files with CRLF on Windows. Candidate
|
|
92
|
+
# identity represents source content, not checkout line endings.
|
|
93
|
+
digest.update(path.read_bytes().replace(b"\r\n", b"\n"))
|
|
94
|
+
digest.update(b"\0")
|
|
95
|
+
return digest.hexdigest()[:16]
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def _repository_identity(root: Path) -> str:
|
|
99
|
+
"""Use the Git origin when available; absolute paths are only a local fallback."""
|
|
100
|
+
try:
|
|
101
|
+
origin = subprocess.run(
|
|
102
|
+
["git", "-C", str(root), "config", "--get", "remote.origin.url"],
|
|
103
|
+
capture_output=True, text=True, timeout=2, check=False,
|
|
104
|
+
).stdout.strip().removesuffix(".git").lower()
|
|
105
|
+
except (OSError, subprocess.SubprocessError):
|
|
106
|
+
origin = ""
|
|
107
|
+
value = origin or str(root)
|
|
108
|
+
kind = "git" if origin else "local"
|
|
109
|
+
return f"repository.{kind}.{sha256(value.encode()).hexdigest()[:16]}"
|
|
110
|
+
|
|
111
|
+
def _run_stage(self, identifier: str, context: RuntimeContext,
|
|
112
|
+
values: dict[str, Any]) -> StageResult:
|
|
113
|
+
started = utc_now()
|
|
114
|
+
handlers: dict[str, Callable[[], dict[str, Any]]] = {
|
|
115
|
+
"repository-discovery": lambda: {"repositoryId": context.repository_id},
|
|
116
|
+
"repository-inspection": lambda: {"rootExists": context.repository_root.is_dir()},
|
|
117
|
+
"language-detection": lambda: {"languages": []},
|
|
118
|
+
"capability-planning": lambda: {"capabilities": list(self._capability_registry.discover())},
|
|
119
|
+
"adapter-planning": lambda: {"adapters": list(self._adapter_registry.discover())},
|
|
120
|
+
"execution-planning": lambda: self._execution_engine.plan(context),
|
|
121
|
+
"execution-context": lambda: {"executionId": context.execution_id},
|
|
122
|
+
"pipeline-execution": lambda: self._execution_engine.execute(context),
|
|
123
|
+
"normalization": lambda: values.get("pipeline-execution", {"measurements": [], "findings": []}),
|
|
124
|
+
"validation": lambda: self._validation(context, values.get("pipeline-execution", {})),
|
|
125
|
+
"policy-evaluation": lambda: self._policy(context, values.get("normalization", {})),
|
|
126
|
+
"qualification": lambda: self._qualification(values.get("policy-evaluation", {})),
|
|
127
|
+
"evidence": lambda: self._evidence(context, values.get("validation", self._validation(context, values.get("pipeline-execution", {}))), values.get("normalization", {}), values.get("policy-evaluation", {})),
|
|
128
|
+
"reporting": lambda: self._report(context, values),
|
|
129
|
+
}
|
|
130
|
+
outputs = handlers[identifier]()
|
|
131
|
+
return StageResult(identifier, {"executionId": context.execution_id}, outputs,
|
|
132
|
+
StageStatus.SUCCESS, "completed", started, utc_now())
|
|
133
|
+
|
|
134
|
+
def _policy(self, context: RuntimeContext, normalized: dict[str, Any]) -> dict[str, Any]:
|
|
135
|
+
try:
|
|
136
|
+
policy = self._policy_engine.load(context.configuration, context.repository_root,
|
|
137
|
+
context.runtime_version, context.schema_version)
|
|
138
|
+
return self._policy_engine.evaluate(policy, normalized, context.configuration)
|
|
139
|
+
except PolicyError as error:
|
|
140
|
+
return {"policy": None, "decision": "BLOCKED", "decisionReason": str(error),
|
|
141
|
+
"triggeredRules": [{"ruleId": "policy.validation", "outcome": "BLOCKED", "reason": str(error)}],
|
|
142
|
+
"thresholds": {}, "affectedCapabilities": [],
|
|
143
|
+
"qualificationReference": {"executionId": context.execution_id}, "qualificationInputs": {}}
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def _qualification(policy_evidence: dict[str, Any]) -> dict[str, Any]:
|
|
147
|
+
"""Qualification is deliberately a projection of Policy Engine output only."""
|
|
148
|
+
decision = policy_evidence.get("decision", "BLOCKED")
|
|
149
|
+
return {"status": decision, "policyDecision": decision, "policy": policy_evidence.get("policy"),
|
|
150
|
+
"triggeredRules": policy_evidence.get("triggeredRules", [])}
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def _validation(context: RuntimeContext, execution: dict[str, Any]) -> dict[str, Any]:
|
|
154
|
+
execution_evidence = execution.get("executionEvidence", {})
|
|
155
|
+
executed = execution_evidence.get("executedCapabilities", [])
|
|
156
|
+
complete = bool(executed) and not execution_evidence.get("blockedCapabilities")
|
|
157
|
+
return {"status": "VALID", "schema": "VALID", "candidateIdentity": "VALID",
|
|
158
|
+
"repositoryIdentity": "VALID", "adapter": "VALID" if complete else "NOT_APPLICABLE", "analyzer": "VALID" if complete else "NOT_APPLICABLE",
|
|
159
|
+
"completeness": "COMPLETE" if executed else "INCOMPLETE", "integrity": "VALID",
|
|
160
|
+
"warnings": [] if executed else ["no capability was executed"], "errors": []}
|
|
161
|
+
|
|
162
|
+
@staticmethod
|
|
163
|
+
def _evidence(context: RuntimeContext, validation: dict[str, Any], normalized: dict[str, Any], policy_evidence: dict[str, Any]) -> dict[str, Any]:
|
|
164
|
+
generated_at = utc_now()
|
|
165
|
+
stable_results = [{key: value for key, value in result.items() if key != "executionTiming"}
|
|
166
|
+
for result in normalized.get("capabilityResults", [])]
|
|
167
|
+
seed = json.dumps({"repository": context.repository_id, "candidate": context.candidate,
|
|
168
|
+
"configuration": RuntimeConfiguration.load(context.configuration).digest(),
|
|
169
|
+
"capabilityResults": stable_results,
|
|
170
|
+
"measurements": normalized.get("measurements", []), "findings": normalized.get("findings", []),
|
|
171
|
+
"policy": policy_evidence}, sort_keys=True, separators=(",", ":"), default=str)
|
|
172
|
+
return {"schemaId": "tde.evidence", "schemaVersion": context.schema_version,
|
|
173
|
+
"runtime": {"id": "tde", "version": context.runtime_version},
|
|
174
|
+
"executionId": context.execution_id,
|
|
175
|
+
"repository": {"id": context.repository_id, "displayName": "local-repository"},
|
|
176
|
+
"candidate": context.candidate,
|
|
177
|
+
"configurationDigest": RuntimeConfiguration.load(context.configuration).digest(),
|
|
178
|
+
"capabilityResults": normalized.get("capabilityResults", []), "adapterResults": normalized.get("adapterResults", []), "measurements": normalized.get("measurements", []), "findings": normalized.get("findings", []), "executionEvidence": normalized.get("executionEvidence", {}), "validation": validation, "policyEvidence": policy_evidence, "runtimeQualification": RuntimeQualificationEngine().qualify({"validation":validation,"capabilityResults":normalized.get("capabilityResults",[]),"executionEvidence":normalized.get("executionEvidence",{}),"executionId":context.execution_id,"policyEvidence":policy_evidence,"integrity":{"contentDigest":seed}}),
|
|
179
|
+
"timestamps": {"executedAt": generated_at, "generatedAt": generated_at},
|
|
180
|
+
"integrity": {"algorithm": "sha-256", "contentDigest": f"sha256:{sha256(seed.encode()).hexdigest()}"}}
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def _report(context: RuntimeContext, values: dict[str, Any]) -> dict[str, Any]:
|
|
184
|
+
execution = values.get("pipeline-execution", {}).get("executionEvidence", {})
|
|
185
|
+
return {"runtimeSummary": {"status": "RUNTIME_READY", "runtimeVersion": context.runtime_version},
|
|
186
|
+
"executionSummary": {"executionId": context.execution_id, "workItems": len(execution.get("workItems", [])),
|
|
187
|
+
"plannedCapabilities": execution.get("plannedCapabilities", []),
|
|
188
|
+
"executedCapabilities": execution.get("executedCapabilities", []),
|
|
189
|
+
"plannedAdapters": execution.get("plannedAdapters", []),
|
|
190
|
+
"executedAdapters": execution.get("executedAdapters", [])},
|
|
191
|
+
"qualification": values.get("qualification", {}),
|
|
192
|
+
"environment": {"schemaVersion": context.schema_version, "capabilities": len(execution.get("plannedCapabilities", [])), "adapters": len(execution.get("plannedAdapters", [])),
|
|
193
|
+
"policies": len(PolicyRegistry().discover())}}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Evidence-only, fail-closed assessment of Runtime analysis trustworthiness."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from hashlib import sha256
|
|
6
|
+
from typing import Any, Mapping
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RuntimeQualificationEngine:
|
|
10
|
+
levels = ("QUALIFIED", "PARTIALLY_QUALIFIED", "BLOCKED", "NOT_SUPPORTED")
|
|
11
|
+
|
|
12
|
+
def qualify(self, evidence: Mapping[str, Any], capability: str | None = None) -> dict[str, Any]:
|
|
13
|
+
state = self._execution_state(evidence)
|
|
14
|
+
selected = [item for item in evidence.get("capabilityResults", []) if capability is None or item.get("capabilityId") == capability]
|
|
15
|
+
level = self._level(state, selected, capability)
|
|
16
|
+
if capability and not selected:
|
|
17
|
+
state["missingCapabilities"].append(capability)
|
|
18
|
+
state["limitations"].append("requested capability evidence is absent")
|
|
19
|
+
confidence = {"BLOCKED": 0.0, "NOT_SUPPORTED": 0.0, "PARTIALLY_QUALIFIED": 0.75}.get(level, 1.0)
|
|
20
|
+
identity = sha256(f"{evidence.get('integrity', {}).get('contentDigest')}:{capability}".encode()).hexdigest()[:16]
|
|
21
|
+
return {"qualificationId": f"runtime-qualification.{identity}", "level": level,
|
|
22
|
+
"confidence": {"analysis": confidence, "repository": confidence, "capability": confidence},
|
|
23
|
+
"limitations": state["limitations"], "missingCapabilities": sorted(set(state["missingCapabilities"])),
|
|
24
|
+
"missingAdapters": sorted(set(state["missingAdapters"])), "unsupportedLanguages": [],
|
|
25
|
+
"supportingEvidence": {"executionId": evidence.get("executionId"), "policy": evidence.get("policyEvidence", {}),
|
|
26
|
+
"capabilityCount": len(selected), "executedCapabilityCount": len(state["executed"]),
|
|
27
|
+
"executedAdapterCount": len(state["executedAdapters"]), "workItemCount": len(state["workItems"])}}
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def _execution_state(evidence: Mapping[str, Any]) -> dict[str, Any]:
|
|
31
|
+
limitations: list[str] = []
|
|
32
|
+
missing_capabilities: list[str] = []
|
|
33
|
+
execution = evidence.get("executionEvidence")
|
|
34
|
+
results = evidence.get("capabilityResults", [])
|
|
35
|
+
if evidence.get("validation", {}).get("status") != "VALID":
|
|
36
|
+
limitations.append("evidence validation failed")
|
|
37
|
+
if not isinstance(execution, Mapping):
|
|
38
|
+
limitations.append("execution evidence is missing")
|
|
39
|
+
execution = {}
|
|
40
|
+
planned = list(execution.get("plannedCapabilities", []))
|
|
41
|
+
executed = list(execution.get("executedCapabilities", []))
|
|
42
|
+
work_items = list(execution.get("workItems", []))
|
|
43
|
+
if not work_items:
|
|
44
|
+
limitations.append("execution work-item list is empty")
|
|
45
|
+
if not executed:
|
|
46
|
+
limitations.append("zero capabilities executed")
|
|
47
|
+
missing_capabilities.extend(identifier for identifier in planned if identifier not in executed or not any(item.get("capabilityId") == identifier for item in results))
|
|
48
|
+
planned_adapters = list(execution.get("plannedAdapters", []))
|
|
49
|
+
executed_adapters = list(execution.get("executedAdapters", []))
|
|
50
|
+
missing_adapters = [identifier for identifier in planned_adapters if identifier not in executed_adapters]
|
|
51
|
+
if missing_adapters:
|
|
52
|
+
limitations.append("required adapter evidence is missing")
|
|
53
|
+
return {"limitations": limitations, "missingCapabilities": missing_capabilities, "missingAdapters": missing_adapters,
|
|
54
|
+
"executed": executed, "executedAdapters": executed_adapters, "workItems": work_items}
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def _level(state: Mapping[str, Any], selected: list[Mapping[str, Any]], capability: str | None) -> str:
|
|
58
|
+
if capability and not selected:
|
|
59
|
+
return "BLOCKED"
|
|
60
|
+
blocked = any(item.get("status") in {"BLOCKED", "FAILED"} for item in selected)
|
|
61
|
+
if state["limitations"] or state["missingCapabilities"] or blocked:
|
|
62
|
+
return "BLOCKED"
|
|
63
|
+
return "PARTIALLY_QUALIFIED" if any(item.get("completeness", 1) < 1 or item.get("status") == "PARTIAL" for item in selected) else "QUALIFIED"
|