From d89d389bef7e688378d6f6e20176da72e5d21d3c Mon Sep 17 00:00:00 2001 From: didericis Date: Fri, 26 Jun 2026 02:19:35 -0400 Subject: [PATCH] ci(coverage): add auto-updated "core coverage" badge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Surface the metric ADR 0004 says matters — the critical security/logic core, currently 95% — as a README badge, distinct from the informational global `coverage` badge. - scripts/critical-modules.txt: single source of truth for the core module list. scripts/coverage.sh now reads it (instead of a hardcoded string) and update-badges.yml reads the same file, so the badge and the `critical` report cannot drift. - update-badges.yml: a `core coverage` step reuses the unit-coverage data (every core module is unit-tested, so unit-only is accurate for it) and sed-updates the new badge, like the existing ones. - README: `core coverage 95%` badge linking to ADR 0004 so a reader can find out what "core" means. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01NkwFXLFff9PYPy4wgVBJp9 --- .gitea/workflows/update-badges.yml | 19 +++++++++++++++++-- README.md | 1 + docs/decisions/0004-coverage-policy.md | 6 ++++++ scripts/coverage.sh | 11 ++++------- scripts/critical-modules.txt | 23 +++++++++++++++++++++++ 5 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 scripts/critical-modules.txt diff --git a/.gitea/workflows/update-badges.yml b/.gitea/workflows/update-badges.yml index 7db0341..34b9871 100644 --- a/.gitea/workflows/update-badges.yml +++ b/.gitea/workflows/update-badges.yml @@ -54,11 +54,23 @@ jobs: echo "percent=$PERCENT" >> $GITHUB_OUTPUT echo "Coverage: $PERCENT%" + - name: Extract core (critical-module) coverage percentage + id: core_coverage + run: | + # Reuses the .coverage data from the previous step. The core list is + # the single source of truth in scripts/critical-modules.txt; every + # core module is unit-tested, so the unit-only run is accurate for it. + INCLUDE=$(grep -vE '^[[:space:]]*(#|$)' scripts/critical-modules.txt | paste -sd, -) + PERCENT=$(python -m coverage report --include="$INCLUDE" 2>/dev/null | grep '^TOTAL' | grep -oP '\d+(?=%)' | tail -1) + echo "percent=$PERCENT" >> $GITHUB_OUTPUT + echo "Core coverage: $PERCENT%" + - name: Update badges in README run: | PYLINT_SCORE="${{ steps.pylint.outputs.score }}" PYRIGHT_ERRORS="${{ steps.pyright.outputs.errors }}" COVERAGE_PERCENT="${{ steps.coverage.outputs.percent }}" + CORE_COVERAGE_PERCENT="${{ steps.core_coverage.outputs.percent }}" PYLINT_SCORE_ENCODED=$(echo "$PYLINT_SCORE" | sed 's|/|%2F|g') @@ -71,9 +83,12 @@ jobs: if [ -n "$COVERAGE_PERCENT" ]; then sed -i "s|/badge/coverage-[^)]*|/badge/coverage-${COVERAGE_PERCENT}%25-brightgreen|" README.md fi + if [ -n "$CORE_COVERAGE_PERCENT" ]; then + sed -i "s|/badge/core%20coverage-[^)]*|/badge/core%20coverage-${CORE_COVERAGE_PERCENT}%25-brightgreen|" README.md + fi echo "Updated badges:" - grep -E "pylint|pyright|coverage" README.md | head -3 + grep -E "pylint|pyright|coverage" README.md | head -4 - name: Commit and push badge updates run: | @@ -86,7 +101,7 @@ jobs: else echo "Badge changes detected, committing..." git add README.md - MSG="chore: update quality badges"$'\n\n'"- Pylint: ${{ steps.pylint.outputs.score }}"$'\n'"- Pyright: ${{ steps.pyright.outputs.errors }} errors"$'\n'"- Coverage: ${{ steps.coverage.outputs.percent }}%"$'\n\n'"[skip ci]" + MSG="chore: update quality badges"$'\n\n'"- Pylint: ${{ steps.pylint.outputs.score }}"$'\n'"- Pyright: ${{ steps.pyright.outputs.errors }} errors"$'\n'"- Coverage: ${{ steps.coverage.outputs.percent }}%"$'\n'"- Core coverage: ${{ steps.core_coverage.outputs.percent }}%"$'\n\n'"[skip ci]" git commit -m "$MSG" git push fi diff --git a/README.md b/README.md index 876a95f..8e8e86d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![pylint](https://img.shields.io/badge/pylint-9.93%2F10-brightgreen)](https://github.com/PyCQA/pylint) [![pyright](https://img.shields.io/badge/pyright-0%20errors-brightgreen)](https://github.com/microsoft/pyright) [![coverage](https://img.shields.io/badge/coverage-79%25-brightgreen)](https://coverage.readthedocs.io/) +[![core coverage](https://img.shields.io/badge/core%20coverage-95%25-brightgreen)](https://gitea.dideric.is/didericis/bot-bottle/src/branch/main/docs/decisions/0004-coverage-policy.md) **Problem:** Developer wants to run a coding agent without supervision, but they don't want a prompt injected or misbehaving agent wrecking their environment or exfiltrating sensitive data. diff --git a/docs/decisions/0004-coverage-policy.md b/docs/decisions/0004-coverage-policy.md index 6141e5b..acaaa4e 100644 --- a/docs/decisions/0004-coverage-policy.md +++ b/docs/decisions/0004-coverage-policy.md @@ -88,3 +88,9 @@ omit list. - PRs #290 (cover the egress adapter), and the coverage-policy PR that introduces this record. - `.coveragerc`, `scripts/coverage.sh`, `scripts/diff_coverage.py`. +- `scripts/critical-modules.txt` — the single source of truth for the + core-module list; read by both `scripts/coverage.sh` and the + `update-badges.yml` "core coverage" badge so they cannot drift. +- The README carries a `core coverage` badge (auto-updated from that + list) — the headline number, distinct from the informational global + `coverage` badge. diff --git a/scripts/coverage.sh b/scripts/coverage.sh index db9556c..0dae61b 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -16,13 +16,10 @@ cd "$(dirname "$0")/.." PY="${PYTHON:-python3}" -# Critical security/logic core held to the high bar by ADR 0004. -CRITICAL="bot_bottle/egress_addon.py,bot_bottle/egress_addon_core.py,\ -bot_bottle/dlp_detectors.py,bot_bottle/egress.py,bot_bottle/manifest.py,\ -bot_bottle/manifest_egress.py,bot_bottle/manifest_agent.py,\ -bot_bottle/manifest_schema.py,bot_bottle/git_gate.py,\ -bot_bottle/git_http_backend.py,bot_bottle/supervise.py,\ -bot_bottle/yaml_subset.py,bot_bottle/bottle_state.py" +# Critical security/logic core held to the high bar by ADR 0004. The list +# lives in one place (scripts/critical-modules.txt) so this report and the +# README "core coverage" badge can't drift; comma-join it for --include. +CRITICAL=$(grep -vE '^[[:space:]]*(#|$)' scripts/critical-modules.txt | paste -sd, -) rm -f .coverage diff --git a/scripts/critical-modules.txt b/scripts/critical-modules.txt new file mode 100644 index 0000000..8f23b04 --- /dev/null +++ b/scripts/critical-modules.txt @@ -0,0 +1,23 @@ +# Critical security/logic core held to the >=90% coverage bar by +# docs/decisions/0004-coverage-policy.md. +# +# SINGLE SOURCE OF TRUTH: scripts/coverage.sh (the `critical` report) and +# .gitea/workflows/update-badges.yml (the "core coverage" badge) both read +# this file. Add a module here when it becomes part of the core; a coverage +# number that silently stops measuring a module is worse than no badge. +# +# One module path per line, relative to the repo root. Blank lines and +# `#` comments are ignored. +bot_bottle/egress_addon.py +bot_bottle/egress_addon_core.py +bot_bottle/dlp_detectors.py +bot_bottle/egress.py +bot_bottle/manifest.py +bot_bottle/manifest_egress.py +bot_bottle/manifest_agent.py +bot_bottle/manifest_schema.py +bot_bottle/git_gate.py +bot_bottle/git_http_backend.py +bot_bottle/supervise.py +bot_bottle/yaml_subset.py +bot_bottle/bottle_state.py