feat(companion): Tag Status Table window (quick task 260519-bs4)#149
Merged
Conversation
…c tests - New TagStatusTableWindow class in libs/FastSenseCompanion/ (handle, classical figure) - Static helpers buildRow_(tag) and filterRows_(rows, query) for unit-testable pure logic - buildRow_ handles every Tag subclass (sensor/state/monitor/composite/derived) plus the error case via try/catch (em-dash placeholders for dynamic columns) - filterRows_ is case-insensitive substring match on columns Key + Name - 11/11 function-style tests pass in tests/test_companion_tag_status_table.m - ThrowingTagStub helper class in tests/helpers/ exercises the error-recovery path Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mpanion - Toolbar grid 1x4 -> 1x5 with new "Tags ↗" button (Tag='CompanionTagStatusBtn') in col 3 - New public method openTagStatusTable() returns the singleton window handle - Three private hooks: attachStatusTable_, detachStatusTable_, markStatusTableDirty_ - New private one-liner shouldScanForStatusTable_ gates the scan scope - scanLiveTagUpdates_ now scans ALL Tag kinds when the status table is open (was Sensor/State only); LiveLogPane.addLiveLogEntry gated to Sensor/State - updatedKeys collected per tick and batched to markStatusTableDirty_ at end - close() and setProject() both tear the window down (companion is the registered DetachClosed listener, so the window's onCloseRequest_ cleanup fires the companion's detachStatusTable_ for us) - Hidden test seams: tagStatusTableWindowForTest_, scanLiveTagUpdatesForTest_ - 7/7 class-based UI lifecycle tests pass in tests/suite/TestTagStatusTableWindow.m - TestFastSenseCompanion 64/64 still pass (no regression) - pure-logic suite 11/11 still pass Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… timer (user feedback) User feedback after first verification: the Tag Status window must keep "Latest" / "Last updated" accurate even when companion is NOT in Live mode, and tags with no update in the last 5 minutes should render as Inactive. Changes (all scoped to TagStatusTableWindow and its two test files; no changes to FastSenseCompanion.m or the existing push-on-write hook): - New "Activity" column at index 9 (between "Last updated" and "Samples"). Values: "Live" if (now - X(end)) < 300s in posixtime, else "Inactive". Empty / NaN / unanchored / future X defensively renders "Inactive". Time-base inference mirrors InspectorPane.formatXTick_ (posixtime >1e9, datenum >7e5). - New window-owned RefreshTimer_ (1s period, fixedSpacing, BusyMode='drop', unique name "TagStatusTable-<UUID>"). Starts in openWith after IsOpen=true; stopped+deleted in onCloseRequest_ BEFORE Listeners cleanup. Callback in try/catch; logs via warning (not uialert); self-stops after 2 consecutive failures to prevent log noise storms. Independent of companion Live mode -- guarantees Activity/Last updated stay accurate even when companion is idle. - buildRow_(tag) -> buildRow_(tag, nowSeconds) -- the nowSeconds parameter makes the static helper pure/unit-testable for the Activity column. Backward-compatible: nargin<2 falls back to TagStatusTableWindow.nowSeconds_. - RowBuffer_ / table data widened to 11 cols. All existing test assertions updated for the new column positions (Samples now at idx 10, Labels at 11). - The original FastSenseCompanion.scanLiveTagUpdates_ -> markStatusTableDirty_ push path is unchanged; both mechanisms now run in parallel. Tests: - test_companion_tag_status_table.m: 11 existing + 5 new (Activity Live / Inactive across posix/datenum/empty/future/filter regression) = 16/16 pass. - TestTagStatusTableWindow.m: 7 existing + 2 new (Activity flip without Live mode, RefreshTimer_ stopped+deleted on close via timerfindall sweep) = 9/9 pass. - TestFastSenseCompanion regression: 64/64 pass. - checkcode / mh_lint / mh_metric: clean (informational notices on now/datenum match codebase convention in InspectorPane and LiveLogPane).
…ty/Activity chips + broader search
- TagStatusTableWindow: new "Last refreshed: HH:MM:SS" label at top of figure;
seeded on openWith, updated on every clean onRefreshTick_ (even when no
rows changed, so it acts as a heartbeat).
- Three chip groups above the table (Type / Criticality / Activity),
multi-toggle within each group, default-all-active so first-open
shows the same 18 rows as today. Active chips use theme.Accent +
bold, matching TagCatalogPane.applyPillStyle_.
- Broadened free-text search from Key+Name to Key+Name+Units+Labels
(Labels are stored comma-joined in column 11). filterRows_ signature
extended to (rows, query, activeTypes, activeCrits, activeActivities)
with full backward compatibility on the 2-arg form via nargin defaults
and an iscell sentinel ([] = skip, {} = exclude-all). Combined
semantics: AND across dimensions, OR within each chip group; a chip
group with zero active entries excludes ALL rows.
- Layout: window 520->580 px tall; five vertical strips (label, search,
chips, table, footer); window stays resizable.
- All widgets are uicontrol 'pushbutton'/'text' so we stay inside the
classical-figure widget family (uibutton is uifigure-only).
- onCloseRequest_ unchanged in ordering: stop+delete RefreshTimer_
BEFORE Listeners cleanup. New handles cleared on close.
- Tests: +4 pure-logic cases (Units search, Labels search, chip AND/OR
semantics, zero-chip-group excludes all) + 2 UI cases (label exists
on open, label updates after tick) via new test seams
lastRefreshedLabelForTest / tickForTest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New uicontrol pushbutton on the last-refreshed header row labelled "Pause polling"/"Resume polling" (classical figure -> uicontrol family) - Public method setPollingActive(tf) drives the flow; the button's callback delegates to it so click + programmatic paths are identical - Pause stops RefreshTimer_ without deleting it; resume re-starts the same handle (with startRefreshTimer_ fallback if it died) - Resume fires a synchronous one-shot onRefreshTick_ so the table is immediately fresh instead of waiting up to 1 s for the next tick - markTagsDirty is now a no-op while paused: the user's mental model is "polling off -> table is frozen". No coupling to FastSenseCompanion - Header label appends " (paused)" suffix in-place when paused; the preceding HH:MM:SS is preserved so the user sees WHEN polling stopped - 4 new TestTagStatusTableWindow cases cover Running state transitions, paused markTagsDirty no-op, and button-label toggling Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surface a per-tag event count in the Tag Status Table so users can see at a glance which tags have produced violations / annotations and which haven't. The count refreshes with the same 1 s polling cadence as the existing Samples column. - Insert "Events" as column 10 (between Activity and Samples); Samples shifts to col 11, Labels to col 12. RowBuffer_ widened to 12 columns. - Add static helper countEventsForTag_(tag): pure function returning a non-negative integer; defers to Tag.eventsAttached() (which itself wraps EventStore.getEventsForTag, the same path CompanionEventViewer uses). Never throws; returns 0 for missing/empty EventStore. - Add private precomputeEventCounts_(keys): single call site that buckets event counts for the listed keys; threaded through rebuildAll_, markTagsDirty, and onRefreshTick_ so each tick does ONE consolidated walk per dirty key set. - Extend buildRow_ with optional 3rd arg eventCountsByKey: when present and the key is a hit, the count reads from the precomputed map; otherwise falls back to countEventsForTag_(tag). Backward compatible with 2-arg callers. - Shave column widths so all 12 columns fit in the default 1100 px window without horizontal scroll; window stays resizable. - Shift rowMatchesSearch_'s Labels column index from 11 to 12. - Update existing tests for the new 12-col row shape; add 4 pure-logic tests (countEventsForTag_ with/without EventStore, buildRow_ Events column placement, bucketed-map precedence) and 1 UI test (testEventsCountColumnPopulatedFromRegistry: tag with 3 stubbed events -> Events='3'; tag with none -> Events='0'). No touch to FastSenseCompanion.m. All errors stay under the existing FastSenseCompanion:tagStatusTable* namespace. mh_style/mh_lint/mh_metric clean; 24/24 pure-logic + 16/16 UI + 64/64 TestFastSenseCompanion pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Conflict resolution in libs/FastSenseCompanion/FastSenseCompanion.m: - Properties: kept both new private fields (TagStatusTableWindow_ + hTagStatusBtn_ from this branch; OpenedFigures_ + hTileBtn_ + hCloseAllBtn_ from main PR #143) - Inner toolbar grid: combined 1x5 (ours: Events|Live|Tags|spacer|gear) and 1x6 (main: Events|Live|Tile|Close all|spacer|gear) into a 1x7: Events | Live | Tags | Tile | Close all | spacer | gear. Column widths {110, 110, 110, 70, 90, '1x', 36}. - Button blocks: re-numbered main's Tile/Close all/gear columns 3->4, 4->5, 6->7. Tags ↗ stays at col 3 next to Live. - Test seams: kept all four hidden test getters (tagStatusTableWindowForTest_, scanLiveTagUpdatesForTest_, getOpenedFiguresForTest_, trackOpenedFigureForTest_). Verified post-merge: - test_companion_tag_status_table 24/24 PASS - TestTagStatusTableWindow 16/16 PASS - test_companion_tile_close_buttons 9/9 PASS - TestFastSenseCompanion 64/64 PASS Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the 260519-bs4 row to the Quick Tasks Completed table (Verified, 6 feature commits + 1 merge commit) and refreshes the Last activity lines after merging main into the branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI MATLAB Lint flagged 5 continuations where && started the new line instead of ending the previous line. Pure formatting — no semantic change. Affected: - libs/FastSenseCompanion/FastSenseCompanion.m lines 905, 1425, 1442, 1443 - tests/test_companion_tag_status_table.m line 543 24/24 pure-logic tests still PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Contributor
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'FastSense Performance'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.10.
| Benchmark suite | Current: e80349d | Previous: 439f18b | Ratio |
|---|---|---|---|
Instantiation mean std(5M) |
0.896 ms |
0.224 ms |
4 |
Render mean std(5M) |
2.176 ms |
1.372 ms |
1.59 |
Zoom cycle mean std(5M) |
0.542 ms |
0.455 ms |
1.19 |
Instantiation mean std10M) |
1.297 ms |
0.989 ms |
1.31 |
Render mean std10M) |
2.562 ms |
0.494 ms |
5.19 |
Zoom cycle mean std10M) |
0.647 ms |
0.483 ms |
1.34 |
Render mean std50M) |
4.647 ms |
1.84 ms |
2.53 |
Zoom cycle mean std50M) |
0.643 ms |
0.425 ms |
1.51 |
Instantiation mean ( std00M) |
112.927 ms |
45.4 ms |
2.49 |
Downsample mean ( std00M) |
3.983 ms |
0.689 ms |
5.78 |
Instantiation mean ( std00M) |
764.685 ms |
45.4 ms |
16.84 |
Render mean (500M) |
392.898 ms |
352.608 ms |
1.11 |
Render mean ( std00M) |
88.784 ms |
2.497 ms |
35.56 |
Zoom cycle mean ( std00M) |
1.544 ms |
0.725 ms |
2.13 |
Dashboard live tick stdmean |
1.341 ms |
0.573 ms |
2.34 |
This comment was automatically generated by workflow using github-action-benchmark.
CC: @HanSur94
CI Octave run failed because test_companion_tag_status_table tried to load TagStatusTableWindow.m to reach the static helpers, but the class file uses MATLAB-only syntax (uifigure / uitable / classdef properties blocks) that Octave cannot parse — fails with "syntax error near line 40, column 22" on every test case. Adds the standard Octave skip guard at the top of the test (same pattern as test_companion_apply_theme_walker, test_companion_open_ad_hoc_plot, test_companion_inspector_resolve_state). Companion is MATLAB-only by design (Phase 1018 cross-cutting rule). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7613a1d to
e80349d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a Tag Status Table window to
FastSenseCompanion, opened via a new Tags ↗ button on the companion's top toolbar. Detached classical figure (not auifigurechild, per cross-cutting rule) with a 12-columnuitablelisting every registered tag and its live status.Designed as a quick task (
/gsd:quick), wave of 6 atomic commits.What you see
tag.Keytag.Nametag.Criticalitytag.UnitsY(end)X(end)formatted as wall-clock timenow − X(end) < 5 min, else InactiveEventStoreper tagnumel(Y)tag.LabelsUI extras on the window:
Refresh model
Two parallel paths so the table is correct regardless of `companion.IsLive`:
Both paths short-circuit while polling is paused. Window-close path runs `stop(t); delete(t);` before `Listeners_` cleanup, in that order.
Commits
`FastSenseCompanion.m` is touched only in commit 02; the other 5 commits are scoped to `TagStatusTableWindow.m` + its two test files.
Verification
Cross-cutting rules respected (locked in Phase 1018)
Deferred / out of scope
Test plan for reviewer
🤖 Generated with Claude Code