Codex stop-gate caught a real correctness bug in the previous
commit (62653659): `set_color_hex` used EQUALITY to match themed
entries against `active_theme`, but `Variable::resolve` uses
SUBSET matching. The mismatch let writes report success without
actually changing the resolved value:
Entry: {theme: Some({"mode": "dark"}), value: "#111"}
Active: {"mode": "dark", "density": "compact"}
Before: equality check fails (entry's theme has fewer keys), so
the write pushes a new entry at the end:
{theme: Some({"mode": "dark", "density": "compact"}), value: NEW}
resolve() walks first-to-last, picks the ORIGINAL entry (mode=
dark is a subset of active), returns "#111" — the new entry is
shadowed and never read.
Fix: `set_color_hex` now mirrors `resolve` exactly. Three-step
lookup matching the resolve walk order:
1. First themed entry whose `theme` map is a subset of
active_theme → write there.
2. Else, the `theme: None` default entry → write there.
3. Else, push a fresh entry keyed to active_theme (or None when
active is empty).
The write is now guaranteed to flip the resolved value when it
returns true.
Tests (2 added — direct repros of the codex BLOCK, 287 shell-core
total):
- `set_color_hex_changes_resolved_value_under_subset_active_theme`
builds the exact pathological case (entry mode-only, active
has mode+density), writes, then asserts `resolve_color`
returns the NEW value. Pre-fix this test would have read the
untouched "#111" and failed.
- `set_color_hex_falls_back_to_default_entry_when_no_subset_match`
covers the second branch — themed entry mismatch + `theme:
None` default + active that matches neither. The write must
update the default (which is what resolve falls back to) and
not append a never-read dark entry.