feat: support nesting for composite color strip sources
Lint & Test / test (push) Successful in 2m17s

Allow composite sources to reference other composite/mapped sources as
layers. Adds cycle detection (via transitive dependency graph walk),
depth limiting (MAX_COMPOSITE_DEPTH=4), and a runtime safety net in the
stream manager. Frontend layer dropdown now shows all source types
except the source being edited.

17 new tests covering cycles, depth limits, and valid nesting — all
715 tests passing.
This commit is contained in:
2026-04-12 20:41:15 +03:00
parent 4940007e54
commit cc9900d801
7 changed files with 348 additions and 30 deletions
+27 -21
View File
@@ -1,28 +1,34 @@
# Group Device Type Implementation
# Composite Nesting Support
## Phase 1: Storage Layer
- [x] Add `group_device_ids`, `group_mode` fields to Device model
- [x] Add cycle detection + led_count resolution + group reference helpers to DeviceStore
## Phase 1: Store — Cycle & Depth Validation
## Phase 2: API Schemas
- [x] Add group fields to DeviceCreate, DeviceUpdate, DeviceResponse
- [x] Add `get_transitive_dependencies()` to ColorStripStore
- [x] Add `validate_nesting()` with cycle detection + depth limit (MAX_DEPTH=4)
## Phase 3: GroupLEDClient + Provider
- [x] Create `group_client.py` — GroupLEDClient (sequence slice / independent resample)
- [x] Create `group_provider.py` — GroupDeviceProvider
- [x] Register group provider in `led_client.py`
## Phase 2: API — Validation in Create/Update
## Phase 4: Routes + Processing Pipeline
- [x] Update device routes — group-specific create/update logic, delete protection, cycle validation
- [x] Add group fields to DeviceInfo + _DEVICE_FIELD_DEFAULTS
- [x] Pass group context (device_store, group fields) to create_led_client
- [x] Call `validate_nesting()` in create handler for composite sources
- [x] Call `validate_nesting()` in update handler for composite sources
## Phase 3: Runtime — Depth Guard in Stream
- [x] Add `depth` parameter to CompositeColorStripStream
- [x] Pass depth through ColorStripStreamManager.acquire()
- [x] Cap depth at runtime to prevent runaway nesting
## Phase 4: Frontend — Allow Composites in Layer Dropdown
- [x] Remove `source_type !== 'composite'` filter (keep self-exclusion)
- [x] Update docstring in composite_stream.py
## Phase 5: Tests
- [x] Unit tests for cycle detection, led_count resolution, GroupLEDClient (20 tests, all passing)
## Phase 6: Frontend
- [x] Group device UI (child picker, mode selector, hide URL for groups)
- [x] i18n keys (en, ru, zh)
- [x] TypeScript types + API helper
- [x] Icon (layers) for group device type
- [x] CSS for group child rows
- [x] Cycle detection tests (A→B→A, self-reference)
- [x] Depth limit tests (chain exceeding MAX_DEPTH)
- [x] Valid nesting tests (A→B both composite, no cycle)
## Phase 6: Lint & Build
- [x] Ruff check passes
- [x] TypeScript build passes
- [x] All existing tests pass (715/715)