680 lines
25 KiB
Markdown
680 lines
25 KiB
Markdown
# Task 005: Repair Parts Tracking
|
|
|
|
## Status
|
|
|
|
Approved implementation specification. Not yet implemented.
|
|
|
|
This task introduces a minimal durable component registry only to support
|
|
tracked-component references in repair-part history. It does not implement the
|
|
broader component inventory, lifecycle, installation, location, or warehouse
|
|
model described as future direction in
|
|
`docs/design/COMPONENT_DOMAIN_MODEL.md`.
|
|
|
|
## Objective
|
|
|
|
Allow authorized operators to record what was used during an active repair as
|
|
exactly one of:
|
|
|
|
1. a quantity-based consumable; or
|
|
2. one tracked component identified by a durable component UUID.
|
|
|
|
Repair-part history is immutable. Corrections use an explicit void action and,
|
|
when necessary, a new replacement entry. Original entries and events are never
|
|
edited or physically deleted.
|
|
|
|
## Product Intent
|
|
|
|
A technician should be able to determine which consumables and tracked
|
|
components were used during a repair. Consumable records preserve their
|
|
description, quantity, and unit. Tracked-component records preserve both the
|
|
live component UUID and an immutable display snapshot captured when the entry
|
|
is recorded.
|
|
|
|
All authoritative actors come from authentication. All authoritative
|
|
timestamps and repair-event sequences are server or database controlled.
|
|
|
|
## Authoritative Boundaries
|
|
|
|
Task 004 remains authoritative for:
|
|
|
|
- control-board replacement;
|
|
- MAC ownership;
|
|
- control-board installation and removal history;
|
|
- current control-board and MAC projections;
|
|
- `CONTROL_BOARD_REPLACED` events; and
|
|
- the `/api/miners/{miner_id}/change-mac` compatibility alias.
|
|
|
|
Task 005 records repair usage only. It must not install, remove, replace,
|
|
transfer, relocate, or change ownership of a tracked component. In particular,
|
|
recording a control-board component must not:
|
|
|
|
- replace a control board or transfer its MAC address;
|
|
- modify `miners.control_board_sn` or `miners.current_mac`;
|
|
- modify `miner_network_identities`, `miner_mac_ownerships`, or
|
|
`miner_control_board_installations`;
|
|
- append `CONTROL_BOARD_REPLACED`; or
|
|
- substitute for Task 004's explicit replacement workflow.
|
|
|
|
The existing `miner_control_board_installations.id` identifies an installation
|
|
history row, not a durable component. The existing `miner_hashboards.id`
|
|
identifies a miner-slot projection, not a component that retains identity
|
|
through movement. Task 005 must not repurpose either UUID as a component UUID or
|
|
infer component identity from a serial number, MAC address, miner, slot, or
|
|
installation.
|
|
|
|
## Scope
|
|
|
|
- a minimal durable component registry;
|
|
- narrow component creation and detail reads;
|
|
- focused repair-part list, record, and void routes;
|
|
- capability-based permissions;
|
|
- mutually exclusive consumable and tracked-component request shapes;
|
|
- fixed-precision quantities and controlled units;
|
|
- immutable tracked-component display snapshots;
|
|
- explicit void and replacement-entry corrections;
|
|
- immutable, explicitly sequenced repair events;
|
|
- atomic transactions and concurrency control;
|
|
- PostgreSQL constraints, foreign keys, and immutability protections; and
|
|
- focused, migration, regression, architecture, and route tests.
|
|
|
|
## Out Of Scope
|
|
|
|
- generalized component inventory or generic component CRUD;
|
|
- component update, delete, list, search, merge, or deduplication APIs;
|
|
- component locations, movements, installations, or removals;
|
|
- component lifecycle or condition workflows;
|
|
- component ownership transfer;
|
|
- warehouse inventory and transfers;
|
|
- stock levels, allocation, or automatic stock deduction;
|
|
- purchasing, suppliers, vendors, pricing, invoices, and cost accounting;
|
|
- warranties and RMA workflows;
|
|
- reorder automation;
|
|
- automatic creation of components while recording repair parts;
|
|
- automatic installation, removal, ownership transfer, or location tracking;
|
|
- specialized component tables or attributes;
|
|
- generalized bills of materials;
|
|
- bulk import;
|
|
- photos and work orders; and
|
|
- redesign or migration of Task 004 structures.
|
|
|
|
## Terminology
|
|
|
|
**Component:** A minimally registered physical part with an immutable UUID and
|
|
constrained type. Task 005 records identity and basic display metadata only.
|
|
|
|
**Repair-part entry:** An immutable record associating one consumable use or one
|
|
tracked component with a repair.
|
|
|
|
**Consumable:** A normalized description, positive quantity, controlled unit,
|
|
and optional notes. It has no component UUID.
|
|
|
|
**Tracked component:** One existing component referenced by UUID. Its repair-part
|
|
entry includes a display snapshot and has no consumable quantity or unit.
|
|
|
|
**Display snapshot:** Immutable component type, Asset ID, manufacturer, model,
|
|
and manufacturer serial values copied from the component when the repair-part
|
|
entry is recorded. Nullable source values remain null.
|
|
|
|
**Void:** A one-way audited correction that preserves the original entry. A
|
|
corrected value is represented by voiding the incorrect entry and recording a
|
|
new entry.
|
|
|
|
## Minimal Component Registry
|
|
|
|
### Component Types
|
|
|
|
The initial controlled component types are:
|
|
|
|
- `control_board`;
|
|
- `hashboard`;
|
|
- `power_supply`; and
|
|
- `fan`.
|
|
|
|
Do not accept `other` or arbitrary free-text types. Expanding the vocabulary
|
|
requires a reviewed migration and corresponding model and test updates.
|
|
|
|
### Component Schema
|
|
|
|
Create the focused `components` table with:
|
|
|
|
- `id` UUID primary key with a database default;
|
|
- `component_type` constrained text, required;
|
|
- `asset_id` text, optional;
|
|
- `manufacturer` text, optional;
|
|
- `model` text, optional;
|
|
- `manufacturer_serial` text, optional;
|
|
- `created_at` timezone-aware timestamp, required and server controlled; and
|
|
- `created_by` authenticated user UUID, required.
|
|
|
|
Rules:
|
|
|
|
- `id`, `component_type`, `created_at`, and `created_by` are immutable;
|
|
- optional text is trimmed, nonblank when present, and limited to 255
|
|
characters;
|
|
- Asset IDs are case-insensitively unique among components after trimming;
|
|
- component Asset IDs do not share a database uniqueness namespace with miner
|
|
Asset IDs in this task;
|
|
- manufacturer serials are not assumed globally unique;
|
|
- `created_by` references the existing user identity with `RESTRICT` deletion
|
|
behavior;
|
|
- no client may supply `id`, `created_at`, or `created_by`;
|
|
- no component is created implicitly from a repair-part request; and
|
|
- Task 005 provides no component metadata update or deletion workflow.
|
|
|
|
The UUID is the authoritative component identity. Asset ID and manufacturer
|
|
metadata do not replace it. A later explicit task may evolve mutable display
|
|
metadata while preserving the UUID and history.
|
|
|
|
## API
|
|
|
|
### Component Routes
|
|
|
|
Task 005 adds exactly these narrow component routes:
|
|
|
|
```http
|
|
POST /api/components
|
|
GET /api/components/{component_id}
|
|
```
|
|
|
|
`POST /api/components` creates one component and returns `201 Created`.
|
|
`GET /api/components/{component_id}` returns one component and `200 OK`.
|
|
Unknown component UUIDs return `404`. Malformed UUIDs use the existing `422`
|
|
validation response.
|
|
|
|
Do not add component list, search, update, patch, delete, install, remove, move,
|
|
or lifecycle routes.
|
|
|
|
### Repair-Part Routes
|
|
|
|
Task 005 adds exactly these repair-part routes:
|
|
|
|
```http
|
|
GET /api/repairs/{repair_id}/parts
|
|
POST /api/repairs/{repair_id}/parts
|
|
POST /api/repairs/{repair_id}/parts/{repair_part_id}/void
|
|
```
|
|
|
|
The list returns `200 OK`. Recording returns `201 Created`. A successful void
|
|
returns `200 OK` with the voided repair-part representation. No route physically
|
|
deletes or destructively edits a repair-part entry.
|
|
|
|
Register and test these routes without changing the precedence or behavior of
|
|
legacy static routes, including `/api/repairs/parts-summary`.
|
|
|
|
### Response Envelopes
|
|
|
|
Use the existing typed success envelope containing `data`, `meta`, and `error`.
|
|
Component responses expose only the approved component fields. Repair-part
|
|
responses expose the stored consumable fields or stored component snapshot,
|
|
plus component UUID, notes, recording metadata, and void metadata.
|
|
|
|
A tracked repair-part response must use its immutable snapshot for historical
|
|
display. It may expose `component_id` so a caller can separately request the
|
|
live component. It must not silently substitute current component metadata for
|
|
the snapshot.
|
|
|
|
### List Ordering
|
|
|
|
Return repair-part entries in ascending recorded repair-event sequence. The
|
|
explicit database sequence, not timestamps, determines order. Void state is
|
|
shown on the original entry; void events remain separately visible in the
|
|
existing repair timeline.
|
|
|
|
No pagination or filtering is added because the collection is bounded to one
|
|
repair. A later change requires compatibility review.
|
|
|
|
## Permissions
|
|
|
|
Introduce these proposed capabilities:
|
|
|
|
- `components.read` for component detail reads;
|
|
- `components.create` for component creation; and
|
|
- `repairs.parts.manage` for recording and voiding repair-part entries.
|
|
|
|
Reuse implemented `repairs.read` for the repair-part list.
|
|
|
|
Role grants:
|
|
|
|
| Capability | Admin | Manager | Technician | Read Only |
|
|
| --- | --- | --- | --- | --- |
|
|
| `components.read` | yes | yes | yes | yes |
|
|
| `components.create` | yes | yes | yes | no |
|
|
| `repairs.read` | yes | yes | yes | yes |
|
|
| `repairs.parts.manage` | yes | yes | yes | no |
|
|
|
|
Routers check capabilities, never role names. Every route requires
|
|
authentication and focused unauthenticated, unauthorized, and authorized-role
|
|
tests.
|
|
|
|
## Request Models
|
|
|
|
Reject unknown fields in every request. Actor UUIDs, timestamps, sequences,
|
|
event payloads, snapshots, and persistence identifiers are never accepted from
|
|
clients.
|
|
|
|
### Create Component
|
|
|
|
```json
|
|
{
|
|
"component_type": "hashboard",
|
|
"asset_id": "HB-0042",
|
|
"manufacturer": "MicroBT",
|
|
"model": "M50 Hashboard",
|
|
"manufacturer_serial": "HB-SN-12345"
|
|
}
|
|
```
|
|
|
|
Only `component_type` is required. Optional text values are normalized by
|
|
trimming surrounding whitespace and are rejected when blank after trimming.
|
|
Duplicate normalized Asset IDs return `409 Conflict` without leaking a database
|
|
exception.
|
|
|
|
### Record Consumable
|
|
|
|
```json
|
|
{
|
|
"consumable": {
|
|
"description": "Thermal paste",
|
|
"quantity": 1,
|
|
"unit": "gram"
|
|
},
|
|
"tracked_component_uuid": null,
|
|
"notes": "Applied during board service."
|
|
}
|
|
```
|
|
|
|
### Record Tracked Component
|
|
|
|
```json
|
|
{
|
|
"consumable": null,
|
|
"tracked_component_uuid": "c79f9f36-1e89-4dbc-8a84-9ff3167614be",
|
|
"notes": "Used during this repair."
|
|
}
|
|
```
|
|
|
|
Exactly one of `consumable` and `tracked_component_uuid` must be non-null. A
|
|
tracked-component request refers to one existing component and never creates,
|
|
installs, removes, transfers, or mutates it.
|
|
|
|
### Void Repair Part
|
|
|
|
```json
|
|
{
|
|
"reason": "Recorded against the wrong repair."
|
|
}
|
|
```
|
|
|
|
The reason is required, trimmed, nonblank, and limited to 1000 characters. The
|
|
actor and timestamp come from authentication and the server.
|
|
|
|
## Consumable Rules
|
|
|
|
A consumable requires:
|
|
|
|
- normalized, nonblank `description`, limited to 255 characters;
|
|
- `quantity` represented as `NUMERIC(12,3)` or the repository-equivalent fixed
|
|
precision;
|
|
- quantity greater than zero;
|
|
- one controlled `unit`; and
|
|
- optional shared notes.
|
|
|
|
The initial controlled units are:
|
|
|
|
- `each`;
|
|
- `gram`;
|
|
- `millilitre`;
|
|
- `metre`; and
|
|
- `centimetre`.
|
|
|
|
Do not accept aliases or arbitrary free-text units. The service and request
|
|
model reject more than three fractional digits or values outside the fixed
|
|
precision range.
|
|
|
|
When `unit` is `each`, quantity must be a whole number. Measurement units permit
|
|
fractional quantities to three decimal places. Quantity describes repair usage
|
|
only and never changes stock.
|
|
|
|
## Repair-Part Schema
|
|
|
|
Create the focused `repair_parts` table with:
|
|
|
|
- `id` UUID primary key with a database default;
|
|
- `repair_id` UUID, required;
|
|
- `consumable_description` text, nullable;
|
|
- `quantity` `NUMERIC(12,3)`, nullable;
|
|
- `unit` constrained text, nullable;
|
|
- `component_id` UUID, nullable;
|
|
- `component_type_snapshot` constrained text, nullable;
|
|
- `component_asset_id_snapshot` text, nullable;
|
|
- `component_manufacturer_snapshot` text, nullable;
|
|
- `component_model_snapshot` text, nullable;
|
|
- `component_manufacturer_serial_snapshot` text, nullable;
|
|
- `notes` text, nullable;
|
|
- `recorded_by` authenticated user UUID, required;
|
|
- `recorded_at` timezone-aware timestamp, required and server controlled;
|
|
- `recorded_event_id` repair-event UUID, required;
|
|
- `voided_by` authenticated user UUID, nullable;
|
|
- `voided_at` timezone-aware timestamp, nullable;
|
|
- `void_reason` text, nullable;
|
|
- `void_event_id` repair-event UUID, nullable.
|
|
|
|
Foreign keys use `RESTRICT` deletion behavior for repairs, users, components,
|
|
and events so authoritative history cannot disappear through cascades.
|
|
|
|
The repository captures tracked-component snapshots from the locked component
|
|
row in the same transaction as the repair-part insert and event. Client values
|
|
cannot populate snapshot columns. Nullable component metadata remains null in
|
|
the snapshot rather than being fabricated.
|
|
|
|
The original entry shape, consumable values, component UUID and snapshot,
|
|
notes, recording actor, timestamp, and recorded event data are immutable. A
|
|
void adds only the write-once void fields and immutable void event.
|
|
|
|
## PostgreSQL Invariants
|
|
|
|
PostgreSQL must enforce the complete XOR shape:
|
|
|
|
- consumable branch:
|
|
- `consumable_description`, `quantity`, and `unit` are all present;
|
|
- `component_id` and every component snapshot field are null; or
|
|
- tracked-component branch:
|
|
- `component_id` and `component_type_snapshot` are present;
|
|
- `consumable_description`, `quantity`, and `unit` are all null;
|
|
- optional snapshot fields match the nullable values captured at record time.
|
|
|
|
Additional constraints and protections:
|
|
|
|
- quantity is greater than zero when present;
|
|
- quantity uses at most three fractional digits;
|
|
- `each` requires `quantity = trunc(quantity)`;
|
|
- unit is one of `each`, `gram`, `millilitre`, `metre`, or `centimetre`;
|
|
- component and snapshot types use the approved component-type constraint;
|
|
- descriptions and snapshot text are trimmed and nonblank when present;
|
|
- notes are null or trimmed, nonblank, and limited to 4000 characters;
|
|
- recording actor, timestamp, and event UUID are required;
|
|
- component UUID references an existing component;
|
|
- recorded and void event UUIDs are unique and reference repair events;
|
|
- void actor, timestamp, reason, and event UUID are either all
|
|
null or all present;
|
|
- void reason is trimmed, nonblank, and limited to 1000 characters;
|
|
- void timestamp is not earlier than recorded timestamp;
|
|
- a repair part cannot be associated with another repair through its event
|
|
references;
|
|
- original entry fields cannot be updated;
|
|
- void fields transition only once from all-null to all-present; and
|
|
- repair-part and repair-event rows cannot be physically deleted.
|
|
|
|
Use PostgreSQL constraints, foreign keys, indexes, and narrowly scoped triggers
|
|
where a declarative constraint cannot enforce cross-row immutability or event
|
|
ownership. Service validation provides clear domain errors but does not replace
|
|
database integrity.
|
|
|
|
## Repair State Rules
|
|
|
|
- component creation and detail reads do not change repair state;
|
|
- record repair-part entries only while the repair is active;
|
|
- void repair-part entries only while the repair is active;
|
|
- list entries and void state after repair completion;
|
|
- completed and cancelled repairs are read-only for Task 005;
|
|
- no unrestricted editing is allowed in any repair state; and
|
|
- Task 005 never changes repair status or miner lifecycle.
|
|
|
|
Begin Repair, Repair Timeline, Return to Service, and Task 004 behavior must
|
|
remain unchanged.
|
|
|
|
## Events
|
|
|
|
Add these immutable repair event types:
|
|
|
|
- `REPAIR_PART_RECORDED`; and
|
|
- `REPAIR_PART_VOIDED`.
|
|
|
|
`REPAIR_PART_RECORDED` contains:
|
|
|
|
- repair-part UUID;
|
|
- entry kind: `consumable` or `tracked_component`;
|
|
- the stored description, quantity, and unit for a consumable; or
|
|
- the stored component UUID and complete component display snapshot for a
|
|
tracked component; and
|
|
- optional notes.
|
|
|
|
It must not claim that a tracked component was installed, removed, replaced,
|
|
transferred, relocated, or changed.
|
|
|
|
`REPAIR_PART_VOIDED` contains:
|
|
|
|
- repair-part UUID; and
|
|
- required correction reason.
|
|
|
|
The event actor, timestamp, and sequence use existing repair-event columns and
|
|
are server controlled. Clients request domain actions, not raw events. Events
|
|
are append-only and ordered only by the repair's explicit database sequence.
|
|
The `repair_parts` table is the queryable record and does not replace Task 004
|
|
history.
|
|
|
|
## Transactions And Concurrency
|
|
|
|
### Component Creation
|
|
|
|
Component creation is one transaction. The database generates its UUID and
|
|
timestamp, the authenticated actor is required, and the normalized Asset ID
|
|
constraint resolves concurrent duplicates. No repair or component event is
|
|
invented for this minimal identity registration.
|
|
|
|
### Record Repair Part
|
|
|
|
Resolve the repair's miner UUID, then lock in this order:
|
|
|
|
```text
|
|
Miner -> Repair -> Component (tracked entries only)
|
|
```
|
|
|
|
After locking, revalidate the miner and active repair state. For a tracked
|
|
entry, lock and load the component, capture its snapshot, insert the repair-part
|
|
row, allocate the next repair-event sequence, and append exactly one
|
|
`REPAIR_PART_RECORDED` event in one transaction.
|
|
|
|
For a consumable, perform the same workflow without a component lock. Any
|
|
validation, insert, snapshot, sequence, or event failure rolls back the complete
|
|
action.
|
|
|
|
### Void Repair Part
|
|
|
|
Resolve the repair's miner UUID, then lock in this order:
|
|
|
|
```text
|
|
Miner -> Repair -> Repair Part
|
|
```
|
|
|
|
Revalidate the active repair and route ownership after locking. Set the
|
|
write-once void fields and append exactly one `REPAIR_PART_VOIDED` event in one
|
|
transaction. Concurrent void requests produce one success and one `409`
|
|
conflict, with exactly one void event.
|
|
|
|
The Miner lock and Repair lock serialize record and void actions with Return to
|
|
Service and Task 004. If repair completion wins, the later Task 005 action sees
|
|
a terminal repair and fails with `409`. If Task 005 wins, its complete entry and
|
|
event commit before completion proceeds.
|
|
|
|
## Architecture
|
|
|
|
Preserve:
|
|
|
|
```text
|
|
Router
|
|
-> Service
|
|
-> Repository
|
|
-> PostgreSQL
|
|
```
|
|
|
|
Routers own authentication, permission checks, request validation, HTTP status
|
|
mapping, and response envelopes. Services own normalization, component and
|
|
repair-part rules, workflow coordination, and domain exceptions. Repositories
|
|
own parameterized SQL, locks, snapshots, persistence, and PostgreSQL integrity
|
|
translation.
|
|
|
|
Routers contain no SQL, database-driver imports, transaction handling, or
|
|
business rules. Services and repositories contain no FastAPI imports.
|
|
|
|
## Error Behavior
|
|
|
|
- unauthenticated: `401`;
|
|
- missing permission: `403`;
|
|
- unknown component, repair, or repair part: `404`;
|
|
- malformed UUID, unknown field, invalid type, invalid unit, invalid shape,
|
|
invalid precision, or invalid text: `422`;
|
|
- duplicate normalized component Asset ID: `409`;
|
|
- inactive repair, already-voided entry, or concurrent state conflict: `409`;
|
|
and
|
|
- persistence failures: translated without exposing SQL or driver details.
|
|
|
|
A repair-part UUID that does not belong to the repair in the route is treated as
|
|
not found and returns `404`.
|
|
|
|
## Migration
|
|
|
|
Create additive Alembic revision `0014_repair_parts_tracking` with parent
|
|
`0013_control_board_replacement`.
|
|
|
|
The migration must:
|
|
|
|
- create `components` with its type, text, actor, timestamp, Asset ID uniqueness,
|
|
immutability, and deletion protections;
|
|
- create `repair_parts` with XOR, quantity, unit, snapshot, actor, timestamp,
|
|
event, void, immutability, and deletion protections;
|
|
- add `components.read`, `components.create`, and `repairs.parts.manage` with the
|
|
approved role assignments;
|
|
- extend the repair-event type constraint with `REPAIR_PART_RECORDED` and
|
|
`REPAIR_PART_VOIDED`;
|
|
- add only indexes justified by component lookup, normalized Asset ID
|
|
uniqueness, repair-part listing, foreign keys, and event relationships;
|
|
- preserve all existing data without inventing components, repair parts,
|
|
actors, timestamps, snapshots, or events; and
|
|
- leave Task 004 tables, constraints, projections, routes, and history intact.
|
|
|
|
There is no legacy backfill. Generic maintenance events, telemetry, serial
|
|
numbers, miner hashboard projections, and Task 004 installation rows are not
|
|
sufficient evidence for durable component identity or repair-part usage.
|
|
|
|
Downgrade is permitted only when no component, repair-part, or Task 005 repair
|
|
event rows exist. Otherwise it must refuse with a clear actionable error rather
|
|
than delete authoritative identity or history. An empty safe downgrade removes
|
|
Task 005 constraints, permissions, tables, and event values in dependency order,
|
|
then supports re-upgrade.
|
|
|
|
Validate previous-head upgrade, preflight behavior, safe downgrade, downgrade
|
|
refusal, re-upgrade, and an empty database from base through head.
|
|
|
|
## Tests
|
|
|
|
### Component Registry
|
|
|
|
- create and detail-read authorization for every role;
|
|
- successful creation with required fields only and with all metadata;
|
|
- UUID, actor, and timestamp authority;
|
|
- all approved component types and rejection of unknown types;
|
|
- whitespace normalization, text limits, and blank optional values;
|
|
- case-insensitive normalized Asset ID uniqueness and concurrent duplicates;
|
|
- nullable manufacturer metadata and nonunique manufacturer serials;
|
|
- immutable identity, type, actor, and timestamp;
|
|
- no implicit component creation from a repair-part request;
|
|
- no list, search, update, delete, lifecycle, location, or installation routes;
|
|
and
|
|
- missing and malformed component UUID behavior.
|
|
|
|
### Repair-Part Requests And Reads
|
|
|
|
- consumable and tracked-component happy paths;
|
|
- controlled units and rejection of aliases or free text;
|
|
- `NUMERIC(12,3)` boundaries and rejection of excess precision;
|
|
- positive fractional measurement quantities;
|
|
- whole-number `each` quantities and rejection of fractional `each`;
|
|
- both entry branches supplied and neither branch supplied;
|
|
- incomplete consumable shapes;
|
|
- unknown, malformed, and client-created component UUIDs;
|
|
- optional notes and blank or overlength text;
|
|
- server-controlled actors, timestamps, sequences, events, and snapshots;
|
|
- stable list ordering by recorded event sequence;
|
|
- response envelopes and historical reads after completion;
|
|
- component metadata changed after recording without changing the stored
|
|
snapshot or historical display;
|
|
- no N+1 component or actor queries; and
|
|
- route ownership for nested repair-part UUIDs.
|
|
|
|
### Workflow And Integrity
|
|
|
|
- add and void authorization for every role;
|
|
- active repair enforcement;
|
|
- explicit void and void-plus-replacement correction;
|
|
- already-voided and cross-repair behavior;
|
|
- concurrent record versus Return to Service;
|
|
- concurrent void requests;
|
|
- rollback on component lookup, snapshot, part insert, sequence, or event
|
|
failure;
|
|
- direct XOR, type, quantity, unit, text, foreign-key, event, and void
|
|
constraints;
|
|
- original-field, event, and physical-deletion protections;
|
|
- confirmation that tracked-component recording changes no component, miner,
|
|
lifecycle, installation, ownership, MAC, location, or condition state;
|
|
- confirmation that a control-board reference cannot bypass or duplicate Task
|
|
004's replacement behavior; and
|
|
- lock-order and transaction-boundary tests.
|
|
|
|
### Regression And Validation
|
|
|
|
- Begin Repair tests;
|
|
- Repair Timeline tests;
|
|
- Return to Service tests;
|
|
- Task 004 control-board replacement and compatibility-alias tests;
|
|
- Inventory lifecycle and permission regressions;
|
|
- legacy static repair-route precedence tests;
|
|
- migration upgrade, downgrade, refusal, re-upgrade, previous-head, and empty
|
|
database tests;
|
|
- full backend suite;
|
|
- disposable development baseline;
|
|
- Python compilation and shell syntax checks;
|
|
- router SQL, driver, transaction, and business-rule audit;
|
|
- service and repository FastAPI audit;
|
|
- exact route-count audit; and
|
|
- `git diff --check`.
|
|
|
|
## Acceptance Criteria
|
|
|
|
1. Components receive immutable UUIDs and only the minimal approved identity and
|
|
display metadata.
|
|
2. Component types and consumable units use the approved controlled values.
|
|
3. Every repair-part entry represents exactly one complete consumable or one
|
|
existing tracked component, never both or neither.
|
|
4. Consumable quantities use fixed precision, remain positive, and enforce
|
|
whole numbers for `each`.
|
|
5. Tracked entries preserve a live component foreign key and immutable display
|
|
snapshot.
|
|
6. Historical display does not change when live component metadata later
|
|
changes.
|
|
7. Recording a tracked component does not install, remove, replace, transfer,
|
|
relocate, create, or merge a component or miner.
|
|
8. Original entries and events are immutable; corrections use an audited void
|
|
and optional replacement entry without physical deletion.
|
|
9. Actors, timestamps, snapshots, and event sequences are authoritative and
|
|
server controlled.
|
|
10. Record and void actions are atomic and concurrency safe.
|
|
11. Task 004 remains authoritative for control-board and MAC behavior.
|
|
12. No generalized inventory, warehouse, purchasing, supplier, stock, location,
|
|
lifecycle, warranty, RMA, or cost system is introduced.
|
|
13. Existing repair workflows and static route precedence remain unchanged.
|
|
14. Migration lifecycle, focused tests, regressions, architecture audits, and
|
|
the disposable baseline pass.
|
|
|
|
## Completion Report
|
|
|
|
Report files, migration revision and parent, routes and exact route count,
|
|
permissions and role grants, component registry schema, repair-part schema,
|
|
request and response models, constraints, snapshots, event payloads and
|
|
sequencing, transaction and lock strategy, downgrade behavior, tests and counts,
|
|
regression results, architecture and route audits, known limitations, and final
|
|
status.
|
|
|
|
Do not commit.
|
|
Do not push.
|