Implementation Plan: Root-vacuous chmod tests + TestOrdering isolation
Branch: claude/happy-rubin-aq1qc6 | Date: 2026-10-04 | Spec: spec.md
Summary
This is a test-quality mission with one dead-code deletion. Every test change is red-first. It is proven against a planted break in product code, and the break is reverted and never committed (C-002).
Technical Context
Language/Version: Python 3.11 Testing: pytest; tests/_support/eacces.py (deny_open_in, deny_path_method) from PR #5656 Constraints: Run as root (uid 0) so the root verdict is the one observed. No full heavy suites (NO_FULL_HEAVY_SUITES_IN_MISSION).
Charter Check
- Standing Order #4 (test remediation): judge the test, not the blame. Each site is classified as follows.
- The three read-only
.gitignoretests are valid; they are proven with no change. - The two seam-injected sites are already valid; they are proven with no change.
TestStoreUnreachableis vacuous; it is fixed by seam injection.TestOrderingis stale (it depends on a hidden import); it is re-pinned.- Standing Order #2 (campsite):
GitignoreManager._atomic_writeis dead insrc/. It is deleted inside the file set this mission already touches. - Smallest viable diff: there are no product behaviour changes. The only
src/edit is deleting the dead method and its two now-unused imports.
Design decisions
1. History-db seam. UpgradeAttemptStore opens SQLite through sqlite3.connect, a C-level open that neither eacces helper reaches. _connect's first filesystem call is self._db_path.parent.mkdir(...), so deny_path_method(monkeypatch, "mkdir", db_dir) denies the open with the shared helper (C-001). That is the real failure for an unwritable, not-yet-created history dir. A recording wrapper around _connect proves the denial reached the store. Without it, a runner that skipped recording would also "return normally", which is exactly why the old test was vacuous. 2. Retarget, don't delete, the _atomic_write tests. The symlink-race intent still applies to the live writer write_gitignore_text.
3. #5186 fixture. A class-scoped autouse fixture calls auto_discover_migrations(). It is not a module-level import, because other tests call MigrationRegistry.clear() (C-003). The count==1 asserts stay (C-004).
- The mkstemp-hooked race test never fired against the live writer, which uses
NamedTemporaryFileand so never callstempfile.mkstemp. It is re-hooked onNamedTemporaryFile. - The probe test now pins the writer's own symlink refusal.
Project Structure
src/specify_cli/gitignore_manager.py # delete _atomic_write (+2 unused imports)
tests/cross_cutting/test_gitignore_manager_unit.py # retarget 2 tests, fix 1 docstring
tests/specify_cli/readiness/test_upgrade_ux_migration.py # seam-inject TestStoreUnreachable
tests/specify_cli/upgrade/migrations/test_provision_kitty_env.py # TestOrdering fixture
Implementation Concern Map
IC-01 — Root-vacuous denied-write tests (#5654)
FR-001, FR-002, FR-003, FR-004, FR-006. WP01.
IC-02 — TestOrdering isolation (#5186)
FR-005. WP01.