forked from genewildish/Mainline
Add ADRs
35
ADR-001-Capability-Based-Dependency-Resolution.md
Normal file
35
ADR-001-Capability-Based-Dependency-Resolution.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# ADR-001: Capability-Based Dependency Resolution
|
||||||
|
|
||||||
|
**Date:** March 2026
|
||||||
|
**Status:** Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The pipeline needed a way for stages to auto-connect without hardcoding dependencies. Legacy system required explicit stage ordering.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Stages declare capabilities (what they provide) and dependencies (what they need). Pipeline resolves dependencies using prefix matching.
|
||||||
|
|
||||||
|
```python
|
||||||
|
class DataSourceStage(Stage):
|
||||||
|
capabilities = ["data"] # provides "data"
|
||||||
|
dependencies = [] # no dependencies
|
||||||
|
|
||||||
|
class FontStage(Stage):
|
||||||
|
capabilities = ["rendered"] # provides "rendered"
|
||||||
|
dependencies = ["data"] # needs "data"
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefix matching: dependency "source" matches capability "source.headlines", "source.poetry", etc.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Positive:** New stages integrate automatically without modifying controller
|
||||||
|
- **Positive:** Flexible composition - swap implementations by changing capabilities
|
||||||
|
- **Negative:** Debugging dependency chains requires understanding prefix resolution
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `engine/pipeline/core.py`: Stage base class
|
||||||
|
- `engine/pipeline/controller.py`: Resolution logic
|
||||||
35
ADR-002-Stage-Based-Pipeline-Architecture.md
Normal file
35
ADR-002-Stage-Based-Pipeline-Architecture.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# ADR-002: Stage-Based Pipeline Architecture
|
||||||
|
|
||||||
|
**Date:** March 2026
|
||||||
|
**Status:** Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Legacy used monolithic RenderStage that coupled fetching, rendering, and display. Hard to extend or test.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Replace with composable Stage classes, each with single responsibility:
|
||||||
|
|
||||||
|
- **DataSourceStage**: Wraps DataSource, provides raw items
|
||||||
|
- **SourceItemsToBufferStage**: Converts items to display buffer
|
||||||
|
- **EffectPluginStage**: Applies effects chain
|
||||||
|
- **DisplayStage**: Renders to display backend
|
||||||
|
- **ViewportFilterStage**: Filters to visible region
|
||||||
|
|
||||||
|
Each Stage implements:
|
||||||
|
- `capabilities`: What it provides
|
||||||
|
- `dependencies`: What it needs
|
||||||
|
- `process(buf, ctx)`: Transform the buffer
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Positive:** Single responsibility - easier to test and debug
|
||||||
|
- **Positive:** Replace any stage without affecting others
|
||||||
|
- **Positive:** Pipeline introspection shows clear data flow
|
||||||
|
- **Negative:** More classes to maintain
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `engine/pipeline/core.py`: Stage ABC
|
||||||
|
- `engine/pipeline/adapters.py`: Concrete stage implementations
|
||||||
40
ADR-003-DataSource-Abstraction.md
Normal file
40
ADR-003-DataSource-Abstraction.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# ADR-003: DataSource Abstraction
|
||||||
|
|
||||||
|
**Date:** March 2026
|
||||||
|
**Status:** Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Headlines, poetry, and pipeline data all had different interfaces. Pipeline stages needed consistent access.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Abstract DataSource class with get_items():
|
||||||
|
|
||||||
|
```python
|
||||||
|
class DataSource(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def get_items(self) -> list[SourceItem]:
|
||||||
|
...
|
||||||
|
|
||||||
|
class SourceRegistry:
|
||||||
|
def get(self, name: str) -> DataSource: ...
|
||||||
|
def list_available(self) -> list[str]: ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Implementations:
|
||||||
|
- **HeadlinesDataSource**: Static RSS feed, cached
|
||||||
|
- **PoetryDataSource**: Static poetry feed, cached
|
||||||
|
- **ListDataSource**: Wraps pre-fetched items (testing)
|
||||||
|
- **PipelineDataSource**: Dynamic, re-fetches each cycle
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Positive:** Consistent interface across all source types
|
||||||
|
- **Positive:** SourceRegistry enables discovery
|
||||||
|
- **Negative:** Runtime overhead for dynamic sources
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `engine/data_sources/sources.py`: DataSource, SourceRegistry
|
||||||
|
- `tests/test_data_sources.py`: Comprehensive tests
|
||||||
41
ADR-004-Display-Protocol-Pattern.md
Normal file
41
ADR-004-Display-Protocol-Pattern.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# ADR-004: Display Protocol Pattern
|
||||||
|
|
||||||
|
**Date:** March 2026
|
||||||
|
**Status:** Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Different display targets (terminal, browser, pygame) needed unified interface. Hard to add new backends.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Display protocol with common interface:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class Display(Protocol):
|
||||||
|
def setup(self, width: int, height: int) -> None: ...
|
||||||
|
def draw(self, buf: Buffer) -> None: ...
|
||||||
|
def refresh(self) -> None: ...
|
||||||
|
def cleanup(self) -> None: ...
|
||||||
|
```
|
||||||
|
|
||||||
|
DisplayRegistry auto-discovers backends from `engine/display/backends/`:
|
||||||
|
|
||||||
|
- **terminal.py**: ANSI escape sequences
|
||||||
|
- **websocket.py**: HTML5 Canvas broadcast
|
||||||
|
- **sixel.py**: Sixel graphics
|
||||||
|
- **kitty.py**: Kitty protocol
|
||||||
|
- **pygame.py**: Pygame window
|
||||||
|
- **null.py**: Headless (testing)
|
||||||
|
- **multi.py**: Forward to multiple backends
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Positive:** Add new backend by implementing protocol
|
||||||
|
- **Positive:** MultiDisplay enables simultaneous outputs
|
||||||
|
- **Positive:** NullDisplay enables headless testing
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `engine/display/__init__.py`: Display, DisplayRegistry
|
||||||
|
- `engine/display/backends/`: Backend implementations
|
||||||
56
ADR-005-Sensor-Framework.md
Normal file
56
ADR-005-Sensor-Framework.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# ADR-005: Sensor Framework
|
||||||
|
|
||||||
|
**Date:** March 2026
|
||||||
|
**Status:** Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Effects should react to real-time input (microphone, pipeline metrics) without hardcoding sensor logic.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Sensor ABC with read() returning SensorValue:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class Sensor(ABC):
|
||||||
|
name: str
|
||||||
|
unit: str = ""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def read(self) -> SensorValue | None: ...
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SensorValue:
|
||||||
|
sensor_name: str
|
||||||
|
value: float
|
||||||
|
timestamp: float
|
||||||
|
unit: str = ""
|
||||||
|
```
|
||||||
|
|
||||||
|
Implementations:
|
||||||
|
- **MicSensor**: Microphone RMS dB (via sounddevice)
|
||||||
|
- **PipelineMetricsSensor**: FPS, frame-time
|
||||||
|
- **OscillatorSensor**: Test sine wave generator
|
||||||
|
|
||||||
|
SensorStage provides values to effects. Effects declare param_bindings:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class GlitchEffect(EffectPlugin):
|
||||||
|
param_bindings = {
|
||||||
|
"intensity": {"sensor": "mic", "transform": "linear"},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Transforms: linear, exponential, threshold
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Positive:** Extensible - add new sensors without touching effects
|
||||||
|
- **Positive:** Effects react to any sensor uniformly
|
||||||
|
- **Negative:** Audio sensors require additional dependencies
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `engine/sensors/__init__.py`: Sensor ABC, SensorRegistry
|
||||||
|
- `engine/sensors/mic.py`: MicSensor
|
||||||
|
- `engine/pipeline/adapters.py`: SensorStage
|
||||||
50
ADR-006-Preset-TOML-Format.md
Normal file
50
ADR-006-Preset-TOML-Format.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# ADR-006: Preset TOML Format
|
||||||
|
|
||||||
|
**Date:** March 2026
|
||||||
|
**Status:** Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Pipeline presets needed configuration format. Avoid external dependencies (no YAML, JSON, etc.).
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
TOML format with validation:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[preset]
|
||||||
|
name = "firehose"
|
||||||
|
display = "terminal"
|
||||||
|
|
||||||
|
[[stages]]
|
||||||
|
type = "datasource"
|
||||||
|
source = "headlines"
|
||||||
|
|
||||||
|
[[stages]]
|
||||||
|
type = "effect"
|
||||||
|
name = "glitch"
|
||||||
|
intensity = 0.5
|
||||||
|
|
||||||
|
[stages.config]
|
||||||
|
param_bindings = { intensity = { sensor = "mic", transform = "linear" } }
|
||||||
|
```
|
||||||
|
|
||||||
|
Locations (in order of priority):
|
||||||
|
1. `./presets.toml` (local override)
|
||||||
|
2. `~/.config/mainline/presets.toml` (user config)
|
||||||
|
3. `engine/presets.toml` (built-in)
|
||||||
|
|
||||||
|
PresetLoader validates structure and signal paths (detects circular deps).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Positive:** No external dependencies (TOML built into Python 3.11+)
|
||||||
|
- **Positive:** Human-readable, easy to edit
|
||||||
|
- **Positive:** Validation prevents runtime errors
|
||||||
|
- **Negative:** Must document TOML schema
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `engine/pipeline/preset_loader.py`: Loading and validation
|
||||||
|
- `engine/pipeline/presets.py`: Preset dataclasses
|
||||||
|
- `engine/presets.toml`: Built-in presets
|
||||||
Reference in New Issue
Block a user