# Design

## Approved navigation consolidation (2026-10-14)

After implementation, the user explicitly approved removing the redundant server-page.tsx and its read-only banner. Pilot Plant report links now open `/maintenance?wo=<id>` directly. The protected legacy routes `/maintenance/pilot-plant` and `/maintenance/pilot-plant/:id` redirect with history replacement to the unified queue/detail. This supersedes F7's earlier requirement to retain the separate read-only screen while preserving old URLs and role-based access. No API or database changes are required.

## D1 approval update (2026-10-14)

D2 follow-up: the user approved the recommended nullable actor for automated scheduler history ("ya gunakan rekomendasi saja"). See scheduler-actor-review.md. Manual actions retain authenticated actor IDs; frontend renders automatic events as “Sistem”.

The user approved the concrete persistence-review.md with "ya setuju". This supersedes all pending D1 language below. The reviewed version comparison, unique request claim, identity/payload matching and atomic result storage cover stale-write and retry scenarios at design level; implementation tests remain mandatory. Application database deployment and scheduler activation are still separate instructions.

## Context

See proposal.md for motivation. Inspected maintenance/page.tsx, data.ts, scheduling.ts, create-work-order-dialog.tsx, update-status-sheet.tsx and work-order-dialog.tsx: all main maintenance mutations are browser-local; usage imports inventory demo data. server-page.tsx and work-orders.service.ts already read persisted WOs. reports.service.ts creates one WO per accepted Pilot Plant report, with history and audit in the acceptance transaction. Actual report submission currently targets Item, while the database also supports Equipment for manual maintenance and PM.

Quarantine already supports hold/list/detail/release/scrap with partial quantities. It creates no WO. The public kiosk has no damage-reporting function and must stay independent. Existing stock posting and reversal helpers enforce balances and audit. The Prisma schema already has WorkOrder, MaintenancePlan, history, comments, CalibrationRecord and quarantine links. Earlier change documents contain superseded checkout and Pilot Plant naming decisions: current source, schema-design.md and public-checkout-review.md govern those integrations.

## Goals / Non-Goals

**Goals:** one transaction boundary per business operation, durable target IDs, explicit execution history, reusable inventory effects, and a frontend compatibility review that preserves user ownership of existing screens.

**Non-Goals:** money/costing, rack/location entry, anonymous maintenance, checkout-to-WO linkage, arbitrary deletion of posted usage, automatic quarantine release on completion, multi-technician allocation or quantity allocation across concurrent repair WOs. No redesign of the existing visual layout is proposed.

## Decisions

### Authorization and approval ledger

Approval record (2026-10-14): after receiving the complete task summary, the user replied "ya setuju keempat nya" and then invoked openspec-apply-change add-maintenance-work-orders. P1–P6 and F1–F7 below are APPROVED within their described scope, including no technician assignment and preserving existing layout/deep links. Do not ask for those approvals again. D1's direction is accepted, but the concrete database review remains required as explained in the acknowledged summary; see persistence-review.md. Application migration deployment and scheduler activation remain separately instructed actions. This record supersedes older PENDING/policy-approval wording below and in the specs.

Confirmed: proposal covers all Maintenance and quarantine review; warehouse WO is created manually from an Inventory quarantine case; checkout only handles taking goods; additions are permitted in a future implementation but changes to existing frontend require explicit approval and reasons; database changes also require confirmation.

Confirmed follow-up: no technician assignment, selector, display or filter is needed; any active technician can execute eligible work and actual actors remain audited. Keep nullable technicianId columns and historical values without using them as authorization gates; do not remove schema columns. Proposed policies still requiring approval: P1 one active WO per case; P2 ADMIN/SUPERVISOR create/cancel and post usage, VIEWER read-only; P3 release/scrap blocked during IN_PROGRESS repair, no automatic disposition; P4 OPEN→IN_PROGRESS→COMPLETED or active→CANCELLED, terminal work not reopened in this MVP; P5 warehouse work is corrective, recurring PM/calibration is Pilot Plant only; P6 structured calibration completion is included.

Alternative to P2: technicians request usage pending supervisor approval. That adds a separate queue and quantity-reservation decisions; do not silently copy public checkout rules or implement this alternative without revising the proposal. Alternative to P1: allocate quantities among multiple WOs, requiring a separate reviewed data design. Keeping a cancelled report linked without reopening may leave a report unresolved; reviewer guidance must explicitly direct users to a new report if further work is needed, rather than re-accepting the old report.

### Existing frontend changes requiring permission

All entries below are PENDING except removal of technician assignment controls/display, which the user explicitly requested and confirmed. A future apply request does not automatically approve the remaining changes. Ask for the relevant IDs with concrete scope and rationale before editing; additions to wiring files are included for transparency.

| ID | Files / area | Proposed change | Reason |
| --- | --- | --- | --- |
| F1 | maintenance/page.tsx, data.ts, wo-table.tsx | Replace demo orders/local saves with API query, pagination and filtered status counts; preserve layout and labels | Current data disappears and omits accepted server WOs |
| F2 | create-work-order-dialog.tsx and technician displays | Approved: remove technician assignment input/display. Pending: target selector by Equipment/Item/case ID and authenticated reporter; manual privileged creation | User has four technicians and does not need assignment; target names cannot safely identify relations |
| F3 | update-status-sheet.tsx, row-actions.tsx | Valid actions and required completion/cancellation information; derived overdue/scheduled labels; add cancelled presentation | Current selector permits impossible states and bypasses report synchronization |
| F4 | work-order-dialog.tsx | Persist notes, read real sparepart catalogue and net ledger usage; replace deletion of posted usage with reasoned return/correction; role restrictions | Local arrays do not post stock and deleting posted rows destroys audit semantics |
| F5 | pm-calendar.tsx, scheduling.ts, PM create fields | Read plan occurrences and actual WOs; navigate projected occurrences as schedules; create/edit plans through API | One recurring demo object cannot track separate execution history |
| F6 | App.tsx, layout/nav-items.tsx | Add protected Inventory → Karantina route/menu and links to WO detail; preserve existing routes | Make the new review page reachable |
| F7 | maintenance/server-page.tsx and existing report navigation | Preserve current read-only route and links; optionally link to unified execution view | Existing Pilot Plant handoff must keep working; replacing/removing it requires separate explicit approval |

New files: quarantine review page, case detail/decision components, maintenance.service.ts, quarantine.service.ts (reuse existing inventory service calls), new history/calibration components, scheduler entry point and backend feature files. No edit to kiosk-checkout is planned. Existing Inventory hold UI and Stock Movement date range/columns remain outside this change. Adding controls inside an existing component still requires the applicable F approval.

### API and service structure

Keep /api/v1, existing cookie authentication, trusted origin checks, roles, Indonesian errors, explicit shared DTOs and page/pageSize contract. Extend existing WO endpoints compatibly; preserve existing DTO fields such as pilotEntityId for current consumers and add explicit target kind/ID/display ancestry, quarantine, names, lifecycle, resolution and usage/history. Move route ownership only with compatibility tests; static routes precede /:id. Services must not import Express types.

| Endpoint proposal | Operation |
| --- | --- |
| GET /work-orders; /work-orders/:id | Expand existing list/detail, bounded q, source/type/status/priority and date filters; no technician filter |
| GET /work-orders/summary | Counts using the same non-status filters; status buckets independent of active selected tab |
| POST /work-orders | Privileged manual corrective/calibration Pilot Plant work; recurring preventive creation uses plans |
| PATCH /work-orders/:id | Guarded metadata, due date and resolution update; no direct status/target/actor or assignment overwrite |
| POST /work-orders/:id/start; /complete; /cancel | Explicit validated transitions with audit/history |
| GET /maintenance/targets | Searchable active Equipment/Item/case target choices; no technician lookup endpoint |
| GET /work-orders/:id/parts; POST /work-orders/:id/parts/issues; /parts/returns | Ledger-derived usage and stock operations |
| GET /quarantine-cases; /quarantine-cases/:id | Extend existing reads with q/status/WO state/date filters and linked work summaries |
| POST /quarantine-cases/:id/work-orders | Manual repair decision; stable request key |
| POST /quarantine-cases/:id/release; /scrap | Reuse existing endpoints with repair-state guard after P3 approval |
| GET/POST /maintenance-plans; GET/PATCH /maintenance-plans/:id | Plan creation, edit and pause through isActive; no deletion of history |
| GET /maintenance/calendar?dateFrom=&dateTo= | Bounded projection merged with real occurrences; no writes |

All dated filters explicitly identify their date basis: queue report dates, WO created/due date as separately named filters, calendar scheduled dates. Date-only inputs use facility timezone; instants use UTC. Document limits and invalid range responses before shipping. No price fields in DTOs. No frontend-generated MAX(number)+1; use existing generatedCode and unique constraints.

### Transaction coordination and persistence checkpoint

Use existing model relations, authenticated user attribution and append-only history. Lock/read current state before transitions. Report-linked actions acquire report then WO locks, matching acceptance's report-first order. Inventory-coordinated work acquires sorted Part locks before WO/case locks, matching existing disposition/reversal. Recheck terminal state, activity and remaining stock inside the transaction. Required audits and related writes share one transaction client; bounded retry only for recognized conflicts.

Concrete persistence proposal D1 (PENDING, no schema changes authorized): add unsigned version default 0 to WorkOrder and MaintenancePlan for stale edits, plus MaintenanceRequest containing unique key, actorId, operation, payloadHash, result JSON and createdAt for manual WO/plan mutations. Existing rows retain identity/history; acceptance-generated WOs use version default. Request claim/result and business writes commit together; mismatched actor/payload/operation conflicts. Keys are not audit metadata. Public DTOs exclude request records. PM occurrence uniqueness already exists on (planId, scheduledFor).

Alternative without D1: conditional updatedAt/state writes plus existing InventoryRequest for stock-only operations; manual creation still requires an explicitly designed replay mechanism. Do not implement a partial retry contract or add unreviewed persistence fields. Prepare exact D1 models, migration SQL and ERD diff as review material before requesting database approval; user has not yet approved D1. Migration deployment is a separate action.

WO part usage calls the shared ledger posting boundary with source WORK_ORDER. Return limits use net issued quantity. The quarantine part being repaired is not consumed merely because a WO starts. An existing public checkout cannot be relabelled as WO consumption. Terminal corrections remain explicit privileged Inventory operations; audit shows actual occurrence after closure rather than rewriting completion time. Refresh usage and report views after reversal.

### PM and calibration

Use MaintenancePlan, no duplicated repeating WO as a plan. Default lead days 0 and first due date equal start date to match the consumer. Interpret a due date as the end of that facility day for overdue display, while release starts the scheduled day. Preserve recurrence anchor through short months and leap years. A dedicated scheduler command processes active plans in bounded batches under locking and uniqueness, advances nextDueAt atomically, disables ONCE after generation and catches up missed occurrences. It never runs implicitly on GET. Deployment must install a recurring job; CLI implementation alone does not make the scheduler operational.

Calendar windows are limited to 366 days and results have a documented ceiling with an explicit narrower-range error rather than truncation. Merge actual WO records by planId/scheduledFor with projections; preserve actual historical status. Restrict edits to immutable target/type/start/cycle after first generation; rescheduling then pauses old plan and creates a new plan without altering released work. Explain this restriction before F5 approval. Archived targets must surface actionable scheduler errors, not silent skipping. Technician assignment never gates generation; inactive authenticated actors cannot execute work.

Calibration completion adds one CalibrationRecord linked to the same target/WO with performer, calibratedAt, nextDueAt, result, optional certificate number and notes. Certificate file upload is deferred; do not invent an upload subsystem from the existing optional URL field. Result and work completion are atomic; nextDueAt > calibratedAt. No mandatory FailureEvent creation is inferred for every corrective ticket; detailed downtime/failure analytics remains separate from this execution scope.

## Risks / Trade-offs

- Existing UI changes exceed new-page additions → enforce F1–F7 before touching existing components; preserve unapproved sections and never mark integration complete while blocked.
- New stock issue plus previous public checkout can double-count a real withdrawal → show explicit separate-workflow guidance; never import checkout usage automatically.
- Repair/disposition/reversal races → shared lock order, validate remaining quantities and active WO inside transactions; integration tests include existing reversal paths.
- No scheduler deployed → plans appear in calendar but no tickets emerge; deployment checklist requires an actual scheduled invocation and health evidence.
- Different operators overwrite notes or duplicate work → D1 or approved equivalent must exist before enabling writes.
- Existing specs live in unarchived changes → preserve compatibility and do not rewrite or archive unrelated changes as part of this proposal.

## Migration Plan

1. Review P1–P6, F1–F7 and D1 separately; record exact approvals in this design before dependent work.
2. If D1 approved, create additive migration and ERD updates, validate on isolated fixtures including existing accepted reports and public checkout rows; do not reset the application DB.
3. Build backend and new components, preserve old contracts, then integrate only approved frontend edits. Ship as a coordinated API/shared/frontend release.
4. Deploy approved migration explicitly, generate client, enable scheduler and verify one released occurrence and one quarantine handoff.
5. Roll back routes/features by disabling writes and scheduler; retain historical work, stock and additive schema. Never undo posted movements by deleting database records.
