@littlebearapps/platform-admin-sdk 1.4.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/dist/templates.d.ts +1 -1
  2. package/dist/templates.js +232 -2
  3. package/package.json +1 -1
  4. package/templates/full/config/audit-targets.yaml +72 -0
  5. package/templates/full/dashboard/src/components/notifications/NotificationBell.tsx +30 -0
  6. package/templates/full/dashboard/src/components/notifications/NotificationList.tsx +116 -0
  7. package/templates/full/dashboard/src/components/notifications/index.ts +2 -0
  8. package/templates/full/dashboard/src/components/patterns/ActivePatterns.tsx +62 -0
  9. package/templates/full/dashboard/src/components/patterns/PatternStats.tsx +60 -0
  10. package/templates/full/dashboard/src/components/patterns/PatternTabs.tsx +116 -0
  11. package/templates/full/dashboard/src/components/patterns/SuggestionsQueue.tsx +115 -0
  12. package/templates/full/dashboard/src/components/patterns/SystemPatterns.tsx +52 -0
  13. package/templates/full/dashboard/src/components/patterns/index.ts +5 -0
  14. package/templates/full/dashboard/src/components/reports/GapDetectionReport.tsx +69 -0
  15. package/templates/full/dashboard/src/components/reports/SdkAuditReport.tsx +72 -0
  16. package/templates/full/dashboard/src/components/reports/index.ts +2 -0
  17. package/templates/full/dashboard/src/components/search/SearchModal.tsx +108 -0
  18. package/templates/full/dashboard/src/pages/api/notifications/[id]/read.ts +37 -0
  19. package/templates/full/dashboard/src/pages/api/notifications/index.ts +47 -0
  20. package/templates/full/dashboard/src/pages/api/notifications/read-all.ts +28 -0
  21. package/templates/full/dashboard/src/pages/api/notifications/unread-count.ts +31 -0
  22. package/templates/full/dashboard/src/pages/api/patterns/approve.ts +55 -0
  23. package/templates/full/dashboard/src/pages/api/patterns/cache-refresh.ts +38 -0
  24. package/templates/full/dashboard/src/pages/api/patterns/discover.ts +36 -0
  25. package/templates/full/dashboard/src/pages/api/patterns/index.ts +36 -0
  26. package/templates/full/dashboard/src/pages/api/patterns/ready-for-review.ts +39 -0
  27. package/templates/full/dashboard/src/pages/api/patterns/reject.ts +54 -0
  28. package/templates/full/dashboard/src/pages/api/patterns/stats.ts +39 -0
  29. package/templates/full/dashboard/src/pages/api/patterns/suggestions.ts +43 -0
  30. package/templates/full/dashboard/src/pages/api/reports/audit.ts +45 -0
  31. package/templates/full/dashboard/src/pages/api/reports/usage.ts +52 -0
  32. package/templates/full/dashboard/src/pages/api/search/index.ts +74 -0
  33. package/templates/full/dashboard/src/pages/api/search/reindex.ts +28 -0
  34. package/templates/full/dashboard/src/pages/api/search/stats.ts +27 -0
  35. package/templates/full/dashboard/src/pages/api/settings/index.ts +37 -0
  36. package/templates/full/dashboard/src/pages/api/settings/update.ts +41 -0
  37. package/templates/full/dashboard/src/pages/api/topology/index.ts +56 -0
  38. package/templates/full/dashboard/src/pages/notifications.astro +11 -0
  39. package/templates/full/migrations/008_auditor.sql +99 -0
  40. package/templates/full/migrations/010_pricing_versions.sql +110 -0
  41. package/templates/full/migrations/011_multi_account.sql +51 -0
  42. package/templates/full/scripts/ops/set-kv-pricing.ts +182 -0
  43. package/templates/full/scripts/ops/universal-backfill.ts +147 -0
  44. package/templates/full/workers/lib/ai-judge-schema.ts +181 -0
  45. package/templates/full/workers/lib/auditor/comprehensive-report.ts +407 -0
  46. package/templates/full/workers/lib/auditor/feature-coverage.ts +348 -0
  47. package/templates/full/workers/lib/auditor/index.ts +9 -0
  48. package/templates/full/workers/lib/auditor/types.ts +167 -0
  49. package/templates/full/workers/platform-auditor.ts +1071 -0
  50. package/templates/full/wrangler.auditor.jsonc.hbs +75 -0
  51. package/templates/shared/.github/workflows/contract-check.yml.hbs +42 -0
  52. package/templates/shared/.github/workflows/dashboard-deploy.yml.hbs +39 -0
  53. package/templates/shared/.github/workflows/platform-check.yml.hbs +28 -0
  54. package/templates/shared/.github/workflows/security.yml +33 -0
  55. package/templates/shared/config/observability.yaml.hbs +276 -0
  56. package/templates/shared/contracts/schemas/envelope.v1.schema.json +64 -0
  57. package/templates/shared/contracts/schemas/error_report.v1.schema.json +65 -0
  58. package/templates/shared/contracts/types/telemetry-envelope.types.ts +139 -0
  59. package/templates/shared/dashboard/astro.config.mjs +21 -0
  60. package/templates/shared/dashboard/package.json.hbs +29 -0
  61. package/templates/shared/dashboard/src/components/Header.astro +29 -0
  62. package/templates/shared/dashboard/src/components/Nav.astro.hbs +59 -0
  63. package/templates/shared/dashboard/src/components/infrastructure/AlertHistory.tsx +57 -0
  64. package/templates/shared/dashboard/src/components/infrastructure/InfrastructureStats.tsx +73 -0
  65. package/templates/shared/dashboard/src/components/infrastructure/ServiceRegistry.tsx +55 -0
  66. package/templates/shared/dashboard/src/components/infrastructure/UptimeStatus.tsx +56 -0
  67. package/templates/shared/dashboard/src/components/infrastructure/index.ts +4 -0
  68. package/templates/shared/dashboard/src/components/overview/ActivityFeed.tsx +134 -0
  69. package/templates/shared/dashboard/src/components/overview/CostQuadrant.tsx +131 -0
  70. package/templates/shared/dashboard/src/components/overview/ErrorsQuadrant.tsx +113 -0
  71. package/templates/shared/dashboard/src/components/overview/HealthQuadrant.tsx +87 -0
  72. package/templates/shared/dashboard/src/components/overview/MissionControl.tsx +139 -0
  73. package/templates/shared/dashboard/src/components/resources/AllowanceStatus.tsx +44 -0
  74. package/templates/shared/dashboard/src/components/resources/CostCentreOverview.tsx +42 -0
  75. package/templates/shared/dashboard/src/components/resources/ResourceTabs.tsx +69 -0
  76. package/templates/shared/dashboard/src/components/resources/index.ts +3 -0
  77. package/templates/shared/dashboard/src/components/settings/SettingsCard.tsx +21 -0
  78. package/templates/shared/dashboard/src/components/settings/index.ts +1 -0
  79. package/templates/shared/dashboard/src/components/ui/AlertBanner.tsx +39 -0
  80. package/templates/shared/dashboard/src/components/ui/Breadcrumbs.tsx +27 -0
  81. package/templates/shared/dashboard/src/components/ui/EmptyState.tsx +26 -0
  82. package/templates/shared/dashboard/src/components/ui/ErrorBoundary.tsx +42 -0
  83. package/templates/shared/dashboard/src/components/ui/LoadingSkeleton.tsx +18 -0
  84. package/templates/shared/dashboard/src/components/ui/PageShell.tsx +26 -0
  85. package/templates/shared/dashboard/src/components/ui/Sparkline.tsx +127 -0
  86. package/templates/shared/dashboard/src/components/ui/StatusDot.tsx +21 -0
  87. package/templates/shared/dashboard/src/components/ui/Toast.tsx +44 -0
  88. package/templates/shared/dashboard/src/components/ui/index.ts +9 -0
  89. package/templates/shared/dashboard/src/components/usage/AnomaliesWidget.tsx +68 -0
  90. package/templates/shared/dashboard/src/components/usage/HourlyUsageChart.tsx +55 -0
  91. package/templates/shared/dashboard/src/components/usage/PlanAllowanceDashboard.tsx +67 -0
  92. package/templates/shared/dashboard/src/components/usage/ProjectCostBreakdown.tsx +55 -0
  93. package/templates/shared/dashboard/src/components/usage/index.ts +4 -0
  94. package/templates/shared/dashboard/src/env.d.ts.hbs +34 -0
  95. package/templates/shared/dashboard/src/layouts/DashboardLayout.astro +37 -0
  96. package/templates/shared/dashboard/src/lib/cloudflare/costs.ts +21 -0
  97. package/templates/shared/dashboard/src/lib/fetch.ts +29 -0
  98. package/templates/shared/dashboard/src/lib/types.ts +72 -0
  99. package/templates/shared/dashboard/src/middleware/auth.ts +100 -0
  100. package/templates/shared/dashboard/src/middleware/index.ts +1 -0
  101. package/templates/shared/dashboard/src/pages/api/costs/overview.ts +65 -0
  102. package/templates/shared/dashboard/src/pages/api/costs/providers.ts +47 -0
  103. package/templates/shared/dashboard/src/pages/api/infrastructure/services.ts +55 -0
  104. package/templates/shared/dashboard/src/pages/api/infrastructure/stats.ts +99 -0
  105. package/templates/shared/dashboard/src/pages/api/overview/summary.ts +311 -0
  106. package/templates/shared/dashboard/src/pages/api/usage/allowances.ts +56 -0
  107. package/templates/shared/dashboard/src/pages/api/usage/anomalies.ts +45 -0
  108. package/templates/shared/dashboard/src/pages/api/usage/billing.ts +53 -0
  109. package/templates/shared/dashboard/src/pages/api/usage/circuit-breakers.ts +44 -0
  110. package/templates/shared/dashboard/src/pages/api/usage/granular.ts +50 -0
  111. package/templates/shared/dashboard/src/pages/api/usage/hourly.ts +45 -0
  112. package/templates/shared/dashboard/src/pages/api/usage/projects.ts +51 -0
  113. package/templates/shared/dashboard/src/pages/api/usage/status.ts +42 -0
  114. package/templates/shared/dashboard/src/pages/api/user/identity.ts +11 -0
  115. package/templates/shared/dashboard/src/pages/dashboard.astro +11 -0
  116. package/templates/shared/dashboard/src/pages/index.astro +3 -0
  117. package/templates/shared/dashboard/src/pages/resources.astro +11 -0
  118. package/templates/shared/dashboard/src/pages/settings/index.astro +28 -0
  119. package/templates/shared/dashboard/src/pages/settings/notifications.astro +34 -0
  120. package/templates/shared/dashboard/src/pages/settings/thresholds.astro +39 -0
  121. package/templates/shared/dashboard/src/pages/settings/usage.astro +28 -0
  122. package/templates/shared/dashboard/src/styles/global.css +29 -0
  123. package/templates/shared/dashboard/tailwind.config.mjs +9 -0
  124. package/templates/shared/dashboard/tsconfig.json +9 -0
  125. package/templates/shared/dashboard/wrangler.json.hbs +47 -0
  126. package/templates/shared/docs/architecture.md +89 -0
  127. package/templates/shared/docs/post-deploy-runbook.md +126 -0
  128. package/templates/shared/docs/troubleshooting.md +91 -0
  129. package/templates/shared/package.json.hbs +17 -1
  130. package/templates/shared/scripts/ops/backfill-cloudflare-daily.ts +145 -0
  131. package/templates/shared/scripts/ops/backfill-cloudflare-hourly.ts +473 -0
  132. package/templates/shared/scripts/ops/backfill-monthly-rollups.ts +125 -0
  133. package/templates/shared/scripts/ops/discover-graphql-datasets.ts +482 -0
  134. package/templates/shared/scripts/ops/reset-budget-state.ts +279 -0
  135. package/templates/shared/scripts/ops/validate-controls.js +141 -0
  136. package/templates/shared/scripts/ops/validate-pipeline.ts +237 -0
  137. package/templates/shared/scripts/ops/verify-account-completeness.ts +236 -0
  138. package/templates/shared/scripts/validate-schemas.js +61 -0
  139. package/templates/shared/tests/contract/validate-schemas.test.ts +130 -0
  140. package/templates/shared/tests/fixtures/telemetry-envelope-invalid.json +9 -0
  141. package/templates/shared/tests/fixtures/telemetry-envelope-valid.json +27 -0
  142. package/templates/shared/tests/helpers/mock-d1.ts +61 -0
  143. package/templates/shared/tests/helpers/mock-kv.ts +37 -0
  144. package/templates/shared/tests/unit/workers/batch-persistence.test.ts +133 -0
  145. package/templates/shared/tests/unit/workers/budget-enforcement.test.ts +214 -0
  146. package/templates/shared/vitest.config.ts +18 -0
  147. package/templates/shared/workers/lib/usage/collectors/anthropic.ts +114 -0
  148. package/templates/shared/workers/lib/usage/collectors/apify.ts +96 -0
  149. package/templates/shared/workers/lib/usage/collectors/custom-http.ts +151 -0
  150. package/templates/shared/workers/lib/usage/collectors/deepseek.ts +92 -0
  151. package/templates/shared/workers/lib/usage/collectors/gemini.ts +263 -0
  152. package/templates/shared/workers/lib/usage/collectors/github.ts +362 -0
  153. package/templates/shared/workers/lib/usage/collectors/index.ts +31 -15
  154. package/templates/shared/workers/lib/usage/collectors/minimax.ts +106 -0
  155. package/templates/shared/workers/lib/usage/collectors/openai.ts +171 -0
  156. package/templates/shared/workers/lib/usage/collectors/resend.ts +79 -0
  157. package/templates/shared/workers/lib/usage/collectors/stripe.ts +192 -0
  158. package/templates/shared/workers/lib/usage/shared/types.ts +46 -0
  159. package/templates/shared/workers/platform-usage.ts +98 -8
  160. package/templates/standard/dashboard/src/components/errors/ErrorStats.tsx +53 -0
  161. package/templates/standard/dashboard/src/components/errors/ErrorsTable.tsx +133 -0
  162. package/templates/standard/dashboard/src/components/errors/index.ts +2 -0
  163. package/templates/standard/dashboard/src/components/health/CircuitBreakerEvents.tsx +69 -0
  164. package/templates/standard/dashboard/src/components/health/CircuitBreakerPanel.tsx +97 -0
  165. package/templates/standard/dashboard/src/components/health/DlqStatusCard.tsx +52 -0
  166. package/templates/standard/dashboard/src/components/health/HealthTabs.tsx +86 -0
  167. package/templates/standard/dashboard/src/components/health/index.ts +4 -0
  168. package/templates/standard/dashboard/src/lib/errors.ts +28 -0
  169. package/templates/standard/dashboard/src/pages/api/errors/[fingerprint]/mute.ts +49 -0
  170. package/templates/standard/dashboard/src/pages/api/errors/[fingerprint]/resolve.ts +36 -0
  171. package/templates/standard/dashboard/src/pages/api/errors/[fingerprint].ts +55 -0
  172. package/templates/standard/dashboard/src/pages/api/errors/index.ts +58 -0
  173. package/templates/standard/dashboard/src/pages/api/errors/stats.ts +55 -0
  174. package/templates/standard/dashboard/src/pages/api/health/audit-history.ts +37 -0
  175. package/templates/standard/dashboard/src/pages/api/health/dlq.ts +43 -0
  176. package/templates/standard/dashboard/src/pages/circuit-breakers.astro +13 -0
  177. package/templates/standard/dashboard/src/pages/errors.astro +13 -0
  178. package/templates/standard/dashboard/src/pages/health.astro +11 -0
  179. package/templates/standard/migrations/009_topology_mapper.sql +65 -0
  180. package/templates/standard/tests/unit/error-collector/capture.test.ts +106 -0
  181. package/templates/standard/tests/unit/error-collector/fingerprint.test.ts +155 -0
  182. package/templates/standard/workers/lib/error-collector/email-health-alerts.ts +37 -3
  183. package/templates/standard/workers/lib/error-collector/gap-alerts.ts +32 -1
  184. package/templates/standard/workers/lib/mapper/attribution-check.ts +339 -0
  185. package/templates/standard/workers/lib/mapper/index.ts +7 -0
  186. package/templates/standard/workers/platform-mapper.ts +482 -0
  187. package/templates/standard/workers/platform-sdk-test-client.ts +125 -0
  188. package/templates/standard/wrangler.mapper.jsonc.hbs +85 -0
  189. package/templates/standard/wrangler.sdk-test-client.jsonc.hbs +62 -0
@@ -0,0 +1,407 @@
1
+ /**
2
+ * Comprehensive Audit Report Module
3
+ *
4
+ * Aggregates findings from all audit dimensions:
5
+ * - Gap detection (platform-sentinel gap_detection_log)
6
+ * - Attribution checking (attribution_reports)
7
+ * - Feature coverage audit
8
+ * - AI Judge results
9
+ *
10
+ * Produces ranked action items sorted by severity.
11
+ *
12
+ * @module workers/lib/auditor/comprehensive-report
13
+ */
14
+
15
+ import type { Logger } from '@littlebearapps/platform-consumer-sdk';
16
+ import type { FeatureCoverageReport } from './feature-coverage';
17
+
18
+ // =============================================================================
19
+ // Types
20
+ // =============================================================================
21
+
22
+ /** Environment bindings (generic to work with both raw Env and TrackedEnv) */
23
+ export interface ComprehensiveReportEnv {
24
+ PLATFORM_DB: {
25
+ prepare: (query: string) => {
26
+ bind: (...args: unknown[]) => {
27
+ run: () => Promise<unknown>;
28
+ all: <T>() => Promise<{ results?: T[] }>;
29
+ first: <T>() => Promise<T | null>;
30
+ };
31
+ all: <T>() => Promise<{ results?: T[] }>;
32
+ first: <T>() => Promise<T | null>;
33
+ };
34
+ };
35
+ PLATFORM_CACHE: {
36
+ get: (key: string) => Promise<string | null>;
37
+ put: (key: string, value: string, options?: { expirationTtl?: number }) => Promise<void>;
38
+ };
39
+ }
40
+
41
+ export type ActionSeverity = 'critical' | 'high' | 'medium' | 'low';
42
+
43
+ export interface ActionItem {
44
+ category: 'gap' | 'attribution' | 'feature' | 'ai_judge';
45
+ severity: ActionSeverity;
46
+ title: string;
47
+ description: string;
48
+ recommendation: string;
49
+ }
50
+
51
+ export interface ComprehensiveReport {
52
+ id: string;
53
+ generatedAt: string;
54
+ gapSummary: {
55
+ eventsCount: number;
56
+ totalMissingHours: number;
57
+ worstGapDay: string | null;
58
+ averageSeverity: string;
59
+ };
60
+ attributionSummary: {
61
+ totalResources: number;
62
+ attributedCount: number;
63
+ unattributedCount: number;
64
+ unattributedResources: Array<{ type: string; name: string }>;
65
+ };
66
+ featureCoverageSummary: {
67
+ definedCount: number;
68
+ activeCount: number;
69
+ dormantCount: number;
70
+ undefinedCount: number;
71
+ };
72
+ aiJudgeSummary: {
73
+ averageScore: number | null;
74
+ recommendations: string[];
75
+ };
76
+ actionItems: ActionItem[];
77
+ criticalItemsCount: number;
78
+ }
79
+
80
+ // =============================================================================
81
+ // Main Report Generator
82
+ // =============================================================================
83
+
84
+ /**
85
+ * Generate comprehensive audit report aggregating all dimensions.
86
+ */
87
+ export async function generateComprehensiveReport(
88
+ env: ComprehensiveReportEnv,
89
+ featureCoverage: FeatureCoverageReport | null,
90
+ log: Logger
91
+ ): Promise<ComprehensiveReport> {
92
+ const id = crypto.randomUUID();
93
+ const generatedAt = new Date().toISOString();
94
+ const actionItems: ActionItem[] = [];
95
+
96
+ // 1. Gap detection summary (last 7 days)
97
+ const gapSummary = await getGapSummary(env, log);
98
+
99
+ if (gapSummary.totalMissingHours > 0) {
100
+ actionItems.push({
101
+ category: 'gap',
102
+ severity: gapSummary.totalMissingHours > 10 ? 'high' : 'medium',
103
+ title: `${gapSummary.totalMissingHours} missing hourly snapshots in last 7 days`,
104
+ description: `Gap detection found ${gapSummary.eventsCount} gap events. Worst day: ${gapSummary.worstGapDay || 'N/A'}`,
105
+ recommendation: 'Run backfill via npm run backfill or investigate collection issues',
106
+ });
107
+ }
108
+
109
+ // 2. Attribution summary (latest report)
110
+ const attributionSummary = await getAttributionSummary(env, log);
111
+
112
+ if (attributionSummary.unattributedCount > 0) {
113
+ actionItems.push({
114
+ category: 'attribution',
115
+ severity: attributionSummary.unattributedCount > 5 ? 'high' : 'medium',
116
+ title: `${attributionSummary.unattributedCount} unattributed Cloudflare resources`,
117
+ description: `Resources not mapped to any project: ${attributionSummary.unattributedResources
118
+ .map((r) => `${r.type}:${r.name}`)
119
+ .slice(0, 5)
120
+ .join(', ')}${attributionSummary.unattributedCount > 5 ? '...' : ''}`,
121
+ recommendation: 'Add resource-to-project mappings in services.yaml',
122
+ });
123
+ }
124
+
125
+ // 3. Feature coverage summary
126
+ const featureCoverageSummary = featureCoverage
127
+ ? {
128
+ definedCount: featureCoverage.summary.totalDefined,
129
+ activeCount: featureCoverage.summary.active,
130
+ dormantCount: featureCoverage.summary.dormant,
131
+ undefinedCount: featureCoverage.summary.undefined,
132
+ }
133
+ : { definedCount: 0, activeCount: 0, dormantCount: 0, undefinedCount: 0 };
134
+
135
+ if (featureCoverageSummary.dormantCount > 0) {
136
+ actionItems.push({
137
+ category: 'feature',
138
+ severity: 'low',
139
+ title: `${featureCoverageSummary.dormantCount} dormant features defined`,
140
+ description: 'Features defined in budgets.yaml but not sending heartbeats in 7 days',
141
+ recommendation: 'Review if these features are still used or can be removed from config',
142
+ });
143
+ }
144
+
145
+ if (featureCoverageSummary.undefinedCount > 0) {
146
+ actionItems.push({
147
+ category: 'feature',
148
+ severity: 'medium',
149
+ title: `${featureCoverageSummary.undefinedCount} undefined features reporting`,
150
+ description: 'Features sending telemetry but not defined in budgets.yaml',
151
+ recommendation: 'Add budget definitions for these features in budgets.yaml',
152
+ });
153
+ }
154
+
155
+ // 4. AI Judge summary (latest results)
156
+ const aiJudgeSummary = await getAIJudgeSummary(env, log);
157
+
158
+ if (aiJudgeSummary.recommendations.length > 0) {
159
+ for (const rec of aiJudgeSummary.recommendations.slice(0, 3)) {
160
+ actionItems.push({
161
+ category: 'ai_judge',
162
+ severity: 'medium',
163
+ title: 'AI Judge recommendation',
164
+ description: rec,
165
+ recommendation: 'Review and implement AI Judge suggestions',
166
+ });
167
+ }
168
+ }
169
+
170
+ // Sort by severity
171
+ const severityOrder: Record<ActionSeverity, number> = {
172
+ critical: 0,
173
+ high: 1,
174
+ medium: 2,
175
+ low: 3,
176
+ };
177
+ actionItems.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
178
+
179
+ const report: ComprehensiveReport = {
180
+ id,
181
+ generatedAt,
182
+ gapSummary,
183
+ attributionSummary,
184
+ featureCoverageSummary,
185
+ aiJudgeSummary,
186
+ actionItems,
187
+ criticalItemsCount: actionItems.filter((i) => i.severity === 'critical').length,
188
+ };
189
+
190
+ log.info('Generated comprehensive audit report', {
191
+ id,
192
+ actionItems: actionItems.length,
193
+ criticalItems: report.criticalItemsCount,
194
+ });
195
+
196
+ return report;
197
+ }
198
+
199
+ // =============================================================================
200
+ // Data Aggregation Helpers
201
+ // =============================================================================
202
+
203
+ /** Get gap detection summary from last 7 days */
204
+ async function getGapSummary(
205
+ env: ComprehensiveReportEnv,
206
+ log: Logger
207
+ ): Promise<ComprehensiveReport['gapSummary']> {
208
+ try {
209
+ const result = await env.PLATFORM_DB.prepare(
210
+ `
211
+ SELECT
212
+ COUNT(*) as events_count,
213
+ SUM(missing_hours_count) as total_missing_hours,
214
+ MAX(CASE WHEN missing_hours_count = (
215
+ SELECT MAX(missing_hours_count) FROM gap_detection_log
216
+ WHERE detection_time > datetime('now', '-7 days')
217
+ ) THEN date(detection_time) END) as worst_gap_day,
218
+ AVG(CASE severity
219
+ WHEN 'critical' THEN 3
220
+ WHEN 'warning' THEN 2
221
+ WHEN 'ok' THEN 1
222
+ ELSE 0
223
+ END) as avg_severity_score
224
+ FROM gap_detection_log
225
+ WHERE detection_time > datetime('now', '-7 days')
226
+ `
227
+ ).first<{
228
+ events_count: number;
229
+ total_missing_hours: number;
230
+ worst_gap_day: string | null;
231
+ avg_severity_score: number;
232
+ }>();
233
+
234
+ if (!result) {
235
+ return { eventsCount: 0, totalMissingHours: 0, worstGapDay: null, averageSeverity: 'ok' };
236
+ }
237
+
238
+ const avgScore = result.avg_severity_score ?? 0;
239
+ let averageSeverity = 'ok';
240
+ if (avgScore >= 2.5) averageSeverity = 'critical';
241
+ else if (avgScore >= 1.5) averageSeverity = 'warning';
242
+
243
+ return {
244
+ eventsCount: result.events_count ?? 0,
245
+ totalMissingHours: result.total_missing_hours ?? 0,
246
+ worstGapDay: result.worst_gap_day,
247
+ averageSeverity,
248
+ };
249
+ } catch (error) {
250
+ log.error('Failed to get gap summary', error);
251
+ return { eventsCount: 0, totalMissingHours: 0, worstGapDay: null, averageSeverity: 'ok' };
252
+ }
253
+ }
254
+
255
+ /** Get attribution summary from latest report */
256
+ async function getAttributionSummary(
257
+ env: ComprehensiveReportEnv,
258
+ log: Logger
259
+ ): Promise<ComprehensiveReport['attributionSummary']> {
260
+ try {
261
+ const result = await env.PLATFORM_DB.prepare(
262
+ `
263
+ SELECT report_json
264
+ FROM attribution_reports
265
+ ORDER BY discovery_time DESC
266
+ LIMIT 1
267
+ `
268
+ ).first<{ report_json: string }>();
269
+
270
+ if (!result?.report_json) {
271
+ return { totalResources: 0, attributedCount: 0, unattributedCount: 0, unattributedResources: [] };
272
+ }
273
+
274
+ const report = JSON.parse(result.report_json) as {
275
+ totalResources: number;
276
+ attributedCount: number;
277
+ unattributedCount: number;
278
+ unattributed: Array<{ resource: { resourceType: string; resourceName: string } }>;
279
+ };
280
+
281
+ return {
282
+ totalResources: report.totalResources,
283
+ attributedCount: report.attributedCount,
284
+ unattributedCount: report.unattributedCount,
285
+ unattributedResources: (report.unattributed || []).map((a) => ({
286
+ type: a.resource.resourceType,
287
+ name: a.resource.resourceName,
288
+ })),
289
+ };
290
+ } catch (error) {
291
+ log.error('Failed to get attribution summary', error);
292
+ return { totalResources: 0, attributedCount: 0, unattributedCount: 0, unattributedResources: [] };
293
+ }
294
+ }
295
+
296
+ /** Get AI Judge summary from latest audit results */
297
+ async function getAIJudgeSummary(
298
+ env: ComprehensiveReportEnv,
299
+ log: Logger
300
+ ): Promise<ComprehensiveReport['aiJudgeSummary']> {
301
+ try {
302
+ const result = await env.PLATFORM_DB.prepare(
303
+ `
304
+ SELECT
305
+ AVG(ai_judge_score) as avg_score,
306
+ GROUP_CONCAT(ai_judge_summary) as summaries
307
+ FROM audit_results
308
+ WHERE created_at > datetime('now', '-7 days')
309
+ AND ai_judge_score IS NOT NULL
310
+ `
311
+ ).first<{ avg_score: number | null; summaries: string | null }>();
312
+
313
+ if (!result?.avg_score) {
314
+ return { averageScore: null, recommendations: [] };
315
+ }
316
+
317
+ const summaries = result.summaries?.split(',') ?? [];
318
+ const uniqueRecommendations = [...new Set(summaries)].filter((s) => s.trim()).slice(0, 5);
319
+
320
+ return {
321
+ averageScore: Math.round(result.avg_score),
322
+ recommendations: uniqueRecommendations,
323
+ };
324
+ } catch (error) {
325
+ log.error('Failed to get AI Judge summary', error);
326
+ return { averageScore: null, recommendations: [] };
327
+ }
328
+ }
329
+
330
+ // =============================================================================
331
+ // Storage
332
+ // =============================================================================
333
+
334
+ /** Store comprehensive audit report in D1 */
335
+ export async function storeComprehensiveReport(
336
+ env: ComprehensiveReportEnv,
337
+ report: ComprehensiveReport,
338
+ log: Logger
339
+ ): Promise<void> {
340
+ try {
341
+ await env.PLATFORM_DB.prepare(
342
+ `
343
+ INSERT INTO comprehensive_audit_reports (
344
+ id, generated_at,
345
+ gap_events_count, total_missing_hours, worst_gap_day, average_gap_severity,
346
+ total_resources, attributed_count, unattributed_count, unattributed_resources,
347
+ defined_features_count, active_features_count, dormant_features_count, undefined_features_count,
348
+ ai_judge_avg_score, ai_judge_recommendations,
349
+ action_items_count, critical_items_count, action_items,
350
+ report_json
351
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
352
+ `
353
+ )
354
+ .bind(
355
+ report.id,
356
+ report.generatedAt,
357
+ report.gapSummary.eventsCount,
358
+ report.gapSummary.totalMissingHours,
359
+ report.gapSummary.worstGapDay,
360
+ report.gapSummary.averageSeverity,
361
+ report.attributionSummary.totalResources,
362
+ report.attributionSummary.attributedCount,
363
+ report.attributionSummary.unattributedCount,
364
+ JSON.stringify(report.attributionSummary.unattributedResources),
365
+ report.featureCoverageSummary.definedCount,
366
+ report.featureCoverageSummary.activeCount,
367
+ report.featureCoverageSummary.dormantCount,
368
+ report.featureCoverageSummary.undefinedCount,
369
+ report.aiJudgeSummary.averageScore,
370
+ JSON.stringify(report.aiJudgeSummary.recommendations),
371
+ report.actionItems.length,
372
+ report.criticalItemsCount,
373
+ JSON.stringify(report.actionItems),
374
+ JSON.stringify(report)
375
+ )
376
+ .run();
377
+
378
+ log.info('Stored comprehensive audit report', { id: report.id });
379
+ } catch (error) {
380
+ log.error('Failed to store comprehensive report', error);
381
+ }
382
+ }
383
+
384
+ /** Get latest comprehensive report from D1 */
385
+ export async function getLatestComprehensiveReport(
386
+ env: ComprehensiveReportEnv,
387
+ log: Logger
388
+ ): Promise<ComprehensiveReport | null> {
389
+ try {
390
+ const result = await env.PLATFORM_DB.prepare(
391
+ `
392
+ SELECT report_json
393
+ FROM comprehensive_audit_reports
394
+ ORDER BY generated_at DESC
395
+ LIMIT 1
396
+ `
397
+ ).first<{ report_json: string }>();
398
+
399
+ if (result?.report_json) {
400
+ return JSON.parse(result.report_json) as ComprehensiveReport;
401
+ }
402
+ return null;
403
+ } catch (error) {
404
+ log.error('Failed to get latest comprehensive report', error);
405
+ return null;
406
+ }
407
+ }