You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**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.
400
400
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.
402
402
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.
Plan: 6 of 6 executed (with 03/04 deferred per Plan 02d data). Shipped plans: 01, 02, 02b, 02d, 05, 06.
29
29
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.
30
30
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
32
32
33
33
### Note on parallel v4.0 work (main branch state)
34
34
@@ -93,6 +93,8 @@ Other main PRs (#138, #139, #141, #144, #145, #146) auto-merged without conflict
93
93
| 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/) |
94
94
| 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/) |
95
95
| 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) |
0 commit comments