---
description: |
  [TOPIC] CI and Codecov — the mandatory audit test and config deferrals
  [DETAILS] Every package ships a `tests/develop/test_audit.py` that runs `scitex-dev ecosystem audit-all <distribution>` as a normal pytest test (generated by `write-audit-test`), so audit conformance is part of the failing-CI signal. Covers the default body, why `skip_rules=` is not for muffling real violations, how to defer a rule to a migration campaign via `<repo>/.scitex/dev/config.yaml audit.skip-rules` (rationale mandatory, honouring never silent, masked-inventory printed), and the `.gitignore` exception that lets CI see the same whitelist as local. Companion to [11_ci-and-codecov.md](11_ci-and-codecov.md).
tags: [scitex-general-package-ci-codecov]
---

# Codecov — the audit test and config deferrals

> Parent leaf: [`11_ci-and-codecov.md`](11_ci-and-codecov.md).

## Mandatory `tests/develop/test_audit.py`

Every package must ship a `tests/develop/test_audit.py` that runs
`scitex-dev ecosystem audit-all <distribution>` as a normal pytest test.
This makes audit conformance part of the failing-CI signal instead of a
separate check the team learns to ignore.

Generate it with `scitex-dev ecosystem write-audit-test` and commit. The
default body looks like:

```python
import shutil
import pytest

def test_audit_all_clean():
    if shutil.which("scitex-dev") is None:
        pytest.skip(
            "scitex-dev not installed — add scitex-dev[cli-audit] to "
            "[project.optional-dependencies.dev]"
        )
    from scitex_dev.testing import audit_all_for_package
    audit_all_for_package("<distribution>")
```

`skip_rules=(...)` is for true convention-deviations that need a fix in
the spec, not for muffling a real violation. If a rule fires, fix the
package; don't add a skip.

### Deferring a rule to a migration campaign

The wrapper's `skip_rules=` kwarg only masked the *pytest* gate. The org
reusable workflow calls `scitex-dev ecosystem audit-all` directly, which
used to bypass it entirely — so `develop` could be green while unified
CI was red on identical code (measured in scitex-hub PR #433).

Declare deferrals in `<repo>/.scitex/dev/config.yaml` instead. Both
gates read the same source:

```yaml
audit:
  skip-rules:
    PS-139: "TQ-migration campaign — tracked in scitex-hub#412"
    "§6": "MCP parity lands with the umbrella-thinning wave"
```

Rules:

- **A rationale is mandatory.** A bare list (`- PS-139`) is rejected
  with exit 2, naming the entry. A deferral that cannot say why is the
  abandonment the mechanism exists to catch.
- **Honouring is never silent.** `audit-all` always prints a MASKED
  INVENTORY — the total, each rule id, its masked count and its written
  reason — in the normal output, not behind a flag.
- **The summary states both numbers**: unmasked errors *and* masked
  count. "0 errors" while 150 are masked is a lie of omission.
- The exit code is still driven by *unmasked* findings. Declaring one
  deferral never blanket-silences anything else.
- A declared rule matching zero violations is reported as removable —
  the campaign it waited on has landed.

This is distinct from the legacy `audit.skip` (bare codes, honoured only
by `audit-project`, applied silently) and from `audit.capabilities` (a
declared package *property* gating a fixed rule set).

## Track `.scitex/dev/config.yaml`

Audit whitelists (`audit.root-whitelist.files:` etc.) live at
`<repo>/.scitex/dev/config.yaml`. The directory is gitignored by default
(`.scitex/` is runtime state for local tooling), so add a `.gitignore`
exception so CI sees the same whitelist as local:

```gitignore
# .gitignore
.scitex/*
!.scitex/dev/
.scitex/dev/*
!.scitex/dev/config.yaml
```

A directory-level `.scitex/` exclusion blocks negation; switch to
file-level so the negation rule applies.
