technical-debt-engine-runtime 1.0.0__tar.gz → 1.0.1__tar.gz

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.
Files changed (79) hide show
  1. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/PKG-INFO +1 -1
  2. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/pyproject.toml +1 -1
  3. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_cli/main.py +1 -1
  4. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/dependency_health.py +63 -18
  5. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/execution.py +2 -1
  6. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/policies/generation-1.json +1 -1
  7. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/runtime.py +1 -1
  8. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/technical_debt_engine_runtime.egg-info/PKG-INFO +1 -1
  9. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_cli.py +2 -2
  10. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_coverage.py +1 -1
  11. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_dependency_health.py +21 -1
  12. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_public_policy_configuration.py +2 -2
  13. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_runtime.py +1 -1
  14. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/README.md +0 -0
  15. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/setup.cfg +0 -0
  16. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_cli/__init__.py +0 -0
  17. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/__init__.py +0 -0
  18. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/analyzer_discovery.py +0 -0
  19. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/assessment.py +0 -0
  20. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/baseline.py +0 -0
  21. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/code_size.py +0 -0
  22. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/complexity.py +0 -0
  23. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/configuration.py +0 -0
  24. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/coverage.py +0 -0
  25. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/differential.py +0 -0
  26. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/docker_artifact.py +0 -0
  27. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/evidence_store.py +0 -0
  28. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/maintainability.py +0 -0
  29. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/models.py +0 -0
  30. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/policies/__init__.py +0 -0
  31. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/policy.py +0 -0
  32. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/profiles/__init__.py +0 -0
  33. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/profiles/minimal.json +0 -0
  34. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/profiles/standard.json +0 -0
  35. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/query.py +0 -0
  36. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/registries.py +0 -0
  37. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/release_bundle.py +0 -0
  38. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/release_candidate.py +0 -0
  39. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/release_certification.py +0 -0
  40. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/release_publication.py +0 -0
  41. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/release_qualification.py +0 -0
  42. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/repository_qualification.py +0 -0
  43. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/runtime_qualification.py +0 -0
  44. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/schemas/__init__.py +0 -0
  45. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/schemas/assessment-decision-evidence.json +0 -0
  46. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/schemas/assessment-evidence.json +0 -0
  47. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/schemas/capability-evidence.json +0 -0
  48. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/schemas/common.schema.json +0 -0
  49. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/schemas/differential-evidence.json +0 -0
  50. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/schemas/policy-evidence.json +0 -0
  51. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/schemas/repository-qualification-evidence.json +0 -0
  52. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/software_assurance.py +0 -0
  53. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/trend.py +0 -0
  54. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/tde_runtime/trusted_delivery.py +0 -0
  55. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/technical_debt_engine_runtime.egg-info/SOURCES.txt +0 -0
  56. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/technical_debt_engine_runtime.egg-info/dependency_links.txt +0 -0
  57. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/technical_debt_engine_runtime.egg-info/entry_points.txt +0 -0
  58. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/technical_debt_engine_runtime.egg-info/requires.txt +0 -0
  59. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/src/technical_debt_engine_runtime.egg-info/top_level.txt +0 -0
  60. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_baseline.py +0 -0
  61. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_build_reproducibility.py +0 -0
  62. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_capability_platform.py +0 -0
  63. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_code_size.py +0 -0
  64. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_complexity.py +0 -0
  65. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_docker_artifact.py +0 -0
  66. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_evidence_store.py +0 -0
  67. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_execution.py +0 -0
  68. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_policy.py +0 -0
  69. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_public_artifacts.py +0 -0
  70. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_query.py +0 -0
  71. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_release_bundle.py +0 -0
  72. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_release_candidate.py +0 -0
  73. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_release_certification.py +0 -0
  74. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_release_publication.py +0 -0
  75. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_release_qualification.py +0 -0
  76. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_runtime_qualification.py +0 -0
  77. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_software_assurance.py +0 -0
  78. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_trend.py +0 -0
  79. {technical_debt_engine_runtime-1.0.0 → technical_debt_engine_runtime-1.0.1}/tests/test_trusted_delivery.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: technical-debt-engine-runtime
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Technical Debt Engine runtime foundation API
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: radon==6.0.1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "technical-debt-engine-runtime"
7
- version = "1.0.0"
7
+ version = "1.0.1"
8
8
  description = "Technical Debt Engine runtime foundation API"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = ["radon==6.0.1"]
@@ -29,7 +29,7 @@ from tde_runtime.repository_qualification import (QualificationRegistry,
29
29
  from tde_runtime.differential import AssessmentBaselineRegistry, DifferentialEngine, DifferentialError
30
30
 
31
31
 
32
- CLI_VERSION = "1.0.0"
32
+ CLI_VERSION = "1.0.1"
33
33
  GENERATION = "1"
34
34
 
35
35
 
@@ -11,7 +11,7 @@ import re
11
11
  import shutil
12
12
  import subprocess
13
13
  import tomllib
14
- from typing import Any
14
+ from typing import Any, Mapping
15
15
  import xml.etree.ElementTree as ET
16
16
 
17
17
 
@@ -21,13 +21,13 @@ CAPABILITY_ID = "dependency_health"
21
21
  CAPABILITY_VERSION = "1.0.0"
22
22
 
23
23
 
24
- def analyze(root: Path, timeout: int) -> dict[str, Any]:
24
+ def analyze(root: Path, timeout: int, settings: Mapping[str, Any] | None = None) -> dict[str, Any]:
25
25
  """Normalize only package ecosystems actually present in a repository."""
26
26
  records = []
27
27
  if (root / "package.json").is_file(): records.append(_npm(root, timeout))
28
28
  python = _python(root, timeout)
29
29
  if python is not None: records.append(python)
30
- nuget = _nuget(root, timeout)
30
+ nuget = _nuget(root, timeout, settings)
31
31
  if nuget is not None: records.append(nuget)
32
32
  if (root / "platformio.ini").is_file(): records.append(_platformio(root, timeout))
33
33
  if (root / "Package.swift").is_file(): records.append(_swift(root, timeout))
@@ -88,35 +88,69 @@ def _python(root: Path, timeout: int) -> dict[str, Any] | None:
88
88
  return _record("PyPI", "pip", sorted(direct), None, None, outdated, {"id": "pip", "version": version}, "".join(raw), limitations)
89
89
 
90
90
 
91
- def _nuget(root: Path, timeout: int) -> dict[str, Any] | None:
91
+ def _nuget(root: Path, timeout: int, settings: Mapping[str, Any] | None = None) -> dict[str, Any] | None:
92
92
  projects = _manifest_files(root, "*.csproj")
93
93
  if not projects: return None
94
- direct = []
95
94
  for project in projects:
96
- try: direct.extend(item.attrib["Include"] for item in ET.parse(project).iter() if item.tag.endswith("PackageReference") and "Include" in item.attrib)
95
+ try: _project_frameworks(project)
97
96
  except (ET.ParseError, OSError) as error: return _blocked("NuGet", "dotnet", str(error))
97
+ direct = _nuget_direct_dependencies(projects)
98
98
  dotnet = shutil.which("dotnet")
99
99
  if not dotnet: return _record("NuGet", "dotnet", sorted(set(direct)), None, None, None, {"id": "dotnet", "version": "UNAVAILABLE"}, "", [_limitation("dependency_health.dotnet.unavailable", "dotnet was not found on PATH")])
100
+ try: selected_framework = _nuget_framework(settings)
101
+ except ValueError as error: return _configuration_failed("NuGet", "dotnet", str(error))
100
102
  raw, outdated, transitive, limitations = [], [], [], []
101
103
  for project in projects:
102
104
  try:
103
- completed = subprocess.run([dotnet, "package", "list", "--project", str(project), "--outdated", "--include-transitive", "--format", "json"], cwd=root, capture_output=True, text=True, timeout=timeout, check=False)
104
- raw.append(completed.stdout); payload = json.loads(completed.stdout or "{}")
105
- problems = payload.get("problems", [])
106
- errors = [item.get("text", "NuGet analysis failed") for item in problems if item.get("level") == "error"]
107
- if completed.returncode != 0 or errors:
108
- detail = "; ".join(errors) or completed.stderr.strip() or f"dotnet exited with status {completed.returncode}"
109
- return _analysis_failed("NuGet", "dotnet", f"{project}: {detail}")
110
- for project_data in payload.get("projects", []):
111
- for framework in project_data.get("frameworks", []):
112
- outdated.extend(item["id"] for item in framework.get("topLevelPackages", []) if item.get("latestVersion"))
113
- outdated.extend(item["id"] for item in framework.get("transitivePackages", []) if item.get("latestVersion"))
114
- transitive.extend(item["id"] for item in framework.get("transitivePackages", []) if item.get("id"))
105
+ output, project_outdated, project_transitive = _nuget_project(dotnet, root, project, selected_framework, timeout)
106
+ raw.append(output); outdated.extend(project_outdated); transitive.extend(project_transitive)
107
+ except ValueError as error:
108
+ return _configuration_failed("NuGet", "dotnet", str(error))
109
+ except _NugetAnalysisError as error:
110
+ return _analysis_failed("NuGet", "dotnet", f"{project}: {error}")
115
111
  except (OSError, subprocess.SubprocessError, json.JSONDecodeError) as error:
116
112
  outdated = None; transitive = None; limitations.append(_limitation("dependency_health.nuget.outdated.unavailable", str(error)))
117
113
  return _record("NuGet", "dotnet", sorted(set(direct)), None if transitive is None else sorted(set(transitive) - set(direct)), None, None if outdated is None else sorted(set(outdated)), {"id": "dotnet", "version": _version(dotnet, root, timeout)}, "".join(raw), limitations)
118
114
 
119
115
 
116
+ class _NugetAnalysisError(Exception):
117
+ """Native package-manager output cannot provide canonical evidence."""
118
+
119
+
120
+ def _nuget_direct_dependencies(projects: list[Path]) -> list[str]:
121
+ return [item.attrib["Include"] for project in projects for item in ET.parse(project).iter()
122
+ if item.tag.endswith("PackageReference") and "Include" in item.attrib]
123
+
124
+
125
+ def _nuget_framework(settings: Mapping[str, Any] | None) -> str | None:
126
+ value = (settings or {}).get("nugetFramework")
127
+ if value is None: return None
128
+ if not isinstance(value, str) or not value.strip():
129
+ raise ValueError("dependency_health.nugetFramework must be a non-empty string")
130
+ return value.strip()
131
+
132
+
133
+ def _nuget_project(dotnet: str, root: Path, project: Path, framework: str | None, timeout: int) -> tuple[str, list[str], list[str]]:
134
+ if framework and (declared := _project_frameworks(project)) and framework not in declared:
135
+ raise ValueError(f"{project}: configured nugetFramework '{framework}' is not declared by the project")
136
+ command = [dotnet, "package", "list", "--project", str(project), "--outdated", "--include-transitive", "--format", "json"]
137
+ if framework: command.extend(["--framework", framework])
138
+ completed = subprocess.run(command, cwd=root, capture_output=True, text=True, timeout=timeout, check=False)
139
+ payload = json.loads(completed.stdout or "{}")
140
+ errors = [item.get("text", "NuGet analysis failed") for item in payload.get("problems", []) if item.get("level") == "error"]
141
+ if completed.returncode != 0 or errors:
142
+ raise _NugetAnalysisError("; ".join(errors) or completed.stderr.strip() or f"dotnet exited with status {completed.returncode}")
143
+ return completed.stdout, *_nuget_packages(payload)
144
+
145
+
146
+ def _nuget_packages(payload: Mapping[str, Any]) -> tuple[list[str], list[str]]:
147
+ frameworks = [framework for project in payload.get("projects", []) for framework in project.get("frameworks", [])]
148
+ outdated = [item["id"] for framework in frameworks for key in ("topLevelPackages", "transitivePackages")
149
+ for item in framework.get(key, []) if item.get("latestVersion")]
150
+ transitive = [item["id"] for framework in frameworks for item in framework.get("transitivePackages", []) if item.get("id")]
151
+ return outdated, transitive
152
+
153
+
120
154
  def _platformio(root: Path, timeout: int) -> dict[str, Any]:
121
155
  direct = []
122
156
  lines = (root / "platformio.ini").read_text(encoding="utf-8").splitlines(); in_deps = False
@@ -148,6 +182,7 @@ def _record(ecosystem: str, manager: str, direct: list[str], transitive: list[st
148
182
 
149
183
  def _blocked(ecosystem: str, manager: str, reason: str) -> dict[str, Any]: return _record(ecosystem, manager, [], None, None, None, {"id": manager, "version": "UNAVAILABLE"}, "", [_limitation("dependency_health.manifest.invalid", reason, True)])
150
184
  def _analysis_failed(ecosystem: str, manager: str, reason: str) -> dict[str, Any]: return _record(ecosystem, manager, [], None, None, None, {"id": manager, "version": "UNAVAILABLE"}, "", [_limitation(f"dependency_health.{manager}.analysis.failed", reason, True)])
185
+ def _configuration_failed(ecosystem: str, manager: str, reason: str) -> dict[str, Any]: return _record(ecosystem, manager, [], None, None, None, {"id": manager, "version": "UNAVAILABLE"}, "", [_limitation("dependency_health.nuget.configuration.invalid", reason, True)])
151
186
  def _unavailable(reason: str) -> dict[str, Any]: return {"status": "VALID", "ecosystems": [], "rawOutput": "", "rawOutputHash": "sha256:" + sha256(b"").hexdigest(), "limitations": [_limitation("dependency_health.ecosystem.unavailable", reason)] , "available": False}
152
187
  def _limitation(identifier: str, description: str, blocking: bool = False) -> dict[str, Any]: return {"id": identifier, "description": description, "cause": "dependency health", "blocking": blocking}
153
188
  def _version(executable: str, root: Path, timeout: int) -> str:
@@ -169,5 +204,15 @@ def _manifest_files(root: Path, pattern: str) -> list[Path]:
169
204
  return sorted(manifests)
170
205
 
171
206
 
207
+ def _project_frameworks(project: Path) -> set[str]:
208
+ """Return target frameworks declared by a project without evaluating it."""
209
+ document = ET.parse(project)
210
+ values = []
211
+ for element in document.iter():
212
+ if element.tag.endswith(("TargetFramework", "TargetFrameworks")) and element.text:
213
+ values.extend(item.strip() for item in element.text.split(";") if item.strip())
214
+ return set(values)
215
+
216
+
172
217
  def _ignored_directory(name: str) -> bool:
173
218
  return name in {".git", "node_modules", "dist", "build", ".venv", "venv", ".build", ".swiftpm", ".pio", ".release", ".public-release"} or name.startswith(".xcode-derived")
@@ -121,7 +121,8 @@ class CapabilityExecutionEngine:
121
121
  return self._blocked(COVERAGE_CAPABILITY_ID, COVERAGE_CAPABILITY_VERSION, [COVERAGE_ADAPTER_ID], result["limitations"], duration, result["status"])
122
122
  return self._coverage_result(context, result, duration)
123
123
  if identifier == DEPENDENCY_CAPABILITY_ID and selected_adapter == DEPENDENCY_ADAPTER_ID:
124
- result = analyze_dependencies(context.repository_root, timeout)
124
+ settings = context.execution_options.get("capabilities", {}).get(DEPENDENCY_CAPABILITY_ID, {})
125
+ result = analyze_dependencies(context.repository_root, timeout, settings)
125
126
  duration = int((perf_counter() - started) * 1000)
126
127
  if result["status"] != "VALID":
127
128
  return self._blocked(DEPENDENCY_CAPABILITY_ID, DEPENDENCY_CAPABILITY_VERSION, [DEPENDENCY_ADAPTER_ID], result["limitations"], duration)
@@ -6,7 +6,7 @@
6
6
  "description": "Default, language-independent Generation 1 qualification policy.",
7
7
  "supportedCapabilities": ["code_size", "complexity", "coverage", "dependency_health"],
8
8
  "supportedSchemas": ["1.0.0"],
9
- "supportedRuntimeVersions": ["0.2.0", "1.0.0rc1", "1.0.0rc2", "1.0.0rc3", "1.0.0"],
9
+ "supportedRuntimeVersions": ["0.2.0", "1.0.0rc1", "1.0.0rc2", "1.0.0rc3", "1.0.0", "1.0.1"],
10
10
  "rules": [
11
11
  {"id": "code_size.repository_lines", "type": "threshold", "capability": "code_size", "metric": "code_size.code_lines", "operator": "greater_than", "threshold": {"warning": 25000, "blocking": 50000}, "severity": {"warning": "WARNING", "blocking": "BLOCKING"}, "enabled": true, "rationale": "Large repositories require deliberate decomposition without treating established product repositories as a blocking condition."},
12
12
  {"id": "complexity.maximum", "type": "threshold", "capability": "complexity", "metric": "complexity.cyclomatic.maximum", "operator": "greater_than", "threshold": {"warning": 15, "blocking": 30}, "severity": {"warning": "WARNING", "blocking": "BLOCKING"}, "enabled": true, "rationale": "High cyclomatic complexity impedes safe change; the blocking threshold accommodates the current measured maximum while retaining a warning signal."},
@@ -20,7 +20,7 @@ from .execution import CapabilityExecutionEngine
20
20
  from .runtime_qualification import RuntimeQualificationEngine
21
21
  from .schemas import SchemaRegistry
22
22
 
23
- RUNTIME_VERSION = "1.0.0"
23
+ RUNTIME_VERSION = "1.0.1"
24
24
  EVIDENCE_SCHEMA_VERSION = "1.0.0"
25
25
 
26
26
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: technical-debt-engine-runtime
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Technical Debt Engine runtime foundation API
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: radon==6.0.1
@@ -41,8 +41,8 @@ class CliFoundationTests(unittest.TestCase):
41
41
  code, output = self.invoke("--format", "json", "--version")
42
42
  self.assertEqual(ExitCode.SUCCESS, code)
43
43
  version = json.loads(output)
44
- self.assertEqual("1.0.0", version["cliVersion"])
45
- self.assertEqual("1.0.0", version["runtimeVersion"])
44
+ self.assertEqual("1.0.1", version["cliVersion"])
45
+ self.assertEqual("1.0.1", version["runtimeVersion"])
46
46
  self.assertEqual("1.0.0", version["schemaVersion"])
47
47
  self.assertEqual("1", version["generation"])
48
48
 
@@ -101,7 +101,7 @@ class CoverageBlackBoxTests(unittest.TestCase):
101
101
 
102
102
  def test_policy_threshold_uses_coverage_without_runtime_specific_policy_logic(self) -> None:
103
103
  (self.root / "coverage.xml").write_text(COBERTURA, encoding="utf-8")
104
- policy = {"identifier": "coverage-policy", "version": "1.0.0", "scope": "repository", "owner": "tests", "description": "coverage threshold", "supportedCapabilities": ["coverage"], "supportedSchemas": ["1.0.0"], "supportedRuntimeVersions": ["1.0.0"], "rules": [{"id": "minimum-line-coverage", "type": "threshold", "capability": "coverage", "metric": "coverage.line_coverage", "operator": "less_than", "threshold": {"warning": 80, "blocking": 60}, "severity": {"warning": "WARNING", "blocking": "BLOCKING"}, "enabled": True, "rationale": "coverage floor"}]}
104
+ policy = {"identifier": "coverage-policy", "version": "1.0.0", "scope": "repository", "owner": "tests", "description": "coverage threshold", "supportedCapabilities": ["coverage"], "supportedSchemas": ["1.0.0"], "supportedRuntimeVersions": ["1.0.1"], "rules": [{"id": "minimum-line-coverage", "type": "threshold", "capability": "coverage", "metric": "coverage.line_coverage", "operator": "less_than", "threshold": {"warning": 80, "blocking": 60}, "severity": {"warning": "WARNING", "blocking": "BLOCKING"}, "enabled": True, "rationale": "coverage floor"}]}
105
105
  path = self.root / "policy.json"; path.write_text(json.dumps(policy), encoding="utf-8")
106
106
  code, result = self.invoke("--policy", str(path), "assess", "--capability", "coverage", str(self.root))
107
107
  self.assertEqual(ExitCode.FAILED, code)
@@ -71,7 +71,7 @@ class DependencyHealthBlackBoxTests(unittest.TestCase):
71
71
 
72
72
  def test_unknown_dependency_and_policy_are_evaluated(self) -> None:
73
73
  self.project(missing=True); self.npm({})
74
- policy = {"identifier": "dependency-policy", "version": "1.0.0", "scope": "repository", "owner": "tests", "description": "dependency threshold", "supportedCapabilities": ["dependency_health"], "supportedSchemas": ["1.0.0"], "supportedRuntimeVersions": ["1.0.0"], "rules": [{"id": "unknown", "type": "threshold", "capability": "dependency_health", "metric": "dependency_health.unknown_dependencies", "operator": "greater_than", "threshold": {"warning": 1, "blocking": 1}, "severity": {"warning": "WARNING", "blocking": "BLOCKING"}, "enabled": True, "rationale": "unknown dependencies"}]}
74
+ policy = {"identifier": "dependency-policy", "version": "1.0.0", "scope": "repository", "owner": "tests", "description": "dependency threshold", "supportedCapabilities": ["dependency_health"], "supportedSchemas": ["1.0.0"], "supportedRuntimeVersions": ["1.0.1"], "rules": [{"id": "unknown", "type": "threshold", "capability": "dependency_health", "metric": "dependency_health.unknown_dependencies", "operator": "greater_than", "threshold": {"warning": 1, "blocking": 1}, "severity": {"warning": "WARNING", "blocking": "BLOCKING"}, "enabled": True, "rationale": "unknown dependencies"}]}
75
75
  path = self.root / "policy.json"; path.write_text(json.dumps(policy), encoding="utf-8")
76
76
  code, result = self.invoke("--policy", str(path), "assess", "--capability", "dependency_health", str(self.root))
77
77
  self.assertEqual(ExitCode.FAILED, code)
@@ -105,6 +105,26 @@ class DependencyHealthBlackBoxTests(unittest.TestCase):
105
105
  self.assertEqual("NuGet", ecosystem["ecosystem"])
106
106
  self.assertEqual(["Example", "Transit"], ecosystem["outdatedDependencies"])
107
107
 
108
+ def test_nuget_uses_configured_declared_target_framework(self) -> None:
109
+ project = self.root / "app.csproj"
110
+ project.write_text('<Project><PropertyGroup><TargetFrameworks>net10.0-maccatalyst;net10.0-windows10.0.19041.0</TargetFrameworks></PropertyGroup><ItemGroup><PackageReference Include="Example" Version="1.0.0" /></ItemGroup></Project>', encoding="utf-8")
111
+ (self.root / ".tde.yml").write_text("capabilities:\n dependency_health:\n nugetFramework: net10.0-windows10.0.19041.0\n", encoding="utf-8")
112
+ payload = {"projects": [{"frameworks": [{"topLevelPackages": [{"id": "Example", "latestVersion": "2.0.0"}], "transitivePackages": []}]}]}
113
+ self.tool("dotnet", 'if [ "$1" = "--version" ]; then echo "10.0"; else case "$*" in *"--framework net10.0-windows10.0.19041.0"*) printf "%s\\n" \'' + json.dumps(payload) + '\';; *) echo "wrong framework command: $*" >&2; exit 1;; esac; fi\n')
114
+ code, result = self.assess()
115
+ self.assertEqual(ExitCode.SUCCESS, code)
116
+ ecosystem = result["evidence"]["adapterResults"][0]["evidence"]["ecosystems"][0]
117
+ self.assertEqual(["Example"], ecosystem["outdatedDependencies"])
118
+
119
+ def test_nuget_rejects_framework_not_declared_by_project(self) -> None:
120
+ (self.root / "app.csproj").write_text('<Project><PropertyGroup><TargetFramework>net10.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include="Example" Version="1.0.0" /></ItemGroup></Project>', encoding="utf-8")
121
+ (self.root / ".tde.yml").write_text("capabilities:\n dependency_health:\n nugetFramework: net10.0-windows10.0.19041.0\n", encoding="utf-8")
122
+ self.tool("dotnet", 'if [ "$1" = "--version" ]; then echo "10.0"; else exit 99; fi\n')
123
+ code, result = self.assess()
124
+ self.assertEqual(ExitCode.BLOCKED, code)
125
+ self.assertEqual("BLOCKED", result["status"])
126
+ self.assertIn("validated canonical evidence", result["reason"])
127
+
108
128
  def test_nuget_restore_failure_fails_closed(self) -> None:
109
129
  (self.root / "app.csproj").write_text('<Project><ItemGroup><PackageReference Include="Example" Version="1.0.0" /></ItemGroup></Project>', encoding="utf-8")
110
130
  payload = {"version": 1, "problems": [{"text": "Restore failed", "level": "error"}]}
@@ -51,7 +51,7 @@ class PublicPolicyConfigurationTests(unittest.TestCase):
51
51
  "identifier": "example.code-size", "version": "2026.1", "scope": "repository",
52
52
  "owner": "example", "description": "Example organization policy.",
53
53
  "supportedCapabilities": ["code_size"], "supportedSchemas": ["1.0.0"],
54
- "supportedRuntimeVersions": ["1.0.0"],
54
+ "supportedRuntimeVersions": ["1.0.1"],
55
55
  "rules": [{"id": "example.code-size.lines", "type": "threshold", "capability": "code_size",
56
56
  "metric": "code_size.code_lines", "operator": "greater_than",
57
57
  "threshold": {"warning": threshold, "blocking": threshold + 1000},
@@ -93,7 +93,7 @@ class PublicPolicyConfigurationTests(unittest.TestCase):
93
93
  *evidence["assessment"]["capabilityExecutions"]]:
94
94
  self.assertEqual("1.0.0", item["schema"]["version"])
95
95
  self.assertEqual("1", item["schema"]["compatibilityVersion"])
96
- self.assertEqual("1.0.0", item["schema"]["runtimeVersion"])
96
+ self.assertEqual("1.0.1", item["schema"]["runtimeVersion"])
97
97
  record = next((location / "evidence").glob("*.json"))
98
98
  persisted = json.loads(record.read_text(encoding="utf-8"))
99
99
  persisted["evidence"]["policyEvidence"]["schema"]["version"] = "999.0.0"
@@ -61,7 +61,7 @@ class RuntimeFoundationTests(unittest.TestCase):
61
61
 
62
62
  def test_context_contains_canonical_runtime_values(self) -> None:
63
63
  result = Runtime().execute(self.root)
64
- self.assertEqual("1.0.0", result.context.runtime_version)
64
+ self.assertEqual("1.0.1", result.context.runtime_version)
65
65
  self.assertEqual("1.0.0", result.context.schema_version)
66
66
  self.assertTrue(result.context.execution_id.startswith("execution."))
67
67
  self.assertEqual("content_digest", result.context.candidate["identityType"])