The two translations a stored patch needs, built from the boxes the desk currently sees:
a live port → the box input it carries, and a box input → the port carrying it right now.
This is where the two declarations meet, and neither is re-derived here. WHICH BOX a node
belongs to comes from the device's published boxId (the registry's identity, overlaid on
detect); WHICH INPUT a port carries comes from resolvePhysicalInput — the contract table in
physical-input-map.ts, not a regex in a consumer. So a new transport adds a row there and
arrives here for free.
A box that has not enrolled has no identity to store a patch against and is skipped: its
ports then store by name, which is the honest fallback for something the console cannot yet
name. Ports whose input the map does not recognise are skipped for the same reason.
Both directions are built in ONE pass over the same source, so they cannot disagree — an
index and its inverse maintained separately drift the first time one of them is edited.
The two translations a stored patch needs, built from the boxes the desk currently sees: a live port → the box input it carries, and a box input → the port carrying it right now.
This is where the two declarations meet, and neither is re-derived here. WHICH BOX a node belongs to comes from the device's published
boxId(the registry's identity, overlaid on detect); WHICH INPUT a port carries comes fromresolvePhysicalInput— the contract table inphysical-input-map.ts, not a regex in a consumer. So a new transport adds a row there and arrives here for free.A box that has not enrolled has no identity to store a patch against and is skipped: its ports then store by name, which is the honest fallback for something the console cannot yet name. Ports whose input the map does not recognise are skipped for the same reason.
Both directions are built in ONE pass over the same source, so they cannot disagree — an index and its inverse maintained separately drift the first time one of them is edited.