From ea908878aff6fe5efe5e80237585a17f926d7fd8 Mon Sep 17 00:00:00 2001 From: Dominikus Nold Date: Wed, 4 Mar 2026 20:43:40 +0100 Subject: [PATCH 1/2] feat: remove flat command shims from grouped registry --- CHANGELOG.md | 4 ++ README.md | 20 ++++-- docs/getting-started/README.md | 12 +++- docs/reference/commands.md | 17 +++--- .../TDD_EVIDENCE.md | 46 ++++++++++++++ .../tasks.md | 36 +++++------ src/specfact_cli/registry/module_packages.py | 61 +------------------ .../test_category_group_routing.py | 13 ++-- tests/unit/registry/test_category_groups.py | 45 ++++++++++---- .../registry/test_module_packages.py | 43 +++++++++++++ 10 files changed, 184 insertions(+), 113 deletions(-) create mode 100644 openspec/changes/module-migration-04-remove-flat-shims/TDD_EVIDENCE.md diff --git a/CHANGELOG.md b/CHANGELOG.md index de7ba9b9..ee9ad660 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,10 @@ All notable changes to this project will be documented in this file. - Marketplace package loader now resolves namespaced command entrypoints (`src///app.py`) for installed modules. - Installed bundle detection now infers `specfact-*` bundle IDs from namespaced module names when manifest `bundle` metadata is absent. +### Removed + +- **BREAKING**: Removed flat root command shims (OpenSpec change `module-migration-04-remove-flat-shims`, issue [#330](https://github.com/nold-ai/specfact-cli/issues/330)). Use grouped commands only, for example `specfact code validate` instead of `specfact validate`. + ### Deprecated - Legacy flat import paths under `specfact_cli.modules.*` are deprecated in favor of bundle namespaces (`specfact_project.*`, `specfact_backlog.*`, `specfact_codebase.*`, `specfact_spec.*`, `specfact_govern.*`) and are planned for removal in the next major release. diff --git a/README.md b/README.md index 4aa459f0..3b522729 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,14 @@ specfact code validate sidecar init my-project /path/to/repo specfact code validate sidecar run my-project /path/to/repo ``` +### Migration Note (Flat Commands Removed) + +As of `0.40.0`, flat root commands are removed. Use grouped commands: + +- `specfact validate ...` -> `specfact code validate ...` +- `specfact plan ...` -> `specfact project plan ...` +- `specfact policy ...` -> `specfact backlog policy ...` + ### Backlog Bridge (60 seconds) SpecFact's USP is closing the drift gap between **backlog -> specs -> code**. @@ -72,7 +80,7 @@ specfact backlog daily ado --ado-org --ado-project "" --state any specfact backlog refine ado --ado-org --ado-project "" --id --preview # 3) Keep backlog + spec intent aligned (avoid silent drift) -specfact policy validate --group-by-item +specfact backlog policy validate --group-by-item ``` For GitHub, replace adapter/org/project with: @@ -140,8 +148,8 @@ Most tools help **either** coders **or** agile teams. SpecFact does both: Recommended command entrypoints: - `specfact backlog ceremony standup ...` - `specfact backlog ceremony refinement ...` -- `specfact policy validate ...` -- `specfact policy suggest ...` +- `specfact backlog policy validate ...` +- `specfact backlog policy suggest ...` What the Policy Engine does in practice: - Turns team agreements (DoR, DoD, flow checks) into executable checks against your real backlog data. @@ -149,9 +157,9 @@ What the Policy Engine does in practice: - Generates patch-ready suggestions so teams can fix policy gaps quickly without guessing. Start with: -- `specfact policy init --template scrum` -- `specfact policy validate --group-by-item` -- `specfact policy suggest --group-by-item --limit 5` +- `specfact backlog policy init --template scrum` +- `specfact backlog policy validate --group-by-item` +- `specfact backlog policy suggest --group-by-item --limit 5` **Try it now** diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index dc8a4bdd..7417f8ee 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -25,7 +25,7 @@ SpecFact runs on a lifecycle-managed module system. ```bash # CLI-only mode (works with uvx, no installation needed) -uvx specfact-cli@latest import from-code my-project --repo . +uvx specfact-cli@latest project import from-code my-project --repo . # Interactive AI Assistant mode (requires pip install + specfact init) # See First Steps guide for IDE integration setup @@ -35,7 +35,7 @@ uvx specfact-cli@latest import from-code my-project --repo . ```bash # CLI-only mode (bundle name as positional argument) -uvx specfact-cli@latest plan init my-project --interactive +uvx specfact-cli@latest project plan init my-project --interactive # Interactive AI Assistant mode (recommended for better results) # Requires: pip install specfact-cli && specfact init @@ -43,6 +43,14 @@ uvx specfact-cli@latest plan init my-project --interactive **Note**: Interactive AI Assistant mode provides better feature detection and semantic understanding, but requires `pip install specfact-cli` and IDE setup. CLI-only mode works immediately with `uvx` but may show 0 features for simple test cases. +### Migration Note (0.40.0) + +Flat root commands were removed. Use grouped command forms: + +- `specfact validate ...` -> `specfact code validate ...` +- `specfact plan ...` -> `specfact project plan ...` +- `specfact policy ...` -> `specfact backlog policy ...` + First-run bundle selection examples: ```bash diff --git a/docs/reference/commands.md b/docs/reference/commands.md index c0136101..0806c40c 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -7,15 +7,21 @@ permalink: /reference/commands/ # Command Reference SpecFact CLI now ships a lean core. Workflow commands are installed from marketplace bundles. +Flat root-level compatibility shims were removed in `0.40.0`; use category-group commands only. ## Top-Level Commands -Fresh install includes only: +Root command surface includes core commands and installed category groups only: - `specfact init` -- `specfact backlog auth` +- `specfact auth` - `specfact module` - `specfact upgrade` +- `specfact code ...` +- `specfact backlog ...` +- `specfact project ...` +- `specfact spec ...` +- `specfact govern ...` Use `specfact init --profile ` (or `--install `) to install workflow bundles. @@ -39,7 +45,7 @@ After bundle install, command groups are mounted by category: | `nold-ai/specfact-spec` | `spec` | `contract`, `api`, `sdd`, `generate` | | `nold-ai/specfact-govern` | `govern` | `enforce`, `patch` | -## Removed Flat Commands +## Migration: Removed Flat Commands Flat compatibility shims were removed in this change. Use grouped commands. @@ -61,11 +67,6 @@ Flat compatibility shims were removed in this change. Use grouped commands. | `specfact enforce ...` | `specfact govern enforce ...` | | `specfact patch ...` | `specfact govern patch ...` | -Legacy reference kept for release-doc parity: - -- `specfact patch apply --dry-run` -- `specfact patch apply --write` - ## Common Flows ```bash diff --git a/openspec/changes/module-migration-04-remove-flat-shims/TDD_EVIDENCE.md b/openspec/changes/module-migration-04-remove-flat-shims/TDD_EVIDENCE.md new file mode 100644 index 00000000..c8bd3ecc --- /dev/null +++ b/openspec/changes/module-migration-04-remove-flat-shims/TDD_EVIDENCE.md @@ -0,0 +1,46 @@ +# TDD Evidence: module-migration-04-remove-flat-shims + +## Pre-Implementation Failing Run + +- Timestamp: 2026-03-04T20:23:10+01:00 +- Command: + +```bash +PYTHONPATH=/home/dom/git/nold-ai/specfact-cli-worktrees/feature/module-migration-04-remove-flat-shims/src \ +/home/dom/git/nold-ai/specfact-cli/.venv/bin/python -m pytest \ +tests/unit/specfact_cli/registry/test_module_packages.py \ +-k grouped_registration_does_not_register_flat_shim_commands -v +``` + +- Result: **FAILED** (expected red phase) +- Failure summary: `validate` was still registered at root (`{'code', 'validate'}`), proving flat shim machinery was active. + +## Post-Implementation Passing Run + +- Timestamp: 2026-03-04T20:24:03+01:00 +- Commands: + +```bash +PYTHONPATH=/home/dom/git/nold-ai/specfact-cli-worktrees/feature/module-migration-04-remove-flat-shims/src \ +/home/dom/git/nold-ai/specfact-cli/.venv/bin/python -m pytest \ +tests/unit/specfact_cli/registry/test_module_packages.py \ +-k grouped_registration_does_not_register_flat_shim_commands -v + +PYTHONPATH=/home/dom/git/nold-ai/specfact-cli-worktrees/feature/module-migration-04-remove-flat-shims/src \ +/home/dom/git/nold-ai/specfact-cli/.venv/bin/python -m pytest \ +tests/unit/registry/test_category_groups.py \ +-k "flat_validate_is_not_found_in_copilot_mode or flat_validate_is_not_found_in_cicd_mode" -v + +PYTHONPATH=/home/dom/git/nold-ai/specfact-cli-worktrees/feature/module-migration-04-remove-flat-shims/src \ +/home/dom/git/nold-ai/specfact-cli/.venv/bin/python -m pytest \ +tests/integration/test_category_group_routing.py \ +-k validate_flat_command_is_not_available -v +``` + +- Result: **PASSED** +- Passing summary: flat `validate` is no longer registered as a root command; category-only behavior is enforced for this shim-removal scope. + +## Scope Note + +- This change intentionally runs shim-removal-focused tests only. +- Broader suite migration/cleanup debt remains out of scope for this change and is deferred per migration planning. diff --git a/openspec/changes/module-migration-04-remove-flat-shims/tasks.md b/openspec/changes/module-migration-04-remove-flat-shims/tasks.md index ae1732b0..7156c243 100644 --- a/openspec/changes/module-migration-04-remove-flat-shims/tasks.md +++ b/openspec/changes/module-migration-04-remove-flat-shims/tasks.md @@ -4,38 +4,38 @@ TDD/SDD order enforced. Version series: **0.40.x**. ## 1. Branch and prep -- [ ] 1.1 Create feature branch from `dev`: `feature/module-migration-04-remove-flat-shims` -- [ ] 1.2 Ensure module-migration-01 is merged to dev (category groups and shims exist) +- [x] 1.1 Create feature branch from `dev`: `feature/module-migration-04-remove-flat-shims` +- [x] 1.2 Ensure module-migration-01 is merged to dev (category groups and shims exist) ## 2. Spec and tests first -- [ ] 2.1 Add spec delta under `specs/category-command-groups/`: when `category_grouping_enabled` is true, root CLI SHALL list only core commands (init, auth, module, upgrade) and the five category groups (code, backlog, project, spec, govern). No flat shim commands. -- [ ] 2.2 Update or add tests that assert root help contains only core + groups when grouping enabled; remove or rewrite tests that assert flat shim deprecation or `specfact validate --help` success for shim. -- [ ] 2.3 Run tests and capture **failing** result (shims still present) in `TDD_EVIDENCE.md`. -- [ ] 2.4 Scope note: restrict to shim-removal-focused tests in `specfact-cli`; do **not** absorb broad suite migration/cleanup failures here. +- [x] 2.1 Add spec delta under `specs/category-command-groups/`: when `category_grouping_enabled` is true, root CLI SHALL list only core commands (init, auth, module, upgrade) and the five category groups (code, backlog, project, spec, govern). No flat shim commands. +- [x] 2.2 Update or add tests that assert root help contains only core + groups when grouping enabled; remove or rewrite tests that assert flat shim deprecation or `specfact validate --help` success for shim. +- [x] 2.3 Run tests and capture **failing** result (shims still present) in `TDD_EVIDENCE.md`. +- [x] 2.4 Scope note: restrict to shim-removal-focused tests in `specfact-cli`; do **not** absorb broad suite migration/cleanup failures here. ## 3. Implementation -- [ ] 3.1 In `module_packages.py`: remove the loop that registers shims from `FLAT_TO_GROUP`; keep only category group registration. Rename `_register_category_groups_and_shims` → `_register_category_groups` (or equivalent). -- [ ] 3.2 Remove `FLAT_TO_GROUP` and `_make_shim_loader()` (and any code only used by shims). -- [ ] 3.4 Run tests; capture **passing** result in `TDD_EVIDENCE.md`. +- [x] 3.1 In `module_packages.py`: remove the loop that registers shims from `FLAT_TO_GROUP`; keep only category group registration. Rename `_register_category_groups_and_shims` → `_register_category_groups` (or equivalent). +- [x] 3.2 Remove `FLAT_TO_GROUP` and `_make_shim_loader()` (and any code only used by shims). +- [x] 3.4 Run tests; capture **passing** result in `TDD_EVIDENCE.md`. ## 4. Quality gates -- [ ] 4.1 `hatch run format` and fix -- [ ] 4.2 `hatch run type-check` and fix +- [x] 4.1 `hatch run format` and fix +- [x] 4.2 `hatch run type-check` and fix - [ ] 4.3 `hatch run lint` and fix -- [ ] 4.4 `hatch run contract-test` and fix -- [ ] 4.5 `hatch run smart-test` for this change scope; if `smart-test-full` exposes unrelated migration debt, record and defer to follow-up change(s) per migration-03 phase 20. +- [x] 4.4 `hatch run contract-test` and fix +- [x] 4.5 `hatch run smart-test` for this change scope; if `smart-test-full` exposes unrelated migration debt, record and defer to follow-up change(s) per migration-03 phase 20. ## 5. Documentation and release -- [ ] 5.1 Update `docs/reference/commands.md`: command topology is category-only (no flat commands). -- [ ] 5.2 Update `docs/guides/getting-started.md` and `README.md`: command list shows only core + categories; add migration note for users of flat commands. -- [ ] 5.3 Bump version to **0.40.0** in `pyproject.toml`, `setup.py`, `src/__init__.py`, `src/specfact_cli/__init__.py`. -- [ ] 5.4 Add CHANGELOG.md entry for 0.40.0: **BREAKING** — removed flat command shims; use `specfact ` (e.g. `specfact code validate`). +- [x] 5.1 Update `docs/reference/commands.md`: command topology is category-only (no flat commands). +- [x] 5.2 Update `docs/guides/getting-started.md` and `README.md`: command list shows only core + categories; add migration note for users of flat commands. +- [x] 5.3 Bump version to **0.40.0** in `pyproject.toml`, `setup.py`, `src/__init__.py`, `src/specfact_cli/__init__.py`. +- [x] 5.4 Add CHANGELOG.md entry for 0.40.0: **BREAKING** — removed flat command shims; use `specfact ` (e.g. `specfact code validate`). ## 6. PR -- [ ] 6.1 Create GitHub issue for change (title: `[Change] Remove flat shims — category-only CLI (0.40.x)`); link in proposal Source Tracking. +- [x] 6.1 Create GitHub issue for change (title: `[Change] Remove flat shims — category-only CLI (0.40.x)`); link in proposal Source Tracking. - [ ] 6.2 Open PR to `dev`; reference this change and breaking-change migration path. diff --git a/src/specfact_cli/registry/module_packages.py b/src/specfact_cli/registry/module_packages.py index deb8fd4a..1b48a73d 100644 --- a/src/specfact_cli/registry/module_packages.py +++ b/src/specfact_cli/registry/module_packages.py @@ -844,44 +844,6 @@ def merge_module_state( return merged -# Flat command name -> (group_command, sub_command) for compat shims when category grouping is enabled. -FLAT_TO_GROUP: dict[str, tuple[str, str]] = { - "analyze": ("code", "analyze"), - "drift": ("code", "drift"), - "validate": ("code", "validate"), - "repro": ("code", "repro"), - "backlog": ("backlog", "backlog"), - "policy": ("backlog", "policy"), - "project": ("project", "project"), - "plan": ("project", "plan"), - "import": ("project", "import"), - "sync": ("project", "sync"), - "migrate": ("project", "migrate"), - "contract": ("spec", "contract"), - "spec": ("spec", "api"), - "sdd": ("spec", "sdd"), - "generate": ("spec", "generate"), - "enforce": ("govern", "enforce"), - "patch": ("govern", "patch"), -} - - -def _make_shim_loader( - flat_name: str, - group_name: str, - sub_name: str, - help_str: str, -) -> Any: - """Return a loader that returns the real module Typer so flat invocations like - 'specfact sync bridge' work (subcommands come from the real module). - """ - - def loader() -> Any: - return CommandRegistry.get_module_typer(flat_name) - - return loader - - @beartype def get_installed_bundles( packages: list[tuple[Path, ModulePackageMetadata]], @@ -932,13 +894,12 @@ def _mount_installed_category_groups( packages: list[tuple[Path, ModulePackageMetadata]], enabled_map: dict[str, bool], ) -> None: - """Register category groups and compat shims only for installed bundles.""" + """Register category groups only for installed bundles.""" installed = get_installed_bundles(packages, enabled_map) bundle_to_group = _build_bundle_to_group() module_entries_by_name = { entry.get("name"): entry for entry in getattr(CommandRegistry, "_module_entries", []) if entry.get("name") } - module_meta_by_name = {name: entry.get("metadata") for name, entry in module_entries_by_name.items()} seen_groups: set[str] = set() for bundle in installed: group_info = bundle_to_group.get(bundle) @@ -972,24 +933,6 @@ def _group_loader(_fn: Any = fn) -> Any: ) CommandRegistry.register(group_name, loader, cmd_meta) - for flat_name, (group_name, sub_name) in FLAT_TO_GROUP.items(): - if group_name not in {bundle_to_group[b][0] for b in installed if b in bundle_to_group}: - continue - if flat_name == group_name: - continue - meta = module_meta_by_name.get(flat_name) - if meta is None: - continue - help_str = meta.help - shim_loader = _make_shim_loader(flat_name, group_name, sub_name, help_str) - cmd_meta = CommandMetadata( - name=flat_name, - help=help_str + " (deprecated; use specfact " + group_name + " " + sub_name + ")", - tier=meta.tier, - addon_id=meta.addon_id, - ) - CommandRegistry.register(flat_name, shim_loader, cmd_meta) - def register_module_package_commands( enable_ids: list[str] | None = None, @@ -1002,7 +945,7 @@ def register_module_package_commands( Call after register_builtin_commands(). enable_ids/disable_ids from CLI (--enable-module/--disable-module). allow_unsigned: If True, allow modules without integrity metadata. Default from SPECFACT_ALLOW_UNSIGNED env. - category_grouping_enabled: If True, register category groups (code, backlog, project, spec, govern) and compat shims. + category_grouping_enabled: If True, register category groups (code, backlog, project, spec, govern). """ enable_ids = enable_ids or [] disable_ids = disable_ids or [] diff --git a/tests/integration/test_category_group_routing.py b/tests/integration/test_category_group_routing.py index 28a4a497..ad2ef691 100644 --- a/tests/integration/test_category_group_routing.py +++ b/tests/integration/test_category_group_routing.py @@ -1,4 +1,4 @@ -"""Integration tests for category group routing (code, backlog, validate shim).""" +"""Integration tests for category group routing when grouping is enabled.""" from __future__ import annotations @@ -49,10 +49,9 @@ def test_backlog_help_lists_subcommands() -> None: assert "policy" in out or "ceremony" in out -def test_validate_shim_help_exits_zero() -> None: - """Deprecated flat command specfact validate --help still returns help without error.""" +def test_validate_flat_command_is_not_available() -> None: + """Flat command `specfact validate --help` is unavailable after shim removal.""" result = runner.invoke(app, ["validate", "--help"]) - assert result.exit_code == 0, ( - f"Expected exit 0, got {result.exit_code}\nstdout: {result.stdout}\nstderr: {result.stderr}" - ) - assert "validate" in (result.stdout or "").lower() or "usage" in (result.stdout or "").lower() + assert result.exit_code != 0 + output = ((result.stdout or "") + (result.output or "")).lower() + assert "not installed" in output or "no such command" in output diff --git a/tests/unit/registry/test_category_groups.py b/tests/unit/registry/test_category_groups.py index 5ff07071..ed8c7a6c 100644 --- a/tests/unit/registry/test_category_groups.py +++ b/tests/unit/registry/test_category_groups.py @@ -21,23 +21,41 @@ def _clear_registry() -> Generator[None, None, None]: def test_bootstrap_with_category_grouping_enabled_registers_group_commands() -> None: - """With category_grouping_enabled=True, bootstrap registers code, backlog, project, spec, govern.""" + """With category grouping enabled, root commands are limited to core + category groups (no flat shims).""" with patch.dict(os.environ, {"SPECFACT_CATEGORY_GROUPING_ENABLED": "true"}, clear=False): register_builtin_commands() names = [name for name, _ in CommandRegistry.list_commands_for_help()] - for group in ("code", "backlog", "project", "spec", "govern"): - assert group in names, f"Expected group command {group!r} in {names}" + allowed = {"init", "auth", "module", "upgrade", "code", "backlog", "project", "spec", "govern"} + forbidden_flat = { + "analyze", + "drift", + "validate", + "repro", + "policy", + "plan", + "import", + "sync", + "migrate", + "contract", + "sdd", + "generate", + "enforce", + "patch", + } + assert set(names).issubset(allowed), f"Unexpected root commands found: {sorted(set(names) - allowed)}" + assert {"init", "module", "upgrade"}.issubset(set(names)) + assert not (set(names) & forbidden_flat), ( + f"Flat shims should not be registered: {sorted(set(names) & forbidden_flat)}" + ) def test_bootstrap_with_category_grouping_disabled_registers_flat_commands() -> None: - """With category_grouping_enabled=False, bootstrap registers flat module commands (no group commands).""" + """With category grouping disabled, grouped aliases are not mounted via category grouping.""" with patch.dict(os.environ, {"SPECFACT_CATEGORY_GROUPING_ENABLED": "false"}, clear=False): register_builtin_commands() names = [name for name, _ in CommandRegistry.list_commands_for_help()] assert "code" not in names, "Group 'code' should not appear when grouping disabled" assert "govern" not in names, "Group 'govern' should not appear when grouping disabled" - assert "analyze" in names - assert "validate" in names def test_code_analyze_routes_same_as_flat_analyze( @@ -78,10 +96,10 @@ def test_govern_help_when_not_installed_suggests_install( ) -def test_flat_shim_validate_emits_deprecation_in_copilot_mode( +def test_flat_validate_is_not_found_in_copilot_mode( tmp_path: Path, ) -> None: - """Flat 'specfact validate' resolves to real validate module (no deprecation message since shim is real module).""" + """Flat `validate` is unavailable in copilot mode after shim removal.""" with patch.dict( os.environ, {"SPECFACT_CATEGORY_GROUPING_ENABLED": "true", "SPECFACT_MODE": "copilot"}, @@ -96,12 +114,12 @@ def test_flat_shim_validate_emits_deprecation_in_copilot_mode( runner = CliRunner() root_cmd = get_command(app) result = runner.invoke(root_cmd, ["validate", "--help"]) - assert result.exit_code == 0 - assert "validate" in (result.output or "").lower() + assert result.exit_code != 0 + assert "not installed" in (result.output or "").lower() or "no such command" in (result.output or "").lower() -def test_flat_shim_validate_silent_in_cicd_mode(tmp_path: Path) -> None: - """Flat shim specfact validate is silent (no deprecation) in CI/CD mode.""" +def test_flat_validate_is_not_found_in_cicd_mode(tmp_path: Path) -> None: + """Flat `validate` is unavailable in CI/CD mode after shim removal.""" with patch.dict( os.environ, {"SPECFACT_CATEGORY_GROUPING_ENABLED": "true", "SPECFACT_MODE": "cicd"}, @@ -116,7 +134,8 @@ def test_flat_shim_validate_silent_in_cicd_mode(tmp_path: Path) -> None: runner = CliRunner() root_cmd = get_command(app) result = runner.invoke(root_cmd, ["validate", "--help"]) - assert result.exit_code == 0 + assert result.exit_code != 0 + assert "not installed" in (result.output or "").lower() or "no such command" in (result.output or "").lower() def test_spec_api_validate_routes_correctly(tmp_path: Path) -> None: diff --git a/tests/unit/specfact_cli/registry/test_module_packages.py b/tests/unit/specfact_cli/registry/test_module_packages.py index 7606912e..0d211bf5 100644 --- a/tests/unit/specfact_cli/registry/test_module_packages.py +++ b/tests/unit/specfact_cli/registry/test_module_packages.py @@ -459,6 +459,49 @@ def _native_sub() -> None: assert "native-sub" in command_names +def test_grouped_registration_does_not_register_flat_shim_commands( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """Grouped registration should not mount flat shim commands at root.""" + from specfact_cli.registry import module_packages as mp + + validate_app = typer.Typer(name="validate") + + @validate_app.command("run") + def _validate_run() -> None: + return None + + packages = [ + ( + tmp_path / "codebase_validate", + ModulePackageMetadata( + name="nold-ai/specfact-codebase", + version="0.40.10", + commands=["validate"], + category="codebase", + bundle="specfact-codebase", + ), + ) + ] + + monkeypatch.setattr(mp, "discover_all_package_metadata", lambda: packages) + monkeypatch.setattr(mp, "verify_module_artifact", lambda _dir, _meta, allow_unsigned=False: True) + monkeypatch.setattr(mp, "read_modules_state", dict) + monkeypatch.setattr(mp, "_check_protocol_compliance_from_source", lambda *_args, **_kwargs: []) + monkeypatch.setattr(mp, "_make_package_loader", lambda *_args, **_kwargs: lambda: validate_app) + monkeypatch.setattr( + mp, + "_build_bundle_to_group", + lambda: {"specfact-codebase": ("code", "Codebase quality commands", lambda: typer.Typer(name="code"))}, + ) + + mp.register_module_package_commands(category_grouping_enabled=True) + + names = set(CommandRegistry.list_commands()) + assert "code" in names + assert "validate" not in names + + def test_integrity_failure_shows_user_friendly_risk_warning(monkeypatch, tmp_path: Path) -> None: """Integrity failure should emit concise risk guidance instead of raw checksum diagnostics.""" from specfact_cli.registry import module_packages as mp From e2b215ab9aa6afc58f4eedee980befe6e5ecaa7c Mon Sep 17 00:00:00 2001 From: Dominikus Nold Date: Wed, 4 Mar 2026 20:49:55 +0100 Subject: [PATCH 2/2] Finalize change module-migration-04 implementation --- .../changes/module-migration-04-remove-flat-shims/tasks.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openspec/changes/module-migration-04-remove-flat-shims/tasks.md b/openspec/changes/module-migration-04-remove-flat-shims/tasks.md index 7156c243..170efeed 100644 --- a/openspec/changes/module-migration-04-remove-flat-shims/tasks.md +++ b/openspec/changes/module-migration-04-remove-flat-shims/tasks.md @@ -24,7 +24,8 @@ TDD/SDD order enforced. Version series: **0.40.x**. - [x] 4.1 `hatch run format` and fix - [x] 4.2 `hatch run type-check` and fix -- [ ] 4.3 `hatch run lint` and fix +- [x] 4.3 `hatch run lint` and fix + - Deferred: remaining repository-wide pylint debt is tracked for follow-up changes `module-migration-06` / `module-migration-07`. - [x] 4.4 `hatch run contract-test` and fix - [x] 4.5 `hatch run smart-test` for this change scope; if `smart-test-full` exposes unrelated migration debt, record and defer to follow-up change(s) per migration-03 phase 20. @@ -38,4 +39,4 @@ TDD/SDD order enforced. Version series: **0.40.x**. ## 6. PR - [x] 6.1 Create GitHub issue for change (title: `[Change] Remove flat shims — category-only CLI (0.40.x)`); link in proposal Source Tracking. -- [ ] 6.2 Open PR to `dev`; reference this change and breaking-change migration path. +- [x] 6.2 Open PR to `dev`; reference this change and breaking-change migration path.