|
| 1 | +# TDD Evidence: module-migration-01-categorize-and-group |
| 2 | + |
| 3 | +## Phase 3 — First-run module selection in `specfact init` |
| 4 | + |
| 5 | +### 5.1 Failing tests (pre-implementation) |
| 6 | + |
| 7 | +Tests were written first in `tests/unit/modules/init/test_first_run_selection.py`. Initial run (before implementation) would fail on: |
| 8 | + |
| 9 | +- Profile resolution and install parsing (no `resolve_profile_bundles`, `resolve_install_bundles`, `is_first_run`, or `install_bundles_for_init`). |
| 10 | +- CLI tests would fail due to missing `--profile`/`--install` and missing first_run_selection integration. |
| 11 | + |
| 12 | +(Exact failing run not captured; implementation followed immediately after test creation.) |
| 13 | + |
| 14 | +### 5.3 Passing tests (post-implementation) |
| 15 | + |
| 16 | +**Timestamp:** 2026-02-28 |
| 17 | +**Command:** `hatch test -- tests/unit/modules/init/test_first_run_selection.py -v` |
| 18 | +**Result:** 16 passed |
| 19 | + |
| 20 | +**Summary:** |
| 21 | + |
| 22 | +- `test_profile_solo_developer_resolves_to_specfact_codebase_only` — profile preset resolution. |
| 23 | +- `test_profile_enterprise_full_stack_resolves_to_all_five_bundles` — enterprise preset. |
| 24 | +- `test_profile_nonexistent_raises_with_valid_list` — invalid profile raises with valid list. |
| 25 | +- `test_install_backlog_codebase_resolves_to_two_bundles` — `--install` parsing. |
| 26 | +- `test_install_all_resolves_to_all_five_bundles` — `--install all`. |
| 27 | +- `test_install_unknown_bundle_raises` — unknown bundle raises. |
| 28 | +- `test_is_first_run_true_when_no_category_bundle_installed` — first-run detection (no category bundle). |
| 29 | +- `test_is_first_run_false_when_category_bundle_installed` — first-run false when bundle present. |
| 30 | +- `test_init_profile_solo_developer_calls_installer_with_specfact_codebase` — CLI `--profile solo-developer`. |
| 31 | +- `test_init_profile_enterprise_full_stack_calls_installer_with_all_five` — CLI `--profile enterprise-full-stack`. |
| 32 | +- `test_init_profile_nonexistent_exits_nonzero_and_lists_valid_profiles` — CLI invalid profile exits non-zero. |
| 33 | +- `test_init_install_backlog_codebase_calls_installer_with_two_bundles` — CLI `--install backlog,codebase`. |
| 34 | +- `test_init_install_all_calls_installer_with_five_bundles` — CLI `--install all`. |
| 35 | +- `test_init_install_widgets_exits_nonzero` — CLI unknown bundle exits non-zero. |
| 36 | +- `test_init_second_run_skips_first_run_flow` — second run does not call installer when no `--profile`/`--install`. |
| 37 | +- `test_spec_bundle_install_includes_project_dep` — `install_bundles_for_init(["specfact-spec"])` installs project dep. |
| 38 | + |
| 39 | +Implementation: `src/specfact_cli/modules/init/src/first_run_selection.py` and `commands.py` (--profile, --install, first_run_selection integration). |
| 40 | + |
| 41 | +### Phase 3 follow-up (5.2.3, 5.2.7) |
| 42 | + |
| 43 | +**Interactive first-run UI (5.2.3):** |
| 44 | +- `_interactive_first_run_bundle_selection()` in commands.py: welcome banner (Panel), questionary.select for profile or "Choose bundles manually", questionary.checkbox for manual bundle selection. When first run and interactive and no --profile/--install, init() calls it and installs selected bundles or shows tip if none. |
| 45 | +- `BUNDLE_DISPLAY` and `PROFILE_DISPLAY_ORDER` in first_run_selection.py for UI labels. |
| 46 | + |
| 47 | +**Graceful degradation (5.2.7):** |
| 48 | +- In `install_bundles_for_init`, each `install_bundled_module` call wrapped in try/except; on exception log warning "Dependency resolver may be unavailable" and re-raise so errors are surfaced. |
| 49 | + |
| 50 | +**Additional tests:** |
| 51 | +- `test_init_first_run_interactive_with_selection_calls_installer`: first run + interactive, mock selection returns ["specfact-codebase"], assert install called. |
| 52 | +- `test_init_first_run_interactive_no_selection_shows_tip`: first run + interactive, mock selection returns [], assert no install and "Tip" / "module install" in output. |
| 53 | + |
| 54 | +**Run:** `hatch test -- tests/unit/modules/init/test_first_run_selection.py -v` — 18 passed. |
| 55 | + |
| 56 | +## Section 6 — Integration and E2E |
| 57 | + |
| 58 | +**Timestamp:** 2026-02-28 |
| 59 | +**Commands:** `hatch test -- tests/integration/test_category_group_routing.py tests/e2e/test_first_run_init.py -v` |
| 60 | +**Result:** 5 passed (3 integration + 2 e2e). |
| 61 | + |
| 62 | +**Integration:** `test_code_analyze_help_exits_zero`, `test_backlog_help_lists_subcommands`, `test_validate_shim_help_exits_zero`. |
| 63 | +**E2E:** `test_init_profile_solo_developer_completes_in_temp_workspace`, `test_after_solo_developer_init_code_analyze_help_available` (install_bundles_for_init mocked). |
| 64 | + |
| 65 | +## Phase 4 — Regression fixes from review (grouped extension merge + project-scoped first-run) |
| 66 | + |
| 67 | +### 4.1 Failing tests (pre-implementation) |
| 68 | + |
| 69 | +**Timestamp:** 2026-02-28 01:00 UTC |
| 70 | +**Command:** `hatch test -- tests/unit/specfact_cli/registry/test_module_packages.py::test_grouped_registration_merges_duplicate_command_extensions tests/unit/modules/init/test_first_run_selection.py::test_is_first_run_false_when_project_scoped_category_bundle_installed -v` |
| 71 | +**Result:** 2 failed. |
| 72 | + |
| 73 | +**Failure summary:** |
| 74 | + |
| 75 | +- `test_grouped_registration_merges_duplicate_command_extensions` failed because grouped registration replaced the earlier `backlog` loader; observed commands were only `('ext_cmd',)` and `base_cmd` was missing. |
| 76 | +- `test_is_first_run_false_when_project_scoped_category_bundle_installed` failed because `is_first_run()` ignored modules discovered with source `project`, returning `True` for an already-initialized workspace. |
| 77 | + |
| 78 | +### 4.2 Passing tests (post-implementation) |
| 79 | + |
| 80 | +**Timestamp:** 2026-02-28 01:01 UTC |
| 81 | +**Command:** `hatch test -- tests/unit/specfact_cli/registry/test_module_packages.py::test_grouped_registration_merges_duplicate_command_extensions tests/unit/modules/init/test_first_run_selection.py::test_is_first_run_false_when_project_scoped_category_bundle_installed -v` |
| 82 | +**Result:** 2 passed. |
| 83 | + |
| 84 | +**Implementation summary:** |
| 85 | + |
| 86 | +- Updated `register_module_package_commands()` grouped path to merge duplicate command loaders via `_make_extending_loader` for module entries (and core root entries), instead of unconditional overwrite. |
| 87 | +- Updated `is_first_run()` source filter to include `project` modules in first-run detection. |
| 88 | + |
| 89 | +## Phase 5 — Regression fix from PR 331 (trust failure should not block unaffected legacy module registration) |
| 90 | + |
| 91 | +### 5.1 Failing test (pre-implementation) |
| 92 | + |
| 93 | +**Timestamp:** 2026-02-28 21:07 local |
| 94 | +**Command:** `hatch test -- tests/unit/specfact_cli/registry/test_module_packages.py::test_unaffected_modules_register_when_one_fails_trust -v` |
| 95 | +**Result:** 1 failed. |
| 96 | + |
| 97 | +**Failure summary:** |
| 98 | + |
| 99 | +- In grouped mode, a module without `category` metadata was routed into grouped registration, so `good_cmd` was not mounted as flat top-level despite warning text indicating flat mounting. |
| 100 | + |
| 101 | +### 5.2 Passing tests (post-implementation) |
| 102 | + |
| 103 | +**Timestamp:** 2026-02-28 21:09 local |
| 104 | +**Command:** `hatch test -- tests/unit/specfact_cli/registry/test_module_packages.py::test_unaffected_modules_register_when_one_fails_trust tests/unit/specfact_cli/registry/test_module_packages.py::test_grouped_registration_merges_duplicate_command_extensions tests/unit/registry/test_module_grouping.py::test_module_package_yaml_without_category_mounts_ungrouped_warning_logged -v` |
| 105 | +**Result:** 3 passed. |
| 106 | + |
| 107 | +**Implementation summary:** |
| 108 | + |
| 109 | +- Updated `register_module_package_commands()` to use grouped registration only when `category_grouping_enabled` is true and module metadata declares `category`. |
| 110 | +- Updated grouped-extension unit fixture metadata to include `category="backlog"` so the test reflects migration-era grouped manifests and remains aligned with category-driven grouping semantics. |
0 commit comments