@uptimizr/db 2.0.0 → 2.1.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 (173) hide show
  1. package/AGENTS.md +228 -3
  2. package/README.md +13 -0
  3. package/dist/cli/seed.js +9 -7
  4. package/dist/cli/seed.js.map +1 -1
  5. package/dist/duckdb/migrations.d.ts.map +1 -1
  6. package/dist/duckdb/migrations.js +169 -0
  7. package/dist/duckdb/migrations.js.map +1 -1
  8. package/dist/duckdb/panelSpecs.d.ts +29 -0
  9. package/dist/duckdb/panelSpecs.d.ts.map +1 -0
  10. package/dist/duckdb/panelSpecs.js +104 -0
  11. package/dist/duckdb/panelSpecs.js.map +1 -0
  12. package/dist/duckdb/projectMetadata.d.ts +43 -0
  13. package/dist/duckdb/projectMetadata.d.ts.map +1 -0
  14. package/dist/duckdb/projectMetadata.js +222 -0
  15. package/dist/duckdb/projectMetadata.js.map +1 -0
  16. package/dist/duckdb/subscriptions.d.ts +45 -0
  17. package/dist/duckdb/subscriptions.d.ts.map +1 -0
  18. package/dist/duckdb/subscriptions.js +202 -0
  19. package/dist/duckdb/subscriptions.js.map +1 -0
  20. package/dist/index.d.ts +18 -4
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +48 -3
  23. package/dist/index.js.map +1 -1
  24. package/dist/insights/anomalies.d.ts +213 -0
  25. package/dist/insights/anomalies.d.ts.map +1 -0
  26. package/dist/insights/anomalies.js +340 -0
  27. package/dist/insights/anomalies.js.map +1 -0
  28. package/dist/insights/baseline.d.ts +51 -0
  29. package/dist/insights/baseline.d.ts.map +1 -0
  30. package/dist/insights/baseline.js +48 -0
  31. package/dist/insights/baseline.js.map +1 -0
  32. package/dist/insights/buckets.d.ts +114 -0
  33. package/dist/insights/buckets.d.ts.map +1 -0
  34. package/dist/insights/buckets.js +220 -0
  35. package/dist/insights/buckets.js.map +1 -0
  36. package/dist/insights/changepoint.d.ts +130 -0
  37. package/dist/insights/changepoint.d.ts.map +1 -0
  38. package/dist/insights/changepoint.js +209 -0
  39. package/dist/insights/changepoint.js.map +1 -0
  40. package/dist/insights/evaluate.d.ts +63 -0
  41. package/dist/insights/evaluate.d.ts.map +1 -0
  42. package/dist/insights/evaluate.js +158 -0
  43. package/dist/insights/evaluate.js.map +1 -0
  44. package/dist/insights/health.d.ts +219 -0
  45. package/dist/insights/health.d.ts.map +1 -0
  46. package/dist/insights/health.js +349 -0
  47. package/dist/insights/health.js.map +1 -0
  48. package/dist/insights/index.d.ts +45 -0
  49. package/dist/insights/index.d.ts.map +1 -0
  50. package/dist/insights/index.js +40 -0
  51. package/dist/insights/index.js.map +1 -0
  52. package/dist/insights/measures.d.ts +222 -0
  53. package/dist/insights/measures.d.ts.map +1 -0
  54. package/dist/insights/measures.js +431 -0
  55. package/dist/insights/measures.js.map +1 -0
  56. package/dist/insights/movers.d.ts +123 -0
  57. package/dist/insights/movers.d.ts.map +1 -0
  58. package/dist/insights/movers.js +166 -0
  59. package/dist/insights/movers.js.map +1 -0
  60. package/dist/insights/significance.d.ts +221 -0
  61. package/dist/insights/significance.d.ts.map +1 -0
  62. package/dist/insights/significance.js +531 -0
  63. package/dist/insights/significance.js.map +1 -0
  64. package/dist/insights/stats.d.ts +104 -0
  65. package/dist/insights/stats.d.ts.map +1 -0
  66. package/dist/insights/stats.js +181 -0
  67. package/dist/insights/stats.js.map +1 -0
  68. package/dist/insights/windows.d.ts +95 -0
  69. package/dist/insights/windows.d.ts.map +1 -0
  70. package/dist/insights/windows.js +97 -0
  71. package/dist/insights/windows.js.map +1 -0
  72. package/dist/metadata.d.ts +178 -1
  73. package/dist/metadata.d.ts.map +1 -1
  74. package/dist/metadata.js +80 -0
  75. package/dist/metadata.js.map +1 -1
  76. package/dist/narrative/build.d.ts +78 -0
  77. package/dist/narrative/build.d.ts.map +1 -0
  78. package/dist/narrative/build.js +437 -0
  79. package/dist/narrative/build.js.map +1 -0
  80. package/dist/narrative/index.d.ts +14 -0
  81. package/dist/narrative/index.d.ts.map +1 -0
  82. package/dist/narrative/index.js +13 -0
  83. package/dist/narrative/index.js.map +1 -0
  84. package/dist/narrative/text.d.ts +25 -0
  85. package/dist/narrative/text.d.ts.map +1 -0
  86. package/dist/narrative/text.js +41 -0
  87. package/dist/narrative/text.js.map +1 -0
  88. package/dist/parity/cases.d.ts +3 -0
  89. package/dist/parity/cases.d.ts.map +1 -1
  90. package/dist/parity/cases.js +387 -7
  91. package/dist/parity/cases.js.map +1 -1
  92. package/dist/parity/compare.d.ts +19 -0
  93. package/dist/parity/compare.d.ts.map +1 -1
  94. package/dist/parity/compare.js +19 -0
  95. package/dist/parity/compare.js.map +1 -1
  96. package/dist/parity/fixtures.d.ts.map +1 -1
  97. package/dist/parity/fixtures.js +25 -0
  98. package/dist/parity/fixtures.js.map +1 -1
  99. package/dist/query/aggregations.d.ts +46 -1
  100. package/dist/query/aggregations.d.ts.map +1 -1
  101. package/dist/query/aggregations.js +86 -0
  102. package/dist/query/aggregations.js.map +1 -1
  103. package/dist/query/clickhouseDialect.d.ts.map +1 -1
  104. package/dist/query/clickhouseDialect.js +5 -1
  105. package/dist/query/clickhouseDialect.js.map +1 -1
  106. package/dist/query/customEventVocabulary.d.ts +46 -0
  107. package/dist/query/customEventVocabulary.d.ts.map +1 -0
  108. package/dist/query/customEventVocabulary.js +127 -0
  109. package/dist/query/customEventVocabulary.js.map +1 -0
  110. package/dist/query/dsl/builders.d.ts +38 -0
  111. package/dist/query/dsl/builders.d.ts.map +1 -0
  112. package/dist/query/dsl/builders.js +35 -0
  113. package/dist/query/dsl/builders.js.map +1 -0
  114. package/dist/query/dsl/compare.d.ts +167 -0
  115. package/dist/query/dsl/compare.d.ts.map +1 -0
  116. package/dist/query/dsl/compare.js +358 -0
  117. package/dist/query/dsl/compare.js.map +1 -0
  118. package/dist/query/dsl/compile.d.ts +89 -0
  119. package/dist/query/dsl/compile.d.ts.map +1 -0
  120. package/dist/query/dsl/compile.js +174 -0
  121. package/dist/query/dsl/compile.js.map +1 -0
  122. package/dist/query/dsl/explain.d.ts +115 -0
  123. package/dist/query/dsl/explain.d.ts.map +1 -0
  124. package/dist/query/dsl/explain.js +143 -0
  125. package/dist/query/dsl/explain.js.map +1 -0
  126. package/dist/query/dsl/generic.d.ts +127 -0
  127. package/dist/query/dsl/generic.d.ts.map +1 -0
  128. package/dist/query/dsl/generic.js +323 -0
  129. package/dist/query/dsl/generic.js.map +1 -0
  130. package/dist/query/dsl/index.d.ts +30 -0
  131. package/dist/query/dsl/index.d.ts.map +1 -0
  132. package/dist/query/dsl/index.js +23 -0
  133. package/dist/query/dsl/index.js.map +1 -0
  134. package/dist/query/dsl/order.d.ts +41 -0
  135. package/dist/query/dsl/order.d.ts.map +1 -0
  136. package/dist/query/dsl/order.js +55 -0
  137. package/dist/query/dsl/order.js.map +1 -0
  138. package/dist/query/dsl/significance.d.ts +130 -0
  139. package/dist/query/dsl/significance.d.ts.map +1 -0
  140. package/dist/query/dsl/significance.js +224 -0
  141. package/dist/query/dsl/significance.js.map +1 -0
  142. package/dist/query/index.d.ts +2 -0
  143. package/dist/query/index.d.ts.map +1 -1
  144. package/dist/query/index.js +8 -0
  145. package/dist/query/index.js.map +1 -1
  146. package/dist/query/summary/index.d.ts +4 -2
  147. package/dist/query/summary/index.d.ts.map +1 -1
  148. package/dist/query/summary/index.js +2 -1
  149. package/dist/query/summary/index.js.map +1 -1
  150. package/dist/query/summary/labels.d.ts +101 -0
  151. package/dist/query/summary/labels.d.ts.map +1 -0
  152. package/dist/query/summary/labels.js +261 -0
  153. package/dist/query/summary/labels.js.map +1 -0
  154. package/dist/query/summary/reading.d.ts.map +1 -1
  155. package/dist/query/summary/reading.js +21 -2
  156. package/dist/query/summary/reading.js.map +1 -1
  157. package/dist/query/summary/schema.d.ts +250 -20
  158. package/dist/query/summary/schema.d.ts.map +1 -1
  159. package/dist/query/summary/schema.js +135 -123
  160. package/dist/query/summary/schema.js.map +1 -1
  161. package/dist/query/summary/summarize.d.ts.map +1 -1
  162. package/dist/query/summary/summarize.js +91 -5
  163. package/dist/query/summary/summarize.js.map +1 -1
  164. package/dist/query/summary/types.d.ts +77 -2
  165. package/dist/query/summary/types.d.ts.map +1 -1
  166. package/dist/query/types.d.ts +47 -1
  167. package/dist/query/types.d.ts.map +1 -1
  168. package/dist/subscriptions.d.ts +208 -0
  169. package/dist/subscriptions.d.ts.map +1 -0
  170. package/dist/subscriptions.js +153 -0
  171. package/dist/subscriptions.js.map +1 -0
  172. package/llms.txt +9 -0
  173. package/package.json +8 -8
@@ -0,0 +1,349 @@
1
+ /**
2
+ * **`scene_health`** — "which scene is in trouble, and why?" (ADR 0051 §4, sketch §D).
3
+ *
4
+ * One number per scene, and — more importantly — the six numbers it was built
5
+ * from. A single score is only useful if it can be taken apart: every factor
6
+ * reports the **metric id** behind it, the **raw** value that metric produced,
7
+ * the **normalised** 0–100 it contributed and the **weight** it carried, so
8
+ * "the lobby is at 38" is always one step away from "because its 5th-percentile
9
+ * FPS is 19 against a project norm of 47".
10
+ *
11
+ * Pure TypeScript over the same bucket series as every other primitive.
12
+ *
13
+ * ## The score is relative, and says so
14
+ *
15
+ * There is no absolute scale on which 60 FPS is "healthy" — a marketing
16
+ * configurator and a six-player VR game do not share one — so every factor is
17
+ * normalised against **the project's own baseline over the previous equal
18
+ * window**, using the same robust centre-and-spread `movers` ranks with
19
+ * (median, and a MAD floored at 1% of the level). A factor exactly at the
20
+ * project norm scores **50**; {@link HEALTH_Z_SPAN} robust deviations better
21
+ * scores 100, the same distance worse scores 0.
22
+ *
23
+ * That makes the score a comparison, not a grade: a project where every scene
24
+ * is equally bad reads 50 across the board. It is the right shape for the
25
+ * question actually asked ("*which* scene should I look at this week"), and the
26
+ * registry entry's caveats say plainly that it is not a quality bar.
27
+ *
28
+ * ## Factors are declared, not inferred
29
+ *
30
+ * {@link HEALTH_FACTORS} is a fixed, ordered catalog. Each entry names the
31
+ * metric it reads, whether it is a level or a rate (and which catalog series
32
+ * supply the numerator and denominator), which direction is good, and its
33
+ * default weight. Nothing here is derived from a request, so the fan-out is a
34
+ * compile-time constant and a caller can only change the **weights** — never
35
+ * which series are read.
36
+ */
37
+ import { BUCKET_SECONDS } from "./measures.js";
38
+ import { referenceSpread } from "./movers.js";
39
+ import { median, round } from "./stats.js";
40
+ import { DAY_MS, floorToBucket } from "./windows.js";
41
+ /**
42
+ * The six factors, in the order they are reported.
43
+ *
44
+ * The weights are a judgement, and they are declared here (and surfaced in the
45
+ * registry entry, so they appear in `capabilities` and in the generated tool
46
+ * catalog) precisely so that judgement is arguable rather than hidden: errors
47
+ * and raw smoothness dominate, frustration signals matter, and exploration is a
48
+ * tiebreak. A project that disagrees passes `weights`.
49
+ */
50
+ export const HEALTH_FACTORS = [
51
+ {
52
+ id: "perf_stability",
53
+ metric: "perf_summary",
54
+ numerator: "p05",
55
+ direction: "up",
56
+ weight: 0.25,
57
+ unit: "FPS",
58
+ note: "The 5th-percentile FPS of the scene's sampled frames — how bad it gets, not how good it " +
59
+ "usually is. A scene whose median is fine and whose p05 is not is a stuttering scene.",
60
+ },
61
+ {
62
+ id: "jank_rate",
63
+ metric: "jank_rate",
64
+ numerator: "numerator",
65
+ denominator: "denominator",
66
+ direction: "down",
67
+ weight: 0.2,
68
+ unit: "long frames per sampled window",
69
+ note: "Long frames per sampled perf window, pooled across sessions. The metric's own endpoint " +
70
+ "reports a per-session median instead, so the two agree in direction but not in value.",
71
+ },
72
+ {
73
+ id: "error_rate",
74
+ metric: "error_heatmap",
75
+ denominator: "denominator",
76
+ direction: "down",
77
+ weight: 0.25,
78
+ unit: "errors per session",
79
+ note: "Runtime errors and engine diagnostics per session started. Divided by sessions because an " +
80
+ "error count with no traffic behind it says nothing.",
81
+ },
82
+ {
83
+ id: "dead_click_rate",
84
+ metric: "dead_clicks",
85
+ denominator: "denominator",
86
+ direction: "down",
87
+ weight: 0.15,
88
+ unit: "share of clicks",
89
+ note: "The share of clicks that hit no mesh at all — the 3D discoverability signal. Rage-click " +
90
+ "bursts are not folded in: they are defined by the gap between consecutive clicks and have " +
91
+ "no portable per-bucket form.",
92
+ },
93
+ {
94
+ id: "coverage",
95
+ metric: "scene_coverage",
96
+ denominator: "denominator",
97
+ direction: "up",
98
+ weight: 0.1,
99
+ unit: "camera samples per session",
100
+ note: "Positioned camera samples per session — how much of the scene visitors actually move " +
101
+ "through. A true voxel-coverage percentage needs the registered scene bounds and has no " +
102
+ "portable per-bucket form, so this is the portable proxy, read only against the project's " +
103
+ "own baseline.",
104
+ },
105
+ {
106
+ id: "xr_abandonment",
107
+ metric: "xr_abandonment",
108
+ numerator: "numerator",
109
+ denominator: "denominator",
110
+ direction: "up",
111
+ weight: 0.05,
112
+ unit: "XR interactions per XR session",
113
+ note: "Interactions per headset session. Absent (not zero) in a project with no XR traffic: a " +
114
+ "scene nobody visited in VR is not an unhealthy VR scene.",
115
+ },
116
+ ];
117
+ /** Every factor id, for validating a `weights` override. */
118
+ export const HEALTH_FACTOR_IDS = HEALTH_FACTORS.map((factor) => factor.id);
119
+ /** The declared default weights, as the registry entry publishes them. */
120
+ export const HEALTH_DEFAULT_WEIGHTS = Object.fromEntries(HEALTH_FACTORS.map((factor) => [factor.id, factor.weight]));
121
+ /**
122
+ * How many robust deviations from the project norm span half the scale.
123
+ *
124
+ * At the norm a factor scores 50; `HEALTH_Z_SPAN` deviations better scores 100
125
+ * and the same distance worse scores 0. Four is chosen so that the everyday
126
+ * range of a metric occupies the middle of the scale rather than saturating it
127
+ * — a scene one MAD off the norm reads 62 or 38, which is a nudge, and it takes
128
+ * a genuinely unusual reading to bottom out.
129
+ */
130
+ export const HEALTH_Z_SPAN = 4;
131
+ /** Decimals a health number is rounded to before it leaves the API. */
132
+ export const HEALTH_PRECISION = 6;
133
+ /**
134
+ * Sessions below which a scene's score is not worth reading.
135
+ *
136
+ * Not a filter: the row is returned with its `sampleSize` so a caller can see
137
+ * why, exactly as `movers` keeps its sub-`minSample` rows.
138
+ */
139
+ export const HEALTH_MIN_SESSIONS = 20;
140
+ /** How many scenes one request may score when the caller names none. */
141
+ export const HEALTH_DEFAULT_SCENES = 5;
142
+ /** The hard cap on scenes per request — the fan-out is linear in this. */
143
+ export const HEALTH_MAX_SCENES = 10;
144
+ /** Default window, in days. */
145
+ export const HEALTH_DEFAULT_WINDOW_DAYS = 7;
146
+ /**
147
+ * Resolve the window a health score is computed over, and the project baseline
148
+ * window immediately before it.
149
+ *
150
+ * **This is the one insight primitive whose window includes the bucket in
151
+ * progress**, and the difference is deliberate. `baseline` and `movers` floor
152
+ * `until` down to the last complete bucket because they report *counts*, and a
153
+ * third of a day of traffic compared against whole days reads as a collapse
154
+ * every morning. Every health factor is a **rate or a percentile** — errors per
155
+ * session, long frames per sampled window, the 5th-percentile FPS — and none of
156
+ * those is distorted by a partial bucket: half a day of data gives half the
157
+ * numerator *and* half the denominator.
158
+ *
159
+ * Flooring here would instead make the score answer about **yesterday**, which
160
+ * is the wrong answer to 'which scene is in trouble' and, on a dashboard whose
161
+ * range is the last hour, would show an empty tile all day.
162
+ *
163
+ * The baseline is the equal window immediately before — the same 'previous
164
+ * equal window' reference `movers` uses, so a health score and a movers list
165
+ * are read against the same past.
166
+ */
167
+ export function resolveHealthWindows(opts) {
168
+ const width = BUCKET_SECONDS[opts.bucket] * 1000;
169
+ // Round `until` **up**, so the bucket the request lands in is whole.
170
+ const until = Math.ceil((opts.until ?? opts.now) / width) * width;
171
+ const days = opts.windowDays ?? HEALTH_DEFAULT_WINDOW_DAYS;
172
+ const sinceRaw = floorToBucket(opts.since ?? until - days * DAY_MS, opts.bucket);
173
+ const since = sinceRaw >= until ? until - width : sinceRaw;
174
+ const span = until - since;
175
+ return { range: { since, until }, baseline: { since: since - span, until: since } };
176
+ }
177
+ /** The sum of a series' finite bucket values; `null` when it carried none. */
178
+ function total(rows) {
179
+ if (rows == null)
180
+ return null;
181
+ let sum = 0;
182
+ let seen = 0;
183
+ for (const row of rows) {
184
+ if (row.value != null && Number.isFinite(row.value)) {
185
+ sum += row.value;
186
+ seen += 1;
187
+ }
188
+ }
189
+ return seen === 0 ? null : sum;
190
+ }
191
+ /** The mean of a series' finite bucket values; `null` when it carried none. */
192
+ function levelOf(rows) {
193
+ if (rows == null)
194
+ return null;
195
+ const sum = total(rows);
196
+ if (sum == null)
197
+ return null;
198
+ let seen = 0;
199
+ for (const row of rows)
200
+ if (row.value != null && Number.isFinite(row.value))
201
+ seen += 1;
202
+ return seen === 0 ? null : sum / seen;
203
+ }
204
+ /**
205
+ * The factor's value over a window.
206
+ *
207
+ * A rate is the **window ratio** — total numerator over total denominator, not
208
+ * the mean of the per-bucket ratios. A day with three sessions and a day with
209
+ * three hundred must not weigh the same in "errors per session", and averaging
210
+ * the daily ratios is exactly the mistake that makes them.
211
+ */
212
+ function windowValue(series, spec) {
213
+ if (spec.denominator == null)
214
+ return levelOf(series.numerator);
215
+ const numerator = total(series.numerator) ?? 0;
216
+ const denominator = total(series.denominator);
217
+ if (denominator == null || denominator <= 0)
218
+ return null;
219
+ return numerator / denominator;
220
+ }
221
+ /**
222
+ * The per-bucket series the baseline's centre and spread are taken over.
223
+ *
224
+ * For a rate that is the bucket-by-bucket ratio, so the spread reflects how
225
+ * much the *rate* normally moves rather than how much traffic does. Buckets
226
+ * whose denominator is zero are dropped: a day with no sessions has no error
227
+ * rate, and counting it as zero would drag every baseline down.
228
+ */
229
+ function baselineSeries(series, spec) {
230
+ if (spec.denominator == null) {
231
+ return series.numerator
232
+ .map((row) => row.value)
233
+ .filter((value) => value != null && Number.isFinite(value));
234
+ }
235
+ const numerators = new Map();
236
+ for (const row of series.numerator) {
237
+ if (row.value != null && Number.isFinite(row.value))
238
+ numerators.set(row.bucket, row.value);
239
+ }
240
+ const out = [];
241
+ for (const row of series.denominator ?? []) {
242
+ if (row.value == null || !Number.isFinite(row.value) || row.value <= 0)
243
+ continue;
244
+ out.push((numerators.get(row.bucket) ?? 0) / row.value);
245
+ }
246
+ return out;
247
+ }
248
+ /**
249
+ * Map a raw value onto 0–100 against a baseline centre and spread.
250
+ *
251
+ * Linear in the robust z and clamped, rather than a logistic or a percentile
252
+ * rank: a reader has to be able to do the arithmetic backwards from the row.
253
+ * `null` when the baseline carried nothing to compare against — an unscored
254
+ * factor, never a default of 50, because "we have no history for this" and
255
+ * "this is exactly normal" are different statements.
256
+ */
257
+ export function normaliseFactor(raw, baseline, direction) {
258
+ const centre = median(baseline);
259
+ if (raw == null || centre == null)
260
+ return { score: null, centre };
261
+ const spread = referenceSpread(baseline);
262
+ if (spread == null || !(spread > 0)) {
263
+ // A baseline with no spread at all (one bucket, or a perfectly flat one)
264
+ // cannot say how unusual anything is. Equal to it is normal; anything else
265
+ // is unquantifiable, so the factor reports the direction and nothing more.
266
+ if (raw === centre)
267
+ return { score: 50, centre };
268
+ const better = direction === "up" ? raw > centre : raw < centre;
269
+ return { score: better ? 100 : 0, centre };
270
+ }
271
+ const z = (raw - centre) / spread;
272
+ const oriented = direction === "up" ? z : -z;
273
+ const score = 50 + (50 * oriented) / HEALTH_Z_SPAN;
274
+ return { score: Math.min(100, Math.max(0, score)), centre };
275
+ }
276
+ /**
277
+ * Resolve the weights a request scores with.
278
+ *
279
+ * An override replaces a factor's weight; factors the override does not mention
280
+ * keep their declared weight, so `weights={"error_rate":0.6}` means "I care
281
+ * much more about errors" rather than "score errors only". Unknown ids are
282
+ * rejected at the route, not silently ignored.
283
+ */
284
+ export function resolveWeights(override) {
285
+ const weights = { ...HEALTH_DEFAULT_WEIGHTS };
286
+ for (const [id, value] of Object.entries(override ?? {})) {
287
+ if (!Object.prototype.hasOwnProperty.call(weights, id))
288
+ continue;
289
+ if (Number.isFinite(value) && value >= 0)
290
+ weights[id] = value;
291
+ }
292
+ return weights;
293
+ }
294
+ /**
295
+ * Score one scene.
296
+ *
297
+ * `inputs` is keyed by factor id; a factor with no entry (a project with no XR
298
+ * traffic, a scene with no perf samples) is reported with `score: null` and
299
+ * excluded from the weighted mean rather than dropped from the row — the reason
300
+ * a score is missing a factor has to be visible in the answer.
301
+ */
302
+ export function computeSceneHealth(opts) {
303
+ const weights = resolveWeights(opts.weights);
304
+ const factors = [];
305
+ let weighted = 0;
306
+ let weightTotal = 0;
307
+ for (const spec of HEALTH_FACTORS) {
308
+ const weight = weights[spec.id] ?? 0;
309
+ const input = opts.inputs[spec.id];
310
+ const raw = input == null ? null : windowValue(input.current, spec);
311
+ const baseline = input == null ? [] : baselineSeries(input.baseline, spec);
312
+ const { score, centre } = normaliseFactor(raw, baseline, spec.direction);
313
+ if (score != null && weight > 0) {
314
+ weighted += weight * score;
315
+ weightTotal += weight;
316
+ }
317
+ factors.push({
318
+ id: spec.id,
319
+ metric: spec.metric,
320
+ raw: round(raw, HEALTH_PRECISION),
321
+ baseline: round(centre, HEALTH_PRECISION),
322
+ score: round(score, HEALTH_PRECISION),
323
+ weight,
324
+ unit: spec.unit,
325
+ note: score == null
326
+ ? `${spec.note} Not scored: ${raw == null ? "the scene produced no value for it in this window" : "the project has no baseline for it in the preceding window"}.`
327
+ : spec.note,
328
+ });
329
+ }
330
+ return {
331
+ scene: opts.scene,
332
+ score: weightTotal > 0 ? round(weighted / weightTotal, HEALTH_PRECISION) : null,
333
+ factors,
334
+ sampleSize: opts.sampleSize,
335
+ since: opts.since,
336
+ until: opts.until,
337
+ };
338
+ }
339
+ /** Least healthy first, then by scene id so the order is a function of the rows. */
340
+ export function rankSceneHealth(rows) {
341
+ return [...rows].sort((a, b) => {
342
+ const left = a.score ?? Number.POSITIVE_INFINITY;
343
+ const right = b.score ?? Number.POSITIVE_INFINITY;
344
+ if (left !== right)
345
+ return left - right;
346
+ return a.scene < b.scene ? -1 : a.scene > b.scene ? 1 : 0;
347
+ });
348
+ }
349
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/insights/health.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * **Insight primitives** — `baseline` and `movers` (ADR 0051 §4, sketch §D).
3
+ *
4
+ * The shape of this directory is the design:
5
+ *
6
+ * - `measures.ts` declares, per comparable registry metric, how its primary
7
+ * column is reproduced as a per-bucket series;
8
+ * - `buckets.ts` renders exactly one generic, dialect-agnostic query from that
9
+ * declaration (parity-tested on DuckDB, ClickHouse, Postgres and SQL Server);
10
+ * - `stats.ts`, `baseline.ts`, `movers.ts` and `windows.ts` are **pure
11
+ * TypeScript** over the rows that query returns — no SQL, no I/O, no dialect.
12
+ *
13
+ * Statistics never run in SQL. Five engines disagree about `quantile`, `median`
14
+ * and `stddev`, and an insight that changes when a self-hoster switches store is
15
+ * not an insight. `anomalies`, `significance` and `scene_health` (ADR 0051 §4)
16
+ * are designed to slot in here the same way: a new pure module over the same
17
+ * bucket series, with no new per-dialect SQL.
18
+ */
19
+ export { BUCKETABLE_METRIC_IDS, BUCKET_MEASURES, BUCKET_SECONDS, MOVERS_DEFAULT_METRICS, MOVERS_MAX_METRICS, bucketMeasureFor, isBucketGrain, isBucketableMetric, } from "./measures.js";
20
+ export type { BucketAggregate, BucketGrain, BucketMeasure, BucketPredicate, BucketRollup, BucketValueColumn, } from "./measures.js";
21
+ export { buildMetricBuckets, toMetricBucketRows } from "./buckets.js";
22
+ export type { MetricBucketOptions, MetricBucketRow } from "./buckets.js";
23
+ export { evaluateBucketMeasure } from "./evaluate.js";
24
+ export type { BucketEventLike, EvaluateBucketOptions } from "./evaluate.js";
25
+ export { BASELINE_PRECISION, computeBaseline } from "./baseline.js";
26
+ export type { BaselineRow } from "./baseline.js";
27
+ export { MIN_SPREAD_FRACTION, MOVERS_PRECISION, computeMover, rankMovers, referenceSpread, rollupWindow, } from "./movers.js";
28
+ export type { MoverInput, MoverRow } from "./movers.js";
29
+ export { MAD_TO_SIGMA, ROBUST_Z_EPSILON, leastSquaresSlope, mean, median, medianAbsoluteDeviation, quantile, relativeChange, robustZ, round, sum, trendOf, } from "./stats.js";
30
+ export { DAY_MS, DEFAULT_BASELINE_WINDOW_DAYS, DEFAULT_MOVERS_RANGE_DAYS, MAX_BASELINE_WINDOW_DAYS, floorToBucket, inWindow, resolveBaselineWindow, resolveMoversWindows, spanningWindow, } from "./windows.js";
31
+ export type { ResolvedWindow } from "./windows.js";
32
+ export { ANOMALY_MAX_CONTRIBUTOR_SCANS, ANOMALY_MIN_TRAILING, ANOMALY_PRECISION, ANOMALY_TRAILING_BUCKETS, DEFAULT_ANOMALY_SENSITIVITY, MAX_ANOMALY_SENSITIVITY, MIN_ANOMALY_SENSITIVITY, attributeContributor, clampSensitivity, contributorDimensionFor, contributorWindows, detectAnomalies, inContributorWindow, } from "./anomalies.js";
33
+ export type { AnomalyContributor, AnomalyKind, AnomalyRow, AnomalyWindow, DetectAnomaliesOptions, } from "./anomalies.js";
34
+ export { CUSUM_DECISION_FACTOR, CUSUM_SLACK, cusumChangePoints, rollingRobustStats, } from "./changepoint.js";
35
+ export type { ChangePoint, RollingRobustPoint } from "./changepoint.js";
36
+ export { byBucketThenDimension } from "./buckets.js";
37
+ export { BUCKET_SPLIT_COLUMNS, isBucketSplitDimension } from "./measures.js";
38
+ export type { BucketSplitDimension } from "./measures.js";
39
+ export { BUCKET_MEASURE_VARIANTS, bucketVariantFor, resolveBucketMeasure } from "./measures.js";
40
+ export type { BucketVariant } from "./measures.js";
41
+ export { SIGNIFICANCE_ALPHA, SIGNIFICANCE_PRECISION, Z_80, binomialCdf, computeSignificance, newcombeDifferenceInterval, normalCdf, normalTwoSidedP, poissonRateTest, regularizedIncompleteBeta, studentTCritical, studentTwoSidedP, twoProportionTest, welchTest, wilsonBounds, } from "./significance.js";
42
+ export type { SignificanceArm, SignificanceInput, SignificanceRow, SignificanceTest, } from "./significance.js";
43
+ export { HEALTH_DEFAULT_SCENES, HEALTH_DEFAULT_WEIGHTS, HEALTH_DEFAULT_WINDOW_DAYS, HEALTH_FACTORS, HEALTH_FACTOR_IDS, HEALTH_MAX_SCENES, HEALTH_MIN_SESSIONS, HEALTH_PRECISION, HEALTH_Z_SPAN, computeSceneHealth, normaliseFactor, rankSceneHealth, resolveHealthWindows, resolveWeights, } from "./health.js";
44
+ export type { HealthFactorInput, HealthFactorRow, HealthFactorSpec, HealthSeries, SceneHealthRow, } from "./health.js";
45
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/insights/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * **Insight primitives** — `baseline` and `movers` (ADR 0051 §4, sketch §D).
3
+ *
4
+ * The shape of this directory is the design:
5
+ *
6
+ * - `measures.ts` declares, per comparable registry metric, how its primary
7
+ * column is reproduced as a per-bucket series;
8
+ * - `buckets.ts` renders exactly one generic, dialect-agnostic query from that
9
+ * declaration (parity-tested on DuckDB, ClickHouse, Postgres and SQL Server);
10
+ * - `stats.ts`, `baseline.ts`, `movers.ts` and `windows.ts` are **pure
11
+ * TypeScript** over the rows that query returns — no SQL, no I/O, no dialect.
12
+ *
13
+ * Statistics never run in SQL. Five engines disagree about `quantile`, `median`
14
+ * and `stddev`, and an insight that changes when a self-hoster switches store is
15
+ * not an insight. `anomalies`, `significance` and `scene_health` (ADR 0051 §4)
16
+ * are designed to slot in here the same way: a new pure module over the same
17
+ * bucket series, with no new per-dialect SQL.
18
+ */
19
+ export { BUCKETABLE_METRIC_IDS, BUCKET_MEASURES, BUCKET_SECONDS, MOVERS_DEFAULT_METRICS, MOVERS_MAX_METRICS, bucketMeasureFor, isBucketGrain, isBucketableMetric, } from "./measures.js";
20
+ export { buildMetricBuckets, toMetricBucketRows } from "./buckets.js";
21
+ export { evaluateBucketMeasure } from "./evaluate.js";
22
+ export { BASELINE_PRECISION, computeBaseline } from "./baseline.js";
23
+ export { MIN_SPREAD_FRACTION, MOVERS_PRECISION, computeMover, rankMovers, referenceSpread, rollupWindow, } from "./movers.js";
24
+ export { MAD_TO_SIGMA, ROBUST_Z_EPSILON, leastSquaresSlope, mean, median, medianAbsoluteDeviation, quantile, relativeChange, robustZ, round, sum, trendOf, } from "./stats.js";
25
+ export { DAY_MS, DEFAULT_BASELINE_WINDOW_DAYS, DEFAULT_MOVERS_RANGE_DAYS, MAX_BASELINE_WINDOW_DAYS, floorToBucket, inWindow, resolveBaselineWindow, resolveMoversWindows, spanningWindow, } from "./windows.js";
26
+ // --- anomalies (#306) ------------------------------------------------------
27
+ // The third insight primitive: which *bucket* was abnormal, in what way, and
28
+ // which dimension value inside the metric accounts for it. Two pure modules over
29
+ // the same bucket series — `changepoint.ts` holds the generic algorithms,
30
+ // `anomalies.ts` the primitive itself. The only new store-facing surface is the
31
+ // optional `groupBy` on `buildMetricBuckets`, exported above.
32
+ export { ANOMALY_MAX_CONTRIBUTOR_SCANS, ANOMALY_MIN_TRAILING, ANOMALY_PRECISION, ANOMALY_TRAILING_BUCKETS, DEFAULT_ANOMALY_SENSITIVITY, MAX_ANOMALY_SENSITIVITY, MIN_ANOMALY_SENSITIVITY, attributeContributor, clampSensitivity, contributorDimensionFor, contributorWindows, detectAnomalies, inContributorWindow, } from "./anomalies.js";
33
+ export { CUSUM_DECISION_FACTOR, CUSUM_SLACK, cusumChangePoints, rollingRobustStats, } from "./changepoint.js";
34
+ export { byBucketThenDimension } from "./buckets.js";
35
+ export { BUCKET_SPLIT_COLUMNS, isBucketSplitDimension } from "./measures.js";
36
+ // --- significance / scene health (#307) ------------------------------------
37
+ export { BUCKET_MEASURE_VARIANTS, bucketVariantFor, resolveBucketMeasure } from "./measures.js";
38
+ export { SIGNIFICANCE_ALPHA, SIGNIFICANCE_PRECISION, Z_80, binomialCdf, computeSignificance, newcombeDifferenceInterval, normalCdf, normalTwoSidedP, poissonRateTest, regularizedIncompleteBeta, studentTCritical, studentTwoSidedP, twoProportionTest, welchTest, wilsonBounds, } from "./significance.js";
39
+ export { HEALTH_DEFAULT_SCENES, HEALTH_DEFAULT_WEIGHTS, HEALTH_DEFAULT_WINDOW_DAYS, HEALTH_FACTORS, HEALTH_FACTOR_IDS, HEALTH_MAX_SCENES, HEALTH_MIN_SESSIONS, HEALTH_PRECISION, HEALTH_Z_SPAN, computeSceneHealth, normaliseFactor, rankSceneHealth, resolveHealthWindows, resolveWeights, } from "./health.js";
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/insights/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAUvB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGpE,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,IAAI,EACJ,MAAM,EACN,uBAAuB,EACvB,QAAQ,EACR,cAAc,EACd,OAAO,EACP,KAAK,EACL,GAAG,EACH,OAAO,GACR,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,MAAM,EACN,4BAA4B,EAC5B,yBAAyB,EACzB,wBAAwB,EACxB,aAAa,EACb,QAAQ,EACR,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,GACf,MAAM,cAAc,CAAC;AAGtB,8EAA8E;AAC9E,6EAA6E;AAC7E,iFAAiF;AACjF,0EAA0E;AAC1E,gFAAgF;AAChF,8DAA8D;AAC9D,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,iBAAiB,EACjB,wBAAwB,EACxB,2BAA2B,EAC3B,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AASxB,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAE7E,8EAA8E;AAE9E,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAGhG,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,IAAI,EACJ,WAAW,EACX,mBAAmB,EACnB,0BAA0B,EAC1B,SAAS,EACT,eAAe,EACf,eAAe,EACf,yBAAyB,EACzB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EACT,YAAY,GACb,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,0BAA0B,EAC1B,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,cAAc,GACf,MAAM,aAAa,CAAC"}