From 12c7fb5ff823755018d8ecdb4c7dbf97ab4a8f6f Mon Sep 17 00:00:00 2001 From: Anto Subash Date: Wed, 11 Nov 2026 01:33:22 +0100 Subject: [PATCH 1/2] fix(host): set path_separator = os in alembic.ini (#872) Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4 --- CHANGELOG.md | 1 + ...026-11-11-alembic-path-separator-design.md | 14 ++++++++ .../templates/host/alembic.ini | 3 ++ framework/cli/tests/test_cli_alembic_ini.py | 32 +++++++++++++++++++ host/alembic.ini | 3 ++ 5 files changed, 53 insertions(+) create mode 100644 docs/superpowers/specs/2026-11-11-alembic-path-separator-design.md create mode 100644 framework/cli/tests/test_cli_alembic_ini.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f77fc98f..49763e9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ All notable changes to this project are documented in this file. The format is b ### Fixed +- `host/alembic.ini`, both this repo's and the one `smpy new` scaffolds, sets `path_separator = os`. Alembic then splits `prepend_sys_path` and `version_locations` on the OS path separator. Without the setting, an app that adds either option gets a `DeprecationWarning` on every migration command, and a checkout path with a space is split in two. An existing app can add the same line under `[alembic]` (#872). - The `simple-module-creating` and `simple-module-conventions` skills, which ship inside `simple_module_cli`, linked the module layout standard as `../../docs/module-layout.md`. That path does not exist in an installed CLI. They now link https://docs.py.simplemodule.dev/module-layout, and a test fails if a shipped skill links a file outside `skills/`. `docs/module-layout.md` gains a "Downstream modules" section on copying this repo's root-layout test (`ALLOWED_ROOT`, `DOMAIN_AREAS`) into an app or a standalone module repo (#875). - `smpy new` warns when the CLI running it is older than the latest `simple_module_cli` on PyPI. A new app pins the framework to the CLI's own version, and `uvx --from simple_module_cli` quietly reuses a copy installed with `uv tool install`, so an old tool used to scaffold an outdated app without saying so. The warning names both fixes: `uv tool upgrade simple_module_cli`, or `uvx --from simple_module_cli@latest`. The check is best effort, with a 3-second timeout; offline or on any PyPI error it is skipped, and it never blocks the scaffold. A CLI that is itself a pre-release, dev or local build is never warned. The README, the CLI and hosting READMEs and the landing page's copy command now use `uvx --from simple_module_cli@latest`, and the installation and quickstart guides, the docs home and the make-commands reference say how to upgrade an installed CLI, with `pipx upgrade` as the alternative (#899). - The first-module guidance now agrees with itself. The guides (`docs/index.md`, quickstart, installation, Your first module) wire a new module with `smpy add ../modules/orders --path host`, the command `smpy create-module` prints, which adds it to `host/pyproject.toml` as a workspace source. They used to say `uv add ./modules/orders`, which in a `smpy new` workspace edits the root `pyproject.toml` instead. A flat app uses `smpy add ./modules/orders`. The landing page `smpy new` generates drops the `create-admin` step (the `/setup` wizard already creates the first administrator), lists the wiring and migration steps after `create-module`, and its "Read the docs" button opens the first-module guide on docs.py.simplemodule.dev instead of the GitHub repo. The first-module guide and the `simple-module-conventions` skill note that SQLModel's `Field(regex=...)` is accepted but not enforced on a DTO, and show a `field_validator` instead (#898). diff --git a/docs/superpowers/specs/2026-11-11-alembic-path-separator-design.md b/docs/superpowers/specs/2026-11-11-alembic-path-separator-design.md new file mode 100644 index 00000000..ca5f57f1 --- /dev/null +++ b/docs/superpowers/specs/2026-11-11-alembic-path-separator-design.md @@ -0,0 +1,14 @@ +# `path_separator = os` in both alembic.ini files (#872) + +Alembic warns ("No path_separator found in configuration; falling back to legacy +splitting...") and splits on spaces only when `prepend_sys_path` or `version_locations` +is set (alembic/config.py). Neither of our files sets them today. But a downstream host +that adds `prepend_sys_path = %(here)s` (smpy_modules did) gets the warning on every +migration command, and a checkout path containing a space breaks. + +Fix: add `path_separator = os` to `host/alembic.ini` and to the scaffold template. It +has been supported since Alembic 1.16, and our floor is 1.16.3. + +Test: `framework/cli/tests/test_cli_alembic_ini.py` copies each ini into a directory +whose name has a space and sets a two-entry `prepend_sys_path`. It expects no warning +(warnings are errors) and two whole paths. It fails without the line and passes with it. diff --git a/framework/cli/simple_module_cli/templates/host/alembic.ini b/framework/cli/simple_module_cli/templates/host/alembic.ini index 9861650b..0fab5ea6 100644 --- a/framework/cli/simple_module_cli/templates/host/alembic.ini +++ b/framework/cli/simple_module_cli/templates/host/alembic.ini @@ -4,6 +4,9 @@ # the repo root is what lets alembic read the repo-root .env — and therefore # the same SM_DATABASE_URL the app itself uses. script_location = %(here)s/migrations +# Split prepend_sys_path / version_locations on os.pathsep, not on spaces, so a +# checkout path with a space works and Alembic does not warn (GH #872). +path_separator = os sqlalchemy.url = # Run the project's ruff on every new revision, so `make migration` output diff --git a/framework/cli/tests/test_cli_alembic_ini.py b/framework/cli/tests/test_cli_alembic_ini.py new file mode 100644 index 00000000..c57a05d9 --- /dev/null +++ b/framework/cli/tests/test_cli_alembic_ini.py @@ -0,0 +1,32 @@ +"""Both alembic.ini files split path lists on os.pathsep (GH #872).""" + +from __future__ import annotations + +import os +import shutil +import warnings +from pathlib import Path + +import pytest +from alembic.config import Config +from simple_module_cli import app_project + +_REPO = Path(__file__).resolve().parents[3] +_INIS = [ + _REPO / "host" / "alembic.ini", + Path(app_project.__file__).parent / "templates" / "host" / "alembic.ini", +] + + +@pytest.mark.parametrize("ini", _INIS, ids=["repo-host", "scaffold-template"]) +def test_prepend_sys_path_with_a_space_splits_on_pathsep(tmp_path: Path, ini: Path) -> None: + """Without ``path_separator`` Alembic warns and splits ``my app`` into two paths.""" + copy = tmp_path / "my app" / "alembic.ini" + copy.parent.mkdir() + shutil.copy(ini, copy) + config = Config(str(copy)) + config.set_main_option("prepend_sys_path", f"%(here)s{os.pathsep}%(here)s/src") + with warnings.catch_warnings(): + warnings.simplefilter("error") + paths = config.get_prepend_sys_paths_list() + assert paths == [str(copy.parent), f"{copy.parent}/src"] diff --git a/host/alembic.ini b/host/alembic.ini index fa92fdcb..151cf74e 100644 --- a/host/alembic.ini +++ b/host/alembic.ini @@ -2,6 +2,9 @@ # Resolve script_location relative to this ini file, not the invocation cwd, # so `alembic -c host/alembic.ini ...` works from the repo root. script_location = %(here)s/migrations +# Split prepend_sys_path / version_locations on os.pathsep, not on spaces, so a +# checkout path with a space works and Alembic does not warn (GH #872). +path_separator = os sqlalchemy.url = # Run the project's ruff on every new revision, so `make migration` output From 41c40d6fe951d3e23028bb955487f106b69c7424 Mon Sep 17 00:00:00 2001 From: Anto Subash Date: Wed, 11 Nov 2026 01:34:52 +0100 Subject: [PATCH 2/2] fix: address code review findings (round 1, pass 1) Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49763e9f..56985ef2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ All notable changes to this project are documented in this file. The format is b ### Fixed -- `host/alembic.ini`, both this repo's and the one `smpy new` scaffolds, sets `path_separator = os`. Alembic then splits `prepend_sys_path` and `version_locations` on the OS path separator. Without the setting, an app that adds either option gets a `DeprecationWarning` on every migration command, and a checkout path with a space is split in two. An existing app can add the same line under `[alembic]` (#872). +- `host/alembic.ini`, both this repo's and the one `smpy new` scaffolds, sets `path_separator = os`. Alembic then splits `prepend_sys_path` and `version_locations` on the OS path separator. Without the setting, an app that adds either option gets a `DeprecationWarning` on every migration command, and a checkout path with a space is split in two. An existing app on Alembic 1.16 or newer can add the same line under `[alembic]` (#872). - The `simple-module-creating` and `simple-module-conventions` skills, which ship inside `simple_module_cli`, linked the module layout standard as `../../docs/module-layout.md`. That path does not exist in an installed CLI. They now link https://docs.py.simplemodule.dev/module-layout, and a test fails if a shipped skill links a file outside `skills/`. `docs/module-layout.md` gains a "Downstream modules" section on copying this repo's root-layout test (`ALLOWED_ROOT`, `DOMAIN_AREAS`) into an app or a standalone module repo (#875). - `smpy new` warns when the CLI running it is older than the latest `simple_module_cli` on PyPI. A new app pins the framework to the CLI's own version, and `uvx --from simple_module_cli` quietly reuses a copy installed with `uv tool install`, so an old tool used to scaffold an outdated app without saying so. The warning names both fixes: `uv tool upgrade simple_module_cli`, or `uvx --from simple_module_cli@latest`. The check is best effort, with a 3-second timeout; offline or on any PyPI error it is skipped, and it never blocks the scaffold. A CLI that is itself a pre-release, dev or local build is never warned. The README, the CLI and hosting READMEs and the landing page's copy command now use `uvx --from simple_module_cli@latest`, and the installation and quickstart guides, the docs home and the make-commands reference say how to upgrade an installed CLI, with `pipx upgrade` as the alternative (#899). - The first-module guidance now agrees with itself. The guides (`docs/index.md`, quickstart, installation, Your first module) wire a new module with `smpy add ../modules/orders --path host`, the command `smpy create-module` prints, which adds it to `host/pyproject.toml` as a workspace source. They used to say `uv add ./modules/orders`, which in a `smpy new` workspace edits the root `pyproject.toml` instead. A flat app uses `smpy add ./modules/orders`. The landing page `smpy new` generates drops the `create-admin` step (the `/setup` wizard already creates the first administrator), lists the wiring and migration steps after `create-module`, and its "Read the docs" button opens the first-module guide on docs.py.simplemodule.dev instead of the GitHub repo. The first-module guide and the `simple-module-conventions` skill note that SQLModel's `Field(regex=...)` is accepted but not enforced on a DTO, and show a `field_validator` instead (#898).