Found while designing #722; recorded as unowned in that slice's ownership map and filed late — caught by the owner after #722 closed.
What
AuditWriter serialises details with new JsonSerializerOptions(JsonSerializerDefaults.Web) (AuditWriter.cs:17-18), which registers no JsonStringEnumConverter. A bare enum in a payload is therefore stored as its underlying integer.
Two call sites do exactly that:
src/Cluckwork.Application/Features/Catalog/CreateProduct/CreateProductHandler.cs:65 — details: new { product.Name, product.ProductType, EggGrade = grade.Name }, storing ProductType as a number
src/Cluckwork.Application/Features/Catalog/UpdateProduct/UpdateProductHandler.cs:78 — same shape, storing DefaultUnit as a number
Why it matters
A stored ordinal is only meaningful against the enum's member order at the time it was written. Reorder or insert a member and every historical row silently re-reads as a different value — with nothing failing, because the JSON is still valid and the number is still a number. That is a data-retention defect in the one table whose whole purpose is being trustworthy after the fact.
The repo already does the right thing elsewhere
Serialising the NAME is the dominant convention for this payload shape:
UpdateFarmSettingsHandler.cs:158,159,163,164 — .ToString() on four enums inside details:
UpdateEggUnitConversionHandler.cs:30 — same
#722 followed that convention for ListPriceBasis and pinned it with two mutation rows (dropping .ToString() reddens a named test on each call site, because GetString() throws on a JSON number).
Scope
Add .ToString() at the two call sites above and assert the name in each handler's audit test, with a mutation row per site proving the assertion reddens. Do not add a converter to AuditWriter's options — that would silently change the shape of every existing payload across ~25 call sites.
Note on existing rows
Rows already written carry ordinals and cannot be repaired from the data alone. This issue prevents the defect widening; a backfill would need the enum's member order at write time, which is not recorded.
Found while designing #722; recorded as unowned in that slice's ownership map and filed late — caught by the owner after #722 closed.
What
AuditWriterserialisesdetailswithnew JsonSerializerOptions(JsonSerializerDefaults.Web)(AuditWriter.cs:17-18), which registers noJsonStringEnumConverter. A bare enum in a payload is therefore stored as its underlying integer.Two call sites do exactly that:
src/Cluckwork.Application/Features/Catalog/CreateProduct/CreateProductHandler.cs:65—details: new { product.Name, product.ProductType, EggGrade = grade.Name }, storingProductTypeas a numbersrc/Cluckwork.Application/Features/Catalog/UpdateProduct/UpdateProductHandler.cs:78— same shape, storingDefaultUnitas a numberWhy it matters
A stored ordinal is only meaningful against the enum's member order at the time it was written. Reorder or insert a member and every historical row silently re-reads as a different value — with nothing failing, because the JSON is still valid and the number is still a number. That is a data-retention defect in the one table whose whole purpose is being trustworthy after the fact.
The repo already does the right thing elsewhere
Serialising the NAME is the dominant convention for this payload shape:
UpdateFarmSettingsHandler.cs:158,159,163,164—.ToString()on four enums insidedetails:UpdateEggUnitConversionHandler.cs:30— same#722 followed that convention for
ListPriceBasisand pinned it with two mutation rows (dropping.ToString()reddens a named test on each call site, becauseGetString()throws on a JSON number).Scope
Add
.ToString()at the two call sites above and assert the name in each handler's audit test, with a mutation row per site proving the assertion reddens. Do not add a converter toAuditWriter's options — that would silently change the shape of every existing payload across ~25 call sites.Note on existing rows
Rows already written carry ordinals and cannot be repaired from the data alone. This issue prevents the defect widening; a backfill would need the enum's member order at write time, which is not recorded.