diff --git a/.planning/session.md b/.planning/session.md index 4308fb6a..379fb654 100644 --- a/.planning/session.md +++ b/.planning/session.md @@ -64,6 +64,14 @@ Agents should read this file before substantive work and update it as work progr ## Session Log +### 2026-03-10 (backward-compat blocker fixes) +- Implemented three backward-compatibility fixes identified during code review: + - B1: `Slide.fromMap()` now migrates legacy `comments` field to `notes` (prevents silent data loss for old JSON payloads) + - B2: `Block.fromMap()` now normalizes legacy `type: "column"` to `ContentBlock.key` (prevents runtime crash for old serialized data) + - B3: Added 3 regression tests: legacy comments migration, notes-over-comments precedence, legacy column type normalization +- Validation: all 709 core tests pass (3 new + 706 existing) +- Committed and pushed to `claude/general-session-jBtv7` + ### 2026-03-09 (review: release-next vs main in progress) - Started a code review of `release/next` against `main` using merge base `5fc2298e7580b9ef47d45cc29bc5a801d42bd622`. - Review focus: concrete regressions only, prioritized by user-visible/runtime impact, with verification against diff context and existing tests/call sites. diff --git a/packages/core/lib/src/models/block_model.dart b/packages/core/lib/src/models/block_model.dart index 3724f794..aa4943e4 100644 --- a/packages/core/lib/src/models/block_model.dart +++ b/packages/core/lib/src/models/block_model.dart @@ -62,7 +62,9 @@ sealed class Block with BlockMappable { ); factory Block.fromMap(Map map) { - final type = map['type'] as String; + var type = map['type'] as String; + // Normalize legacy 'column' type to 'block'. + if (type == 'column') type = ContentBlock.key; return switch (type) { SectionBlock.key => SectionBlock.fromMap(map), ContentBlock.key => ContentBlock.fromMap(map), diff --git a/packages/core/lib/src/models/slide_model.dart b/packages/core/lib/src/models/slide_model.dart index 989ff958..2697bc9a 100644 --- a/packages/core/lib/src/models/slide_model.dart +++ b/packages/core/lib/src/models/slide_model.dart @@ -33,8 +33,14 @@ class Slide with SlideMappable { this.notes = const [], }); - factory Slide.fromMap(Map map) => - SlideMapper.fromMap(Map.from(map)); + factory Slide.fromMap(Map map) { + final normalized = Map.from(map); + // Migrate legacy 'comments' field to 'notes'. + if (normalized.containsKey('comments') && !normalized.containsKey('notes')) { + normalized['notes'] = normalized.remove('comments'); + } + return SlideMapper.fromMap(normalized); + } /// Validation schema for slide data. static final schema = slideSchema.extend({ diff --git a/packages/core/test/src/models/block_model_test.dart b/packages/core/test/src/models/block_model_test.dart index b9f72232..01085362 100644 --- a/packages/core/test/src/models/block_model_test.dart +++ b/packages/core/test/src/models/block_model_test.dart @@ -760,6 +760,14 @@ void main() { expect((block as WidgetBlock).name, 'Test'); }); + test('normalizes legacy column type to block', () { + final map = {'type': 'column', 'content': 'Legacy content'}; + final block = Block.fromMap(map); + + expect(block, isA()); + expect((block as ContentBlock).content, 'Legacy content'); + }); + test('throws for unknown type', () { final map = {'type': 'unknown'}; expect(() => Block.fromMap(map), throwsArgumentError); diff --git a/packages/core/test/src/models/slide_model_test.dart b/packages/core/test/src/models/slide_model_test.dart index 5b46a30e..5128f83c 100644 --- a/packages/core/test/src/models/slide_model_test.dart +++ b/packages/core/test/src/models/slide_model_test.dart @@ -182,6 +182,25 @@ void main() { expect(slide.notes, ['Comment 1', 'Comment 2']); }); + test('migrates legacy comments field to notes', () { + final slide = Slide.fromMap({ + 'key': 'legacy', + 'comments': ['Old comment 1', 'Old comment 2'], + }); + + expect(slide.notes, ['Old comment 1', 'Old comment 2']); + }); + + test('prefers notes over comments when both present', () { + final slide = Slide.fromMap({ + 'key': 'both', + 'notes': ['New note'], + 'comments': ['Old comment'], + }); + + expect(slide.notes, ['New note']); + }); + test('tolerates null options for legacy payloads', () { final slide = Slide.fromMap({'key': 'legacy-slide', 'options': null});