# Local 2×2 route E2E This harness runs one bot-free control stack plus four isolated data-node Compose projects. Every data node contains its own qBittorrent, Syncthing, and archive-client containers and its own bind-mounted config, secrets, state, backup, qB data, and sync roots. The stacks share only the explicitly named `archive-control-e2e` network. The initial scenario starts with no route folders. With the control policy set to `eager_mesh`, two cache and two archive registrations must produce four independent, bidirectionally verified Syncthing routes. The assertion talks to the loopback-only test adapter through a curl sidecar sharing the control container's network namespace; Telegram is not initialized. Run from the archive-clients checkout: ```bash ./e2e/scripts/up.sh ./e2e/scripts/logs.sh ./e2e/scripts/down.sh ``` `up.sh` defaults `ARCHIVE_CONTROL_SOURCE` to the sibling playground `mogic-bot/mogic-repo` checkout. Set that environment variable to test another control worktree. `E2E_WAIT_SECONDS` overrides the 360-second assertion deadline. `down.sh` removes only the five exact Compose projects and the labelled E2E network; it intentionally retains all bind-mounted runtime state. Set `E2E_RUN_TRANSFER=1` to follow route verification with a real cache-1 → archive-1 transfer, covered cache-1 eviction, and archive-1 → cache-2 unarchive. The scenario creates a deterministic one-file torrent in the isolated cache qBittorrent, uses fresh preview revisions for all jobs, waits for the durable five/three/five-step flows, and verifies target qBittorrent selections, archive retention, safe cache removal, and target file digests. Set `E2E_RUN_COMPLEX=1` to run the Phase 8 selective and recovery matrix. It uses a four-file torrent with complementary cache selections to verify selective archive placement, multi-job archive/cache merging, union coverage across two archive nodes, uncovered/no-op/stale-preview rejection, and retention of shared and unknown files during eviction. It then pauses the test scheduler at durable boundaries to verify record-only queued cancellation, rollback after target materialization, control restart after source staging, and command replay after a target client disconnect. The final hostile cases change the source selection and make an absent target appear after confirmation; each must fail before commit with its exact precondition reason. qBittorrent is also checked for zero downloaded bytes at the replayed target. Finally, a wrong-size pre-existing target and an unwritable target root must fail without committing or losing the source; the permission fixture restores the target root's original mode even on an aborted run. The scheduler pause/resume endpoints exist only on the loopback test adapter; normal control and Telegram orchestration always use the automatic scheduler. The Syncthing 2.1.2 and LinuxServer qBittorrent multi-platform image indexes are digest-pinned. Runtime secrets are generated with mode 0600 and ignored by Git. The qBittorrent test config limits its authentication bypass to the isolated E2E network and must never be reused for deployment.