|
| 1 | +# Milestone v2.0: Tag-Based Domain Model |
| 2 | + |
| 3 | +**Status:** ✅ SHIPPED 2026-04-17 |
| 4 | +**Phases:** 1004-1011 (8 phases) |
| 5 | +**Total Plans:** 27 |
| 6 | +**Commits:** 119 |
| 7 | +**Files Changed:** 224 (13,799 insertions, 10,747 deletions — net +3,052 lines) |
| 8 | +**Timeline:** 2026-04-16 → 2026-04-17 |
| 9 | + |
| 10 | +## Overview |
| 11 | + |
| 12 | +Reboot the SensorThreshold subsystem on a unified `Tag` foundation. Everything is a Tag — `Sensor`/`Threshold`/`StateChannel`/`CompositeThreshold` rewritten as Tag subclasses (`SensorTag`, `StateTag`, `MonitorTag`, `CompositeTag`). New primitives deliver derived time-series health signals. Events bind to tags and overlay in FastSense. Strangler-fig sequencing: parallel hierarchy phases 1004-1008, consumer migration phase 1009, event binding phase 1010, legacy deletion phase 1011. |
| 13 | + |
| 14 | +## Key Accomplishments |
| 15 | + |
| 16 | +1. **Unified Tag foundation** — `Tag` abstract base class with `TagRegistry` singleton, two-phase JSON deserializer, label/metadata/criticality support. Proven Octave-safe throw-from-base pattern. |
| 17 | +2. **MonitorTag derived signals** — Lazy-by-default 0/1 binary time series from any parent Tag + condition function. Debounce (ISA-18.2 MinDuration), hysteresis (alarm-on/alarm-off), streaming `appendData` for live pipelines, opt-in disk persistence. |
| 18 | +3. **CompositeTag aggregation** — 7 modes (AND/OR/MAJORITY/COUNT/WORST/SEVERITY/USER_FN) via vectorized merge-sort streaming. Key-equality cycle detection. 0.125x output-size ratio at 8×100k children (Pitfall 3 gate). |
| 19 | +4. **Event↔Tag binding** — Many-to-many `EventBinding` registry replacing denormalized carrier strings. `Event.TagKeys` cell. `Tag.addManualEvent` convenience. FastSense `renderEventLayer_` draws toggleable round markers. |
| 20 | +5. **Full consumer migration** — Every widget (FastSenseWidget, MultiStatusWidget, IconCardWidget, EventTimelineWidget, SensorDetailPlot) + EventDetector + LiveEventPipeline migrated to Tag API. 0.3% tick overhead. |
| 21 | +6. **Clean legacy deletion** — 8 legacy classes + 13 private helpers + 37 legacy-only test files deleted. Golden integration test rewritten to Tag API with preserved assertion semantics. Net -3,995 lines in libs/. |
| 22 | + |
| 23 | +## Phases |
| 24 | + |
| 25 | +### Phase 1004: Tag Foundation + Golden Test |
| 26 | +**Goal**: Establish parallel Tag hierarchy and untouchable regression guard. |
| 27 | +**Plans**: 3 (Tag base + MockTag, TagRegistry + two-phase loader, Golden test + budget verification) |
| 28 | +**Key deliverables**: Tag.m (6 abstract-by-convention stubs), TagRegistry.m (CRUD + query + loadFromStructs), TestGoldenIntegration.m |
| 29 | +**Completed**: 2026-04-16 |
| 30 | + |
| 31 | +### Phase 1005: SensorTag + StateTag (data carriers) |
| 32 | +**Goal**: Port raw-data half of domain into Tag subclasses with polymorphic FastSense.addTag. |
| 33 | +**Plans**: 3 (SensorTag composition wrapper, StateTag ZOH valueAt, FastSense.addTag dispatcher) |
| 34 | +**Key deliverables**: SensorTag.m (HAS-A Sensor delegate), StateTag.m (ZOH), FastSense.addTag (getKind dispatch) |
| 35 | +**Completed**: 2026-04-16 |
| 36 | + |
| 37 | +### Phase 1006: MonitorTag (lazy, in-memory) |
| 38 | +**Goal**: First-class derived signal replacing Sensor.resolve() side-effect pipeline. |
| 39 | +**Plans**: 3 (Core lazy memoize + observer hook, Debounce + hysteresis + events, Integration + bench) |
| 40 | +**Key deliverables**: MonitorTag.m (500 SLOC), SensorTag/StateTag listener hooks, bench: 3.3x faster than legacy |
| 41 | +**Completed**: 2026-04-16 |
| 42 | + |
| 43 | +### Phase 1007: MonitorTag streaming + persistence |
| 44 | +**Goal**: Opt-in performance/persistence levers for live pipelines. |
| 45 | +**Plans**: 3 (appendData streaming, Persist + FastSenseDataStore monitors API, Bench) |
| 46 | +**Key deliverables**: appendData with boundary-state continuity, storeMonitor/loadMonitor/clearMonitor, bench: 11.1x speedup |
| 47 | +**Completed**: 2026-04-16 |
| 48 | + |
| 49 | +### Phase 1008: CompositeTag |
| 50 | +**Goal**: Aggregate MonitorTags/CompositeTags via merge-sort streaming with 7 aggregation modes. |
| 51 | +**Plans**: 3 (Core + addChild + cycle DFS, Merge-sort + ALIGN + 3-deep round-trip, Integration + bench) |
| 52 | +**Key deliverables**: CompositeTag.m (vectorized sort merge), valueAt fast-path, bench: 53ms at 8×100k |
| 53 | +**Completed**: 2026-04-16 |
| 54 | + |
| 55 | +### Phase 1009: Consumer migration (one widget at a time) |
| 56 | +**Goal**: Migrate every consumer to Tag API — one widget per commit, green CI each. |
| 57 | +**Plans**: 4 (FastSense layer, Dashboard widgets, EventDetection + LEP appendData wire-up, Bench) |
| 58 | +**Key deliverables**: 19 production files cleaned, LiveEventPipeline MonitorTargets, bench: 0.3% overhead |
| 59 | +**Completed**: 2026-04-17 |
| 60 | + |
| 61 | +### Phase 1010: Event ↔ Tag binding + FastSense overlay |
| 62 | +**Goal**: Replace denormalized Event carriers with EventBinding registry; render event markers. |
| 63 | +**Plans**: 3 (Event.TagKeys + EventBinding, Tag.addManualEvent + renderEventLayer_, Bench) |
| 64 | +**Key deliverables**: EventBinding.m singleton, Event.TagKeys/Severity/Category, FastSense renderEventLayer_ |
| 65 | +**Completed**: 2026-04-17 |
| 66 | + |
| 67 | +### Phase 1011: Cleanup — collapse parallel hierarchy + delete legacy |
| 68 | +**Goal**: Delete 8 legacy classes, rewrite golden test, ship unified Tag-only domain. |
| 69 | +**Plans**: 5 (SensorTag inline + delete classes, Delete tests, Consumer branch removal, Example migration, Golden rewrite + grep audit) |
| 70 | +**Key deliverables**: 8 classes deleted, 37 test files deleted, golden rewritten, net -3,995 lines in libs/ |
| 71 | +**Completed**: 2026-04-17 |
| 72 | + |
| 73 | +## Milestone Summary |
| 74 | + |
| 75 | +### Key Decisions |
| 76 | +- Strangler-fig sequencing (parallel hierarchy → consumer migration → deletion) |
| 77 | +- Composition over inheritance for SensorTag (HAS-A Sensor delegate, later inlined) |
| 78 | +- Lazy-by-default MonitorTag (Pitfall 2 discipline) |
| 79 | +- Key-equality cycle detection (Octave SIGILL avoidance) |
| 80 | +- Vectorized sort-based merge for CompositeTag (pointer-loop too slow) |
| 81 | +- Event.TagKeys via EventBinding registry (no handle cross-references) |
| 82 | +- Separate renderEventLayer_ (no render-path pollution) |
| 83 | + |
| 84 | +### Tech Debt (from audit) |
| 85 | +1. EventDetector.detect(tag, threshold) references deleted Threshold API — dead code |
| 86 | +2. DashboardSerializer .m export doesn't handle source.type='tag' — JSON works |
| 87 | +3. 93 MATLAB-only test refs to deleted Threshold class in 42 suite files |
| 88 | + |
| 89 | +### Performance Gates (all passed) |
| 90 | +- Pitfall 3: CompositeTag 0.125x output ratio, 53ms compute |
| 91 | +- Pitfall 9 (Phase 1006): MonitorTag 3.3x faster than legacy Sensor.resolve |
| 92 | +- Pitfall 9 (Phase 1007): appendData 11.1x speedup vs full recompute |
| 93 | +- Pitfall 9 (Phase 1009): Consumer migration 0.3% tick overhead |
0 commit comments