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.

TipRun it
just edit 01                                       # from a checkout
uvx marimo run --sandbox notebooks/01_id_grammar.py

Or straight from GitHub, nothing cloned:

uvx marimo run --sandbox https://raw.githubusercontent.com/zachlewis/color_interop_demo/main/notebooks/01_id_grammar.py