feat: add Mine Map v1

This commit is contained in:
lussierj committed 2026-08-10 19:55:20 +00:00
1 parent 548051899e
commit 60a831950c
16 files changed
+2027 -163

No files matched your search

+67 -107
View File
@@ -2,16 +2,10 @@
## Status
This document is the implementation-ready design baseline for Mine Map v1. It
defines the smallest useful spatial operations view that can be built from the
current MinerOps location, inventory, telemetry, and repair foundations. It
does not claim that the described v1 interface is implemented.
The current application exposes a legacy Location Map at `/locations`, a Mine
Map placeholder at `/monitoring/map`, and a bulk location response at
`GET /api/location/map`. Mine Map v1 replaces the placeholder with a read-only,
permission-aware operational view. Existing location-management workflows
remain available separately and are not redesigned by this specification.
This document is the design baseline for the implemented Mine Map v1. The
current application exposes the read-only operational view at `/monitoring/map`
and retains the legacy Location Map at `/locations` for existing location
management workflows. Those mutation workflows are not redesigned by v1.
## Purpose
@@ -245,8 +239,8 @@ Operational Status is the default layer. It composes current facts with this
display precedence:
1. Location conflict, blocked position, switch, or empty position.
2. Lifecycle `retired`.
3. Active repair or lifecycle `in_repair`.
2. Active repair or lifecycle `in_repair`.
3. Lifecycle `retired` or decommissioned location state.
4. Lifecycle `spare`.
5. Runtime status explicitly equal to `offline`.
6. `hash_stable = false`, labelled **Hash unstable**.
@@ -310,19 +304,13 @@ physical hierarchy or selection model.
## Filtering And Search
V1 filters are allowlisted and based on returned facts:
- explicit runtime status;
- lifecycle;
- model or miner type;
- authoritative active repair presence;
- location state;
- position kind/status; and
- presence or absence of current telemetry.
Search supports Asset ID, miner serial, current MAC, current IP, model, room,
rack, shelf, and position label where those fields are available. Asset ID and
lifecycle require the map response addition described below.
The implemented filters are allowlisted and use returned miner facts: derived
operational status, lifecycle, model, and authoritative active-repair presence.
Search covers Asset ID, miner serial, current MAC, current IP, and model. Location
context remains visible while nonmatching occupied tiles are deemphasized. Users
without `miners.view` receive none of these miner fields; miner search, miner
filters, and non-status layers are disabled rather than operating on redacted
values.
Filtering preserves physical context. Positions remain in the layout;
nonmatching occupied tiles are deemphasized and marked as filtered. A separate
@@ -347,12 +335,12 @@ Where authorized and available, the inspector shows:
- miner serial number;
- current MAC and IP address;
- full site, room, rack, shelf, and position path;
- runtime status and last seen time;
- runtime status and telemetry observation age;
- lifecycle;
- active repair status, reason, and repair UUID;
- active repair status and reason;
- latest hashrate in TH/s;
- latest power in watts;
- latest chip maximum and average temperature;
- latest chip maximum temperature;
- telemetry observation time; and
- a link to full miner detail.
@@ -366,30 +354,27 @@ needed by the map response.
Shareable state is URL-backed. The initial query contract is:
```text
/monitoring/map?site_id={uuid}&room_id={uuid}&rack={label}&layer=status&miner_id={uuid}
/monitoring/map?site_id={uuid}&room_id={uuid}&layer=status&miner_id={uuid}
```
Optional repeated or encoded parameters hold allowlisted filters and search.
Rack is a label because racks do not have UUIDs. Unknown, unauthorized, or
malformed parameters are ignored with a nonblocking notice; they never broaden
data access.
Optional encoded parameters hold allowlisted filters and search. Rack and shelf
do not have URL identifiers because they are labels rather than durable entities.
Unknown, unauthorized, or malformed parameters are discarded and the canonical
URL is replaced; they never broaden data access.
The URL preserves:
- selected site and room;
- optional rack focus;
- layer;
- filters and search; and
- selected miner.
History state or session state preserves room scroll position, focused tile,
and any future map viewport that is too transient for a URL. Selecting or
closing a miner updates history so browser Back and Forward restore predictable
selection. Refresh and bookmarked links reconstruct the same shareable view.
Selecting or closing a miner updates history so browser Back and Forward restore
predictable URL-backed selection. Refresh and bookmarked links reconstruct the
same valid shareable state. Scroll position is not persisted by v1.
When full miner detail opens in the same tab, the return URL contains the map
query state. Returning restores the prior scroll position when it remains
valid. A new tab may be offered as a normal browser link, not forced.
query state. A new tab remains available through normal browser behavior.
## Responsive Behavior
@@ -502,27 +487,17 @@ It returns:
or decommissioned location states; and
- counts in `meta`.
Limitations:
The implemented additive response also supplies site and room UUIDs, Asset ID,
lifecycle, authoritative active repair context, generated-at metadata, typed
contracts, and optional server-side site and room filters. Its read path follows
Router -> Service -> Repository -> PostgreSQL. Users with `locations.view` but
without `miners.view` receive physical layout and occupancy with miner identity,
telemetry, repair, and virtual-miner details removed.
- no site ID or site name;
- no room UUID in each position;
- room ordering joins positions to rooms by case-insensitive room name, which is
ambiguous when different sites use the same room name;
- no Asset ID;
- no lifecycle status;
- no authoritative active repair UUID/status/reason;
- no tags;
- no environment temperature;
- no generated-at or response freshness metadata;
- no server-side site/room filter; and
- integrated `main.py` implementation with no typed response model.
The endpoint is already efficient in shape. V1 should preserve its path for
compatibility and implement an additive, typed map read model through the
required Router -> Service -> Repository -> PostgreSQL boundaries. It should
accept allowlisted `site_id` and `room_id` filters and add the missing identity,
lifecycle, site, and authoritative repair fields. A second competing map
endpoint is not required solely for v1.
Tags and environment temperature are not included because v1 does not require
them. Rack and shelf remain labels. The frontend renders legacy positions without
`room_id` in a separate **Unscoped legacy positions** section; it does not attach
their denormalized room labels to a selected site or room.
### `GET /api/sites`
@@ -569,10 +544,10 @@ needs these fields, but it is not used to populate tiles.
### `GET /api/miners/{miner_id}/status-snapshots`
Authentication is required by the route, but the current legacy endpoint does
not enforce `miners.view`. Mine Map must not treat authentication alone as
sufficient authorization. Any v1 use of this supplemental endpoint requires a
backend permission correction to `miners.view` first.
Authentication and `miners.view` are required. Admin is allowed by the existing
admin override; Manager, Technician, Read Only, and custom roles are authorized
according to their effective `miners.view` permission. Mine Map does not call
this endpoint per tile.
Returns up to 100 recent snapshots with hashrate, power, efficiency,
environment and chip temperatures, fans, uptime, stability, rejection rate,
@@ -603,10 +578,9 @@ need these endpoints because the location map already includes the relevant raw
latest measurements. V1 must not combine their classifications into a new
status policy by accident.
## Required Map Read Model Additions
## Implemented Map Read Model
Before the full v1 behavior is enabled, the additive `GET /api/location/map`
contract must provide:
The additive `GET /api/location/map` contract provides:
- site UUID and name;
- room UUID as well as room name;
@@ -618,10 +592,12 @@ contract must provide:
- per-miner latest telemetry observation time; and
- truthful nulls for unavailable fields.
The query must remain set-based, parameterized, permission-controlled, and
bounded by selected scope. The repository owns SQL, the service owns response
composition and any future status policy, and the router owns HTTP validation,
authorization, and the typed response envelope.
The query is set-based, parameterized, permission-controlled, and uses four
fixed database executions independent of fleet size. Optional site and room UUID
filters are supported by the API; the current frontend performs one bulk request
and renders the selected room locally. The repository owns SQL, the service owns
response composition, and the router owns HTTP validation, authorization, and
the typed response envelope.
V1 does not require a write endpoint. Reading the map, changing layer, applying
filters, selecting a miner, or opening the inspector must never mutate location,
@@ -638,11 +614,11 @@ lifecycle, repair, or telemetry state.
remain authoritative.
All default roles currently receive both `locations.view` and `miners.view`,
but custom role combinations are possible. Before implementing the additive map
response, decide whether users with only `locations.view` receive a reduced
miner shape or whether the enriched response requires both permissions. The API
must not expose fields merely because the standard role matrix usually grants
both.
but custom role combinations are possible. The backend implements the boundary:
`locations.view` alone returns layout and occupancy while redacting Asset ID,
miner UUID, expected/current MAC, IP, serials, model, runtime/lifecycle/location
state, telemetry, active repair, and virtual-miner data. This is authoritative
server-side redaction rather than frontend hiding.
## Architecture Boundary
@@ -683,7 +659,7 @@ The smallest useful v1 includes:
- selected-miner inspector;
- existing miner-detail navigation;
- URL-backed context and predictable browser history;
- loading, empty, denied, error, partial, and refresh-failure states;
- loading, empty, denied, error, and partial-data states;
- desktop, tablet, and phone behavior;
- keyboard, screen-reader, touch, zoom, and reduced-motion support;
- permission-aware presentation; and
@@ -713,31 +689,17 @@ The following are explicitly outside v1:
- selected-miner telemetry charts; and
- generalized infrastructure modelling beyond current position records.
## Backend And Data Gaps
## Remaining Backend And Data Gaps
These gaps block parts of the acceptance criteria:
1. **Multi-site location identity:** The current map response omits site and
room UUIDs and joins room ordering by name. Same-named rooms across sites
cannot be represented reliably until the query is site-scoped.
2. **Asset and lifecycle context:** Asset ID and lifecycle are not in the map
response. Without them, the map cannot truthfully distinguish spare,
retired, and in-repair lifecycle states.
3. **Authoritative repair context:** No bulk modular repair projection is joined
to map miners. The legacy repair queue is not an equivalent source.
4. **Freshness policy:** No single approved stale/down threshold exists. V1 can
show explicit offline state and observation age, but cannot classify stale
consistently until policy is centralized.
5. **Health and thermal policy:** Hashrate, rejection, power, and temperature
1. **Freshness policy:** No single approved stale/down threshold exists. V1
shows explicit offline state and observation age but does not classify stale.
2. **Health and thermal policy:** Hashrate, rejection, power, and temperature
measurements exist, but model-aware degraded and hot thresholds do not.
6. **Location vocabularies:** Position and location-state values are not
database-constrained. Unknown legacy values require truthful fallback and a
later preflight before constraints are added.
7. **Rack and shelf identity/order:** Rack and shelf are labels, and racks lack
an explicit sort field. V1 can render them but cannot provide durable rack or
shelf links or guaranteed operator-defined rack ordering.
8. **Map contract:** The current response has no typed model or generated-at
metadata and cannot be server-filtered by site or room.
3. **Location vocabularies:** Position and location-state values are not
database-constrained. Unknown legacy values require truthful fallback.
4. **Rack and shelf identity/order:** Rack and shelf are labels, and racks lack
an explicit sort field. V1 cannot provide durable rack or shelf links or
operator-defined rack ordering.
## Acceptance Criteria
@@ -777,15 +739,13 @@ boundary rather than implying complete health classification.
## Unresolved Decisions
The following decisions must be made before implementation begins:
The following decisions remain for later map and data work:
1. Whether the enriched map response requires both `locations.view` and
`miners.view`, or returns a reduced miner shape to location-only users.
2. Which backend-owned policy defines stale, down, hot, and degraded states,
1. Which backend-owned policy defines stale, down, hot, and degraded states,
including scope, thresholds, collector cadence, and configuration source.
3. Whether rack ordering may use a documented natural-label sort for v1 or
requires an additive persisted sort field before rollout.
4. Whether `location_positions.room_id` can be made required after legacy-data
2. Whether rack ordering may use a documented natural-label sort or requires an
additive persisted sort field.
3. Whether `location_positions.room_id` can be made required after legacy-data
preflight, eliminating name-only room association for production map data.
These decisions do not require changing the durable miner identity, existing