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
57 changes: 57 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,63 @@ All notable changes to this project will be documented in this file.

---

## [0.23.1] - 2026-01-07

### Fixed (0.23.1)

- **Contract Extraction Performance**: Fixed critical performance bottleneck causing extremely slow contract extraction
- **Nested Parallelism Removal**: Eliminated GIL contention from nested ThreadPoolExecutor instances
- Removed file-level parallelism within features (features already processed in parallel at command level)
- Files within each feature now processed sequentially to avoid thread contention
- Performance improvement: contract extraction for large codebases (300+ features) now completes in reasonable time instead of hours
- Resolves issue where CPU usage was low despite long processing times due to GIL contention
- **Cache Invalidation Logic**: Fixed cache update logic to properly detect and handle file changes
- Changed double-check pattern to compare file hashes before updating cache
- Cache now correctly updates when file content changes, not just on cache misses
- Ensures AST cache reflects current file state after modifications
- **Test Robustness**: Enhanced cache invalidation test to handle Path object differences
- Test now handles both `test_file` and `resolved_file` as cache keys
- Path objects are compared by value, ensuring correct cache lookups
- Added assertions to verify cache keys exist before accessing

- **Import Command Bug Fixes**: Fixed critical bugs in enrichment and contract extraction workflow
- **Unhashable Type Error**: Fixed `TypeError: unhashable type: 'Feature'` when applying enrichment reports
- Changed `dict[Feature, list[Path]]` to `dict[str, list[Path]]` using feature keys instead of Feature objects
- Added `feature_objects: dict[str, Feature]` mapping to maintain Feature object references
- Prevents runtime errors during contract extraction when enrichment adds new features
- **Enrichment Performance Regression**: Fixed severe performance issue where enrichment forced full contract regeneration
- Removed `or enrichment` condition from `_check_incremental_changes` that forced full regeneration
- Enrichment now only triggers contract extraction for new features (without contracts)
- Existing contracts are not regenerated when only metadata changes (confidence adjustments, business context)
- Performance improvement: enrichment with unchanged files now completes in seconds instead of 80+ minutes for large bundles
- **Contract Extraction Order**: Fixed contract extraction to run after enrichment application
- Ensures new features from enrichment reports are included in contract extraction
- New features without contracts now correctly get contracts extracted

### Added (0.23.1)

- **Contract Extraction Profiling Tool**: Added diagnostic tool for performance analysis
- New `tools/profile_contract_extraction.py` script for profiling contract extraction bottlenecks
- Helps identify performance issues in contract extraction process
- Provides detailed timing and profiling information for individual features

- **Comprehensive Test Coverage**: Added extensive test suite for import and enrichment bugs
- **Integration Tests**: New `test_import_enrichment_contracts.py` with 5 test cases (552 lines)
- Tests enrichment not forcing full contract regeneration
- Tests new features from enrichment getting contracts extracted
- Tests incremental contract extraction with enrichment
- Tests feature objects not used as dictionary keys
- Tests performance regression prevention
- **Unit Tests**: New `test_import_contract_extraction.py` with 5 test cases (262 lines)
- Tests Feature objects not being hashable (regression test)
- Tests contract extraction using feature keys, not objects
- Tests incremental contract regeneration logic
- Tests enrichment not forcing contract regeneration
- Tests new features from enrichment getting contracts
- **Updated Existing Tests**: Enhanced `test_import_command.py` with enrichment regression test

---

## [0.23.0] - 2026-01-07

### Added (0.23.0)
Expand Down
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.23.0"
version = "0.23.1"
description = "Brownfield-first CLI: Reverse engineer legacy Python → specs → enforced contracts. Automate legacy code documentation and prevent modernization regressions."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
if __name__ == "__main__":
_setup = setup(
name="specfact-cli",
version="0.23.0",
version="0.23.1",
description="SpecFact CLI - Spec -> Contract -> Sentinel tool for contract-driven development",
packages=find_packages(where="src"),
package_dir={"": "src"},
Expand Down
2 changes: 1 addition & 1 deletion src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
"""

# Define the package version (kept in sync with pyproject.toml and setup.py)
__version__ = "0.23.0"
__version__ = "0.23.1"
2 changes: 1 addition & 1 deletion src/specfact_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
- Validating reproducibility
"""

__version__ = "0.23.0"
__version__ = "0.23.1"

__all__ = ["__version__"]
11 changes: 9 additions & 2 deletions src/specfact_cli/analyzers/contract_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,15 @@ def _ast_to_value_string(self, node: ast.AST) -> str:
return repr(node.value)
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.NameConstant): # Python < 3.8
return str(node.value)
# Python < 3.8 compatibility - suppress deprecation warning
import warnings

with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
# ast.NameConstant is deprecated in Python 3.8+, removed in 3.14
# Keep for backward compatibility with older Python versions
if hasattr(ast, "NameConstant") and isinstance(node, ast.NameConstant):
return str(node.value)

# Use ast.unparse if available
if hasattr(ast, "unparse"):
Expand Down
Loading
Loading