refactor(db): migrate career profile aggregate

Transfer the canonical career profile, revision history, and six relational child collections to provider-aware migration ownership. Preserve legacy JSON, ordered child data, indexes, and cascade semantics.
This commit is contained in:
cesnimda
2026-08-30 21:54:54 +02:00
parent a906916acc
commit 90ca23de33
13 changed files with 404 additions and 355 deletions
+13 -13
View File
@@ -40,9 +40,7 @@ Neither a single reconciliation position nor one shared provider sequence works:
- **Pass 1 must come first.** A legacy database has hand-added columns and Identity tables that
predate the migrations; without repairing them (and stamping the legacy migration id into
`__EFMigrationsHistory`) `Migrate()` collides with them. `AddCareerProfileRelationalChildren` also
adds children that reference `CareerProfiles`, a **reconciler-owned** table — so it must exist
before migrations run.
`__EFMigrationsHistory`) `Migrate()` collides with them.
- **The final pass must come after.** On a brand-new database the migration-owned tables do not exist during
pass 1, so every reconciler table that references one (FK into `JobApplications`) is skipped, as
are the index and `AUTO_INCREMENT` repairs.
@@ -67,7 +65,9 @@ already-correct database. Two consequences worth knowing:
Created by EF migrations, never by the reconciler:
`AccountDeletionFiles`, `AccountDeletionRequests`, `AiInteractions`, `AiUsageRecords`, `AiWorkspaceNotes`,
`ApplicationChecklistItems`, `Attachments`, `Companies`, `CoverLetterVersions`,
`ApplicationChecklistItems`, `Attachments`, `CareerCertifications`, `CareerEducations`,
`CareerExperiences`, `CareerLanguages`, `CareerProfiles`, `CareerProfileVersions`, `CareerProjects`,
`CareerSkills`, `Companies`, `CoverLetterVersions`,
`Correspondences`, `CvExtractionRuns`, `CvUploadArtifacts`, `CvVariants`, `CvVariantVersions`,
`EmailDrafts`, `EmailSendAttempts`, `GmailConnections`,
`GmailReviewDecisions`, `ImapConnections`, `InterviewPrepItems`, `InterviewPrepNotes`, `JobApplications`, `JobEvents`, `Jobs`,
@@ -127,6 +127,11 @@ remains cascading.
AI-generated questions, answers, preparation state, sources, and ordering are retained; application
deletion remains cascading.
The Career Profile aggregate (`CareerProfiles`, `CareerProfileVersions`, and all six relational
child tables) moved in `20260830133000_AdoptCareerProfileSchema`. Canonical/long-tail JSON,
append-only history, stable child keys and ordering are retained; bounded MariaDB owner/item keys
keep all aggregate indexes provider-safe.
The reconciler may **repair** these (add a missing column, add an index, fix a non-`AUTO_INCREMENT`
primary key) and may seed the default `RuleSettings` row — but it must never `CREATE TABLE` them.
It used to create `RuleSettings`, which is precisely why a clean install failed with
@@ -134,12 +139,7 @@ It used to create `RuleSettings`, which is precisely why a clean install failed
### Reconciler-owned
Created by `StartupInitializationExtensions`, with a **no-op migration** holding the model snapshot:
`CareerProfiles`, `CareerProfileVersions`, the six CareerProfile children (`CareerExperiences`,
`CareerEducations`, `CareerSkills`, `CareerProjects`, `CareerCertifications`, `CareerLanguages`).
The seven ASP.NET Identity tables are also currently reconciler-owned, despite older wording that
The seven ASP.NET Identity tables are currently reconciler-owned, despite older wording that
called them migration-owned: `AspNetRoles`, `AspNetUsers`, `AspNetRoleClaims`, `AspNetUserClaims`,
`AspNetUserLogins`, `AspNetUserRoles`, and `AspNetUserTokens`. Guarded migration bootstraps for
`AspNetUsers` supports standalone traversal but does not yet transfer ownership. The historical
@@ -153,11 +153,11 @@ No-op migrations, each with a comment explaining why:
| Migration | Tables |
|---|---|
| `20260717222917_AddCareerProfileRelationalChildren` | the six CareerProfile children |
| `20260717222917_AddCareerProfileRelationalChildren` | historical no-op; ownership transferred by `20260830133000_AdoptCareerProfileSchema` |
| `20260718074509_AddCvVariants` | historical no-op; ownership transferred by `20260830128000_AdoptCvVariantSchema` |
| `20260718131138_AddAiInteractions` | historical no-op; ownership transferred by `20260830129000_AdoptAiInteractionSchema` |
| `20260719085904_AddApplicationChecklistItems` | historical no-op; ownership transferred by `20260830130000_AdoptApplicationChecklistSchema` |
| `20260719094728_SyncCareerChildKeyLengths` | snapshot sync only |
| `20260719094728_SyncCareerChildKeyLengths` | historical snapshot sync; ownership transferred by `20260830133000_AdoptCareerProfileSchema` |
| `20260719120954_AddCoverLetterVersions` | historical no-op; ownership transferred by `20260830131000_AdoptCoverLetterVersionSchema` |
| `20260719145044_AddInterviewPrepItems` | historical no-op; ownership transferred by `20260830132000_AdoptInterviewPrepItemSchema` |
@@ -170,7 +170,7 @@ skips it on a fresh database and pass 2 creates it:
|---|---|
| `TailoredCvDrafts`, `InterviewPrepNotes`, `AiWorkspaceNotes`, `CvVariants`, `AiInteractions`, `ApplicationChecklistItems`, `CoverLetterVersions`, `InterviewPrepItems` | `JobApplications` (migration-owned) |
| `CvVariantVersions` | `CvVariants` |
| `CareerProfileVersions`, the six CareerProfile children | `CareerProfiles` |
| `CareerProfileVersions`, the six CareerProfile children | `CareerProfiles` (migration-owned) |
| `CvExtractionRuns` | `CvUploadArtifacts` |
Index creation goes through one helper, `EnsureMySqlIndex`, which is guarded on **table** existence