## Status

Draft

## Story

**As a** plugin author,
**I want** formal, versioned contracts for sinks, redactors, enrichers, and processors,
**so that** I can confidently implement plugins that remain compatible and safe across library upgrades.

## Acceptance Criteria

1. Canonical async Protocols exist and are public for all plugin types:
   - `BaseSink` at `src/fapilog/plugins/sinks/__init__.py`
   - `BaseEnricher` at `src/fapilog/plugins/enrichers/__init__.py`
   - `BaseProcessor` at `src/fapilog/plugins/processors/__init__.py`
   - `BaseRedactor` at `src/fapilog/plugins/redactors/__init__.py`
   - Protocols are `@runtime_checkable`, have explicit method signatures and docstrings, and are re-exported from `src/fapilog/plugins/__init__.py` for author consumption.
2. Introduce a plugin API version with clear compatibility rules:
   - Define `PLUGIN_API_VERSION: tuple[int, int] = (1, 0)` (semantic major/minor) and document policy: major must match; minor may be equal or lower to be compatible.
   - Extend plugin metadata to include an `api_version` field (e.g., "1.0") that is parsed and validated.
   - Provide `is_plugin_api_compatible()` utility consistent with the policy above.
3. Enforce compatibility at load time:
   - During registry load (`src/fapilog/plugins/registry.py`), validate both Fapilog core version compatibility and plugin API version compatibility.
   - Incompatibilities are rejected gracefully with a precise error message and diagnostics via `core.diagnostics.warn`.
4. Built-ins are migrated to declare API version and comply with Protocols:
   - `src/fapilog/plugins/sinks/stdout_json.py` and `src/fapilog/plugins/sinks/rotating_file.py` updated to include `PLUGIN_METADATA["api_version"] = "1.0"` and satisfy `BaseSink`.
   - Built-in enrichers/redactors in `src/fapilog/plugins/enrichers/` and `src/fapilog/plugins/redactors/` satisfy their Protocols.
5. Backward compatibility is guaranteed for all v1.x plugin contracts (no breaking changes to Protocol method signatures within major=1).

## Tasks / Subtasks

- [ ] Protocols: tighten and document
  - [ ] Ensure `BaseSink`, `BaseEnricher`, `BaseProcessor`, `BaseRedactor` Protocols have comprehensive docstrings and remain `@runtime_checkable` (AC: 1).
  - [ ] Confirm all four are re-exported from `src/fapilog/plugins/__init__.py` (already present) (AC: 1).
- [ ] Versioning utilities
  - [ ] Add `PLUGIN_API_VERSION = (1, 0)` and `parse_api_version(str) -> tuple[int,int]` plus `is_plugin_api_compatible(tuple[int,int], tuple[int,int]) -> bool` in `src/fapilog/plugins/metadata.py` or new `src/fapilog/plugins/versioning.py` (AC: 2).
  - [ ] Extend `PluginMetadata` (in `src/fapilog/plugins/metadata.py`) with `api_version: str` and robust parsing/validation (AC: 2).
- [ ] Load-time enforcement
  - [ ] In `AsyncComponentRegistry.load_plugin()` add API-version check alongside existing `validate_fapilog_compatibility()` (AC: 3).
  - [ ] Emit `core.diagnostics.warn` on failures with fields: `plugin`, `declared_api_version`, `expected_api_version`, `reason` (AC: 3).
- [ ] Built-ins migration
  - [ ] Update `src/fapilog/plugins/sinks/stdout_json.py` and `src/fapilog/plugins/sinks/rotating_file.py` `PLUGIN_METADATA` to include `api_version: "1.0"` (AC: 4).
  - [ ] Verify enrichers (`enrichers/context_vars.py`, `enrichers/runtime_info.py`) and redactors (`redactors/field_mask.py`, `redactors/regex_mask.py`, `redactors/url_credentials.py`) conform to Protocols (AC: 4).
- [ ] Guidance
  - [ ] Author a short migration note in `docs/plugins/contracts-and-versioning.md` describing the API policy and `PLUGIN_METADATA` keys (AC: 5).

## Dev Notes

- Existing locations and exports:
  - Protocols already exist and are public: `BaseSink`, `BaseEnricher`, `BaseProcessor`, `BaseRedactor` (see files in Acceptance Criteria).
  - Discovery and runtime metadata live in `src/fapilog/plugins/discovery.py`, `src/fapilog/plugins/metadata.py`, and registry in `src/fapilog/plugins/registry.py`.
  - Built-in sinks: `src/fapilog/plugins/sinks/stdout_json.py`, `src/fapilog/plugins/sinks/rotating_file.py`.
  - Built-in enrichers/redactors: `src/fapilog/plugins/enrichers/*.py`, `src/fapilog/plugins/redactors/*.py`.
- Compatibility design:
  - Keep `PluginCompatibility` for core version checks and add `api_version` for contract-level checks.
  - Policy: compatible if `declared.major == current.major` and `declared.minor <= current.minor`.
  - Prefer storing `api_version` as a string (e.g., "1.0") in metadata and parse to a tuple for comparison to simplify authoring.
- Error handling:
  - Use `PluginRegistryError` for hard failures, and `core.diagnostics.warn` for structured logs. Never crash the pipeline on discovery; fail fast at load.
- Public API:
  - Ensure `api_version` addition to `PluginMetadata` is non-breaking for existing metadata construction helpers.

### Testing

- Locations:
  - Unit tests: `tests/unit/plugins/`
  - Integration tests: `tests/integration/plugins/`
  - Optionally keep `tests/unit/test_plugins.py` for legacy aggregation, but prefer the structure above.
- Async tests use `pytest.mark.asyncio` (pattern already prevalent across the suite).
- Add contract conformance tests using `isinstance(x, BaseSink)` etc. thanks to `@runtime_checkable`.
- Negative tests: incompatible `api_version` (e.g., plugin declares 2.0 while core is 1.0) must be rejected with clear diagnostics; ensure discovery still lists plugin with `load_error` when applicable.
- Coverage: maintain >90% for new/modified code paths.

## Change Log

| Date       | Version | Description                                                                   | Author    |
| ---------- | ------- | ----------------------------------------------------------------------------- | --------- |
| 2025-08-15 | 0.2     | Corrected paths; clarified contracts; added API versioning + enforcement plan | Architect |

## Dev Agent Record

### Agent Model Used

{{agent_model_name_version}}

### Debug Log References

### Completion Notes List

### File List

## QA Results
