Skip to content
MainsKeeper MainsKeeper Design review Fictional demonstration data Dataset: Demo fleet

Checkpoint 0.2.3 · Battery Assurance History filters · complete-design review · not final · source-only · browser review pending

MainsKeeper — complete design review

One product design: a browsable prototype of the web app (dark by default, light by switch) on a shared prototype state, a design system, onboarding and enrollment, network UPS setup, and a proposed CLI contract. Use this page to walk ten journeys, open every page and state, and check what is designed.

Read this first: no page in this round has been rendered or visually inspected, and there are no screenshots. Pages were written from source. Checks ran in Node only: the shared-logic script (203/203 at 0.1.10; 180/180 at 0.1.9; 169/169 at 0.1.8; 136/136 at 0.1.7), builders’ fake-DOM text checks and the CLI self-check. “Designed” below means “built in the prototype source and linked”, not “visually verified”. See the unverified visual checklist and the round-6 notes.

Checkpoint 0.2.0 · Battery Assurance · source-only review checkpoint

0.1.10 — exact monitoring-gap intervals

Browser review was not performed for this checkpoint. This environment does not permit opening, rendering or screenshotting the prototype. No browser journey, responsive width, theme, focus, dialog or overflow check has been run and no screenshot exists. Everything below was verified in Node from source.

What changed and what was verified

Route A — immediate off/on (30 s gap)

  1. Reset demo. network-source.html?collector=bw-pi-01 → NUT 192.168.1.20, UPS name deskups → credentials → test → Identify → Use.Expect: first readings at 15:22:27.
  2. collector-config.html?id=bw-pi-01 → UPS monitoring off → save → “Simulate check-in” → UPS monitoring on → save → “Simulate check-in”.Expect: gap 15:22:27–15:22:57, “No records — UPS monitoring disabled”, in the chart (line broken at both ends), power lane, history list and hourly view. 24 h availability: “30 s unknown”, not “No unknown time”. Range 15:22–15:23: coverage 50.0%. BW Rack UPS shows its own gap 15:22:26–15:22:57 (31 s).

Route B — two gaps 15 s apart

  1. As route A step 1, then off → check-in → advance 330 s → on → check-in → advance 15 s → off → check-in → advance 300 s → on → check-in. (The demo clock buttons advance in fixed steps; the 330 s / 15 s / 300 s sequence is the one the state checks drive through MK.store.advanceClock.)Expect: two gaps 15:22:27–15:28:27 and 15:28:42–15:34:12 in every view. The 15 s between them is on utility in the power lane with no chart point. 24 h unknown: exactly 11.5 min (“12 min unknown” after display rounding). Range 15:25–15:30: 4.75 min unknown, 0.25 min observed, coverage 5.0%. BW Rack UPS: 15:22:26–15:28:27 and 15:28:41–15:34:12.
  2. Record more readings, run another short cycle, reload.Expect: the first two gaps keep their boundaries; a third is added; all survive the reload.

Checkpoint 0.1.9 · source-only review checkpoint

0.1.9 — persistent monitoring gaps

Browser review was not performed for this checkpoint. This environment does not permit opening, rendering or screenshotting the prototype, so no browser journey, responsive width, theme, focus, dialog or overflow check has been run and no screenshot exists. Everything below was verified in Node from source.

What changed and what was verified

Route — two off/on cycles (bw-pi-01)

  1. Reset demo. network-source.html?collector=bw-pi-01 → NUT 192.168.1.20, UPS name deskups → credentials → test → Identify → Use.Expect: monitoring with first readings at 15:22:27.
  2. collector-config.html?id=bw-pi-01 → UPS monitoring off → save → “Simulate check-in” → “Advance demo clock 5 min” → UPS monitoring on → save → “Simulate check-in”.Expect: gap 15:22:27–15:27:57 on the UPS’s history, labelled “No records — UPS monitoring disabled”.
  3. “Advance demo clock 5 min”, then repeat the off → check-in → advance → on → check-in cycle.Expect: the first gap is unchanged at 15:22:27–15:27:57 and a second gap 15:33:27–15:38:57 appears. BW Rack UPS shows the same two periods (ending 15:27:57 and 15:38:57).
  4. Reload the page.Expect: both gaps unchanged.

Round 7 · checkpoint 0.1.8 · source-only correction pass

0.1.8 corrections — routes, steps and expected outcomes

Press Reset demo before each case. Browser QA was deferred to a separate browser-capable environment: none of the routes below has been opened in a browser for this checkpoint. Expected outcomes come from the shared selectors run in Node.

What was exercised (source / Node only)

A · UPS role on → off → on (bw-pi-01)

  1. Add and use a NUT source: network-source.html?collector=bw-pi-01 → NUT 192.168.1.20, UPS name deskups → credentials → test → Identify → Use.Expect: monitoring, first readings, “On schedule”.
  2. collector-config.html?id=bw-pi-01 → turn UPS monitoring off → save → review control “Simulate check-in”.Expect: roles Applied (connectivity only). Every UPS read by bw-pi-01 — the new one, BW Rack UPS and BW Office UPS — shows “Monitoring disabled · last known HH:MM”, not current.
  3. Review control “Advance demo clock 5 min” (any collector page), then open the UPS, fleet, sources and history.Expect: the last reading time and values do not move; source “Monitoring disabled — UPS role off”; power lane and charts show “No records — UPS monitoring disabled” from the last reading to now; availability counts it as unknown.
  4. Turn UPS monitoring back on → save.Expect: Pending — still “Monitoring disabled”; nothing resumes before the collector applies it.
  5. “Simulate check-in”.Expect: applied, then one new reading (“readings resumed: …” in the review output); status current again; the disabled span stays a gap between the last reading and the first new reading.

B · Deferred activation — SNMP 10.0.20.16 while bw-pi-01 is offline

  1. network-source.html?collector=bw-pi-01 → review control “Simulate disconnect”.Expect: bw-pi-01 not reporting.
  2. SNMP 10.0.20.16 → credentials Configured → Test read access.Expect: “Test queued — collector not reporting”, never success.
  3. Name it, then Save choice.Expect: “Configured — waiting for collector”, with the name kept.
  4. “Simulate collector reconnect”.Expect: queued test succeeds; the UPS is identified (APC Smart-UPS SMT1500 · AP9631 card, serial AS1811240377); monitoring does not start — “Identified — confirm possible duplicate”, “Possibly the same UPS as BW Rack UPS (serial match)”. No UPS asset is created and no charge is invented.
  5. “It is a separate UPS — start monitoring”.Expect: the UPS is created with your saved name and the identified model; readings are the endpoint’s reported values. A value the device does not report shows “Not reported”, never a default 100 %.

Immediate and deferred activation now pass the same prerequisites: successful read test → identification → duplicate reconciliation → confirmation when needed.

C · Copy and documentation

Round 6 · checkpoint 0.1.7

0.1.7 corrections — routes, steps and expected outcomes

Press Reset demo before each case. Cases (b) and the home-laptop journey both register the same identity, home-lan|nut|192.168.1.20|3493|rackups. Whichever you run second hits the hard-duplicate block, so reset between them. Cases (a) and (c) use broadwater-lan and do not collide with either. All rules come from shared logic (CONTRACT.md §18); pages only display them.

What was exercised (and what was not)

1 · Network UPS identity (a / b / c) and untrustworthy serials

Identity = namespace | protocol | host | port | NUT UPS name. A host alone is never a UPS identity. A trustworthy serial match at a different identity can only raise a question. It never blocks.

  1. (a) One server, two UPSs. network-source.html?collector=bw-pi-01 → NUT, host 192.168.1.20, UPS name rackups → save → Simulate credentials “Configured” → Test read access → Identify UPS → Use.Expect: identity broadwater-lan|nut|192.168.1.20|3493|rackups, CyberPower OR1500LCDRM1U, monitoring starts. Then “Add another network UPS” → same host, UPS name deskups: it registers as a second UPS (APC Back-UPS RS 1500).
  2. Untrustworthy serial. The deskups identify step shows serial 000000000000 flagged untrustworthy.Expect: the serial decides nothing. No soft or hard duplicate.
  3. Re-adding rackups on bw-pi-01 (same identity).Expect: blocked before any draft, “Already monitored by bw-pi-01 as <the name you gave rackups>”.
  4. (b) Same address, other network. Reset demo. network-source.html?collector=home-studio&state=calm → NUT 192.168.1.20 rackups.Expect: allowed, key home-lan|nut|192.168.1.20|3493|rackups (Eaton 5P 1550, a different physical UPS). Credential step for home-studio (macOS) shows sudo mainskeeper sources auth … and the macOS Keychain.
  5. (c) Hard duplicate. Reset demo. bw-pi-01 → SNMP 10.0.20.15.Expect: blocked before any draft is created: “Already monitored by bw-pi-01 as BW Office UPS”.
  6. Soft duplicate. Same page → SNMP 10.0.20.16 → credentials → test → Identify UPS.Expect: confirm step “Possibly the same UPS as BW Rack UPS (serial match) — confirm” with continue or cancel. Continue → Use creates a separate UPS. Cancel removes the draft and adds nothing.

Deviations: Identify is required before Use. The soft-duplicate confirmation is page-local, so reloading the page asks again. CLI: add rackups · add deskups · duplicate (exit 1) · serial match (exit 10).

2 · Collector availability and UPS-role gating — home-laptop journey

  1. network-source.html?collector=home-laptophome-laptop is a macOS workstation, connectivity only, asleep since 13:05. Add NUT 192.168.1.20 rackups. Saving works while it sleeps.
  2. Simulate credentials “Configured” → Test read access.Expect: badge “Test queued — collector not reporting”, with “home-laptop is asleep — test will run when it reports”. Neither failure nor success.
  3. collector-config.html?id=home-laptopTurn UPS monitoring on and save. Expect: “pending — desired on, applies at the next check-in”.
  4. Review control “Simulate collector reconnect”.Expect: home-laptop reporting again 15:22:30. The check-in applies the role and the 120 s reporting interval, and the queued test runs: success.
  5. Back on network-source (&source=src-home-laptop-nut-192-168-1-20-rackups resumes it) → Identify UPS → Use.Expect: first readings 15:22:57 (after the reconnect), reading “On schedule · read N s ago · every 120 s”.
  6. collector.html?id=home-laptopSame collector and UPS. “Simulate disconnect (sleep)” → the source reads “Configured — waiting for collector”.
  7. Role off variant. Reset demo. Simulate reconnect first, leave UPS monitoring off, then test → identify → Use.Expect: saved as “Configured — waiting for UPS role”, with a link to Configuration. No readings until the role applies.

Account connectivity-only path (onboarding): Reset demo → welcome.html?scenario=empty → step 2 site “Cabin” (namespace cabin-lan) → step 3 Windows, Connectivity only → approve PLHV-GWCS (den-win-01). Step 7 expects “Enable UPS role later” → configuration. The network branch lists the endpoints reachable in cabin-lan. A network source saved there reads “Configured — waiting for UPS role” until the role applies at a check-in. The macOS studio-mbp equivalent (code WDJB-MJHT) is the store-checked path. CLI: add on studio-mbp · local test, cloud unreachable · use → waiting for UPS role (exit 0) · roles pending · CLI gating.

Deviations: there is no queued-test CLI screen, because the CLI runs on the collector. A collector given the UPS role later shows “No USB UPS found”, and there is no re-discover action.

3 · Witness down — no data after 15:04:12

  1. history.html?site=home&ups=office-desk-ups&scenario=witness-down&from=14:30&to=15:22Expect: power lane on utility to 15:04:12, then “No records — collector not reporting” to 15:22. Charge and load end at 15:04. Coverage 65.8% (17.8 min unknown, shown as “18 min unknown”). The site reachability badge is separate, and Office Desk UPS power is never derived from home-esp32-garage.
  2. …&range=7dExpect: the 15:00 hour bucket covers the 22 minutes elapsed by 15:22 — 4.2 min observed on utility (15:00–15:04:12) and 17.8 min unknown, labelled “No records — collector not reporting”; coverage 19.1%. (The same 4.2 of 22 / 17.8 unknown applies to a 15:00–15:22 range; the 14:30–15:22 range above is 52 min with 17.8 unknown.)
  3. ups.html?id=office-desk-ups&scenario=witness-downExpect: “Stale · last known 15:04 · read 17 min ago · every 30 s”. 24 h availability counts the gap as unknown, not as up or down.

4 · History range 15:00–15:22 (point / interval rule)

  1. history.html?site=duplex&from=15:00&to=15:22Expect: no yesterday episode (INC-0409) and no 14:03–14:30 episode. The replay receipt event (e-dx-replay) appears at 15:17:40, its receive time.
  2. history.html?from=15:00&to=15:22Expect: only the ongoing BW Rack episode (ep-bw-current). No Home 06:42 event.

Rule: a point is included if from ≤ time ≤ to. An interval is included if start ≤ to and end ≥ from. Its end is the end time, else the last record, else now only while ongoing. Deviations: receipt events sit at their receive time. The default availability period changed. “View this episode” pads the range by 25 min.

5 · Unknown evidence propagates everywhere

6 · Freshness by cadence (30 s vs 600 s)

I = reporting interval, g = max(30 s, I/2). On schedule ≤ I + g · Overdue ≤ 2I + g · Stale · last known beyond that, or when contact is lost. 30 s → ≤ 60 s / ≤ 90 s / > 90 s. 600 s → ≤ 15 min / ≤ 25 min / > 25 min. Heartbeat is judged separately.

No fixture UPS is currently Overdue. Use the legend to check that wording.

7 · Platform-specific instructions

Deviation: OS install lines (package names and installers) are page-local; every command, path, secret store and log location comes from MK.cli.platform.

Regression routes (should behave as in 0.1.6)

Previous round · 0.1.6 (corrected where 0.1.7 changed it)

What changed in 0.1.6

One shared prototype state

Journeys completed

Corrected semantics

One configuration and CLI contract

a · Start here

Start here

Reset demo

The prototype remembers what you do in this browser: acknowledgments, the INC-0412 walkthrough step, configuration changes, network sources and the first-run account. Opening some review links changes this state too. For example, incident.html?id=INC-0412&step=ack acknowledges INC-0412.

Reset demo is in the review bar at the top of every product page and in this page’s header. It asks you to confirm, clears the store and returns to the pristine demo fleet. Reset before each journey to get the results described below.

Dataset switch

The review bar shows Dataset: Demo fleet | Your account (first run).

Link parameters

Links carry only state, scenario, theme and motion. Entity parameters (id, site, collector, ups, focus) and page states (auth, enroll) are never carried to the next page. An unknown id shows “not found”:

Design system, CLI and brand

b · Journeys

Required journeys, step by step

Reset demo before each journey. Steps that change the shared state are marked “changes state”. The pages after them reflect the change.

1 · Empty account → site → install → approval → roles & sources → first readings → fleet → same collector and UPS

  1. Sign in with GitHub (Cloudflare Access)
  2. CallbackAn empty account continues to the welcome flow
  3. Empty fleetNo sites, collectors or UPSs; points to setup
  4. Welcome · step 1Progress is kept in the store; ?step= only chooses the step shown. Walk the steps in order.
  5. Step 2 · create the siteName “Home”, time zone America/Los_Angeles. Changes state: the dataset switches to “Your account (first run)”.
  6. Step 3 · choose the collectorRaspberry Pi · both roles (UPS and Connectivity) · site witness
  7. Step 4 · installsudo apt install ./mainskeeper_0.4.2_arm64.deb · detail: install.html?platform=pi
  8. Step 5 · start enrollment on the devicesudo mainskeeper enroll --headless shows code BKTN-XRWP
  9. Step 6 · confirm BKTN-XRWP in the browserApprove changes state: creates collector garage-pi-02 and its discovered USB source
  10. Approved
  11. Step 7 · roles and sourcesUSB branch: “Use this UPS” on the CyberPower CP1500PFCLCD (changes state). Network branch: network-source.html?collector=garage-pi-02&from=welcome (returns to step 8)
  12. Step 8 · first readings100% · est. 38 min · load 18% · 121 V · temperature Unsupported; reading “On schedule · read N s ago · every 30 s” (only after garage-pi-02 reports with the UPS role applied)
  13. Go to fleet · your accountNo state/scenario parameter, so the account dataset shows: “1 of 1 expected collectors reporting”
  14. Same collector · garage-pi-02
  15. Same UPS · CyberPower CP1500PFCLCDAlso: equipment.html?cond=waiting (before the first reading) · sources · configuration

One enrollment code per platform per run. Use Reset demo to enroll the same device again.

2 · On-battery alert → investigation → acknowledgment → utility restoration

  1. Fleet: Broadwater 1 of 2 UPSs on battery
  2. INC-0412 · alertFirst evidence 15:09:00 · Grace period started 15:09:00 · Declared 15:10:00 · email 15:10:04 · push 15:10:05
  3. Investigate the evidence
  4. BW Rack UPS detailUPS estimate vs observed duration; freshness legend
  5. Acknowledge (changes state)jp 15:22:00 “Utility outage on circuit A — checking with the building”; the incident stays open
  6. Incidents list shows the same acknowledgment
  7. Utility restored (changes state)Clock 15:32 · restored 15:31:00 · 22 min observed on battery · recovered, still open until stable
  8. Resolved (changes state)Clock 15:37 · resolved 15:36:00 after 5 min stable
  9. Same state everywhere: fleet · site · equipment · UPS · incidents · history · notifications

Low runtime: rule below 10 min. No fixture UPS crosses it. The round-4 ?runtime=low demo was removed.

3 · Site contact lost → offline recording → recovery → historical backfill (Duplex)

  1. INC-0411 timestampsFirst evidence 13:41:00 (local probe, received 15:02:06) · Grace period started 13:44:00 · Declared 13:49:00 · Acknowledged jp 13:52 · Recovered 15:02:06 · Resolved 15:07:06
  2. Duplex site · local probe evidence
  3. duplex-pi-probe bufferJourney steps: 1 · 2 · 3 · 4 · offline · recording · replay + live
  4. duplex-pi-probe · backlog draining
  5. History 13:30–15:30Replay tagging (> 2 min late while reconnecting), the 14:30–14:52 gap, and the backfill alerting note
  6. Episode: at least 27 minutes on battery; endpoint unknown

Backfill policy: “Historical backfill updates history at observation time and does not generate duplicate or obsolete notifications. Fresh evidence of an active condition remains alertable while a backlog drains. Ordinary transmission latency does not make a reading historical replay.”

6 · Network UPS setup — outcomes, identity and gating

Open network-source.html?collector=bw-pi-01 (demo fleet). Steps: Protocol → Endpoint → Local credentials → Test → Identify → Duplicate check → Use. Credentials must show Configured before the test. The browser never takes a secret. You run the platform’s credential command on the collector (for example sudo mainskeeper sources auth <source> on Linux and macOS, or an Administrator PowerShell on Windows; ESP32 uses its setup page). In the prototype, use the review-only “Simulate” control. A collector that is not reporting queues the test (see correction 2). &source=<id> resumes a draft.

  1. Success · Remote NUT server 192.168.1.20, UPS name rackups (identity broadwater-lan|nut|192.168.1.20|3493|rackups)Also SNMP v3 192.168.1.25 (APC SRT 2200). deskups on the same server is a second UPS (correction 1).
  2. Unreachable endpoint · NUT 192.168.1.99
  3. Rejected credentials · NUT 192.168.1.22, UPS name office (or choose “Entered a wrong secret” in the simulator)
  4. Unsupported capability (partial) · SNMP v2c 192.168.1.26
  5. Already monitored · SNMP 10.0.20.15 (same identity broadwater-lan|snmp|10.0.20.15|161|) → blocked before any draft: “Already monitored by bw-pi-01 as BW Office UPS”. No second asset is created. The same address in another namespace is a different identity.
  6. Possibly the same UPS · SNMP 10.0.20.16 → after Identify: “Possibly the same UPS as BW Rack UPS (serial match) — confirm”. Continue or cancel; never a block.

Onboarding branch: journey 1, step 7.

7 · Witness down vs site contact lost vs locally observed connectivity failure

  1. Fleet · witness downHome: “Site still reachable: home-esp32-garage reporting”
  2. Home site · witness down
  3. INC-0415 · Collector not reporting — home-studio
  4. home-studio · last contact 15:04:12
  5. Collectors · witness down
  6. Relationships · Home, home-studio focused
  7. Shop · site contact lost (all witnesses silent, cause unknown)INC-0413 · relationships focus
  8. Duplex · locally observed connectivity failure (probe evidence 13:41)INC-0411

Compare the two silence panels: INC-0413 and INC-0411.

10 · Mobile incident detail · acknowledge

  1. INC-0413 on a phone (≤ 767 px)Fixed action bar: check → evidence → acknowledge (bottom sheet)
  2. Acknowledge with a note (changes state). Then open incidents and the fleet: they show the same record.
  3. INC-0412 on a phone

The action bar is in source only. It has not been checked at 375 px or on a device.

c · Configuration ownership

Configuration ownership (one table for web and CLI)

The table below comes from MK.config.contract when the shared scripts load; otherwise it shows a summary. The same rows are used by collector-config.html?id=bw-pi-01 and the CLI configuration-ownership section. Override example on bw-pi-01: “Effective: 15 s (local override) · Desired in dashboard: 30 s”. Rows are platform-neutral here; each collector’s configuration page resolves paths, elevation and secret store for its own platform (MK.config.contractFor). Other states: failed (shop-esp32-net, max 3 targets) · pending (home-laptop asleep).

Configuration ownership: setting, owner, edit location, effective value source, application behaviour
SettingOwnerEdit locationEffective value sourceApplication behaviour
Site, display name, roles, expected availability, heartbeat interval, reporting interval, connectivity targetsDashboardDashboard › Collectors › collector › ConfigurationDashboard (heartbeat: unless a local override is set)Desired → pending → applied on next check-in, or failed with a reason
Alert rules & notification thresholdsDashboard (server)Dashboard › Settings › Alert rulesServerServer-side, immediate
NUT credentials, SNMP v3 secrets, SNMP communityLocal secretOn the collector: mainskeeper sources auth <source>, elevated per platform (ESP32: its local setup page)The platform’s secret store (macOS Keychain · /etc/mainskeeper/secrets.d · Windows DPAPI · on the device)Dashboard shows only Configured / Missing / Rejected + last test time
Storage path, disk cap, retention target, log level, log pathLocalThe platform’s collector.toml (macOS /Library/Application Support/MainsKeeper · Linux /etc/mainskeeper · Windows C:\ProgramData\MainsKeeper) or an elevated mainskeeper config setCollectorRead-only in the dashboard, “Set locally”
USB host, flash ring, UPS classes, max targetsCapabilityNot editableHardwareShown as capabilities; ESP32 values illustrative

d · Product map

Product map

Every page, what it is for, and direct links to its states. Links without state= or scenario= open the active dataset (the demo incident fixture unless you switched to your account). Add &theme=light to any link for the light theme.

Fleet & sites

index.html — Fleet overviewLanding page. Sites with reachability, open incidents, recoveries, history sync; counts derived from the merged data. IncidentCalmLoadingEmpty accountUPS comms lostCollector revokedWitness down
site.html — Site detailUPS cards by circuit, collectors with contact state, reachability, connectivity, history, availability with coverage. Broadwater (on battery)HomeHome · witness downShop (site contact lost)Duplex (local probe)Not found
site-setup.html — Site settingsName and time zone edits; witness changes go through the desired → pending lifecycle; some expectations read-only. Edit HomeNew site

Equipment & UPS

Incidents & notifications

incidents.html — IncidentsOpen, recovered and resolved incidents; acknowledgments from the shared store. OpenRecoveredResolvedNotification failuresCalmEmpty filter result

Collectors, coverage & sources

collectors.html — CollectorsContact state, roles, site witnesses vs workstations, buffers, secrets status. AllWitness downUPS role · contact lostShop
network-source.html — Add network UPS (NUT / SNMP)Protocol → endpoint (identity and hard-duplicate check) → local credentials (status only) → test (queued while the collector is not reporting) → identify → serial-match confirm → use (monitoring, or “Configured — waiting …”). bw-pi-01home-studio (home-lan)home-laptop (asleep · queued test)ESP32 (not applicable)RevokedNot found
collector-config.html — Collector configurationOwnership table, desired/pending/applied/failed, override, secrets status, local read-only, capabilities. bw-pi-01 (override)shop-esp32-net (failed)home-laptop (pending)home-esp32-garage (ESP32)bw-pi-01 revoked

Onboarding & enrollment

sign-in.html — Sign-inCloudflare Access with GitHub; ?auth= states (never ?state=). Signed outRedirectingCallbackAccess deniedSession expiredError
welcome.html — First run (8 steps)Empty account to first readings, in the account dataset. See journey 1.
install.html — Install a collectorPer-platform install with elevation. Installers are design targets. macOSWindowsFedoraRaspberry PiESP32 probeUnknown platform
enroll.html — EnrollmentShort user code, browser approval. Expired, denied and offline are display-only. WaitingConfirmExpiredDeniedOfflineRevokedRe-enrollUnknown code

e · State coverage

State-coverage checklist

Designed = built in prototype source and linked. Partial = built with a stated gap. Not designed = absent. No row has been visually verified. Totals: 51 Designed · 8 Partial · 1 Not designed.

State coverage: state, where to see it, status, notes
StateWhereStatusNotes
First use · no devicesFleet emptyWelcomeEquipment emptyDesignedAccount dataset, empty until you create a site.
First run to your own fleet (same collector and UPS)Welcomegarage-pi-02CyberPower UPSDesignedCreated entities live in the store. These ids exist only after you walk journey 1. Before that, they show “not found”.
New collector after approval (sources, configuration)SourcesConfigurationDesignedNo silent fallback to bw-pi-01.
Dataset switch · Reset demoReview bar on any pageDesignedReset asks for confirmation and removes entity params that no longer exist.
Not-found statesSiteUPSIncidentSourcesSyncRelationshipsDesignedNever substitutes another entity.
Parameter contractsign-in ?auth=?focus=DesignedCarried: state, scenario, theme, motion. Not carried: auth, enroll, id, site, collector, ups, focus.
Initial loadingFleetHistoryPartialSkeletons only on fleet and history.
HealthyFleet calmHomeSync healthyDesigned
Partial capabilities (unsupported readings)BW Office UPSSourceEquipmentDesigned“Unsupported” / “Not reported”, never 0.
On battery (notify after 60 s)BW Rack UPSINC-0412RuleDesignedHome 38 s episode: “Below 60 s delay — event only”.
Low runtimeRuleBW Rack UPSPartialThe rule is designed. No fixture UPS falls below 10 min, and the round-4 page-local demo was removed.
Utility restorationINC-0412 restoredResolvedUnknown restore timeDesignedStore-driven (clock 15:32 / 15:37); every page agrees.
AcknowledgedINC-0412 ackIncidentsINC-0411DesignedOne record per incident in the shared store.
Incident timestamps (six labels)INC-0413INC-0411DesignedResolved = recovered + 5 min stable (decision to confirm).
Witness down (collector failure, site still reachable)HomeINC-0415home-studioDesigned“Site still reachable: home-esp32-garage reporting”.
Site contact lost vs locally observed connectivity failureShop · cause unknownDuplex · local probeDesigned
UPS comms lost while the collector reportsGarage UPSINC-0414Designed
Workstation asleep (normal)home-laptopbw-desk-wsDesignedExcluded from expected collector counts.
Freshness: On schedule · Overdue · Stale (per cadence)Shop Bench UPSEquipment staleDesignedRelative to the reporting interval (30 s: ≤ 60 s · ≤ 90 s · > 90 s; 600 s: ≤ 15 · ≤ 25 · > 25 min), with absolute age and a legend. No fixture UPS is currently Overdue.
Recovery with uploads drainingINC-0411SyncDesigned
Historical backfill and replayDuplex historyReplay + liveDesignedExact policy text; replay = > 2 min late while reconnecting.
Storage limitsPressureRecords lostCLIDesignedESP32 values illustrative.
History gaps · uncertain timestampsDuplex 14:30–14:52DesignedHatched “No records”; ±90 s until clock sync.
History filtered by UPS (site context separate)BW Office UPSDesigned
Charts: 7-day hourly aggregates with coverageShop 7 dUPS 7 dDesignedCustom ranges are limited to the last 7 days. The all-sites 7 d SVG is heavy.
Chart range selection on touchHistoryPartialDrag selection off on touch; use the range form.
Network UPS setup (outcomes + identity)network-source.htmlDesignedSuccess, unreachable, rejected, unsupported, already monitored (same identity), serial-match confirm.
Network identity: same host, different UPS / namespacebw-pi-01 rackups + deskupshome-studio home-lanDesignedCorrection 1 (a)/(b). Untrustworthy serial 000000000000 decides nothing.
Test queued (collector not reporting)home-laptopDesigned“Test queued — collector not reporting”; runs at the next check-in. No CLI screen (CLI runs on the collector).
Configured — waiting for UPS role / for collectorhome-laptop configurationhome-laptopCLIDesignedStore-enforced; readings only after the role applies and the collector reports.
Unknown evidence after a witness goes silentHistoryUPSDesignedNothing after 15:04:12; unknown in lanes, charts, aggregates, coverage and availability.
Awaiting next report (600 s cadence)BW Office UPSDesigned“Not reported yet — next report due by 15:25:00 (every 600 s)”.
Platform instructions (Windows, ESP32, macOS, Pi/Fedora)WindowsESP32macOSCLIDesignedFrom MK.cli.platform; OS install lines page-local.
Configuration: pending · applied · failed · overridePendingFailedOverrideDesignedApplied on next page load (simulated check-in).
Secrets status (Configured · Missing · Rejected)bw-pi-01RejectedDesignedAn abandoned network draft can make the summary read Missing until it is removed.
Enrollment: waiting · confirm · approved · expired · denied · offlineWaitingExpiredDeniedOfflinePartialExpired, denied and offline are display-only; they don’t change the store.
Enrollment: revoked · re-enrollRevokedRe-enrollDesignedenroll --reenroll.
Auth expirationSession expiredDesigned
Sign-in statesSigned outRedirectingCallbackDeniedErrorDesigned?auth= no longer collides with the review bar.
Revoked credentialsCollectorCLI doctorJSON errorDesigned
MaintenanceWindowsNew · HomeSuppressedDesigned
Recovery ≠ acknowledged ≠ resolvedRecovered tabResolved tabDesigned
Empty resultsIncidentsHistoryDesigned
Notification failureFailedRetryingIncidentsDesigned
Configuration validationAlert ruleMaintenanceSiteRangeDesigned
Source casesLiveNone foundComms failingDesignedRenamed cases: live, credentials-rejected, unreachable (old names still work).
Sync cases + journeyCasesJourneyDesigned
Relationships and coverage (site filter, focus)Focus ShopMissingDesigned
ESP32 values labelled illustrativeshop-esp32-nethome-esp32-garageDesigned“Illustrative — validated board/UPS list pending”.
Equipment tableEquipmentPartialStore integration exercised only with a stub harness.
Mobile alert-acknowledgeINC-0413 at ≤ 767 pxPartialAction bar in source; not checked at 375 px. Dense list rows at 375 px are a risk.
Tablet layout (768–1023)Any pagePartialNot reviewed separately.
Dark / light themesDarkLightGalleryPartialContrast calculated, not visually checked.
Calm stateIncidentsCollectorsDesigned
Availability with unknown and maintenanceHomeHistoryDesigned
Keyboard (shortcuts, Esc, focus)Equipment inspectorDesignedIn source; not tested with assistive technology.
CLI dark · light · NO_COLOR at 80 / 12080 dark120 lightNO_COLORDesigned
CLI grammar, elevation, exit codesElevationExit codesUnelevatedDesignedstatus exits 0 when the query succeeds.
CLI JSONSchemastatusqueueDesigned
Rendered screenshots / visual inspectionVisual checklistNot designedNot produced. Rendering is not permitted in this environment.

f · Visual checklist · UNVERIFIED

UNVERIFIED visual checklist

Every item below is “Not verified (rendering not permitted in this environment)”. Nothing has been opened in a browser or on a device. Use this as the checklist for the first visual review: open each journey’s routes at each width in both themes and record defects.

Visual checklist: journey by viewport width and theme. All cells not verified.
Journey375 px · dark375 px · light768 px · dark768 px · light1024 px · dark1024 px · light1440 px · dark1440 px · light
1 · Empty account → first readings → fleetNot verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)
2 · On-battery → acknowledgment → restorationNot verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)
3 · Contact lost → backfill (Duplex)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)
4 · UPS source stops, collector onlineNot verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)
5 · Credential revoked → re-enrollmentNot verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)
6 · Network UPS setup (5 outcomes)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)
7 · Witness down vs contact lost vs local probeNot verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)
8 · Workstation sleepNot verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)
9 · Equipment-filtered history · 7 dNot verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)
0.1.7 corrections (identity, gating, witness-down, ranges, freshness, platforms)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)
10 · Mobile incident acknowledgeNot verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)Not verified(rendering not permitted in this environment)

Cross-cutting checks

Cross-cutting visual and interaction checks, all not verified
CheckWhat to look forStatus
Keyboard navigationTab order, skip link, shortcuts, review bar, dataset switch, Reset demoNot verified(rendering not permitted in this environment)
FocusVisible focus ring in both themes; focus returns after dialogs, inspector and bottom sheetNot verified(rendering not permitted in this environment)
OverflowNo page-level horizontal scroll at 375/768/1024/1440; tables scroll inside their wrappers; long ids wrapNot verified(rendering not permitted in this environment)
Chart labelsAxis and band labels legible; “No records” hatch, coverage bands, Site context; 7 d aggregates at 375 pxNot verified(rendering not permitted in this environment)
DialogsReset demo, acknowledge, network hard-duplicate card and serial-match confirm; Esc closes; focus trapped and restoredNot verified(rendering not permitted in this environment)
FormsSite, network source endpoint, alert rule, maintenance; validation messages next to fields; 44 px targets on mobileNot verified(rendering not permitted in this environment)
Terminal readabilityOnboarding command blocks and CLI mockups at 80/120 columns in dark, light and NO_COLORNot verified(rendering not permitted in this environment)
Click and submit wiringEvery 0.1.7 button (Simulate reconnect/check-in/disconnect, Test, Identify, Use, confirm/cancel, role switch save) does what correction cards 1–7 describeNot verified(rendering not permitted in this environment)
Reduced motionNo animation with prefers-reduced-motionNot verified(rendering not permitted in this environment)
Contrast in placeStatus colours and muted text on real surfaces in both themesNot verified(rendering not permitted in this environment)

g · Screenshots

Requested screenshots

None captured, in any round: rendering is not permitted in this environment. Exact routes to capture are below. Reset demo first, and walk journey 1 before the first-run shot.

Requested screenshots, route to capture and status
ScreenshotRoute to captureStatus
Fleet overviewapp/index.html?state=incidentlightNot captured
First-run enrollmentapp/enroll.html?scenario=empty&enroll=confirm&code=BKTN-XRWP&from=welcome(after welcome steps 1–5)Not captured
Network source setupapp/network-source.html?collector=bw-pi-01(NUT 192.168.1.20 · rackups; also the four failure outcomes)Not captured
Incident acknowledgment and recoveryapp/incident.html?id=INC-0412&step=ack…&step=restoredNot captured
Equipment-filtered historyapp/history.html?site=broadwater&ups=bw-office-ups&range=24h7 dNot captured
Mobile incident detailapp/incident.html?id=INC-0413(375 px viewport)Not captured
Network identity · hard duplicate and serial-match confirmapp/network-source.html?collector=bw-pi-01(SNMP 10.0.20.15 blocked; 10.0.20.16 confirm)Not captured
Queued test and waiting statesapp/network-source.html?collector=home-laptopconfiguration (pending)Not captured
Witness-down history (unknown after 15:04:12)app/history.html?site=home&ups=office-desk-ups&scenario=witness-down&from=14:30&to=15:22Not captured
Freshness at 600 s cadenceapp/ups.html?id=bw-office-upsNot captured
Windows and ESP32 instructionsinstall · Windowsinstall · ESP32Not captured
CLI status and enrollmentcli/index.html?w=80#g-status#g-enrollstatus-bw.80.txtenroll-headless.80.txtNot captured

h · CLI examples

CLI examples by category (151 files)

Generated from one source (cli/cli-screens.js). Added in 0.1.7 (15 screens × 80/120 col = 30 files): config-set-windows-unelevated, config-validate-macos, config-validate-windows, help-esp32, logs-macos, logs-windows, roles-show-pending, sources-add-deskups, sources-add-rackups, sources-add-studio, sources-local-access-fedora, sources-local-access-macos, sources-test-offline, sources-test-serial-match, sources-use-waiting. Renamed in 0.1.6: error-credential-revoked.json → error-enrollment-revoked.json; sources-auth.* → sources-access-rejected.*; roles-perm.* → sources-test-unelevated.*; sources-partial.* → sources-show.*. All files are in the checkpoint archive.

sources (46)

JSON errors (1)

i · Design system

Design-system summary

Colour roles (current theme)

Ground--mk-bg · #101418 / #F4F6F8
Surface--mk-surface-1
Text--mk-text · #E9EEF2 / #101418
Mint — live, observed, OK, links#7FE0C4 / #0B7358
Sodium — brand, selection, focus only#FF9F43 / #A34D00
Critical — on battery#FF6B6B / #C02F2F
Warning — contact lost#E8D85C / #6E6100
Unknown, last known, unsupported#A3B0BC / #4F5C69
Connectivity — local probe evidence#5CB8F5 / #1D66A8
Sync — pending, replayed#A99CF7 / #5747C2
Maintenance#B4BFCA / #56626E
“Powers” lines#C5CFD8 / #56626E

Dark is the default; light uses [data-theme="light"]. Every text role is ≥4.5:1 on bg, surface-1 and surface-2 in both themes, calculated in CONTRACT §3.2. Warning yellow differs from sodium by 24–25° of hue and uses a different glyph. Type: Sora (UI), DM Mono with tabular numerals (data). Body 16 px, dense cells 14 px, minimum 12 px.

Status glyph conventions

Status is always glyph + word. Colour supports the meaning but never carries it alone.

On battery
Critical. Battery glyph.
On utility
OK. Filled circle.
Contact lost
Warning. Dashed circle with “?”. Server-observed, cause unknown.
Connectivity failure
Connectivity blue. Bars with ×. Confirmed by local probe.
UPS comms lost
Warning. The collector still reports.
Last known
Dashed hollow circle, always with time and age. Stale beyond 2 × the reporting interval + allowance, or when contact is lost.
Unsupported
Hatched dash. Never 0.
Received late · replayed
Sync violet. Received more than 2 min after observation while reconnecting. Backfill does not send duplicate or obsolete notifications.
Recovered
Outlined check.
Maintenance
Wrench, hatched band.
Not acknowledged
Neutral, dashed. Acknowledgment changes nothing on equipment.
Selected
Sodium ring plus tint. Never a status.

Chart conventions

Measured
Solid line, mint.
Estimate
Dashed line, labelled “est.” (UPS estimate).
Received late
Hollow marker plus received time.
No records
Hatched band. Never interpolated, never drawn as zero.
History arriving
Periwinkle hatch: records exist on the collector but are not yet uploaded.
Uncertain
Bracket with ±seconds.
Maintenance / battery
Background bands.
Live edge
“Now” marker at 15:22.

Coverage model

Directly monitored
A collector reads it now.
Relationship recorded — no direct monitoring expected
For example, a sleeping workstation with no agent behind a monitored UPS.
Measurement unsupported
The UPS or source cannot report this value.
Expected collector/source missing
Something that should report is not reporting.

Links are reads (collector → UPS), powers (UPS → equipment) and checks (collector → target). All are marked “configured”, not measured.

Wording glossary

Contact lost — cause unknown
Server stopped hearing; no local evidence.
Connectivity failure confirmed by local probe
Probe records show targets failing.
On schedule · Overdue · Stale · last known 15:10
Relative to the reporting interval I with allowance g = max(30 s, I/2): ≤ I + g · ≤ 2I + g · beyond. Always with “read N ago · every I”.
No records — collector not reporting
Unknown, never filled, joined or counted as up or down.
Configured — waiting for UPS role / for collector
A saved network source that cannot be read yet.
Possibly the same UPS as … (serial match) — confirm
A trustworthy serial match at a different identity. A question, never a block.
Unsupported / Not reported
Cannot report / supported but missing.
UPS estimate (est.)
A prediction, not “runtime remaining”.
Observed on battery
Measured between observed transitions.
At least 27 minutes on battery; endpoint unknown
No observed end.
Received late · replayed
Received more than 2 min after observation while the collector reconnected from its buffer. Normal latency is not replay.
History arriving / No records / No records received
Pending upload / none exist / nothing since last contact.
Site witness
Always-on collector that carries site expectations.
Asleep (ordinary workstation) — no incident
A workstation that is not a witness; excluded from expected counts.
Site still reachable: home-esp32-garage reporting
A witness is down but another collector at the site still reports.
First evidence · Grace period started · Declared · Acknowledged · Recovered · Resolved
Incident timestamps. Resolved = recovered and stable for 5 min.

Avoid “offline” for contact lost, “down”, “battery health” for charge, “0%” for missing data, and any control verbs.

Checkpoint 0.2.3 · Battery Assurance History filters

Battery Assurance — review scenarios

Press Reset demo before each case. Browser verification and screenshots skipped by user request. Visual and browser-interaction acceptance remain unverified. Outcomes below come from shared selectors in Node (state-checks.js groups 10–12 · 294 passed at 0.2.3; groups 10–11 · 277 passed at 0.2.2 remain historical).

0.2.3 fixes: History "all" site/UPS normalization for manual records; date-only range uses day-interval overlap (noon is not a known clock time); exact timestamps keep the point rule.

0.2.2 fixes (historical): historical association / predates-installation; investigating stays counted; resolution evidence validation + observation text; policy true-patch; awaitingEvidence from composed state; date-only display; standalone History records.

Monitoring only: no control starts a UPS self-test, transfer test, shutdown, reboot, or outlet switch. Current power/charge stay separate from battery-operation evidence, policy age, and unresolved findings.

  1. ups.html?id=bw-office-ups&scenario=assurance-no-evidenceFull charge on utility, no battery-operation evidence. Expect: “No battery-operation evidence recorded.” Not Verified/Certified.
  2. …scenario=assurance-self-testUPS-reported PASS only; test date unknown. Polling must not refresh age.
  3. …duplex-utility-ups&scenario=assurance-observedBattery operation under load ending in utility restoration (17m · ~29% load).
  4. …scenario=assurance-unknown-end · historyTelemetry ended during battery operation; outcome unknown. Collector silence ≠ failure.
  5. …shop-bench-ups&scenario=assurance-transfer-fail · equipment filterOperator-reported transfer failure unresolved while utility is back. Resolve only with documented evidence.
  6. …scenario=assurance-overdue · assurance reminder ruleEvidence overdue under 60-day policy. Reminder ≠ device failure.
  7. …office-desk-ups&scenario=assurance-replacedBattery replacement recorded; new battery awaiting evidence. Prior successes do not qualify.

Also on the default incident fixture: BW Rack live battery operation · Garage Unsupported self-test · BW Office undated cached PASS · fleet Assurance pulse · CLI assurance excerpt.

Workflow: Failure recorded → investigation → validation pending → explicit resolution with documented support (evidence ids and/or authored observation). Empty resolutions are rejected. Utility restore / ack / snooze / replacement do not auto-resolve. Resolved findings remain listed.

j · Scenario

Scenario summary (from the fixture)

Now = Wed 30 Sep 2026 15:22:00 PDT. Fictional demonstration data. The counts below are read from the demo incident fixture (MK.select.fleetCounts(MK.data('incident','default'))) when scripts load, whatever dataset is active. Store changes such as acknowledgments still apply.

4 of 6expected collectors reporting · 1 workstation asleep
5 of 6UPSs communicating
1UPS on battery
2open incidents (1 critical, 1 warning; 0 acknowledged)
1recovery today

Two open incidents + one recent recovery

Site summaries

Broadwater
1 of 2 UPSs on battery · 1 on utility (separate circuit)
Home
Healthy
Shop
Contact lost · last known 15:10
Duplex
Recovered 15:02 · history arriving (1,248 pending)

Duplex 14:30–14:52

13:41
Local probe: all 3 targets fail (confirmed). Received 15:02:06.
13:44
Server: contact lost, cause unknown.
14:03
UPS on battery, recorded locally. Received 15:17:40 · replayed (historical backfill, no duplicate or obsolete notification).
14:30–14:52
Probe offline: no records. Connectivity unknown. Power is a gap. “At least 27 minutes on battery; endpoint unknown.”
14:52:10
First post-gap observation: utility present, charge 41%, ±90 s until clock sync at 14:54. Restoration happened at an unknown time between 14:30 and 14:52.
15:02:06
Fresh contact and probe evidence → INC-0411 recovered; resolved 15:07:06 after 5 min stable. Backlog keeps draining.

k · Preserved exploration

Preserved exploration (unchanged references)

Rounds 1–3 explored nine directions. They are kept unchanged for reference. This design takes its identity from concept 8 and its equipment workflows from concept 7; see the notes for provenance.