SET ANSI_NULLS ON; GO SET QUOTED_IDENTIFIER ON; GO ;WITH LatestBookRun AS ( SELECT CAST(r.ProjectID AS int) AS ProjectID, CAST(r.BookID AS int) AS BookID, r.StoryIntelligenceRunID, r.Status AS RunStatus, r.CurrentStage, ROW_NUMBER() OVER (PARTITION BY r.BookID ORDER BY r.StoryIntelligenceRunID DESC) AS RowNumber, MAX(CASE WHEN r.Status IN (N'Pending', N'Running') THEN 1 ELSE 0 END) OVER (PARTITION BY r.BookID) AS HasActiveRun FROM dbo.StoryIntelligenceRuns r INNER JOIN dbo.Books b ON b.BookID = r.BookID INNER JOIN dbo.Projects p ON p.ProjectID = r.ProjectID WHERE r.ProjectID IS NOT NULL AND r.BookID IS NOT NULL AND b.IsArchived = 0 AND p.IsArchived = 0 ) MERGE dbo.StoryIntelligenceBookPipelines AS target USING ( SELECT ProjectID, BookID, StoryIntelligenceRunID, CASE WHEN HasActiveRun = 1 THEN N'InProgress' ELSE N'NeedsReview' END AS Status, CASE WHEN HasActiveRun = 1 THEN N'Chapters' ELSE N'CharacterReview' END AS CurrentStage, CASE WHEN HasActiveRun = 1 THEN NULL ELSE N'SceneImport' END AS LastCompletedStage, CASE WHEN HasActiveRun = 1 THEN NULL ELSE N'CharacterReview' END AS CurrentReviewStage FROM LatestBookRun WHERE RowNumber = 1 ) AS source ON target.BookID = source.BookID WHEN MATCHED THEN UPDATE SET ProjectID = source.ProjectID, LastRunID = source.StoryIntelligenceRunID, Status = CASE WHEN source.Status = N'InProgress' THEN source.Status ELSE target.Status END, CurrentStage = CASE WHEN source.Status = N'InProgress' THEN source.CurrentStage ELSE target.CurrentStage END, UpdatedUtc = SYSUTCDATETIME() WHEN NOT MATCHED THEN INSERT (ProjectID, BookID, CurrentStage, LastCompletedStage, CurrentReviewStage, Status, CompletedUtc, LastRunID) VALUES (source.ProjectID, source.BookID, source.CurrentStage, source.LastCompletedStage, source.CurrentReviewStage, source.Status, NULL, source.StoryIntelligenceRunID); GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceBookPipeline_GetByIDForUser @StoryIntelligenceBookPipelineID int, @UserID int AS BEGIN SET NOCOUNT ON; SELECT p.StoryIntelligenceBookPipelineID, p.ProjectID, project.ProjectName, p.BookID, book.BookTitle, book.Subtitle AS BookSubtitle, p.CurrentStage, p.LastCompletedStage, p.CurrentReviewStage, p.Status, p.CompletedUtc, p.LastRunID, p.CreatedUtc, p.UpdatedUtc FROM dbo.StoryIntelligenceBookPipelines p INNER JOIN dbo.Projects project ON project.ProjectID = p.ProjectID INNER JOIN dbo.Books book ON book.BookID = p.BookID INNER JOIN dbo.ProjectUserAccess access ON access.ProjectID = p.ProjectID AND access.UserID = @UserID AND access.IsActive = 1 WHERE p.StoryIntelligenceBookPipelineID = @StoryIntelligenceBookPipelineID AND book.IsArchived = 0 AND project.IsArchived = 0; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_ListByBookForUser @BookID int, @UserID int AS BEGIN SET NOCOUNT ON; SELECT r.StoryIntelligenceRunID, r.UserID, r.ProjectID, p.ProjectName AS ProjectTitle, r.BookID, b.BookTitle, r.ChapterID, r.ChapterNumber, r.Status, r.SourceType, r.SourceText, r.SourceFileName, r.SourceFileSizeBytes, r.SourceWordCount, r.SourceCharacterCount, r.SourceParagraphCount, r.SourceChapterCount, r.SourceDetectedImagesCount, r.SourceDetectedTablesCount, r.SourceDetectedFootnotesCount, r.SourceDetectedCommentsCount, r.CurrentStage, r.CurrentMessage, r.TotalDetectedScenes, r.CompletedScenes, r.FailedScenes, r.CancellationRequestedUtc, r.CancelledUtc, r.PromptVersion, r.PromptVersionsSummary, r.Model, r.StartedUtc, r.CompletedUtc, r.FailureStage, r.TotalInputTokens, r.TotalOutputTokens, r.TotalTokens, r.TotalDurationMs, r.EstimatedCostGBP, r.EstimatedCostUSD, r.ErrorMessage, r.ErrorDetail, r.CreatedUtc, r.UpdatedUtc FROM dbo.StoryIntelligenceRuns r INNER JOIN dbo.Projects p ON p.ProjectID = r.ProjectID INNER JOIN dbo.Books b ON b.BookID = r.BookID INNER JOIN dbo.ProjectUserAccess access ON access.ProjectID = r.ProjectID AND access.UserID = @UserID AND access.IsActive = 1 LEFT JOIN dbo.Chapters chapter ON chapter.ChapterID = r.ChapterID WHERE r.BookID = @BookID AND r.ProjectID IS NOT NULL AND b.IsArchived = 0 AND p.IsArchived = 0 ORDER BY COALESCE(chapter.SortOrder, 2147483647), COALESCE(r.ChapterNumber, chapter.ChapterNumber, 999999), r.StoryIntelligenceRunID; END; GO