# PLAN — v5.49.10: Untested Modules Coverage Wave 5 (backlog tail)

**Status:** SHIPPED 2026-06-10 (tag `v5.49.10`). **Backfilled retroactively** — wave 5 was executed ad-hoc from a live re-audit; this doc records it as-built for continuity with the wave 1–4 plans.

**Branches (as-built):** `feat/v5.49.10-wave-5-group-a`, `feat/v5.49.10-wave-5-group-b` off master. Merged + deleted.

**Effort (actual):** ~1 hour wall-clock. 2 parallel worktree agents.

---

## 1. Background

Coverage debt clearance, final wave:
- Wave 1 (v5.49.6): 10 modules, 315 tests.
- Wave 2 (v5.49.7): 10 modules, 302 tests.
- Wave 3 (v5.49.8): 11 modules, 134 tests.
- Wave 4 (v5.49.9): 10 modules, 291 tests.
- **Wave 5 (v5.49.10):** the tail — every remaining module from the original `<10%` audit list.

Rolling audit: `docs/UNTESTED_MODULES_V5_49_6.md`.

---

## 2. Re-audit (Phase 0) — done before any test writing

Prior audits flagged ~18 candidate modules remaining after wave 4. A verification re-audit (per-module `coverage run --include=<path> -m pytest ... -n 0`; pytest-cov breaks on this env's Py3.14/numpy) found **only 6 genuinely needed tests**. The other 12 were false positives or already-covered:

- **False positives / already covered (DROP):** `platform_paths.py` 88%, `scripts/wiki_snapshot.py` 100%, `hooks/session-start-context.py` 83%, `cli/config.py` 68%, `cli/vacuum.py` 95%, `update/check.py` 100%, `storage/bitemporal.py` 94%, `cli/viz.py` 78%.
- **Underscore-lib modules already covered (DROP):** `hooks/subagent_stop.py` 87%, `hooks/instructions_loaded.py` 72%, `hooks/file_changed.py` 63%, `hooks/subagent_start.py` 75%. These are SEPARATE files from the hyphen entry scripts (`file-changed.py` etc.) covered in wave 3; both now covered.

**Key dedup lesson:** `yadgar/hooks/` holds BOTH hyphenated entry scripts AND underscore library modules. Do not double-count them.

---

## 3. Scope — Wave 5 go-list (6 modules, 114 stmts)

| # | Module | Stmts | Pre-cov | Test file |
|---|---|---|---|---|
| 1 | `yadgar/cli/_shared.py` | 20 | 0% | `yadgar/tests/test_cli_shared_module.py` |
| 2 | `yadgar/cli/restore.py` | 15 | 40% | `yadgar/tests/test_cli_restore_module.py` |
| 3 | `yadgar/cli/capture.py` | 23 | 48% | `yadgar/tests/test_cli_capture_module.py` |
| 4 | `yadgar/cli/drain.py` | 14 | 43% | `yadgar/tests/test_cli_drain_module.py` |
| 5 | `yadgar/hooks/db-lockdown-check.py` | 24 | 0% | `yadgar/tests/test_hook_db_lockdown_check_unit.py` |
| 6 | `yadgar/scripts/yadgar_setup.py` | 18 | 0% | `yadgar/tests/test_scripts_yadgar_setup_module.py` |

Resolved decisions A–G unchanged from wave-4 plan (`pytest-cov` extras; <10% threshold; ≥80% target or documented floor; mock at boundaries; refactor paused).

---

## 4. Parallel worktree split (as-built)

### Group A — `feat/v5.49.10-wave-5-group-a` (cli cluster)
`cli/_shared.py`, `cli/restore.py`, `cli/capture.py`, `cli/drain.py`.
**Rationale:** `_shared.py` is imported by `drain.py` + `restore.py` — testing the cli cluster together shares the engine-init mocks.
**Agent:** `general-purpose`, sonnet, `isolation: worktree`.

### Group B — `feat/v5.49.10-wave-5-group-b` (hook + script)
`hooks/db-lockdown-check.py`, `scripts/yadgar_setup.py`.
**Rationale:** hyphenated hook filename requires `importlib.util.spec_from_file_location` loading (cannot `import`); pattern mirrors `test_session_start_context_hook.py`. `yadgar_setup.py` is the Python shim (existing tests only assert the `.sh` script text).
**Agent:** `general-purpose`, sonnet, `isolation: worktree`.

---

## 5. Results (as-built)

| Group | Module | Post-cov | New tests | Notes |
|---|---|---|---|---|
| A | `cli/_shared.py` | 100% | — | engine-init mocked |
| A | `cli/restore.py` | 100% | — | |
| A | `cli/capture.py` | 100% | — | |
| A | `cli/drain.py` | 100% | — | |
| B | `hooks/db-lockdown-check.py` | 96% | — | importlib-loaded; floor: `__main__` guard |
| B | `scripts/yadgar_setup.py` | 100% | — | |

Group A: 54 tests. Group B: 33 tests. **Wave 5 total: 87 tests.**

**Cumulative (waves 1–5): 1129 new tests, 47 modules. Original `<10%` backlog EXHAUSTED.**

---

## 6. Acceptance gates (met)

- 6/6 modules ≥96% coverage (all ≥80% target met; 1 documented floor: `__main__` guard).
- All existing tests green post-merge (87/87 wave-5 tests pass).
- Pre-commit clean. No `--no-verify` / `--no-gpg-sign`. `check-versions` passed.
- Both group branches merged cleanly (test-only, no conflicts).

---

## 7. Ship sequence (as-built)

1. Groups A + B ran in parallel worktrees.
2. Each: tests pass → commit → merge `--no-ff` to master.
3. Single bump commit 5.49.9 → 5.49.10 (`7868801`) + CHANGELOG + audit doc.
4. Trailing fix: `.gitignore` += `.venv-test/` (`391cf1e`) — stale untracked venv was polluting the sdist and breaking the wheel build during PyPI publish.
5. Shipped all targets: master → codeberg, container `docker.io/openfantasy/yadgar:5.49.10` → dockerhub, PyPI 5.49.10 (wheel + sdist), nix `yadger_core_version` → 5.49.10, daemon switched. Tag `v5.49.10` (backfilled).

---

## 8. Non-goals

Same as waves 1–4. No refactor. No integration tests. No source edits beyond the `.gitignore` packaging fix.

---

## 9. References

- `docs/PLAN_V5_49_6.md` … `docs/PLAN_V5_49_9.md` (Waves 1–4)
- `docs/UNTESTED_MODULES_V5_49_6.md` (rolling audit — wave 5 re-audit + results sections)
- `docs/PLAN_V5_90.md` (refactor track — paused)
