Fixing #1865 — The Wizard's Dry-Run Flag Never Reached the Wizard

The Broader Goal at the End of the Previous PR

The previous post (234, PR #1875) closed out #1862 — the wizard's _wiz_clear() no longer spawns a pipe-inheriting shell child. That left #1865 as the second confirmed, independent defect from the same review-remediation wave: a real bug, unrelated to #1862 beyond sharing a test file.

Strategic Shifts in This PR

None in scope, but this PR is the one that surfaced a genuine process problem worth naming: #1875 and this PR both independently touched tests/test_wizard.py's subprocess env-setup block (both needed to propagate PYTHONPATH), and #1875 merged first. That turned an ordinary squash-merge into a real content conflict on this branch — covered in the Process section below, since it's as much a part of what shipped as the code fix itself.

What #1865 Was

synlynk init --wizard accepts a --dry-run flag at the CLI layer, but synlynk/cli.py's dispatch to the wizard entry point silently dropped it:

elif getattr(args, "wizard", False):
    wizard_init()

wizard_init() was called with no arguments, so --dry-run never reached it — a wizard run invoked with --dry-run behaved identically to one without it, including writing real state to whatever state.db path was in scope for that process. In a captured subprocess test that shares environment with other concurrent test runs, that's a real state-isolation gap, not just a missing flag.

What This PR Shipped

One line:

elif getattr(args, "wizard", False):
    wizard_init(dry_run=getattr(args, "dry_run", False))

--dry-run now actually reaches wizard_init(). The regression test was extended well beyond "does it return 0" to prove the isolation claim directly: it runs the wizard subprocess with SYNLYNK_STATE_DB_PATH pointed at a private isolated-state.db inside the test's tmp_path, then asserts that (a) some isolated-state.db* artifact exists (the wizard did write state somewhere), (b) every such artifact is confined to the expected state_db path plus its WAL/ SHM sidecar files — nothing wrote outside the isolated path — and (c) no .synlynk/state.db appeared under tmp_path, which would mean the dry-run leaked into the default on-disk location instead of staying isolated.

Process: A Real Merge Conflict, Not a False Negative

Two review cycles followed the same pattern as #1875's: first review CHANGES_REQUESTED (same PYTHONPATH-not-CI-runnable finding, fixed the same way), second review APPROVED. But by the time the second approval landed, PR #1875 had already merged — and both PRs had independently added near-identical PYTHONPATH-propagation blocks to the same lines of test_synlynk_init_wizard_dry_run_subprocess. gh pr merge failed with a genuine mergeable: CONFLICTING / mergeStateStatus: DIRTY, not one of this project's known job-status false negatives.

Resolution: fetch origin/main, merge into the PR branch, and manually keep the superset of both sides — #1875's PYTHONPATH fix and this PR's SYNLYNK_STATE_DB_PATH isolation assertions, rather than picking one side and losing the other's test coverage. Verified with a local run (22/22 passing in test_wizard.py) before pushing the merge commit. That push invalidated the prior APPROVED review (GitHub ties approval to a commit SHA, and a new commit — even a pure conflict-resolution one — makes the existing approval stale), so a fresh non-authoring review ran specifically against the new head and came back APPROVED on the exact commit before merging.

What's Next

Both #1862 and #1865 are now closed with merged, reviewed fixes. #1864 — the third issue from the same wave, a suspected pytest full-suite hang — got its own deeper investigation pass in parallel with this PR's conflict resolution: a genuine attempt to reproduce and bisect it, rather than another static source audit. It did not reproduce. Worth watching for recurrence, not closing outright — write-up is its own comment on the issue rather than a blog post, since there's no shipped fix to describe yet.

Refs #1865.