# ADR-008: Versioning Strategy

## Status

Accepted

## Context

StarPunk is an IndieWeb CMS currently in active development, working toward its first production release. We need a comprehensive versioning strategy that:

1. **Communicates clearly** what type of changes each release contains
2. **Works with Python ecosystem** tools (pip, uv, PyPI compatibility)
3. **Aligns with IndieWeb values** of simplicity and sustainability
4. **Supports the project lifecycle** from development through maintenance
5. **Enables dependency management** for users who may build on StarPunk
6. **Provides predictability** for users about what to expect in updates

### Current State

The project currently uses informal version terminology:
- "V1" for the overall first release goal
- "Phase 1.1", "Phase 1.2" for development milestones
- "v1.1", "v2.0" for future iteration references

This works for internal planning but lacks the precision needed for:
- Public releases
- Dependency management
- Communicating breaking changes
- Git tagging and release management

### Requirements

1. **Version number format** that indicates change severity
2. **Python ecosystem compliance** (PEP 440)
3. **Git workflow integration** (tagging, branching)
4. **Changelog format** for human-readable history
5. **Pre-release versioning** for alphas/betas if needed
6. **Upgrade communication** strategy
7. **Simplicity** appropriate for an indie project

## Decision

We will adopt **Semantic Versioning 2.0.0 (SemVer)** with **Python PEP 440 compliance** for all StarPunk releases.

### Version Number Format

**Structure**: `MAJOR.MINOR.PATCH[-PRERELEASE]`

**Examples**:
- `0.1.0` - Development version (Phase 1.1 complete)
- `0.2.0` - Development version (Phase 1.2 complete)
- `1.0.0` - First stable release (all V1 features complete)
- `1.0.1` - Bug fix release
- `1.1.0` - Feature release (backward compatible)
- `2.0.0` - Major release (breaking changes)
- `1.0.0a1` - Alpha pre-release (PEP 440 format)
- `1.0.0b2` - Beta pre-release (PEP 440 format)
- `1.0.0rc1` - Release candidate (PEP 440 format)

### Version Component Rules

**MAJOR version** - Increment when making incompatible changes:
- Breaking API changes
- Database schema changes requiring migration
- Configuration file format changes requiring user intervention
- Removal of deprecated features
- Major architectural changes

**MINOR version** - Increment when adding functionality in backward-compatible manner:
- New features
- New API endpoints
- Non-breaking enhancements
- Optional new configuration parameters
- Significant performance improvements

**PATCH version** - Increment for backward-compatible bug fixes:
- Bug fixes
- Security patches
- Documentation corrections
- Minor performance improvements
- Dependency updates (without feature changes)

### Development Phase (0.x.y)

During development (pre-1.0), we use `0.MINOR.PATCH`:
- MINOR increments for phase completions (Phase 1.1 → 0.1.0, Phase 1.2 → 0.2.0)
- PATCH increments for bug fixes during development
- Breaking changes are allowed without major version increment
- Public API is not considered stable

### Git Tagging Convention

**Format**: `vMAJOR.MINOR.PATCH[-PRERELEASE]`

**Examples**:
- `v0.1.0` - Development version tag
- `v1.0.0` - Stable release tag
- `v1.0.1` - Bug fix release tag
- `v1.0.0-alpha.1` - Alpha pre-release tag (Git format)

**Tag type**: Annotated tags (not lightweight)
- Contains tagger, date, message
- Can include release notes
- Can be GPG signed

### Version Storage

**Primary source of truth**: `starpunk/__init__.py`
```python
__version__ = "1.0.0"
__version_info__ = (1, 0, 0)
```

**Secondary locations**:
- `pyproject.toml` - Package metadata (if used)
- Git tags - Release markers
- `CHANGELOG.md` - Human-readable history

**Synchronization**: Manual for V1 (simple, no automation dependencies)

### Changelog Format

**File**: `CHANGELOG.md`

**Format**: Based on [Keep a Changelog](https://keepachangelog.com/)

**Categories**:
- Added - New features
- Changed - Changes to existing functionality
- Deprecated - Features that will be removed
- Removed - Features that have been removed
- Fixed - Bug fixes
- Security - Security vulnerability fixes

**Example**:
```markdown
## [1.0.0] - 2024-11-18

### Added
- IndieAuth authentication via IndieLogin
- Micropub endpoint for publishing
- RSS feed generation
- File-based note storage

### Security
- Implemented path traversal protection
- Added CSRF protection for authentication flows
```

### Pre-Release Versioning

**Format**: PEP 440 compliant
- Alpha: `1.0.0a1`, `1.0.0a2`
- Beta: `1.0.0b1`, `1.0.0b2`
- Release Candidate: `1.0.0rc1`, `1.0.0rc2`

**Git tags use hyphen**: `v1.0.0-alpha.1` (for readability)
**Python `__version__` uses PEP 440**: `1.0.0a1` (for pip compatibility)

### Phase-to-Version Mapping

**Implementation phases** (internal planning) map to **version numbers** (public releases):

```
Phase 1.1 complete → Version 0.1.0
Phase 1.2 complete → Version 0.2.0
Phase 1.3 complete → Version 0.3.0
Phase 2.1 complete → Version 0.4.0
All V1 complete   → Version 1.0.0
V1.1 features     → Version 1.1.0
V2 features       → Version 2.0.0
```

**Clarification**:
- "V1" refers to feature scope, not version number
- Version 1.0.0 implements the "V1 feature set"
- Phases are development milestones, versions are public releases

## Rationale

### Why Semantic Versioning?

1. **Industry Standard**: Used by Flask, Django, Requests, and most Python packages
2. **Clear Communication**: Version number immediately conveys impact of changes
3. **Predictable**: Users know what to expect from each version increment
4. **Dependency Management**: Works seamlessly with pip version specifiers
5. **Simple**: Easy to understand and apply without complex rules

### Why Not Calendar Versioning (CalVer)?

CalVer (e.g., `2024.11.18`) was considered but rejected:

**Pros**:
- Shows when software was released
- No ambiguity about version order

**Cons**:
- Doesn't communicate impact of changes (patch vs breaking change)
- Less common in Python ecosystem
- Doesn't help users assess upgrade risk
- Overkill for indie project release cadence

**Conclusion**: SemVer's semantic meaning is more valuable than date information

### Why Not ZeroVer (0.x forever)?

Some projects stay at 0.x.y indefinitely to signal "still evolving". Rejected because:

- Creates uncertainty about production readiness
- Version 1.0.0 signals "ready for production use"
- We have a clear 1.0 feature scope (V1)
- Users deserve clarity about stability

### Why PEP 440 Compliance?

**PEP 440** is Python's version identification standard:

- Required for PyPI publication (if we ever publish)
- Compatible with pip, uv, and all Python package managers
- Slightly different pre-release format than SemVer (e.g., `1.0.0a1` vs `1.0.0-alpha.1`)
- Used by all major Python frameworks

**Decision**: Use PEP 440 format in Python code, SemVer-style in Git tags (Git tags are more flexible)

### Why Manual Version Management (V1)?

Considered automation tools:
- `bump2version` - Automates version bumping
- `python-semantic-release` - Determines version from commit messages
- `setuptools_scm` - Derives version from Git tags

**Decision**: Manual for V1 because:
1. Simple - no extra dependencies
2. Full control - explicit about versions
3. Aligns with indie philosophy - minimal tooling
4. Can add automation later if needed

### Why Annotated Tags?

**Annotated tags** vs lightweight tags:

Annotated tags:
- Contain metadata (tagger, date, message)
- Can include release notes
- Can be GPG signed
- Treated as full objects in Git

**Decision**: Always use annotated tags for releases

### Why CHANGELOG.md?

**Changelog** provides human-readable release history:
- Users can quickly see what changed
- Easier to read than Git commits
- Standard location (`CHANGELOG.md`)
- Standard format (Keep a Changelog)
- Can be generated from commits or written manually

**Decision**: Maintain manually for V1 (precise control over messaging)

## Consequences

### Positive

1. **Clear Communication**: Users know exactly what each version means
2. **Ecosystem Compatibility**: Works with all Python tools
3. **Predictable Upgrades**: Users can assess risk before upgrading
4. **Professional Image**: Signals mature, well-maintained software
5. **Dependency Management**: Other projects can depend on StarPunk versions
6. **Git Integration**: Clean tagging and release workflow
7. **Flexible**: Can add automation later without changing format
8. **Standards-Based**: Uses established, documented standards

### Negative

1. **Manual Effort**: Requires discipline to update version in multiple places
2. **Coordination**: Must remember to update version, changelog, and tag
3. **Breaking Change Discipline**: Must carefully evaluate what constitutes breaking change
4. **Learning Curve**: Contributors need to understand SemVer rules

### Neutral

1. **0.x Signals Development**: May discourage early adopters who want "1.0" stability
2. **Commitment to Backward Compatibility**: Once at 1.0, breaking changes require major version
3. **Changelog Maintenance**: Ongoing effort to document changes

### Mitigations

**For manual effort**:
- Document clear release process in `versioning-strategy.md`
- Create release checklist
- Consider automation in V2+ if needed

**For breaking change discipline**:
- Deprecate features one version before removal when possible
- Document breaking changes prominently
- Provide upgrade guides for major versions

**For 0.x concerns**:
- Clearly communicate that 0.x is pre-production
- Move to 1.0.0 once V1 features are complete and tested
- Don't stay at 0.x longer than necessary

## Implementation

### Immediate Actions

1. **Set current version**: `0.1.0` (Phase 1.1 development)
2. **Create `starpunk/__init__.py`**:
   ```python
   __version__ = "0.1.0"
   __version_info__ = (0, 1, 0)
   ```
3. **Create `CHANGELOG.md`** with `[Unreleased]` section
4. **Update README.md** with version information
5. **Tag current state**: `git tag -a v0.1.0 -m "Development version 0.1.0"`

### Release Workflow

When ready to release:

1. Update `starpunk/__init__.py` with new version
2. Update `CHANGELOG.md` with release date
3. Commit: `git commit -m "Bump version to X.Y.Z"`
4. Tag: `git tag -a vX.Y.Z -m "Release X.Y.Z: [description]"`
5. Push: `git push origin main vX.Y.Z`

### Documentation

- Create comprehensive `docs/standards/versioning-strategy.md`
- Document examples, decision tree, and FAQ
- Include upgrade guide template
- Reference in README.md

### Future Enhancements

**V2+ Considerations**:
- Add `bump2version` for automation if manual process becomes tedious
- Consider API versioning if need to support multiple incompatible API versions
- Add database schema versioning if migrations become complex
- Automated changelog generation from commit messages

## Alternatives Considered

### Alternative 1: Calendar Versioning (CalVer)

**Format**: `YYYY.MM.PATCH` (e.g., `2024.11.0`)

**Pros**:
- Shows release date
- Clear chronological order
- Used by Ubuntu, PyCharm

**Cons**:
- Doesn't communicate change impact
- Less common in Python web frameworks
- Doesn't indicate breaking changes
- Overkill for indie project cadence

**Rejected**: Semantic meaning more important than date

### Alternative 2: Simple Integer Versioning

**Format**: `1`, `2`, `3` (e.g., like TeX: 3.14159...)

**Pros**:
- Extremely simple
- No ambiguity

**Cons**:
- No information about change severity
- Doesn't work with Python dependency tools
- Not standard in Python ecosystem
- Too minimalist for practical use

**Rejected**: Too simplistic, poor ecosystem fit

### Alternative 3: Modified SemVer (Django-style)

**Format**: `MAJOR.FEATURE.PATCH` (e.g., Django's `4.2.7`)

**Pros**:
- Used by Django
- Separates features from bug fixes

**Cons**:
- Non-standard SemVer interpretation
- Confusing: when to increment feature vs major?
- Doesn't clearly indicate breaking changes
- Less predictable

**Rejected**: Standard SemVer is clearer

### Alternative 4: ZeroVer (0.x forever)

**Format**: Stay at `0.x.y` indefinitely

**Pros**:
- Signals "always evolving"
- No commitment to stability

**Cons**:
- Users can't tell when production-ready
- Doesn't signal stability improvements
- Version 1.0.0 has meaning: "ready to use"

**Rejected**: We have clear 1.0 goals, should signal when achieved

### Alternative 5: Hybrid SemVer + CalVer

**Format**: `YYYY.MINOR.PATCH` (e.g., `2024.1.0`)

**Pros**:
- Combines date and semantic information

**Cons**:
- Unusual, confusing
- Not standard
- Year increments don't mean anything semantically

**Rejected**: Combines worst of both approaches

## References

### Standards

- [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) - Official SemVer specification
- [PEP 440 - Version Identification](https://peps.python.org/pep-0440/) - Python version numbering standard
- [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Changelog format standard
- [Calendar Versioning](https://calver.org/) - CalVer specification (considered but not adopted)

### Examples from Python Ecosystem

- [Flask Versioning](https://github.com/pallets/flask/releases) - Uses SemVer
- [Django Release Process](https://docs.djangoproject.com/en/stable/internals/release-process/) - Modified SemVer
- [Requests Versioning](https://github.com/psf/requests/releases) - Uses SemVer
- [FastAPI Versioning](https://github.com/tiangolo/fastapi/releases) - Uses SemVer

### Tools

- [bump2version](https://github.com/c4urself/bump2version) - Version bump automation
- [python-semantic-release](https://python-semantic-release.readthedocs.io/) - Automated semantic releases
- [setuptools_scm](https://github.com/pypa/setuptools_scm) - SCM-based versioning

### Internal Documentation

- [Versioning Strategy](/home/phil/Projects/starpunk/docs/standards/versioning-strategy.md) - Complete versioning specification
- [Architecture Overview](/home/phil/Projects/starpunk/docs/architecture/overview.md) - System architecture
- [Development Setup](/home/phil/Projects/starpunk/docs/standards/development-setup.md) - Development workflow

---

**ADR**: 008
**Date**: 2024-11-18
**Status**: Accepted
**Decision**: Adopt Semantic Versioning 2.0.0 with PEP 440 compliance
**Supersedes**: None
