feat(paths): centralize data/template/plugin dirs under one Documents app root - #29
Conversation
…app root Single configurable app_root (default ~/Documents/ExLabWizard) with templates/plugins/data derived; removes per-equipment local_root and the creation-vs-poller divergence; repurposes the paths setup gate to a writability check. Clean break, no migration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… app root Collapse PathsConfig's three roots (templates_dir/plugin_dir/local_root) into a single configurable `app_root` (default ~/Documents/ExLabWizard) with derived read-only properties; remove the per-equipment EquipmentConfig.local_root. - paths.py: os_documents_path / default_app_root / ensure_app_dirs / app_root_writable (Windows Known Folder + USERPROFILE/home fallbacks); add DISPLAY_NAME constant + _display_name() test-mode suffix. - Unify equipment data-dir derivation across run creation, the quiescence poller, and the validator audit roots on config.paths.local_root (== <app_root>/data/<id>), eliminating two divergence sources. - Repurpose the setup gate: INCOMPLETE_MISSING_PATHS -> INCOMPLETE_PATHS_UNWRITABLE, driven by an injected paths_writable flag (callers compute app_root_writable). - Settings: single "Data folder" input + derived read-only labels; the equipment wizard drops its local-root step; ensure_app_dirs wired at save and tray bring-up. - Clean break (pre-release): no migration shims; legacy configs that still carry the retired keys fail validation and route to the setup wizard. Tests: full unit+integration green (2607 passed); new regression pins creation/poller/validator data-dir agreement; e2e flows + page objects updated for the single-input UI. ruff + mypy clean; coverage 91.17%. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code reviewFound 1 issue:
ExLabWizard/src/exlab_wizard/config/models.py Lines 116 to 136 in 3f8d0d4 ExLabWizard/src/exlab_wizard/config/loader.py Lines 139 to 168 in 3f8d0d4 🤖 Generated with Codex - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Code review updatePer maintainer guidance, legacy-schema compatibility is intentionally out of scope here; disregard my earlier legacy-config comment. I found 4 current-shape issues:
ExLabWizard/src/exlab_wizard/config/models.py Lines 114 to 121 in 3f8d0d4 ExLabWizard/src/exlab_wizard/ui/pages/settings.py Lines 517 to 519 in 3f8d0d4 ExLabWizard/src/exlab_wizard/paths.py Lines 357 to 376 in 3f8d0d4
ExLabWizard/src/exlab_wizard/ui/mount.py Lines 1857 to 1861 in 3f8d0d4
ExLabWizard/tests/e2e/test_flow_00_full_lifecycle.py Lines 256 to 260 in 3f8d0d4
ExLabWizard/docs/UX_INTERACTIONS.md Lines 24 to 44 in 3f8d0d4 ExLabWizard/tests/e2e/test_ux_documentation.py Lines 66 to 83 in 3f8d0d4 |
- test_flow_00_full_lifecycle asserted the derived data root appears in the saved config.yaml, but the config now persists only `app_root` (data/ is derived) -- assert `app_root` instead. Run-location assertions keep using the derived `data_root`. - Regenerate docs/UX_INTERACTIONS.md from the updated ux_catalog.py (single "Data folder" app-root input; per-dir + per-equipment local-root rows gone). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Collapses the three independently-configured roots (
paths.templates_dir/paths.plugin_dir/paths.local_root) into a singlepaths.app_rootthat defaults under the OS Documents folder (~/Documents/ExLabWizard).templates/,plugins/, and the experimentdata/root are now derived read-only properties, and each equipment's data lives at<app_root>/data/<EQUIPMENT_ID>/….This also removes the per-equipment
EquipmentConfig.local_root, which fixes a latent data-loss-class bug: run creation used the global root while the auto-sync quiescence poller (and the validator's audit roots) used the per-equipment one — if they diverged, runs were written to one tree and watched in another. All three now deriveconfig.paths.local_root(<app_root>/data/<id>).Design:
docs/superpowers/specs/2026-06-01-centralize-data-dir-design.md.Key changes
paths.py:os_documents_path()(macOS~/Documents, Windows Known-Folder viaSHGetKnownFolderPathwith%USERPROFILE%/home fallbacks, Linux$XDG_DOCUMENTS_DIR),default_app_root(),ensure_app_dirs(),app_root_writable();DISPLAY_NAMEconstant + test-mode (-test) suffix.PathsConfig: single storedapp_root; deriveddata_root/local_root/templates_dir/plugin_dir(onlyapp_rootserializes).INCOMPLETE_MISSING_PATHS→INCOMPLETE_PATHS_UNWRITABLE, driven by an injectedpaths_writableflag (callers computeapp_root_writable).ensure_app_dirsruns on save and at tray bring-up.Test Plan
ruff check .+ruff format --check .— cleanmypy src/exlab_wizard— 0 issues (167 files)pytest tests/unit tests/integration --cov-fail-under=91— 2607 passed, 22 skipped, coverage 91.17%tests/unit/test_data_root_agreement.pypins creation/poller/validator data-dir agreementpytest tests/e2e— flows/page-objects updated for the single-input UI and are lint-clean, but not executed in this environment (needs Playwright + chromium + a live server); please run before merge.Notes / follow-ups
Validator.from_configgatesstaging_rootongetattr(orch, "enabled", …), butOrchestratorConfighas noenabledfield — so that branch is always dead. Unrelated to this refactor (the production_build_validatorpath is correct); flagged for a separate change.🤖 Generated with Claude Code