Draft: fix(Data model): #3591607 Make component version hash stable across config-schema type casting
Component version hashes were computed from the raw, un-cast settings returned by Mapping::toArray(). A default value generated as a native PHP value (e.g. int 2 from an SDC examples entry for a number + enum prop, which maps to list_float) therefore hashed differently from the same value after config-schema casting (string "2", since core types field.value.list_float.value as string). The stored active_version then no longer matched the hash recomputed during config validation — breaking recipe application whenever config validation runs (clean installs skip it).
generateVersionHash() now hashes the config-schema-canonical values: it walks the typed config and casts each primitive leaf via PrimitiveInterface::getCastedValue() (preserving toArray()'s NULL-vs-array distinction, so unaffected components keep their hash). Both generation (PHP) and validation (post-cast) now converge.
Update path: canvas_post_update_0019_recompute_component_version_hashes() fixes existing Component config entities whose stored active_version predates this fix. It recomputes the hash and createVersion()s it, preserving the previous (incorrect) hash as a past version so existing component instances that reference it keep resolving. Components whose hash is unchanged are skipped.
AI-Generated: Yes (Used Claude Code (Opus 4.8) to root-cause the bug with the amazing info human @phenaproxima provided and draft the fix + regression test + update path).
Testing instructions
- Run
ComponentValidationTest::testActiveVersionHashIsStableForListFloatProp— it adds anumber+enum(→list_float) SDC fixture; pre-fix it fails with "The version … does not match the hash of the settings", post-fix it passes. - Run
ComponentVersionHashTypedDataCastUpdateTest— seeds a Component with the pre-fix hash and asserts it's invalid beforerunUpdates()and valid afterwards (with the old hash retained as a past version).
Closes #3591607