diff --git a/PlotLine/App_Data/DataProtectionKeys/key-49c1e5c2-21a2-4a05-8080-08fa2b0dd614.xml b/PlotLine/App_Data/DataProtectionKeys/key-49c1e5c2-21a2-4a05-8080-08fa2b0dd614.xml new file mode 100644 index 0000000..8171144 --- /dev/null +++ b/PlotLine/App_Data/DataProtectionKeys/key-49c1e5c2-21a2-4a05-8080-08fa2b0dd614.xml @@ -0,0 +1,16 @@ + + + 2026-09-12T18:32:05.1596463Z + 2026-09-12T18:32:05.1596463Z + 2026-12-11T18:32:05.1596463Z + + + + + + + 7wzHKkUnWgH96NmDXrAbiYoKOD0O5wQ18zR38r7ouCpJrN2EP3p8cE5JPO7nqaScKovUoH/iRzophulwPFgm2A== + + + + \ No newline at end of file diff --git a/PlotLine/Sql/140_Phase21J3_DemandIllustrationStyleMarkers.sql b/PlotLine/Sql/140_Phase21J3_DemandIllustrationStyleMarkers.sql index 4631953..31e980c 100644 --- a/PlotLine/Sql/140_Phase21J3_DemandIllustrationStyleMarkers.sql +++ b/PlotLine/Sql/140_Phase21J3_DemandIllustrationStyleMarkers.sql @@ -6,6 +6,9 @@ demand-generated character illustrations produced before template 21J.3 so the admin can deliberately regenerate them with the corrected art direction. */ +SET QUOTED_IDENTIFIER ON; +GO + UPDATE dbo.IllustrationLibraryItems SET MetadataJson = JSON_MODIFY( JSON_MODIFY( diff --git a/PlotLine/Sql/141_Phase21K_IllustrationAssignmentAuditFields.sql b/PlotLine/Sql/141_Phase21K_IllustrationAssignmentAuditFields.sql index 45d1429..49440db 100644 --- a/PlotLine/Sql/141_Phase21K_IllustrationAssignmentAuditFields.sql +++ b/PlotLine/Sql/141_Phase21K_IllustrationAssignmentAuditFields.sql @@ -6,6 +6,9 @@ validate and safely invalidate illustration assignments. Existing rows remain active and are backfilled with conservative defaults. */ +SET QUOTED_IDENTIFIER ON; +GO + IF COL_LENGTH(N'dbo.StoryIntelligenceCharacterIllustrationAssignments', N'ImportSessionID') IS NULL ALTER TABLE dbo.StoryIntelligenceCharacterIllustrationAssignments ADD ImportSessionID int NULL; diff --git a/PlotLine/Sql/159_Phase25D_ManuscriptSourceHardDeleteCascades.sql b/PlotLine/Sql/159_Phase25D_ManuscriptSourceHardDeleteCascades.sql index ac3d387..7d94083 100644 --- a/PlotLine/Sql/159_Phase25D_ManuscriptSourceHardDeleteCascades.sql +++ b/PlotLine/Sql/159_Phase25D_ManuscriptSourceHardDeleteCascades.sql @@ -33,18 +33,6 @@ IF OBJECT_ID(N'dbo.SceneManuscriptSources', N'U') IS NOT NULL THROW 52010, 'Scene manuscript source hard-delete cascade was not applied.', 1; GO -IF OBJECT_ID(N'dbo.ManuscriptStructuralChapterSources', N'U') IS NOT NULL - AND NOT EXISTS - ( - SELECT 1 - FROM sys.foreign_keys - WHERE name = N'FK_ManuscriptStructuralChapterSources_Chapters' - AND parent_object_id = OBJECT_ID(N'dbo.ManuscriptStructuralChapterSources') - AND delete_referential_action_desc = N'CASCADE' - ) - THROW 52011, 'Structural chapter source hard-delete cascade was not applied.', 1; -GO - IF OBJECT_ID(N'dbo.ManuscriptStructuralChapterSources', N'U') IS NOT NULL AND EXISTS ( @@ -62,3 +50,15 @@ BEGIN FOREIGN KEY (ChapterID) REFERENCES dbo.Chapters(ChapterID) ON DELETE CASCADE; END; GO + +IF OBJECT_ID(N'dbo.ManuscriptStructuralChapterSources', N'U') IS NOT NULL + AND NOT EXISTS + ( + SELECT 1 + FROM sys.foreign_keys + WHERE name = N'FK_ManuscriptStructuralChapterSources_Chapters' + AND parent_object_id = OBJECT_ID(N'dbo.ManuscriptStructuralChapterSources') + AND delete_referential_action_desc = N'CASCADE' + ) + THROW 52011, 'Structural chapter source hard-delete cascade was not applied.', 1; +GO diff --git a/PlotLine/Sql/177_Phase27D_StoryIntelligencePersistedReviewCandidates.sql b/PlotLine/Sql/177_Phase27D_StoryIntelligencePersistedReviewCandidates.sql index 0f53805..9ff786c 100644 --- a/PlotLine/Sql/177_Phase27D_StoryIntelligencePersistedReviewCandidates.sql +++ b/PlotLine/Sql/177_Phase27D_StoryIntelligencePersistedReviewCandidates.sql @@ -1,3 +1,8 @@ +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO + IF OBJECT_ID(N'dbo.StoryIntelligenceReviewCandidateGenerations', N'U') IS NULL BEGIN CREATE TABLE dbo.StoryIntelligenceReviewCandidateGenerations diff --git a/PlotLine/Sql/197_SceneRelationshipDevelopments.sql b/PlotLine/Sql/197_SceneRelationshipDevelopments.sql index 3836fcd..618628e 100644 --- a/PlotLine/Sql/197_SceneRelationshipDevelopments.sql +++ b/PlotLine/Sql/197_SceneRelationshipDevelopments.sql @@ -1,4 +1,8 @@ -- Reuse canonical relationships and events. Existing author data is not rewritten. +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO SET XACT_ABORT ON; GO IF COL_LENGTH('dbo.RelationshipEvents','SourceReviewKey') IS NULL diff --git a/PlotLine/Sql/198_FixDevelopmentAccountResetObsoleteIllustrationAssignmentReference.sql b/PlotLine/Sql/198_FixDevelopmentAccountResetObsoleteIllustrationAssignmentReference.sql new file mode 100644 index 0000000..f8d8b06 --- /dev/null +++ b/PlotLine/Sql/198_FixDevelopmentAccountResetObsoleteIllustrationAssignmentReference.sql @@ -0,0 +1,131 @@ +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO + +/* +Remove obsolete dependency from the development reset procedure. +The old visualisation assignment table was intentionally dropped by script 150. +*/ + +CREATE OR ALTER PROCEDURE dbo.StoryIntelligence_DevelopmentAccountReset + @DevelopmentEmail nvarchar(256) +AS +BEGIN + SET NOCOUNT ON; + SET XACT_ABORT ON; + + DECLARE @UserID int; + SELECT @UserID = UserID + FROM dbo.AppUser + WHERE Email = @DevelopmentEmail; + + IF @UserID IS NULL + THROW 52101, 'Development account email was not found.', 1; + + DECLARE @ProjectIDs TABLE (ProjectID int NOT NULL PRIMARY KEY); + INSERT @ProjectIDs (ProjectID) + SELECT DISTINCT p.ProjectID + FROM dbo.Projects p + INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = p.ProjectID + WHERE pua.UserID = @UserID + AND pua.AccessRole = N'Owner' + AND pua.IsActive = 1; + + DECLARE @DeletedProjects table (ProjectID int NOT NULL PRIMARY KEY, ProjectName nvarchar(200) NULL); + INSERT @DeletedProjects(ProjectID, ProjectName) + SELECT p.ProjectID, p.ProjectName + FROM dbo.Projects p + INNER JOIN @ProjectIDs ids ON ids.ProjectID = p.ProjectID; + + BEGIN TRY + BEGIN TRANSACTION; + + UPDATE dbo.StoryIntelligenceRuns + SET Status = CASE WHEN Status IN (N'Pending', N'Running') THEN N'Cancelled' ELSE Status END, + CurrentStage = N'Cancelled by Phase 21U reset', + CurrentMessage = N'Development reset invalidated this run.', + CompletedUtc = COALESCE(CompletedUtc, SYSUTCDATETIME()), + UpdatedUtc = SYSUTCDATETIME() + WHERE ProjectID IN (SELECT ProjectID FROM @ProjectIDs) + AND Status IN (N'Pending', N'Running'); + + UPDATE dbo.IllustrationLibraryItems + SET Status = CASE WHEN Status IN (N'Queued', N'Generating') THEN N'Failed' ELSE Status END, + LastError = N'Invalidated by Phase 21U development reset', + UpdatedUtc = SYSUTCDATETIME() + WHERE Category IN (N'Character', N'Location', N'Asset') + AND Status IN (N'Queued', N'Generating'); + + DELETE FROM dbo.StoryMemoryIllustrationAssignments; + DELETE FROM dbo.StoryMemoryIllustrationDemands; + DELETE FROM dbo.StoryMemoryRelationships; + DELETE FROM dbo.StoryMemoryCharacterAttributes; + DELETE FROM dbo.StoryMemoryCharacterAliases; + DELETE FROM dbo.StoryMemoryAssets; + DELETE FROM dbo.StoryMemoryLocations; + DELETE FROM dbo.StoryMemoryCharacters; + DELETE FROM dbo.StoryMemoryProcessedSceneResults; + DELETE FROM dbo.StoryMemoryImportPreferences; + DELETE FROM dbo.StoryIntelligenceIllustrationDemands; + + IF OBJECT_ID(N'dbo.IllustrationGenerationAttempts', N'U') IS NOT NULL + DELETE FROM dbo.IllustrationGenerationAttempts; + + IF OBJECT_ID(N'dbo.IllustrationLibraryApprovalHistory', N'U') IS NOT NULL + DELETE FROM dbo.IllustrationLibraryApprovalHistory; + + IF OBJECT_ID(N'dbo.IllustrationLibraryLineage', N'U') IS NOT NULL + DELETE FROM dbo.IllustrationLibraryLineage; + + UPDATE dbo.IllustrationLibraryItems + SET ParentItemID = NULL + WHERE Category IN (N'Character', N'Location', N'Asset'); + + DELETE FROM dbo.IllustrationLibraryItems + WHERE Category IN (N'Character', N'Location', N'Asset'); + + COMMIT TRANSACTION; + END TRY + BEGIN CATCH + IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; + THROW; + END CATCH; + + DECLARE @ProjectID int; + DECLARE project_cursor CURSOR LOCAL FAST_FORWARD FOR + SELECT ProjectID FROM @ProjectIDs ORDER BY ProjectID; + + OPEN project_cursor; + FETCH NEXT FROM project_cursor INTO @ProjectID; + WHILE @@FETCH_STATUS = 0 + BEGIN + UPDATE dbo.Projects + SET IsArchived = 1, + ArchivedDate = COALESCE(ArchivedDate, SYSUTCDATETIME()), + ArchivedReason = N'Phase 21U development reset' + WHERE ProjectID = @ProjectID; + + EXEC dbo.ProjectHardDelete_ForOwner @ProjectID = @ProjectID, @UserID = @UserID; + FETCH NEXT FROM project_cursor INTO @ProjectID; + END + CLOSE project_cursor; + DEALLOCATE project_cursor; + + SELECT N'ProjectsDeleted' AS Metric, COUNT(*) AS Value FROM @DeletedProjects + UNION ALL SELECT N'RemainingOwnedProjects', COUNT(*) + FROM dbo.Projects p + INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = p.ProjectID + WHERE pua.UserID = @UserID AND pua.IsActive = 1 + UNION ALL SELECT N'RemainingStoryIntelligenceRuns', COUNT(*) + FROM dbo.StoryIntelligenceRuns + WHERE UserID = @UserID + UNION ALL SELECT N'RemainingIllustrationLibraryItems', COUNT(*) + FROM dbo.IllustrationLibraryItems + WHERE Category IN (N'Character', N'Location', N'Asset'); + + SELECT ProjectID, ProjectName + FROM @DeletedProjects + ORDER BY ProjectID; +END; +GO diff --git a/PlotLine/Sql/199_FixCharacterEnrichmentClaimNextQuotedIdentifier.sql b/PlotLine/Sql/199_FixCharacterEnrichmentClaimNextQuotedIdentifier.sql new file mode 100644 index 0000000..2bbb6d3 --- /dev/null +++ b/PlotLine/Sql/199_FixCharacterEnrichmentClaimNextQuotedIdentifier.sql @@ -0,0 +1,58 @@ +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO + +CREATE OR ALTER PROCEDURE dbo.CharacterEnrichmentRun_ClaimNext + @LeaseMinutes int = 90 +AS +BEGIN + SET NOCOUNT ON; + SET XACT_ABORT ON; + + DECLARE @RunID int; + + BEGIN TRANSACTION; + + SELECT TOP (1) @RunID = CharacterEnrichmentRunID + FROM dbo.CharacterEnrichmentRuns WITH (UPDLOCK, HOLDLOCK) + WHERE IsCurrent = 1 + AND ( + Status = N'Queued' + OR (Status = N'Running' AND StartedUtc < DATEADD(minute, -ABS(@LeaseMinutes), SYSUTCDATETIME())) + ) + ORDER BY QueuedUtc, CharacterEnrichmentRunID; + + IF @RunID IS NULL + BEGIN + COMMIT TRANSACTION; + SELECT TOP (0) * FROM dbo.CharacterEnrichmentRuns; + RETURN; + END; + + UPDATE dbo.CharacterEnrichmentRuns + SET Status = N'Running', + StartedUtc = SYSUTCDATETIME(), + AttemptCount = AttemptCount + 1, + CurrentStage = N'Preparing character analysis', + CurrentMessage = N'Preparing character analysis...', + UpdatedUtc = SYSUTCDATETIME() + WHERE CharacterEnrichmentRunID = @RunID + AND IsCurrent = 1 + AND ( + Status = N'Queued' + OR (Status = N'Running' AND StartedUtc < DATEADD(minute, -ABS(@LeaseMinutes), SYSUTCDATETIME())) + ); + + IF @@ROWCOUNT = 0 + BEGIN + COMMIT TRANSACTION; + SELECT TOP (0) * FROM dbo.CharacterEnrichmentRuns; + RETURN; + END; + + SELECT * FROM dbo.CharacterEnrichmentRuns WHERE CharacterEnrichmentRunID = @RunID; + + COMMIT TRANSACTION; +END; +GO