Skip to content

Commit 59ac4e0

Browse files
HanSur94claude
andcommitted
Merge origin/main into Phase 1039 — reconcile with PR #171 (email send)
PR #171 (real SMTP send + pluggable mailer) merged first and overlapped Phase 1039. Resolution (slim #170 to its unique value): - libs/EventDetection/LiveEventPipeline.m: take main's (#171's) version wholesale. Drops #170's now-superseded 'NotificationService' constructor NV-pair and duplicate sensorDataForEvent_/runCycle sensorData fix; #171's processMonitorTag_ sensorData-return mechanism is canonical. - Demo + tests: inject NotificationService via the public property post-construction (no NV-pair). test_live_event_pipeline_notif_sensor_data now guards #171's sensorData behavior. Retained unique #170 work (untouched by #171): runBackgroundMonitoring headless entry, README_background_email, NotificationRule open-event guards, generateEventSnapshot fig-leak fix, demo, and runner/suite tests. Verified post-merge on MATLAB + Octave: sensorData 2/2, runner 5/5 (MATLAB) / 3/3+skip (Octave), TestBackgroundEmailMonitoring 9/9. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2 parents 0228c9c + fa45b1a commit 59ac4e0

31 files changed

Lines changed: 3064 additions & 159 deletions

.planning/ROADMAP.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -396,19 +396,21 @@ Plans:
396396
397397
### Phase 1039: Background monitoring with email notifications — COMPLETE 2026-05-29
398398

399-
**Goal:** Wire `NotificationService` into `LiveEventPipeline` as a first-class constructor NV-pair (default `[]`, replacing the auto-created dry-run instance); fix `runCycle` to pass real per-event sensor data to `notify()` so snapshots render correctly (was `struct()`); add headless entry point `runBackgroundMonitoring(setupFcn)` for `matlab -batch` use under launchd/systemd/cron; ship demo example + README with SMTP and service-supervision config; add tests for snapshot-data integrity and the runner entry.
399+
**Goal:** Add a headless entry point `runBackgroundMonitoring(setupFcn)` for `matlab -batch` use under launchd/systemd/cron; ship a demo example + README with SMTP and service-supervision config; harden the notification snapshot path (open-event guards + figure-leak fix); add tests for the runner entry and the live snapshot-data contract.
400400

401-
**Verification:** passed (7/7 must-haves; sensorData regression test green on Octave + MATLAB). Two human-verification items recorded (timer-driven live loop is MATLAB-only since Octave lacks `timer`; real-email/PNG-attachment smoke needs an SMTP relay). Bonus: fixed two latent library bugs en route — missing `monitor.EventStore` wiring in setup, and NaN-`EndTime` open-event crash in `NotificationRule.fillTemplate` / `generateEventSnapshot`.
401+
**Reconciliation note (2026-05-29):** Sibling PR #171 ("Background-monitoring email alerts: real SMTP send + pluggable external mailer") merged to main first and independently delivered the `notify(ev, struct())`real-sensorData fix (via `processMonitorTag_` returning `sensorData`) plus the `NotificationService` Transport/cooldown rework. On merge, Phase 1039's two overlapping pieces were **dropped as superseded**: the `LiveEventPipeline` `'NotificationService'` constructor NV-pair and the duplicate `sensorDataForEvent_`/`runCycle` sensorData fix. The phase's `test_live_event_pipeline_notif_sensor_data` was retained and now serves as a regression guard for #171's sensorData mechanism (demo/tests inject the service via the public property post-construction). Net unique contribution of #170: the headless runner, ops README, open-event/fig-leak robustness, and the demo + tests.
402402

403-
**Depends on:** Phase 1032 (`MonitorTag.emitEvent_` deferred-notify) — no dependency on cluster-mode work; auto-wiring works in single-user and cluster modes alike.
404-
**Requirements:** none — CONTEXT.md decisions (D-01..D-06) are the contract
405-
**Plans:** 4/4 plans complete
403+
**Verification:** passed. Post-#171-merge: `test_live_event_pipeline_notif_sensor_data` 2/2, `test_run_background_monitoring` 5/5 (MATLAB) / 3/3+skip (Octave), `TestBackgroundEmailMonitoring` 9/9 — all green on Octave + MATLAB. Timer-driven live loop is MATLAB-only (Octave lacks `timer`); real-email/PNG smoke needs an SMTP relay. Also fixed two latent library bugs en route — missing `monitor.EventStore` wiring in setup, and NaN-`EndTime` open-event crash in `NotificationRule.fillTemplate` / `generateEventSnapshot`.
404+
405+
**Depends on:** Phase 1032 (`MonitorTag.emitEvent_` deferred-notify). Lands on top of PR #171 (shares the `NotificationService`/`LiveEventPipeline` email path).
406+
**Requirements:** none — CONTEXT.md decisions (D-01..D-06) are the contract; D-01/D-03 superseded by #171.
407+
**Plans:** 4/4 plans complete (01's NV-pair/sensorData portion superseded by #171; runner/docs/tests retained)
406408

407409
Plans:
408-
- [x] 1039-01-PLAN.md — LiveEventPipeline NotificationService NV-pair + sensorDataForEvent_ helper + runCycle notify fix (Wave 1, no deps)
410+
- [x] 1039-01-PLAN.md — (sensorData fix + NV-pair superseded by #171; no net change retained from this plan)
409411
- [x] 1039-02-PLAN.md — new libs/EventDetection/runBackgroundMonitoring.m headless entry function (Wave 1, no deps)
410-
- [x] 1039-03-PLAN.md — examples/05-events/example_background_email_monitor.m + README_background_email.md (Wave 2, depends on 01 + 02)
411-
- [x] 1039-04-PLAN.md — tests/test_live_event_pipeline_notif_sensor_data.m + tests/test_run_background_monitoring.m + tests/CaptureNotificationService.m (Wave 2, depends on 01 + 02)
412+
- [x] 1039-03-PLAN.md — examples/05-events/example_background_email_monitor*.m + README_background_email.md + open-event/fig-leak hardening (Wave 2)
413+
- [x] 1039-04-PLAN.md — tests/test_live_event_pipeline_notif_sensor_data.m + tests/test_run_background_monitoring.m + tests/CaptureNotificationService.m + tests/suite/TestBackgroundEmailMonitoring.m (Wave 2)
412414

413415
## Backlog
414416

.planning/STATE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Phase: 1028 (tag-update-perf-mex-simd) — COMPLETE 2026-05-19 (this branch)
2828
Plan: 6 of 6 executed (with 03/04 deferred per Plan 02d data). Shipped plans: 01, 02, 02b, 02d, 05, 06.
2929
Milestone: v3.0 FastSense Companion — SHIPPED 2026-04-30; v4.0 Multi-User LAN Concurrency — shipping via PR #152 (parallel branch); v1.0 perf line tracks phase 1028 — now COMPLETE via PR #114.
3030
Status: Phase 1028 closed. WithIO `tickMin` reduced 4497 ms → 3603 ms (−19.9%) on Octave Linux x86_64 CI run 26089658442, almost entirely from Plan 02d's in-memory prior-state cache. Plan 06 ships per-tick fs-stat coalescing reducing 1600 → 1 syscalls/tick (−99.94% mechanism-level; wall-time +3.2% within variance on tmpfs CI). PR #114 carries the phase. Follow-up candidates for a future perf phase: in-memory propagation refactor; `containers.Map` → struct-array refactor; `.mat` save-side optimization. K2/K3/K4 deferred per data (target regions bucket as 0 ms post-cache).
31-
Last activity: 2026-05-26 - Completed quick task 260526-r9x: Add PerTag composer mode to FastSenseCompanion - spawn one DashboardEngine window per selected tag
31+
Last activity: 2026-05-29 - Completed 260529-fnt (via /gsd:fast): FunctionTransport adapter — reuse an external/company MATLAB mailer as a NotificationService Transport, no SMTP config
3232

3333
### Note on parallel v4.0 work (main branch state)
3434

@@ -93,6 +93,8 @@ Other main PRs (#138, #139, #141, #144, #145, #146) auto-merged without conflict
9393
| 260519-bs4 | Add Tag Status Table window to FastSenseCompanion — new `TagStatusTableWindow.m` (classical figure, not uifigure, per CONTEXT.md), opened via new **Tags ↗** button on companion top toolbar (col 3 in the post-merge 1×7 grid: Events / Live / Tags / Tile / Close all / spacer / gear). Detached-only window with 12-column `uitable`: Key, Name, Type, Criticality, Units, Latest, Status (smart per-type — Monitor→OK/ALARM, State→state label, others→—), Last updated (X(end) timestamp), Activity (Live/Inactive at 5-min threshold), Events (count from EventStore), Samples, Labels. All 18 demo tags listed (snapshot from `TagRegistry.find(@(t)true)`). Two parallel refresh paths: (a) push-on-write via existing `FastSenseCompanion.scanLiveTagUpdates_` → `markStatusTableDirty_(keys)` when companion is in Live mode, (b) window-owned `RefreshTimer_` (1s fixedSpacing, unique UUID name, BusyMode='drop', self-stop after 2 consecutive tick errors) so the table refreshes regardless of companion's IsLive — addresses user feedback that Activity/Last updated must stay correct when companion is idle. Pause/Resume polling toggle freezes both paths (markTagsDirty becomes a no-op while paused; header shows "Last refreshed: HH:MM:SS (paused)"). "Last refreshed" heartbeat label updates every tick. Filter chips mirror TagCatalogPane pattern: Type (Sensor/Monitor/Composite/State/Derived), Criticality (Low/Medium/High/Safety), Activity (Live/Inactive) — multi-toggle, AND-across-groups / OR-within-group; broadened free-text search across Key+Name+Units+Labels. Push-on-write hook in companion stays — both mechanisms run in parallel. Six atomic commits + 1 merge: 01 base class + 11 pure-logic tests; 02 companion wiring + 7 lifecycle tests; 03 Activity column + own timer (+5 logic + 2 lifecycle tests, deviation from "push-on-write only" CONTEXT decision per user); 04 last-refreshed header + chip filters + broader search (+4 logic + 2 lifecycle tests); 05 Pause/Resume polling toggle (+4 lifecycle tests); 06 Events count column (+4 logic + 1 lifecycle test); 07 merge with main (PR #143 toolbar grid conflict). Final test counts post-merge: `test_companion_tag_status_table` 24/24 (pure-logic), `TestTagStatusTableWindow` 16/16 (UI lifecycle), `test_companion_tile_close_buttons` 9/9 (main's new test still PASS), `TestFastSenseCompanion` 64/64 (no regression) = 113/113 total. Verified end-to-end on live industrial-plant demo: 4 MonitorTags showed real event counts (29/32/33/35), 14 others showed 0; Activity flipped Live→Inactive at exactly 5-min boundary via static buildRow_ proof; companion IsLive=0 throughout (window polled itself). Deferred / out-of-scope: (1) polling-scope clarification dismissed by user (heartbeat-only vs. passive-observation vs. only-update-changed-cells — left as-is, table updates all cells every tick); (2) Info button + markdown help — scoped up to a milestone-sized "unified in-app help/wiki" effort, parked as backlog 999.1. | 2026-05-19 | b2ed937, e8a1be5, 43d2d3b, 2a24965, 50d464c, 10df740, 73a3bf1 | Verified | [260519-bs4-implement-a-new-table-view-in-the-compan](./quick/260519-bs4-implement-a-new-table-view-in-the-compan/) |
9494
| 260526-tcf | Fix two pre-existing column assertions in `TestFastSenseCompanion.m` to match the post-PR-#159 1x9 companion toolbar grid — `testToolbarHasWikiButton` now asserts Wiki at col **7** (was 6), `testToolbarGearMovedToColumn8` now asserts Settings gear at col **9** (was 8). Production source-of-truth: `FastSenseCompanion.m:410` (`hWikiBtn_.Layout.Column = 7`) and `FastSenseCompanion.m:423` (`hSettingsBtn_.Layout.Column = 9`); commit `e2ded77` migrated the parallel `TestFastSenseCompanionPlantLogToolbar.m` file but missed these two assertions. Column-value fix only — method name `testToolbarGearMovedToColumn8` retained per user choice; rename to `testToolbarGearAtColumn9` + matching docstring cleanup deferred to a separate task. Diagnostic-message strings on the two `verifyEqual` calls updated alongside the literals so failure messages stay coherent. Pre-existing nature confirmed in briefing: both failures reproduce against HEAD~1 and survived a stash-revert of the parallel quick task `260526-r9x`. MATLAB test verification (expected: 73/73 PASS, or 74/74 if PerTag commit landed first) deferred to the user's local session — `mcp__matlab__*` tools route to local MATLAB and are not reachable from the remote sandbox. | 2026-05-26 | e321ac7 | Ready for verification | [260526-tcf-fix-companion-toolbar-1x9-grid-test-cols](./quick/260526-tcf-fix-companion-toolbar-1x9-grid-test-cols/) |
9595
| 260526-pqz | Raise per-signal slider-preview cap from 400 → 1000 buckets in `DashboardEngine.computePreviewEnvelopeReturning_` — three textual edits (1 code clamp + 2 documenting comments) in `libs/Dashboard/DashboardEngine.m` plus one consistency comment in `tests/test_dashboard_preview_overlay.m` (no assertion change; `numel(xd) >= 4` is cap-independent). Edit sites: line 3524 doc-comment (`computePreviewEnvelope` range), line 3542 inline comment (clamp range), line 3555 actual clamp `max(50, min(1000, floor(axWpx / 2)))`. Out of scope per plan: cache invalidation of `PreviewNBuckets_` — running demos must restart (or trigger the existing resize-invalidation path at `DashboardEngine.m:2241`) for the new cap to take effect. Static analysis clean: `mh_lint` + `mh_style` on both edited files report "everything seems fine"; regression sweep `grep -rn "\b400\b" tests/ \| grep -iE "(preview\|bucket\|envelope)"` returns no matches. MATLAB R2025a: `test_dashboard_preview_envelope` 7/7, `test_dashboard_preview_overlay` 10/10. Octave 11.1.0: `test_dashboard_preview_envelope` 2/2 (5 skipped — pre-existing TimeRangeSelector guard for patch+FaceAlpha+NaN on xvfb), `test_dashboard_preview_overlay` skipped entirely (pre-existing). | 2026-05-26 | 834b43c | — | [260526-pqz-raise-preview-line-cap-per-signal-from-4](./quick/260526-pqz-raise-preview-line-cap-per-signal-from-4/) |
96+
| 260529-rxf | Real per-event email alerts for background monitoring — new `EmailTransport` (SMTP auth/STARTTLS:587 default, also `none`/`ssl`; Octave `exist('sendmail','file')` log-and-skip guard; pure static `buildMailProps` CI seam) that `NotificationService` now delegates to via an injectable `Transport` property; per-(sensor,threshold) email cooldown (default 5 min, 0 disables; dry-run honors it too) with public `SuppressedCount`; `LiveEventPipeline.processMonitorTag_`/`runCycle` now forward real per-event `sensorData` (X/Y/thresholdValue/thresholdDirection from the live tick) so `IncludeSnapshot` rules attach PNGs in live mode. MATLAB-only per user decision. **Backward-compat preserved**: pipeline still defaults to `NotificationService('DryRun', true)` and all prior tests stay green. Verified locally (R2025a, live MATLAB MCP): `test_email_transport` 5/5, `test_notification_service` 10/10 (7 original + 3 new: delegation / cooldown-suppress / cooldown-expiry-via-Hidden-DI-seam), `test_live_event_pipeline_tag` 3/3, plus class suites `TestEmailTransport` 5/5, `TestNotificationService` 7/7, `TestLiveEventPipelineTag` 3/3. MISS_HIT (`mh_style`+`mh_lint`) clean on all 8 files; MATLAB Code Analyzer clean on the 3 new/edited libs. Real SMTP delivery is the single manual step via `examples/05-events/smoke_email_send.m` (FASTSENSE_SMTP_* env vars, STARTTLS:587), out of CI. | 2026-05-29 | 203da7a, 2ac6887, 341bab2, cef1fc5 | Verified | [260529-rxf-real-per-event-email-alerts-for-backgrou](./quick/260529-rxf-real-per-event-email-alerts-for-backgrou/) |
97+
| 260529-fnt | Add `FunctionTransport` adapter (`libs/EventDetection/FunctionTransport.m`) — wraps a user-supplied function handle as a `NotificationService` `Transport` so an existing site/company MATLAB mailer can be reused for alerts with **no SMTP config** (no server/port/creds, no Gmail App Password). Drop-in duck-typed `send(recipients,subject,body,attachments)` (same as EmailTransport), normalizes recipients to a flat cellstr, defaults attachments to `{}`, Octave-safe (only calls user code). Purely additive — EmailTransport/NotificationService behavior unchanged. Built via **/gsd:fast** (inline, no subagents). Verified (R2025a): `test_function_transport` 5/5 (forwarding / recipients-normalization / attachments-default / invalid-handle / NotificationService integration), `test_notification_service` 10/10 (no regression); MISS_HIT + Code Analyzer clean on all touched files. `example_live_pipeline.m` gains a commented FunctionTransport option. Follow-up to 260529-rxf after the user opted to reuse their company mailer instead of configuring Gmail SMTP. | 2026-05-29 | 706e9d5 | Verified | (inline) |
9698

9799
## Progress Bar
98100

0 commit comments

Comments
 (0)