Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,41 @@ All notable changes to this project will be documented in this file.

---

## [0.46.22] - 2026-05-12

### Fixed

- **Module review hardening**: avoid false versioned dependency failures when
callers do not provide installed-version maps, and require exact namespace
matches for fully qualified `module doctor` IDs.

---

## [0.46.21] - 2026-05-12

### Fixed

- **Module diagnostics CI hardening**: keep JSON request payload typing,
legacy category grouping, and stale shim delegation compatible with the
required lint, type-check, and test gates.

---

## [0.46.20] - 2026-05-12

### Added

- **Module scope diagnostics**: add `specfact module doctor`.

### Fixed

- **Module scope diagnostics**: enforce versioned module dependency mismatches
across project and user scoped modules.
- **Module signature PR verification**: keep the relaxed PR verifier aligned
with its version-bump-only policy when signatures are deferred to CI signing.

---

## [0.46.19] - 2026-05-07

### Added
Expand Down
9 changes: 7 additions & 2 deletions docs/module-system/installing-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ Notes:
- If the requested module is already installed but disabled in `modules.json`, install repairs the lifecycle state by enabling the manifest module id and reports that action.
- Missing command guidance distinguishes truly absent modules from installed-but-disabled or installed-but-skipped modules and suggests the matching recovery command.
- Invalid ids show an explicit error (`name` or `namespace/name` only).
- Use `specfact module doctor [module-id]` to inspect the effective module copy, shadowed duplicates, and configured development source roots.

## Dependency resolution

Before installing a marketplace module, SpecFact resolves its dependencies (other modules and optional pip packages) from manifest `pip_dependencies` and `module_dependencies`. If conflicts are detected (e.g. incompatible versions), install fails unless you override.
Before installing a marketplace module, SpecFact resolves its dependencies (other modules and optional pip packages) from manifest `pip_dependencies`, `module_dependencies`, and versioned bundle dependency declarations. If conflicts are detected (e.g. incompatible versions), install fails unless you override.

```bash
# Install with dependency resolution (default)
Expand All @@ -62,6 +63,7 @@ specfact module install nold-ai/specfact-backlog --force

- Use `--skip-deps` when you want to install a single module without pulling its dependencies or when you manage dependencies yourself.
- Use `--force` to proceed when resolution reports conflicts (e.g. for local overrides or known-compatible versions). Enable/disable and dependency-aware cascades still respect `--force` where applicable.
- If a dependency already exists in the selected install scope but its version does not satisfy a declared bundle dependency range, reinstall or upgrade that dependency in the same scope before retrying.

See [Dependency resolution](../reference/dependency-resolution.md) for how resolution works and conflict detection.

Expand Down Expand Up @@ -106,6 +108,7 @@ specfact module list
specfact module list --show-origin
specfact module list --source marketplace
specfact module list --show-bundled-available
specfact module doctor nold-ai/specfact-codebase
```

Default columns:
Expand All @@ -116,7 +119,9 @@ Default columns:
- `Trust` (`official`, `community`, `local-dev`)
- `Publisher`

With `--show-origin`, an additional `Origin` column is shown (`built-in`, `marketplace`, `custom`).
With `--show-origin`, an additional `Origin` column is shown (`built-in`, `project`, `user`, `marketplace`, `custom`).

`module doctor` keeps discovery metadata-only and reports effective vs shadowed duplicate copies, exact manifest versions, paths, enabled state, configured development source roots, and recovery commands. Use it when project-scoped modules under `<repo>/.specfact/modules` and user-scoped modules under `~/.specfact/modules` disagree.

## Show Detailed Module Info

Expand Down
13 changes: 7 additions & 6 deletions docs/module-system/module-marketplace.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ audience: [solo, team, enterprise]
expertise_level: [beginner, intermediate]
---

# Module Marketplace

SpecFact supports centralized marketplace distribution with local multi-source discovery.

For the curated official bundle list and trust/dependency quick reference, see
Expand All @@ -38,8 +36,9 @@ Local module discovery scans these roots in priority order:

1. `built-in` modules (`src/specfact_cli/modules`)
2. `project` modules (`<repo>/.specfact/modules`)
3. `user` modules (`~/.specfact/modules`)
4. legacy/custom roots (`~/.specfact/marketplace-modules`, `~/.specfact/custom-modules`, `SPECFACT_MODULES_ROOTS`)
3. explicit development/custom roots (`SPECFACT_MODULES_ROOTS`)
4. `user` modules (`~/.specfact/modules`)
5. marketplace and custom roots (`~/.specfact/marketplace-modules`, `~/.specfact/custom-modules`)

If module names collide, higher-priority sources win and lower-priority entries are shadowed.

Expand All @@ -48,14 +47,17 @@ If module names collide, higher-priority sources win and lower-priority entries
SpecFact shows both trust semantics and origin details:

- `Trust` column (default): `official`, `community`, `local-dev`
- `Origin` column (`--show-origin`): `built-in`, `marketplace`, `custom`
- `Origin` column (`--show-origin`): `built-in`, `project`, `user`, `marketplace`, `custom`

Use:

```bash
specfact module list --show-origin
specfact module doctor nold-ai/specfact-codebase
```

`module doctor` additionally reports shadowed duplicate copies, exact manifest versions, paths, enabled state, configured development source roots, and recovery commands.

## Security Model

Install workflow enforces integrity and compatibility checks:
Expand Down Expand Up @@ -123,5 +125,4 @@ Scope boundary:
> Modules docs handoff: this page remains in the core docs set as release-line overview content.
> Canonical bundle-specific deep guidance now lives in the canonical modules docs site, currently
> published at `https://modules.specfact.io/`.

> for the current release line and are planned to migrate to `specfact-cli-modules`.
5 changes: 3 additions & 2 deletions openspec/CHANGE_ORDER.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ should be implemented.

| Bucket | Count | Location |
|---|---|---|
| **Active** | 24 | [`openspec/changes/`](changes/) |
| **Active** | 25 | [`openspec/changes/`](changes/) |
| **Parked** | 20 | [`openspec/parking-lot/`](parking-lot/) |
| **Archived** | 105 | [`openspec/changes/archive/`](changes/archive/) |

Expand All @@ -25,7 +25,7 @@ for the un-park trigger of each parked change.

## Active tracks

The 24 active changes group into five independent tracks. Tracks can run in
The 25 active changes group into five independent tracks. Tracks can run in
parallel; within a track, follow the order column.

### Track A — Full-chain traceability (core thesis)
Expand Down Expand Up @@ -90,6 +90,7 @@ README. Snapshot/CI work folds into 03 and 04 if and when needed.
|---|---|---|
| `dep-security-cleanup` | 62/69 done | Apache-2.0 license-cleanliness pass |
| `marketplace-07-module-install-state-consistency` | ✓ archive pending | Resolves install-state disagreement across scopes |
| `module-scope-version-diagnostics` | active | Resolves project/user module version mismatch diagnostics and enforcement under #565 / #353 |
| `upgrade-01-install-method-aware` | ✓ archive pending | Bug-fix for uvx/uv users |
| `governance-02-exception-management` | active | Time-bound policy exceptions |
| `architecture-02-well-architected-review` | **gated on architecture-01 shipping + 1 cycle of usage** | Boundary/ADR review pillar |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-12
41 changes: 41 additions & 0 deletions openspec/changes/module-scope-version-diagnostics/TDD_EVIDENCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# TDD Evidence

## Failing Before

- `hatch run pytest tests/unit/specfact_cli/registry/test_module_dependencies.py::test_validate_module_dependencies_detects_version_mismatch tests/unit/registry/test_module_installer.py::test_install_module_rejects_existing_bundle_dependency_version_mismatch tests/unit/modules/module_registry/test_commands.py::test_doctor_reports_effective_and_shadowed_duplicate_modules -q`
- Result: FAIL (`3 failed`) before production edits.
- Failures: `_validate_module_dependencies` rejected the new version map argument; install accepted an out-of-range existing dependency; `module doctor` did not exist.

## Passing After

- `hatch run pytest tests/unit/specfact_cli/registry/test_module_dependencies.py::test_validate_module_dependencies_detects_version_mismatch tests/unit/registry/test_module_installer.py::test_install_module_rejects_existing_bundle_dependency_version_mismatch tests/unit/modules/module_registry/test_commands.py::test_doctor_reports_effective_and_shadowed_duplicate_modules -q`
- Result: PASS (`3 passed`).
- `hatch run pytest tests/unit/specfact_cli/registry/test_module_dependencies.py tests/unit/registry/test_module_installer.py tests/unit/modules/module_registry/test_commands.py -q`
- Result: PASS (`96 passed`).

## Quality Gates

- `hatch run format`
- Result: PASS (`All checks passed!`).
- `openspec validate module-scope-version-diagnostics --strict`
- Result: PASS.
- `basedpyright src/specfact_cli/modules/module_registry/src/commands.py src/specfact_cli/registry/module_installer.py src/specfact_cli/registry/module_packages.py src/specfact_cli/registry/module_state.py tests/unit/modules/module_registry/test_commands.py tests/unit/registry/test_module_installer.py tests/unit/specfact_cli/registry/test_module_dependencies.py`
- Result: PASS (`0 errors`, existing pytest monkeypatch typing warnings only).
- `hatch run specfact code review run --json --out .specfact/code-review.changed.json --scope changed`
- Result: PASS (`0 blocking`, 17 warning-only findings).
- Warning disposition: remaining findings are pre-existing local patterns in touched modules (parameter-count helpers, duplicate Typer validator shapes, existing naming heuristics, and legacy helper exposure). They were not expanded by this change except the validated dependency complexity, which was refactored before the passing review run.

## Real-World Tmp Smoke

- Created a temporary git repo with project module `nold-ai/specfact-codebase` version `0.41.0` and isolated user-scope module `nold-ai/specfact-codebase` version `0.40.0`.
- Command: `hatch run env HOME=<tmp-home> specfact module doctor nold-ai/specfact-codebase --repo <tmp-repo>`
- Result: PASS. Output showed project `0.41.0` as `effective`, user `0.40.0` as `shadowed`, and recovery command `specfact module uninstall nold-ai/specfact-codebase --scope user`.
- Created a temporary install root with existing dependency `nold-ai/dep` version `1.0.0` and installed a local tarball declaring `bundle_dependencies: [{id: nold-ai/dep, version: ">=2.0.0"}]` through the worktree installer.
- Command: `hatch run python <tmp installer smoke>`
- Result: PASS. Install failed with `Dependency nold-ai/dep requires >=2.0.0, but installed version is 1.0.0. Reinstall or upgrade the dependency in the same module scope.`

## Known Gate Caveat

- `hatch run lint`
- Result: FAIL on repository-wide pre-existing `JsonType` basedpyright errors in `src/specfact_cli/adapters/ado.py`, `src/specfact_cli/adapters/github.py`, and `src/specfact_cli/validators/change_proposal_integration.py`.
- Scoped basedpyright for this change passes with 0 errors.
16 changes: 16 additions & 0 deletions openspec/changes/module-scope-version-diagnostics/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## Overview

Keep module precedence deterministic while making the effective runtime explainable. The CLI will continue to select the first discovered module id by source priority, but diagnostics will expose duplicate copies and version drift. Dependency enforcement will use manifest metadata already parsed by the runtime.

## Decisions

- Add `specfact module doctor [MODULE_ID] [--repo PATH]` as the focused diagnostic entrypoint instead of overloading `module list`.
- Use metadata-only discovery with shadowed duplicates retained, so diagnostics do not import module command code.
- Report development source roots from `SPECFACT_MODULES_REPO`, `SPECFACT_CLI_MODULES_REPO`, and `SPECFACT_MODULES_ROOTS` when configured because those can affect Python import resolution even when install manifests come from `.specfact/modules`.
- Enforce versioned module dependencies with `packaging.specifiers.SpecifierSet` and `packaging.version.Version`; malformed specifiers remain non-blocking with debug logging, matching existing `core_compatibility` tolerance.
- Install-time enforcement checks existing dependencies before accepting them, installs missing dependencies as today, then validates the installed manifest version.

## Risks

- Users with stale duplicate installs may see new warnings or skipped modules. Mitigation: diagnostics include exact uninstall/reinstall guidance.
- Marketplace install cannot select the newest version satisfying a range yet. Mitigation: install the normal resolved artifact, then fail with an actionable version mismatch if it does not satisfy the manifest requirement.
40 changes: 40 additions & 0 deletions openspec/changes/module-scope-version-diagnostics/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## Why

A key user reported module conflicts and version mismatches when SpecFact modules exist in both project-local and user scopes across split repositories and monorepos. Today project scope correctly wins, but lower-priority copies and implicit development source roots are hard to see, and declared module dependency version ranges are not enforced consistently.

## What Changes

- Add a user-facing module diagnostics command that shows effective and shadowed module copies across project, user, marketplace, custom, and development source roots.
- Surface exact module versions, origins, paths, and recovery guidance when duplicate module ids exist.
- Enforce versioned module dependency requirements from module manifests during marketplace install and command registration.
- Preserve current project-over-user precedence; this change makes mismatches visible and prevents incompatible dependency sets from loading silently.

## Capabilities

### New Capabilities

- `module-scope-diagnostics`: Diagnose effective module origin, shadowed duplicates, development source roots, and version mismatch recovery steps.

### Modified Capabilities

- `module-packages`: Module registration enforces declared module dependency version ranges instead of checking presence only.
- `module-installation`: Module install enforces declared bundle dependency version ranges after dependency discovery/install.

## Impact

- Affected code: module discovery diagnostics, `specfact module` command surface, module dependency validation, and marketplace install dependency handling.
- Affected tests: unit tests for module diagnostics output, registration skip behavior, and install-time dependency version mismatch handling.
- Documentation: module installation/marketplace docs should mention diagnostics and duplicate-scope remediation.

---

## Source Tracking

<!-- source_repo: nold-ai/specfact-cli -->
- **Parent Feature**: [#353](https://github.com/nold-ai/specfact-cli/issues/353)
- **Change User Story**: [#565](https://github.com/nold-ai/specfact-cli/issues/565)
- **GitHub Issue**: [#565](https://github.com/nold-ai/specfact-cli/issues/565)
- **Issue Relationships**: `#565` is a sub-issue of Feature `#353`; Feature `#353` is a sub-issue of Epic `#194`.
- **Repository**: nold-ai/specfact-cli
- **Last Synced Status**: GitHub story, labels, parent relationship, and source tracking synced
- **Sanitized**: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## MODIFIED Requirements

### Requirement: Module install enforces versioned bundle dependencies

The system SHALL validate versioned bundle dependency declarations during module installation.

#### Scenario: Existing dependency version is too old

- **GIVEN** a module being installed declares a bundle dependency with a version range
- **AND** that dependency already exists in the target install root with a version outside the range
- **WHEN** the user installs the dependent module
- **THEN** install fails before accepting the dependency set
- **AND** the error identifies the dependency id, required version range, and installed version

#### Scenario: Newly installed dependency version is validated

- **GIVEN** a module being installed declares a missing bundle dependency with a version range
- **WHEN** dependency installation completes
- **THEN** the installed dependency version is validated against the declared range
- **AND** install fails if the installed dependency still does not satisfy the range
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## MODIFIED Requirements

### Requirement: Module registration enforces versioned module dependencies

The system SHALL skip registering an enabled module when its declared versioned module dependency is absent, disabled, or present at a version that does not satisfy the declared specifier.

#### Scenario: Enabled dependency version is too old

- **GIVEN** an enabled module declares a dependency on another module with a minimum version
- **AND** the dependency is enabled but its manifest version is below that minimum
- **WHEN** module package commands are registered
- **THEN** the dependent module is skipped
- **AND** diagnostics report the required version and the discovered version

#### Scenario: Dependency version satisfies the declared range

- **GIVEN** an enabled module declares a versioned module dependency
- **AND** the dependency is enabled and its version satisfies the declared range
- **WHEN** module package commands are registered
- **THEN** the dependent module remains eligible for registration
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## ADDED Requirements

### Requirement: Module doctor reports effective and shadowed module copies

The system SHALL provide a `specfact module doctor` diagnostic that reports module origin, version, path, and shadowing state without importing module command code.

#### Scenario: Duplicate project and user module copies are visible

- **GIVEN** a module id exists in project scope and user scope with different versions
- **WHEN** the user runs `specfact module doctor <module-id>`
- **THEN** the output identifies the project copy as effective
- **AND** the output identifies the user copy as shadowed
- **AND** the output shows both versions and paths
- **AND** the output includes a recovery command for removing the stale user-scope copy

#### Scenario: Development source roots are disclosed

- **GIVEN** development source root environment variables are configured
- **WHEN** the user runs `specfact module doctor`
- **THEN** the output lists the configured development source roots that may influence import resolution
24 changes: 24 additions & 0 deletions openspec/changes/module-scope-version-diagnostics/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## 1. Governance

- [x] 1.1 Create GitHub issue #565 with labels, parent Feature #353, and proposal source tracking.
- [x] 1.2 Keep internal wiki source tracking aligned for `module-scope-version-diagnostics`.

## 2. Specs and Tests

- [x] 2.1 Add OpenSpec proposal, design, and spec deltas for diagnostics and version enforcement.
- [x] 2.2 Add failing tests for module doctor duplicate-scope diagnostics.
- [x] 2.3 Add failing tests for install-time versioned bundle dependency enforcement.
- [x] 2.4 Add failing tests for registration-time versioned module dependency enforcement.

## 3. Implementation

- [x] 3.1 Implement `specfact module doctor`.
- [x] 3.2 Enforce versioned bundle dependency declarations during install.
- [x] 3.3 Enforce versioned module dependency declarations during registration and lifecycle checks.
- [x] 3.4 Update module docs with duplicate-scope remediation guidance.

## 4. Evidence and Gates

- [x] 4.1 Record failing-before and passing-after test evidence in `TDD_EVIDENCE.md`.
- [x] 4.2 Run targeted tests for touched module registry/installer behavior.
- [x] 4.3 Run required scoped quality gates and SpecFact code review.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "specfact-cli"
version = "0.46.19"
version = "0.46.22"
description = "The swiss knife CLI for agile DevOps teams. Keep backlog, specs, tests, and code in sync with validation and contract enforcement for new projects and long-lived codebases."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
Loading
Loading