Compositor-agnostic floating window state cache.
Tracks which windows are floating, dual-keyed so both float kinds resolve:
- Instance-specific floats (a single window the user/rule floated) are keyed by the STABLE instanceId, like
ZoneCache. A window's appId can mutate mid-session (Electron / CEF apps rename their window class), changing the composite appId|instanceId but never the instanceId — keying by it keeps a floated window resolvable across that rename.
- App-wide floats (a bare appId, e.g. a session-restore marker floating every instance of an app) are keyed by appId and match every instance via the fallback in
isFloating. Unlike the instanceId keyspace, this one is NOT class-mutation-safe: a bare-appId entry inserted as slack is not resolved by a later Slack. This matches the authoritative daemon's own appId-keyed markers (same exposure); the rename-robustness guarantee covers only the instance keyspace above.
Mirrors the authoritative WindowTrackingService::setFloating: unfloating a specific instance ALSO drops the coarse app-wide (bare appId) entry. Still- floating siblings keep their own instance entries, so this never clears them; it only removes the session-restore appId marker (matching the daemon, which removes the appId entry unconditionally on instance unfloat — no sibling scan, which would not survive a mid-session class rename anyway).