docs: add repair domain design documents

This commit is contained in:
lussierj committed 2026-08-02 03:44:15 +00:00
1 parent f2b736b08e
commit 62c91a2c3e
4 files changed
+778

No files matched your search

+156
View File
@@ -0,0 +1,156 @@
# MinerOps API Style Guide
## Purpose
Keep MinerOps APIs explicit, consistent, auditable, and aligned with the domain architecture. Implemented conventions and approved task specifications take precedence.
## Principles
1. Prefer domain commands over generic CRUD.
2. Use nouns for resources and explicit action routes for operational commands.
3. Keep writes focused and atomic.
4. Return truthful domain representations.
5. Separate authentication, permission, validation, domain logic, and persistence.
6. Preserve static-route compatibility.
7. Do not expose database structure as the public API.
## Current and Proposed Examples
Implemented modular repair routes:
```http
POST /api/repairs
GET /api/repairs/{repair_id}
GET /api/repairs/{repair_id}/events
POST /api/repairs/{repair_id}/complete
```
Relevant legacy compatibility routes include:
```http
GET /api/repairs/queue
GET /api/repairs/parts-summary
POST /api/miners/{miner_id}/change-mac
```
These integrated routes predate the modular domain conventions and are not
models for new code.
Proposed by Task 004, not implemented:
```http
POST /api/miners/{miner_id}/replace-control-board
```
Good:
```http
POST /api/repairs/{repair_id}/complete
```
Avoid:
```http
PATCH /api/repairs/{repair_id}
{"status":"completed"}
```
## Methods
- GET is read-only.
- POST creates a resource or invokes an explicit command.
- PATCH is limited to non-operational metadata when partial update semantics are
appropriate.
- Meaningful operational changes use explicit commands rather than generic PUT,
PATCH, or DELETE routes.
- Historical records are corrected with explicit append-only or void workflows,
not physical deletion.
## Status Codes
- 200: successful read or command with response
- 201: created resource
- 204: success with no useful response
- 401: unauthenticated
- 403: lacks permission
- 404: resource missing
- 409: valid request conflicts with domain state
- 422: request validation failure
## Identifiers
Use typed UUIDs for authoritative entity identifiers. Do not expose sequential database IDs as public identity.
## Time and Actors
Use timezone-aware ISO 8601 timestamps. Authoritative timestamps and actor IDs are server controlled. Never accept client-supplied event actor, event timestamp, completion timestamp, or sequence.
## Permissions
Implemented modular repair capabilities are:
- `repairs.read`
- `repairs.create`
- `repairs.complete`
Task 004 proposes `miners.replace_control_board`; it is not registered today.
Do not duplicate role logic in routers.
## Request Models
Use constrained types, normalize whitespace, reject unknown values, enforce length limits, avoid persistence-only fields, and never accept arbitrary client event payloads.
## Response Models
The modular Inventory and Repairs APIs use typed success envelopes containing
`data`, `meta`, and `error`. Expose useful domain state, not secrets or unrelated
nested data. Preserve null when historical truth is unknown.
## Errors
The application does not yet have one uniform typed error envelope. Modular
routes currently use FastAPI validation responses and `HTTPException` detail
responses while translating domain exceptions to 404, 409, or 422 as
appropriate. New routes must follow the established behavior of their domain
and must not leak SQL or driver errors. Repositories do not raise HTTP
exceptions. Services do not import FastAPI.
## Pagination
Potentially large collections require a bounded pagination design. The current
Inventory collection uses bounded `limit` and `offset`; do not claim cursor
pagination until a domain adopts it. The implemented repair timeline returns
all events for one repair by explicit Task 002 design. Any later change to that
contract requires compatibility review.
## Filtering
Use explicit allowlisted filters and sort fields. Never pass arbitrary column names into queries.
## Static Routes
Register static routes before overlapping UUID routes:
```text
/api/repairs/queue
/api/repairs/parts-summary
/api/repairs/{repair_id}
```
Add route-precedence tests.
## Idempotency and Concurrency
Use database invariants, row locks, affected-row checks, and narrow operation
identifiers where demonstrated need exists. Do not add a generalized
idempotency platform speculatively.
The implemented repair workflows prevent duplicate active repairs, duplicate
completion, duplicate event sequences, and partial state. Task 004 proposes the
additional invariant that concurrent requests cannot assign one normalized MAC
to two miners.
## Events
Clients request domain actions, not raw events. The service constructs immutable events.
## Versioning
MinerOps does not currently expose versioned API prefixes. Any incompatible API
versioning scheme requires a separate compatibility decision; this guide does
not reserve a future path.
## Route Specification Checklist
Every new route should define:
- method and path
- authentication and permission
- request and response
- status codes
- domain rules
- transaction boundary
- concurrency behavior
- events
- tests
- out-of-scope items
## Review Checklist
Confirm explicit action choice, status codes, permission, actor and timestamp authority, UUID typing, limits, transaction, concurrency, event construction, route precedence, architecture boundaries, and regressions.
+174
View File
@@ -0,0 +1,174 @@
# MinerOps Repair Domain Model
## Purpose
This design guide describes repair-domain concepts, relationships, lifecycle, invariants, and operational boundaries. Implemented repository behavior and approved task specifications remain authoritative.
## Core Concepts
### Miner
A durable physical mining asset whose identity is independent of IP address, MAC address, control board, hostname, location, and repair state.
### Repair
A bounded operational episode for one miner. It has one start, zero or one completion, immutable timeline events, actors, and timestamps.
### Repair Event
An immutable fact ordered by explicit sequence. Implemented event types are
`REPAIR_STARTED`, `INITIAL_NOTE`, and `REPAIR_COMPLETED`.
### Network Identity History
`miner_network_identities` associates MAC, IP, and hostname observations with a
durable miner and identifies current versus historical rows. It is not a
separate asset identity.
### Proposed Component History
Task 004 proposes preserving control-board replacement details without
redefining the miner. No dedicated immutable component-history table currently
exists; the legacy generic maintenance-event table is not the modular repair
timeline.
### Proposed Repair Part Entry
Task 005 proposes a record of a part installed, removed, or consumed during a
repair. It is not a warehouse inventory transaction, and no `repair_parts`
table currently exists.
## Relationships
Current relationships:
```text
Miner -> many Repairs -> ordered Repair Events
Miner -> Network Identity History
```
Control-board replacement history and repair-part entries are proposed
extensions, not implemented relationships.
## Lifecycle
Current miner states relevant to repair:
- `active`
- `spare`
- `in_repair`
- `retired`
Eligible Begin Repair sources:
- active
- spare
Eligible Return to Service destinations:
- active
- spare
Terminal:
- retired
The repair schema defines these statuses:
- Active: `OPEN`, `IN_PROGRESS`, `WAITING_FOR_PARTS`, `READY_FOR_TEST`
- Terminal: `COMPLETED`, `CANCELLED`
The modular API currently creates repairs as `OPEN` and can complete any active
repair. It does not provide status-transition or cancellation routes. The
implemented modular workflow is therefore:
```text
OPEN -> COMPLETED
```
The other active values remain valid schema states for compatibility and later
explicit workflows. Cancellation or abandonment requires a separate task.
## Current Workflow
```text
Miner active/spare
-> Begin Repair
-> Miner in_repair
-> Timeline events
-> Return to Service
-> Miner active/spare
-> Repair completed
```
## Invariants
- replacing a board or MAC must not create a new miner
- asset-ID and network-identity history must be preserved
- no more than one active repair per miner is database-enforced
- modular repair actions require the miner and repair state to agree
- Return to Service prevents a completed repair from leaving the miner in `in_repair`
- multi-record transitions are atomic
- events are append-only and explicitly sequenced
- actor comes from authentication
- timestamps are server/database controlled
- new completions require completion time, actor, and eligible resulting lifecycle
- legacy unknown values remain unknown rather than fabricated
## Transaction Boundaries
### Begin Repair
Lock miner, create repair, transition miner to `in_repair`, append start event.
### Return to Service
Lock miner, lock repair, validate active state, complete repair, transition miner to active/spare, append completion event.
### Proposed Control Board Replacement
Task 004 proposes locking miner, active repair, and network-identity rows;
validating MAC ownership; updating current projections; and appending one event.
### Proposed Repair Part Recording
Task 005 proposes locking the affected repair or repair-part row, adding or
voiding the entry, and appending the corresponding event atomically.
## Lock Order
Use:
```text
Miner -> Repair -> related rows
```
This aligns workflows and reduces deadlock risk.
## Commands vs CRUD
Implemented explicit commands:
- Begin Repair
- Return to Service
Proposed explicit commands:
- Replace Control Board
- Record Repair Part
- Void Repair Part
Avoid generic repair patches for meaningful operations.
## Read Models
Implemented modular reads:
- repair detail
- ordered repair timeline
The integrated application also has legacy repair-queue, parts-summary, and
miner network-identity reads. Those routes are compatibility behavior, not the
proposed Task 004 or Task 005 read models.
Proposed focused reads include repair parts and control-board replacement
history. Read models do not weaken write invariants.
## Permissions
Implemented permissions:
- `repairs.read`
- `repairs.create`
- `repairs.complete`
Proposed permissions:
- `repairs.parts.manage`
- `miners.replace_control_board`
Task 005 reuses `repairs.read` for its proposed read route. Roles receive capabilities;
endpoints should not hard-code role names when permission checks exist.
## Legacy Data
Preserve truth, allow nullable historical fields where necessary, enforce stronger rules for new or updated rows, use PostgreSQL `NOT VALID` constraints where appropriate, and never backfill invented actors, states, or events.
## Future Extensions
Repair cancellation, diagnosis, work orders, technician assignment, burn-in testing, photos, warranty/RMA, component inventory, and donor-unit tracking should each arrive through explicit tasks.