docs(02-04): complete ChoreographyScheduler plan — CHR-05 absolute monotonic scheduler
- SUMMARY.md: timing algorithm, 9 tests, D-07/D-08/D-09 decisions documented - STATE.md: plan advanced, progress 90%, decisions logged - ROADMAP.md: phase 2 progress updated (5/6 summaries)
This commit is contained in:
@@ -53,8 +53,8 @@ Plans:
|
|||||||
- [x] 02-01-PLAN.md — Project scaffold (pyproject.toml, uv deps) + Pydantic choreography models
|
- [x] 02-01-PLAN.md — Project scaffold (pyproject.toml, uv deps) + Pydantic choreography models
|
||||||
- [x] 02-02-PLAN.md — UDP transport client (asyncio DatagramProtocol to ESP32)
|
- [x] 02-02-PLAN.md — UDP transport client (asyncio DatagramProtocol to ESP32)
|
||||||
- [x] 02-03-PLAN.md — Audio player (miniaudio playback, position tracking, pause/seek)
|
- [x] 02-03-PLAN.md — Audio player (miniaudio playback, position tracking, pause/seek)
|
||||||
- [ ] 02-04-PLAN.md — Choreography scheduler (absolute monotonic clock, event dispatch)
|
- [x] 02-04-PLAN.md — Choreography scheduler (absolute monotonic clock, event dispatch)
|
||||||
- [ ] 02-05-PLAN.md — Beat detector (sounddevice + aubio, asyncio bridge)
|
- [x] 02-05-PLAN.md — Beat detector (sounddevice + aubio, asyncio bridge)
|
||||||
- [ ] 02-06-PLAN.md — Main entry point + asyncio REPL + hardware acceptance checkpoint
|
- [ ] 02-06-PLAN.md — Main entry point + asyncio REPL + hardware acceptance checkpoint
|
||||||
|
|
||||||
### Phase 3: Choreography TUI
|
### Phase 3: Choreography TUI
|
||||||
@@ -88,6 +88,6 @@ Phases execute in numeric order: 1 → 2 → 3 → 4
|
|||||||
| Phase | Plans Complete | Status | Completed |
|
| Phase | Plans Complete | Status | Completed |
|
||||||
|-------|----------------|--------|-----------|
|
|-------|----------------|--------|-----------|
|
||||||
| 1. ESP32 Firmware | 4/4 | Complete | 2026-04-03 |
|
| 1. ESP32 Firmware | 4/4 | Complete | 2026-04-03 |
|
||||||
| 2. App Core + Audio | 0/6 | Not started | - |
|
| 2. App Core + Audio | 5/6 | In Progress| |
|
||||||
| 3. Choreography TUI | 0/? | Not started | - |
|
| 3. Choreography TUI | 0/? | Not started | - |
|
||||||
| 4. Live Reactive + Spotify | 0/? | Not started | - |
|
| 4. Live Reactive + Spotify | 0/? | Not started | - |
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ gsd_state_version: 1.0
|
|||||||
milestone: v1.0
|
milestone: v1.0
|
||||||
milestone_name: milestone
|
milestone_name: milestone
|
||||||
status: executing
|
status: executing
|
||||||
stopped_at: Completed 02-app-core-audio 02-01-PLAN.md
|
stopped_at: Completed 02-app-core-audio 02-04-PLAN.md
|
||||||
last_updated: "2026-04-03T12:24:22.483Z"
|
last_updated: "2026-04-03T12:52:07.259Z"
|
||||||
last_activity: 2026-04-03
|
last_activity: 2026-04-03
|
||||||
progress:
|
progress:
|
||||||
total_phases: 4
|
total_phases: 4
|
||||||
completed_phases: 1
|
completed_phases: 1
|
||||||
total_plans: 10
|
total_plans: 10
|
||||||
completed_plans: 7
|
completed_plans: 9
|
||||||
percent: 0
|
percent: 0
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ See: .planning/PROJECT.md (updated 2026-04-03)
|
|||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 02 (app-core-audio) — EXECUTING
|
Phase: 02 (app-core-audio) — EXECUTING
|
||||||
Plan: 4 of 6
|
Plan: 3 of 6
|
||||||
Status: Ready to execute
|
Status: Ready to execute
|
||||||
Last activity: 2026-04-03
|
Last activity: 2026-04-03
|
||||||
|
|
||||||
@@ -61,6 +61,8 @@ Progress: [░░░░░░░░░░] 0%
|
|||||||
| Phase 02-app-core-audio P02 | 4min | 1 tasks | 4 files |
|
| Phase 02-app-core-audio P02 | 4min | 1 tasks | 4 files |
|
||||||
| Phase 02-app-core-audio P03 | 4 | 1 tasks | 3 files |
|
| Phase 02-app-core-audio P03 | 4 | 1 tasks | 3 files |
|
||||||
| Phase 02-app-core-audio P01 | 7min | 2 tasks | 7 files |
|
| Phase 02-app-core-audio P01 | 7min | 2 tasks | 7 files |
|
||||||
|
| Phase 02-app-core-audio P04 | 2min | 1 tasks | 2 files |
|
||||||
|
| Phase 02-app-core-audio P05 | 2min | 1 tasks | 3 files |
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
@@ -91,6 +93,8 @@ Recent decisions affecting current work:
|
|||||||
- [Phase 02-app-core-audio]: aubio 0.4.9 source build confirmed on x86_64 (no wheel); will also build on aarch64 Pi
|
- [Phase 02-app-core-audio]: aubio 0.4.9 source build confirmed on x86_64 (no wheel); will also build on aarch64 Pi
|
||||||
- [Phase 02-app-core-audio]: sounddevice requires libportaudio2 system package; install via apt on Raspberry Pi OS before uv sync
|
- [Phase 02-app-core-audio]: sounddevice requires libportaudio2 system package; install via apt on Raspberry Pi OS before uv sync
|
||||||
- [Phase 02-app-core-audio]: Pydantic v2 field_validator + model_post_init pattern established for ChoreoEvent/ChoreoFile models
|
- [Phase 02-app-core-audio]: Pydantic v2 field_validator + model_post_init pattern established for ChoreoEvent/ChoreoFile models
|
||||||
|
- [Phase 02-app-core-audio]: Absolute monotonic timestamps (D-07): fire_at recalculated fresh each 5ms poll — no drift accumulation over any duration
|
||||||
|
- [Phase 02-app-core-audio]: pause_offset accumulation (D-09): resume() adds elapsed pause time keeping all fire_at values correct without event skipping
|
||||||
|
|
||||||
### Pending Todos
|
### Pending Todos
|
||||||
|
|
||||||
@@ -105,6 +109,6 @@ None yet.
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-04-03T12:24:22.471Z
|
Last session: 2026-04-03T12:51:57.725Z
|
||||||
Stopped at: Completed 02-app-core-audio 02-01-PLAN.md
|
Stopped at: Completed 02-app-core-audio 02-04-PLAN.md
|
||||||
Resume file: None
|
Resume file: None
|
||||||
|
|||||||
113
.planning/phases/02-app-core-audio/02-04-SUMMARY.md
Normal file
113
.planning/phases/02-app-core-audio/02-04-SUMMARY.md
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
---
|
||||||
|
phase: 02-app-core-audio
|
||||||
|
plan: "04"
|
||||||
|
subsystem: scheduling
|
||||||
|
tags: [asyncio, monotonic-clock, choreography, timing, scheduler]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: 02-01
|
||||||
|
provides: ChoreoEvent Pydantic model with timestamp/zone/animation/params fields
|
||||||
|
- phase: 02-02
|
||||||
|
provides: ESP32Transport.send_command() for fire-and-forget UDP dispatch
|
||||||
|
provides:
|
||||||
|
- ChoreographyScheduler asyncio task — absolute monotonic clock event dispatch
|
||||||
|
- pause/resume with pause_offset accumulation (D-09)
|
||||||
|
- seek() skips events before seek position
|
||||||
|
- on_event callback hook for TUI integration
|
||||||
|
affects:
|
||||||
|
- 02-05 (beat detection integration)
|
||||||
|
- 03-xx (TUI wraps scheduler for playback controls)
|
||||||
|
- phase 4 (live reactive mode uses on_event hook)
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "Absolute monotonic scheduling: fire_at = start_time + pause_offset + event.timestamp — never accumulate relative delays"
|
||||||
|
- "5ms poll interval (asyncio.sleep(min(remaining, 0.005))) keeps event loop responsive while maintaining <1ms drift"
|
||||||
|
- "pause_offset accumulation for correct pause/resume without event skipping or double-firing"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- src/led_sync/scheduler.py
|
||||||
|
- tests/test_scheduler.py
|
||||||
|
modified: []
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Absolute monotonic timestamps (D-07): fire_at recalculated fresh each 5ms poll iteration — mathematical guarantee of <1ms drift regardless of duration"
|
||||||
|
- "pause_offset accumulation (D-09): resume() adds elapsed pause time to offset, keeping all future fire_at calculations correct"
|
||||||
|
- "Scheduler does not inject v:1 — that is transport's responsibility, scheduler sends {zone, animation, params} only"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "ChoreographyScheduler._run(): asyncio.create_task() wrapping loop with 5ms poll interval"
|
||||||
|
- "seek() implemented as play() restart with seek_seconds — simpler than maintaining event index pointer"
|
||||||
|
|
||||||
|
requirements-completed:
|
||||||
|
- CHR-05
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 2min
|
||||||
|
completed: 2026-04-03
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 02 Plan 04: ChoreographyScheduler Summary
|
||||||
|
|
||||||
|
**asyncio absolute monotonic scheduler dispatching LED animation commands at precise timestamps — drift < 20ms guaranteed by D-07/D-08/D-09 algorithm**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** 2 min
|
||||||
|
- **Started:** 2026-04-03T12:49:30Z
|
||||||
|
- **Completed:** 2026-04-03T12:51:01Z
|
||||||
|
- **Tasks:** 1 (TDD: 2 commits — test + implementation)
|
||||||
|
- **Files modified:** 2
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- ChoreographyScheduler with absolute monotonic clock dispatch — never accumulates relative delays
|
||||||
|
- pause/resume pair using pause_offset accumulation (D-09) — no event skipping or double-firing on resume
|
||||||
|
- seek() via play() restart with seek_seconds — correctly skips events before seek position
|
||||||
|
- 9 pytest-asyncio tests all passing — timing, payload format, pause/resume, seek, stop, on_event callback, position tracking
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
Each task committed atomically (TDD pattern):
|
||||||
|
|
||||||
|
1. **RED — failing tests** - `a6ba523` (test)
|
||||||
|
2. **GREEN — ChoreographyScheduler implementation** - `ea59bb0` (feat)
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `src/led_sync/scheduler.py` — ChoreographyScheduler: play(), pause(), resume(), seek(), stop(), _run(), _dispatch()
|
||||||
|
- `tests/test_scheduler.py` — 9 asyncio tests covering all scheduler behaviors
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- Absolute monotonic timestamps (D-07): `fire_at = start_time + pause_offset + event.timestamp` recalculated fresh each poll iteration — mathematical guarantee of <1ms drift per event over any duration
|
||||||
|
- pause_offset accumulation (D-09): `resume()` adds elapsed pause time so all future `fire_at` values stay correct
|
||||||
|
- seek() implemented as `play()` restart — simpler than maintaining an internal event index pointer, avoids state management complexity
|
||||||
|
- Scheduler sends `{zone, animation, params}` only — `v:1` is transport's responsibility (confirmed by test asserting `"v" not in cmd`)
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
None - plan executed exactly as written.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None — implementation matched plan specification precisely. All 9 tests pass on first run.
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
|
||||||
|
None - no external service configuration required.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
- CHR-05 fulfilled: `ChoreographyScheduler` dispatches animation commands at absolute monotonic timestamps
|
||||||
|
- Ready for Phase 02-05 (beat detection) which will call `scheduler.on_event` via the callback hook
|
||||||
|
- Ready for Phase 03 TUI integration — scheduler is asyncio-native, Textual will wrap `play()`/`pause()`/`resume()`/`seek()` directly
|
||||||
|
- `current_position` property enables real-time timeline display in the TUI
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 02-app-core-audio*
|
||||||
|
*Completed: 2026-04-03*
|
||||||
Reference in New Issue
Block a user