- Date Created: 2025-12-15
- Last Modified: 2025-12-15
Migration Plan: Legacy → Unified Device Response Protocol(v1.18.0-v2.0.0)¶
Task Description¶
Currently, Kurikintons maintains two parallel implementations controlled by the ENABLE_DEVICE_RESPONSE flag:
- Legacy path:
main.cpp(ENABLE_DEVICE_RESPONSE=0) - SSV/TSV/CSV/JSONL output formats - Next path:
main_next.cpp(ENABLE_DEVICE_RESPONSE=1) - Unified JSONL device_response_t schema
Both are built into separate environments:
- esp32dev-release, esp32dev-dev: Legacy (exclude main_next, device_response modules)
- esp32dev-next: Unified (exclude main, serial_protocol, stream_formatter modules)
Goal: Consolidate to unified protocol, removing legacy code by v2.0.0.
Current Dual-Path Structure¶
Legacy Path (main.cpp):
- 234 lines, procedural main loop
- serial_protocol.cpp/h - Binary/text command dispatcher
- text_command.cpp/h, text_command_manager.cpp/h - Command parsing
- stream_formatter.cpp/h - Multiple output format handlers (SSV/TSV/CSV/JSONL)
- stream_data.h - event_t structure
- response.cpp/h, runtime_config.cpp/h - Response types and configuration
- detection_buffer.cpp/h - Ring buffer for events
- cosmic_detector.cpp/h - GPIO detection engine (procedural C API)
Next Path (main_next.cpp):
- 114 lines, cleaner main loop
- device_response.cpp/h - Unified JSONL schema and serialization
- device_response_builder.cpp/h - Response construction helpers
- device_response_send.cpp/h - Transmission layer
- command_queue.cpp/h - FreeRTOS-based command buffering (10-item)
- event_queue.cpp/h - FreeRTOS-based event buffering (200-item)
- command.cpp/h - Command singleton configuration management
- detector.cpp/h - Class-based detection engine (modern C++ API)
- detection_processor.cpp/h - Sensor integration and event creation
Problem with Dual-Path Approach¶
| Issue | Impact |
|---|---|
| Code duplication | 47 source files + 28 headers (many similar) |
| Maintenance burden | Same fix must be applied to two paths |
| Test complexity | Every feature tested twice |
| Confusion for users | Which path to use? Different output formats |
| Config explosion | ENABLE_DEVICE_RESPONSE + STREAM_FORMAT + other flags |
| Build time | Both implementations compiled (unused code in final binary) |
| Flash size | Extra code for unused path (~10KB overhead) |
Why Unify to Next Path?¶
- Cleaner architecture: FreeRTOS queues vs ring buffers, C++ singleton vs procedural
- Unified schema: JSON standard vs custom SSV format
- Better debugging: device_response_t includes type/status/error_code
- Future-proof: Phase 6 Payload Pointer Pattern enables extensibility without struct changes
- Reduced complexity: Single main loop, single response protocol
- Microsecond timestamps: v1.17.0 improvement available in next path
Outcome¶
Phase 1: Stabilization & Feature Parity(v1.18.0)¶
Verification tasks:
- Core functionality identical between both builds:
- Detection accuracy: Compare 24-hour event counts (expected 99.9%+ match)
- Sensor readings: Verify BME280/ADC values match exactly
- Timestamps: Check uptime/timedelta/detected_at precision
-
Commands: All 17 handlers work identically
-
Document feature parity (FEATURES_PARITY.md):
- Detection: ✅ Identical
- ADC: ✅ Identical
- Sensors (BME280): ✅ Identical
- RTC: ✅ Identical
- GNSS: ✅ Identical
- Commands (17): ✅ Identical
- Event output: Different (SSV/CSV vs JSONL)
- Response schema: Different (untyped vs unified)
-
FreeRTOS usage: Different (Ring buffer vs FreeRTOS Queue)
-
Measure performance:
- Flash: Legacy ~330KB, Next ~340KB (acceptable +10KB)
- RAM: Legacy ~30%, Next ~31% (acceptable +1%)
-
24-hour stability: No memory leaks in either path
-
Create migration guide (MIGRATION_USER_GUIDE.md):
- For serial monitor users: Explain SSV vs JSONL format
- For data pipelines: How to update parsers
- For developers: Which path to use (and when)
Phase 2: Promote Next as Primary(v1.19.0)¶
Reorganize build environments:
# Old naming
[env:esp32dev-next] → [env:esp32dev] (new primary)
[env:esp32dev-dev] → [env:esp32dev-legacy] (deprecated)
[env:esp32dev-release] → Uses unified protocol
# Build by default
task build # Uses esp32dev-next (new)
task build:legacy # Uses esp32dev-legacy (old, deprecated)
Update documentation:
- README: Default is now unified protocol
- CLAUDE.md: Migration timeline, deprecation notice
- getting-started.md: Recommend task build for unified protocol
Phase 3: Remove Legacy Code(v2.0.0)¶
Delete these files:
Source files (src/): - main.cpp (replace with main_next.cpp → main.cpp) - serial_protocol.cpp/h - stream_data.cpp/h - stream_formatter.cpp/h - text_command.cpp/h, text_command_manager.cpp/h - binary_command.cpp/h (no longer needed) - response.cpp/h - runtime_config.cpp/h - detection_buffer.cpp/h - cosmic_detector.cpp/h (replace with detector.cpp)
Headers (include/): - response.h, stream_data.h, stream_formatter.h - text_command.h, text_command_manager.h, binary_command.h - runtime_config.h, detection_buffer.h
Build config: - Remove ENABLE_DEVICE_RESPONSE flag (becomes mandatory) - Remove STREAM_FORMAT flag (always JSONL) - Simplify platformio.ini (no build_src_filter needed)
Benefits: - -10KB Flash (330KB → 320KB) - -9 source files (47 → 38) - -8 headers (28 → 20) - Maintenance burden reduced (1 path instead of 2) - Unified codebase easier to enhance
Implementation Timeline¶
| Phase | Version | Timeline | Deliverable |
|---|---|---|---|
| 1: Verify | v1.18.0 | 1-2 weeks | FEATURES_PARITY.md, MIGRATION_USER_GUIDE.md |
| 2: Promote | v1.19.0 | 1 week | Reorganized builds, updated docs |
| 3: Consolidate | v2.0.0 | 1 week | Legacy code removed, unified codebase |
Learnings¶
Dual-Path Overhead is Significant¶
- 9 parallel source files (mostly similar logic)
- 8 parallel headers
- ~10KB Flash overhead
- Maintenance effort ~2x (every fix applied twice)
- Build time ~1.3x (both paths compiled)
Next Path is Production-Ready¶
- All 17 commands working
- Microsecond timestamp precision (v1.17.0)
- FreeRTOS queues proven stable
- Device_response_t schema mature (Phase 6 complete)
- No known regressions vs legacy path
Build System Complexity¶
Current system uses build_src_filter to exclude files:
- Legacy builds exclude: main_next, device_response, command_queue, event_queue, etc.
- Next builds exclude: main, serial_protocol, stream_formatter, text_command, detection_buffer, etc.
This works but requires careful management. Single-path approach would be cleaner.
Migration Path Clear¶
- Stabilize next (v1.18.0)
- Make it default (v1.19.0)
- Remove legacy (v2.0.0)
Users get 6+ months notice before forced migration.
Next Steps¶
Immediate (v1.18.0)¶
- Create verification test plan:
- Detection accuracy test (24-hour comparison)
- Command response validation (all 17 handlers)
- Memory profiling (24-hour uptime)
-
Timestamp precision measurement
-
Document feature parity:
- Create
docs/FEATURES_PARITY.md - Create
docs/MIGRATION_USER_GUIDE.md -
Update
CLAUDE.mdwith migration section -
Commit migration plan:
- This progress log (design documentation)
- Feature parity checklist
- User migration guide
Short-term (v1.19.0)¶
- Rename environments in platformio.ini
esp32dev-next→esp32dev(primary)-
Create
esp32dev-legacyalias -
Update Taskfile for new names
task builduses esp32dev (next)-
task build:legacyuses esp32dev-legacy -
Update user documentation
- getting-started.md recommends unified protocol
- Add deprecation notice for legacy path
Long-term (v2.0.0)¶
- Delete legacy files (main.cpp, serial_protocol, stream_formatter, etc.)
- Rename main_next.cpp → main.cpp
- Remove ENABLE_DEVICE_RESPONSE flag (always on)
- Remove STREAM_FORMAT flag (always JSONL)
- Simplify platformio.ini
- Run regression tests
- Verify flash/RAM savings
Decision Points for User Input¶
- Priority timing: Can we dedicate v1.18.0 to stabilization, or should we continue adding features?
- Testing scope: Do we need live detector hardware testing in Phase 1, or is bench testing sufficient?
- User communication: When to announce deprecation (v1.18.0 or v1.19.0)?
- Long-term support: Maintain legacy branch for archived projects after v2.0.0?