Skip to content

refactor(infra): split SimulationDataSeeder into per-phase partials - #1266

Merged
mforce merged 2 commits into
mainfrom
refactor/1215-simulation-seeder
Oct 12, 2026
Merged

mforce merged 2 commits into
mainfrom
refactor/1215-simulation-seeder

Conversation

@mforce

@mforce mforce commented Oct 12, 2026

Copy link
Copy Markdown
Owner

Part of #1215

Splits src/Cluckwork.Infrastructure/Persistence/SimulationDataSeeder.cs (2,532 lines) into ten partial files of the same class, one per seeding phase. The type name, namespace, member names and signatures do not change. No member moved to another type, so every ledger row keyed by SimulationDataSeeder.<Member> still matches. No ledger, adapter or coupling-matrix file changed.

File Lines Contents
SimulationDataSeeder.cs 424 Class header comment, constructor, shared constants, SeedAsync, Require helpers
SimulationDataSeeder.Preflight.cs 78 MissingBaseDataAsync, FindOwnerAsync
SimulationDataSeeder.Cast.cs 231 SimActor, SimCast, Pick, ActAs, SeedCastAsync, WorkerFor, EnsureUserAsync
SimulationDataSeeder.Flocks.cs 272 Flock topology, picker catalog, lifecycle transitions, explicit bird movements, RestrictOneWorkerAsync
SimulationDataSeeder.Accounts.cs 75 SeedPrimaryTimeZoneAsync, SeedSecondAccountAsync
SimulationDataSeeder.ProductionHistory.cs 138 Daily-entry history, LiveBirdsOnDay, EggsOnDay, LoadSaleableGradesAsync
SimulationDataSeeder.Sales.cs 345 Catalog, customers, orders, payments
SimulationDataSeeder.Inventory.cs 378 Inventory and expenses. They share one section header in the original, so they stay together.
SimulationDataSeeder.ManifestExpectations.cs 205 Manifest section header, ManifestSchemaVersion, the expected-count constants, DrainLockSweepAsync, ExpectedLockedEntryCount
SimulationDataSeeder.Manifest.cs 451 EmitManifestAsync, ComputeCountsAsync, ComputeExpectedCounts, ValidateCounts, fingerprint, manifest file, and the manifest record types

Members stay in their original order inside each file. The FileSizeLimitTests legacy entry for the old file is removed. One Playwright comment in tools/simulation/ui/specs/named-entity-picker.spec.ts cited SimulationDataSeeder.cs ~L824. It now cites SimulationDataSeeder.Flocks.cs ~L97.

Pure-move proof

A script cut the partials from origin/main's file by line range. A second script put them back together in the original order and diffed the result against origin/main, ignoring blank lines. Only these lines differ:

  • public sealed class SimulationDataSeeder( became public sealed partial class SimulationDataSeeder(.
  • 15 comment lines said "above" or "below" about code that now lives in another partial. Only that location phrase changed, to name the partial, for example "as sales above" became "as sales in the Sales partial".

Blank-line differences occur only at the seams between files. A comment sweep finds 986 of the original's 1,001 comment lines unchanged in the new files. The 15 that are missing are the 15 edited lines above. git diff --color-moved=plain -w origin/main...HEAD shows the same set: moved blocks, the partial wrappers and usings, the keyword, and those 15 comments. Each partial's usings were pruned with dotnet format style --diagnostics IDE0005, which removed only using lines.

Seed-dump comparison

This is supporting evidence. The source comparison above is the primary proof.

I seeded four fresh databases in one isolated Postgres container, two from origin/main (base 1, base 2) and two from this branch (head 1, head 2). Each ran migrate, bootstrap-admin, then seed --profile simulation with default options on the same UTC day. I dumped every table (43 tables, 3,889 rows each).

Comparing base 1 with base 2 found the columns that change between two seeds of the same code. I normalized exactly those:

  • Clock stamps: every CreatedAtUtc and UpdatedAtUtc, plus AuditEvents.OccurredAtUtc, DailyEntries.LockedAtUtc and simulation_seed_state.CompletedAtUtc. Only today's timestamps are masked. The default account's fixed 1970 value is compared as is.
  • Random UUID keys, primary and foreign, in 24 tables. Each UUID is replaced by a hash of its own row's normalized content and its outgoing references, so a foreign key still has to point at an equal row.
  • UUIDs inside AuditEvents.DetailsJson, labeled the same way. The rest of the JSON is compared as is.
  • SalesOrders.ReferenceNumber. It is SO- plus the first 8 hex digits of the random order id. The script asserts that before it masks the value.
  • AspNetUsers.PasswordHash, SecurityStamp and ConcurrencyStamp, which are salted or random.
  • EggLots.Version, plus Sequence on EggLots and EggInventoryMovements. A per-column check misses the Sequence change, because the same values land on different rows. See the note below.

Sequence on every other table matched without normalization.

Results:

  • All four runs printed the same fingerprint (8987971d…), and the four completion manifests are byte-identical.
  • Head 2 is identical to base 1 after normalization, and head 1 is identical to base 2.
  • Base 1 and base 2 still differ from each other in 2 egg lots, 16 allocations and 18 egg inventory movements. Every one of those rows references one of the two lots in the FIFO tie described below.

So the four runs split into the two outcomes of that tie, and each outcome includes a base run and a head run.

Guard mutations

Each guard went red on its mutation and green again after the file was restored.

  • Adding an ICommerceModule parameter to SeedSecondAccountAsync in SimulationDataSeeder.Accounts.cs made AdapterReachRealTreeTests report an undeclared Commerce reach for that member. The type+member key still finds code in a partial.
  • Padding SimulationDataSeeder.Manifest.cs to 511 lines made FileSizeLimitTests fail with "over the 500-line limit".

FileSizeLimitTests reads only git-tracked files, so a new file is invisible to it until it is staged.

Checks run locally

  • dotnet build Cluckwork.slnx: 0 warnings, 0 errors.
  • dotnet test tests/Cluckwork.Application.Tests --filter "FullyQualifiedName~FileSizeLimitTests|FullyQualifiedName~ModuleLedger|FullyQualifiedName~AdapterReach|FullyQualifiedName~TenantBypass|FullyQualifiedName~AuditVocabulary|FullyQualifiedName~CouplingMatrix|FullyQualifiedName~SeamSurface": 265 passed.
  • dotnet test tests/Cluckwork.Api.IntegrationTests --filter "FullyQualifiedName~Simulation|FullyQualifiedName~AccessSeed|FullyQualifiedName~AccountSlugMigration": 79 passed.

Pre-existing, not fixed here

  • FIFO tie. Two Large lots dated 2026-07-14, one from each operational flock, tie in EggLotRepository's ORDER BY "ProductionDate", "Id" (EggLotRepository.cs:57). The ids are random v4 Guids, so which lot an order drains first varies between seeds. In one run the 185-egg lot ends empty and the 161-egg lot keeps 94; in the other run it is the reverse. Counts, totals and the fingerprint do not change.
  • Unstable Sequence order. Within one SaveChanges, the egg lots and egg inventory movements of a daily entry are inserted in a different order on each run, so their Sequence values attach to different rows. Each row still gets a unique Sequence in a given database.

No UI change, so no screenshots.

Moves the 2,532-line seeder into ten partial files of the same class,
one per seeding phase: the SeedAsync orchestrator and Require helpers,
preflight, cast, flocks, accounts, production history, sales,
inventory and expenses, manifest expectations, and the manifest with
its records. Members move unchanged and in their original order.

Fifteen comments that located code in another phase with "above" or
"below" now name the partial it lives in. The Playwright spec comment
that cited SimulationDataSeeder.cs ~L824 now cites
SimulationDataSeeder.Flocks.cs ~L97. The file's FileSizeLimitTests
legacy entry is removed.

Part of #1215
@mforce mforce added this to the Platform hardening milestone Oct 12, 2026
…ion-seeder

# Conflicts:
#	tests/Cluckwork.Application.Tests/Architecture/FileSizeLimitTests.cs
@mforce
mforce merged commit 87a3f4c into main Oct 12, 2026
21 checks passed
@mforce
mforce deleted the refactor/1215-simulation-seeder branch October 12, 2026 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants