diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index dc6f2c7a..1a99fc02 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -298,7 +298,7 @@ jobs: - name: Combined coverage (unit + docker integration) run: PYTHON=python3 bash scripts/coverage.sh aggregate critical - - name: Diff-coverage gate (changed lines >= 90%) + - name: Diff-coverage gate (changed lines >= 80%) run: | git fetch --no-tags origin main:refs/remotes/origin/main - python3 scripts/diff_coverage.py --base origin/main --min 90 + python3 scripts/diff_coverage.py --base origin/main --min 80 diff --git a/docs/decisions/0004-coverage-policy.md b/docs/decisions/0004-coverage-policy.md index cb0ff858..3961dab1 100644 --- a/docs/decisions/0004-coverage-policy.md +++ b/docs/decisions/0004-coverage-policy.md @@ -3,6 +3,10 @@ - **Status:** Accepted - **Date:** 2026-06-25 - **Deciders:** didericis +- **Revised:** 2026-07-27 — thresholds relaxed (critical minimum 90→85%, + diff-coverage gate 90→80%) to cut low-value test churn on changed lines. + The risk-weighting structure and the "global is informational" rule are + unchanged. ## Context @@ -34,7 +38,7 @@ a regression (Goodhart's law). Coverage is **risk-weighted**, measured over the **combined unit + integration** suites, with three rules: -1. **Critical modules must remain ≥ 90%.** The curated security/logic core +1. **Critical modules must remain ≥ 85%.** The curated security/logic core covers the host and gateway egress policy, manifest trust boundary, git-gate enforcement, supervise protocol/server, YAML parser, and bottle state. The concrete module list lives in `scripts/critical-modules.txt`; @@ -55,7 +59,7 @@ integration** suites, with three rules: The forward-looking guard is a **diff-coverage gate** (`scripts/diff_coverage.py`): new/changed executable lines on a branch -must be ≥ 90% covered. This catches regressions where they are +must be ≥ 80% covered. This catches regressions where they are introduced without forcing a back-fill crusade through legacy glue. The gate skips lines in omitted files (there is no coverage data for them), so the omit list cannot launder *new* logic into the dark: anything that diff --git a/scripts/coverage.sh b/scripts/coverage.sh index 0dab9a84..0eff4540 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -13,7 +13,7 @@ # are re-executed; no KVM or Docker dependency. # # Pass "critical" as the last argument in either mode to also report just the -# critical modules (ADR 0004 target: 90%). +# critical modules (ADR 0004 target: 85%). set -euo pipefail cd "$(dirname "$0")/.." @@ -34,8 +34,8 @@ if [ "${1:-}" = "aggregate" ]; then "$PY" -m coverage report -m if [ "${2:-}" = "critical" ]; then - echo "== critical modules (ADR 0004 minimum: 90%) ==" >&2 - "$PY" -m coverage report --include="$CRITICAL" --fail-under=90 + echo "== critical modules (ADR 0004 minimum: 85%) ==" >&2 + "$PY" -m coverage report --include="$CRITICAL" --fail-under=85 fi exit 0 fi @@ -55,6 +55,6 @@ echo "== combined report ==" >&2 "$PY" -m coverage report -m if [ "${1:-}" = "critical" ]; then - echo "== critical modules (ADR 0004 minimum: 90%) ==" >&2 - "$PY" -m coverage report --include="$CRITICAL" --fail-under=90 + echo "== critical modules (ADR 0004 minimum: 85%) ==" >&2 + "$PY" -m coverage report --include="$CRITICAL" --fail-under=85 fi diff --git a/scripts/critical-modules.txt b/scripts/critical-modules.txt index 1da2dbd0..7625ab43 100644 --- a/scripts/critical-modules.txt +++ b/scripts/critical-modules.txt @@ -1,4 +1,4 @@ -# Critical security/logic core held to the >=90% coverage bar by +# Critical security/logic core held to the >=85% coverage bar by # docs/decisions/0004-coverage-policy.md. # # SINGLE SOURCE OF TRUTH: scripts/coverage.sh (the `critical` report) and diff --git a/scripts/diff_coverage.py b/scripts/diff_coverage.py index 546c655c..ae225787 100755 --- a/scripts/diff_coverage.py +++ b/scripts/diff_coverage.py @@ -13,8 +13,8 @@ policy. Usage: scripts/coverage.sh # produce .coverage first - python3 scripts/diff_coverage.py # gate against origin/main, min 90% - python3 scripts/diff_coverage.py --base main --min 85 + python3 scripts/diff_coverage.py # gate against origin/main, min 80% + python3 scripts/diff_coverage.py --base main --min 75 """ from __future__ import annotations @@ -74,7 +74,7 @@ def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--base", default="origin/main", help="git ref to diff against (default: origin/main)") - ap.add_argument("--min", type=float, default=90.0, + ap.add_argument("--min", type=float, default=80.0, help="minimum %% of changed executable lines covered") args = ap.parse_args()