Skip to content

v5.0 Multi-Machine Fleet — cross-machine sensor mapping, machine-scoped companion, comparison view#206

Open
HanSur94 wants to merge 114 commits into
mainfrom
claude/friendly-leakey-0bc166
Open

v5.0 Multi-Machine Fleet — cross-machine sensor mapping, machine-scoped companion, comparison view#206
HanSur94 wants to merge 114 commits into
mainfrom
claude/friendly-leakey-0bc166

Conversation

@HanSur94

Copy link
Copy Markdown
Owner

v5.0 Multi-Machine Fleet

Lets a MATLAB engineer work with a fleet of near-identical machines: define machines with isolated tag catalogs, auto-map the same logical sensor across machines whose raw keys differ, scope the companion to one machine, and overlay the same sensor across machines — all toolbox-free, all through the existing DashboardWidget/FastSense contracts.

Milestone delivered at 5 phases and audit-PASSED (see .planning/v5.0-MILESTONE-AUDIT.md).

Phases

Phase What it adds Key files
1041 CanonicalMapper logical-sensor mapping: confidence levels, auto-suggest (toolbox-free edit-distance), manual overrides, unmapped-tail libs/Fleet/CanonicalMapper.m
1042 Machine + Fleet + DI seam isolated per-machine tag catalogs; fleet JSON persistence; pipeline tagSource_ DI; machine tags never enter the global TagRegistry libs/Fleet/Machine.m, libs/Fleet/Fleet.m
1043 Serializer resolver seam machine-scoped dashboard load (DashboardEngine.load(...,'TagResolver',fn)) + backward compat libs/Dashboard/{DashboardEngine,DashboardSerializer,FastSenseWidget}.m
1044 Companion machine dimension machine selector rail, setProject wiring, active-machine indicator, clean timer lifecycle on switch libs/FastSenseCompanion/{FastSenseCompanion,MachineSelectorPane}.m
1045 Cross-machine comparison modeless compare-builder dialog, resolve-once caching, confidence gate, per-machine stable colors; code+UI reviewed, human-verify approved libs/FastSenseCompanion/CompareBuilderDialog.m

Audit summary (PASSED)

  • 24/24 in-scope requirements satisfied (3-source cross-reference; no orphan).
  • 5/5 phases VERIFICATION: passed.
  • 4/4 cross-phase wirings WIRED, E2E flow holds end-to-end (gsd-integration-checker), zero hard breaks.
  • Critical invariants hold: grep TagRegistry.register libs/Fleet/ = 0; no UI in the Fleet data model; Octave-safe libs/Fleet/ (no contains).

Scope decision — DASH-03/04 (clone/remap) dropped

Phase 1046 (dashboard clone/remap) was discussed, planned, and checker-verified, then deliberately dropped before execution: its only v5.0 value would have been a programmatic-only API (the companion UI hook was already deferred), and the headline value — cross-machine comparison — shipped in 1045. The enabling 1043 resolver seam stays in place; the 1046 plans live in git history if revived.

Non-blocking tech-debt (from the 1046 drop)

  • The 1043 TagResolver seam (DASH-01/02) and Fleet.resolveLogical (FLEET-06) are built + unit-tested but currently unconsumed by any shipped flow (their consumer was 1046). Dormant infrastructure, not defects.
  • Nyquist coverage PARTIAL on 1041–1044 (discovery-only; all passed verification).
  • Pre-existing PerTag/ADHOC05 timer flake (documented).

Reviewer note — where the code is

113 commits: 45 touch libs//tests/ (the code), 71 are .planning/ docs (force-tracked by the GSD workflow in this repo). For code review, focus on the libs/ + tests/ file diff. Tests: TestFastSenseCompanion 91/91 (CMP block), test_compare_resolution 7/7, plus each phase's suite.

🤖 Generated with Claude Code

HanSur94 and others added 30 commits June 2, 2026 17:00
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pitfalls + summary); archive v4.0 research

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…; add CMP-UX research; archive v4.0 roadmap

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wave 0: TestCanonicalMapper.m (30 RED tests) + install.m Fleet path
Wave 1: CanonicalMapper core (suggest/confidence/units) — CANON-01/02
Wave 2: override/persist/query API — CANON-03/04
Wave 3: standalone CanonicalMapEditor uifigure + checkpoint — CANON-05

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- W1 (1041-02 Task 1 verify): remove misleading conditional; state that
  only the two grep gates are GREEN after Task 1, all data-model tests
  (incl. testEditDistanceKnownPairs) stay RED until Task 2 builds suggest()
- W2 (1041-01 + 1041-02): lock a single concrete LOW-confidence fixture
  (M01/M02 'abcdefghij' seed centroid + M03 'abzzzzzzzz' at sim 0.20 ->
  within-cluster LOW); specify seed-then-assign clustering with per-member
  confidence scored against the centroid; both plans now reference the
  identical keys + hand-confirmed sim math
- W3 (1041-04): add phase-completion truth (all 30 tests green on MATLAB,
  29 + 1 skip on Octave) to must_haves.truths

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Plan 1041-01 Task 1. Adds `addpath(fullfile(root,'libs','Fleet'))` after the
libs/Help entry (10 libs paths total) and creates libs/Fleet/.gitkeep so the
new Fleet library directory is tracked. CanonicalMapper.m / CanonicalMapEditor.m
land here in Plans 02-04.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Plan 1041-01 Task 2. Full Nyquist test suite for CanonicalMapper: 30 methods
across CANON-01..05 + the two Octave-safety/no-toolbox grep gates, with real
assertion bodies (not stubs). Runs RED end-to-end (28 errored: CanonicalMapper
not yet implemented; 2 grep gates skip cleanly via assumeTrue file-exists guard).

Locked decisions carried forward to Plan 02:
- Clustering is seed-then-assign with ATTACH_THRESHOLD_=0.15. The plan/checker said
  "no floor" but that is inconsistent: testConfidenceLowThreshold needs M03 (sim 0.20
  to centroid) to attach as LOW, while testUnmappedReturnsUnresolved needs 'pressure'
  (hand-computed sim 0.10 to centroid 'temp_motor') to stay unmapped. A 0.15 floor
  separates them cleanly.
- Per-member confidence is scored against the centroid (longest key, tie->lex-smallest).
- Reconciled VALIDATION.md count: docs said "30" but enumerated 29; added a 30th test
  (testNormalizeCollapsesRepeats, CANON-01 normalize collapse/trim) and synced the map.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SUMMARY for Plan 1041-01 (Wave 0). Marks plan 01 complete in ROADMAP/STATE.
30-method RED suite + libs/Fleet path registration done.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… 02 task 1)

Plan 1041-02 Task 1. Handle-class scaffold: Entries_ map, LastTagInfos_ seam,
threshold constants (HIGH 0.90 / MEDIUM 0.60 / ATTACH 0.15), constructor, and the
toolbox-free local helpers normalize_, editDistance_ (Wagner-Fischer), similarity_,
plus assignConfidence_ (with a 1e-12 boundary tolerance). Octave-safe: no contains(,
helper named editDistance_ so the no-toolbox grep gate stays green.

Suite: the 2 grep gates now PASS (file exists, Octave-safe); the 14 CANON-01/02
data tests remain RED (suggest() not yet implemented) — intended Task 1 state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…2 task 2)

Plan 1041-02 Task 2. Implements suggest(tagInfos): seed-then-assign clustering
(single-link seeds at sim>=0.60; leftovers attach to nearest centroid at
sim>=ATTACH_THRESHOLD_=0.15), per-member confidence scored against the centroid
(0.90/0.60 inclusive), and the unit-mismatch flag + one-level confidence downgrade.
Non-AUTO entries are preserved across re-suggest (override precedence seam for Plan 03).

Result: 17 GREEN (6 CANON-01 incl. testNormalizeCollapsesRepeats, 5 confidence,
4 unit, 2 grep gates). testConfidenceLowThreshold passes via the LOCKED 3-member
fixture (M03 attaches at sim 0.20 -> LOW). 13 RED remain (CANON-03/04/05 -> Plans 03/04).

Test refinement: testSuggestNoMatches now asserts the CANON-01 "no cluster forms"
fact only (dropped a premature m.unmapped() call that belongs to CANON-04/Plan 03).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SUMMARY for Plan 1041-02 (Wave 1). suggest/confidence/units done; CANON-01/02 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lan 03 task 1)

Plan 1041-03 Task 1. CANON-03 precedence (override -> OVERRIDDEN/HIGH via upsertEntry_,
confirm -> CONFIRMED; suggest already preserves non-AUTO) and CANON-04 query API
(reviewPending = LOW-AUTO or unitMismatch; isResolvable = the Phase 1045 exclusion
gate; unmapped = the unresolved tail, sorted). 26/30 GREEN; only persistence (3) +
editor (1) remain RED.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…task 2)

Plan 1041-03 Task 2. CANON-03 persistence: toStruct (version 1 + flat entry cell),
static fromStruct (normalizeToCell_ handles jsondecode struct-array collapse), save
(per-entry jsonencode + strjoin + atomic movefile, EventStore pattern), static load.
normalizeToCell_ ported verbatim from Dashboard (no cross-dependency).

Fix vs LOCKED snippet: used read-modify-write for containers.Map buckets instead of
the invalid `map(key){end+1}=e` indexing.

Result: 29/30 GREEN (all CANON-01..04 + 2 grep gates). Only testEditorConstructs
RED -> Plan 04 (CanonicalMapEditor).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SUMMARY for Plan 1041-03 (Wave 2). CanonicalMapper data model complete; CANON-03/04 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… 04 tasks 1-2)

Plan 1041-04 Tasks 1+2. Standalone MATLAB-only review/edit window per UI-SPEC:
3-row uigridlayout (toolbar / 6-col read-only uitable / action row), theme via
CompanionTheme.get('dark') with a self-contained dark fallback + stripe pair.
Promote (gated by the Low-Confidence and Unit-Mismatch uiconfirm warnings) ->
mapper.confirm; Override (inputdlg) -> mapper.override; Save (uiputfile) ->
mapper.save; Show-Pending toggle; text filter; selection styling; unsaved-changes
close gate. All callbacks try/catch-guarded with non-blocking uialert.

Result: 30/30 TestCanonicalMapper GREEN on MATLAB (testEditorConstructs included).
No existing file modified (CanonicalMapper.m untouched). Octave skips the editor
smoke test cleanly via assumeTrue.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pproved)

SUMMARY for Plan 1041-04 (Wave 3). Standalone editor done; CANON-05 satisfied; manual UAT approved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ilter (code review)

Addresses code-review findings on the Phase 1041 source:
- CR-01 (critical): reviewPending() gated the unitMismatch branch on nothing, so a
  CONFIRMED/OVERRIDDEN unit-mismatch entry stayed "pending" forever and disagreed with
  isResolvable(). Now an entry needs review only when NOT user-vouched AND (LOW or
  unitMismatch). Extended testReviewPendingExcludesGoodEntries with a confirmed-mismatch
  regression case (locks the fix). 30/30 still green.
- WR-02: save() now wraps movefile in try/catch and deletes the orphaned .tmp on failure.
- IN-02: CanonicalMapEditor filter haystack now includes machineId.

Deferred (advisory, low-severity/edge/future-phase) — see SUMMARY follow-ups: WR-01
(same-machine duplicate keys in one cluster), WR-03 (two clusters normalizing to the
same logicalId), WR-04 (LOW+mismatch shows only the mismatch dialog), IN-01/IN-03
(Listeners_ destructor / PENDING status are Phase 1044 seams).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gsd-verifier: status=passed, 5/5 must-haves. gsd-code-reviewer: 1 critical + 4 warn + 3 info
(critical CR-01 + WR-02 + IN-02 fixed in 8f67297; remainder deferred as follow-ups).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…AT approved)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… plans, 3 waves)

- Plan 01 (Wave 1): RED test scaffold (TestMachine/TestFleet + Octave flat) + Fleet-private normalizeToCell_
- Plan 02 (Wave 1): tagSource_ DI seam on Batch/LiveTagPipeline (FLEET-03; single-machine unchanged)
- Plan 03 (Wave 2): Machine class — isolated catalog, duck-type API, ingest wrappers, EventStore, lazy load, timer-safe delete
- Plan 04 (Wave 3): Fleet class — composable filters + JSON round-trip with embedded canonical map + fleetConfigVersion

Covers FLEET-01..06; all critical invariants baked into acceptance criteria.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
HanSur94 and others added 29 commits June 10, 2026 18:36
The fleet auto-select branch assigned firstMachine.Dashboards to
Engines_/Dashboards with only an iscell wrap, skipping the
DashboardEngine validation every other intake path enforces.
Machine.Dashboards is a public, unvalidated property, so e.g.
m.Dashboards = {42} constructed a companion that failed later with an
un-namespaced error, while switching TO the same machine failed fast
via setProject. Reuse the exact Step-4 check and
FastSenseCompanion:invalidDashboard id for a uniform contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ariant doc

MachineSelectorPane:setThemeFailed namespace (was FastSenseCompanion:*);
explicit ORDERING INVARIANT comment on the construction selectById-before-
listener sequence. UI-REVIEW.md committed (57/60 advisory).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Compare-builder composition (fleet-only toolbar entry, per-machine row
grid, quick-fill + per-row overrides, singleton modeless dialog);
resolves the STATE-flagged openAdHocPlot injection decision (additive
SeriesColors/SeriesLabels NV args); insertion-index stable colors;
per-row confidence gate, consolidated open-time warnings, per-row
promote (in-memory).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CompareBuilderDialog 600x480 modeless contract: 5-row outer grid,
[1 6] machine rows w/ 4-state machine, quick-fill strip, Accent CTA,
SeriesColors/SeriesLabels openAdHocPlot NV extension, locked copy +
badge glyphs + ASCII fallbacks, R2020b caveats honored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sion, wave plan

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Key finding: CanonicalMapper.resolve() does not exist — Wave-1 prereq.
Confidence gate lives in the dialog (Fleet.resolveLogical ungated).
CMP-05 invariant via ResolvedTags_ cache-identity seam (no profiler).
Toolbar [1 11]->[1 12] fleet-only shift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s, 4 waves)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g verifies, Fleet.mapper accessor)

- 1045-05: add CMP-03 to requirements + testCMP03_SkipGraceful (skip-alert + opens-with-remaining)
- 1045-01/02/03/05: replace grep-only verifies with run_matlab test-runner invocations
- 1045-01: add Fleet.mapper() public accessor (mirrors machineIds()); helper uses it instead of private Mapper_
- 1045-01: declare optional 3-arg buildCompareResolution_(fleet,logicalId,theme) in must_haves + Ttheme test
- 1045-03: executor note on Task 2 (method-by-method stub-then-fill; do not split)
- 1045-VALIDATION: nyquist_compliant true; add CMP-03 row + Fleet.mapper wave-0 entry

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ia mapper() accessor)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ution helpers

Add the pure-logic foundation for cross-machine comparison:
- CanonicalMapper.resolve(logicalId, machineId) -> entry struct | [] (the
  CMP-05 resolve-once seam; no side effects)
- Fleet.mapper() public accessor mirroring machineIds()
- buildCompareResolution_ Octave-safe per-machine row assembly with the
  LOW-confidence gate (invariant #4) + unit-mismatch detection; optional
  3-arg theme path populates per-row colors
- compareSeriesColor_ stable per-machine color by fleet insertion index
- runCompareResolutionTests + tests/test_compare_resolution.m wrapper
  (7 tests green: resolve/mapper/states/unit-mismatch/theme-color/stability)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add additive optional 'SeriesColors' (cell of 1x3 RGB) and 'SeriesLabels'
(cellstr) name-value args so the cross-machine comparison overlay can inject
per-machine stable colors and machine-qualified legend labels (CMP-02):
- inputParser parses the NV args; arity mismatch throws
  openAdHocPlot:seriesColorsMismatch BEFORE any figure spawns
- the tags-with-data filter carries colors/labels through index-aligned so a
  dropped (no-data) tag drops its color/label too
- plotOverlay_ draws each line with an explicit per-series Color (immune to
  ColorOrderIndex) and the supplied DisplayName when present; legacy 3-arg
  calls are byte-unchanged (ColorOrder auto-assign + tag-Name DisplayName)
- runOpenAdHocPlotTests gains T-NV1 (legacy byte-compat), T-NV2 (color+label
  injection), T-NV3 (mismatch error, no figure) — 12/12 green via the flat
  wrapper

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Open

Phase 1045 Plan 03 (Wave 2): the headline cross-machine comparison builder.

New libs/FastSenseCompanion/CompareBuilderDialog.m (classdef < handle):
- Modeless 600x480 second uifigure on the CompanionSettingsDialog lifecycle
  (no WindowStyle='modal'; friend-class CompareBuilderDlg_ write-back on close,
  guarded so it stands alone before Plan 05 declares the property).
- Quick-fill shared-sensor uidropdown populated from CanonicalMapper keys;
  selecting a logical sensor assembles per-machine rows via
  buildCompareResolution_ (3-arg form -> per-machine swatch colors).
- Four-state row machine (auto / confirm_needed / override / none): LOW+AUTO
  renders 'confirm_needed' unchecked-by-default (invariant #4); missing sensors
  render 'none' (excluded). Color swatch + include checkbox + machine name +
  per-row override dropdown + action slot + status badge per UI-SPEC.
- onOpenComparison_ resolves each included tag ONCE into ResolvedTags_ and calls
  openAdHocPlot with SeriesColors/SeriesLabels; consolidated non-blocking
  unit-mismatch + skipped-machine alerts (CMP-03); spawned overlay tracked via
  the companion. No CanonicalMapper call after the cache (invariant #5).
- onRowAction_ is an inert bounds-guarded stub; Plan 04 fills Confirm/Promote.

FastSenseCompanion.m: add public fleet() read accessor (mirrors
Fleet.mapper()/machineIds()). Deviation from the plan's literal app.Fleet_ /
app.trackOpenedFigure_ private-member reads: the dialog reaches the companion
through PUBLIC seams (fleet() + the existing public trackOpenedFigure()),
matching the CompanionSettingsDialog idiom (dialogs call only public app
methods). No change to the private Fleet_ field or its internal readers.

Verify (live MATLAB, worktree on path): check_matlab_code clean on both files;
10/10 smoke green — shell + invalidApp guard, four-state resolution
{auto,confirm_needed,auto,none} with LOW+none unchecked, Open gating, resolve-once
cache (2 tags), tracked overlay, and per-machine legends
('Press Line 3: Temp 1', 'Compressor A: Temp 3'); timers clean after teardown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ic-seam deviation)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 1045 Plan 04 (Wave 3): layer the per-row CMP-06 actions + theme refresh
onto CompareBuilderDialog.

- onConfirm_(i) (public): include a confirm_needed (LOW/unreviewed) row —
  state -> override, checkbox forced checked, action 'Confirm' -> 'Promote',
  badge -> override, all in place (no full rebuild; Pitfall 6).
- onPromote_(i) (private, fired by the Promote button): R2020b-safe async
  uiconfirm (DefaultOption=Cancel, CancelOption=Cancel); the override is applied
  in the CloseFcn, never inline (Pitfall 4).
- onPromoteConfirmed_(i, event) (public): only on 'Promote', calls
  CanonicalMapper.override via the public mapper() seam — IN-MEMORY ONLY, never
  Fleet.save (deferred idea honored). Marks the row promoted: badge
  '<check> promoted' (Accent), Promote button removed. Pitfall 3 accepted.
- onRowAction_ now dispatches by state (confirm_needed -> onConfirm_;
  override-unpromoted -> onPromote_).
- applyTheme_(themeArg) (public): repaint figure + walker, then re-assert the
  post-walk overrides — Open-button background by includedCount, per-row badge
  FontColor by state, and per-machine swatch colors (series colors, NOT theme
  tokens — preserved across a theme switch). Plan 05 wires the companion
  theme-switch to call it.

onConfirm_/onPromoteConfirmed_/applyTheme_ are public underscore-suffixed seams
(matching the codebase's getOpenedFiguresForTest_/trackOpenedFigureForTest_
convention) so the Plan 05 class-suite drives CMP-06 + theme without the async
uiconfirm.

Verify (live MATLAB): check_matlab_code clean; 9/9 smoke green — Confirm ->
override+Promote button; onPromoteConfirmed_('Promote') -> mapper.resolve status
OVERRIDDEN + '✓ promoted' badge + button gone; Cancel no-op (M01 stays AUTO);
applyTheme_('light') repaints figure + auto-badge to light tokens, swatch color
persists. timers 0 after teardown. The only '.save(' in the file is the locked
instructional copy string ('Call Fleet.save() to persist.'), not a call.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mory override)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 1045 Plan 05 (Wave 4): integration — make the comparison builder
reachable and prove the phase invariants at the class-suite level.

FastSenseCompanion.m:
- Fleet-mode toolbar grows [1 11] -> [1 12]: a fleet-only 'Compare' button
  (Tag 'CompanionCompareBtn', 80 px) at col 9; the flex spacer/active-machine
  label/gear shift to cols 10/11/12. Legacy toolbar stays [1 10] byte-identical.
- hCompareBtn_ (private) + CompareBuilderDlg_ friend property
  (SetAccess = ?CompareBuilderDialog).
- openCompareBuilder_ focus-or-create singleton (mirrors openSettings);
  close() teardown deletes CompareBuilderDlg_ and resets it to [].
- applyTheme propagates to an open builder (CompareBuilderDlg_.applyTheme_,
  guarded) — the builder owns its own uifigure, not walked by the companion.

TestFastSenseCompanion.m: 7 CMP tests after the MACH block —
testCompareButtonFleetOnly (12-col fleet / 10-col legacy, MACH-05 intact),
testCompareBuilderSingleton, testCompareBuilderClosesWithCompanion,
testOpenComparisonLaunchesOverlay, testCMP03_SkipGraceful,
testCMP05_NoResolveInTick (cache handle-identity + canonical-map signature
unchanged across one engine tick), testPromoteUpdatesMapper. Plus private
fixtures (buildSharedSensorFleetApp_/buildLowConfidenceFleetApp_/
openBuilderAndSelect_/mapperSignature_) and a closeSpawnedFigs_ local helper
that close()s overlays (fires engine stopLive; delete() would leak the timer).

Verify (live MATLAB): TestFastSenseCompanion 91/0/0 (7 new CMP green; both
pre-existing PerTag/ADHOC05 flakes green this run — above the 82/84 baseline);
test_compare_resolution 7/7; check_matlab_code clean (pre-existing patterns
only). Invariants: TagRegistry.register in libs/Fleet = 0; UI primitives in
libs/Fleet only in the pre-existing CanonicalMapEditor.m; contains( in
CanonicalMapper.m = 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ite)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…1/02 summaries

- 1045-VERIFICATION.md: 5/5 success criteria VERIFIED + 5 critical invariants
  PASS. Evidence: TestFastSenseCompanion 91/0/0, test_compare_resolution 7/7,
  per-plan smokes 10/9/11, human-verify checkpoint approved.
- Backfill 1045-01 / 1045-02 SUMMARYs (Wave-1 code committed earlier without
  summaries when the execution agent terminated early).
- ROADMAP: Phase 1045 [x] complete; all 5 plan boxes checked.
- STATE: completed_phases 4->5 (83%); position 1045 complete -> 1046 next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Independent code-review (1045-REVIEW.md, 0 blockers) + UI-review
(1045-UI-REVIEW.md, 20/20 full conformance) of the Phase 1045 changes.
Fixes for all 3 warnings + 3 cheap infos:

- WR-01: onPromoteConfirmed_ bounds-checks the async-captured row index AND
  re-verifies the row is still an unpromoted 'override' before calling
  CanonicalMapper.override — a stale/out-of-range index can no longer promote
  the wrong machine's mapping (the headline data-correctness path).
- WR-02: the Open-time unit-mismatch alert now prints the diverging TAG unit
  (via a shared tagUnits_ helper, also used by detectRowUnitMismatch_) instead
  of the canonical reference unit.
- WR-03: the constructor throws CompareBuilderDialog:notFleetMode when
  app.fleet() is [] (legacy mode) instead of an opaque double-dispatch error.
- IN-01: new CanonicalMapper.logicalIds() public accessor; the quick-fill
  dropdown uses it instead of poking the private Entries_ map.
- IN-04: the '— none —' transition clears stale localUnits/localName/
  confidence/status.
- IN-05: a single isIncluded_(rs) predicate shared by includedCount_/
  includedIndices_ so the count, Open set, and gating cannot drift.

Verify (live MATLAB): check_matlab_code clean on both files; 10-check
review-fix smoke green (legacy-guard, logicalIds, out-of-range + re-promote
guards, valid promote -> OVERRIDDEN, none-clear, unit-mismatch override shows
diverging 'K'). Full TestFastSenseCompanion.m re-run 90/91 — the lone failure
is the pre-existing testADHOC05 orphan-debounce-timer flake (legacy-mode
ad-hoc path that delete()s its spawned figure; exercises none of the changed
code; green in the phase-verification run). All 7 CMP tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 1046 (final milestone phase) discuss. Decisions locked (user deferred
both forks to me):
- Cloner lives as a DashboardSerializer static (keeps Fleet a pure data model;
  Dashboard->Fleet direction preserved).
- Reverse canonical lookup -> new CanonicalMapper.logicalIdFor(machineId, key).
- Scope: programmatic API + tests (no companion UI hook this phase).
- DASH-04 graceful bind: a target-scoped resolver that never throws, collecting
  unresolved bindings into a returned warnings struct array {sourceKey,
  logicalId, reason}.

Verified against live code: the 1043 resolver seam reaches the 'tag' path
(FastSenseWidget.fromStruct:1501); clone reuses the whole DashboardEngine.load
path via a temp JSON + TagResolver (single + multi-page for free).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…IED)

- 1046-01 (Wave 1): CanonicalMapper.logicalIdFor reverse lookup + flat
  Octave-safe test (test_canonical_reverse).
- 1046-02 (Wave 2): DashboardSerializer.cloneForMachine static — target-scoped
  resolver (logicalIdFor -> resolve -> targetMachine.get) that never throws,
  accumulating {sourceKey, logicalId, reason} warnings via a nested function;
  reuses DashboardEngine.load via a temp JSON + 'TagResolver' (single +
  multi-page); + TestFleetDashboardClone class-suite (rebind, DASH-04 warnings,
  A->B round-trip, arg validation).

gsd-plan-checker: VERIFIED — all 3 success criteria + DASH-03/04 trace to tasks;
every foundation claim confirmed against live code (fromStruct:1524 resolver
seam, load JSON-only resolver path, saveJSON/widgetsToConfig exist, single-page
load populates Widgets:4421, headless Tag read, nested-fn warnings accumulation
correct); invariants preserved (no TagRegistry.register / contains / UI in Fleet;
no signature changes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nding)

ROADMAP: Phase 1046 plan list (2 plans, 2 waves). STATE: position -> 1046
discussed+planned, execute pending; total_plans 21->23.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…phases

User decision (2026-06-17): drop DASH-03/04 / Phase 1046 entirely rather than
build a programmatic-only clone API with no concrete demand. The milestone's
headline value (cross-machine comparison) shipped in Phase 1045.

- REQUIREMENTS: DASH-03/04 struck + dropped note; coverage 24 delivered / 2
  dropped.
- ROADMAP: phases_total 6->5, phases_complete 5; Phase 1046 line + detail
  section marked DROPPED (original spec retained for the record); Overview note.
- STATE: status complete, 5/5 phases, 100%; position = milestone delivered.
- Removed .planning/phases/1046-* (CONTEXT + 2 checker-verified plans) — retained
  in git history (7e0477f, e080318); the 1043 resolver seam stays in place, so
  clone/remap is revivable (~1 hr) if a real need appears.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…2E holds

Cross-phase milestone audit of v5.0 Multi-Machine Fleet (delivered at 5 phases;
1046 dropped out of scope):
- Requirements 24/24 in-scope satisfied (3-source cross-ref: traceability ×
  VERIFICATION × SUMMARY; no orphan; FLEET-02 isolation confirmed).
- Phases 5/5 VERIFICATION: passed.
- Integration 4/4 wirings WIRED (Fleet⇄Mapper, Companion⇄Fleet,
  CompareBuilder⇄Fleet+Mapper); E2E fleet→compare flow holds end-to-end; zero
  hard breaks (gsd-integration-checker).
- Tech-debt (non-blocking, expected from the 1046 drop): 1043 TagResolver seam
  (DASH-01/02) + Fleet.resolveLogical (FLEET-06) built+tested but unconsumed by
  any shipped flow; nyquist PARTIAL on 1041-1044 (discovery-only); pre-existing
  PerTag/ADHOC05 timer flake.

Report: .planning/v5.0-MILESTONE-AUDIT.md. Milestone shippable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant