4.1 The ID grammar
Everything starts with parsing. The grammar defines what a color interop ID is as a string: the published base IDs, vendor: namespaces with the strip-the-leftmost-namespace fallback, the config-local form (<config>:local:<base>) for spaces that exist in only one config, the reserved inner local namespace, and the utility tokens (data, unknown, bypass) that short-circuit resolution entirely. Resolution runs through the existing resolve()/equivalent() verbs: a name or ID either resolves to a space in the active config — via name, alias, or declared interop_id — or it resolves to nothing. There is no “closest match” tier.
The grammar is deliberately boring, and that is its virtue: every later part (the priority list, the planner, the search API) delegates its string-handling here, so namespacing and fallback behave identically at every entry point. In the demo, color_interop_helper.py carries the reference implementation and ColorConfig.resolve() in color_interop_oiio.py routes through it.