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 ADDED
@@ -0,0 +1,5 @@
1
+ """Thin command-line presentation layer for the TDE runtime."""
2
+
3
+ from .main import main
4
+
5
+ __all__ = ["main"]
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())
@@ -0,0 +1,6 @@
1
+ """Public, CLI-independent API for the Technical Debt Engine runtime foundation."""
2
+
3
+ from .configuration import RuntimeConfiguration
4
+ from .runtime import Runtime
5
+
6
+ __all__ = ["Runtime", "RuntimeConfiguration"]
@@ -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}