Two Conventions for One Job
The panel that docks an assistant surface into an app grew three times, independently — a full-tree sweep later established exactly three lineages. This lesson is about the moment the second one became a decision. One convention had been promoted into the shared repository. The second grew in a different corner of the family and was hand-copied into three more apps — same purpose, different mechanism: one rendered the panel as a layout sibling the host wrapped, another as a bare fixed overlay with no packing at all, which meant it covered the content instead of pushing it aside.
At its third copy, the second convention had — by the family's own heuristic — earned promotion. A fourth adopter existed and nobody knew it yet; lesson 3 is about how it surfaced.
The Trap Inside a Correct Rule
This is the interesting part. Applying the rule of three mechanically gives an answer that is locally correct and globally absurd: promote it, and now the repository that exists to remove duplication contains two components for the same purpose, with different mechanisms, both carrying the shared layer's authority. Every future change to the panel has to be made twice. Every new app has to choose. The duplication has been formalized rather than removed.
The call was to merge instead: adopt the existing shared chrome, retire the copies, and end the second lineage entirely. Four apps converted in a day, and no file of the retired convention remains anywhere in the family.
What the Merge Cost, and What It Revealed
Adopting was not free, and what it cost is specific to this merge. One of the four converting apps explains its controls with a tooltip — a component that renders a sibling node beside the control, which no number of attributes on the control can express. So the shared chrome grew a second injection shape: wrap the control in the app's own node, alongside the one it already had, which merges attributes onto it. Both optional; the consumers that use neither were untouched.
That is the pattern worth carrying away: the apps converging on the shared layer is also how the shared layer finds out what it was missing. A merge is not one-directional. The converting app had been solving the same problem for as long as the shared version had, and it had solved one part of it in a way the shared version could not express.
A later wave of the same convergence — the third lineage, retired the day after — cost considerably more per app, and the next track prices it in full: a permission that would have vanished silently, a region above the frame, and a layout fix one app handed back to everyone.