Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / fail2ban Regex (push) Has been cancelled
CI / Compose Bucket Idempotency (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Failing after 2m35s
CI / OCR Service Tests (pull_request) Successful in 16s
CI / Backend Unit Tests (pull_request) Successful in 4m21s
CI / fail2ban Regex (pull_request) Successful in 39s
CI / Compose Bucket Idempotency (pull_request) Failing after 11s
Branches gate was blocking CI at 75% measured coverage. The 80% floor suffers Istanbul parent/child denominator coupling (long-tail grind, per #496) that makes the remaining gap disproportionately costly to close. Drop branches to 75 to match current state; leave lines/functions/ statements at 80. ADR-013 documents the rationale and the ratchet rule for raising the gate back incrementally. Closes #556 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
93 lines
4.9 KiB
Markdown
93 lines
4.9 KiB
Markdown
# 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 approximately **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 30–100+ 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 44–51)
|