Injection contract consumed by core's thin side-panel delegate. The plugin's public API is
itself this contract — no import relationship across the plugin/core boundary, only a
structurally-compatible shape on each side.
🛑 This interface had an INERT HOMONYM for months, which is what makes this paragraph
useful rather than anecdotal. A same-named file under contracts/ declared the same
three members, better documented, and was implemented by nothing. That is the most
misleading configuration there is: a reader looking for "the contract" opens the file
that bears its name first, finds it commented, and codes against a signature nobody
satisfies.
⚠️ The written instruction prescribed the opposite — pointing THIS file at the contract
— and applying it would have produced two regressions, on the only two points where the
copies had actually diverged:
① The contract declared coordinatesrequired where the runtime emits it
optional (event-bus.contract.ts) — a type narrower than the runtime, exactly
the failure mode the instruction itself warned against reproducing.
② It typed type/style as free string where this file imports the CLOSED
vocabulary AttributeWidget/AttributeEmphasis from attributes.contract.ts —
adopting it would have UNDONE the consolidation that removed five parallel
vocabularies. The contract admitted as much itself: that vocabulary "now lives in
contracts/attributes.contract.ts".
✅ The duplicate was REMOVED on 19/08/2026. It was not reachable through the
package's exports map (36 entries, no wildcard covered it), so no integrator could
name it despite its shipped .d.ts; and it had no importer. The removal therefore takes
nothing nameable away. Three homonym-free interfaces leave with it —
SidePanelFeatureDetail, SidePanelFeatureGeometry, SidePanelLayoutField — all at
zero importers: they described the payload under names the runtime never uses.
📌 The lesson carries beyond this file: a file's name does not establish its
authority. What is authoritative is what the runtime emits and what callers import,
and both are measurable.
Injection contract consumed by core's thin side-panel delegate. The plugin's public API is itself this contract — no import relationship across the plugin/core boundary, only a structurally-compatible shape on each side.
🛑 This interface had an INERT HOMONYM for months, which is what makes this paragraph useful rather than anecdotal. A same-named file under
contracts/declared the same three members, better documented, and was implemented by nothing. That is the most misleading configuration there is: a reader looking for "the contract" opens the file that bears its name first, finds it commented, and codes against a signature nobody satisfies.⚠️ The written instruction prescribed the opposite — pointing THIS file at the contract — and applying it would have produced two regressions, on the only two points where the copies had actually diverged:
① The contract declared
coordinatesrequired where the runtime emits it optional (event-bus.contract.ts) — a type narrower than the runtime, exactly the failure mode the instruction itself warned against reproducing. ② It typedtype/styleas freestringwhere this file imports the CLOSED vocabularyAttributeWidget/AttributeEmphasisfromattributes.contract.ts— adopting it would have UNDONE the consolidation that removed five parallel vocabularies. The contract admitted as much itself: that vocabulary "now lives incontracts/attributes.contract.ts".✅ The duplicate was REMOVED on 19/08/2026. It was not reachable through the package's
exportsmap (36 entries, no wildcard covered it), so no integrator could name it despite its shipped.d.ts; and it had no importer. The removal therefore takes nothing nameable away. Three homonym-free interfaces leave with it —SidePanelFeatureDetail,SidePanelFeatureGeometry,SidePanelLayoutField— all at zero importers: they described the payload under names the runtime never uses.📌 The lesson carries beyond this file: a file's name does not establish its authority. What is authoritative is what the runtime emits and what callers import, and both are measurable.