Files
familienarchiv/docs/adr/013-client-branches-coverage-threshold.md
Marcel 582191d014
Some checks failed
CI / Backend Unit Tests (pull_request) Successful in 4m20s
CI / fail2ban Regex (pull_request) Successful in 38s
CI / Compose Bucket Idempotency (pull_request) Failing after 11s
CI / Unit & Component Tests (pull_request) Failing after 2m35s
CI / OCR Service Tests (pull_request) Successful in 16s
CI / Unit & Component Tests (push) Failing after 2m35s
CI / OCR Service Tests (push) Successful in 17s
CI / Backend Unit Tests (push) Successful in 4m18s
CI / fail2ban Regex (push) Successful in 39s
CI / Compose Bucket Idempotency (push) Failing after 11s
docs(adr-013): set exact baseline to 75% (confirmed in CI)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 09:03:45 +02:00

93 lines
4.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR 013 — Client-Project Branch Coverage Threshold
**Status:** Accepted
**Date:** 2026-05-14
**Issues:** [#556 — threshold drop](https://git.raddatz.cloud/marcel/familienarchiv/issues/556) · [#496 — long-tail-grind tracking](https://git.raddatz.cloud/marcel/familienarchiv/issues/496)
---
## Context
The browser-mode component test suite (`vitest.client-coverage.config.ts`) enforces Istanbul coverage thresholds across `lines`, `functions`, `branches`, and `statements`. The `branches` metric was set to 80%, but the codebase sits at **75%** — below the gate — causing every CI run of `unit-tests` and `coverage-flake-probe` to fail on this check alone, even when all tests are green.
**Measured baseline (2026-05-14, branch `feat/issue-553-birpc-async-mock-factory`, head `2e6cc346`):**
```
branches: 75% (below the 80% gate — reason for this ADR)
lines: ≥ 80%
functions: ≥ 80%
statements: ≥ 80%
```
Reproducer:
```bash
cd frontend && npm ci && npx vitest run -c vitest.client-coverage.config.ts --coverage
```
### The long-tail-grind problem
In Istanbul's branch accounting, when a child component gains test coverage its branches are added to the parent's denominator. A child moving from 40% → 80% coverage can drag a parent from 78% → 72% because more branches in the call graph become reachable and must be covered. This is not a bug — it is how branch accounting works — but it means that on a large SvelteKit application the denominator grows with every coverage improvement, making an arbitrary 80% ceiling a constant grind. Per #496, the expected cost to reach 80% branches from 75% is 30100+ commits with no guarantee of stability.
### Why this layer is different
The 80% branch floor used for backend unit/integration tests is appropriate for Java service code and permission logic. Browser-mode component coverage measures Svelte template branches: conditional class bindings, `{#if}` blocks, empty/loaded/error state guards. These branches have a fundamentally different accounting model and a higher inherent denominator. This ADR **only** lowers the browser-mode component gate; the backend test coverage gates are unaffected.
### Security-relevant uncovered components
The following auth/permission-boundary components currently have low or zero branch coverage. When ratchet-up work begins (see below), these are the highest-priority targets:
- `src/routes/login/+page.svelte`
- `src/routes/forgot-password/+page.svelte`
- `src/routes/reset-password/+page.svelte`
- `src/routes/register/+page.svelte`
Note: the 75% figure already reflects the absence of coverage on these files. Lowering the gate does not create this gap — it makes the existing state legible.
---
## Decision
Drop the `branches` threshold from `80``75` in `frontend/vitest.client-coverage.config.ts`. Leave `lines`, `functions`, and `statements` at `80`.
The 75% figure matches the measured current state, allowing CI to pass while deliberate coverage improvement work (tracked in #496) continues without blocking other PRs. The asymmetry in the thresholds block is intentional and documented with an inline comment pointing here.
---
## Ratchet Rule
The branches threshold ratchets **up by 3 percentage points** when the rolling 3-PR-average client-project branches figure on `main` stays at or above `threshold + 3pp` for ≥ 30 consecutive days. Direction is **up-only** — never lower the floor below 75 without a new ADR superseding this one. Manual today (verify before any `vitest.client-coverage.config.ts` edit); a future automation issue may codify the check.
Concretely:
- When `main` sustains ≥ 78% branches across 3 consecutive PRs for 30 days → raise gate to 78%
- When `main` sustains ≥ 81% branches across 3 consecutive PRs for 30 days → raise gate back to 80%
---
## Non-goals
- **Not** raising actual branch coverage — that is #496's job, tracked separately.
- **Not** touching the server-project coverage configuration (`vitest.config.ts`) — only the client project hits the long-tail-grind pattern.
- **Not** removing or relaxing any existing test files, `skipIf` guards, or axe-playwright accessibility runs.
---
## Consequences
**Easier:**
- CI unblocked — `unit-tests` and `coverage-flake-probe` jobs pass when all tests are green
- The ratchet rule creates a concrete, observable path back to 80%
**Harder:**
- The gate now has near-zero headroom — any branch regression that drops below 75% will fail CI immediately
- The 75% floor must not be treated as a permanent ceiling; the ratchet discipline requires active attention
---
## References
- [#496 — Branch coverage long-tail grind](https://git.raddatz.cloud/marcel/familienarchiv/issues/496)
- [#556 — This threshold drop](https://git.raddatz.cloud/marcel/familienarchiv/issues/556)
- [ADR 012 — Browser-Mode Test Mocking Strategy](./012-browser-test-mocking-strategy.md)
- `frontend/vitest.client-coverage.config.ts` — thresholds block (lines 4451)