WHO device lifecycle gap — implementation checklist
Ovahol's data model was compared against the WHO medical device lifecycle model described in Inventory and maintenance management information system for medical devices (WHO, 2025) — see the publication. WHO frames the device lifecycle as eight stages: procurement & selection, installation, initial inspection/commissioning, user training, performance monitoring, routine maintenance, post-market surveillance & QA, and decommissioning & disposal.
Ovahol already has strong coverage for training, routine maintenance (work orders + PM schedules), and decommissioning/disposal (decommissioning review → disposition decision → disposal record is more granular than WHO's own description). This checklist tracks the remaining gaps, ordered by priority.
Priority 1 — Performance monitoring (inspection & calibration records) — done
This was the clearest gap. WHO describes periodic inspection as checks run at a frequency driven by device risk classification, feeding into a next-due date. This checklist's boxes were still unchecked, but auditing the actual code (2026-08-31, on branch feat/inspection-calibration) found the feature fully built in an earlier session; the doc was just never updated.
- [x] Entity shape: single
PerformanceCheckentity with acheck_typediscriminator (INSPECTION|CALIBRATION) (internal/database/tenant_migrations/versions/041_performance_check.sql). - [x] Module ownership:
maintenance, alongsidePMSchedule/WorkOrder/FaultReport. - [x] Schema:
device_record_idFK,check_type,trigger,performed_at,performed_by,result, visual/functional/safety/software-validation check booleans, calibration fields (reference_standard,baseline_value,adjustment_notes,verification_value,certificate_number),next_due_date,resulting_work_order_id. Goes beyond this checklist's originally-scoped three check categories — see the "Refinements" note indocs/who-maintenance-cmms-gap-checklist.md, which is also already satisfied by this same schema. - [x] Repository:
internal/modules/maintenance/repository/performance_check.repository.go+ embeddedsql/performance_check.sql, sameQueryLoader/TenantRepositorypattern as the rest of the module. - [x] Service + DTO: full
Create/Get/List/Update/Delete/GetOverdue(service/performance_check.go,dto/performance_check.go). - [x] Handler + routes, RBAC-gated (
routes/performance_check.go: read roles includeRoleFacilityAdmin/RoleEngineeringManager/RoleEngineeringTech/RoleOrgAdmin, write narrower). - [x] Link failed checks to corrective action:
GenerateWorkOrderservice method, exposed atPOST /performance-checks/:id/generate, mirroringFaultReportService.ConvertToWorkOrder. - [x] Frontend: list/detail/create views (
frontend/src/features/tenant/performance-check/). Dashboard card was the one actually-missing piece — addedPerformanceCheckCard.vue(overdue inspections/calibrations count + top overdue list) to the facility dashboard, fed by a newoverdueChecks/fetchOverdueChecksstate onperformanceCheckStore. - [x] Export:
ListPerformanceChecksForExporton themaintenancefacade, wired intodeviceexport/service/export.go's lifecycle export. - Note:
check_type/trigger/resultended up as fixedCHECKconstraint enums rather than lookup-driven tables — a deliberate simpler choice, not a gap.
Priority 2 — Installation record — done
WHO treats installation as its own stage: site-readiness checks (power, ventilation, network, structural requirements) before the device is physically installed and connected. Ovahol previously only had device_record.installation_date — a single timestamp, no structured record of what was checked or confirmed.
- [x] Went with the lightweight option:
site_readiness_confirmed,installed_by,connections_verified(plusinstalled_by's resolved name) added directly todevice_record(internal/database/tenant_migrations/versions/045_installation_record.sql), not a fullInstallationRecordentity — matches the doc's own "don't over-build this one" note, and stays distinct from the existingDeviceCommissioningEvententity (which covers the later "goes into service" checklist, not physical installation).installed_byis a facility-member picker (samefacilityService.listMemberspattern used elsewhere), not free text, matching thecreated_by/reviewed_by/submitted_byconvention used throughout the rest of the schema. - [x] Extended for external installers:
installed_byonly fits facility staff (it's acore_public.usersFK), but installation is often done by a vendor/supplier technician instead. Addedinstaller_name(free text — external techs won't have a system account) andinstaller_vendor_id(FKcore_public.vendor) todevice_record(internal/database/tenant_migrations/versions/046_external_installer.sql), mirroring the exactvendor_idpatternRefurbishmentRecordalready uses (SQLLEFT JOIN core_public.vendor, no new cross-module facade needed). Frontend vendor options are fetched viacounterpartyApi.listVendors, the same call the refurbishment dialogs use.
Priority 3 — Post-market surveillance / regulatory reporting — done
Ovahol already had decent coverage here relative to WHO's description: IncidentReport.reportable_to_regulator and RecallNotice cover the internal tracking. What was missing is the outbound half WHO describes — tracking that a report was actually filed, not just that it should be.
- [x] Added regulator-submission tracking to
IncidentReport:regulator_name,report_reference,submitted_at,submitted_by(tenant migration044). Implemented as a dedicated action —SubmitIncidentReportToRegulator(internal/modules/lifecycle/service/asset_lifecycle.go), exposed atPOST /incidents/:id/submit-to-regulator— rather than folded intoUpdateIncidentReportRequest, matching the existing acknowledge/close-recall convention for discrete, audited workflow actions. Submitting also forcesreportable_to_regulator = TRUEfor consistency. Surfaced onIncidentDetail.vue(a "Submit to Regulator" action + display fields) and added to the Excel lifecycle export's "Incident Reports" sheet. - [x] Manufacturer notification tracking. Added
manufacturer_name,manufacturer_reference,manufacturer_notified_at,manufacturer_notified_bytoIncidentReport(internal/database/tenant_migrations/versions/049_incident_manufacturer_notification.sql), mirroring the regulator-submission columns field-for-field — a device incident often needs the manufacturer notified separately from the regulator (e.g. to trigger a field safety corrective action). Same dedicated-action pattern:NotifyIncidentManufacturer(internal/modules/lifecycle/service/asset_lifecycle.go), exposed atPOST /incidents/:id/notify-manufacturer, gated by the same management-only role restriction as the regulator-submission action. Surfaced onIncidentDetail.vue(a "Notify Manufacturer" action + display fields, independent ofreportable_to_regulatorsince a manufacturer notification doesn't require regulator reportability) and added to the Excel lifecycle export's "Incident Reports" sheet.
Priority 4 (deferred) — Predictive maintenance
WHO calls out IoT/AI-driven predictive maintenance as an emerging practice layered on top of performance monitoring. Ovahol has no sensor/IoT integration and this is a large, separate capability — not worth scoping in detail until Priority 1 (performance monitoring) exists, since predictive maintenance is meaningless without the underlying inspection/calibration history to predict from.
- [ ] Once Priority 1 ships and has real data, consider a lightweight first step: simple trend-based due-date suggestions from accumulated inspection/calibration results, short of any actual ML/sensor integration.
Explicitly out of scope
- Procurement needs-assessment and evidence-based device selection (WHO stage 1's pre-purchase decision process) — this is a health-system planning/finance concern, not equipment-management data. Ovahol's
procurementmodule already covers the acquisition transaction itself (vendor, receipt, acceptance approval); the selection decision upstream of that is a different kind of tool.