CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRun_ListActiveBookSummaryForUser @UserID int AS BEGIN SET NOCOUNT ON; ;WITH ActiveScopes AS ( SELECT r.UserID, r.ProjectID, r.BookID, LatestCreatedUtc = MAX(r.CreatedUtc) FROM dbo.StoryIntelligenceRuns r WHERE r.UserID = @UserID AND r.Status IN (N'Pending', N'Running') GROUP BY r.UserID, r.ProjectID, r.BookID ), ScopedRuns AS ( SELECT r.* FROM dbo.StoryIntelligenceRuns r INNER JOIN ActiveScopes scope ON scope.UserID = r.UserID AND ISNULL(scope.ProjectID, -1) = ISNULL(r.ProjectID, -1) AND ISNULL(scope.BookID, -1) = ISNULL(r.BookID, -1) AND r.CreatedUtc >= DATEADD(minute, -30, scope.LatestCreatedUtc) WHERE r.Status IN (N'Pending', N'Running', N'Completed', N'CompletedWithWarnings', N'Failed', N'Cancelled') ), CanonicalSceneScope AS ( SELECT DISTINCT r.UserID, r.ProjectID, r.BookID, s.SceneID FROM ScopedRuns r INNER JOIN dbo.Chapters c ON c.ChapterID = r.ChapterID AND c.IsArchived = 0 INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0 ), RunSummary AS ( SELECT r.UserID, r.ProjectID, r.BookID, COUNT(1) AS RunCount, SUM(CASE WHEN r.Status = N'Pending' THEN 1 ELSE 0 END) AS PendingRunCount, SUM(CASE WHEN r.Status = N'Running' THEN 1 ELSE 0 END) AS RunningRunCount, SUM(CASE WHEN r.Status IN (N'Completed', N'CompletedWithWarnings') THEN 1 ELSE 0 END) AS CompletedRunCount, SUM(CASE WHEN r.Status IN (N'Failed', N'Cancelled') THEN 1 ELSE 0 END) AS FailedRunCount, SUM(ISNULL(r.TotalDetectedScenes, 0)) AS TotalDetectedScenes, SUM(ISNULL(r.CompletedScenes, 0)) AS CompletedScenes, SUM(ISNULL(r.FailedScenes, 0)) AS FailedScenes, SUM(CASE WHEN r.Status = N'Running' THEN CASE WHEN ISNULL(r.TotalDetectedScenes, 0) - ISNULL(r.CompletedScenes, 0) - ISNULL(r.FailedScenes, 0) > 0 THEN ISNULL(r.TotalDetectedScenes, 0) - ISNULL(r.CompletedScenes, 0) - ISNULL(r.FailedScenes, 0) ELSE 0 END ELSE 0 END) AS ActiveScenes, SUM(ISNULL(r.TotalDurationMs, 0)) AS TotalDurationMs, MIN(r.CreatedUtc) AS FirstCreatedUtc, MAX(r.UpdatedUtc) AS UpdatedUtc FROM ScopedRuns r GROUP BY r.UserID, r.ProjectID, r.BookID ), ExpectedSceneSummary AS ( SELECT scope.UserID, scope.ProjectID, scope.BookID, COUNT(scope.SceneID) AS TotalExpectedScenes FROM CanonicalSceneScope scope GROUP BY scope.UserID, scope.ProjectID, scope.BookID ) SELECT summary.UserID, summary.ProjectID, p.ProjectName AS ProjectTitle, summary.BookID, b.BookTitle, b.Subtitle AS BookSubtitle, summary.RunCount, summary.PendingRunCount, summary.RunningRunCount, summary.CompletedRunCount, summary.FailedRunCount, COALESCE(expected.TotalExpectedScenes, NULLIF(summary.TotalDetectedScenes, 0), 0) AS TotalExpectedScenes, summary.TotalDetectedScenes, summary.CompletedScenes, summary.FailedScenes, summary.ActiveScenes, CASE WHEN COALESCE(expected.TotalExpectedScenes, NULLIF(summary.TotalDetectedScenes, 0), 0) <= 0 THEN 0 ELSE CONVERT(int, ROUND(summary.CompletedScenes * 100.0 / COALESCE(expected.TotalExpectedScenes, NULLIF(summary.TotalDetectedScenes, 0), 1), 0)) END AS ProgressPercent, CASE WHEN summary.RunningRunCount > 0 AND COALESCE(expected.TotalExpectedScenes, NULLIF(summary.TotalDetectedScenes, 0), 0) > 0 AND summary.CompletedScenes + summary.FailedScenes >= COALESCE(expected.TotalExpectedScenes, NULLIF(summary.TotalDetectedScenes, 0), 0) THEN N'Finalising' WHEN summary.FailedRunCount > 0 OR summary.FailedScenes > 0 THEN N'CompletedWithWarnings' WHEN summary.RunningRunCount > 0 THEN N'Running' WHEN summary.PendingRunCount > 0 THEN N'Pending' ELSE N'Completed' END AS Status, summary.TotalDurationMs, summary.FirstCreatedUtc, summary.UpdatedUtc FROM RunSummary summary LEFT JOIN ExpectedSceneSummary expected ON expected.UserID = summary.UserID AND ISNULL(expected.ProjectID, -1) = ISNULL(summary.ProjectID, -1) AND ISNULL(expected.BookID, -1) = ISNULL(summary.BookID, -1) LEFT JOIN dbo.Projects p ON p.ProjectID = summary.ProjectID LEFT JOIN dbo.Books b ON b.BookID = summary.BookID WHERE summary.PendingRunCount > 0 OR summary.RunningRunCount > 0 ORDER BY summary.UpdatedUtc DESC, summary.FirstCreatedUtc; END; GO