Coverage exclusion policy
RAG006 sets two coverage floors on src/rag_sample/, enforced as a CI hard-fail: a 90% aggregate floor on combined line and branch coverage, and a 70% per-file floor on combined line and branch coverage for any non-excluded file. This page is the companion exclusion policy. It is intentionally maintained outside the ADR so it can evolve without amending the decision.
Both numbers in RAG006 are floors, not targets. Coverage above the floors is fine; below either floor fails CI.
What is excluded
Section titled “What is excluded”The following are excluded from coverage measurement, in the order they appear in pyproject.toml [tool.coverage.run] omit:
| Path or pattern | Reason |
|---|---|
src/rag_sample/__init__.py and any other package __init__.py | Trivial re-exports; coverage adds no signal |
src/rag_sample/settings.py (and any pydantic-settings model module) | Configuration dataclasses; covered indirectly by every test that loads settings |
src/rag_sample/__main__.py and module entrypoints | Imported as scripts at runtime; not exercised by unit tests |
src/rag_sample/api/lambda_handler.py (the Mangum entrypoint) | One-liner handler = Mangum(app); covered functionally via integration testing only |
scripts/export_openapi.py | Build-time helper, not application code |
Generated code under src/rag_sample/_generated/ if any | Re-generated; tests target the inputs and the consumers |
Line-level exclusions
Section titled “Line-level exclusions”Code may opt out of coverage at line granularity using # pragma: no cover. Use this only for branches that cannot meaningfully be tested:
- defensive
if TYPE_CHECKING:blocks - platform-specific code paths that the CI environment does not exercise (rare in this product)
raise NotImplementedErrorplaceholders in unfinished abstract methods, removed once implemented
Do not use # pragma: no cover to bypass coverage on logic that is testable but inconvenient. If a branch is hard to test, write a test or refactor the code.
How to change the exclusion list
Section titled “How to change the exclusion list”This list is the policy of record. Add or remove entries here and update pyproject.toml [tool.coverage.run] omit in the same PR. No ADR amendment is required — RAG006 references this page rather than enumerating the exclusions itself.
Two operational notes:
- Never add an entry just to dodge a failing CI run. Investigate why coverage dropped and fix the underlying gap before considering an exclusion. Exclusions are for code that genuinely cannot be unit-tested, not for code that is hard to unit-test.
- Keep the list short. A growing exclusion list silently lowers the effective floor. If the list grows beyond a handful of entries, that is a signal to revisit RAG006 itself.
CI command (for reference)
Section titled “CI command (for reference)”The canonical invocation is documented in RAG006. The exclusion list applies through pyproject.toml:
[tool.coverage.run]branch = truesource = ["rag_sample"]omit = [ "src/rag_sample/**/__init__.py", "src/rag_sample/settings.py", "src/rag_sample/__main__.py", "src/rag_sample/api/lambda_handler.py", "scripts/export_openapi.py",]Keep this snippet in sync with the table above.
The per-file 70% floor is enforced as a separate CI step after the suite (since pytest-cov has no native per-file --fail-under); the canonical implementation is coverage report --fail-under=70 --skip-covered, which fails if any non-excluded file is below 70% combined line and branch.
© 2026 Benjamin Arunski