mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
fix(plan): legacy room drafts are healed, not a locked door
A plan that still carried a `room_drafts` key while already on the current wall model could not be edited at all. The card mirrors the same migration, so a structural edit was refused before the request ever left the browser; the toast sent the user to "Optimize plans", which reports that everything is already optimal because it looks at something else entirely; and the export path calls the same migration, so the one way out — take a backup, fix the file by hand — was shut too. An empty `room_drafts: []`, carrying no data at all, was enough to do it. The carrier is now removed the way the first migration removes it: an empty key silently, drafts converted one for one into partitions. The #478 protection against a stale client re-adding the carrier moves to the layer that can actually tell the two apart — `validate_wall_model_transition` sees both the submission and the stored plan, and refuses when the drafts appear over a plan that does not have them. It no longer keys on the submitted model number: a stale card echoes back the number it was given, which is exactly how the outdated client slipped past this guard and met "conflicting wall identifiers" instead of "update the card and reload the page". The schema invariant keeps refusing a non-empty carrier as the last line. Both mirrors change together and stay identical; the parity fixture is untouched. Issue: #529 User-Visible: yes
This commit is contained in:
@@ -3611,6 +3611,53 @@ const MUTANT_DEFINITIONS = [
|
||||
replace: " if (ledger.caught[mutant.id] === fingerprint || mutant.id in ledger.caught) skipped.push(mutant);",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'room-drafts-refuse-instead-of-heal',
|
||||
guard: 'node scripts/backend-test-guard.mjs '
|
||||
+ 'room_drafts_on_current_model_convert_exactly_like_the_first_migration '
|
||||
+ 'tests_backend/test_wall_segment_model.py',
|
||||
because: '#529: наследие обязано сниматься миграцией, а не запирать план — отказ здесь '
|
||||
+ 'отбивал структурную правку ещё до отправки на сервер и заодно ломал экспорт, '
|
||||
+ 'то есть единственный способ вытащить бэкап и починить конфиг руками',
|
||||
patches: [{
|
||||
file: 'custom_components/houseplan/wall_segment_model.py',
|
||||
find: ' if not drafts:\n'
|
||||
+ ' space.pop("room_drafts", None)\n'
|
||||
+ ' return 0, 0',
|
||||
replace: ' if True:\n'
|
||||
+ ' raise WallSegmentMigrationError("duplicate-id", "model v10 must not contain room_drafts")',
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'outdated-client-detected-by-model-number',
|
||||
guard: 'node scripts/backend-test-guard.mjs '
|
||||
+ 'outdated_client_is_recognised_by_the_carrier_not_by_the_model_number '
|
||||
+ 'tests_backend/test_wall_segment_model.py',
|
||||
because: '#529: устаревшая карточка возвращает эхом полученный номер модели, поэтому '
|
||||
+ 'признаком служит сам носитель поверх чистого сохранённого конфига; сверка по '
|
||||
+ 'номеру пропускала ровно тот случай, ради которого сторож и написан (#478 AC2)',
|
||||
patches: [{
|
||||
file: 'custom_components/houseplan/validation.py',
|
||||
find: ' if old_model >= 10 and not stored_drafts and any(',
|
||||
replace: ' if old_model >= 10 and new_model < 10 and any(',
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'room-drafts-mirror-still-throws',
|
||||
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '
|
||||
+ '&& node --test --test-name-pattern="#529" test/wall-segment-model.test.mjs',
|
||||
because: '#529: зеркало в карточке лечит хранилище — именно оно снимает ключ в кандидате, '
|
||||
+ 'который уходит в config/set; если оно снова бросает, план остаётся запертым '
|
||||
+ 'независимо от того, что умеет бэкенд',
|
||||
patches: [{
|
||||
file: 'src/wall-segment-model.ts',
|
||||
find: ' if (!drafts.length) {\n'
|
||||
+ ' delete space.room_drafts;\n'
|
||||
+ ' return { drafts: 0, segments: 0 };\n'
|
||||
+ ' }',
|
||||
replace: " if (true) throw new WallSegmentModelError('duplicate-id', 'model v10 must not contain room_drafts');",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'css-minifier-skips-typescript-output',
|
||||
guard: 'node --test --test-name-pattern="#526 AC4|#526 AC2" test/bundle-assets.test.mjs',
|
||||
|
||||
Reference in New Issue
Block a user