Skip to content

Sync morph type data#2192

Open
rmunn wants to merge 52 commits intodevelopfrom
feat/sync-morph-types
Open

Sync morph type data#2192
rmunn wants to merge 52 commits intodevelopfrom
feat/sync-morph-types

Conversation

@rmunn
Copy link
Contributor

@rmunn rmunn commented Mar 5, 2026

Fixes #1847.

CRDT CRUD operations for MorphTypeData are not yet implemented, so this
will fail to sync and throw a NotImplementedException if you actually
try to sync.
@rmunn rmunn self-assigned this Mar 5, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1919341c-a51f-4ff5-b466-3bf02f63e536

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request adds comprehensive MorphTypeData support to the CRDT system, including database migrations, change tracking, API implementations, sync logic, and test infrastructure. Key features include unique constraint enforcement on MorphType, immutability of MorphType post-creation, duplicate prevention, and nullable LeadingToken/TrailingToken properties.

Changes

Cohort / File(s) Summary
Database Migration
backend/FwLite/LcmCrdt/Migrations/20260306045826_AddMorphTypeDataTable*
Adds MorphTypeData table with Id (PK), MorphType (unique), Name/Abbreviation/Description (jsonb), LeadingToken/TrailingToken (nullable), SecondaryOrder, DeletedAt, SnapshotId (FK). Includes designer definitions and Up/Down migration logic.
CRDT Change Type
backend/FwLite/LcmCrdt/Changes/CreateMorphTypeDataChange.cs
New public class implementing CreateChange with properties for all MorphTypeData fields, constructor mapping, JsonConstructor support, and NewEntity override that enforces unique MorphType constraint by checking for existing entries and setting DeletedAt accordingly.
API Implementation
backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs
Updated six methods (GetAllMorphTypeData, GetMorphTypeData, CreateMorphTypeData, UpdateMorphTypeData overloads, DeleteMorphTypeData) to async patterns; implements duplicate checking, MorphType patch rejection, and change tracking via CreateMorphTypeDataChange, JsonPatchChange, and DeleteChange.
CRDT Infrastructure
backend/FwLite/LcmCrdt/LcmCrdtKernel.cs, backend/FwLite/LcmCrdt/LcmCrdtDbContext.cs, backend/FwLite/LcmCrdt/Data/MiniLcmRepository.cs
Registers MorphTypeData as object type and change types (JsonPatchChange, DeleteChange, CreateMorphTypeDataChange); adds AllMorphTypeData queryable property; configures unique index on MorphType.
Database Model Snapshots
backend/FwLite/LcmCrdt/Migrations/LcmCrdtDbContextModelSnapshot.cs, backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyDbModel.verified.txt, backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyChangeModels.verified.txt, backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyIObjectWithIdModels.verified.txt
Updates model snapshots to reflect new MorphTypeData entity with all properties, unique indexes, and change type definitions (CreateMorphTypeDataChange, JsonPatchChange, DeleteChange).
Property Nullability & Models
backend/FwLite/MiniLcm/Models/MorphType.cs, backend/FwLite/FwDataMiniLcmBridge/Api/UpdateProxy/UpdateMorphTypeDataProxy.cs, backend/FwLite/MiniLcm/Models/IObjectWithId.cs
Changes LeadingToken and TrailingToken from non-nullable strings with defaults to nullable strings (string?); adds JsonDerivedType attribute for MorphTypeData polymorphic serialization.
Sync & Constraints
backend/FwLite/MiniLcm/SyncHelpers/MorphTypeDataSync.cs, backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
Implements MorphTypeDataSync with guard preventing MorphType changes post-creation; adds patch validation in FwDataMiniLcmApi.UpdateMorphTypeData to reject MorphType modifications with InvalidOperationException.
Project Snapshot
backend/FwLite/MiniLcm/ProjectSnapshot.cs, backend/FwLite/MiniLcm/MiniLcmApiExtensions.cs
Adds AllMorphTypeData field to ProjectSnapshot record after ComplexFormTypes; updates TakeProjectSnapshot to collect MorphTypeData via GetAllMorphTypeData().ToArrayAsync().
Import & Resumable API
backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs, backend/FwLite/FwLiteProjectSync/MiniLcmImport.cs
Adds CreateMorphTypeData method to ResumableImportApi using HasCreated pattern; adds morph type import loop in MiniLcmImport after publications; corrects log message for publications.
Test Infrastructure
backend/FwLite/FwLiteProjectSync.Tests/Import/ResumableTests.cs, backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs, backend/FwLite/LcmCrdt.Tests/SnapshotAtCommitServiceTests.cs
Adds MorphTypeData test data, expectedMorphTypes dataset, mock returns, end-to-end assertions; implements CreateMorphTypeData in UnreliableApi test wrapper; adds .WithoutStrictOrderingFor(x => x.AllMorphTypeData) to snapshot equivalence checks; increases maxTries from 20 to 30.
Test Snapshots & Verified Data
backend/FwLite/FwLiteProjectSync.Tests/sena-3-live_snapshot.verified.txt, backend/FwLite/LcmCrdt.Tests/Changes/ChangeDeserializationRegressionData.latest.verified.txt, backend/FwLite/LcmCrdt.Tests/Changes/UseChangesTests.cs, backend/FwLite/LcmCrdt.Tests/Data/MigrationTests.cs, backend/FwLite/LcmCrdt.Tests/Data/Migration*verified.txt, backend/FwLite/LcmCrdt.Tests/SnapshotDeserializationRegressionData.latest.verified.txt, backend/FwLite/FwLiteProjectSync.Tests/ProjectSnapshotSerializationTests.cs
Populates snapshots with AllMorphTypeData entries; adds MorphTypeDataChange deserialization test data; updates MigrationTests to fetch and pass AllMorphTypeData; standardizes path construction; includes 18+ morph type entries (Prefix, Suffix, Infix, etc.) with full field definitions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • #1913 — Shares ProjectSnapshot test comparison logic (AssertSnapshotsAreEquivalent with AllMorphTypeData ordering).
  • #1857 — Modifies UpdateMorphTypeDataProxy nullability and overlaps on MorphTypeData API surface.
  • #1760 — Introduces ResumableImportApi infrastructure that this PR extends with MorphTypeData support.

Suggested reviewers

  • hahn-kev
  • myieye

Poem

🐰 Hops through the database with glee,
MorphTypes now dance in harmony!
Constraints locked tight, changes denied,
A CRDT structure with rabbity pride!
Snapshots and syncs in perfect refrain—
Another great feature in LangBox's domain! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Sync morph type data' clearly and concisely describes the main objective of the changeset, which is to implement CRDT synchronization support for MorphTypeData.
Description check ✅ Passed The description references issue #1847 and is related to the changeset's primary objective of implementing MorphTypeData synchronization in the CRDT system.
Linked Issues check ✅ Passed The PR implements all objectives from #1847: models MorphTypeData in CRDT system, adds change types (CreateMorphTypeDataChange, JsonPatchChange, DeleteChange), enforces MorphType uniqueness via SQLite inline constraint, makes MorphType immutable post-creation, and prevents duplicate creation via tombstoning logic in NewEntity.
Out of Scope Changes check ✅ Passed All code changes are within scope of #1847 objectives. Changes include database schema, CRDT change handling, validation logic, test updates, and synchronization logic—all directly supporting MorphTypeData CRDT sync implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/sync-morph-types
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label Mar 5, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

UI unit Tests

  1 files  ±0   54 suites  ±0   26s ⏱️ -1s
140 tests ±0  140 ✅ ±0  0 💤 ±0  0 ❌ ±0 
207 runs  ±0  207 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit ae339ea. ± Comparison against base commit 469a825.

♻️ This comment has been updated with latest results.

@argos-ci
Copy link

argos-ci bot commented Mar 5, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Mar 16, 2026, 8:10 AM

rmunn added 4 commits March 6, 2026 12:26
In liblcm, the .Prefix and .Postfix properties can be (and often are)
null, so the corresponding LeadingToken and TrailingToken properties in
MorphTypeData need to be nullable as well. The UI will have to do ?? ""
in appropriate places as it displays the leading/trailing tokens.
@rmunn rmunn changed the title WIP (not working yet): sync morph type data WIP: sync morph type data Mar 6, 2026
@rmunn rmunn force-pushed the feat/sync-morph-types branch from 8003c78 to 7563c10 Compare March 9, 2026 07:05
rmunn added 2 commits March 9, 2026 14:13
This one used yet another snapshot file that I hadn't updated yet
@rmunn
Copy link
Contributor Author

rmunn commented Mar 9, 2026

Kind of bothered by the fact that I don't see FwDataApiDoesNotUpdateMorphType failing yet; that test is supposed to start failing once we start syncing morph type. The fact that it's not failing yet may mean I've failed to implement something.

rmunn added 4 commits March 9, 2026 15:05
This time instead of an empty list of morph types, we'll use the actual
morph types found in the .fwdata file, which is what the test will
actually see.
Needed both the Snapshot *and* the sqlite3 database to be updated to
expect the new MorphTypeData table.
Check in two places: the API, which is the most likely place to catch
duplicates, but also in the Change object itself, in case two people
somehow end up submitting the same change twice (e.g., one person was
offline for a period of time).
@rmunn
Copy link
Contributor Author

rmunn commented Mar 10, 2026

Okay, tests are finally green, so I can finally push the "don't create duplicate MorphTypes" logic and make sure it doesn't break the tests.

Now that the API prevents creating duplicate MorphTypes, we can add a
database constraint to enforce that at the DB level.
@rmunn rmunn changed the title WIP: sync morph type data Sync morph type data Mar 10, 2026
rmunn added 3 commits March 12, 2026 10:39
Instead of passing all the object's properties as separate params to
CreateMorphTypeDataChange, it's much simpler to just pass in the entire
object, similarly to how it's done for Entry and other large objects.
Turns out we don't need ON CONFLICT IGNORE since the wrappers we have
built around EF Core will handle it for us: any object that gets created
in an already-deleted state (as we do for duplicate morph types) will
never be sent to the DB in the first place. So it's safe to use EF
Core's API for the migration after all, keeping it consistent with how
the other migrations are written.
Editing typo that caused far too much head-scratching
@rmunn
Copy link
Contributor Author

rmunn commented Mar 12, 2026

@CodeRabbit fullreview

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

@rmunn: Triggering a full review of the PR now.


🧠 Learnings used
Learnt from: rmunn
Repo: sillsdev/languageforge-lexbox PR: 0
File: :0-0
Timestamp: 2026-03-10T08:33:24.170Z
Learning: In backend/FwLite/LcmCrdt, the MorphTypeData table has a unique index on the MorphType column (corresponding to the MorphType C# enum). The intended conflict resolution is "first one wins" using SQLite's ON CONFLICT IGNORE, because: (1) CRDT's NewEntity() cannot return null, so duplicate CreateMorphTypeDataChange objects (from concurrent offline users) result in a second row with DeletedAt set, potentially violating the unique constraint; (2) In practice, users cannot create new morph types in FieldWorks Classic or Lite. SQLite's ON CONFLICT IGNORE clause can only be applied to constraints defined inline in CREATE TABLE (not to separately-created indexes via CREATE UNIQUE INDEX), so implementing this requires either rebuilding the table with an inline UNIQUE ... ON CONFLICT IGNORE constraint or using an EF Core SaveChanges interceptor with INSERT OR IGNORE.

Learnt from: rmunn
Repo: sillsdev/languageforge-lexbox PR: 0
File: :0-0
Timestamp: 2026-03-10T08:50:35.872Z
Learning: In sillsdev/languageforge-lexbox LcmCrdt, when a SQLite table needs `UNIQUE ON CONFLICT IGNORE` on a column (e.g., MorphTypeData.MorphType), the EF Core migration must use `migrationBuilder.Sql()` for the CREATE TABLE DDL (since EF Core's CreateTable() API doesn't support ON CONFLICT clauses). A separate named `CREATE UNIQUE INDEX` should also be emitted via `migrationBuilder.Sql()` so EF Core's model snapshot stays consistent with `HasIndex(...).IsUnique()` configuration. SQLite allows both the inline constraint and the named index on the same column; the inline ON CONFLICT IGNORE fires first on duplicate inserts.

Learnt from: rmunn
Repo: sillsdev/languageforge-lexbox PR: 2192
File: backend/FwLite/LcmCrdt/Changes/CreateMorphTypeDataChange.cs:21-32
Timestamp: 2026-03-11T02:23:21.607Z
Learning: In sillsdev/languageforge-lexbox LcmCrdt, the MorphTypeData table's UNIQUE ON CONFLICT IGNORE constraint on the MorphType column is implemented in migration 20260306045826_AddMorphTypeDataTable.cs using migrationBuilder.Sql() with a hand-written CREATE TABLE DDL (since EF Core's CreateTable() API doesn't support ON CONFLICT clauses). A separate migrationBuilder.CreateIndex() for IX_MorphTypeData_MorphType is also emitted so EF Core's model snapshot stays consistent with HasIndex(...).IsUnique() configuration. A separate migration 20260310065457_UniqueIndexOnMorphType (which used CREATE UNIQUE INDEX without ON CONFLICT support) was deleted as part of this consolidation.

Learnt from: rmunn
Repo: sillsdev/languageforge-lexbox PR: 0
File: :0-0
Timestamp: 2026-03-10T08:34:03.262Z
Learning: In the LcmCrdt SQLite database for sillsdev/languageforge-lexbox, `ON CONFLICT IGNORE` for a unique constraint on MorphTypeData.MorphType cannot be implemented via a standalone `CREATE UNIQUE INDEX` statement (SQLite doesn't support ON CONFLICT clauses on CREATE INDEX). It must be a table-level constraint in the CREATE TABLE DDL, requiring a table-rebuild migration. The practical risk is low because morph type creation is restricted in both FieldWorks Classic and FW Lite, but the theoretical CRDT offline-sync race condition exists.

Learnt from: CR
Repo: sillsdev/languageforge-lexbox PR: 0
File: backend/FwLite/AGENTS.md:0-0
Timestamp: 2026-02-04T14:29:56.355Z
Learning: Applies to backend/FwLite/**/MiniLcm/SyncHelpers/*.cs : When adding new fields to MiniLcm models, add diff logic for the new field in the appropriate sync helper file (e.g., EntrySync.cs for Entry fields). Sync bugs here cause data loss.

Learnt from: rmunn
Repo: sillsdev/languageforge-lexbox PR: 2192
File: backend/FwLite/FwLiteProjectSync/CrdtFwdataProjectSyncService.cs:125-127
Timestamp: 2026-03-11T04:58:14.077Z
Learning: In sillsdev/languageforge-lexbox backend/FwLite/FwLiteProjectSync/CrdtFwdataProjectSyncService.cs SyncInternal, the two-pass sync pattern is intentional and lossless: pass 1 (e.g., line 126 for MorphTypeData) updates the live CRDT from FW data using the project snapshot as "before"; pass 2 (e.g., line 127) reads the *already-updated* live CRDT and pushes it to FW. This means a null value in the CRDT snapshot cannot destructively wipe an FW value — it will be reconciled to the FW value in pass 1 before pass 2 ever runs. A null reaching FW in pass 2 only happens when the user intentionally deleted it in CRDT, which is correct bidirectional sync behavior.

Learnt from: CR
Repo: sillsdev/languageforge-lexbox PR: 0
File: backend/FwLite/AGENTS.md:0-0
Timestamp: 2026-02-04T14:29:56.355Z
Learning: Applies to backend/FwLite/**/MiniLcm/Models/Entry.cs : Entry is a core model that affects all downstream systems. When adding fields to Entry, ensure proper implementation in Copy(), GetReferences(), RemoveReference(), CRDT mirroring, change handling, sync logic, FwData mapping, and comprehensive test coverage across all three test projects

Learnt from: CR
Repo: sillsdev/languageforge-lexbox PR: 0
File: backend/FwLite/AGENTS.md:0-0
Timestamp: 2026-02-04T14:29:56.355Z
Learning: Applies to backend/FwLite/**/FwLiteProjectSync.Tests/**/*.cs : When fixing a sync bug, reproduce it with a test. Use `DryRunMiniLcmApi` to see what changes would be made. Debug through `CrdtFwdataProjectSyncService.Sync()` and verify `ProjectSnapshot` handling.

Learnt from: CR
Repo: sillsdev/languageforge-lexbox PR: 0
File: backend/FwLite/AGENTS.md:0-0
Timestamp: 2026-02-04T14:29:56.355Z
Learning: Applies to backend/FwLite/**/*.cs : Before making changes to MiniLcm, LcmCrdt, FwDataMiniLcmBridge, or FwLiteProjectSync: read the relevant section thoroughly, understand the sync flow end-to-end, run the full test suite with `dotnet test FwLiteOnly.slnf`, and test with real FwData projects

Learnt from: CR
Repo: sillsdev/languageforge-lexbox PR: 0
File: backend/FwLite/AGENTS.md:0-0
Timestamp: 2026-02-04T14:29:56.355Z
Learning: Applies to backend/FwLite/**/LcmCrdt/Objects/*.cs : When adding a new field/property to MiniLcm models, mirror the property exactly in the corresponding LcmCrdt entity class

Learnt from: CR
Repo: sillsdev/languageforge-lexbox PR: 0
File: backend/FwLite/AGENTS.md:0-0
Timestamp: 2026-02-04T14:29:56.355Z
Learning: Before making changes to MiniLcm, LcmCrdt, FwDataMiniLcmBridge, or FwLiteProjectSync: read the relevant section thoroughly, understand the sync flow end-to-end, run the full test suite with `dotnet test FwLiteOnly.slnf`, and test with real FwData projects
✅ Actions performed

Full review triggered.

@rmunn rmunn requested a review from myieye March 12, 2026 06:37
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
backend/FwLite/MiniLcm/MiniLcmApiExtensions.cs (1)

20-20: Sort AllMorphTypeData before snapshotting.

This new array currently inherits repository iteration order, so snapshot files can churn on order-only changes. The new WithoutStrictOrderingFor(x => x.AllMorphTypeData) in backend/FwLite/LcmCrdt.Tests/SnapshotAtCommitServiceTests.cs is already compensating for that. Sorting here by an immutable key such as MorphType keeps snapshots deterministic.

♻️ Proposed fix
-            await api.GetAllMorphTypeData().ToArrayAsync(),
+            (await api.GetAllMorphTypeData().ToArrayAsync())
+                .OrderBy(m => m.MorphType)
+                .ToArray(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/FwLite/MiniLcm/MiniLcmApiExtensions.cs` at line 20, The array
returned by awaiting api.GetAllMorphTypeData() is not ordered and causes
snapshot churn; modify the call in MiniLcmApiExtensions (the invocation of
GetAllMorphTypeData().ToArrayAsync()) to sort the sequence by the immutable key
MorphType before materializing (e.g., OrderBy(m => m.MorphType) or equivalent)
so AllMorphTypeData is deterministic when snapshotting.
backend/FwLite/MiniLcm/Models/MorphType.cs (1)

38-39: Potential sync churn between null and empty string.

The SimpleStringDiff.GetStringDiff() method (in MiniLcm/SyncHelpers/SimpleStringDiff.cs) uses direct equality comparison (before == after), which treats null and "" as distinct values. If FwData stores empty strings for Prefix/Postfix while CRDT stores null, each sync cycle may generate spurious patch operations.

Per the retrieved learnings, the two-pass sync pattern prevents data loss — any null in CRDT gets reconciled from FwData in pass 1 before pass 2 runs. However, you may want to consider normalizing empty strings to null (or vice versa) in GetStringDiff or during entity creation to avoid unnecessary operations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/FwLite/MiniLcm/Models/MorphType.cs` around lines 38 - 39, The
equality check in SimpleStringDiff.GetStringDiff treats null and "" as
different, causing churn with MorphType's LeadingToken/TrailingToken; update
GetStringDiff to normalize string values before comparison (e.g., map both null
and empty string to a canonical value) or add normalization when
creating/updating MorphType entities so LeadingToken and TrailingToken are
consistently stored (null or ""), ensuring comparisons like in GetStringDiff use
the normalized values to avoid spurious diffs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs`:
- Around line 352-365: Filter out tombstoned rows by only querying live rows
(where DeletedAt == null) in both GetAllMorphTypeData and GetMorphTypeData:
replace usages of repo.AllMorphTypeData with a filtered query (e.g.,
repo.AllMorphTypeData.Where(x => x.DeletedAt == null) or call a repository
helper that returns only live rows) so GetAllMorphTypeData yields only
non-deleted MorphTypeData and GetMorphTypeData uses the same filtered source
when calling SingleOrDefaultAsync; this ensures DeleteMorphTypeData tombstones
won’t be returned by these APIs.

---

Nitpick comments:
In `@backend/FwLite/MiniLcm/MiniLcmApiExtensions.cs`:
- Line 20: The array returned by awaiting api.GetAllMorphTypeData() is not
ordered and causes snapshot churn; modify the call in MiniLcmApiExtensions (the
invocation of GetAllMorphTypeData().ToArrayAsync()) to sort the sequence by the
immutable key MorphType before materializing (e.g., OrderBy(m => m.MorphType) or
equivalent) so AllMorphTypeData is deterministic when snapshotting.

In `@backend/FwLite/MiniLcm/Models/MorphType.cs`:
- Around line 38-39: The equality check in SimpleStringDiff.GetStringDiff treats
null and "" as different, causing churn with MorphType's
LeadingToken/TrailingToken; update GetStringDiff to normalize string values
before comparison (e.g., map both null and empty string to a canonical value) or
add normalization when creating/updating MorphType entities so LeadingToken and
TrailingToken are consistently stored (null or ""), ensuring comparisons like in
GetStringDiff use the normalized values to avoid spurious diffs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e566f485-c8f8-4d47-8bff-4847158d06e7

📥 Commits

Reviewing files that changed from the base of the PR and between 6a341ca and ad2a9fd.

📒 Files selected for processing (34)
  • backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
  • backend/FwLite/FwDataMiniLcmBridge/Api/UpdateProxy/UpdateMorphTypeDataProxy.cs
  • backend/FwLite/FwLiteProjectSync.Tests/Import/ResumableTests.cs
  • backend/FwLite/FwLiteProjectSync.Tests/ProjectSnapshotSerializationTests.cs
  • backend/FwLite/FwLiteProjectSync.Tests/Snapshots/sena-3_snapshot.2026-03-11.verified.txt
  • backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs
  • backend/FwLite/FwLiteProjectSync.Tests/sena-3-live.verified.sqlite
  • backend/FwLite/FwLiteProjectSync.Tests/sena-3-live_snapshot.verified.txt
  • backend/FwLite/FwLiteProjectSync/CrdtFwdataProjectSyncService.cs
  • backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs
  • backend/FwLite/FwLiteProjectSync/MiniLcmImport.cs
  • backend/FwLite/LcmCrdt.Tests/Changes/ChangeDeserializationRegressionData.latest.verified.txt
  • backend/FwLite/LcmCrdt.Tests/Changes/UseChangesTests.cs
  • backend/FwLite/LcmCrdt.Tests/Data/MigrationTests.cs
  • backend/FwLite/LcmCrdt.Tests/Data/MigrationTests_FromScriptedDb.v1.ProjectSnapshot.verified.txt
  • backend/FwLite/LcmCrdt.Tests/Data/MigrationTests_FromScriptedDb.v2.ProjectSnapshot.verified.txt
  • backend/FwLite/LcmCrdt.Tests/Data/SnapshotDeserializationRegressionData.latest.verified.txt
  • backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyChangeModels.verified.txt
  • backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyDbModel.verified.txt
  • backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyIObjectWithIdModels.verified.txt
  • backend/FwLite/LcmCrdt.Tests/SnapshotAtCommitServiceTests.cs
  • backend/FwLite/LcmCrdt/Changes/CreateMorphTypeDataChange.cs
  • backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs
  • backend/FwLite/LcmCrdt/Data/MiniLcmRepository.cs
  • backend/FwLite/LcmCrdt/LcmCrdtDbContext.cs
  • backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
  • backend/FwLite/LcmCrdt/Migrations/20260306045826_AddMorphTypeDataTable.Designer.cs
  • backend/FwLite/LcmCrdt/Migrations/20260306045826_AddMorphTypeDataTable.cs
  • backend/FwLite/LcmCrdt/Migrations/LcmCrdtDbContextModelSnapshot.cs
  • backend/FwLite/MiniLcm/MiniLcmApiExtensions.cs
  • backend/FwLite/MiniLcm/Models/IObjectWithId.cs
  • backend/FwLite/MiniLcm/Models/MorphType.cs
  • backend/FwLite/MiniLcm/ProjectSnapshot.cs
  • backend/FwLite/MiniLcm/SyncHelpers/MorphTypeDataSync.cs

Copy link
Collaborator

@myieye myieye left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just two small things I noticed before I do a deeper investigation.

Comment on lines +29 to +31
public MultiString? Name { get; set; }
public MultiString? Abbreviation { get; set; }
public RichMultiString? Description { get; set; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are all non-nullable in MorphTypeData. So, I think it makes sense for them to be non-nullable here too.
Then you can remove the occurences of ?? [] in NewEntity below.

Suggested change
public MultiString? Name { get; set; }
public MultiString? Abbreviation { get; set; }
public RichMultiString? Description { get; set; }
public MultiString Name { get; set; }
public MultiString Abbreviation { get; set; }
public RichMultiString Description { get; set; }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to make them nullable or else the parameterless JSON constructor was erroring: the compiler was saying "Hey, you have a constructor that doesn't set all the values". I've run into this behavior before and I don't remember how to tell C# what I'm doing here. The easiest way to get the compiler to shut up was to make these all nullable. Is there a better way? What did I miss/forget?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. I think you need to mark them as required? And I wonder if init would be better than set? But that's not so important. I'm not sure if we've been consistent at all.

Copy link
Contributor Author

@rmunn rmunn Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit f511c6c. (With followup commit fd91b96 because I'm still not 100% used to how required works and I forgot you also need to use the SetsRequiredProperties attribute to hint to the compiler that your main constructor fulfills the required contract).

rmunn added 7 commits March 13, 2026 11:19
Now we get to iterate over the actual enum values, rather than their int
equivalents which have to be converted. Which means we can also use the
enum names in test strings and so on. Handy.
Next commit will rename the MorphType enum to MorphTypeKind, and the
MorphTypeData class to plain MorphType. Also, the MorphType property of
the renamed class will become Kind. No changes will be made to the
property name in the Entry class, so its JSON will not need to be
adjusted; the type of the Entry.MorphType property will be renamed to
MorphTypeKind, but that name never shows up in JSON serialization so
that's not a problem.
Also rename MorphTypeData (now MorphType)'s property of type
MorphTypeKind. Formerly named MorphType, now named Kind.

Also rename AllMorphTypeData to MorphTypes, plural, to regain
consistency with rest of API.

This mostly completes the great renaming.
This may reduce the number of test failures we get, but not to zero
since we will likely have missed something in at least one file (and we
definitely haven't gotten every verified file yet, as the sena-3 Sqlite
file needs to run a DB migration and a sync before it will have its
correct contents, so there's going to be at least one test failure on
this commit).
Now that the great renaming is complete, the DB migration will have the
new table name and column names, so it's time to bring it back.
@github-actions github-actions bot added the 📦 Lexbox issues related to any server side code, fw-headless included label Mar 14, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 14, 2026

C# Unit Tests

165 tests  ±0   165 ✅ ±0   19s ⏱️ ±0s
 23 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit ae339ea. ± Comparison against base commit 469a825.

♻️ This comment has been updated with latest results.

rmunn added 3 commits March 14, 2026 13:49
Needs to match the C# name since we autogenerate the Typescript types
from the C# types.
Missed these the first time around
@rmunn
Copy link
Contributor Author

rmunn commented Mar 14, 2026

I suspect I need to change the JSON-encoded Sena-3 data in demo-entry-data.ts, but that's going to be a bit of a pain and I don't want to do it right now. I'll do that on Monday.

rmunn added 2 commits March 14, 2026 16:36
Will need to rename some new entries in the demo data from develop,
which are causing the tests to fail because they have the old names.
@rmunn
Copy link
Contributor Author

rmunn commented Mar 14, 2026

Turns out the test failures were because of a use of the MorphType enum in develop that wasn't in my branch, so I didn't see it while renaming everything.

@rmunn rmunn requested a review from myieye March 16, 2026 06:06
rmunn added 2 commits March 16, 2026 13:37
This allows us to not mark them nullable, even though the parameterless
constructor doesn't set them, pushing the burden of setting those
properties off onto the calling code that creates the object.
The CreateMorphTypeChange main constructor sets all required properties,
but C# needs a compiler hint to tell it so.
@rmunn
Copy link
Contributor Author

rmunn commented Mar 16, 2026

@myieye - Fixed the CreateMorphTypeChange type by using required so I no longer need to mark the MultiString properties as nullable. That's the semantics I wanted anyway, so thank you for reminding me of the details of how the required keyword works.

Did you hear from Jason about renaming LeadingToken and TrailingToken? I'm about to do that rename (once I verify that the tests have turned green again), then this should be ready for final approval. I tagged you for re-review already, but if you want to hold off until I push the LeadingToken and TrailingToken rename, that's fine.

P.S. I plan to do the rename of LeadingToken and TrailingToken in a single commit, which I will get running locally and then push. That way it will be easy to revert if we decide later that we don't want to match the FwData names. But it seems likely that we'll want to match FwData's names internally, so I'll go ahead with the rename before hearing what Jason thinks¸ as the work isn't likely to be wasted.

Matches the names from liblcm, which may help reduce confusion.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related 📦 Lexbox issues related to any server side code, fw-headless included

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support morph type in CRDTs

2 participants