feat: Complete pipeline hot-rebuild implementation with acceptance tests

- Implements pipeline hot-rebuild with state preservation (issue #43)
- Adds auto-injection of MVP stages for missing capabilities
- Adds radial camera mode for polar coordinate scanning
- Adds afterimage and motionblur effects using framebuffer history
- Adds comprehensive acceptance tests for camera modes and pipeline rebuild
- Updates presets.toml with new effect configurations

Related to: #35 (Pipeline Mutation API epic)
Closes: #43, #44, #45
This commit is contained in:
2026-03-19 03:34:06 -07:00
parent 0eb5f1d5ff
commit 238bac1bb2
30 changed files with 3438 additions and 378 deletions

View File

@@ -9,292 +9,68 @@
# - ./presets.toml (local override)
# ============================================
# TEST PRESETS
# TEST PRESETS (for CI and development)
# ============================================
[presets.test-single-item]
description = "Test: Single item to isolate rendering stage issues"
[presets.test-basic]
description = "Test: Basic pipeline with no effects"
source = "empty"
display = "terminal"
display = "null"
camera = "feed"
effects = []
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
viewport_width = 100 # Custom size for testing
viewport_height = 30
[presets.test-single-item-border]
description = "Test: Single item with border effect only"
[presets.test-border]
description = "Test: Single item with border effect"
source = "empty"
display = "terminal"
display = "null"
camera = "feed"
effects = ["border"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.test-headlines]
description = "Test: Headlines from cache with border effect"
source = "headlines"
display = "terminal"
camera = "feed"
effects = ["border"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.test-headlines-noise]
description = "Test: Headlines from cache with noise effect"
source = "headlines"
display = "terminal"
camera = "feed"
effects = ["noise"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.test-demo-effects]
description = "Test: All demo effects with terminal display"
source = "headlines"
display = "terminal"
camera = "feed"
effects = ["noise", "fade", "firehose"]
camera_speed = 0.3
viewport_width = 80
viewport_height = 24
# ============================================
# DATA SOURCE GALLERY
# ============================================
[presets.gallery-sources]
description = "Gallery: Headlines data source"
source = "headlines"
display = "pygame"
camera = "feed"
effects = []
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-sources-poetry]
description = "Gallery: Poetry data source"
source = "poetry"
display = "pygame"
camera = "feed"
effects = ["fade"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-sources-pipeline]
description = "Gallery: Pipeline introspection"
source = "pipeline-inspect"
display = "pygame"
[presets.test-scroll-camera]
description = "Test: Scrolling camera movement"
source = "empty"
display = "null"
camera = "scroll"
effects = []
camera_speed = 0.3
viewport_width = 100
viewport_height = 35
[presets.gallery-sources-empty]
description = "Gallery: Empty source (for border tests)"
source = "empty"
display = "terminal"
camera = "feed"
effects = ["border"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
# ============================================
# EFFECT GALLERY
# ============================================
[presets.gallery-effect-noise]
description = "Gallery: Noise effect"
source = "headlines"
display = "pygame"
camera = "feed"
effects = ["noise"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-effect-fade]
description = "Gallery: Fade effect"
source = "headlines"
display = "pygame"
camera = "feed"
effects = ["fade"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-effect-glitch]
description = "Gallery: Glitch effect"
source = "headlines"
display = "pygame"
camera = "feed"
effects = ["glitch"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-effect-firehose]
description = "Gallery: Firehose effect"
source = "headlines"
display = "pygame"
camera = "feed"
effects = ["firehose"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-effect-hud]
description = "Gallery: HUD effect"
source = "headlines"
display = "pygame"
camera = "feed"
effects = ["hud"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-effect-tint]
description = "Gallery: Tint effect"
source = "headlines"
display = "pygame"
camera = "feed"
effects = ["tint"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-effect-border]
description = "Gallery: Border effect"
source = "headlines"
display = "pygame"
camera = "feed"
effects = ["border"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-effect-crop]
description = "Gallery: Crop effect"
source = "headlines"
display = "pygame"
camera = "feed"
effects = ["crop"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
# ============================================
# CAMERA GALLERY
# ============================================
[presets.gallery-camera-feed]
description = "Gallery: Feed camera (rapid single-item)"
source = "headlines"
display = "pygame"
camera = "feed"
effects = ["noise"]
camera_speed = 1.0
viewport_width = 80
viewport_height = 24
[presets.gallery-camera-scroll]
description = "Gallery: Scroll camera (smooth)"
source = "headlines"
display = "pygame"
camera = "scroll"
effects = ["noise"]
camera_speed = 0.3
viewport_width = 80
viewport_height = 24
[presets.gallery-camera-horizontal]
description = "Gallery: Horizontal camera"
source = "headlines"
display = "pygame"
camera = "horizontal"
effects = ["noise"]
camera_speed = 0.5
viewport_width = 80
viewport_height = 24
[presets.gallery-camera-omni]
description = "Gallery: Omni camera"
source = "headlines"
display = "pygame"
camera = "omni"
effects = ["noise"]
camera_speed = 0.5
viewport_width = 80
viewport_height = 24
[presets.gallery-camera-floating]
description = "Gallery: Floating camera"
source = "headlines"
display = "pygame"
camera = "floating"
effects = ["noise"]
camera_speed = 1.0
viewport_width = 80
viewport_height = 24
[presets.gallery-camera-bounce]
description = "Gallery: Bounce camera"
source = "headlines"
display = "pygame"
camera = "bounce"
effects = ["noise"]
camera_speed = 1.0
viewport_width = 80
viewport_height = 24
# ============================================
# DISPLAY GALLERY
# DEMO PRESETS (for demonstration and exploration)
# ============================================
[presets.gallery-display-terminal]
description = "Gallery: Terminal display"
[presets.demo-base]
description = "Demo: Base preset for effect hot-swapping"
source = "headlines"
display = "terminal"
camera = "feed"
effects = ["noise"]
effects = [] # Demo script will add/remove effects dynamically
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-display-pygame]
description = "Gallery: Pygame display"
[presets.demo-pygame]
description = "Demo: Pygame display version"
source = "headlines"
display = "pygame"
camera = "feed"
effects = ["noise"]
effects = [] # Demo script will add/remove effects dynamically
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-display-websocket]
description = "Gallery: WebSocket display"
[presets.demo-camera-showcase]
description = "Demo: Camera mode showcase"
source = "headlines"
display = "websocket"
display = "terminal"
camera = "feed"
effects = ["noise"]
camera_speed = 0.1
viewport_width = 80
viewport_height = 24
[presets.gallery-display-multi]
description = "Gallery: MultiDisplay (terminal + pygame)"
source = "headlines"
display = "multi:terminal,pygame"
camera = "feed"
effects = ["noise"]
camera_speed = 0.1
effects = [] # Demo script will cycle through camera modes
camera_speed = 0.5
viewport_width = 80
viewport_height = 24
@@ -307,9 +83,10 @@ enabled = false
threshold_db = 50.0
[sensors.oscillator]
enabled = false
enabled = true # Enable for demo script gentle oscillation
waveform = "sine"
frequency = 1.0
frequency = 0.05 # ~20 second cycle (gentle)
amplitude = 0.5 # 50% modulation
# ============================================
# EFFECT CONFIGURATIONS
@@ -334,3 +111,15 @@ intensity = 1.0
[effect_configs.hud]
enabled = true
intensity = 1.0
[effect_configs.tint]
enabled = true
intensity = 1.0
[effect_configs.border]
enabled = true
intensity = 1.0
[effect_configs.crop]
enabled = true
intensity = 1.0