Files
minerops/docs/CODING_STANDARDS.md

143 lines
5.8 KiB
Markdown

# MinerOps Coding Standards
## Purpose
This document defines the engineering conventions for MinerOps. Task specifications may add feature-specific requirements, but they should not weaken these standards without an explicit architectural decision.
## Core Principles
1. Prefer explicit domain operations over generic CRUD.
2. Preserve operational history.
3. Enforce critical invariants in PostgreSQL.
4. Keep operations atomic.
5. Separate network identity from durable asset identity.
6. Use server-controlled timestamps and authenticated actor identities.
7. Design for self-hosted deployment and safe recovery.
8. Prefer clear, maintainable code over clever abstractions.
9. Keep each implementation task focused and reviewable.
10. Treat safety as more important than convenience for actions affecting miners.
## Layered Architecture
```text
Router
-> Service
-> Repository
-> PostgreSQL
```
### Router
Routers may define HTTP routes, validate requests, enforce authentication and permissions, and serialize responses.
Routers must not contain SQL, manage transactions, implement domain decisions, import database drivers directly, or bypass the service layer.
### Service
Services implement domain workflows, coordinate repositories, enforce domain rules, and raise domain exceptions.
Services must not import FastAPI, raise HTTP-specific exceptions, contain raw SQL, or trust actor identity supplied in request bodies.
### Repository
Repositories own database queries, row locking, persistence details, and PostgreSQL-specific integrity behavior.
Repositories must not import FastAPI, make permission decisions, define HTTP behavior, or commit independently inside a larger service transaction.
## Domain Operations
Use explicit operations such as:
```http
POST /api/repairs
POST /api/repairs/{repair_id}/complete
POST /api/miners/{miner_id}/move
POST /api/miners/{miner_id}/replace-control-board
```
Only `POST /api/repairs` is implemented by the modular Repairs domain today.
The other routes are illustrative names for possible future explicit workflows,
not existing API contracts.
Avoid broad mutable CRUD endpoints when the request represents a meaningful operational event.
## Transactions and Concurrency
Any workflow that changes multiple authoritative records must execute atomically.
Use row locks and database constraints when concurrent requests could violate an invariant. Do not rely only on pre-insert application checks when PostgreSQL can enforce the rule.
## Database Integrity
Use constraints for uniqueness, valid state combinations, referential integrity, one-active-record rules, event-sequence integrity, and actor/timestamp requirements.
Operational history should generally use `RESTRICT` rather than disappearing through cascades.
Every migration must support upgrade, safe downgrade or explicit refusal, previous-head testing, re-upgrade, and empty-database-through-head validation. A downgrade must never invent historical values.
## Identity
A miner's durable identity must not depend on its current IP address, MAC address, control board, hostname, or location.
Network identifiers are observations or replaceable hardware attributes and must retain history.
Actor identity must come from the authenticated principal, never an authoritative request-body field.
## Time
Use timezone-aware timestamps. Authoritative operational timestamps are server- or database-controlled. Never use timestamp order as a substitute for an explicit event sequence.
## Immutable Operational History
Timeline and audit events are append-only. Once written, an event is never edited, deleted, reordered, or silently replaced. Corrections are new events.
Sequenced domain timeline events should include an event UUID, parent UUID,
sequence, event type, actor UUID, timestamp, and structured JSON payload.
## API Design
- Use UUID types for UUID fields.
- Use constrained domain values.
- Use 404 for missing resources, 409 for state conflicts, 422 for invalid input, and established project behavior for 401 and 403.
- Do not expose internal fields without a product requirement.
- Keep routes focused.
- Protect existing static routes when introducing parameter routes.
## Permissions
Permissions describe capabilities, not roles. Implemented examples include
`repairs.read` and `repairs.create`. Names such as `repairs.complete` and
`miners.move` illustrate possible future capabilities and are not currently
registered permissions.
Every protected route requires unauthenticated, unauthorized, and authorized-role tests. Read-only roles may view history but may not perform actions.
## Testing and Validation
Feature work should cover happy paths, validation, permissions, missing records, conflicts, rollback, concurrency where relevant, constraints, migrations, route compatibility, and architecture boundaries.
Unless a task says otherwise, run:
- Python compilation
- shell syntax validation
- focused tests
- relevant regression suites
- migration upgrade, downgrade, re-upgrade, and empty database
- disposable development baseline
- router SQL/driver/transaction audit
- service/repository FastAPI audit
- route-count audit
- `git diff --check`
## Security
Never commit credentials, tokens, environment files, dumps, private configuration, caches, or local database data. Do not log secrets. Apply least privilege and preserve audit history for sensitive actions.
## Documentation
Every implementation task should have a version-controlled specification under `docs/tasks/`. Update roadmaps only after validation passes. Add an ADR only for a genuinely new architectural principle.
## Review Checklist
Before approval, confirm scope, transaction boundaries, database invariants, actor attribution, timestamps, immutable history, permissions, migrations, regressions, route behavior, performance risks, and the reviewed file set.