IF OBJECT_ID(N'dbo.StoryIntelligenceReviewCandidateGenerations', N'U') IS NULL BEGIN CREATE TABLE dbo.StoryIntelligenceReviewCandidateGenerations ( ReviewCandidateGenerationID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_StoryIntelligenceReviewCandidateGenerations PRIMARY KEY, ProjectID int NOT NULL, BookID int NOT NULL, Module nvarchar(40) NOT NULL, Status nvarchar(30) NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidateGenerations_Status DEFAULT N'Preparing', Source nvarchar(80) NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidateGenerations_Source DEFAULT N'SceneIntelligencePreparation', CandidateCount int NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidateGenerations_CandidateCount DEFAULT 0, PendingCount int NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidateGenerations_PendingCount DEFAULT 0, ErrorMessage nvarchar(1000) NULL, IsCurrent bit NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidateGenerations_IsCurrent DEFAULT 1, CreatedUtc datetime2 NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidateGenerations_CreatedUtc DEFAULT SYSUTCDATETIME(), PreparedUtc datetime2 NULL, FailedUtc datetime2 NULL, UpdatedUtc datetime2 NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidateGenerations_UpdatedUtc DEFAULT SYSUTCDATETIME(), CONSTRAINT FK_StoryIntelligenceReviewCandidateGenerations_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID), CONSTRAINT FK_StoryIntelligenceReviewCandidateGenerations_Books FOREIGN KEY (BookID) REFERENCES dbo.Books(BookID) ON DELETE CASCADE ); END; GO IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'dbo.StoryIntelligenceReviewCandidateGenerations') AND name = N'UX_StoryIntelligenceReviewCandidateGenerations_Current') CREATE UNIQUE INDEX UX_StoryIntelligenceReviewCandidateGenerations_Current ON dbo.StoryIntelligenceReviewCandidateGenerations(BookID, Module) WHERE IsCurrent = 1; GO IF OBJECT_ID(N'dbo.StoryIntelligenceReviewCandidates', N'U') IS NULL BEGIN CREATE TABLE dbo.StoryIntelligenceReviewCandidates ( ReviewCandidateID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_StoryIntelligenceReviewCandidates PRIMARY KEY, ReviewCandidateGenerationID int NOT NULL, ProjectID int NOT NULL, BookID int NOT NULL, Module nvarchar(40) NOT NULL, StableKey nvarchar(450) NOT NULL, CandidateType nvarchar(80) NULL, DisplayName nvarchar(450) NOT NULL, SortOrder int NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidates_SortOrder DEFAULT 0, Confidence decimal(9,4) NULL, ExistingCanonicalID int NULL, DecisionStatus nvarchar(30) NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidates_DecisionStatus DEFAULT N'Pending', DecisionAction nvarchar(40) NULL, DecisionTargetKey nvarchar(450) NULL, DecisionCanonicalID int NULL, CandidatePayloadJson nvarchar(max) NOT NULL, CreatedUtc datetime2 NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidates_CreatedUtc DEFAULT SYSUTCDATETIME(), ReviewedUtc datetime2 NULL, UpdatedUtc datetime2 NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidates_UpdatedUtc DEFAULT SYSUTCDATETIME(), CONSTRAINT FK_StoryIntelligenceReviewCandidates_Generation FOREIGN KEY (ReviewCandidateGenerationID) REFERENCES dbo.StoryIntelligenceReviewCandidateGenerations(ReviewCandidateGenerationID) ON DELETE CASCADE ); END; GO IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'dbo.StoryIntelligenceReviewCandidates') AND name = N'UX_StoryIntelligenceReviewCandidates_Generation_Key') CREATE UNIQUE INDEX UX_StoryIntelligenceReviewCandidates_Generation_Key ON dbo.StoryIntelligenceReviewCandidates(ReviewCandidateGenerationID, StableKey); GO IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'dbo.StoryIntelligenceReviewCandidates') AND name = N'IX_StoryIntelligenceReviewCandidates_Page') CREATE INDEX IX_StoryIntelligenceReviewCandidates_Page ON dbo.StoryIntelligenceReviewCandidates(BookID, Module, DecisionStatus, SortOrder, ReviewCandidateID) INCLUDE (ReviewCandidateGenerationID, DisplayName, ExistingCanonicalID); GO IF OBJECT_ID(N'dbo.StoryIntelligenceReviewCandidateEvidence', N'U') IS NULL BEGIN CREATE TABLE dbo.StoryIntelligenceReviewCandidateEvidence ( ReviewCandidateEvidenceID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_StoryIntelligenceReviewCandidateEvidence PRIMARY KEY, ReviewCandidateID int NOT NULL, SceneID int NULL, EvidenceText nvarchar(1000) NOT NULL, Confidence decimal(9,4) NULL, SortOrder int NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidateEvidence_SortOrder DEFAULT 0, CreatedUtc datetime2 NOT NULL CONSTRAINT DF_StoryIntelligenceReviewCandidateEvidence_CreatedUtc DEFAULT SYSUTCDATETIME(), CONSTRAINT FK_StoryIntelligenceReviewCandidateEvidence_Candidate FOREIGN KEY (ReviewCandidateID) REFERENCES dbo.StoryIntelligenceReviewCandidates(ReviewCandidateID) ON DELETE CASCADE, CONSTRAINT FK_StoryIntelligenceReviewCandidateEvidence_Scene FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID) ON DELETE SET NULL ); END; GO IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'dbo.StoryIntelligenceReviewCandidateEvidence') AND name = N'IX_StoryIntelligenceReviewCandidateEvidence_Candidate') CREATE INDEX IX_StoryIntelligenceReviewCandidateEvidence_Candidate ON dbo.StoryIntelligenceReviewCandidateEvidence(ReviewCandidateID, SortOrder); GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceReviewCandidateGeneration_Begin @ProjectID int, @BookID int, @Module nvarchar(40), @Source nvarchar(80) = N'SceneIntelligencePreparation' AS BEGIN SET NOCOUNT ON; UPDATE dbo.StoryIntelligenceReviewCandidateGenerations SET IsCurrent = 0, UpdatedUtc = SYSUTCDATETIME() WHERE BookID = @BookID AND Module = @Module AND IsCurrent = 1; INSERT dbo.StoryIntelligenceReviewCandidateGenerations (ProjectID, BookID, Module, Status, Source) VALUES (@ProjectID, @BookID, @Module, N'Preparing', @Source); SELECT * FROM dbo.StoryIntelligenceReviewCandidateGenerations WHERE ReviewCandidateGenerationID = CAST(SCOPE_IDENTITY() AS int); END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceReviewCandidate_Save @ReviewCandidateGenerationID int, @ProjectID int, @BookID int, @Module nvarchar(40), @StableKey nvarchar(450), @CandidateType nvarchar(80) = NULL, @DisplayName nvarchar(450), @SortOrder int, @Confidence decimal(9,4) = NULL, @ExistingCanonicalID int = NULL, @CandidatePayloadJson nvarchar(max) AS BEGIN SET NOCOUNT ON; MERGE dbo.StoryIntelligenceReviewCandidates AS target USING (SELECT @ReviewCandidateGenerationID AS ReviewCandidateGenerationID, @StableKey AS StableKey) AS source ON target.ReviewCandidateGenerationID = source.ReviewCandidateGenerationID AND target.StableKey = source.StableKey WHEN MATCHED THEN UPDATE SET CandidateType = @CandidateType, DisplayName = @DisplayName, SortOrder = @SortOrder, Confidence = @Confidence, ExistingCanonicalID = @ExistingCanonicalID, CandidatePayloadJson = @CandidatePayloadJson, UpdatedUtc = SYSUTCDATETIME() WHEN NOT MATCHED THEN INSERT (ReviewCandidateGenerationID, ProjectID, BookID, Module, StableKey, CandidateType, DisplayName, SortOrder, Confidence, ExistingCanonicalID, CandidatePayloadJson) VALUES (@ReviewCandidateGenerationID, @ProjectID, @BookID, @Module, @StableKey, @CandidateType, @DisplayName, @SortOrder, @Confidence, @ExistingCanonicalID, @CandidatePayloadJson); SELECT * FROM dbo.StoryIntelligenceReviewCandidates WHERE ReviewCandidateGenerationID = @ReviewCandidateGenerationID AND StableKey = @StableKey; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceReviewCandidateEvidence_Save @ReviewCandidateID int, @SceneID int = NULL, @EvidenceText nvarchar(1000), @Confidence decimal(9,4) = NULL, @SortOrder int AS BEGIN SET NOCOUNT ON; INSERT dbo.StoryIntelligenceReviewCandidateEvidence (ReviewCandidateID, SceneID, EvidenceText, Confidence, SortOrder) VALUES (@ReviewCandidateID, @SceneID, @EvidenceText, @Confidence, @SortOrder); END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceReviewCandidateGeneration_Complete @ReviewCandidateGenerationID int AS BEGIN SET NOCOUNT ON; UPDATE generation SET Status = N'Ready', CandidateCount = counts.CandidateCount, PendingCount = counts.PendingCount, PreparedUtc = SYSUTCDATETIME(), FailedUtc = NULL, ErrorMessage = NULL, UpdatedUtc = SYSUTCDATETIME() FROM dbo.StoryIntelligenceReviewCandidateGenerations generation CROSS APPLY ( SELECT COUNT(*) AS CandidateCount, ISNULL(SUM(CASE WHEN candidate.DecisionStatus = N'Pending' THEN 1 ELSE 0 END), 0) AS PendingCount FROM dbo.StoryIntelligenceReviewCandidates candidate WHERE candidate.ReviewCandidateGenerationID = generation.ReviewCandidateGenerationID ) counts WHERE generation.ReviewCandidateGenerationID = @ReviewCandidateGenerationID; SELECT * FROM dbo.StoryIntelligenceReviewCandidateGenerations WHERE ReviewCandidateGenerationID = @ReviewCandidateGenerationID; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceReviewCandidateGeneration_Fail @ReviewCandidateGenerationID int, @ErrorMessage nvarchar(1000) AS BEGIN SET NOCOUNT ON; UPDATE dbo.StoryIntelligenceReviewCandidateGenerations SET Status = N'Failed', ErrorMessage = @ErrorMessage, FailedUtc = SYSUTCDATETIME(), UpdatedUtc = SYSUTCDATETIME() WHERE ReviewCandidateGenerationID = @ReviewCandidateGenerationID; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceReviewCandidateGeneration_GetCurrent @BookID int, @Module nvarchar(40) AS BEGIN SET NOCOUNT ON; SELECT TOP (1) * FROM dbo.StoryIntelligenceReviewCandidateGenerations WHERE BookID = @BookID AND Module = @Module AND IsCurrent = 1 ORDER BY ReviewCandidateGenerationID DESC; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceReviewCandidate_ListPage @BookID int, @Module nvarchar(40), @PageNumber int = 1, @PageSize int = 25 AS BEGIN SET NOCOUNT ON; IF @PageNumber < 1 SET @PageNumber = 1; IF @PageSize < 1 SET @PageSize = 25; IF @PageSize > 100 SET @PageSize = 100; DECLARE @GenerationID int; SELECT TOP (1) @GenerationID = ReviewCandidateGenerationID FROM dbo.StoryIntelligenceReviewCandidateGenerations WHERE BookID = @BookID AND Module = @Module AND IsCurrent = 1 AND Status = N'Ready' ORDER BY ReviewCandidateGenerationID DESC; ;WITH CurrentCandidates AS ( SELECT candidate.*, COUNT(*) OVER () AS TotalCount, SUM(CASE WHEN candidate.DecisionStatus = N'Pending' THEN 1 ELSE 0 END) OVER () AS PendingCount, ROW_NUMBER() OVER (ORDER BY candidate.SortOrder, candidate.ReviewCandidateID) AS RowNumber FROM dbo.StoryIntelligenceReviewCandidates candidate WHERE candidate.ReviewCandidateGenerationID = @GenerationID AND candidate.DecisionStatus = N'Pending' ) SELECT * FROM CurrentCandidates WHERE RowNumber BETWEEN ((@PageNumber - 1) * @PageSize) + 1 AND @PageNumber * @PageSize ORDER BY RowNumber; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceReviewCandidate_MarkReviewed @BookID int, @Module nvarchar(40), @StableKey nvarchar(450), @DecisionAction nvarchar(40), @DecisionTargetKey nvarchar(450) = NULL, @DecisionCanonicalID int = NULL AS BEGIN SET NOCOUNT ON; DECLARE @GenerationID int; SELECT TOP (1) @GenerationID = ReviewCandidateGenerationID FROM dbo.StoryIntelligenceReviewCandidateGenerations WHERE BookID = @BookID AND Module = @Module AND IsCurrent = 1 ORDER BY ReviewCandidateGenerationID DESC; UPDATE dbo.StoryIntelligenceReviewCandidates SET DecisionStatus = N'Reviewed', DecisionAction = @DecisionAction, DecisionTargetKey = @DecisionTargetKey, DecisionCanonicalID = @DecisionCanonicalID, ReviewedUtc = SYSUTCDATETIME(), UpdatedUtc = SYSUTCDATETIME() WHERE ReviewCandidateGenerationID = @GenerationID AND StableKey = @StableKey; UPDATE generation SET PendingCount = counts.PendingCount, UpdatedUtc = SYSUTCDATETIME() FROM dbo.StoryIntelligenceReviewCandidateGenerations generation CROSS APPLY ( SELECT ISNULL(SUM(CASE WHEN candidate.DecisionStatus = N'Pending' THEN 1 ELSE 0 END), 0) AS PendingCount FROM dbo.StoryIntelligenceReviewCandidates candidate WHERE candidate.ReviewCandidateGenerationID = generation.ReviewCandidateGenerationID ) counts WHERE generation.ReviewCandidateGenerationID = @GenerationID; SELECT * FROM dbo.StoryIntelligenceReviewCandidateGenerations WHERE ReviewCandidateGenerationID = @GenerationID; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceReviewCandidate_ResetBook @BookID int AS BEGIN SET NOCOUNT ON; DELETE dbo.StoryIntelligenceReviewCandidateGenerations WHERE BookID = @BookID; END; GO