Skip to content

Subsystem 4c: Smart Storage Monitors (#storagemonitors channel, pair/validate, live contents embed + Refresh/Rename)#27

Merged
HandyS11 merged 14 commits into
developfrom
feat/storage-monitors
Jun 26, 2026
Merged

Subsystem 4c: Smart Storage Monitors (#storagemonitors channel, pair/validate, live contents embed + Refresh/Rename)#27
HandyS11 merged 14 commits into
developfrom
feat/storage-monitors

Conversation

@HandyS11

Copy link
Copy Markdown
Owner

Third smart-device slice after 4a Smart Switches (#18) and 4b Smart Alarms (#23). Adds in-game Smart Storage Monitors: pair-in-game via FCM → a user-validated "Add it?" prompt in a per-server #storagemonitors channel → a per-monitor live contents embed (item names + quantities, Tool-Cupboard decay-protection status, slot count) with Refresh and Rename. Contents are primed on (re)connect and updated live on the socket's OnStorageMonitorTriggered broadcast.

Architecture

Mirrors the 4a Switches template. The one difference is the data shape — a storage monitor carries contents (StorageContentsSnapshot: capacity, decay-protection, item list), not a boolean on/off. Approach A: a storage-specific read seam (GetStorageMonitorInfoAsync) + bus event (StorageMonitorTriggeredEvent) sit beside the existing boolean device plumbing rather than overloading SmartDeviceTriggeredEvent. Storage monitors have no control surface (read-only), so the only interactions are Refresh, Rename, and the pairing Accept/Dismiss.

  • New RustPlusBot.Features.StorageMonitors project (renderer / coordinator / poster / relay / component module / hosted service / item-name resolver).
  • FCM OnStorageMonitorPairingPairingHandler third arm → StorageMonitorPairedEvent.
  • ConnectionSupervisor subscribes the socket trigger + primes persisted monitors on connect; IRustServerQuery.GetStorageContentsAsync backs Refresh.
  • SmartStorageMonitor entity + StorageMonitorStore + SmartStorageMonitors migration (FK→RustServer cascade). Contents are live-only, never persisted.
  • Per-server #storagemonitors channel spec + locator; shared .resx keys (EN/FR).
  • Bundled items.json (id→name, names only) for human-readable contents — recycle/upkeep-cost calc + alerting deferred to subsystem 6.

Verification

  • Build 0/0 under -warnaserror; jb ReformatAndReorder idempotent-clean.
  • 551 tests / 14 assemblies green (baseline 514, +37): new StorageMonitors 17, Connections 64→69, Pairing 29→30, Persistence 83→93, Workspace 60→64.
  • No EF model drift beyond the new SmartStorageMonitors migration.
  • Built subagent-driven (per-task TDD + spec/quality reviews) + final whole-branch review = no Critical/Important findings. Untested-by-design integration shims: RustPlusSocketSource storage methods + DiscordStorageMonitorChannelPoster.

🤖 Generated with Claude Code

HandyS11 and others added 14 commits June 26, 2026 19:35
…iredEvent

Add StorageMonitor switch arm in PairingHandler.HandleEntityAsync and
OnStorageMonitorPairing subscription in RustPlusFcmPairingSource,
mirroring the SmartAlarm pattern. Includes TDD test (30 total, +1).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…upport

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n, and tests (Task 6)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add StorageMonitorEmbedRenderer (pure), StorageMonitorComponentIds,
and StorageMonitorRenameModal; add 19 EN/FR localization keys (storage.*
+ channel.storagemonitors.name); bump parity test key count to 212.
Uses inline FormatRemaining helper — no Features.Commands reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add ServerStorageMonitors channel key (order 6), ChannelSpec (Interactive/PerServer),
IStorageMonitorChannelLocator interface, StorageMonitorChannelLocator (CachingChannelLocator),
DI registration, and full xUnit test coverage (spec-provider + locator cache tests).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add IStorageMonitorChannelPoster, DiscordStorageMonitorChannelPoster (untested
integration shim delegating to DiscordChannelMessenger), and
StorageMonitorPairingCoordinator (mirrors SwitchPairingCoordinator: posts an
"Add it?" prompt on paired event, persists via IStorageMonitorStore.AddAsync on
accept with contents:null render, race-guarded TryAccept/TryDismiss).
Five new tests cover: prompt posted for new monitor, already-managed ignored,
accept persists and returns true, accept on race returns false, dismiss without
pending returns false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rageContentsAsync

- Subscribe StorageMonitorTriggered in the connected window and fire-and-forget PublishStorageTriggerAsync
- Add PrimeDevicesAsync third arm: list IStorageMonitorStore, call PublishStoragePrimeAsync per entity (null → skip)
- Add IRustServerQuery.GetStorageContentsAsync; implement on ConnectionSupervisor via GetStorageMonitorInfoAsync
- Add IStorageMonitorStore + IAlarmStore registrations to AlarmPrimingTests and SwitchQueryTests harnesses
- Add FakeRustSocketSource.EnqueueStorageInfo for race-free priming test setup
- Add StorageMonitorPrimingTests: priming event, no-socket null, query-path, trigger relay (69 total, +4)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion status unreachable)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add StorageMonitorsHostedService (3 bus loops: paired, triggered,
connection-status), AddStorageMonitors() DI extension, IStorageMonitorStore
Persistence registration, Program.cs + Host csproj wiring, and registration test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ToFrozenDictionary throws on a duplicate key; two distinct string keys in
items.json can parse to the same int (NumberStyles.Integer allows a leading
sign/whitespace). In the static initializer that would surface as a
TypeInitializationException and hard-fault the feature on first use. Group by
the parsed id (last wins) so a future regenerated/hand-edited items.json
degrades instead of crashing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HandyS11 HandyS11 merged commit d2d57a1 into develop Jun 26, 2026
3 checks passed
@HandyS11 HandyS11 deleted the feat/storage-monitors branch June 26, 2026 20:10
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