The previous commit let users cycle empty matrix cells in the modal but the engine only iterated `payload.matrix` — overrides for keys absent from the library were silently ignored. Cycling produced dead UI: state saved, engine ignored, runtime identical. Engine extension: after the library-merge pass, do a second pass over `cell_mode_overrides` ∪ `cell_factor_overrides` minus `covered_keys`. Each leftover key becomes a new matrix entry with implicit baseline mode "allow" + factor 1.0; whichever side(s) the user set populate the rest. Disabled set still wins — entries in `disabled_matrix_cells` skip both passes. Added `decode_key` helper alongside `encode_key` for the second-pass src/tgt extraction. Round-trip + malformed-input rejection covered by new key tests. Malformed override keys in the resolve path are skipped silently rather than crashing the run (defensive — instance JSON could have rotted across schema migrations). Updated the existing "unknown key silently ignored" test to assert the new behavior (entry created from override). Added focused tests for: factor-only override defaults to allow mode, disabled set still skips empty-cell overrides, malformed key in override map doesn't crash.
27 lines
952 B
Python
27 lines
952 B
Python
"""Composite-key encoder. Mirrors JS `encodeKey` byte-for-byte.
|
|
|
|
Spec invariant: composite keys are JSON-encoded string arrays, no
|
|
whitespace, unicode literal (not escaped), array order preserved.
|
|
Cross-language fitness test in `tests/test_engine_keys.py` enforces
|
|
byte-exact match.
|
|
|
|
See: docs/superpowers/specs/2026-05-07-instance-overrides-modal-design.md §4.5
|
|
"""
|
|
import json
|
|
|
|
|
|
def encode_key(parts: list[str]) -> str:
|
|
return json.dumps(parts, separators=(",", ":"), ensure_ascii=False)
|
|
|
|
|
|
def decode_key(key: str) -> list[str]:
|
|
"""Inverse of encode_key. Round-trip safe by spec invariant.
|
|
|
|
Raises ValueError on malformed input — caller should treat unknown
|
|
keys as "skip silently" rather than crash the resolve.
|
|
"""
|
|
parts = json.loads(key)
|
|
if not isinstance(parts, list) or not all(isinstance(p, str) for p in parts):
|
|
raise ValueError(f"composite key must decode to list[str], got {parts!r}")
|
|
return parts
|