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_runtime/models.py ADDED
@@ -0,0 +1,70 @@
1
+ """Runtime-owned value objects. No capability or adapter behavior belongs here."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import asdict, dataclass, field
6
+ from datetime import datetime, timezone
7
+ from enum import StrEnum
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+
12
+ class StageStatus(StrEnum):
13
+ SUCCESS = "SUCCESS"
14
+ FAILED = "FAILED"
15
+ BLOCKED = "BLOCKED"
16
+
17
+
18
+ class RuntimeQualification(StrEnum):
19
+ READY = "RUNTIME_READY"
20
+ FAILED = "RUNTIME_FAILED"
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class RuntimeContext:
25
+ repository_root: Path
26
+ repository_id: str
27
+ candidate: dict[str, str]
28
+ configuration: dict[str, Any]
29
+ runtime_version: str
30
+ schema_version: str
31
+ execution_id: str
32
+ working_directory: Path
33
+ temporary_directory: Path
34
+ execution_options: dict[str, Any] = field(default_factory=dict)
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class StageResult:
39
+ identifier: str
40
+ inputs: dict[str, Any]
41
+ outputs: dict[str, Any]
42
+ status: StageStatus
43
+ result: str
44
+ started_at: str
45
+ finished_at: str
46
+
47
+ @property
48
+ def duration_ms(self) -> int:
49
+ start = datetime.fromisoformat(self.started_at)
50
+ finish = datetime.fromisoformat(self.finished_at)
51
+ return int((finish - start).total_seconds() * 1000)
52
+
53
+
54
+ @dataclass(frozen=True)
55
+ class RuntimeResult:
56
+ context: RuntimeContext
57
+ stages: tuple[StageResult, ...]
58
+ evidence: dict[str, Any]
59
+ validation: dict[str, Any]
60
+ qualification: RuntimeQualification
61
+ report: dict[str, Any]
62
+
63
+
64
+ def utc_now() -> str:
65
+ return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
66
+
67
+
68
+ def serialise_context(context: RuntimeContext) -> dict[str, Any]:
69
+ data = asdict(context)
70
+ return {key: str(value) if isinstance(value, Path) else value for key, value in data.items()}
@@ -0,0 +1 @@
1
+ """Bundled, versioned Technical Debt Engine policies."""
@@ -0,0 +1,18 @@
1
+ {
2
+ "identifier": "tde.generation-1.default",
3
+ "version": "1.0.0",
4
+ "scope": "repository",
5
+ "owner": "TDE policy governance",
6
+ "description": "Default, language-independent Generation 1 qualification policy.",
7
+ "supportedCapabilities": ["code_size", "complexity", "maintainability", "dependency_health"],
8
+ "supportedSchemas": ["1.0.0"],
9
+ "supportedRuntimeVersions": ["0.1.0"],
10
+ "rules": [
11
+ {"id": "code_size.repository_lines", "type": "threshold", "metricKey": "code_size.code_lines", "direction": "greater_than", "warning": 10000, "blocking": 25000},
12
+ {"id": "complexity.maximum", "type": "threshold", "metricKey": "complexity.cyclomatic.maximum", "direction": "greater_than", "warning": 15, "blocking": 25},
13
+ {"id": "maintainability.minimum", "type": "threshold", "metricKey": "maintainability.index", "direction": "less_than", "warning": 50, "blocking": 30},
14
+ {"id": "dependency.count", "type": "threshold", "metricKey": "dependency.count", "direction": "greater_than", "warning": 100, "blocking": 250},
15
+ {"id": "critical.finding", "type": "finding_severity", "severity": "CRITICAL", "outcome": "FAIL"},
16
+ {"id": "comparison.regression", "type": "comparison_regression", "outcome": "FAIL"}
17
+ ]
18
+ }
tde_runtime/policy.py ADDED
@@ -0,0 +1,202 @@
1
+ """Configuration-driven, versioned policy evaluation for qualification."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from copy import deepcopy
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Any, Iterable, Mapping
9
+
10
+
11
+ POLICY_SCHEMA_VERSION = "1.0.0"
12
+ POLICY_DECISIONS = ("PASS", "PASS_WITH_WARNINGS", "FAIL", "BLOCKED", "NOT_APPLICABLE")
13
+
14
+
15
+ class PolicyError(ValueError):
16
+ """Raised when a policy cannot be loaded or is incompatible."""
17
+
18
+
19
+ class PolicyEngine:
20
+ """Loads and evaluates policies without knowledge of Runtime stages or adapters."""
21
+
22
+ def __init__(self, policy_directories: Iterable[Path] | None = None) -> None:
23
+ bundled = Path(__file__).with_name("policies")
24
+ self._policy_directories = tuple(policy_directories) if policy_directories is not None else (bundled,)
25
+
26
+ def discover(self, additional_directories: Iterable[Path] = ()) -> tuple[dict[str, str], ...]:
27
+ entries: list[dict[str, str]] = []
28
+ for path in self._policy_files((*self._policy_directories, *additional_directories)):
29
+ policy = self._read(path)
30
+ self.validate(policy)
31
+ entries.append({"id": policy["identifier"], "version": policy["version"], "path": str(path)})
32
+ return tuple(entries)
33
+
34
+ def load(self, configuration: Mapping[str, Any], repository_root: Path,
35
+ runtime_version: str, schema_version: str) -> dict[str, Any]:
36
+ settings = dict(configuration.get("executionOptions", {}).get("policy", configuration.get("policy", {})))
37
+ directories = list(self._policy_directories)
38
+ # Later directories have higher precedence: bundled < workspace < repository.
39
+ for setting in (settings.get("workspace"), settings.get("repository")):
40
+ if setting:
41
+ candidate = Path(setting)
42
+ directories.append(candidate if candidate.is_absolute() else repository_root / candidate)
43
+ policies = [self._read(path) for path in self._policy_files(directories)]
44
+ for candidate in policies:
45
+ self.validate(candidate)
46
+ if not policies:
47
+ raise PolicyError("no policy is available")
48
+ policy_id = settings.get("id")
49
+ candidates = [item for item in policies if not policy_id or item["identifier"] == policy_id]
50
+ if not candidates:
51
+ raise PolicyError(f"requested policy is not available: {policy_id}")
52
+ policy = candidates[-1]
53
+ self.validate(policy)
54
+ self._validate_compatibility(policy, runtime_version, schema_version)
55
+ resolved = deepcopy(policy)
56
+ overrides = settings.get("overrides", {})
57
+ if not isinstance(overrides, dict):
58
+ raise PolicyError("policy.overrides must be an object")
59
+ for rule in resolved["rules"]:
60
+ configured = overrides.get(rule["id"], {})
61
+ if not isinstance(configured, dict):
62
+ raise PolicyError(f"override for {rule['id']} must be an object")
63
+ rule.update({key: value for key, value in configured.items() if key in {"enabled", "warning", "blocking", "outcome"}})
64
+ return resolved
65
+
66
+ def evaluate(self, policy: Mapping[str, Any], normalized: Mapping[str, Any],
67
+ configuration: Mapping[str, Any]) -> dict[str, Any]:
68
+ measurements = list(normalized.get("measurements", []))
69
+ findings = list(normalized.get("findings", []))
70
+ results = list(normalized.get("capabilityResults", []))
71
+ limitations = [limitation for result in results for limitation in result.get("limitations", [])]
72
+ triggered: list[dict[str, Any]] = []
73
+ for rule in policy["rules"]:
74
+ if not rule.get("enabled", True):
75
+ continue
76
+ if rule["type"] == "threshold":
77
+ triggered.extend(self._threshold_matches(rule, measurements))
78
+ elif rule["type"] == "finding_severity":
79
+ triggered.extend(self._finding_matches(rule, findings))
80
+ elif rule["type"] == "capability":
81
+ triggered.extend(self._capability_matches(rule, results, configuration))
82
+ elif rule["type"] == "comparison_regression":
83
+ triggered.extend({"ruleId": rule["id"], "outcome": self._outcome(rule.get("outcome", "FAIL")),
84
+ "comparisonId": normalized.get("comparison", {}).get("comparisonId"),
85
+ "findingId": item, "affectedEvidence": {"comparisonId": normalized.get("comparison", {}).get("comparisonId")}}
86
+ for item in normalized.get("comparison", {}).get("regressions", []))
87
+ if any(item.get("blocking") is True for item in limitations):
88
+ triggered.append({"ruleId": "limitation.blocking", "outcome": "BLOCKED", "reason": "blocking limitation"})
89
+ decision = self._decision(triggered, measurements, findings, results)
90
+ return {
91
+ "policy": {key: policy[key] for key in ("identifier", "version", "scope", "owner")},
92
+ "decision": decision,
93
+ "decisionReason": self._decision_reason(decision, triggered),
94
+ "triggeredRules": triggered,
95
+ "thresholds": {rule["id"]: {key: rule[key] for key in ("warning", "blocking", "direction") if key in rule}
96
+ for rule in policy["rules"] if rule["type"] == "threshold"},
97
+ "affectedCapabilities": sorted({item["capabilityId"] for item in triggered if item.get("capabilityId")} | {item["affectedCapability"] for item in triggered if item.get("affectedCapability")} ),
98
+ "qualificationReference": {"measurementIds": sorted(str(item.get("measurementId")) for item in measurements),
99
+ "findingIds": sorted(str(item.get("findingId")) for item in findings)},
100
+ "qualificationInputs": {"measurementCount": len(measurements), "findingCount": len(findings),
101
+ "capabilityResultCount": len(results), "limitationCount": len(limitations),
102
+ "configuration": dict(configuration)},
103
+ }
104
+
105
+ @staticmethod
106
+ def validate(policy: Mapping[str, Any]) -> None:
107
+ required = {"identifier", "version", "scope", "owner", "description", "supportedCapabilities",
108
+ "supportedSchemas", "supportedRuntimeVersions", "rules"}
109
+ missing = required - set(policy)
110
+ if missing:
111
+ raise PolicyError(f"policy is missing required fields: {sorted(missing)}")
112
+ if not all(isinstance(policy[key], str) and policy[key] for key in ("identifier", "version", "scope", "owner", "description")):
113
+ raise PolicyError("policy identity fields must be non-empty strings")
114
+ if not all(isinstance(policy[key], list) and all(isinstance(item, str) for item in policy[key])
115
+ for key in ("supportedCapabilities", "supportedSchemas", "supportedRuntimeVersions")):
116
+ raise PolicyError("policy compatibility fields must be string arrays")
117
+ if not isinstance(policy["rules"], list):
118
+ raise PolicyError("policy.rules must be an array")
119
+ identifiers: set[str] = set()
120
+ for rule in policy["rules"]:
121
+ if not isinstance(rule, dict) or not isinstance(rule.get("id"), str) or rule.get("type") not in {"threshold", "finding_severity", "capability", "comparison_regression"}:
122
+ raise PolicyError("every policy rule needs an id and supported type")
123
+ if rule["id"] in identifiers:
124
+ raise PolicyError(f"policy rule identifiers must be unique: {rule['id']}")
125
+ identifiers.add(rule["id"])
126
+
127
+ @staticmethod
128
+ def _policy_files(directories: Iterable[Path]) -> tuple[Path, ...]:
129
+ return tuple(path for directory in directories if directory.is_dir() for path in sorted(directory.glob("*.json")))
130
+
131
+ @staticmethod
132
+ def _read(path: Path) -> dict[str, Any]:
133
+ try:
134
+ value = json.loads(path.read_text(encoding="utf-8"))
135
+ except (OSError, json.JSONDecodeError) as error:
136
+ raise PolicyError(f"invalid policy {path}: {error}") from error
137
+ if not isinstance(value, dict):
138
+ raise PolicyError(f"policy {path} must be an object")
139
+ return value
140
+
141
+ @staticmethod
142
+ def _validate_compatibility(policy: Mapping[str, Any], runtime_version: str, schema_version: str) -> None:
143
+ if runtime_version not in policy["supportedRuntimeVersions"]:
144
+ raise PolicyError("policy is incompatible with this runtime version")
145
+ if schema_version not in policy["supportedSchemas"]:
146
+ raise PolicyError("policy is incompatible with this schema version")
147
+
148
+ @staticmethod
149
+ def _outcome(value: str) -> str:
150
+ aliases = {"WARNING": "PASS_WITH_WARNINGS", "BLOCKING": "FAIL"}
151
+ outcome = aliases.get(value, value)
152
+ if outcome not in POLICY_DECISIONS:
153
+ raise PolicyError(f"unsupported policy outcome: {value}")
154
+ return outcome
155
+
156
+ def _threshold_matches(self, rule: Mapping[str, Any], measurements: list[Mapping[str, Any]]) -> list[dict[str, Any]]:
157
+ matches: list[dict[str, Any]] = []
158
+ for measurement in measurements:
159
+ if measurement.get("metricKey") != rule.get("metricKey") or not isinstance(measurement.get("value"), (int, float)):
160
+ continue
161
+ value, direction = measurement["value"], rule.get("direction", "greater_than")
162
+ def reached(threshold: Any) -> bool:
163
+ return threshold is not None and ((direction == "greater_than" and value >= threshold) or (direction == "less_than" and value <= threshold))
164
+ threshold, outcome = (rule.get("blocking"), "FAIL") if reached(rule.get("blocking")) else (rule.get("warning"), "PASS_WITH_WARNINGS") if reached(rule.get("warning")) else (None, None)
165
+ if outcome:
166
+ matches.append({"ruleId": rule["id"], "outcome": outcome, "metricKey": rule["metricKey"], "measuredValue": value,
167
+ "threshold": threshold, "affectedCapability": measurement.get("capabilityId"),
168
+ "affectedEvidence": {"measurementId": measurement.get("measurementId"), "targetEntityId": measurement.get("targetEntityId")}})
169
+ return matches
170
+
171
+ def _finding_matches(self, rule: Mapping[str, Any], findings: list[Mapping[str, Any]]) -> list[dict[str, Any]]:
172
+ outcome = self._outcome(rule.get("outcome", "PASS_WITH_WARNINGS"))
173
+ return [{"ruleId": rule["id"], "outcome": outcome, "findingId": finding.get("findingId"), "severity": rule.get("severity"),
174
+ "affectedCapability": finding.get("capabilityId"), "affectedEvidence": {"findingId": finding.get("findingId")}}
175
+ for finding in findings if finding.get("severity") == rule.get("severity")]
176
+
177
+ def _capability_matches(self, rule: Mapping[str, Any], results: list[Mapping[str, Any]], configuration: Mapping[str, Any]) -> list[dict[str, Any]]:
178
+ capability_id = rule.get("capabilityId")
179
+ configured = configuration.get("executionOptions", {}).get("capabilities", {}).get(capability_id, {})
180
+ present = any(result.get("capabilityId") == capability_id for result in results)
181
+ required = rule.get("required", rule.get("enabled") is True)
182
+ if not required or present:
183
+ return []
184
+ return [{"ruleId": rule["id"], "outcome": self._outcome(rule.get("outcome", "BLOCKED")), "capabilityId": capability_id,
185
+ "reason": "required capability evidence is missing", "affectedEvidence": {"capabilityId": capability_id, "configured": configured}}]
186
+
187
+ @staticmethod
188
+ def _decision(triggered: list[Mapping[str, Any]], measurements: list[Mapping[str, Any]], findings: list[Mapping[str, Any]], results: list[Mapping[str, Any]]) -> str:
189
+ outcomes = {item["outcome"] for item in triggered}
190
+ if "BLOCKED" in outcomes:
191
+ return "BLOCKED"
192
+ if "FAIL" in outcomes:
193
+ return "FAIL"
194
+ if "PASS_WITH_WARNINGS" in outcomes:
195
+ return "PASS_WITH_WARNINGS"
196
+ return "PASS" if measurements or findings or results else "NOT_APPLICABLE"
197
+
198
+ @staticmethod
199
+ def _decision_reason(decision: str, triggered: list[Mapping[str, Any]]) -> str:
200
+ if not triggered:
201
+ return "no policy rule was triggered" if decision == "PASS" else "no applicable capability evidence is available"
202
+ return f"{len(triggered)} policy rule(s) triggered; highest outcome is {decision}"
tde_runtime/query.py ADDED
@@ -0,0 +1,36 @@
1
+ """Versioned, read-only query layer over canonical evidence."""
2
+ from __future__ import annotations
3
+ from hashlib import sha256
4
+ from time import perf_counter
5
+ from typing import Any, Mapping
6
+
7
+ QUERY_LANGUAGE_VERSION = "1.0.0"
8
+
9
+ class QueryEngine:
10
+ def execute(self, evidence: Mapping[str, Any], query: Mapping[str, Any]) -> dict[str, Any]:
11
+ started = perf_counter()
12
+ resource = query.get("resource", "repositories")
13
+ comparisons = list(query.get("comparisons", []))
14
+ comparison_findings = [
15
+ {"comparisonId": item.get("comparisonId"), **transition}
16
+ for item in comparisons for transition in item.get("comparison", {}).get("findingTransitions", [])
17
+ ]
18
+ collections = {"repositories": [evidence.get("repository", {})], "capabilities": evidence.get("capabilityResults", []), "metrics": evidence.get("measurements", []), "findings": evidence.get("findings", []), "policies": [evidence.get("policyEvidence", {})], "qualification": [evidence.get("policyEvidence", {})], "baselines": query.get("baselines", []), "comparisons": comparisons, "comparison_findings": comparison_findings, "trends": query.get("trends", [])}
19
+ if resource not in collections: raise ValueError(f"unsupported query resource: {resource}")
20
+ rows = list(collections[resource])
21
+ for key, expected in query.get("filter", {}).items(): rows = [row for row in rows if str(row.get(key)) == str(expected)]
22
+ sort = query.get("sort")
23
+ if sort: rows.sort(key=lambda row: str(row.get(sort, "")), reverse=query.get("descending", False))
24
+ aggregate = query.get("aggregate")
25
+ if aggregate == "count": rows = [{"count": len(rows)}]
26
+ group = query.get("groupBy")
27
+ if group:
28
+ grouped: dict[str, int] = {}
29
+ for row in rows: grouped[str(row.get(group, ""))] = grouped.get(str(row.get(group, "")), 0) + 1
30
+ rows = [{group: key, "count": value} for key, value in sorted(grouped.items())]
31
+ projection = query.get("projection")
32
+ if projection: rows = [{key: row.get(key) for key in projection} for row in rows]
33
+ offset, limit = int(query.get("offset", 0)), query.get("limit")
34
+ rows = rows[offset: offset + int(limit) if limit is not None else None]
35
+ identity = sha256(str(sorted(query.items())).encode()).hexdigest()[:16]
36
+ return {"queryEvidence": {"queryId": f"query.{identity}", "languageVersion": QUERY_LANGUAGE_VERSION, "resource": resource, "execution": "READ_ONLY", "durationMs": int((perf_counter()-started)*1000), "resultCount": len(rows), "limitations": []}, "results": rows}
@@ -0,0 +1,21 @@
1
+ """Registry boundaries. Entries arrive only in later capability/adapter increments."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ class CapabilityRegistry:
7
+ def discover(self) -> tuple[object, ...]:
8
+ return ({"id": "code_size", "version": "0.1.0", "status": "VALIDATED"},{"id":"complexity","version":"0.1.0","status":"VALIDATED"},{"id":"maintainability","version":"0.1.0","status":"VALIDATED"},{"id":"dependency_health","version":"0.1.0","status":"VALIDATED"})
9
+
10
+
11
+ class AdapterRegistry:
12
+ def discover(self) -> tuple[object, ...]:
13
+ return ({"id": "code_size.cloc", "version": "0.1.0", "analyzer": "cloc"},{"id":"complexity.radon","version":"0.1.0","analyzer":"radon"})
14
+
15
+
16
+ class PolicyRegistry:
17
+ """Discovery boundary for configuration-driven policy files."""
18
+
19
+ def discover(self) -> tuple[object, ...]:
20
+ from .policy import PolicyEngine
21
+ return PolicyEngine().discover()
@@ -0,0 +1,34 @@
1
+ """Immutable manifest and integrity checks for 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
+
10
+ def digest(path: Path) -> str:
11
+ hasher = sha256()
12
+ with path.open("rb") as stream:
13
+ for chunk in iter(lambda: stream.read(1024 * 1024), b""):
14
+ hasher.update(chunk)
15
+ return "sha256:" + hasher.hexdigest()
16
+
17
+
18
+ def canonical(value: Mapping[str, Any]) -> bytes:
19
+ return (json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + "\n").encode("utf-8")
20
+
21
+
22
+ def verify(root: str | Path) -> dict[str, Any]:
23
+ directory = Path(root)
24
+ try:
25
+ manifest = json.loads((directory / "bundle-manifest.json").read_text(encoding="utf-8"))
26
+ sums = (directory / "SHA256SUMS").read_bytes()
27
+ expected = {line.split(maxsplit=1)[1]: "sha256:" + line.split(maxsplit=1)[0] for line in sums.decode().splitlines() if line}
28
+ valid_files = bool(expected) and all((directory / name).is_file() and digest(directory / name) == value for name, value in expected.items())
29
+ complete = {"wheel", "source_distribution", "oci_archive", "release_manifest", "release_qualification", "release_certification", "docker_provenance"}.issubset({item["kind"] for item in manifest["contents"]})
30
+ valid = (manifest.get("schemaId") == "tde.certified-release-bundle" and valid_files and complete
31
+ and manifest.get("bundleChecksum") == "sha256:" + sha256(sums).hexdigest())
32
+ except (OSError, ValueError, KeyError, IndexError, json.JSONDecodeError):
33
+ manifest, valid, complete = {}, False, False
34
+ return {"bundle": manifest, "integrity": valid, "complete": complete}
@@ -0,0 +1,75 @@
1
+ """Fail-closed validation for immutable mainline release-candidate snapshots."""
2
+ from __future__ import annotations
3
+
4
+ from datetime import datetime, timezone
5
+ import re
6
+ import subprocess
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+
11
+ SHA = re.compile(r"^[0-9a-f]{40}$")
12
+ VERSION = re.compile(r"^\d+\.\d+\.\d+(?:[A-Za-z0-9.+-]+)?$")
13
+
14
+
15
+ def _git(root: Path, *arguments: str) -> tuple[bool, str]:
16
+ result = subprocess.run(["git", "-C", str(root), *arguments], text=True,
17
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
18
+ return result.returncode == 0, result.stdout.strip()
19
+
20
+
21
+ def validate_snapshot(root: str | Path, candidate_sha: str, version: str, profile: str,
22
+ main_ref: str = "main") -> dict[str, Any]:
23
+ """Return an independently-verifiable candidate record or fail closed.
24
+
25
+ A candidate is a reference only: it creates neither a tag nor source changes.
26
+ """
27
+ repository = Path(root).resolve()
28
+ if not SHA.fullmatch(candidate_sha):
29
+ raise ValueError("candidate SHA must be an exact lowercase 40-character Git SHA")
30
+ if not VERSION.fullmatch(version):
31
+ raise ValueError("candidate version is invalid")
32
+ if not profile:
33
+ raise ValueError("candidate release profile is required")
34
+ exists, object_type = _git(repository, "cat-file", "-t", candidate_sha)
35
+ if not exists or object_type != "commit":
36
+ raise ValueError("candidate SHA does not identify a commit")
37
+ main_exists, _ = _git(repository, "rev-parse", "--verify", f"{main_ref}^{{commit}}")
38
+ if not main_exists:
39
+ raise ValueError(f"mainline reference {main_ref!r} is unavailable")
40
+ ancestor, _ = _git(repository, "merge-base", "--is-ancestor", candidate_sha, main_ref)
41
+ if not ancestor:
42
+ raise ValueError("candidate SHA is not an ancestor of main; sibling and unmerged candidates are prohibited")
43
+ clean, status = _git(repository, "status", "--porcelain")
44
+ if not clean or status:
45
+ raise ValueError("candidate repository must be clean")
46
+ main_sha_ok, main_sha = _git(repository, "rev-parse", f"{main_ref}^{{commit}}")
47
+ source_ref_ok, source_ref = _git(repository, "branch", "--show-current")
48
+ actor_ok, actor = _git(repository, "config", "user.email")
49
+ return {
50
+ "schemaId": "tde.mainline-candidate-snapshot",
51
+ "schemaVersion": "1.0.0",
52
+ "candidateSha": candidate_sha,
53
+ "version": version,
54
+ "profile": profile,
55
+ "sourceBranch": source_ref if source_ref_ok and source_ref else main_ref,
56
+ "mainlineSha": main_sha if main_sha_ok else None,
57
+ "actor": actor if actor_ok and actor else "github-actions",
58
+ "createdAt": datetime.now(timezone.utc).replace(microsecond=0).isoformat(),
59
+ "ancestryVerified": True,
60
+ "immutable": True,
61
+ "publicationSource": "exact candidate SHA only",
62
+ }
63
+
64
+
65
+ def candidate_superseded(candidate_sha: str, changed_paths: list[str]) -> bool:
66
+ """Classify later intended-release changes without changing candidate identity."""
67
+ if not SHA.fullmatch(candidate_sha):
68
+ raise ValueError("candidate SHA must be exact")
69
+ administrative_prefixes = ("docs/history/prompts/",)
70
+ administrative_files = {
71
+ "ENGINEERING_STATUS.md", "REPOSITORY_STATUS.md", "MANAGEMENT_SUMMARY.md",
72
+ "PROMPT_INDEX.md", "RELEASE_PUBLICATION.md",
73
+ }
74
+ return any(path not in administrative_files and not path.startswith(administrative_prefixes)
75
+ for path in changed_paths)
@@ -0,0 +1,87 @@
1
+ """Canonical, evidence-only Internal Release certification."""
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
+
10
+ def _canonical(value: object) -> bytes:
11
+ return (json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + "\n").encode("utf-8")
12
+
13
+
14
+ def _read(path: str | Path) -> tuple[dict[str, Any] | None, str | None]:
15
+ try:
16
+ raw = Path(path).read_bytes()
17
+ value = json.loads(raw)
18
+ return (value if isinstance(value, dict) else None), "sha256:" + sha256(raw).hexdigest()
19
+ except (OSError, ValueError, json.JSONDecodeError):
20
+ return None, None
21
+
22
+
23
+ class ReleaseCertification:
24
+ """Certify a qualified candidate by validating, never regenerating, evidence."""
25
+
26
+ def certify(self, qualification_path: str | Path, report_output: str | Path) -> dict[str, Any]:
27
+ qualification, digest = _read(qualification_path)
28
+ limitations: list[str] = []
29
+ if qualification is None:
30
+ limitations.append("release qualification evidence is missing or invalid JSON")
31
+ qualification = {}
32
+ elif isinstance(qualification.get("releaseQualificationEvidence"), dict):
33
+ qualification = qualification["releaseQualificationEvidence"]
34
+
35
+ candidate = qualification.get("releaseCandidate", {})
36
+ artifacts = qualification.get("artifacts", [])
37
+ manifest = qualification.get("manifest", {})
38
+ runtime = qualification.get("runtimeEvidence", {})
39
+ checks = qualification.get("checks", {})
40
+ release_evidence_reference, release_evidence_digest, release_evidence_integrity = _release_evidence_check(qualification, candidate, checks)
41
+ evidence_checks = {
42
+ "candidateIdentity": isinstance(candidate.get("sha"), str) and len(candidate["sha"]) == 40,
43
+ "artifactIdentity": isinstance(artifacts, list) and bool(artifacts) and all(item.get("digest", "").startswith("sha256:") for item in artifacts if isinstance(item, dict)),
44
+ "artifactIntegrity": bool(checks.get("artifactIntegrity")),
45
+ "artifactReproducibility": bool(checks.get("buildReproducibility")),
46
+ "workflowIntegrity": qualification.get("trustedDelivery", {}).get("decision") == "PASS",
47
+ "softwareAssurance": qualification.get("softwareAssurance", {}).get("decision") == "PASS",
48
+ "trustedDelivery": qualification.get("trustedDelivery", {}).get("decision") == "PASS",
49
+ "releaseQualification": qualification.get("decision") == "RELEASE_QUALIFIED" and qualification.get("releaseDecision") == "READY",
50
+ "buildProvenance": bool(checks.get("buildReproducibility")),
51
+ "canonicalEvidence": bool(digest) and bool(manifest.get("integrity")),
52
+ "releaseEvidence": release_evidence_integrity,
53
+ "runtimeQualification": (runtime.get("validation", {}).get("status") == "VALID"
54
+ and runtime.get("runtimeQualification") == "QUALIFIED"),
55
+ "policyEvidence": runtime.get("policyDecision") in {"PASS", "PASS_WITH_WARNINGS"},
56
+ "dockerArtifact": bool(checks.get("dockerArtifact", True)),
57
+ }
58
+ for name, valid in evidence_checks.items():
59
+ if not valid:
60
+ limitations.append(f"required {name} evidence is unavailable, invalid, or did not pass")
61
+ decision = "RELEASE_CERTIFIED" if all(evidence_checks.values()) else "RELEASE_NOT_CERTIFIED"
62
+ report = {
63
+ "schemaId": "tde.release-certification", "schemaVersion": "1.0.0",
64
+ "candidate": candidate, "repository": candidate.get("repository"),
65
+ "certificationInputs": {"releaseQualification": {"path": str(qualification_path), "digest": digest},
66
+ "softwareAssuranceId": qualification.get("softwareAssurance", {}).get("assuranceId"),
67
+ "trustedDeliveryId": qualification.get("trustedDelivery", {}).get("trustedDeliveryId"),
68
+ "manifest": manifest, "artifacts": artifacts, "runtimeEvidence": runtime,
69
+ "releaseEvidence": {"reference": release_evidence_reference, "digest": release_evidence_digest}},
70
+ "checks": evidence_checks, "decision": decision, "limitations": limitations,
71
+ "decisionRationale": "All required canonical evidence passed." if decision == "RELEASE_CERTIFIED" else "Certification is fail-closed because required canonical evidence did not pass.",
72
+ }
73
+ report["certificationId"] = "release-certification.sha256." + sha256(_canonical(report)).hexdigest()
74
+ output = Path(report_output).resolve(); output.parent.mkdir(parents=True, exist_ok=True); output.write_bytes(_canonical(report))
75
+ return {**report, "report": {"path": str(output), "digest": "sha256:" + sha256(output.read_bytes()).hexdigest(), "integrity": True}}
76
+
77
+
78
+ def _release_evidence_check(qualification: Mapping[str, Any], candidate: Mapping[str, Any], checks: Mapping[str, Any]) -> tuple[dict[str, Any], str | None, bool]:
79
+ reference = qualification.get("releaseEvidence", {})
80
+ if not isinstance(reference, dict): return {}, None, False
81
+ evidence, digest = _read(reference.get("path", ""))
82
+ if not evidence: return reference, digest, False
83
+ unsigned = {key: value for key, value in evidence.items() if key != "releaseEvidenceId"}
84
+ expected = "release-evidence.sha256." + sha256(_canonical(unsigned)).hexdigest()
85
+ valid = digest == reference.get("digest") and evidence.get("releaseEvidenceId") == reference.get("id")
86
+ valid = valid and evidence.get("releaseEvidenceId") == expected and evidence.get("candidate") == candidate
87
+ return reference, digest, valid and evidence.get("releaseQualification", {}).get("checks") == checks