# Story 1.20: Plugin Metadata Version Alignment

**Status:** Complete
**Priority:** Medium
**Depends on:** None

---

## Context / Background

The GPT-5.2 audit identified that 9 plugins claim `min_fapilog_version: 0.4.0` in their metadata, while the current project version is 0.3.x. This creates confusion and potential future compatibility issues.

**Affected plugins:**

| Plugin | File |
|--------|------|
| adaptive_sampling | `src/fapilog/plugins/filters/adaptive_sampling.py:121` |
| rate_limit | `src/fapilog/plugins/filters/rate_limit.py:125` |
| sampling | `src/fapilog/plugins/filters/sampling.py:59` |
| trace_sampling | `src/fapilog/plugins/filters/trace_sampling.py:73` |
| first_occurrence | `src/fapilog/plugins/filters/first_occurrence.py:88` |
| level | `src/fapilog/plugins/filters/level.py:63` |
| audit | `src/fapilog/plugins/sinks/audit.py:158` |
| routing | `src/fapilog/plugins/sinks/routing.py:127` |
| postgres | `src/fapilog/plugins/sinks/contrib/postgres.py:393` |

**Problem:**
- Metadata claims compatibility with unreleased version
- If version checking is enforced, these plugins would fail
- Confuses plugin authors about versioning expectations
- Migration hazard when 0.4.0 is actually released

---

## Scope (In / Out)

### In Scope

- Change all `min_fapilog_version: 0.4.0` to `0.3.0`
- Add pre-commit hook to validate plugin metadata versions
- Document plugin metadata versioning policy

### Out of Scope

- Implementing version enforcement at runtime (future story)
- Changing plugin metadata schema
- Updating other metadata fields

---

## Acceptance Criteria

### AC1: All Plugins Claim Valid Version

**Description:** No plugin claims a `min_fapilog_version` higher than current release.

**Validation:**
```bash
# Should return no matches
grep -r "min_fapilog_version.*0\.4" src/fapilog/plugins/
```

### AC2: Plugins Claim 0.3.0

**Description:** Affected plugins now claim `min_fapilog_version: 0.3.0`.

**Validation:**
```bash
# Should return 9 matches
grep -r "min_fapilog_version.*0\.3\.0" src/fapilog/plugins/ | wc -l
```

### AC3: Pre-commit Hook Validates Versions

**Description:** A pre-commit hook checks that no plugin claims a future version.

**Validation:**
```yaml
# In .pre-commit-config.yaml
- id: check-plugin-versions
  name: Check plugin min_fapilog_version
  entry: python scripts/check_plugin_versions.py
  language: python
```

### AC4: Documentation Updated

**Description:** Plugin authoring docs explain versioning policy.

**Validation:**
- `docs/plugins/` contains versioning guidance
- States: use earliest version where plugin works

---

## Implementation Notes

### File Changes

```
src/fapilog/plugins/filters/adaptive_sampling.py (MODIFIED)
src/fapilog/plugins/filters/rate_limit.py (MODIFIED)
src/fapilog/plugins/filters/sampling.py (MODIFIED)
src/fapilog/plugins/filters/trace_sampling.py (MODIFIED)
src/fapilog/plugins/filters/first_occurrence.py (MODIFIED)
src/fapilog/plugins/filters/level.py (MODIFIED)
src/fapilog/plugins/sinks/audit.py (MODIFIED)
src/fapilog/plugins/sinks/routing.py (MODIFIED)
src/fapilog/plugins/sinks/contrib/postgres.py (MODIFIED)
scripts/check_plugin_versions.py (NEW)
.pre-commit-config.yaml (MODIFIED)
docs/plugins/authoring.md (MODIFIED or NEW)
```

### Version Check Script

```python
#!/usr/bin/env python3
"""Check that plugin min_fapilog_version doesn't exceed current version."""

import re
import sys
from pathlib import Path

# Current released version (update on release)
CURRENT_VERSION = "0.3.5"

def parse_version(v: str) -> tuple[int, ...]:
    return tuple(int(x) for x in v.split("."))

def main() -> int:
    errors = []
    pattern = re.compile(r'"min_fapilog_version":\s*"([^"]+)"')

    for path in Path("src/fapilog/plugins").rglob("*.py"):
        content = path.read_text()
        for match in pattern.finditer(content):
            claimed = match.group(1)
            if parse_version(claimed) > parse_version(CURRENT_VERSION):
                errors.append(f"{path}: claims {claimed} > {CURRENT_VERSION}")

    if errors:
        print("Plugin version errors:")
        for e in errors:
            print(f"  {e}")
        return 1
    return 0

if __name__ == "__main__":
    sys.exit(main())
```

---

## Tasks

### Phase 1: Fix Versions

- [ ] Update `adaptive_sampling.py` to 0.3.0
- [ ] Update `rate_limit.py` to 0.3.0
- [ ] Update `sampling.py` to 0.3.0
- [ ] Update `trace_sampling.py` to 0.3.0
- [ ] Update `first_occurrence.py` to 0.3.0
- [ ] Update `level.py` to 0.3.0
- [ ] Update `audit.py` to 0.3.0
- [ ] Update `routing.py` to 0.3.0
- [ ] Update `postgres.py` to 0.3.0

### Phase 2: Validation Script

- [ ] Create `scripts/check_plugin_versions.py`
- [ ] Add to `.pre-commit-config.yaml`
- [ ] Test hook locally

### Phase 3: Documentation

- [ ] Add versioning guidance to plugin docs
- [ ] Update CHANGELOG

---

## Tests

### Manual Verification

```bash
# Verify no future versions claimed
grep -r "min_fapilog_version" src/fapilog/plugins/ | grep -v "0\.3"

# Run version check script
python scripts/check_plugin_versions.py
```

### Pre-commit Test

```bash
pre-commit run check-plugin-versions --all-files
```

---

## Definition of Done

### Code Complete

- [x] All 9 plugins updated to 0.3.0
- [x] Version check script created
- [x] Pre-commit hook configured

### Quality Assurance

- [x] `ruff check` passes
- [x] Pre-commit hook passes
- [x] No grep matches for 0.4.0

### Documentation

- [x] Plugin versioning policy documented
- [ ] CHANGELOG updated (N/A for chore)

---

## Risks / Rollback

### Risks

1. **Risk:** Breaking changes in 0.3.x that plugins actually need
   - **Mitigation:** Review each plugin; 0.3.0 is safe baseline

2. **Risk:** Script maintenance burden
   - **Mitigation:** Simple regex-based; update CURRENT_VERSION on release

### Rollback Plan

If issues occur:
1. Revert plugin changes
2. Remove pre-commit hook
3. Investigate actual version requirements

---

## Related Stories

- **Related:** Story 1.15 - Formal plugin contracts
- **Related:** Story 4.23 - Plugin metadata completeness

---

## Code Review

**Date:** 2026-01-17
**Reviewer:** Claude
**Verdict:** OK to PR

### Summary

Reviewed version metadata updates across 8 plugins, new validation script with 17 tests at 100% coverage, pre-commit hook configuration, and versioning policy documentation.

### AC Verification

| Criterion | Evidence |
|-----------|----------|
| AC1: No future versions | `grep -r "min_fapilog_version.*0\.4" --include="*.py"` returns no matches |
| AC2: Plugins claim 0.3.0 | 8 plugin files updated: `adaptive_sampling.py`, `rate_limit.py`, `sampling.py`, `trace_sampling.py`, `first_occurrence.py`, `level.py`, `routing.py`, `postgres.py` |
| AC3: Pre-commit hook | `.pre-commit-config.yaml:78-85` - hook passes |
| AC4: Documentation | `docs/plugins/contracts-and-versioning.md:68-96` |

### Quality Gates

- [x] ruff check passed
- [x] ruff format passed
- [x] mypy passed
- [x] 100% test coverage on new script
- [x] No weak assertions
- [x] No dead code

### Notes

- `audit.py` was moved to `packages/fapilog-audit/` in story 4.44
- Script updated to scan `packages/` directory for extracted packages
- All 9 plugins now corrected

---

## Change Log

| Date | Change | Author |
|------|--------|--------|
| 2026-01-17 | Code review passed, status → Complete | Claude |
| 2026-01-16 | Initial draft from GPT-5.2 audit | Claude |
