Every registered harness name, derived from ADAPTERS — "claude-code" | "codex" today, and whatever the array holds tomorrow.
⚠️ WHAT THIS DOES NOT CATCH, stated because the type looks stronger than it
is: two adapters declaring the SAME name collapse the union silently (a union
of duplicates is that one member), so this type would still read
"claude-code" | "codex" with three adapters registered and two of them
called codex. adapter-contract.test.ts asserts the set size instead —
a test, because there is no type-level cardinality to assert against.
⚠️ AND IT IS NOT A CEILING ON COMPARING NAMES. A branded or literal string is
still comparable to a string literal in TypeScript (=== uses comparability,
not assignability — measured in the redesign's section 7), so this type cannot
make adapter.name === "codex" an error. What stops that is the
local/no-harness-names lint rule; what this type does is remove the REASON
to write such a comparison, by letting the one list be the only list.
Every registered harness name, derived from ADAPTERS —
"claude-code" | "codex"today, and whatever the array holds tomorrow.⚠️ WHAT THIS DOES NOT CATCH, stated because the type looks stronger than it is: two adapters declaring the SAME name collapse the union silently (a union of duplicates is that one member), so this type would still read
"claude-code" | "codex"with three adapters registered and two of them calledcodex.adapter-contract.test.tsasserts the set size instead — a test, because there is no type-level cardinality to assert against.⚠️ AND IT IS NOT A CEILING ON COMPARING NAMES. A branded or literal string is still comparable to a string literal in TypeScript (
===uses comparability, not assignability — measured in the redesign's section 7), so this type cannot makeadapter.name === "codex"an error. What stops that is thelocal/no-harness-nameslint rule; what this type does is remove the REASON to write such a comparison, by letting the one list be the only list.