From e9334096988cb5c00ab9d92a70fe39f93d00e82f Mon Sep 17 00:00:00 2001 From: cesnimda Date: Sun, 30 Aug 2026 18:52:08 +0200 Subject: [PATCH] refactor(db): migrate application checklist Move workflow checklist state into an additive provider-aware migration. Preserve system-key idempotency, manual progress, ordering, and application cascade semantics. --- JobTrackerApi.Tests/MigrationChainTests.cs | 63 ++++++++++++++ ...0130000_AdoptApplicationChecklistSchema.cs | 84 +++++++++++++++++++ .../StartupInitializationExtensions.cs | 55 ------------ .../Services/StartupSchemaOwnership.cs | 2 +- docs/audits/audit-remediation-backlog.md | 2 +- docs/audits/verification-log.md | 1 + docs/infrastructure/database-ownership.md | 10 ++- docs/verification/jt-019-schema-ownership.md | 8 +- docs/work-programmes/master-progress.md | 3 +- 9 files changed, 165 insertions(+), 63 deletions(-) create mode 100644 JobTrackerApi/Migrations/20260830130000_AdoptApplicationChecklistSchema.cs diff --git a/JobTrackerApi.Tests/MigrationChainTests.cs b/JobTrackerApi.Tests/MigrationChainTests.cs index 6542a81..6e7eedd 100644 --- a/JobTrackerApi.Tests/MigrationChainTests.cs +++ b/JobTrackerApi.Tests/MigrationChainTests.cs @@ -115,6 +115,7 @@ public sealed class MigrationChainTests Assert.Contains("CREATE TABLE IF NOT EXISTS `AiWorkspaceNotes`", script, StringComparison.Ordinal); Assert.Contains("CREATE TABLE IF NOT EXISTS `CvVariants`", script, StringComparison.Ordinal); Assert.Contains("CREATE TABLE IF NOT EXISTS `CvVariantVersions`", script, StringComparison.Ordinal); + Assert.Contains("CREATE TABLE IF NOT EXISTS `ApplicationChecklistItems`", script, StringComparison.Ordinal); Assert.Contains("`UiLanguage` varchar(16)", script, StringComparison.Ordinal); Assert.All( Regex.Matches(script, "CONSTRAINT `([^`]+)`").Select(match => match.Groups[1].Value), @@ -703,6 +704,68 @@ public sealed class MigrationChainTests Assert.Empty(await db.Database.GetPendingMigrationsAsync()); } + [Fact] + public async Task Checklist_adoption_preserves_system_and_manual_workflow_state() + { + await using var connection = new SqliteConnection("Data Source=:memory:"); + await connection.OpenAsync(); + await using var db = Context(connection); + var migrator = db.GetService(); + await migrator.MigrateAsync("20260830129000_AdoptAiInteractionSchema"); + await ExecuteAsync(connection, """ + INSERT INTO Companies (Name) VALUES ('Fixture company'); + INSERT INTO JobApplications + (CompanyId, JobTitle, DateApplied, SavedAt, Status, ResponseReceived, OwnerUserId, + HasResume, HasCoverLetter, HasPortfolio, HasOtherAttachment, IsDeleted) + VALUES + (1, 'Fixture role', '2026-08-01T09:00:00', '2026-07-20T09:00:00', + 'Applied', 0, 'owner-fixture', 0, 0, 0, 0, 0); + CREATE TABLE "ApplicationChecklistItems" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_ApplicationChecklistItems" PRIMARY KEY AUTOINCREMENT, + "OwnerUserId" TEXT NOT NULL, "JobApplicationId" INTEGER NOT NULL, + "SystemKey" TEXT NULL, "AutoSignal" TEXT NULL, "Title" TEXT NOT NULL, + "Description" TEXT NULL, "Category" TEXT NOT NULL, "Status" TEXT NOT NULL, + "Section" TEXT NULL, "SortOrder" INTEGER NOT NULL, + "IsSystemGenerated" INTEGER NOT NULL, "IsAutoCompleted" INTEGER NOT NULL, + "CompletedAt" TEXT NULL, "CreatedAtUtc" TEXT NOT NULL, "UpdatedAtUtc" TEXT NOT NULL, + CONSTRAINT "FK_ApplicationChecklistItems_JobApplications_JobApplicationId" + FOREIGN KEY ("JobApplicationId") REFERENCES "JobApplications" ("Id") ON DELETE CASCADE + ); + INSERT INTO "ApplicationChecklistItems" + ("OwnerUserId", "JobApplicationId", "SystemKey", "AutoSignal", "Title", + "Category", "Status", "Section", "SortOrder", "IsSystemGenerated", + "IsAutoCompleted", "CompletedAt", "CreatedAtUtc", "UpdatedAtUtc") + VALUES + ('owner-fixture', 1, 'prepare-cv', 'cv-ready', 'Prepare CV', 'preparation', 'done', + 'cv', 10, 1, 1, '2026-08-30T09:10:00+00:00', + '2026-08-30T09:00:00+00:00', '2026-08-30T09:10:00+00:00'), + ('owner-fixture', 1, NULL, NULL, 'Call recruiter', 'custom', 'pending', + NULL, 20, 0, 0, NULL, '2026-08-30T09:00:00+00:00', + '2026-08-30T09:00:00+00:00'); + """); + + await migrator.MigrateAsync(); + Assert.Equal("done", await ScalarAsync(connection, + "SELECT Status FROM ApplicationChecklistItems WHERE SystemKey = 'prepare-cv';")); + Assert.Equal("Call recruiter", await ScalarAsync(connection, + "SELECT Title FROM ApplicationChecklistItems WHERE SystemKey IS NULL;")); + + await migrator.MigrateAsync("20260830129000_AdoptAiInteractionSchema"); + Assert.Equal(2L, await ScalarAsync(connection, + "SELECT COUNT(*) FROM ApplicationChecklistItems WHERE JobApplicationId = 1;")); + await migrator.MigrateAsync(); + Assert.Equal(2L, await ScalarAsync(connection, """ + SELECT COUNT(*) FROM sqlite_master + WHERE type = 'index' AND name IN ( + 'IX_ApplicationChecklistItems_JobApplicationId_SystemKey', + 'IX_ApplicationChecklistItems_Owner_Job_Sort'); + """)); + + await ExecuteAsync(connection, "DELETE FROM JobApplications WHERE Id = 1;"); + Assert.Equal(0L, await ScalarAsync(connection, "SELECT COUNT(*) FROM ApplicationChecklistItems;")); + Assert.Empty(await db.Database.GetPendingMigrationsAsync()); + } + private static JobTrackerContext Context(SqliteConnection connection) { var currentUser = new Mock(); diff --git a/JobTrackerApi/Migrations/20260830130000_AdoptApplicationChecklistSchema.cs b/JobTrackerApi/Migrations/20260830130000_AdoptApplicationChecklistSchema.cs new file mode 100644 index 0000000..623fe2d --- /dev/null +++ b/JobTrackerApi/Migrations/20260830130000_AdoptApplicationChecklistSchema.cs @@ -0,0 +1,84 @@ +using System; +using JobTrackerApi.Data; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JobTrackerApi.Migrations; + +/// +/// Moves the per-application workflow checklist into migration ownership while preserving stable +/// system keys, manual items, ordering, and completion state. +/// +[DbContext(typeof(JobTrackerContext))] +[Migration("20260830130000_AdoptApplicationChecklistSchema")] +public sealed class AdoptApplicationChecklistSchema : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + if (ActiveProvider.Contains("MySql", StringComparison.OrdinalIgnoreCase)) + { + migrationBuilder.Sql(""" + CREATE TABLE IF NOT EXISTS `ApplicationChecklistItems` ( + `Id` int NOT NULL AUTO_INCREMENT, + `OwnerUserId` varchar(255) NOT NULL, + `JobApplicationId` int NOT NULL, + `SystemKey` varchar(64) NULL, + `AutoSignal` varchar(64) NULL, + `Title` varchar(255) NOT NULL, + `Description` longtext NULL, + `Category` varchar(32) NOT NULL, + `Status` varchar(32) NOT NULL, + `Section` varchar(64) NULL, + `SortOrder` int NOT NULL, + `IsSystemGenerated` tinyint(1) NOT NULL, + `IsAutoCompleted` tinyint(1) NOT NULL, + `CompletedAt` datetime(6) NULL, + `CreatedAtUtc` datetime(6) NOT NULL, + `UpdatedAtUtc` datetime(6) NOT NULL, + PRIMARY KEY (`Id`), + CONSTRAINT `FK_ApplicationChecklistItems_JobApplications_JobApplicationId` + FOREIGN KEY (`JobApplicationId`) REFERENCES `JobApplications` (`Id`) ON DELETE CASCADE + ) CHARACTER SET=utf8mb4; + CREATE UNIQUE INDEX IF NOT EXISTS `IX_ApplicationChecklistItems_JobApplicationId_SystemKey` + ON `ApplicationChecklistItems` (`JobApplicationId`, `SystemKey`); + CREATE INDEX IF NOT EXISTS `IX_ApplicationChecklistItems_Owner_Job_Sort` + ON `ApplicationChecklistItems` (`OwnerUserId`, `JobApplicationId`, `SortOrder`); + """); + return; + } + + migrationBuilder.Sql(""" + CREATE TABLE IF NOT EXISTS "ApplicationChecklistItems" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_ApplicationChecklistItems" PRIMARY KEY AUTOINCREMENT, + "OwnerUserId" TEXT NOT NULL, + "JobApplicationId" INTEGER NOT NULL, + "SystemKey" TEXT NULL, + "AutoSignal" TEXT NULL, + "Title" TEXT NOT NULL, + "Description" TEXT NULL, + "Category" TEXT NOT NULL, + "Status" TEXT NOT NULL, + "Section" TEXT NULL, + "SortOrder" INTEGER NOT NULL, + "IsSystemGenerated" INTEGER NOT NULL, + "IsAutoCompleted" INTEGER NOT NULL, + "CompletedAt" TEXT NULL, + "CreatedAtUtc" TEXT NOT NULL, + "UpdatedAtUtc" TEXT NOT NULL, + CONSTRAINT "FK_ApplicationChecklistItems_JobApplications_JobApplicationId" + FOREIGN KEY ("JobApplicationId") REFERENCES "JobApplications" ("Id") ON DELETE CASCADE + ); + CREATE UNIQUE INDEX IF NOT EXISTS "IX_ApplicationChecklistItems_JobApplicationId_SystemKey" + ON "ApplicationChecklistItems" ("JobApplicationId", "SystemKey"); + CREATE INDEX IF NOT EXISTS "IX_ApplicationChecklistItems_Owner_Job_Sort" + ON "ApplicationChecklistItems" ("OwnerUserId", "JobApplicationId", "SortOrder"); + """); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + // Preserve workflow decisions that may pre-date explicit migration ownership. + } +} diff --git a/JobTrackerApi/Services/StartupInitializationExtensions.cs b/JobTrackerApi/Services/StartupInitializationExtensions.cs index 7e33b66..df43f46 100644 --- a/JobTrackerApi/Services/StartupInitializationExtensions.cs +++ b/JobTrackerApi/Services/StartupInitializationExtensions.cs @@ -712,34 +712,6 @@ public static class StartupInitializationExtensions EnsureColumn(c, "AiInteractions", "EstimatedTokenCount", "ALTER TABLE AiInteractions ADD COLUMN EstimatedTokenCount INTEGER NOT NULL DEFAULT 0;"); } - // Phase 5 Milestone 2: the application checklist (workflow guidance over readiness signals). - static void EnsureApplicationChecklistTable(DbConnection c) - { - Exec(c, """ - CREATE TABLE IF NOT EXISTS "ApplicationChecklistItems" ( - "Id" INTEGER NOT NULL CONSTRAINT "PK_ApplicationChecklistItems" PRIMARY KEY AUTOINCREMENT, - "OwnerUserId" TEXT NOT NULL, - "JobApplicationId" INTEGER NOT NULL, - "SystemKey" TEXT NULL, - "AutoSignal" TEXT NULL, - "Title" TEXT NOT NULL, - "Description" TEXT NULL, - "Category" TEXT NOT NULL, - "Status" TEXT NOT NULL, - "Section" TEXT NULL, - "SortOrder" INTEGER NOT NULL, - "IsSystemGenerated" INTEGER NOT NULL, - "IsAutoCompleted" INTEGER NOT NULL, - "CompletedAt" TEXT NULL, - "CreatedAtUtc" TEXT NOT NULL, - "UpdatedAtUtc" TEXT NOT NULL, - CONSTRAINT "FK_ApplicationChecklistItems_JobApplications_JobApplicationId" FOREIGN KEY ("JobApplicationId") REFERENCES "JobApplications" ("Id") ON DELETE CASCADE - ); - """); - Exec(c, """CREATE UNIQUE INDEX IF NOT EXISTS "IX_ApplicationChecklistItems_JobApplicationId_SystemKey" ON "ApplicationChecklistItems" ("JobApplicationId", "SystemKey");"""); - Exec(c, """CREATE INDEX IF NOT EXISTS "IX_ApplicationChecklistItems_Owner_Job_Sort" ON "ApplicationChecklistItems" ("OwnerUserId", "JobApplicationId", "SortOrder");"""); - } - // Phase 5.4: append-only cover letter history. static void EnsureCoverLetterVersionsTable(DbConnection c) { @@ -784,7 +756,6 @@ public static class StartupInitializationExtensions ReconcileGmailConnectionColumns(conn); EnsureCareerProfileTables(conn); ReconcileAiInteractionUsageColumns(conn); - EnsureApplicationChecklistTable(conn); EnsureCoverLetterVersionsTable(conn); EnsureInterviewPrepItemsTable(conn); @@ -1180,32 +1151,6 @@ public static class StartupInitializationExtensions DropMalformedMySqlTable(conn, "CoverLetterVersions", "CreatedAtUtc", "datetime"); DropMalformedMySqlTable(conn, "InterviewPrepItems", "CreatedAtUtc", "datetime"); - if (!HasMySqlTable(conn, "ApplicationChecklistItems") && HasMySqlTable(conn, "JobApplications")) - { - using var cmd = conn.CreateCommand(); - cmd.CommandText = @"CREATE TABLE IF NOT EXISTS `ApplicationChecklistItems` ( - `Id` int NOT NULL AUTO_INCREMENT, - `OwnerUserId` varchar(255) NOT NULL, - `JobApplicationId` int NOT NULL, - `SystemKey` varchar(64) NULL, - `AutoSignal` varchar(64) NULL, - `Title` varchar(255) NOT NULL, - `Description` longtext NULL, - `Category` varchar(32) NOT NULL, - `Status` varchar(32) NOT NULL, - `Section` varchar(64) NULL, - `SortOrder` int NOT NULL, - `IsSystemGenerated` tinyint(1) NOT NULL, - `IsAutoCompleted` tinyint(1) NOT NULL, - `CompletedAt` datetime(6) NULL, - `CreatedAtUtc` datetime(6) NOT NULL, - `UpdatedAtUtc` datetime(6) NOT NULL, - PRIMARY KEY (`Id`), - CONSTRAINT `FK_ApplicationChecklistItems_JobApplications_JobApplicationId` FOREIGN KEY (`JobApplicationId`) REFERENCES `JobApplications` (`Id`) ON DELETE CASCADE - );"; - cmd.ExecuteNonQuery(); - } - if (!HasMySqlTable(conn, "CoverLetterVersions") && HasMySqlTable(conn, "JobApplications")) { using var cmd = conn.CreateCommand(); diff --git a/JobTrackerApi/Services/StartupSchemaOwnership.cs b/JobTrackerApi/Services/StartupSchemaOwnership.cs index 45e15b4..1aea193 100644 --- a/JobTrackerApi/Services/StartupSchemaOwnership.cs +++ b/JobTrackerApi/Services/StartupSchemaOwnership.cs @@ -14,6 +14,7 @@ internal static class StartupSchemaOwnership "AiUsageRecords", "AiWorkspaceNotes", "AiInteractions", + "ApplicationChecklistItems", "Attachments", "Companies", "Correspondences", @@ -44,7 +45,6 @@ internal static class StartupSchemaOwnership internal static readonly IReadOnlySet ReconcilerOwnedTables = new HashSet(StringComparer.Ordinal) { - "ApplicationChecklistItems", "AspNetRoleClaims", "AspNetRoles", "AspNetUserClaims", diff --git a/docs/audits/audit-remediation-backlog.md b/docs/audits/audit-remediation-backlog.md index a08151d..5e07980 100644 --- a/docs/audits/audit-remediation-backlog.md +++ b/docs/audits/audit-remediation-backlog.md @@ -558,7 +558,7 @@ SEC-008 implements the same durable state machine with `.uploading` and ` ### P3-1 — Reduce dual schema ownership incrementally -**Status (2026-08-30): in progress.** All 49 model tables now have an executable, disjoint creation-owner classification. Seventeen formerly reconciler-created tables—including authentication support, email-provider connections, CV extraction persistence, tailored CV drafts, job-workspace AI notes, CV variant history, and append-only AI interactions—have moved through additive provider-aware migrations with legacy-row preservation, downgrade/retry safety, startup-DDL removal, fresh SQLite runtime proof and generated MariaDB SQL. The Gmail decision migration also closes its missing MariaDB creation path. Eighteen startup-created tables remain; see `docs/verification/jt-019-schema-ownership.md` and V-194–V-203. +**Status (2026-08-30): in progress.** All 49 model tables now have an executable, disjoint creation-owner classification. Eighteen formerly reconciler-created tables—including authentication support, email-provider connections, CV persistence/history, job-workspace AI notes, append-only AI interactions, and application workflow checklists—have moved through additive provider-aware migrations with legacy-row preservation, downgrade/retry safety, startup-DDL removal, fresh SQLite runtime proof and generated MariaDB SQL. The Gmail decision migration also closes its missing MariaDB creation path. Seventeen startup-created tables remain; see `docs/verification/jt-019-schema-ownership.md` and V-194–V-204. - **Findings/scope:** JT-019; inventory reconciler operations and move stable schema ownership to EF migrations in small steps. - **Dependencies:** provider upgrade fixtures and P2-2 restore safety. diff --git a/docs/audits/verification-log.md b/docs/audits/verification-log.md index fa3308c..adc7169 100644 --- a/docs/audits/verification-log.md +++ b/docs/audits/verification-log.md @@ -235,3 +235,4 @@ Output was reduced to filenames and commit counts. The token artifact appears un | V-201 | Ownership/migration chain; populated adoption/downgrade/retry; FK/index assertions; MariaDB script; full backend | Repository root / disposable local SQLite | Move persisted interview-preparation and irregular AI workspace notes to migration ownership without losing reviewed outputs | PASS — focused ownership/migration 13/13; interview summary and candidate-fit JSON survive adoption, downgrade and re-upgrade; both uniqueness indexes exist; deleting the parent application cascades through both tables; generated MariaDB SQL is provider-safe; startup creates neither table; full backend 729/729 | MariaDB SQL generated only; no provider account or production migration. Guarded MariaDB index/auto-increment repairs remain temporarily. Twenty-one reconciler-owned tables remain | JT-019 job-workspace note transfer complete; CV variants and career-profile aggregates remain dependency-aware batches | | V-202 | Ownership/migration chain; populated adoption/downgrade/retry; relationship/index assertions; MariaDB script; full backend | Repository root / disposable local SQLite | Move saved CV variants and append-only revision history to migration ownership without losing user documents | PASS — focused ownership/migration 14/14; public slug, settings and two revisions survive adoption, downgrade and re-upgrade; four indexes exist; deleting the job nulls only the CV link and deleting the CV cascades through revisions; generated MariaDB SQL is provider-safe; startup creates neither table; full backend 730/730 | MariaDB SQL generated only; no provider account or production migration. Guarded malformed-empty/index/auto-increment repair remains temporarily. Nineteen reconciler-owned tables remain | JT-019 CV variant aggregate transfer complete; career-profile aggregate and remaining feature tables continue incrementally | | V-203 | Ownership/migration chain; populated adoption/downgrade/retry; usage/FK/index assertions; MariaDB script; full backend | Repository root / disposable local SQLite | Move append-only AI interaction history to explicit migration ownership without losing generated results or usage data | PASS — focused ownership/migration 15/15; result JSON, mode, provider and usage counters survive adoption, downgrade and re-upgrade; both indexes exist; deleting the parent application cascades through history; generated MariaDB SQL is provider-safe; startup no longer creates the table; full backend 731/731 | MariaDB SQL generated only; no provider account or production migration. Historical compatibility bootstrap and guarded counter/shape/index repairs remain. Eighteen reconciler-owned tables remain | JT-019 AI interaction transfer complete; checklist/document/preparation tables continue incrementally | +| V-204 | Ownership/migration chain; populated adoption/downgrade/retry; workflow/FK/index assertions; MariaDB script; full backend | Repository root / disposable local SQLite | Move application workflow checklist state to migration ownership without losing automatic or manual progress | PASS — focused ownership/migration 16/16; auto-completed system and pending manual items survive adoption, downgrade and re-upgrade; idempotency/order indexes exist; deleting the parent application cascades through checklist items; generated MariaDB SQL is provider-safe; startup no longer creates the table; full backend 732/732 | MariaDB SQL generated only; no provider account or production migration. Guarded malformed-empty/index/auto-increment repair remains temporarily. Seventeen reconciler-owned tables remain | JT-019 checklist transfer complete; cover-letter and interview-preparation tables continue incrementally | diff --git a/docs/infrastructure/database-ownership.md b/docs/infrastructure/database-ownership.md index 35c65d9..0efe037 100644 --- a/docs/infrastructure/database-ownership.md +++ b/docs/infrastructure/database-ownership.md @@ -67,7 +67,7 @@ already-correct database. Two consequences worth knowing: Created by EF migrations, never by the reconciler: `AccountDeletionFiles`, `AccountDeletionRequests`, `AiInteractions`, `AiUsageRecords`, `AiWorkspaceNotes`, -`Attachments`, `Companies`, +`ApplicationChecklistItems`, `Attachments`, `Companies`, `Correspondences`, `CvExtractionRuns`, `CvUploadArtifacts`, `CvVariants`, `CvVariantVersions`, `EmailDrafts`, `EmailSendAttempts`, `GmailConnections`, `GmailReviewDecisions`, `ImapConnections`, `InterviewPrepNotes`, `JobApplications`, `JobEvents`, `Jobs`, @@ -115,6 +115,10 @@ sets the CV link to null, and deleting a CV cascades through its append-only rev migration retains its compatibility bootstrap for historical chain traversal, but startup no longer creates the table; only additive counter and guarded MariaDB shape/index repairs remain. +`ApplicationChecklistItems` moved in `20260830130000_AdoptApplicationChecklistSchema`. Its stable +system-key uniqueness and owner/job/sort index preserve idempotent seeding alongside freely ordered +manual tasks; application deletion remains cascading. + 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 @@ -126,7 +130,7 @@ Created by `StartupInitializationExtensions`, with a **no-op migration** holding `CareerProfiles`, `CareerProfileVersions`, the six CareerProfile children (`CareerExperiences`, `CareerEducations`, `CareerSkills`, `CareerProjects`, `CareerCertifications`, `CareerLanguages`), -`ApplicationChecklistItems`, `CoverLetterVersions`, and `InterviewPrepItems`. +`CoverLetterVersions` and `InterviewPrepItems`. The seven ASP.NET Identity tables are also currently reconciler-owned, despite older wording that called them migration-owned: `AspNetRoles`, `AspNetUsers`, `AspNetRoleClaims`, `AspNetUserClaims`, @@ -145,7 +149,7 @@ No-op migrations, each with a comment explaining why: | `20260717222917_AddCareerProfileRelationalChildren` | the six CareerProfile children | | `20260718074509_AddCvVariants` | historical no-op; ownership transferred by `20260830128000_AdoptCvVariantSchema` | | `20260718131138_AddAiInteractions` | historical no-op; ownership transferred by `20260830129000_AdoptAiInteractionSchema` | -| `20260719085904_AddApplicationChecklistItems` | `ApplicationChecklistItems` | +| `20260719085904_AddApplicationChecklistItems` | historical no-op; ownership transferred by `20260830130000_AdoptApplicationChecklistSchema` | | `20260719094728_SyncCareerChildKeyLengths` | snapshot sync only | | `20260719120954_AddCoverLetterVersions` | `CoverLetterVersions` | | `20260719145044_AddInterviewPrepItems` | `InterviewPrepItems` | diff --git a/docs/verification/jt-019-schema-ownership.md b/docs/verification/jt-019-schema-ownership.md index 8678d29..1720940 100644 --- a/docs/verification/jt-019-schema-ownership.md +++ b/docs/verification/jt-019-schema-ownership.md @@ -35,6 +35,8 @@ cohesive table groups. It does not attempt to delete the legacy reconciler whole retaining public slugs, builder settings, and job-link/version retention semantics. - Added `20260830129000_AdoptAiInteractionSchema` to take ownership from both the startup reconciler and historical compatibility bootstrap while retaining append-only results and usage counters. +- Added `20260830130000_AdoptApplicationChecklistSchema` for application workflow state, retaining + stable system keys, manual steps, completion state, and user ordering. - Corrected the ownership runbook: new tables now default to migrations; the reconciler is legacy compatibility code to retire one dependency group at a time. @@ -68,14 +70,16 @@ migration. its job clears only the optional link, while deleting the CV cascades through its revisions. - A representative AI result, mode, provider, and usage counts survive adoption, downgrade, and re-upgrade; deleting the parent application still cascades through its interaction history. +- Representative system-generated and manual checklist steps survive adoption, downgrade, and + re-upgrade; deleting the parent application still cascades through its workflow items. - Generated MariaDB SQL contains the provider-correct `SystemEmailSettings` DDL. -- Full backend: 731/731 passed after the AI interaction history transfer. +- Full backend: 732/732 passed after the application checklist transfer. - Fresh application startup over a new disposable SQLite database applied `20260830120000_AdoptSystemEmailSettingsSchema` and reached the healthy listening state. ## Remaining JT-019 work -Eighteen model tables remain startup-created, including the Identity group and several tables +Seventeen model tables remain startup-created, including the Identity group and several tables with parent dependencies. Transfer them in small dependency-aware migrations with blank, populated, retry and MariaDB runtime proof. Column/index repairs must remain until historical upgrade fixtures prove each one redundant. diff --git a/docs/work-programmes/master-progress.md b/docs/work-programmes/master-progress.md index 1074bbb..54624d5 100644 --- a/docs/work-programmes/master-progress.md +++ b/docs/work-programmes/master-progress.md @@ -53,6 +53,7 @@ Updated: 2026-08-30 - Moved persisted interview-preparation and AI workspace notes into one provider-aware migration; reviewed outputs, uniqueness, and application cascades are preserved. - Moved saved CV variants and append-only revision history into one provider-aware migration; public slugs, settings, indexes, job-link nulling, and history cascades are preserved. - Moved append-only AI interaction history into a provider-aware migration; generated results, modes, providers, usage counters, indexes, and application cascades are preserved. +- Moved application checklist workflow state into a provider-aware migration; system-key idempotency, manual tasks, ordering, completion state, and application cascades are preserved. ### In progress @@ -83,7 +84,7 @@ Updated: 2026-08-30 - Focused frontend: 2 suites, 6 tests passed. - Full frontend: 64 suites, 272 tests passed. - Next production build and TypeScript: passed. -- Full backend: 731/731 tests passed after the AI interaction history JT-019 transfer. +- Full backend: 732/732 tests passed after the application checklist JT-019 transfer. - Portable Playwright launcher: resolved the user-local .NET 9 SDK; backend Release build passed with 0 warnings/errors. - Playwright: initial full run 9/10 exposed the intentional mobile Settings control change; updated focused rerun passed 1/1. A final complete browser rerun remains in the end-of-batch gate. - Focused backend match/intelligence verification: 34/34 passed.