Skip to content

fix(ci): enforce the coverage threshold once, where the bypass can reach it - #242

Closed
gcko wants to merge 1 commit into
mainfrom
fix/drc-4278-coverage-gate
Closed

fix(ci): enforce the coverage threshold once, where the bypass can reach it#242
gcko wants to merge 1 commit into
mainfrom
fix/drc-4278-coverage-gate

Conversation

@gcko

@gcko gcko commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Implements DRC-4278 — Decide whether the coverage threshold should be enforced twice, on two interpreters.

The decision, and the better bug behind it

The ticket asked a yes/no question. Answering it surfaced something worth more than the answer: the coverage-exception bypass that AGENTS.md documents has never worked.

The test job ended its suite step with a bare coverage report. That reads fail_under from pyproject.toml and exits non-zero — and it ran before the "Check coverage-exception label" step and before the exception-aware step that honours the label. A PR below threshold failed at that line and never reached its own bypass. runtime-floor carried a second copy of the same bare call, pre-empting it again.

So the label was documented as a way to merge below threshold and was not one.

The decision itself

runtime-floor stops running the suite under coverage. Nothing consumed its coverage data — coverage json, the artifact, the label check and the sticky comment all live in test — and nothing in the product branches on sys.version_info, so a second measurement on 3.11 told us nothing the 3.12 run did not. The job's purpose is the floor, and it still does that: the direct-launch smoke plus the whole suite on 3.11.

This does not lower the ratchet. fail_under is untouched at 73; it is now enforced once, in the step that can see the label.

It also lets the job install validation requirements only. The rest of requirements-dev.txt is ruff, mypy and coverage, all of which run on 3.12 in their own jobs. PyYAML stays because scripts.tests.test_validate_plugins imports the validator. The pip cache added in #241 is repointed to match.

All seven script test modules are kept. The ticket suggested copying platform-tests' step shape; that job runs only five, so copying it would have silently dropped test_bench_event_latency — which #241 had just finished adding — and test_derive_prompt_shapes.

Verification

This change edits only workflow YAML and one AGENTS.md sentence, so there is no product behaviour to pin with a test. Nothing in the repo reads quality-gate.yml (grep -rn "quality-gate.yml" --include="*.py" . is empty). Evidence instead:

  • The bug, demonstrated. coverage report --fail-under=99 exits 2; coverage report --fail-under=0 exits 0. The first is the line removed here, the second is the form the bypass uses — so the removed line provably aborted the job before the label was read.
  • The job's new commands, run on this tree. python -m unittest discover -s cargento/skills/cargento/tests -t . → 2,006 tests, OK. The seven script modules → 192 tests, OK. Neither needs anything from requirements-dev.txt.
  • Dependency check. None of the seven script tests import ruff, mypy or coverage; only validate_plugins needs PyYAML, which is in requirements-validation.txt.
  • actionlint (required check) is the parser-level proof the YAML is well formed.
  • ruff check . and scripts/validate_plugins.py clean.

Note for the reviewer

The one thing worth checking hardest is that removing the bare coverage report from the test job does not remove enforcement. It does not: the else branch of "Coverage report and threshold" still runs a plain coverage report, which fails below threshold exactly as before. What changed is that a labelled PR can now reach it.

…ach it

DRC-4278 asked whether the threshold should be enforced twice, on two
interpreters. Answering it turned up something better: the documented
`coverage-exception` bypass has never worked.

The `test` job ended its suite step with a bare `coverage report`. That reads
`fail_under` from pyproject and exits non-zero, and it ran BEFORE the
"Check coverage-exception label" step and before the exception-aware step that
actually honours the label. So a PR below threshold failed at that line and
never reached its own bypass. AGENTS.md documents the label as a way to merge
below threshold; it was not one. `runtime-floor` carried a second copy of the
same bare call, pre-empting it again.

Demonstrated rather than argued: `coverage report --fail-under=99` exits 2,
`coverage report --fail-under=0` exits 0. The first is the line removed here;
the second is the form the bypass uses. The threshold is now enforced in one
place, the step that knows about the label.

On the original question, per the decision recorded on DRC-4278: runtime-floor
stops running the suite under `coverage`. Nothing consumed its coverage data,
and nothing in the product branches on `sys.version_info`, so the second
measurement told us nothing the 3.12 run did not. What the job is for is the
floor itself, and it still does that: the direct-launch smoke plus the whole
suite on 3.11.

It can then install validation requirements only. The rest of
requirements-dev.txt is ruff, mypy and coverage, which run on 3.12 in their own
jobs; PyYAML stays because scripts.tests.test_validate_plugins imports the
validator. The pip cache added in #241 is repointed to match what the job now
installs.

All seven script test modules are kept. `platform-tests` runs only five, and
copying its shape here would have quietly dropped test_bench_event_latency,
which #241 had just finished adding, and test_derive_prompt_shapes.

Verified by running the job's new commands on this tree: 2,006 dashboard tests
and 192 script tests, green, with no dev toolchain involved beyond the
interpreter.

Implements DRC-4278.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
@github-actions

Copy link
Copy Markdown
Contributor

Coverage

Name                                                                  Stmts   Miss Branch BrPart  Cover
-------------------------------------------------------------------------------------------------------
cargento/skills/cargento/agy_hook.py                                     79     14     28      7  78.5%
cargento/skills/cargento/cargento_runtime/__init__.py                     0      0      0      0 100.0%
cargento/skills/cargento/cargento_runtime/aggregate.py                  213      1     64      0  99.6%
cargento/skills/cargento/cargento_runtime/asks.py                       110      0     28      0 100.0%
cargento/skills/cargento/cargento_runtime/claude_data.py                305     33    142     16  89.0%
cargento/skills/cargento/cargento_runtime/cli.py                        127     14     26      3  87.6%
cargento/skills/cargento/cargento_runtime/collectors/__init__.py          0      0      0      0 100.0%
cargento/skills/cargento/cargento_runtime/collectors/antigravity.py     410     41    166     25  87.2%
cargento/skills/cargento/cargento_runtime/collectors/claude.py          256     15     94     12  91.7%
cargento/skills/cargento/cargento_runtime/collectors/codex.py           101      7     38      7  89.9%
cargento/skills/cargento/cargento_runtime/collectors/copilot.py         148      6     50      2  96.0%
cargento/skills/cargento/cargento_runtime/collectors/cursor.py          276     19    104     16  90.3%
cargento/skills/cargento/cargento_runtime/collectors/droid.py            32      3      6      1  89.5%
cargento/skills/cargento/cargento_runtime/collectors/gemini.py           53      7     16      4  84.1%
cargento/skills/cargento/cargento_runtime/collectors/goose.py            89     11     28      4  87.2%
cargento/skills/cargento/cargento_runtime/collectors/opencode.py         78      6     26      2  92.3%
cargento/skills/cargento/cargento_runtime/collectors/pi.py              326     34    152     20  88.7%
cargento/skills/cargento/cargento_runtime/config.py                     189      1     20      1  99.0%
cargento/skills/cargento/cargento_runtime/diagnostics.py                 84      4     26      4  92.7%
cargento/skills/cargento/cargento_runtime/dismissals.py                 113      2     28      2  97.2%
cargento/skills/cargento/cargento_runtime/events.py                     169      0     64      0 100.0%
cargento/skills/cargento/cargento_runtime/git_status.py                  28      2      8      2  88.9%
cargento/skills/cargento/cargento_runtime/http_api.py                   525     34    176     10  93.7%
cargento/skills/cargento/cargento_runtime/io.py                         126      2     28      0  98.7%
cargento/skills/cargento/cargento_runtime/lifecycle.py                  325     15    106      6  95.1%
cargento/skills/cargento/cargento_runtime/notifications.py              174     14     60      4  91.5%
cargento/skills/cargento/cargento_runtime/observation.py                271      9     74      1  97.1%
cargento/skills/cargento/cargento_runtime/observer.py                   239     25    108     13  87.3%
cargento/skills/cargento/cargento_runtime/probe.py                       44      0     18      1  98.4%
cargento/skills/cargento/cargento_runtime/quota.py                      333      2    112      1  99.3%
cargento/skills/cargento/cargento_runtime/records.py                    256      5    114      9  96.2%
cargento/skills/cargento/cargento_runtime/sessions.py                   101      0     44      0 100.0%
cargento/skills/cargento/cargento_runtime/snapshot.py                    36      0      4      0 100.0%
cargento/skills/cargento/cargento_runtime/spacedock.py                  454     48    238     26  89.0%
cargento/skills/cargento/cargento_runtime/state.py                       65      0      2      0 100.0%
cargento/skills/cargento/cargento_runtime/stream.py                      57      0      8      0 100.0%
cargento/skills/cargento/cargento_runtime/transcripts.py                509     29    266     26  92.9%
cargento/skills/cargento/cargento_runtime/turns.py                      197     14    104     12  90.7%
cargento/skills/cargento/cargento_runtime/web/__init__.py                 0      0      0      0 100.0%
cargento/skills/cargento/cargento_runtime/web/page.py                    54      0     14      0 100.0%
cargento/skills/cargento/event_hook.py                                   86      4     28      3  93.9%
cargento/skills/cargento/mcp_server.py                                  377     22    112     14  92.2%
cargento/skills/cargento/notify_hook.py                                  49     15      6      1  67.3%
cargento/skills/cargento/server.py                                        3      0      2      1  80.0%
cargento/skills/cargento/statusline_hook.py                             131     13     46      8  87.0%
scripts/bench_collect.py                                                211     10     54      6  94.0%
scripts/bench_event_latency.py                                           67     21     14      1  67.9%
scripts/bump_version.py                                                  60     12     24      5  77.4%
scripts/capture_hook.py                                                 287     30     86     11  88.5%
scripts/derive_prompt_shapes.py                                         210     16     88     14  89.3%
scripts/lint_embedded.py                                                 92      3     28      2  95.8%
scripts/validate_plugins.py                                             675    190    390     60  69.2%
-------------------------------------------------------------------------------------------------------
TOTAL                                                                  9200    753   3468    363  90.2%

Threshold: fail_under in pyproject.toml · label coverage-exception to bypass (visible in PR timeline).

@gcko

gcko commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

Merged as 0857627 on main, despite this record still reading open.

gh pr merge --squash returned a 502 from GitHub. The squash commit was created and pushed — 0857627 on main carries this PR's number in its subject and the two files are byte-identical to this branch's heads — but the PR record never flipped to merged.

Verified before closing:

  • git merge-base --is-ancestor 0857627 origin/main → true
  • .github/workflows/quality-gate.yml and AGENTS.md hash-identical between this branch and origin/main
  • git diff origin/fix/drc-4278-coverage-gate origin/main over both files → empty

Closing rather than re-merging, which would either be rejected as having no commits or duplicate the change. DRC-4278 is delivered.

@gcko gcko closed this Aug 29, 2026
@gcko
gcko deleted the fix/drc-4278-coverage-gate branch August 29, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant