diff --git a/docs/adr/ADR-018-metrology-instruments-are-machines.md b/docs/adr/ADR-018-metrology-instruments-are-machines.md new file mode 100644 index 00000000..e3659cb3 --- /dev/null +++ b/docs/adr/ADR-018-metrology-instruments-are-machines.md @@ -0,0 +1,124 @@ +# ADR-018: Metrology instruments are machines + +- Status: ACCEPTED +- Date: 2026-09-04 +- Deciders: ShopDB maintainers +- Relates to: ADR-001 (asset as the platform contract), ADR-006 (collector + contract), ADR-008 (a plugin owns its tables and its migration chain) +- Supersedes: ADR-005 (equipment plugin scope vs measuringtools plugin) + +## Context + +ADR-005 split manufacturing machinery from metrology instruments into two +plugins on the grounds that they are two asset classes: machines make parts, +measuring tools measure them. It named the metrology population as "CMMs, +Keyence vision systems, wax-and-trace surface profilometers, GenSpec +instruments", and the `measuringtools` plugin was built against a docstring +naming a different population again: "calipers, micrometers, thread gages, +bore gages, height gages". + +Those two lists are not the same thing, and the difference is the whole +decision. One is bay-mounted capital equipment with a controlling PC. The other +is hand tools in a drawer. The plugin was built for the second and populated +with neither. + +Sixteen months on, the evidence is in: + +- `measuringtools` holds **zero rows**, against 204 machines, 305 computers, + 73 network devices and 53 printers. +- The metrology inventory actually waiting to be entered is 134 instruments: + 35 CMM, 32 form tracer, 23 vision, 18 wax trace, 14 measuring, 8 Genspect, + 2 eddy current, 1 spline. Every one is bay-mounted with a controlling PC. +- Not one caliper, micrometer, thread gage, bore gage or height gage. The + population the plugin's own docstring justifies it with does not exist at + the reference site and is not planned. +- `CMM` already exists as a `machinetype`, and `Part Marker` is already + classed as a machine while `Form Tracer` is classed as a measuring tool. + Two bay instruments with a controlling PC, split across two plugins, with + the line drawn somewhere no operator can predict. + +The split has therefore been paying an ongoing cost - a duplicate type page, a +settings group, a nav section, a migration chain, a searcher, dashboard cards, +map colours, label printing and four collector `pctypemap` entries - to model a +distinction that the inventory does not make. + +## Decision + +Metrology and inspection instruments are machines. The `measuringtools` plugin +is retired and the `measuring_tool` asset type is removed. + +### Calibration moves to machines, and is not lost + +`machines` gains the calibration lifecycle as nullable columns: + +``` +machines +- calibrationintervaldays integer, null +- lastcalibrationdate date, null +- nextcalibrationdate date, null +- calibrationprovider varchar(150), null +``` + +Calibration status stays **derived at read time** from `nextcalibrationdate` +(`overdue` / `duesoon` / `current` / `unknown`, 30-day window), exactly as +`measuringtools` derived it and for the same reason: a stored status is stale +the day after it is written. The helper moves across unchanged. + +This is a net gain rather than a port. A machine could not record calibration +at all before, and a part marker with a certified fixture, or a machine with a +calibrated probe, had nowhere to put it. + +### `machinetypes.ismetrology` answers ADR-005's objection + +ADR-005 made one concrete technical argument, and it deserves a concrete +answer rather than silence: + +> "show me all measuring tools" requires an enumeration of measuring-instrument +> equipmenttype values, which scales badly. + +That is correct about enumeration and it is why `machinetypes` gains a boolean +`ismetrology`. The metrology view stays a first-class single-column filter - +`WHERE mt.ismetrology = 1` - not a hardcoded list of type names that drifts the +first time someone adds an instrument type. The calibration report, the gage +lab view and any compliance export all hang off that flag. + +### What does not change + +- `assets.gaugelabreference` stays on the core asset. The gage tag was never a + `measuringtools` column, so it survives untouched and stays searchable for + every asset type. +- The collector's device identity file and the `measuringtoolid` payload alias + stay accepted. Bays staged with `measuringtool-id.txt` keep reporting; only + the asset type they resolve to changes. +- Instrument types keep their names. A Form Tracer is still a Form Tracer, now + as a `machinetype` with `ismetrology = 1`. + +## Consequences + +**The migration is free today and expensive later.** `measuringtools` has zero +rows, so retirement is a `DROP TABLE`, not a data migration. Once the 134 +staged instruments are entered they carry relationships, audit history, map +positions and collector links, and every one of those would have to be +rewritten. This ADR is being taken now specifically because the window is open. + +**A site that did populate it needs a real migration.** The reference site did +not and the sister site shows no sign of having done so - its branch contains +no deliberate work on the plugin in its entire history - but that is inference, +not a query against their database. Confirm before the removal ships to another +site. If rows exist, the retirement migration must copy them into `machines` +with `ismetrology` types rather than drop them. + +**Four collector `pctypemap` entries change asset type.** `gea-shopfloor-cmm`, +`-keyence`, `-genspect` and `-waxtrace` resolve to `machine` instead of +`measuring_tool`. The `collector:measuringtool` relationship label is kept as a +recognised origin marker so links a collector already created are still +archived correctly rather than orphaned. + +**One fewer of everything.** A duplicate type page, a settings group, a plugin +migration chain, a gated searcher, a set of dashboard cards, a map colour entry +and a label-printing branch all go. That is the point. + +**ADR-005 is superseded, not reversed in principle.** Its reasoning was sound +for the population it described. The population never arrived. If a site ever +does track hand gages in quantity, the answer is a `handgages` plugin against +that real inventory - not the reinstatement of this one.