Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
__pycache__/
wiki-gen-summary.md
.claude/worktrees/
.planning/
# .planning/ and docs/superpowers/ are tracked GSD planning docs (un-ignored intentionally).
.superpowers/
docs/superpowers/
.matlab-tests-passed
151 changes: 151 additions & 0 deletions .planning/MILESTONES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Milestones

## v2.0 Tag-Based Domain Model (Shipped: 2026-04-17)

**Phases completed:** 10 phases, 37 plans, 26 tasks

**Key accomplishments:**

- FastSenseWidget.refresh() now reuses FastSenseObj via updateData() on sensor-identity match, and getTimeRange() returns O(1) cached CachedXMin/CachedXMax instead of full array scan
- Non-active pages now defer widget realization until first switchPage, with batched drawnow-interleaved realization reducing multi-page startup time proportional to page count.
- One-liner:
- One-liner:
- 1. [Rule 1 - Bug] GaugeWidget.deriveRange had no early return after allVals calculation
- IncrementalEventDetector, LiveEventPipeline, and EventViewer fully migrated from ThresholdRules/addThresholdRule to Thresholds/addThreshold, with zero ThresholdRules references remaining in EventDetection production code
- One-liner:
- Total calls migrated:
- 1. [Rule 2 - Missing Critical Functionality] ThresholdRegistry.clear() added
- Standalone Threshold binding via Threshold property on IconCardWidget, isstruct dispatch in MultiStatusWidget, and per-chip threshold fields in ChipBarWidget resolveChipColor
- One-liner:
- One-liner:
- One-liner:
- One-liner:
- One-liner:
- One-liner:
- One-liner:
- One-liner:
- One-liner:
- One-liner:
- Octave smoke harness + recursive auto-default run_all_examples.m, both with byte-identical skip-list blocks and TagRegistry/EventBinding singleton-cleanup discipline — deterministic per-folder verification gate that every Wave 2 migration plan consumes
- No-op audit pass.
- No-op audit pass.
- InfoColor added to DashboardTheme and IconCardWidget implemented with state-colored circle icon, numeric value display, and three-path data binding
- 1. [Rule 1 - Bug] Fixed testChipColorUpdate using containers.Map for mutable closure
- Sparkline rendering:
- 1. [Rule 3 - Blocking] Wave 1 widget files not yet in worktree
- One-liner:
- One-liner:

---

## v1.0 Dashboard Performance Optimization (Shipped: 2026-04-04)

**Phases completed:** 1 phases, 3 plans, 2 tasks

**Key accomplishments:**

- One-liner:
- Task 1: Consolidated onLiveTick with updateLiveTimeRangeFrom

---

## v1.0 Dashboard Engine Code Review Fixes (Shipped: 2026-04-03)

**Phases completed:** 1 phases, 4 plans, 2 tasks

**Key accomplishments:**

- Four correctness bugs patched in DashboardEngine: multi-page removeWidget, resize reflow, sensor listener parity, and dead removeDetached parameter removed
- One-liner:
- One-liner:

---

## v1.0 FastSense Advanced Dashboard (Shipped: 2026-04-03)

**Phases completed:** 9 phases, 24 plans, 21 tasks

**Key accomplishments:**

- One-liner:
- One-liner:
- DashboardSerializer.save() now correctly emits constructor calls and addChild() for all GroupWidget children in panel, collapsible, and tabbed modes, making .m round-trips reliable for any dashboard using groups
- testTimerContinuesAfterError rewritten to trigger ErrorFcn indirectly via a throwing TimerFcn, giving INFRA-01 runnable automated coverage without calling any private method
- 1. [Pre-existing] TestGroupWidget/testFullDashboardIntegration
- One-liner:
- One-liner:
- One-liner:
- DashboardPage handle class with Name/Widgets/addWidget/toStruct, DashboardEngine.addPage() routing, and 8-method TestDashboardMultiPage scaffold with 3 tests green immediately
- DashboardEngine extended with Pages/ActivePage properties, visible PageBar with themed buttons for multi-page dashboards, switchPage() navigation, and activePageWidgets() scoping for all widget iteration methods
- One-liner:
- testSaveLoadRoundTrip now asserts that ActivePage index 2 is preserved through JSON save/load, closing the LAYOUT-05 coverage gap for DashboardEngine.m lines 1063-1070
- 1. [Rule 1 - Bug] Sensor constructor positional argument
- DetachCallback property + addDetachButton() added to DashboardLayout, injecting a '^' button at [0.82 0.90 0.08 0.08] in every widget panel when callback is wired — DETACH-01 satisfied
- DashboardEngine gains DetachedMirrors registry + detachWidget/removeDetached methods + onLiveTick mirror loop, completing all 7 DETACH tests (DETACH-01 through DETACH-07)
- Multi-page JSON save/load round-trip tests covering SERIAL-01, SERIAL-04, SERIAL-05 with a bug fix for single-named-page save routing to widgetsPagesToConfig
- Multi-page .m export fixed to emit a proper MATLAB function + switchPage routing; 5 new round-trip tests covering SERIAL-02 and SERIAL-03 all pass
- One-liner:
- One-liner:
- One-liner:
- One-liner:
- One-liner:
- One-liner:

---

## v1.0 Advanced Dashboard (Shipped: 2026-04-03)

**Phases completed:** 8 phases, 22 plans, 21 tasks

**Key accomplishments:**

- One-liner:
- One-liner:
- DashboardSerializer.save() now correctly emits constructor calls and addChild() for all GroupWidget children in panel, collapsible, and tabbed modes, making .m round-trips reliable for any dashboard using groups
- testTimerContinuesAfterError rewritten to trigger ErrorFcn indirectly via a throwing TimerFcn, giving INFRA-01 runnable automated coverage without calling any private method
- 1. [Pre-existing] TestGroupWidget/testFullDashboardIntegration
- One-liner:
- One-liner:
- One-liner:
- DashboardPage handle class with Name/Widgets/addWidget/toStruct, DashboardEngine.addPage() routing, and 8-method TestDashboardMultiPage scaffold with 3 tests green immediately
- DashboardEngine extended with Pages/ActivePage properties, visible PageBar with themed buttons for multi-page dashboards, switchPage() navigation, and activePageWidgets() scoping for all widget iteration methods
- One-liner:
- testSaveLoadRoundTrip now asserts that ActivePage index 2 is preserved through JSON save/load, closing the LAYOUT-05 coverage gap for DashboardEngine.m lines 1063-1070
- 1. [Rule 1 - Bug] Sensor constructor positional argument
- DetachCallback property + addDetachButton() added to DashboardLayout, injecting a '^' button at [0.82 0.90 0.08 0.08] in every widget panel when callback is wired — DETACH-01 satisfied
- DashboardEngine gains DetachedMirrors registry + detachWidget/removeDetached methods + onLiveTick mirror loop, completing all 7 DETACH tests (DETACH-01 through DETACH-07)
- Multi-page JSON save/load round-trip tests covering SERIAL-01, SERIAL-04, SERIAL-05 with a bug fix for single-named-page save routing to widgetsPagesToConfig
- Multi-page .m export fixed to emit a proper MATLAB function + switchPage routing; 5 new round-trip tests covering SERIAL-02 and SERIAL-03 all pass
- One-liner:
- One-liner:
- One-liner:
- One-liner:

---

## v1.0 Advanced Dashboard (Shipped: 2026-04-03)

**Phases completed:** 7 phases, 19 plans, 21 tasks

**Key accomplishments:**

- One-liner:
- One-liner:
- DashboardSerializer.save() now correctly emits constructor calls and addChild() for all GroupWidget children in panel, collapsible, and tabbed modes, making .m round-trips reliable for any dashboard using groups
- testTimerContinuesAfterError rewritten to trigger ErrorFcn indirectly via a throwing TimerFcn, giving INFRA-01 runnable automated coverage without calling any private method
- 1. [Pre-existing] TestGroupWidget/testFullDashboardIntegration
- One-liner:
- One-liner:
- One-liner:
- DashboardPage handle class with Name/Widgets/addWidget/toStruct, DashboardEngine.addPage() routing, and 8-method TestDashboardMultiPage scaffold with 3 tests green immediately
- DashboardEngine extended with Pages/ActivePage properties, visible PageBar with themed buttons for multi-page dashboards, switchPage() navigation, and activePageWidgets() scoping for all widget iteration methods
- One-liner:
- testSaveLoadRoundTrip now asserts that ActivePage index 2 is preserved through JSON save/load, closing the LAYOUT-05 coverage gap for DashboardEngine.m lines 1063-1070
- 1. [Rule 1 - Bug] Sensor constructor positional argument
- DetachCallback property + addDetachButton() added to DashboardLayout, injecting a '^' button at [0.82 0.90 0.08 0.08] in every widget panel when callback is wired — DETACH-01 satisfied
- DashboardEngine gains DetachedMirrors registry + detachWidget/removeDetached methods + onLiveTick mirror loop, completing all 7 DETACH tests (DETACH-01 through DETACH-07)
- Multi-page JSON save/load round-trip tests covering SERIAL-01, SERIAL-04, SERIAL-05 with a bug fix for single-named-page save routing to widgetsPagesToConfig
- Multi-page .m export fixed to emit a proper MATLAB function + switchPage routing; 5 new round-trip tests covering SERIAL-02 and SERIAL-03 all pass
- One-liner:

---
149 changes: 149 additions & 0 deletions .planning/PROJECT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# FastSense Advanced Dashboard

## What This Is

A MATLAB sensor data dashboard engine with nested layout organization (tabs, collapsible groups, multi-page navigation), per-widget info tooltips with Markdown rendering, and detachable live-mirrored widgets that pop out as independent figure windows. Built for MATLAB engineers analyzing sensor data with threshold-based monitoring.

## Core Value

Users can organize complex dashboards into navigable sections and pop out any widget for detailed analysis without losing the dashboard context.

## Requirements

### Validated

- ✓ Widget-based dashboard composition with 20+ widget types — existing
- ✓ 24-column grid layout system — existing
- ✓ Dashboard serialization (JSON/.m export) — existing
- ✓ Theming (light/dark, custom colors) — existing
- ✓ Live data refresh via DashboardEngine timer — existing
- ✓ GroupWidget for basic widget grouping — existing
- ✓ DashboardToolbar with global controls — existing
- ✓ WebBridge for browser-based visualization — existing
- ✓ Timer error recovery (ErrorFcn + auto-restart) — v1.0
- ✓ GroupWidget .m export preserves children — v1.0
- ✓ Shared normalizeToCell helper for jsondecode normalization — v1.0
- ✓ Collapsible sections with grid reflow — v1.0
- ✓ Tab persistence through save/load — v1.0
- ✓ Widget info tooltips with Markdown rendering — v1.0
- ✓ Multi-page dashboards with PageBar navigation — v1.0
- ✓ Active page persistence through save/load — v1.0
- ✓ Detachable live-mirrored widgets — v1.0
- ✓ Independent time axis zoom on detached FastSenseWidget — v1.0
- ✓ Multi-page JSON and .m round-trip serialization — v1.0
- ✓ Collapsed state persistence — v1.0
- ✓ Legacy JSON backward compatibility — v1.0
- ✓ DividerWidget for visual dashboard section separation — v1.0 Phase 8
- ✓ addCollapsible convenience method on DashboardEngine — v1.0 Phase 8
- ✓ Configurable Y-axis limits (YLimits) on FastSenseWidget — v1.0 Phase 8
- ✓ Threshold mini-labels (ShowThresholdLabels) on FastSense and FastSenseWidget — v1.0 Phase 9

### Active

- ✓ Dashboard performance optimization: theme caching, O(1) widget dispatch, single-pass live tick, in-place resize, visibility page switch — v1.0 Performance
- ✓ Tag-based domain model: unified `Tag` foundation, `TagRegistry`, `MonitorTag` derived time-series, `CompositeTag` aggregation — v2.0
- ✓ Events attached to tags with FastSense overlay rendering — v2.0

## Current State

**Shipped:** v2.0 Tag-Based Domain Model (2026-04-17)

The SensorThreshold subsystem has been fully rebooted on a unified `Tag` foundation. Legacy `Sensor`/`Threshold`/`StateChannel`/`CompositeThreshold` classes are deleted. All consumers (FastSenseWidget, dashboard widgets, EventDetection, LiveEventPipeline) operate through the Tag API (`addTag`, `getXY`, `valueAt`). Events bind to tags via `EventBinding` registry and render as toggleable round markers in FastSense. All `examples/` scripts have been migrated to the Tag API and a dedicated 5-script showcase lives under `examples/02-sensors/tags/`.

**Vocabulary:** `SensorTag`, `StateTag`, `MonitorTag`, `CompositeTag`, `TagRegistry`, `EventBinding`. FastSense API: `addTag(t)`.

## Current Milestone: v2.1 Tag-API Tech Debt Cleanup

**Goal:** Close the 4 non-blocking tech debt items surfaced by the v2.0 milestone audit so the Tag-API codebase is free of dead code, test-skip gaps, and stubbed example demos.

**Target items (from `.planning/milestones/v2.0-MILESTONE-AUDIT.md`):**
- Stub or delete `EventDetector.detect(tag, threshold)` dead code referencing the deleted `Threshold` API
- Fix `DashboardSerializer` `.m` script export to handle `source.type='tag'` (currently silently omits Tag-bound widgets; JSON path works)
- Clean up 93 `Threshold(` constructor references across 42 MATLAB-only suite test files (fail on MATLAB, skip on Octave today)
- Rewrite `examples/05-events/example_event_detection_live.m` and `example_event_viewer_from_file.m` as fully-migrated `MonitorTag + EventStore + EventBinding` pipelines (remove deprecation stubs)

**Deferred to future milestones:**
- Asset hierarchy (Asset tree, templates, tag-to-asset binding, browse rollups)
- Custom event GUI (click-drag region selection in FastSense → label dialog)
- Calc tags / formula evaluator for arbitrary derived tags
- Tri-state / continuous severity MonitorTag output
- WebBridge parity for Tag API features

### Out of Scope

- Drag-and-drop visual rearrangement — complexity vs. value for MATLAB-script-driven workflows
- Cross-filtering between widgets — would require a data binding framework
- Interactive controls (dropdowns, sliders) — DashboardEngine is visualization, not control panel
- Browser/WebBridge parity for new features — future milestone
- GroupWidget children individual detach buttons — v1 limitation, top-level only
- Time panel in multi-page mode — works on active page only (known limitation)

## Context

- FastSense is a MATLAB library for high-performance time series visualization with sensor/threshold modeling
- Dashboard engine (`libs/Dashboard/`) has DashboardEngine, DashboardWidget (20+ types), DashboardLayout (24-col grid), DashboardSerializer, DashboardTheme, DashboardBuilder, DashboardPage, DetachedMirror, MarkdownRenderer
- v1.0 shipped: 9 phases, 24 plans, 44 requirements, 2948 lines added across 24 files
- v1.0 code review shipped: 1 phase, 4 plans, 14 bug fixes across DashboardEngine, GroupWidget, DashboardSerializer, DashboardLayout, DashboardWidget, DashboardTheme, HeatmapWidget, BarChartWidget, HistogramWidget
- v1.0 performance shipped: 1 phase, 3 plans — theme caching (getCachedTheme), containers.Map dispatch, single-pass onLiveTick, repositionPanels for resize, visibility toggle for page switch
- New classes added: DashboardPage.m, DetachedMirror.m, DividerWidget.m
- Key patterns established: central injection via realizeWidget(), ReflowCallback for layout updates, DetachCallback for widget pop-out, normalizeToCell for jsondecode safety, markRealized/markUnrealized for lifecycle encapsulation, linesForWidget for shared serialization dispatch, getCachedTheme for theme struct caching, WidgetTypeMap_ for O(1) widget dispatch
- 24,473 LOC MATLAB across libs/ (as of v1.0 completion)
- Known tech debt: 5 items (INFO-03 Markdown downgrade, missing serialization tests, single-page save edge case) — code review tech debt resolved

## Constraints

- **Tech stack**: Pure MATLAB (no external dependencies) — consistent with existing codebase
- **Backward compatibility**: Existing dashboard scripts and serialized dashboards continue to work
- **Widget contract**: Features work through the existing DashboardWidget base class interface
- **Performance**: Detached live-mirrored widgets share the engine timer, no extra timers

## Key Decisions

| Decision | Rationale | Outcome |
|----------|-----------|---------|
| Extend GroupWidget for tabs/collapsible | GroupWidget already handles widget containment | ✓ Good |
| Info tooltip via widget header icon | Minimal UI footprint, consistent placement | ✓ Good |
| Live mirror via DashboardEngine timer | Reuse existing refresh infrastructure | ✓ Good |
| Central injection via realizeWidget() | Single choke-point for all 20+ widget types | ✓ Good |
| DetachedMirror as separate class (not DashboardWidget) | Avoids grid layout interference | ✓ Good |
| Clone via toStruct/fromStruct round-trip | Works for all widget types without per-type dispatch | ✓ Good |
| containers.Map for CloseRequestFcn reference | Solves closure-over-cell-array mutation limitation | ✓ Good |
| Plain text popup (HTML stripped) over javacomponent | Cross-platform safe (Octave compatible) | ✓ Good |
| DividerWidget via uipanel not axes | Simpler, no zoom/pan interaction, cheaper render | ✓ Good |
| addCollapsible on DashboardEngine (not DashboardBuilder) | DashboardEngine owns programmatic API | ✓ Good |
| YLimits=[] for auto, [min max] for fixed | Consistent with MATLAB ylim() convention | ✓ Good |
| ShowThresholdLabels opt-in (default false) | Backward compatible; labels only when explicitly requested | ✓ Good |
| Threshold label at right edge, repositions on zoom/pan | Always visible in view, doesn't move with data | ✓ Good |
| Realized property SetAccess=private with markRealized/markUnrealized | Enforces lifecycle contract, prevents external bypass | ✓ Good |
| linesForWidget shared static helper in DashboardSerializer | Eliminates exportScript/exportScriptPages drift | ✓ Good |
| wireListeners private helper in DashboardEngine | Single listener-wiring call for both page-routed and single-page paths | ✓ Good |
| getCachedTheme with preset invalidation | Eliminates 4 redundant DashboardTheme() calls per render/switch/tick | ✓ Good |
| containers.Map widget dispatch (WidgetTypeMap_) | O(1) type lookup replacing 17-case switch; kpi alias preserved | ✓ Good |
| repositionPanels for onResize | In-place panel repositioning vs destroy+recreate; fallback to rerenderWidgets on missing handles | ✓ Good |
| switchPage visibility toggle | Hide/show panels instead of full rerender; pre-allocate all page panels at render() | ✓ Good |
| Single-pass onLiveTick with updateLiveTimeRangeFrom | One activePageWidgets() call, merged mark-dirty+refresh loop | ✓ Good |
| v2.0 reboot under unified `Tag` root (Option 2) | No-users codebase; preserves design wins from 1001-1003 as concepts; cleanest end state vs. interface-shim approach (Option 3) | Pending v2.0 |
| Vocabulary: `Tag` suffix on all primitives (`SensorTag`, `MonitorTag`, ...) | Trendminer-faithful; uniform mental model; `addTag()` API replaces `addSensor()` | Pending v2.0 |
| Single `TagRegistry` (replaces `SensorRegistry` + `ThresholdRegistry`) | One namespace, one search surface; fewer parallel singletons | Pending v2.0 |
| MonitorTag as full time-series signal (not current-state only) | Plottable, persistable, event-detectable; reuses existing infrastructure | Pending v2.0 |
| Defer asset hierarchy (D), custom event GUI (F), calc tags (G) to later milestones | Ambitious tier (A+B+C+E) is shippable on its own; D/F/G are independent additions | Pending v2.0 |

## Evolution

This document evolves at phase transitions and milestone boundaries.

**After each phase transition** (via `/gsd:transition`):
1. Requirements invalidated? → Move to Out of Scope with reason
2. Requirements validated? → Move to Validated with phase reference
3. New requirements emerged? → Add to Active
4. Decisions to log? → Add to Key Decisions
5. "What This Is" still accurate? → Update if drifted

**After each milestone** (via `/gsd:complete-milestone`):
1. Full review of all sections
2. Core Value check — still the right priority?
3. Audit Out of Scope — reasons still valid?
4. Update Context with current state

---
*Last updated: 2026-04-22 — v2.1 milestone (Tag-API Tech Debt Cleanup) started*
Loading
Loading