IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'ChapterID') IS NULL BEGIN ALTER TABLE dbo.StoryIntelligenceRuns ADD ChapterID int NULL; END; GO IF COL_LENGTH(N'dbo.StoryIntelligenceRuns', N'ChapterNumber') IS NULL BEGIN ALTER TABLE dbo.StoryIntelligenceRuns ADD ChapterNumber decimal(9, 2) NULL; END; GO IF NOT EXISTS ( SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_StoryIntelligenceRuns_Chapters' AND parent_object_id = OBJECT_ID(N'dbo.StoryIntelligenceRuns') ) BEGIN ALTER TABLE dbo.StoryIntelligenceRuns ADD CONSTRAINT FK_StoryIntelligenceRuns_Chapters FOREIGN KEY (ChapterID) REFERENCES dbo.Chapters(ChapterID); END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceSource_ProjectListAdmin AS BEGIN SET NOCOUNT ON; SELECT ProjectID AS Value, ProjectName AS Text FROM dbo.Projects WHERE IsArchived = 0 ORDER BY ProjectName; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceSource_BookListAdmin @ProjectID int AS BEGIN SET NOCOUNT ON; SELECT BookID AS Value, CASE WHEN NULLIF(LTRIM(RTRIM(Subtitle)), N'') IS NULL THEN BookTitle ELSE CONCAT(BookTitle, N': ', Subtitle) END AS Text FROM dbo.Books WHERE ProjectID = @ProjectID AND IsArchived = 0 ORDER BY SortOrder, BookNumber, BookTitle; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceSource_ChapterListAdmin @BookID int AS BEGIN SET NOCOUNT ON; SELECT ChapterID AS Value, CONCAT(N'Chapter ', CONVERT(nvarchar(20), ChapterNumber), N': ', ChapterTitle) AS Text FROM dbo.Chapters WHERE BookID = @BookID AND IsArchived = 0 ORDER BY SortOrder, ChapterNumber, ChapterTitle; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceSource_ChapterText_GetAdmin @ChapterID int AS BEGIN SET NOCOUNT ON; SELECT p.ProjectID, p.ProjectName, b.BookID, b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle, CAST(NULL AS nvarchar(max)) AS SourceText, CAST(NULL AS int) AS SourceWordCount, CAST(NULL AS int) AS SourceCharacterCount FROM dbo.Chapters c INNER JOIN dbo.Books b ON b.BookID = c.BookID INNER JOIN dbo.Projects p ON p.ProjectID = b.ProjectID WHERE c.ChapterID = @ChapterID AND c.IsArchived = 0 AND b.IsArchived = 0 AND p.IsArchived = 0; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_QueueAdminText @UserID int, @ProjectID int = NULL, @BookID int = NULL, @ChapterID int = NULL, @ChapterNumber decimal(9, 2) = NULL, @SourceType nvarchar(50), @SourceLabel nvarchar(300), @SourceText nvarchar(max), @SourceWordCount int = NULL, @SourceCharacterCount int = NULL, @SourceChapterCount int = NULL, @Model nvarchar(100), @PromptVersionsSummary nvarchar(500) AS BEGIN SET NOCOUNT ON; INSERT dbo.StoryIntelligenceRuns ( UserID, ProjectID, BookID, ChapterID, ChapterNumber, Status, SourceType, SourceLabel, SourceText, SourceWordCount, SourceCharacterCount, SourceChapterCount, Model, PromptVersion, PromptVersionsSummary, StartedUtc, CurrentStage, CurrentMessage, CompletedScenes, FailedScenes ) VALUES ( @UserID, @ProjectID, @BookID, @ChapterID, @ChapterNumber, N'Pending', @SourceType, @SourceLabel, @SourceText, @SourceWordCount, @SourceCharacterCount, @SourceChapterCount, @Model, @PromptVersionsSummary, @PromptVersionsSummary, SYSUTCDATETIME(), N'Pending', N'Queued for Story Intelligence processing.', 0, 0 ); SELECT CAST(SCOPE_IDENTITY() AS int) AS StoryIntelligenceRunID; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_ClaimNextPending AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; DECLARE @StoryIntelligenceRunID int; SELECT TOP (1) @StoryIntelligenceRunID = StoryIntelligenceRunID FROM dbo.StoryIntelligenceRuns WITH (UPDLOCK, READPAST) WHERE Status = N'Pending' ORDER BY CreatedUtc; IF @StoryIntelligenceRunID IS NULL BEGIN SELECT TOP (0) StoryIntelligenceRunID, UserID, ProjectID, BookID, ChapterID, ChapterNumber, Status, SourceType, SourceLabel, SourceText, SourceWordCount, SourceCharacterCount, SourceChapterCount, PromptVersion, PromptVersionsSummary, Model, StartedUtc, CompletedUtc, FailureStage, TotalInputTokens, TotalOutputTokens, TotalTokens, TotalDurationMs, EstimatedCostGBP, EstimatedCostUSD, ErrorMessage, ErrorDetail, CurrentStage, CurrentMessage, TotalDetectedScenes, CompletedScenes, FailedScenes, CancellationRequestedUtc, CancelledUtc, CreatedUtc, UpdatedUtc FROM dbo.StoryIntelligenceRuns; RETURN; END; UPDATE dbo.StoryIntelligenceRuns SET Status = N'Running', StartedUtc = SYSUTCDATETIME(), CurrentStage = N'ChapterStructure', CurrentMessage = N'Running Chapter Structure analysis.', UpdatedUtc = SYSUTCDATETIME() WHERE StoryIntelligenceRunID = @StoryIntelligenceRunID AND Status = N'Pending'; SELECT StoryIntelligenceRunID, UserID, ProjectID, BookID, ChapterID, ChapterNumber, Status, SourceType, SourceLabel, SourceText, SourceWordCount, SourceCharacterCount, SourceChapterCount, PromptVersion, PromptVersionsSummary, Model, StartedUtc, CompletedUtc, FailureStage, TotalInputTokens, TotalOutputTokens, TotalTokens, TotalDurationMs, EstimatedCostGBP, EstimatedCostUSD, ErrorMessage, ErrorDetail, CurrentStage, CurrentMessage, TotalDetectedScenes, CompletedScenes, FailedScenes, CancellationRequestedUtc, CancelledUtc, CreatedUtc, UpdatedUtc FROM dbo.StoryIntelligenceRuns WHERE StoryIntelligenceRunID = @StoryIntelligenceRunID; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_ListAdmin AS BEGIN SET NOCOUNT ON; SELECT r.StoryIntelligenceRunID, r.CreatedUtc, r.UserID, r.ProjectID, p.ProjectName AS ProjectTitle, r.BookID, b.BookTitle, r.ChapterID, r.ChapterNumber, r.Status, r.SourceType, r.FailureStage, COUNT(sr.SceneResultID) AS SceneCount, r.TotalTokens, r.TotalDurationMs, r.EstimatedCostGBP, r.EstimatedCostUSD, COALESCE(cr.ValidationErrorsCount, 0) + COALESCE(SUM(sr.ValidationErrorsCount), 0) AS ValidationErrorsCount, COALESCE(cr.ValidationWarningsCount, 0) + COALESCE(SUM(sr.ValidationWarningsCount), 0) AS ValidationWarningsCount FROM dbo.StoryIntelligenceRuns r LEFT JOIN dbo.Projects p ON p.ProjectID = r.ProjectID LEFT JOIN dbo.Books b ON b.BookID = r.BookID LEFT JOIN dbo.StoryIntelligenceChapterResults cr ON cr.StoryIntelligenceRunID = r.StoryIntelligenceRunID LEFT JOIN dbo.StoryIntelligenceSceneResults sr ON sr.StoryIntelligenceRunID = r.StoryIntelligenceRunID GROUP BY r.StoryIntelligenceRunID, r.CreatedUtc, r.UserID, r.ProjectID, p.ProjectName, r.BookID, b.BookTitle, r.ChapterID, r.ChapterNumber, r.Status, r.SourceType, r.FailureStage, r.TotalTokens, r.TotalDurationMs, r.EstimatedCostGBP, r.EstimatedCostUSD, cr.ValidationErrorsCount, cr.ValidationWarningsCount ORDER BY r.CreatedUtc DESC; END; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_GetAdmin @StoryIntelligenceRunID 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.SourceFileName, r.SourceFileSizeBytes, r.SourceWordCount, r.SourceCharacterCount, 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 LEFT JOIN dbo.Projects p ON p.ProjectID = r.ProjectID LEFT JOIN dbo.Books b ON b.BookID = r.BookID WHERE r.StoryIntelligenceRunID = @StoryIntelligenceRunID; END; GO