From d3f5d52737c170127f3b6d89917d022e67ac6c37 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 00:14:37 +0900 Subject: [PATCH 01/16] test(quality): require explicit function and line coverage evidence --- tests/test_coverage_metric_evidence.py | 140 +++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 tests/test_coverage_metric_evidence.py diff --git a/tests/test_coverage_metric_evidence.py b/tests/test_coverage_metric_evidence.py new file mode 100644 index 0000000..600a697 --- /dev/null +++ b/tests/test_coverage_metric_evidence.py @@ -0,0 +1,140 @@ +"""Regression contract for explicit owned-production coverage evidence. + +Statement/branch coverage remains the normative 100% gate. These tests add a +machine-readable human-visible line/function metric so reviewers can see what +the aggregate percentage represents instead of inferring it from a summary. +""" + +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + +REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +REPORTER = REPOSITORY_ROOT / "scripts" / "ci" / "report_coverage_metrics.py" +CI_WORKFLOW = REPOSITORY_ROOT / ".github" / "workflows" / "ci.yml" + + +def _write_fixture( + tmp_path: Path, + *, + executed_lines: list[int], + missing_lines: list[int], +) -> tuple[Path, Path]: + """Create one tiny owned source tree plus coverage.py-style JSON evidence.""" + source_root = tmp_path / "src" / "egressweave" + source_root.mkdir(parents=True) + source_file = source_root / "sample.py" + source_file.write_text( + "def choose(value: bool) -> int:\n" + " if value:\n" + " return 1\n" + " return 0\n" + "\n\n" + "async def answer() -> int:\n" + " return 42\n", + encoding="utf-8", + ) + coverage_json = tmp_path / "coverage.json" + measured = len(executed_lines) + len(missing_lines) + coverage_json.write_text( + json.dumps( + { + "meta": {"branch_coverage": True, "version": "fixture"}, + "files": { + str(source_file): { + "executed_lines": executed_lines, + "missing_lines": missing_lines, + "excluded_lines": [], + "summary": { + "covered_lines": len(executed_lines), + "num_statements": measured, + "missing_lines": len(missing_lines), + }, + } + }, + } + ), + encoding="utf-8", + ) + return source_root, coverage_json + + +def _run_reporter(source_root: Path, coverage_json: Path) -> subprocess.CompletedProcess[str]: + """Run the repository reporter exactly as CI will invoke it.""" + return subprocess.run( + [ + sys.executable, + str(REPORTER), + "--coverage-json", + str(coverage_json), + "--source-root", + str(source_root), + ], + cwd=REPOSITORY_ROOT, + check=False, + capture_output=True, + text=True, + ) + + +def test_reporter_exposes_exact_line_and_function_body_metrics(tmp_path: Path) -> None: + """A fully covered source tree reports explicit 100% line/function evidence.""" + source_root, coverage_json = _write_fixture( + tmp_path, + executed_lines=[1, 2, 3, 4, 7, 8], + missing_lines=[], + ) + + result = _run_reporter(source_root, coverage_json) + + assert result.returncode == 0, result.stderr + assert "line=100.00% (6/6)" in result.stdout + assert "function=100.00% (2/2)" in result.stdout + + +def test_reporter_fails_closed_when_a_function_body_has_a_missing_line( + tmp_path: Path, +) -> None: + """A missing executable function-body line must make the evidence non-passing.""" + source_root, coverage_json = _write_fixture( + tmp_path, + executed_lines=[1, 2, 3, 7, 8], + missing_lines=[4], + ) + + result = _run_reporter(source_root, coverage_json) + + assert result.returncode == 1 + assert "line=83.33% (5/6)" in result.stdout + assert "function=50.00% (1/2)" in result.stdout + assert "sample.py:choose" in result.stderr + + +def test_reporter_rejects_incomplete_owned_source_coverage(tmp_path: Path) -> None: + """Every owned Python source file must be represented by coverage evidence.""" + source_root, coverage_json = _write_fixture( + tmp_path, + executed_lines=[1, 2, 3, 4, 7, 8], + missing_lines=[], + ) + (source_root / "unreported.py").write_text("def hidden() -> int:\n return 1\n", encoding="utf-8") + + result = _run_reporter(source_root, coverage_json) + + assert result.returncode == 2 + assert "coverage data missing owned source files" in result.stderr + assert "unreported.py" in result.stderr + + +def test_ci_exposes_the_exact_metrics_after_coverage_collection() -> None: + """The hosted matrix must publish the reporter output on every exact head.""" + workflow = CI_WORKFLOW.read_text(encoding="utf-8") + + assert "coverage json -o coverage.json" in workflow + assert ( + "python scripts/ci/report_coverage_metrics.py --coverage-json coverage.json " + "--source-root src/egressweave" + ) in workflow From 95eb7777c05ddec622fe9def3177f3101241dfa6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 00:17:54 +0900 Subject: [PATCH 02/16] feat(quality): report exact function and line coverage metrics --- scripts/ci/report_coverage_metrics.py | 228 ++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 scripts/ci/report_coverage_metrics.py diff --git a/scripts/ci/report_coverage_metrics.py b/scripts/ci/report_coverage_metrics.py new file mode 100644 index 0000000..a84c18f --- /dev/null +++ b/scripts/ci/report_coverage_metrics.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +"""Expose exact owned-production line and function-body coverage metrics. + +Coverage.py remains the source of statement and branch truth. This helper reads +its JSON output, verifies that every owned Python source file is represented, +and adds a conservative function-body view: a function counts as covered only +when every measured executable line in its body executed. The helper adds no +runtime dependency and exits non-zero whenever the evidence is incomplete or +anything measurable falls below 100%. +""" + +from __future__ import annotations + +import argparse +import ast +import json +import sys +from pathlib import Path +from typing import Any + +_MAX_COVERAGE_JSON_BYTES = 16 * 1024 * 1024 + + +class CoverageEvidenceError(ValueError): + """Describe malformed or incomplete coverage evidence that cannot be trusted.""" + + +def _parse_args() -> argparse.Namespace: + """Parse the two explicit filesystem inputs used by hosted CI.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--coverage-json", required=True, type=Path) + parser.add_argument("--source-root", required=True, type=Path) + return parser.parse_args() + + +def _load_json(path: Path) -> dict[str, Any]: + """Load one bounded regular JSON file and require an object at the root.""" + resolved = path.resolve(strict=True) + if not resolved.is_file(): + raise CoverageEvidenceError("coverage JSON must be a regular file") + if resolved.stat().st_size > _MAX_COVERAGE_JSON_BYTES: + raise CoverageEvidenceError("coverage JSON exceeds the 16 MiB evidence limit") + try: + payload = json.loads(resolved.read_text(encoding="utf-8")) + except (OSError, UnicodeError, json.JSONDecodeError) as exc: + raise CoverageEvidenceError("coverage JSON is unreadable or malformed") from exc + if type(payload) is not dict: + raise CoverageEvidenceError("coverage JSON root must be an object") + return payload + + +def _line_set(value: object, *, field: str) -> set[int]: + """Validate a coverage.py line-number list without accepting bool subclasses.""" + if type(value) is not list: + raise CoverageEvidenceError(f"{field} must be a list") + lines: set[int] = set() + for item in value: + if type(item) is not int or item <= 0: + raise CoverageEvidenceError(f"{field} must contain positive exact integers") + if item in lines: + raise CoverageEvidenceError(f"{field} contains a duplicate line number") + lines.add(item) + return lines + + +def _is_within(path: Path, root: Path) -> bool: + """Return whether one resolved path is contained by the resolved source root.""" + try: + path.relative_to(root) + except ValueError: + return False + return True + + +def _owned_coverage_records( + payload: dict[str, Any], + *, + source_root: Path, +) -> dict[Path, dict[str, Any]]: + """Map every owned source path to its unique coverage.py file record.""" + files = payload.get("files") + if type(files) is not dict: + raise CoverageEvidenceError("coverage JSON files must be an object") + + records: dict[Path, dict[str, Any]] = {} + for raw_path, raw_record in files.items(): + if type(raw_path) is not str or type(raw_record) is not dict: + raise CoverageEvidenceError("coverage file records must map strings to objects") + candidate = Path(raw_path) + if not candidate.is_absolute(): + candidate = Path.cwd() / candidate + resolved = candidate.resolve(strict=False) + if not _is_within(resolved, source_root): + continue + if resolved in records: + raise CoverageEvidenceError("coverage JSON aliases one owned source file twice") + records[resolved] = raw_record + + owned_files = {path.resolve() for path in source_root.rglob("*.py") if path.is_file()} + if not owned_files: + raise CoverageEvidenceError("source root contains no owned Python source files") + + missing = sorted(owned_files - records.keys()) + if missing: + relative = ", ".join(str(path.relative_to(source_root)) for path in missing[:20]) + suffix = "" if len(missing) <= 20 else f" (+{len(missing) - 20} more)" + raise CoverageEvidenceError( + f"coverage data missing owned source files: {relative}{suffix}" + ) + + unexpected = sorted(records.keys() - owned_files) + if unexpected: + relative = ", ".join(str(path.relative_to(source_root)) for path in unexpected[:20]) + suffix = "" if len(unexpected) <= 20 else f" (+{len(unexpected) - 20} more)" + raise CoverageEvidenceError( + f"coverage data references absent owned source files: {relative}{suffix}" + ) + return records + + +def _function_body_lines(node: ast.FunctionDef | ast.AsyncFunctionDef) -> tuple[int, int]: + """Return the inclusive source span containing a function's executable body.""" + if not node.body: + raise CoverageEvidenceError(f"function {node.name!r} has no syntax body") + start = node.body[0].lineno + end = node.end_lineno + if end is None: + raise CoverageEvidenceError(f"function {node.name!r} has no end-line metadata") + return start, end + + +def _analyse_source( + path: Path, + record: dict[str, Any], + *, + source_root: Path, +) -> tuple[int, int, int, int, list[str]]: + """Return covered/total line and function counts for one owned source file.""" + executed = _line_set(record.get("executed_lines"), field="executed_lines") + missing = _line_set(record.get("missing_lines"), field="missing_lines") + overlap = executed & missing + if overlap: + raise CoverageEvidenceError("executed_lines and missing_lines overlap") + measured = executed | missing + if not measured: + raise CoverageEvidenceError( + f"owned source file has no measured executable lines: {path.relative_to(source_root)}" + ) + + try: + source = path.read_text(encoding="utf-8") + tree = ast.parse(source, filename=str(path)) + except (OSError, UnicodeError, SyntaxError) as exc: + raise CoverageEvidenceError( + f"owned source file is unreadable or syntactically invalid: {path.relative_to(source_root)}" + ) from exc + + function_total = 0 + function_covered = 0 + uncovered: list[str] = [] + for node in ast.walk(tree): + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + continue + start, end = _function_body_lines(node) + measured_body = {line for line in measured if start <= line <= end} + function_total += 1 + if measured_body and measured_body <= executed: + function_covered += 1 + else: + uncovered.append(f"{path.relative_to(source_root)}:{node.name}") + + return len(executed), len(measured), function_covered, function_total, uncovered + + +def _percentage(covered: int, total: int) -> float: + """Return a percentage while refusing an empty denominator.""" + if total <= 0: + raise CoverageEvidenceError("coverage metric denominator must be positive") + return 100.0 * covered / total + + +def main() -> int: + """Validate evidence, print exact metrics, and fail below complete coverage.""" + args = _parse_args() + try: + source_root = args.source_root.resolve(strict=True) + if not source_root.is_dir(): + raise CoverageEvidenceError("source root must be a directory") + payload = _load_json(args.coverage_json) + records = _owned_coverage_records(payload, source_root=source_root) + + line_covered = 0 + line_total = 0 + function_covered = 0 + function_total = 0 + uncovered_functions: list[str] = [] + for path in sorted(records): + metrics = _analyse_source(path, records[path], source_root=source_root) + line_covered += metrics[0] + line_total += metrics[1] + function_covered += metrics[2] + function_total += metrics[3] + uncovered_functions.extend(metrics[4]) + + line_percentage = _percentage(line_covered, line_total) + function_percentage = _percentage(function_covered, function_total) + except (OSError, CoverageEvidenceError) as exc: + print(f"coverage-evidence-error: {exc}", file=sys.stderr) + return 2 + + print( + "coverage-metrics: " + f"line={line_percentage:.2f}% ({line_covered}/{line_total}) " + f"function={function_percentage:.2f}% ({function_covered}/{function_total})" + ) + if line_covered != line_total or function_covered != function_total: + if uncovered_functions: + preview = ", ".join(uncovered_functions[:20]) + suffix = "" if len(uncovered_functions) <= 20 else ( + f" (+{len(uncovered_functions) - 20} more)" + ) + print(f"uncovered function bodies: {preview}{suffix}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From a425e31d50e8a9069d2132f2de779ccfbc70005b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 00:18:46 +0900 Subject: [PATCH 03/16] ci(quality): publish exact function and line coverage metrics --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9304c3..c2859fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,10 @@ jobs: - run: ruff check . - run: coverage run -m pytest -q - run: coverage report -m + - name: Expose exact line and function coverage metrics + run: | + coverage json -o coverage.json + python scripts/ci/report_coverage_metrics.py --coverage-json coverage.json --source-root src/egressweave - run: python scripts/ci/hourly_product_guard.py self-test - run: python -m compileall -q src tests scripts From 90c402e55bff5123d9d8d808fbde6213993f97ef Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 00:21:47 +0900 Subject: [PATCH 04/16] fix(quality): keep coverage reporter non-executable --- scripts/ci/report_coverage_metrics.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/ci/report_coverage_metrics.py b/scripts/ci/report_coverage_metrics.py index a84c18f..f130b3f 100644 --- a/scripts/ci/report_coverage_metrics.py +++ b/scripts/ci/report_coverage_metrics.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 """Expose exact owned-production line and function-body coverage metrics. Coverage.py remains the source of statement and branch truth. This helper reads From a41b7b5268f0cde66a3b7b668d73673a5481df01 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 01:18:09 +0900 Subject: [PATCH 05/16] test(quality): define measurable function coverage --- tests/test_coverage_metric_evidence.py | 49 ++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/test_coverage_metric_evidence.py b/tests/test_coverage_metric_evidence.py index 600a697..8473de8 100644 --- a/tests/test_coverage_metric_evidence.py +++ b/tests/test_coverage_metric_evidence.py @@ -62,6 +62,43 @@ def _write_fixture( return source_root, coverage_json +def _write_noop_fixture(tmp_path: Path) -> tuple[Path, Path]: + """Create one measured function plus a docstring-only no-op function.""" + source_root = tmp_path / "src" / "egressweave" + source_root.mkdir(parents=True) + source_file = source_root / "sample.py" + source_file.write_text( + "def measured() -> int:\n" + " return 1\n" + "\n\n" + "def noop() -> None:\n" + " \"\"\"Intentionally has no measurable executable body.\"\"\"\n", + encoding="utf-8", + ) + coverage_json = tmp_path / "coverage.json" + coverage_json.write_text( + json.dumps( + { + "meta": {"branch_coverage": True, "version": "fixture"}, + "files": { + str(source_file): { + "executed_lines": [1, 2, 5], + "missing_lines": [], + "excluded_lines": [], + "summary": { + "covered_lines": 3, + "num_statements": 3, + "missing_lines": 0, + }, + } + }, + } + ), + encoding="utf-8", + ) + return source_root, coverage_json + + def _run_reporter(source_root: Path, coverage_json: Path) -> subprocess.CompletedProcess[str]: """Run the repository reporter exactly as CI will invoke it.""" return subprocess.run( @@ -95,6 +132,18 @@ def test_reporter_exposes_exact_line_and_function_body_metrics(tmp_path: Path) - assert "function=100.00% (2/2)" in result.stdout +def test_reporter_excludes_functions_without_measurable_body_lines(tmp_path: Path) -> None: + """Do not turn a docstring-only no-op into impossible function-coverage debt.""" + source_root, coverage_json = _write_noop_fixture(tmp_path) + + result = _run_reporter(source_root, coverage_json) + + assert result.returncode == 0, result.stderr + assert "line=100.00% (3/3)" in result.stdout + assert "function=100.00% (1/1)" in result.stdout + assert "noop" not in result.stderr + + def test_reporter_fails_closed_when_a_function_body_has_a_missing_line( tmp_path: Path, ) -> None: From 885584ea95d34299c63921719725a12a6f99a70e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 02:05:01 +0900 Subject: [PATCH 06/16] fix(quality): exclude unmeasured function bodies --- scripts/ci/report_coverage_metrics.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/ci/report_coverage_metrics.py b/scripts/ci/report_coverage_metrics.py index f130b3f..34c59dc 100644 --- a/scripts/ci/report_coverage_metrics.py +++ b/scripts/ci/report_coverage_metrics.py @@ -134,7 +134,7 @@ def _analyse_source( *, source_root: Path, ) -> tuple[int, int, int, int, list[str]]: - """Return covered/total line and function counts for one owned source file.""" + """Return covered/total line and measurable-function counts for one source file.""" executed = _line_set(record.get("executed_lines"), field="executed_lines") missing = _line_set(record.get("missing_lines"), field="missing_lines") overlap = executed & missing @@ -162,8 +162,10 @@ def _analyse_source( continue start, end = _function_body_lines(node) measured_body = {line for line in measured if start <= line <= end} + if not measured_body: + continue function_total += 1 - if measured_body and measured_body <= executed: + if measured_body <= executed: function_covered += 1 else: uncovered.append(f"{path.relative_to(source_root)}:{node.name}") From 319e0a3321a14ae957f1a67cd01bb50237651edf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 11:10:57 +0900 Subject: [PATCH 07/16] test(quality): isolate nested function coverage bodies --- tests/test_coverage_metric_evidence.py | 56 +++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/tests/test_coverage_metric_evidence.py b/tests/test_coverage_metric_evidence.py index 8473de8..c159475 100644 --- a/tests/test_coverage_metric_evidence.py +++ b/tests/test_coverage_metric_evidence.py @@ -99,6 +99,42 @@ def _write_noop_fixture(tmp_path: Path) -> tuple[Path, Path]: return source_root, coverage_json +def _write_nested_fixture(tmp_path: Path) -> tuple[Path, Path]: + """Create a covered outer function whose uncalled nested function is missing.""" + source_root = tmp_path / "src" / "egressweave" + source_root.mkdir(parents=True) + source_file = source_root / "sample.py" + source_file.write_text( + "def outer() -> int:\n" + " def inner() -> int:\n" + " return 2\n" + " return 1\n", + encoding="utf-8", + ) + coverage_json = tmp_path / "coverage.json" + coverage_json.write_text( + json.dumps( + { + "meta": {"branch_coverage": True, "version": "fixture"}, + "files": { + str(source_file): { + "executed_lines": [1, 2, 4], + "missing_lines": [3], + "excluded_lines": [], + "summary": { + "covered_lines": 3, + "num_statements": 4, + "missing_lines": 1, + }, + } + }, + } + ), + encoding="utf-8", + ) + return source_root, coverage_json + + def _run_reporter(source_root: Path, coverage_json: Path) -> subprocess.CompletedProcess[str]: """Run the repository reporter exactly as CI will invoke it.""" return subprocess.run( @@ -144,6 +180,21 @@ def test_reporter_excludes_functions_without_measurable_body_lines(tmp_path: Pat assert "noop" not in result.stderr +def test_reporter_counts_nested_function_bodies_only_for_the_nested_function( + tmp_path: Path, +) -> None: + """An uncalled inner body must not make its fully covered outer body missing.""" + source_root, coverage_json = _write_nested_fixture(tmp_path) + + result = _run_reporter(source_root, coverage_json) + + assert result.returncode == 1 + assert "line=75.00% (3/4)" in result.stdout + assert "function=50.00% (1/2)" in result.stdout + assert "sample.py:inner" in result.stderr + assert "sample.py:outer" not in result.stderr + + def test_reporter_fails_closed_when_a_function_body_has_a_missing_line( tmp_path: Path, ) -> None: @@ -169,7 +220,10 @@ def test_reporter_rejects_incomplete_owned_source_coverage(tmp_path: Path) -> No executed_lines=[1, 2, 3, 4, 7, 8], missing_lines=[], ) - (source_root / "unreported.py").write_text("def hidden() -> int:\n return 1\n", encoding="utf-8") + (source_root / "unreported.py").write_text( + "def hidden() -> int:\n return 1\n", + encoding="utf-8", + ) result = _run_reporter(source_root, coverage_json) From 2b6f230e3d1657cc5468dd8af97d30af63df8c57 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 11:15:32 +0900 Subject: [PATCH 08/16] fix(quality): isolate nested function coverage bodies --- scripts/ci/report_coverage_metrics.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/scripts/ci/report_coverage_metrics.py b/scripts/ci/report_coverage_metrics.py index 34c59dc..ca70262 100644 --- a/scripts/ci/report_coverage_metrics.py +++ b/scripts/ci/report_coverage_metrics.py @@ -128,6 +128,22 @@ def _function_body_lines(node: ast.FunctionDef | ast.AsyncFunctionDef) -> tuple[ return start, end +def _nested_function_body_lines( + node: ast.FunctionDef | ast.AsyncFunctionDef, +) -> set[int]: + """Return body lines owned by nested functions rather than their enclosing one.""" + nested_lines: set[int] = set() + for child in ast.walk(node): + if child is node or not isinstance( + child, + (ast.FunctionDef, ast.AsyncFunctionDef), + ): + continue + start, end = _function_body_lines(child) + nested_lines.update(range(start, end + 1)) + return nested_lines + + def _analyse_source( path: Path, record: dict[str, Any], @@ -161,7 +177,12 @@ def _analyse_source( if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): continue start, end = _function_body_lines(node) - measured_body = {line for line in measured if start <= line <= end} + nested_body_lines = _nested_function_body_lines(node) + measured_body = { + line + for line in measured + if start <= line <= end and line not in nested_body_lines + } if not measured_body: continue function_total += 1 From 4b29dd9fd3454d4c9e3539eec3fde32ca3a834f5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 11:22:58 +0900 Subject: [PATCH 09/16] test(quality): reject synthetic one-line function coverage --- tests/test_coverage_metric_evidence.py | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/test_coverage_metric_evidence.py b/tests/test_coverage_metric_evidence.py index c159475..c8a3e16 100644 --- a/tests/test_coverage_metric_evidence.py +++ b/tests/test_coverage_metric_evidence.py @@ -135,6 +135,41 @@ def _write_nested_fixture(tmp_path: Path) -> tuple[Path, Path]: return source_root, coverage_json +def _write_single_line_nested_fixture(tmp_path: Path) -> tuple[Path, Path]: + """Create a nested function whose definition and body share one trace line.""" + source_root = tmp_path / "src" / "egressweave" + source_root.mkdir(parents=True) + source_file = source_root / "sample.py" + source_file.write_text( + "def outer() -> int:\n" + " def inner() -> int: return 2\n" + " return 1\n", + encoding="utf-8", + ) + coverage_json = tmp_path / "coverage.json" + coverage_json.write_text( + json.dumps( + { + "meta": {"branch_coverage": True, "version": "fixture"}, + "files": { + str(source_file): { + "executed_lines": [1, 2, 3], + "missing_lines": [], + "excluded_lines": [], + "summary": { + "covered_lines": 3, + "num_statements": 3, + "missing_lines": 0, + }, + } + }, + } + ), + encoding="utf-8", + ) + return source_root, coverage_json + + def _run_reporter(source_root: Path, coverage_json: Path) -> subprocess.CompletedProcess[str]: """Run the repository reporter exactly as CI will invoke it.""" return subprocess.run( @@ -195,6 +230,19 @@ def test_reporter_counts_nested_function_bodies_only_for_the_nested_function( assert "sample.py:outer" not in result.stderr +def test_reporter_excludes_single_line_bodies_without_distinct_trace_evidence( + tmp_path: Path, +) -> None: + """Definition-line execution alone must not claim an uncalled body is covered.""" + source_root, coverage_json = _write_single_line_nested_fixture(tmp_path) + + result = _run_reporter(source_root, coverage_json) + + assert result.returncode == 0, result.stderr + assert "line=100.00% (3/3)" in result.stdout + assert "function=100.00% (1/1)" in result.stdout + + def test_reporter_fails_closed_when_a_function_body_has_a_missing_line( tmp_path: Path, ) -> None: From 0c645f32eca115baf60e99ee7a4a274a08d36c3a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 11:25:34 +0900 Subject: [PATCH 10/16] fix(quality): exclude unmeasurable one-line function bodies --- scripts/ci/report_coverage_metrics.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/ci/report_coverage_metrics.py b/scripts/ci/report_coverage_metrics.py index ca70262..29e1f2b 100644 --- a/scripts/ci/report_coverage_metrics.py +++ b/scripts/ci/report_coverage_metrics.py @@ -140,7 +140,9 @@ def _nested_function_body_lines( ): continue start, end = _function_body_lines(child) - nested_lines.update(range(start, end + 1)) + nested_lines.update( + line for line in range(start, end + 1) if line > child.lineno + ) return nested_lines @@ -181,7 +183,9 @@ def _analyse_source( measured_body = { line for line in measured - if start <= line <= end and line not in nested_body_lines + if start <= line <= end + and line > node.lineno + and line not in nested_body_lines } if not measured_body: continue From 5c7489f69021400024048238aa7bcd2a9a081f23 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 11:32:23 +0900 Subject: [PATCH 11/16] test(quality): reject owned-source symlink escapes --- tests/test_coverage_metric_evidence.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_coverage_metric_evidence.py b/tests/test_coverage_metric_evidence.py index c8a3e16..0bd93cf 100644 --- a/tests/test_coverage_metric_evidence.py +++ b/tests/test_coverage_metric_evidence.py @@ -280,6 +280,27 @@ def test_reporter_rejects_incomplete_owned_source_coverage(tmp_path: Path) -> No assert "unreported.py" in result.stderr +def test_reporter_rejects_owned_source_symlinks_without_path_disclosure( + tmp_path: Path, +) -> None: + """Do not follow a source-tree symlink or leak its external resolved target.""" + source_root, coverage_json = _write_fixture( + tmp_path, + executed_lines=[1, 2, 3, 4, 7, 8], + missing_lines=[], + ) + external_source = tmp_path / "external-secret-name.py" + external_source.write_text("def outside() -> int:\n return 1\n", encoding="utf-8") + (source_root / "linked.py").symlink_to(external_source) + + result = _run_reporter(source_root, coverage_json) + + assert result.returncode == 2 + assert "owned source tree contains a symbolic link" in result.stderr + assert str(external_source) not in result.stderr + assert "Traceback" not in result.stderr + + def test_ci_exposes_the_exact_metrics_after_coverage_collection() -> None: """The hosted matrix must publish the reporter output on every exact head.""" workflow = CI_WORKFLOW.read_text(encoding="utf-8") From e2b4eeb140921daedc8edbafffc6a4af517a65f8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 11:34:56 +0900 Subject: [PATCH 12/16] fix(quality): reject owned-source symlink escapes --- scripts/ci/report_coverage_metrics.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/scripts/ci/report_coverage_metrics.py b/scripts/ci/report_coverage_metrics.py index 29e1f2b..0445250 100644 --- a/scripts/ci/report_coverage_metrics.py +++ b/scripts/ci/report_coverage_metrics.py @@ -95,7 +95,25 @@ def _owned_coverage_records( raise CoverageEvidenceError("coverage JSON aliases one owned source file twice") records[resolved] = raw_record - owned_files = {path.resolve() for path in source_root.rglob("*.py") if path.is_file()} + owned_files: set[Path] = set() + for path in source_root.rglob("*.py"): + relative = path.relative_to(source_root) + if path.is_symlink(): + raise CoverageEvidenceError( + f"owned source tree contains a symbolic link: {relative}" + ) + if not path.is_file(): + continue + resolved = path.resolve(strict=True) + if not _is_within(resolved, source_root): + raise CoverageEvidenceError( + f"owned source path escapes source root: {relative}" + ) + if resolved in owned_files: + raise CoverageEvidenceError( + "owned source tree aliases one Python source file twice" + ) + owned_files.add(resolved) if not owned_files: raise CoverageEvidenceError("source root contains no owned Python source files") From 9acb1f910ccfc5ee15499fc6b99a1d20ebe9f7b8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 11:40:48 +0900 Subject: [PATCH 13/16] test(quality): reject coverage lines outside owned source --- tests/test_coverage_metric_evidence.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_coverage_metric_evidence.py b/tests/test_coverage_metric_evidence.py index 0bd93cf..f092fa1 100644 --- a/tests/test_coverage_metric_evidence.py +++ b/tests/test_coverage_metric_evidence.py @@ -301,6 +301,27 @@ def test_reporter_rejects_owned_source_symlinks_without_path_disclosure( assert "Traceback" not in result.stderr +def test_reporter_rejects_line_numbers_beyond_owned_source(tmp_path: Path) -> None: + """Fabricated line evidence outside the source file must fail closed.""" + source_root, coverage_json = _write_fixture( + tmp_path, + executed_lines=[1, 2, 3, 4, 7, 8], + missing_lines=[], + ) + payload = json.loads(coverage_json.read_text(encoding="utf-8")) + record = next(iter(payload["files"].values())) + record["executed_lines"].append(999) + record["summary"]["covered_lines"] += 1 + record["summary"]["num_statements"] += 1 + coverage_json.write_text(json.dumps(payload), encoding="utf-8") + + result = _run_reporter(source_root, coverage_json) + + assert result.returncode == 2 + assert "coverage data line number exceeds owned source file" in result.stderr + assert "Traceback" not in result.stderr + + def test_ci_exposes_the_exact_metrics_after_coverage_collection() -> None: """The hosted matrix must publish the reporter output on every exact head.""" workflow = CI_WORKFLOW.read_text(encoding="utf-8") From da7bcc9c8243caf7258eefea1c7ff174623066df Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 11:43:44 +0900 Subject: [PATCH 14/16] fix(quality): reject coverage lines outside owned source --- scripts/ci/report_coverage_metrics.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/ci/report_coverage_metrics.py b/scripts/ci/report_coverage_metrics.py index 0445250..ae41ab2 100644 --- a/scripts/ci/report_coverage_metrics.py +++ b/scripts/ci/report_coverage_metrics.py @@ -190,6 +190,13 @@ def _analyse_source( f"owned source file is unreadable or syntactically invalid: {path.relative_to(source_root)}" ) from exc + source_line_count = len(source.splitlines()) + if any(line > source_line_count for line in measured): + raise CoverageEvidenceError( + "coverage data line number exceeds owned source file: " + f"{path.relative_to(source_root)}" + ) + function_total = 0 function_covered = 0 uncovered: list[str] = [] From 8fd97bf7c2ad38ab776bd84b6691d30f460df241 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 11:48:00 +0900 Subject: [PATCH 15/16] test(quality): reject malformed coverage file paths --- tests/test_coverage_metric_evidence.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_coverage_metric_evidence.py b/tests/test_coverage_metric_evidence.py index f092fa1..b58a665 100644 --- a/tests/test_coverage_metric_evidence.py +++ b/tests/test_coverage_metric_evidence.py @@ -322,6 +322,27 @@ def test_reporter_rejects_line_numbers_beyond_owned_source(tmp_path: Path) -> No assert "Traceback" not in result.stderr +def test_reporter_rejects_invalid_coverage_path_without_traceback( + tmp_path: Path, +) -> None: + """Malformed file-record paths must remain inside the generic evidence error.""" + source_root, coverage_json = _write_fixture( + tmp_path, + executed_lines=[1, 2, 3, 4, 7, 8], + missing_lines=[], + ) + payload = json.loads(coverage_json.read_text(encoding="utf-8")) + record = next(iter(payload["files"].values())) + payload["files"] = {"\u0000": record} + coverage_json.write_text(json.dumps(payload), encoding="utf-8") + + result = _run_reporter(source_root, coverage_json) + + assert result.returncode == 2 + assert "coverage file path is invalid" in result.stderr + assert "Traceback" not in result.stderr + + def test_ci_exposes_the_exact_metrics_after_coverage_collection() -> None: """The hosted matrix must publish the reporter output on every exact head.""" workflow = CI_WORKFLOW.read_text(encoding="utf-8") From 298cf5b6c686f7c92443c4317f89ba6693cd4081 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 11:50:39 +0900 Subject: [PATCH 16/16] fix(quality): normalize malformed coverage paths --- scripts/ci/report_coverage_metrics.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/ci/report_coverage_metrics.py b/scripts/ci/report_coverage_metrics.py index ae41ab2..3b72c2a 100644 --- a/scripts/ci/report_coverage_metrics.py +++ b/scripts/ci/report_coverage_metrics.py @@ -85,10 +85,13 @@ def _owned_coverage_records( for raw_path, raw_record in files.items(): if type(raw_path) is not str or type(raw_record) is not dict: raise CoverageEvidenceError("coverage file records must map strings to objects") - candidate = Path(raw_path) - if not candidate.is_absolute(): - candidate = Path.cwd() / candidate - resolved = candidate.resolve(strict=False) + try: + candidate = Path(raw_path) + if not candidate.is_absolute(): + candidate = Path.cwd() / candidate + resolved = candidate.resolve(strict=False) + except (OSError, RuntimeError, UnicodeError, ValueError): + raise CoverageEvidenceError("coverage file path is invalid") from None if not _is_within(resolved, source_root): continue if resolved in records: