- Make dry-run runtime roots genuinely optional instead of projecting the preview path from `plan.py`. The ripple is 12 production files: `request.py`, `plan.py`, `context.py`, `__init__.py`, `composition_spawn.py`, `artifact_io.py`, `process/runner.py`, `streaming_runner.py`, `../state/artifact_store.py`, `../ops/runtime.py`, `../ops/spawn/prepare.py`, and `../ops/spawn/execute_runner.py`. `LocalStore` must refuse writes when its root is `None`, while preparation and binding skip runtime-path derivations until an execution path proves a materialized root. Done when `get_dry_run_preview_root` (`state/user_paths.py`) and its two callers (`launch/plan.py`, `ops/spawn/prepare.py`) are deleted.
