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
tde_cli/__init__.py
ADDED
tde_cli/main.py
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
"""CLI framework that consumes only the public Runtime API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import sys
|
|
10
|
+
from typing import Any, Sequence, TextIO
|
|
11
|
+
|
|
12
|
+
from tde_runtime import Runtime, RuntimeConfiguration
|
|
13
|
+
from tde_runtime.baseline import BaselineError, BaselineRepository, ComparisonEngine, ComparisonRepository
|
|
14
|
+
from tde_runtime.policy import PolicyEngine, PolicyError
|
|
15
|
+
from tde_runtime.trend import TrendEngine
|
|
16
|
+
from tde_runtime.evidence_store import EvidenceStore
|
|
17
|
+
from tde_runtime.runtime_qualification import RuntimeQualificationEngine
|
|
18
|
+
from tde_runtime.software_assurance import SoftwareAssurance
|
|
19
|
+
from tde_runtime.trusted_delivery import TrustedDelivery
|
|
20
|
+
from tde_runtime.release_qualification import ReleaseQualification
|
|
21
|
+
from tde_runtime.release_certification import ReleaseCertification
|
|
22
|
+
from tde_runtime.runtime import EVIDENCE_SCHEMA_VERSION, RUNTIME_VERSION
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
CLI_VERSION = "0.1.0"
|
|
26
|
+
GENERATION = "1"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ExitCode:
|
|
30
|
+
SUCCESS = 0
|
|
31
|
+
WARNING = 1
|
|
32
|
+
FAILED = 2
|
|
33
|
+
BLOCKED = 3
|
|
34
|
+
NOT_SUPPORTED = 4
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _policy_exit_code(decision: str) -> int:
|
|
38
|
+
"""The CLI maps canonical Policy decisions; it never re-evaluates policy."""
|
|
39
|
+
return {"PASS": ExitCode.SUCCESS, "PASS_WITH_WARNINGS": ExitCode.WARNING,
|
|
40
|
+
"FAIL": ExitCode.FAILED, "BLOCKED": ExitCode.BLOCKED,
|
|
41
|
+
"NOT_APPLICABLE": ExitCode.NOT_SUPPORTED}.get(decision, ExitCode.BLOCKED)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
COMMANDS: dict[str, dict[str, str]] = {
|
|
45
|
+
"help": {"purpose": "Show generated CLI help."},
|
|
46
|
+
"validate": {"purpose": "Validate runtime configuration and context."},
|
|
47
|
+
"inspect": {"purpose": "Inspect a target and planned Code Size execution."},
|
|
48
|
+
"assess": {"purpose": "Assess a target through selected capabilities."},
|
|
49
|
+
"baseline": {"purpose": "Create an immutable baseline from persisted canonical evidence."},
|
|
50
|
+
"compare": {"purpose": "Persist and qualify a canonical comparison against a baseline."},
|
|
51
|
+
"trend": {"purpose": "Aggregate canonical evidence history into trends."},
|
|
52
|
+
"query": {"purpose": "Query canonical engineering evidence."},
|
|
53
|
+
"store": {"purpose": "Persist canonical Runtime evidence."},
|
|
54
|
+
"history": {"purpose": "List persisted canonical evidence."},
|
|
55
|
+
"run": {"purpose": "Execute registered capabilities through the Execution Engine."},
|
|
56
|
+
"qualify": {"purpose": "Qualify canonical Runtime evidence."},
|
|
57
|
+
"assure": {"purpose": "Assure repository and artifact integrity."},
|
|
58
|
+
"trusted-delivery": {"purpose": "Validate immutable candidate and delivery evidence."},
|
|
59
|
+
"release-qualify": {"purpose": "Qualify a release candidate without publication."},
|
|
60
|
+
"certify": {"purpose": "Certify a qualified Internal Release candidate without publication."},
|
|
61
|
+
"report": {"purpose": "Render a Code Size evidence projection."},
|
|
62
|
+
"explain": {"purpose": "Explain a result (not implemented)."},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _global_options(parser: argparse.ArgumentParser) -> None:
|
|
67
|
+
parser.add_argument("--verbose", action="store_true", help="Enable INFO logging.")
|
|
68
|
+
parser.add_argument("--quiet", action="store_true", help="Suppress non-error logging.")
|
|
69
|
+
parser.add_argument("--config", help="Path to repository .tde.yml configuration.")
|
|
70
|
+
parser.add_argument("--output", help="Output destination (reserved; console is used now).")
|
|
71
|
+
parser.add_argument("--format", choices=("human", "json", "markdown"), default="human", help="Output format.")
|
|
72
|
+
parser.add_argument("--log-level", choices=("ERROR", "WARNING", "INFO", "DEBUG", "TRACE"), help="Logging level.")
|
|
73
|
+
parser.add_argument("--policy", help="Repository or workspace policy directory.")
|
|
74
|
+
parser.add_argument("--policy-override", action="append", default=[], metavar="RULE=JSON", help="Override a policy rule with a JSON object.")
|
|
75
|
+
parser.add_argument("--baseline-location", help="Directory used for immutable baselines.")
|
|
76
|
+
parser.add_argument("--history-depth", type=int, help="Maximum baseline history depth for trends.")
|
|
77
|
+
parser.add_argument("--store-location", help="Directory used for canonical evidence storage.")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
81
|
+
parser = argparse.ArgumentParser(prog="tde", description="Technical Debt Engine CLI foundation.")
|
|
82
|
+
parser.add_argument("--version", action="store_true", help="Show CLI, runtime, schema, and generation versions.")
|
|
83
|
+
_global_options(parser)
|
|
84
|
+
subcommands = parser.add_subparsers(dest="command", title="commands")
|
|
85
|
+
for identifier, metadata in COMMANDS.items():
|
|
86
|
+
command = subcommands.add_parser(identifier, help=metadata["purpose"], description=metadata["purpose"])
|
|
87
|
+
command.add_argument("target", nargs="?", default=".", help="Repository target (default: current directory).")
|
|
88
|
+
command.add_argument("--capability", action="append", default=[], help="Enable a registered capability.")
|
|
89
|
+
if identifier == "baseline":
|
|
90
|
+
command.add_argument("--name", help="Immutable baseline name.")
|
|
91
|
+
if identifier == "compare":
|
|
92
|
+
command.add_argument("--baseline", help="Baseline name or JSON path.")
|
|
93
|
+
if identifier == "query":
|
|
94
|
+
command.add_argument("--resource", default="repositories", help="Evidence resource.")
|
|
95
|
+
command.add_argument("--filter", action="append", default=[], metavar="KEY=VALUE")
|
|
96
|
+
command.add_argument("--aggregate", choices=("count",))
|
|
97
|
+
if identifier == "assure":
|
|
98
|
+
command.add_argument("--artifact-directory", action="append", default=[], metavar="DIRECTORY",
|
|
99
|
+
help="Candidate build directory; repeat for independent reproducibility verification.")
|
|
100
|
+
if identifier == "trusted-delivery":
|
|
101
|
+
command.add_argument("--artifact-directory", action="append", default=[], metavar="DIRECTORY",
|
|
102
|
+
help="Candidate build directory; repeat for independent reproducibility verification.")
|
|
103
|
+
command.add_argument("--manifest", metavar="PATH", help="Canonical JSON delivery manifest for this candidate.")
|
|
104
|
+
if identifier == "release-qualify":
|
|
105
|
+
command.add_argument("--artifact-directory", action="append", default=[], metavar="DIRECTORY")
|
|
106
|
+
command.add_argument("--docker-artifact-directory", metavar="DIRECTORY")
|
|
107
|
+
command.add_argument("--manifest-output", required=True, metavar="PATH")
|
|
108
|
+
command.add_argument("--release-capability", action="append", default=[], metavar="CAPABILITY",
|
|
109
|
+
help="Required release capability; repeat for every selected capability.")
|
|
110
|
+
if identifier == "certify":
|
|
111
|
+
command.add_argument("--qualification-evidence", required=True, metavar="PATH")
|
|
112
|
+
command.add_argument("--report-output", required=True, metavar="PATH")
|
|
113
|
+
return parser
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _load_configuration(path: str | None, target: str) -> RuntimeConfiguration:
|
|
117
|
+
return RuntimeConfiguration.discover(target, path)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _configure_logging(arguments: argparse.Namespace) -> None:
|
|
121
|
+
level = arguments.log_level or ("INFO" if arguments.verbose else "ERROR" if arguments.quiet else "WARNING")
|
|
122
|
+
logging.basicConfig(level=logging.DEBUG if level == "TRACE" else getattr(logging, level), format="%(levelname)s %(message)s")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _render(value: dict[str, Any], output_format: str, stream: TextIO) -> None:
|
|
126
|
+
if output_format == "json":
|
|
127
|
+
print(json.dumps(value, sort_keys=True), file=stream)
|
|
128
|
+
return
|
|
129
|
+
if output_format == "markdown":
|
|
130
|
+
for key, item in value.items():
|
|
131
|
+
print(f"- **{key}:** `{item}`", file=stream)
|
|
132
|
+
return
|
|
133
|
+
for key, item in value.items():
|
|
134
|
+
print(f"{key}: {item}", file=stream)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _store_location(arguments: argparse.Namespace, target: str) -> Path:
|
|
138
|
+
location = Path(arguments.store_location or ".tde/evidence")
|
|
139
|
+
return location if location.is_absolute() else Path(target) / location
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _runtime_result(command: str, target: str, configuration: RuntimeConfiguration,
|
|
143
|
+
store: EvidenceStore | None = None) -> tuple[int, dict[str, Any]]:
|
|
144
|
+
result = Runtime().execute(target, configuration)
|
|
145
|
+
payload = {"command": command, "runtime": result.report["runtimeSummary"],
|
|
146
|
+
"execution": result.report["executionSummary"], "environment": result.report["environment"], "qualification": result.report["qualification"],
|
|
147
|
+
"runtimeQualification": result.evidence["runtimeQualification"], "validation": result.validation,
|
|
148
|
+
"evidence": result.evidence, "evidenceId": result.evidence["integrity"]["contentDigest"]}
|
|
149
|
+
if store is not None:
|
|
150
|
+
payload["evidenceStore"] = store.persist(result.evidence)
|
|
151
|
+
decision = result.evidence["policyEvidence"]["decision"]
|
|
152
|
+
if decision == "BLOCKED":
|
|
153
|
+
return ExitCode.BLOCKED, payload
|
|
154
|
+
if command in {"assess", "run"} and result.evidence["runtimeQualification"]["level"] != "QUALIFIED":
|
|
155
|
+
return ExitCode.BLOCKED, payload
|
|
156
|
+
return (_policy_exit_code(decision) if command in {"assess", "run"} else ExitCode.SUCCESS), payload
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _render_capability_report(evidence: dict[str, Any], capability: str, output_format: str, stream: TextIO) -> None:
|
|
160
|
+
metrics = {item["metricKey"]: item["value"] for item in evidence.get("measurements", []) if item.get("scope") == "repository" and item.get("capabilityId") == capability}
|
|
161
|
+
title = "Code Size" if capability == "code_size" else "Complexity"
|
|
162
|
+
payload = {"schemaId": "tde.report", "schemaVersion": evidence["schemaVersion"], "evidenceId": evidence["integrity"]["contentDigest"],
|
|
163
|
+
"format": output_format, "content": {"capability": capability, "metrics": metrics,
|
|
164
|
+
"qualification": evidence["runtimeQualification"]["level"]}}
|
|
165
|
+
if output_format == "markdown":
|
|
166
|
+
print(f"# {title} Report", file=stream)
|
|
167
|
+
print("", file=stream)
|
|
168
|
+
for key, value in sorted(metrics.items()):
|
|
169
|
+
print(f"- **{key}:** {value}", file=stream)
|
|
170
|
+
print(f"- **qualification:** {payload['content']['qualification']}", file=stream)
|
|
171
|
+
return
|
|
172
|
+
_render(payload, output_format, stream)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def main(argv: Sequence[str] | None = None, stdout: TextIO | None = None) -> int:
|
|
176
|
+
stream = stdout or sys.stdout
|
|
177
|
+
parser = build_parser()
|
|
178
|
+
arguments = parser.parse_args(argv)
|
|
179
|
+
prepared = _prepare_command(arguments, parser, stream)
|
|
180
|
+
if isinstance(prepared, int): return prepared
|
|
181
|
+
configuration = prepared
|
|
182
|
+
if arguments.command in {"store", "history"}:
|
|
183
|
+
store = EvidenceStore(_store_location(arguments, arguments.target))
|
|
184
|
+
try:
|
|
185
|
+
if arguments.command == "history":
|
|
186
|
+
_render({"command": "history", "records": store.history()}, arguments.format, stream)
|
|
187
|
+
return ExitCode.SUCCESS
|
|
188
|
+
record = Runtime().execute(arguments.target, configuration)
|
|
189
|
+
_render({"command": "store", "record": store.persist(record.evidence)}, arguments.format, stream)
|
|
190
|
+
return ExitCode.SUCCESS
|
|
191
|
+
except (ValueError, OSError, json.JSONDecodeError) as error:
|
|
192
|
+
_render({"command": arguments.command, "status": "BLOCKED", "reason": str(error)}, arguments.format, stream)
|
|
193
|
+
return ExitCode.BLOCKED
|
|
194
|
+
return _execute_command(arguments, configuration, stream)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _prepare_command(arguments: argparse.Namespace, parser: argparse.ArgumentParser, stream: TextIO) -> RuntimeConfiguration | int:
|
|
198
|
+
if arguments.version:
|
|
199
|
+
_render({"cliVersion": CLI_VERSION, "runtimeVersion": RUNTIME_VERSION,
|
|
200
|
+
"schemaVersion": EVIDENCE_SCHEMA_VERSION, "generation": GENERATION}, arguments.format, stream)
|
|
201
|
+
return ExitCode.SUCCESS
|
|
202
|
+
if not arguments.command:
|
|
203
|
+
parser.print_help(stream)
|
|
204
|
+
return ExitCode.SUCCESS
|
|
205
|
+
if arguments.command == "help":
|
|
206
|
+
parser.print_help(stream)
|
|
207
|
+
return ExitCode.SUCCESS
|
|
208
|
+
_configure_logging(arguments)
|
|
209
|
+
try:
|
|
210
|
+
configuration = _load_configuration(arguments.config, arguments.target)
|
|
211
|
+
except ValueError as error:
|
|
212
|
+
_render({"status": "BLOCKED", "reason": str(error)}, arguments.format, stream)
|
|
213
|
+
return ExitCode.BLOCKED
|
|
214
|
+
if arguments.policy or arguments.policy_override:
|
|
215
|
+
values = configuration.as_dict()
|
|
216
|
+
policy = dict(values["executionOptions"].get("policy", {}))
|
|
217
|
+
if arguments.policy:
|
|
218
|
+
policy["repository"] = arguments.policy
|
|
219
|
+
overrides = dict(policy.get("overrides", {}))
|
|
220
|
+
try:
|
|
221
|
+
for item in arguments.policy_override:
|
|
222
|
+
rule, raw_value = item.split("=", 1)
|
|
223
|
+
overrides[rule] = json.loads(raw_value)
|
|
224
|
+
if not isinstance(overrides[rule], dict):
|
|
225
|
+
raise ValueError("override must be a JSON object")
|
|
226
|
+
except (ValueError, json.JSONDecodeError) as error:
|
|
227
|
+
_render({"status": "BLOCKED", "reason": f"invalid policy override: {error}"}, arguments.format, stream)
|
|
228
|
+
return ExitCode.BLOCKED
|
|
229
|
+
policy["overrides"] = overrides
|
|
230
|
+
values["policy"] = policy
|
|
231
|
+
values["executionOptions"].pop("policy", None)
|
|
232
|
+
configuration = RuntimeConfiguration.load(values)
|
|
233
|
+
if arguments.baseline_location:
|
|
234
|
+
values = configuration.as_dict()
|
|
235
|
+
values["baseline"] = {"location": arguments.baseline_location}
|
|
236
|
+
values["executionOptions"].pop("baseline", None)
|
|
237
|
+
configuration = RuntimeConfiguration.load(values)
|
|
238
|
+
if arguments.history_depth is not None:
|
|
239
|
+
if arguments.history_depth < 0:
|
|
240
|
+
_render({"status": "BLOCKED", "reason": "history depth must not be negative"}, arguments.format, stream)
|
|
241
|
+
return ExitCode.BLOCKED
|
|
242
|
+
values = configuration.as_dict()
|
|
243
|
+
values["trend"] = {"historyDepth": arguments.history_depth}
|
|
244
|
+
values["executionOptions"].pop("trend", None)
|
|
245
|
+
configuration = RuntimeConfiguration.load(values)
|
|
246
|
+
if arguments.command in {"assess", "baseline", "compare", "run", "store", "inspect", "validate", "qualify", "report"} and arguments.capability:
|
|
247
|
+
supported = {"code-size": "code_size", "complexity": "complexity"}
|
|
248
|
+
if len(arguments.capability) != 1 or arguments.capability[0] not in supported:
|
|
249
|
+
_render({"command": arguments.command, "status": "NOT_SUPPORTED", "reason": "This vertical slice supports code-size and complexity."}, arguments.format, stream)
|
|
250
|
+
return ExitCode.NOT_SUPPORTED
|
|
251
|
+
configuration = configuration.with_capability(supported[arguments.capability[0]])
|
|
252
|
+
if arguments.command == "release-qualify":
|
|
253
|
+
supported = {"code-size": "code_size", "complexity": "complexity"}
|
|
254
|
+
if not arguments.release_capability or any(item not in supported for item in arguments.release_capability):
|
|
255
|
+
_render({"command": "release-qualify", "status": "BLOCKED",
|
|
256
|
+
"reason": "release-qualify requires one or more supported --release-capability values"}, arguments.format, stream)
|
|
257
|
+
return ExitCode.BLOCKED
|
|
258
|
+
for capability in sorted(set(arguments.release_capability)):
|
|
259
|
+
configuration = configuration.with_capability(supported[capability])
|
|
260
|
+
return configuration
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _execute_command(arguments: argparse.Namespace, configuration: RuntimeConfiguration, stream: TextIO) -> int:
|
|
264
|
+
if arguments.command in {"baseline", "compare"}:
|
|
265
|
+
return _baseline_command(arguments, configuration, stream)
|
|
266
|
+
if arguments.command == "trend":
|
|
267
|
+
return _trend_command(arguments, configuration, stream)
|
|
268
|
+
if arguments.command == "query":
|
|
269
|
+
return _query_command(arguments, configuration, stream)
|
|
270
|
+
if arguments.command == "qualify":
|
|
271
|
+
try:
|
|
272
|
+
result=Runtime().execute(arguments.target,configuration)
|
|
273
|
+
capability=arguments.capability[0].replace("-","_") if arguments.capability else None
|
|
274
|
+
qualification=RuntimeQualificationEngine().qualify(result.evidence,capability)
|
|
275
|
+
_render({"command":"qualify","runtimeQualification":qualification},arguments.format,stream)
|
|
276
|
+
return ExitCode.SUCCESS if qualification["level"]!="BLOCKED" else ExitCode.BLOCKED
|
|
277
|
+
except ValueError as error:
|
|
278
|
+
_render({"command":"qualify","status":"BLOCKED","reason":str(error)},arguments.format,stream); return ExitCode.BLOCKED
|
|
279
|
+
if arguments.command == "report":
|
|
280
|
+
return _report_command(arguments, stream)
|
|
281
|
+
if arguments.command == "assure":
|
|
282
|
+
evidence=SoftwareAssurance().assure(arguments.target,arguments.artifact_directory)
|
|
283
|
+
_render({"command":"assure","assuranceEvidence":evidence},arguments.format,stream)
|
|
284
|
+
return _policy_exit_code(evidence["decision"])
|
|
285
|
+
if arguments.command == "trusted-delivery":
|
|
286
|
+
result=Runtime().execute(arguments.target,configuration)
|
|
287
|
+
assurance=SoftwareAssurance().assure(arguments.target,arguments.artifact_directory)
|
|
288
|
+
evidence=TrustedDelivery().validate(arguments.target,result.evidence,assurance,arguments.manifest)
|
|
289
|
+
_render({"command":"trusted-delivery","trustedDeliveryEvidence":evidence,"softwareAssurance":assurance},arguments.format,stream)
|
|
290
|
+
return _policy_exit_code(evidence["qualification"])
|
|
291
|
+
if arguments.command == "release-qualify":
|
|
292
|
+
result=Runtime().execute(arguments.target,configuration)
|
|
293
|
+
evidence=ReleaseQualification().qualify(arguments.target,result.evidence,arguments.artifact_directory,arguments.manifest_output,
|
|
294
|
+
[item.replace("-", "_") for item in arguments.release_capability], arguments.docker_artifact_directory)
|
|
295
|
+
_render({"command":"release-qualify","releaseQualificationEvidence":evidence},arguments.format,stream)
|
|
296
|
+
return ExitCode.SUCCESS if evidence["decision"] == "RELEASE_QUALIFIED" else ExitCode.FAILED
|
|
297
|
+
if arguments.command == "certify":
|
|
298
|
+
evidence = ReleaseCertification().certify(arguments.qualification_evidence, arguments.report_output)
|
|
299
|
+
_render({"command": "certify", "releaseCertificationEvidence": evidence}, arguments.format, stream)
|
|
300
|
+
return ExitCode.SUCCESS if evidence["decision"] == "RELEASE_CERTIFIED" else ExitCode.BLOCKED
|
|
301
|
+
if arguments.command in {"validate", "inspect", "assess", "run"}:
|
|
302
|
+
if arguments.command in {"assess", "run"}:
|
|
303
|
+
if not arguments.capability:
|
|
304
|
+
_render({"command": "assess", "status": "NOT_IMPLEMENTED", "reason": "Only code-size and complexity are delivered."}, arguments.format, stream)
|
|
305
|
+
return ExitCode.NOT_SUPPORTED
|
|
306
|
+
try:
|
|
307
|
+
store = EvidenceStore(_store_location(arguments, arguments.target)) if arguments.command in {"assess", "run"} else None
|
|
308
|
+
code, payload = _runtime_result(arguments.command, arguments.target, configuration, store)
|
|
309
|
+
except (ValueError, OSError, json.JSONDecodeError) as error:
|
|
310
|
+
_render({"status": "BLOCKED", "reason": str(error)}, arguments.format, stream)
|
|
311
|
+
return ExitCode.BLOCKED
|
|
312
|
+
_render(payload, arguments.format, stream)
|
|
313
|
+
return code
|
|
314
|
+
_render({"command": arguments.command, "status": "NOT_IMPLEMENTED",
|
|
315
|
+
"reason": "This command framework is present; its capability behavior is not delivered."}, arguments.format, stream)
|
|
316
|
+
return ExitCode.NOT_SUPPORTED
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _baseline_command(arguments: argparse.Namespace, configuration: RuntimeConfiguration, stream: TextIO) -> int:
|
|
320
|
+
location = configuration.execution_options.get("baseline", {}).get("location", ".tde/baselines")
|
|
321
|
+
repository = BaselineRepository(Path(arguments.target) / location if not Path(location).is_absolute() else location)
|
|
322
|
+
evidence_store = EvidenceStore(_store_location(arguments, arguments.target))
|
|
323
|
+
try:
|
|
324
|
+
current = Runtime().execute(arguments.target, configuration)
|
|
325
|
+
persisted = evidence_store.persist(current.evidence)
|
|
326
|
+
evidence = evidence_store.retrieve(persisted["id"])["evidence"]
|
|
327
|
+
if arguments.command == "baseline": return _create_baseline(arguments, repository, evidence, persisted, stream)
|
|
328
|
+
return _compare_baseline(arguments, repository, current, evidence, persisted, stream)
|
|
329
|
+
except (BaselineError, PolicyError, ValueError) as error:
|
|
330
|
+
_render({"command": arguments.command, "status": "BLOCKED", "reason": str(error)}, arguments.format, stream)
|
|
331
|
+
return ExitCode.BLOCKED
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _create_baseline(arguments, repository, evidence, persisted, stream):
|
|
335
|
+
baseline = repository.create(evidence, arguments.name)
|
|
336
|
+
_render({"command": "baseline", "status": "VALID", "evidenceStore": persisted, "baseline": baseline}, arguments.format, stream)
|
|
337
|
+
return ExitCode.SUCCESS
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _compare_baseline(arguments, repository, current, evidence, persisted, stream):
|
|
341
|
+
if not arguments.baseline: raise BaselineError("compare requires --baseline")
|
|
342
|
+
baseline = repository.load(arguments.baseline)
|
|
343
|
+
comparison = ComparisonEngine().compare(evidence, baseline)
|
|
344
|
+
policy = PolicyEngine().load(current.context.configuration, current.context.repository_root, current.context.runtime_version, current.context.schema_version)
|
|
345
|
+
normalized = {"measurements": evidence["measurements"], "findings": evidence["findings"], "capabilityResults": evidence["capabilityResults"], "comparison": comparison}
|
|
346
|
+
policy_evidence = PolicyEngine().evaluate(policy, normalized, current.context.configuration)
|
|
347
|
+
persisted_comparison = ComparisonRepository(repository.location.parent / "comparisons").persist(comparison, policy_evidence, baseline)
|
|
348
|
+
_render({"command": "compare", "evidenceStore": persisted, "comparison": comparison, "comparisonStore": persisted_comparison, "policyEvidence": policy_evidence, "qualificationDelta": persisted_comparison["qualificationDelta"]}, arguments.format, stream)
|
|
349
|
+
return ExitCode.BLOCKED if policy_evidence["decision"] == "BLOCKED" else ExitCode.SUCCESS
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _trend_command(arguments, configuration, stream):
|
|
353
|
+
location = configuration.execution_options.get("baseline", {}).get("location", ".tde/baselines")
|
|
354
|
+
location = Path(arguments.target) / location if not Path(location).is_absolute() else Path(location)
|
|
355
|
+
try:
|
|
356
|
+
current = Runtime().execute(arguments.target, configuration)
|
|
357
|
+
trend = TrendEngine().build(current.evidence, location, configuration.execution_options.get("trend", {}).get("historyDepth"))
|
|
358
|
+
policy = PolicyEngine().load(current.context.configuration, current.context.repository_root, current.context.runtime_version, current.context.schema_version)
|
|
359
|
+
inputs = {"measurements": current.evidence["measurements"], "findings": current.evidence["findings"], "capabilityResults": current.evidence["capabilityResults"], "trend": trend}
|
|
360
|
+
evidence = PolicyEngine().evaluate(policy, inputs, current.context.configuration)
|
|
361
|
+
_render({"command": "trend", "trendEvidence": trend, "policyEvidence": evidence}, arguments.format, stream)
|
|
362
|
+
return ExitCode.BLOCKED if evidence["decision"] == "BLOCKED" else ExitCode.SUCCESS
|
|
363
|
+
except (ValueError, PolicyError) as error:
|
|
364
|
+
_render({"command": "trend", "status": "BLOCKED", "reason": str(error)}, arguments.format, stream); return ExitCode.BLOCKED
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def _query_command(arguments, configuration, stream):
|
|
368
|
+
try:
|
|
369
|
+
filters = dict(item.split("=", 1) for item in arguments.filter)
|
|
370
|
+
records = EvidenceStore(_store_location(arguments, arguments.target)).history()
|
|
371
|
+
if not records: raise ValueError("no persisted evidence is available; run assess first")
|
|
372
|
+
evidence = records[-1]["evidence"]
|
|
373
|
+
location = configuration.execution_options.get("baseline", {}).get("location", ".tde/baselines")
|
|
374
|
+
baseline_path = Path(arguments.target) / location if not Path(location).is_absolute() else Path(location)
|
|
375
|
+
comparisons = ComparisonRepository(baseline_path.parent / "comparisons").history()
|
|
376
|
+
baselines = [BaselineRepository(baseline_path).load(path) for path in sorted(baseline_path.glob("*.json"))] if baseline_path.is_dir() else []
|
|
377
|
+
response = Runtime().query(evidence, {"resource": arguments.resource, "filter": filters, "aggregate": arguments.aggregate, "baselines": baselines, "comparisons": comparisons})
|
|
378
|
+
_render({"command": "query", "evidenceId": evidence["integrity"]["contentDigest"], **response}, arguments.format, stream); return ExitCode.SUCCESS
|
|
379
|
+
except (ValueError, PolicyError) as error:
|
|
380
|
+
_render({"command": "query", "status": "BLOCKED", "reason": str(error)}, arguments.format, stream); return ExitCode.BLOCKED
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def _report_command(arguments, stream):
|
|
384
|
+
supported = {"code-size": "code_size", "complexity": "complexity"}
|
|
385
|
+
if len(arguments.capability) != 1 or arguments.capability[0] not in supported:
|
|
386
|
+
_render({"command": "report", "status": "NOT_SUPPORTED", "reason": "report requires --capability code-size or --capability complexity"}, arguments.format, stream); return ExitCode.NOT_SUPPORTED
|
|
387
|
+
try:
|
|
388
|
+
capability = supported[arguments.capability[0]]
|
|
389
|
+
records = EvidenceStore(_store_location(arguments, arguments.target)).history()
|
|
390
|
+
evidence = next((record["evidence"] for record in reversed(records) if any(item.get("capabilityId") == capability for item in record["evidence"].get("capabilityResults", []))), None)
|
|
391
|
+
if evidence is None: raise ValueError("no persisted evidence is available for this capability; run assess first")
|
|
392
|
+
except ValueError as error:
|
|
393
|
+
_render({"command": "report", "status": "BLOCKED", "reason": str(error)}, arguments.format, stream); return ExitCode.BLOCKED
|
|
394
|
+
if evidence["runtimeQualification"]["level"] != "QUALIFIED":
|
|
395
|
+
_render({"command": "report", "status": "BLOCKED", "reason": "capability evidence is not qualified"}, arguments.format, stream); return ExitCode.BLOCKED
|
|
396
|
+
_render_capability_report(evidence, capability, arguments.format, stream); return ExitCode.SUCCESS
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def console_main() -> None:
|
|
400
|
+
raise SystemExit(main())
|
tde_runtime/__init__.py
ADDED
tde_runtime/baseline.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Immutable canonical-evidence baselines and deterministic comparisons."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from hashlib import sha256
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Mapping
|
|
9
|
+
|
|
10
|
+
from .models import utc_now
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaselineError(ValueError):
|
|
14
|
+
"""Raised when a baseline cannot be created, loaded, or compared."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BaselineRepository:
|
|
18
|
+
def __init__(self, location: str | Path) -> None:
|
|
19
|
+
self.location = Path(location)
|
|
20
|
+
|
|
21
|
+
def create(self, evidence: Mapping[str, Any], name: str | None = None) -> dict[str, Any]:
|
|
22
|
+
self._validate_evidence(evidence)
|
|
23
|
+
created_at = utc_now()
|
|
24
|
+
digest = evidence["integrity"]["contentDigest"]
|
|
25
|
+
identity = sha256(f"{digest}:{created_at}".encode()).hexdigest()[:16]
|
|
26
|
+
baseline_id = name or f"baseline.{identity}"
|
|
27
|
+
destination = self.location / f"{baseline_id}.json"
|
|
28
|
+
if destination.exists():
|
|
29
|
+
raise BaselineError(f"baseline already exists and is immutable: {baseline_id}")
|
|
30
|
+
record = {"baselineId": baseline_id, "sourceEvidenceId": digest,
|
|
31
|
+
"repositoryId": evidence["repository"]["id"], "candidateId": evidence["candidate"]["id"],
|
|
32
|
+
"schemaVersion": evidence["schemaVersion"], "runtimeVersion": evidence["runtime"]["version"],
|
|
33
|
+
"createdAt": created_at,
|
|
34
|
+
"capabilityVersions": {item["capabilityId"]: item.get("capabilityVersion")
|
|
35
|
+
for item in evidence.get("capabilityResults", [])},
|
|
36
|
+
"policyVersion": evidence.get("policyEvidence", {}).get("policy", {}).get("version"),
|
|
37
|
+
"integrityDigest": f"sha256:{sha256(json.dumps(evidence, sort_keys=True).encode()).hexdigest()}",
|
|
38
|
+
"evidence": evidence}
|
|
39
|
+
self.location.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
destination.write_text(json.dumps(record, sort_keys=True, indent=2) + "\n", encoding="utf-8")
|
|
41
|
+
return record
|
|
42
|
+
|
|
43
|
+
def load(self, reference: str | Path) -> dict[str, Any]:
|
|
44
|
+
path = Path(reference)
|
|
45
|
+
path = path if path.is_absolute() or path.exists() else self.location / f"{reference}.json"
|
|
46
|
+
try:
|
|
47
|
+
value = json.loads(path.read_text(encoding="utf-8"))
|
|
48
|
+
except (OSError, json.JSONDecodeError) as error:
|
|
49
|
+
raise BaselineError(f"cannot load baseline {reference}: {error}") from error
|
|
50
|
+
if not isinstance(value, dict) or "evidence" not in value:
|
|
51
|
+
raise BaselineError("baseline does not contain canonical evidence")
|
|
52
|
+
self._validate_evidence(value["evidence"])
|
|
53
|
+
return value
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def _validate_evidence(evidence: Mapping[str, Any]) -> None:
|
|
57
|
+
if evidence.get("schemaId") != "tde.evidence" or evidence.get("validation", {}).get("status") != "VALID":
|
|
58
|
+
raise BaselineError("baseline requires validated canonical evidence")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ComparisonEngine:
|
|
62
|
+
"""Compares canonical evidence; it never produces a qualification decision."""
|
|
63
|
+
|
|
64
|
+
def compare(self, current: Mapping[str, Any], baseline: Mapping[str, Any]) -> dict[str, Any]:
|
|
65
|
+
BaselineRepository._validate_evidence(current)
|
|
66
|
+
previous = baseline["evidence"]
|
|
67
|
+
compatible = self._compatible(current, previous)
|
|
68
|
+
baseline_id = baseline["baselineId"]
|
|
69
|
+
if not compatible:
|
|
70
|
+
return self._blocked(current, baseline_id, "schema, runtime, or repository compatibility failed")
|
|
71
|
+
metric_deltas, metric_limitations = self._metrics(current.get("measurements", []), previous.get("measurements", []))
|
|
72
|
+
finding_transitions, regressions, improvements = self._findings(current.get("findings", []), previous.get("findings", []))
|
|
73
|
+
return {"comparisonId": self._identity(current, baseline_id),
|
|
74
|
+
"currentEvidenceId": current["integrity"]["contentDigest"], "baselineEvidenceId": previous["integrity"]["contentDigest"],
|
|
75
|
+
"baselineId": baseline_id, "schemaCompatibility": "COMPATIBLE", "metricDeltas": metric_deltas,
|
|
76
|
+
"findingTransitions": finding_transitions, "regressions": regressions, "improvements": improvements,
|
|
77
|
+
"unchangedFindings": [item["findingId"] for item in finding_transitions if item["transition"] == "UNCHANGED"],
|
|
78
|
+
"capabilityComparison": self._capabilities(current, previous), "limitations": metric_limitations, "status": "VALID"}
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def _compatible(current: Mapping[str, Any], baseline: Mapping[str, Any]) -> bool:
|
|
82
|
+
return (current.get("schemaVersion") == baseline.get("schemaVersion") and
|
|
83
|
+
current.get("runtime", {}).get("version") == baseline.get("runtime", {}).get("version") and
|
|
84
|
+
current.get("repository", {}).get("id") == baseline.get("repository", {}).get("id"))
|
|
85
|
+
|
|
86
|
+
def _blocked(self, current: Mapping[str, Any], baseline_id: str, reason: str) -> dict[str, Any]:
|
|
87
|
+
return {"comparisonId": self._identity(current, baseline_id), "currentEvidenceId": current["integrity"]["contentDigest"],
|
|
88
|
+
"baselineEvidenceId": "unknown", "baselineId": baseline_id, "schemaCompatibility": "INCOMPATIBLE",
|
|
89
|
+
"metricDeltas": [], "findingTransitions": [], "regressions": [], "improvements": [], "unchangedFindings": [], "capabilityComparison": [],
|
|
90
|
+
"limitations": [{"reason": reason, "blocking": True}], "status": "BLOCKED"}
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def _metrics(current: list[Mapping[str, Any]], baseline: list[Mapping[str, Any]]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
|
94
|
+
key = lambda item: (item.get("metricKey"), item.get("scope"), item.get("targetEntityId"))
|
|
95
|
+
old = {key(item): item for item in baseline if isinstance(item.get("value"), (int, float))}
|
|
96
|
+
deltas: list[dict[str, Any]] = []
|
|
97
|
+
limitations: list[dict[str, Any]] = []
|
|
98
|
+
for item in current:
|
|
99
|
+
if key(item) not in old or not isinstance(item.get("value"), (int, float)):
|
|
100
|
+
continue
|
|
101
|
+
previous = old[key(item)]["value"]
|
|
102
|
+
value = item["value"]
|
|
103
|
+
delta = value - previous
|
|
104
|
+
percentage = None if previous == 0 else delta / previous
|
|
105
|
+
if previous == 0:
|
|
106
|
+
limitations.append({"reason": f"percentage delta unavailable for {item.get('metricKey')}", "blocking": False})
|
|
107
|
+
deltas.append({"metricKey": item.get("metricKey"), "scope": item.get("scope"), "baseline": previous,
|
|
108
|
+
"current": value, "numericDelta": delta, "percentageDelta": percentage,
|
|
109
|
+
"ratioDelta": delta if item.get("unit") == "ratio" else None,
|
|
110
|
+
"distributionDelta": None, "trend": "INCREASED" if delta > 0 else "DECREASED" if delta < 0 else "UNCHANGED"})
|
|
111
|
+
return deltas, limitations
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def _findings(current: list[Mapping[str, Any]], baseline: list[Mapping[str, Any]]) -> tuple[list[dict[str, Any]], list[str], list[str]]:
|
|
115
|
+
old, new = ({item.get("findingId"): item for item in values} for values in (baseline, current))
|
|
116
|
+
severity = {"INFO": 0, "LOW": 1, "MEDIUM": 2, "HIGH": 3, "CRITICAL": 4}
|
|
117
|
+
transitions: list[dict[str, Any]] = []
|
|
118
|
+
regressions: list[str] = []
|
|
119
|
+
improvements: list[str] = []
|
|
120
|
+
for finding_id in sorted(set(old) | set(new)):
|
|
121
|
+
if finding_id not in old:
|
|
122
|
+
transition = "INTRODUCED"; regressions.append(finding_id)
|
|
123
|
+
elif finding_id not in new:
|
|
124
|
+
transition = "RESOLVED"; improvements.append(finding_id)
|
|
125
|
+
else:
|
|
126
|
+
before, after = severity.get(old[finding_id].get("severity"), 0), severity.get(new[finding_id].get("severity"), 0)
|
|
127
|
+
transition = "SEVERITY_INCREASED" if after > before else "SEVERITY_DECREASED" if after < before else "UNCHANGED"
|
|
128
|
+
(regressions if after > before else improvements if after < before else []).append(finding_id)
|
|
129
|
+
transitions.append({"findingId": finding_id, "transition": transition})
|
|
130
|
+
return transitions, regressions, improvements
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def _capabilities(current: Mapping[str, Any], baseline: Mapping[str, Any]) -> list[dict[str, Any]]:
|
|
134
|
+
old = {item["capabilityId"]: item for item in baseline.get("capabilityResults", [])}
|
|
135
|
+
return [{"capabilityId": item["capabilityId"], "comparisonSupport": "SUPPORTED" if item["capabilityId"] in old else "UNSUPPORTED",
|
|
136
|
+
"limitations": [] if item["capabilityId"] in old else ["baseline has no compatible capability result"]}
|
|
137
|
+
for item in current.get("capabilityResults", [])]
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def _identity(current: Mapping[str, Any], baseline_id: str) -> str:
|
|
141
|
+
return "comparison." + sha256(f"{current['integrity']['contentDigest']}:{baseline_id}".encode()).hexdigest()[:16]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class ComparisonRepository:
|
|
145
|
+
"""Immutable repository for comparison evidence and its policy projection."""
|
|
146
|
+
|
|
147
|
+
def __init__(self, location: str | Path) -> None:
|
|
148
|
+
self.location = Path(location)
|
|
149
|
+
|
|
150
|
+
def persist(self, comparison: Mapping[str, Any], policy_evidence: Mapping[str, Any],
|
|
151
|
+
baseline: Mapping[str, Any]) -> dict[str, Any]:
|
|
152
|
+
if comparison.get("status") not in {"VALID", "BLOCKED"} or not comparison.get("comparisonId"):
|
|
153
|
+
raise BaselineError("comparison requires canonical comparison evidence")
|
|
154
|
+
payload = {"comparison": dict(comparison), "policyEvidence": dict(policy_evidence),
|
|
155
|
+
"qualificationDelta": self._qualification_delta(
|
|
156
|
+
baseline.get("evidence", {}).get("policyEvidence", {}).get("decision", "NOT_APPLICABLE"),
|
|
157
|
+
policy_evidence.get("decision", "BLOCKED"))}
|
|
158
|
+
digest = "sha256:" + sha256(json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
|
|
159
|
+
comparison_id = str(comparison["comparisonId"])
|
|
160
|
+
path = self.location / f"{comparison_id}.json"
|
|
161
|
+
if path.exists():
|
|
162
|
+
existing = self.load(comparison_id)
|
|
163
|
+
if existing["comparisonDigest"] != digest:
|
|
164
|
+
raise BaselineError(f"comparison identity collision: {comparison_id}")
|
|
165
|
+
return existing
|
|
166
|
+
record = {"comparisonId": comparison_id, "baselineId": comparison.get("baselineId"),
|
|
167
|
+
"currentIdentity": comparison.get("currentEvidenceId"),
|
|
168
|
+
"baselineIdentity": comparison.get("baselineEvidenceId"), "createdAt": utc_now(),
|
|
169
|
+
"comparisonDigest": digest, **payload}
|
|
170
|
+
self.location.mkdir(parents=True, exist_ok=True)
|
|
171
|
+
path.write_text(json.dumps(record, sort_keys=True, indent=2) + "\n", encoding="utf-8")
|
|
172
|
+
return record
|
|
173
|
+
|
|
174
|
+
def load(self, reference: str | Path) -> dict[str, Any]:
|
|
175
|
+
path = Path(reference)
|
|
176
|
+
path = path if path.is_absolute() or path.exists() else self.location / f"{reference}.json"
|
|
177
|
+
try:
|
|
178
|
+
record = json.loads(path.read_text(encoding="utf-8"))
|
|
179
|
+
except (OSError, json.JSONDecodeError) as error:
|
|
180
|
+
raise BaselineError(f"cannot load comparison {reference}: {error}") from error
|
|
181
|
+
payload = {key: record.get(key) for key in ("comparison", "policyEvidence", "qualificationDelta")}
|
|
182
|
+
digest = "sha256:" + sha256(json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
|
|
183
|
+
if digest != record.get("comparisonDigest"):
|
|
184
|
+
raise BaselineError("comparison evidence integrity check failed")
|
|
185
|
+
return record
|
|
186
|
+
|
|
187
|
+
def history(self) -> list[dict[str, Any]]:
|
|
188
|
+
return [self.load(path) for path in sorted(self.location.glob("*.json"))] if self.location.is_dir() else []
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def _qualification_delta(baseline: str, current: str) -> dict[str, str]:
|
|
192
|
+
# A baseline without applicable policy evidence is neutral; a newly
|
|
193
|
+
# applicable passing policy is not a qualification regression.
|
|
194
|
+
rank = {"NOT_APPLICABLE": 1, "PASS": 1, "PASS_WITH_WARNINGS": 2, "FAIL": 3, "BLOCKED": 4}
|
|
195
|
+
direction = "REGRESSED" if rank.get(current, 4) > rank.get(baseline, 4) else "IMPROVED" if rank.get(current, 4) < rank.get(baseline, 4) else "UNCHANGED"
|
|
196
|
+
return {"baselineDecision": baseline, "currentDecision": current, "direction": direction}
|