@obsunified/collector 2.0.1

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 (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/dist/auth.d.ts +2 -0
  4. package/dist/auth.js +9 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/chunk-UGWDOGGH.js +319 -0
  7. package/dist/chunk-UGWDOGGH.js.map +1 -0
  8. package/dist/chunk-XKQTVIRE.js +671 -0
  9. package/dist/chunk-XKQTVIRE.js.map +1 -0
  10. package/dist/index.d.ts +1197 -0
  11. package/dist/index.js +17707 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/ingest-auth-HFVO1zIp.d.ts +165 -0
  14. package/dist/openai-FJYOQ7WO.js +384 -0
  15. package/dist/openai-FJYOQ7WO.js.map +1 -0
  16. package/package.json +73 -0
  17. package/src/ai-parity/ai-parity.acceptance.test.ts +449 -0
  18. package/src/analyses/derive.ts +642 -0
  19. package/src/analyses/index.ts +50 -0
  20. package/src/analyses/investigations.ts +277 -0
  21. package/src/analyses/tier0.ts +568 -0
  22. package/src/auth/dashboard-auth.ts +269 -0
  23. package/src/auth/ingest-auth.ts +147 -0
  24. package/src/durable-objects/tail-hub.test.ts +32 -0
  25. package/src/durable-objects/tail-hub.ts +158 -0
  26. package/src/framework/collector.ts +467 -0
  27. package/src/framework/env.ts +90 -0
  28. package/src/framework/logger.ts +81 -0
  29. package/src/index.ts +211 -0
  30. package/src/lib/action-aggregates.ts +1031 -0
  31. package/src/lib/action-identity.ts +106 -0
  32. package/src/lib/actions-store.ts +397 -0
  33. package/src/lib/ai-pricing.ts +80 -0
  34. package/src/lib/ai-store/calls.ts +148 -0
  35. package/src/lib/ai-store/evaluations.test.ts +47 -0
  36. package/src/lib/ai-store/evaluations.ts +125 -0
  37. package/src/lib/ai-store/helpers.ts +37 -0
  38. package/src/lib/ai-store/retention.ts +17 -0
  39. package/src/lib/ai-store/sessions.ts +277 -0
  40. package/src/lib/ai-store/spans.ts +109 -0
  41. package/src/lib/ai-store/types.ts +112 -0
  42. package/src/lib/ai-store.ts +76 -0
  43. package/src/lib/alerts-store.ts +458 -0
  44. package/src/lib/analyses-runner.ts +439 -0
  45. package/src/lib/analyses-store.test.ts +243 -0
  46. package/src/lib/analyses-store.ts +454 -0
  47. package/src/lib/ask.test.ts +302 -0
  48. package/src/lib/ask.ts +390 -0
  49. package/src/lib/blob-store-s3.ts +213 -0
  50. package/src/lib/blob-store.ts +366 -0
  51. package/src/lib/eval-cases-store.test.ts +171 -0
  52. package/src/lib/eval-cases-store.ts +1033 -0
  53. package/src/lib/evidence-references.test.ts +10 -0
  54. package/src/lib/evidence-references.ts +684 -0
  55. package/src/lib/hash.ts +26 -0
  56. package/src/lib/identity-index/action-lookups.ts +805 -0
  57. package/src/lib/identity-index/constants.ts +1 -0
  58. package/src/lib/identity-index/key-lookups.ts +199 -0
  59. package/src/lib/identity-index/mappers.ts +349 -0
  60. package/src/lib/identity-index/types.ts +196 -0
  61. package/src/lib/identity-index/user-lookup.ts +137 -0
  62. package/src/lib/identity-index.test.ts +624 -0
  63. package/src/lib/identity-index.ts +85 -0
  64. package/src/lib/json.ts +39 -0
  65. package/src/lib/llm.ts +263 -0
  66. package/src/lib/logs-store.ts +188 -0
  67. package/src/lib/metrics-store.test.ts +189 -0
  68. package/src/lib/metrics-store.ts +312 -0
  69. package/src/lib/narrate-gate.test.ts +260 -0
  70. package/src/lib/narrate-gate.ts +253 -0
  71. package/src/lib/openai.test.ts +300 -0
  72. package/src/lib/openai.ts +512 -0
  73. package/src/lib/otlp.ts +212 -0
  74. package/src/lib/parse-pprof.test.ts +64 -0
  75. package/src/lib/parse-pprof.ts +184 -0
  76. package/src/lib/payload-capture.ts +22 -0
  77. package/src/lib/projects-store.ts +207 -0
  78. package/src/lib/propagation-metric.test.ts +196 -0
  79. package/src/lib/propagation-metric.ts +175 -0
  80. package/src/lib/sql-db-dialect.test.ts +22 -0
  81. package/src/lib/sql-db-postgres.ts +229 -0
  82. package/src/lib/sql-db.ts +166 -0
  83. package/src/lib/stage6.test.ts +216 -0
  84. package/src/lib/store/export.ts +65 -0
  85. package/src/lib/store/helpers.ts +367 -0
  86. package/src/lib/store/ingest.ts +68 -0
  87. package/src/lib/store/issues.ts +158 -0
  88. package/src/lib/store/overview.ts +127 -0
  89. package/src/lib/store/retention.ts +10 -0
  90. package/src/lib/store/service-map.ts +384 -0
  91. package/src/lib/store/trace-detail.bench.ts +76 -0
  92. package/src/lib/store/trace-detail.test.ts +410 -0
  93. package/src/lib/store/trace-detail.ts +427 -0
  94. package/src/lib/store/trace-queries.test.ts +29 -0
  95. package/src/lib/store/trace-queries.ts +89 -0
  96. package/src/lib/store-overview.test.ts +77 -0
  97. package/src/lib/store.ts +96 -0
  98. package/src/lib/tail-publisher.ts +64 -0
  99. package/src/lib/test-utils/mem-sql-db.test.ts +93 -0
  100. package/src/lib/test-utils/mem-sql-db.ts +86 -0
  101. package/src/lib/ua-parser.ts +61 -0
  102. package/src/lib/usage-store.ts +567 -0
  103. package/src/migrations/001_telemetry_spans.sql +28 -0
  104. package/src/migrations/002_usage_events.sql +24 -0
  105. package/src/migrations/003_usage_analytics_columns.sql +13 -0
  106. package/src/migrations/004_logs.sql +19 -0
  107. package/src/migrations/005_ai_calls.sql +25 -0
  108. package/src/migrations/006_user_profiles.sql +11 -0
  109. package/src/migrations/007_session_replay_metadata.sql +10 -0
  110. package/src/migrations/008_session_replay_storage_bytes.sql +1 -0
  111. package/src/migrations/009_projects_and_keys.sql +22 -0
  112. package/src/migrations/010_signal_project_id.sql +15 -0
  113. package/src/migrations/011_default_project_seed.sql +3 -0
  114. package/src/migrations/012_alert_rules.sql +17 -0
  115. package/src/migrations/013_alert_evaluations.sql +13 -0
  116. package/src/migrations/014_alert_state.sql +9 -0
  117. package/src/migrations/015_metrics.sql +58 -0
  118. package/src/migrations/016_metrics_exp_histogram_summary.sql +5 -0
  119. package/src/migrations/017_span_dropped_counts.sql +11 -0
  120. package/src/migrations/018_log_flags.sql +8 -0
  121. package/src/migrations/019_ai_span_payloads.sql +30 -0
  122. package/src/migrations/020_ai_span_evaluations.sql +33 -0
  123. package/src/migrations/021_ai_span_session.sql +13 -0
  124. package/src/migrations/022_span_log_session_id.sql +13 -0
  125. package/src/migrations/023_service_map_indexes.sql +32 -0
  126. package/src/migrations/024_analysis_results.sql +49 -0
  127. package/src/migrations/025_stage6_autopinning_alerts.sql +38 -0
  128. package/src/migrations/026_obs_dashboard_project_seed.sql +18 -0
  129. package/src/migrations/027_identity_propagation.sql +50 -0
  130. package/src/migrations/028_profile_blobs.sql +57 -0
  131. package/src/migrations/029_telemetry_sdk_name.sql +20 -0
  132. package/src/migrations/030_analysis_claim.sql +18 -0
  133. package/src/migrations/031_agent_action_graph.sql +154 -0
  134. package/src/migrations/032_payload_capture_flags.sql +8 -0
  135. package/src/migrations/033_eval_cases.sql +52 -0
  136. package/src/migrations/034_eval_case_results.sql +23 -0
  137. package/src/migrations/035_metric_exemplars.sql +33 -0
  138. package/src/migrations/036_trace_instrumentation_gaps.sql +19 -0
  139. package/src/migrations/037_eval_runs.sql +37 -0
  140. package/src/migrations/038_tool_call_evidence.sql +11 -0
  141. package/src/migrations/039_drop_trace_instrumentation_gaps.sql +11 -0
  142. package/src/migrations/040_evidence_retrieval_refs.sql +50 -0
  143. package/src/migrations-postgres/001_telemetry_spans.sql +33 -0
  144. package/src/migrations-postgres/002_usage_events.sql +24 -0
  145. package/src/migrations-postgres/003_usage_analytics_columns.sql +13 -0
  146. package/src/migrations-postgres/004_logs.sql +19 -0
  147. package/src/migrations-postgres/005_ai_calls.sql +25 -0
  148. package/src/migrations-postgres/006_user_profiles.sql +11 -0
  149. package/src/migrations-postgres/007_session_replay_metadata.sql +10 -0
  150. package/src/migrations-postgres/008_session_replay_storage_bytes.sql +2 -0
  151. package/src/migrations-postgres/009_projects_and_keys.sql +25 -0
  152. package/src/migrations-postgres/010_signal_project_id.sql +18 -0
  153. package/src/migrations-postgres/011_default_project_seed.sql +5 -0
  154. package/src/migrations-postgres/012_alert_rules.sql +17 -0
  155. package/src/migrations-postgres/013_alert_evaluations.sql +13 -0
  156. package/src/migrations-postgres/014_alert_state.sql +9 -0
  157. package/src/migrations-postgres/015_metrics.sql +47 -0
  158. package/src/migrations-postgres/016_metrics_exp_histogram_summary.sql +1 -0
  159. package/src/migrations-postgres/017_span_dropped_counts.sql +4 -0
  160. package/src/migrations-postgres/018_log_flags.sql +2 -0
  161. package/src/migrations-postgres/019_ai_span_payloads.sql +18 -0
  162. package/src/migrations-postgres/020_ai_span_evaluations.sql +21 -0
  163. package/src/migrations-postgres/021_ai_span_session.sql +7 -0
  164. package/src/migrations-postgres/022_span_log_session_id.sql +7 -0
  165. package/src/migrations-postgres/023_service_map_indexes.sql +9 -0
  166. package/src/migrations-postgres/024_analysis_results.sql +41 -0
  167. package/src/migrations-postgres/025_stage6_autopinning_alerts.sql +17 -0
  168. package/src/migrations-postgres/026_obs_dashboard_project_seed.sql +14 -0
  169. package/src/migrations-postgres/027_identity_propagation.sql +27 -0
  170. package/src/migrations-postgres/028_profile_blobs.sql +48 -0
  171. package/src/migrations-postgres/029_telemetry_sdk_name.sql +5 -0
  172. package/src/migrations-postgres/030_analysis_claim.sql +5 -0
  173. package/src/migrations-postgres/031_agent_action_graph.sql +190 -0
  174. package/src/migrations-postgres/032_payload_capture_flags.sql +8 -0
  175. package/src/migrations-postgres/033_eval_cases.sql +55 -0
  176. package/src/migrations-postgres/034_eval_case_results.sql +26 -0
  177. package/src/migrations-postgres/035_metric_exemplars.sql +31 -0
  178. package/src/migrations-postgres/036_trace_instrumentation_gaps.sql +19 -0
  179. package/src/migrations-postgres/037_eval_runs.sql +40 -0
  180. package/src/migrations-postgres/038_tool_call_evidence.sql +11 -0
  181. package/src/migrations-postgres/039_drop_trace_instrumentation_gaps.sql +11 -0
  182. package/src/migrations-postgres/040_evidence_retrieval_refs.sql +47 -0
  183. package/src/migrations-postgres/README.md +40 -0
  184. package/src/otlp/acceptance.test.ts +373 -0
  185. package/src/otlp/decode/body.ts +127 -0
  186. package/src/otlp/decode/logs.ts +96 -0
  187. package/src/otlp/decode/metrics.ts +402 -0
  188. package/src/otlp/decode/traces.ts +92 -0
  189. package/src/otlp/decode/values.ts +150 -0
  190. package/src/otlp/decode.test.ts +800 -0
  191. package/src/otlp/decode.ts +11 -0
  192. package/src/otlp/gen/opentelemetry/proto/collector/logs/v1/logs_service_pb.ts +132 -0
  193. package/src/otlp/gen/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.ts +158 -0
  194. package/src/otlp/gen/opentelemetry/proto/collector/trace/v1/trace_service_pb.ts +158 -0
  195. package/src/otlp/gen/opentelemetry/proto/common/v1/common_pb.ts +289 -0
  196. package/src/otlp/gen/opentelemetry/proto/logs/v1/logs_pb.ts +397 -0
  197. package/src/otlp/gen/opentelemetry/proto/metrics/v1/metrics_pb.ts +1329 -0
  198. package/src/otlp/gen/opentelemetry/proto/resource/v1/resource_pb.ts +86 -0
  199. package/src/otlp/gen/opentelemetry/proto/trace/v1/trace_pb.ts +710 -0
  200. package/src/otlp/response.test.ts +158 -0
  201. package/src/otlp/response.ts +129 -0
  202. package/src/plugins/_context.ts +15 -0
  203. package/src/plugins/action-graph-processor/enrichers.ts +45 -0
  204. package/src/plugins/action-graph-processor/redaction.ts +84 -0
  205. package/src/plugins/action-graph-processor.test.ts +1023 -0
  206. package/src/plugins/action-graph-processor.ts +1172 -0
  207. package/src/plugins/action-routes.test.ts +716 -0
  208. package/src/plugins/action-routes.ts +343 -0
  209. package/src/plugins/ai-receiver.ts +254 -0
  210. package/src/plugins/ai-span-payloads-processor.test.ts +90 -0
  211. package/src/plugins/ai-span-payloads-processor.ts +180 -0
  212. package/src/plugins/alerts-evaluator.test.ts +91 -0
  213. package/src/plugins/alerts-evaluator.ts +328 -0
  214. package/src/plugins/alerts-routes.ts +101 -0
  215. package/src/plugins/analyses-routes.ts +138 -0
  216. package/src/plugins/ask-routes.ts +182 -0
  217. package/src/plugins/bot-filter.ts +19 -0
  218. package/src/plugins/connected-routes/manifest.ts +531 -0
  219. package/src/plugins/connected-routes.test.ts +1791 -0
  220. package/src/plugins/connected-routes.ts +1103 -0
  221. package/src/plugins/dashboard-routes.ts +41 -0
  222. package/src/plugins/default-span-enrichment.ts +92 -0
  223. package/src/plugins/eval-cases-routes.test.ts +391 -0
  224. package/src/plugins/eval-cases-routes.ts +529 -0
  225. package/src/plugins/evidence-retrieval-routes.benchmark.test.ts +245 -0
  226. package/src/plugins/evidence-retrieval-routes.test.ts +932 -0
  227. package/src/plugins/evidence-retrieval-routes.ts +2179 -0
  228. package/src/plugins/gen-ai-normalizer.test.ts +680 -0
  229. package/src/plugins/gen-ai-normalizer.ts +302 -0
  230. package/src/plugins/identity-receiver.ts +96 -0
  231. package/src/plugins/issue-enrichment.ts +132 -0
  232. package/src/plugins/issue-insights.ts +81 -0
  233. package/src/plugins/logs-receiver.ts +160 -0
  234. package/src/plugins/metrics-receiver.ts +91 -0
  235. package/src/plugins/onboarding-routes.ts +54 -0
  236. package/src/plugins/otlp-receiver.ts +124 -0
  237. package/src/plugins/platform-routes.ts +165 -0
  238. package/src/plugins/profile-routes.ts +468 -0
  239. package/src/plugins/projects-routes.ts +92 -0
  240. package/src/plugins/query-routes.ts +220 -0
  241. package/src/plugins/redaction-processor.ts +132 -0
  242. package/src/plugins/replay-query-routes.test.ts +38 -0
  243. package/src/plugins/replay-query-routes.ts +199 -0
  244. package/src/plugins/replay-receiver.ts +81 -0
  245. package/src/plugins/tail-routes.test.ts +47 -0
  246. package/src/plugins/tail-routes.ts +42 -0
  247. package/src/plugins/timeline-routes.ts +244 -0
  248. package/src/plugins/ua-enrichment.ts +27 -0
  249. package/src/plugins/usage-normalization.ts +46 -0
  250. package/src/plugins/usage-privacy.ts +55 -0
  251. package/src/plugins/usage-query-routes.ts +102 -0
  252. package/src/plugins/usage-receiver.ts +129 -0
  253. package/src/plugins/users-query-routes.ts +78 -0
  254. package/src/plugins/utm-enrichment.ts +36 -0
@@ -0,0 +1,245 @@
1
+ import type { EvidenceBundle } from "@obsunified/types";
2
+ import { Hono } from "hono";
3
+ import { describe, expect, it } from "vitest";
4
+ import { CollectorRuntime } from "../framework/collector";
5
+ import type { CollectorEnv } from "../framework/env";
6
+ import { MemSqlDb } from "../lib/test-utils/mem-sql-db";
7
+ import { evidenceRetrievalRoutesPlugin } from "./evidence-retrieval-routes";
8
+
9
+ const BENCH_TRACE_ID = "trace-ccr-benchmark";
10
+ const BENCH_PROJECT_ID = "default";
11
+ const REPEATED_LOG_COUNT = 500;
12
+
13
+ const estimateTokens = (value: unknown): number =>
14
+ Math.ceil(JSON.stringify(value).length / 4);
15
+
16
+ const jsonBytes = (value: unknown): number =>
17
+ new TextEncoder().encode(JSON.stringify(value)).byteLength;
18
+
19
+ const pctReduction = (before: number, after: number): number =>
20
+ Number((((before - after) / before) * 100).toFixed(1));
21
+
22
+ const makeSpanRows = () => [
23
+ {
24
+ project_id: BENCH_PROJECT_ID,
25
+ trace_id: BENCH_TRACE_ID,
26
+ span_id: "root",
27
+ parent_span_id: null,
28
+ service_name: "checkout-api",
29
+ scope_name: null,
30
+ scope_version: null,
31
+ span_name: "POST /checkout",
32
+ span_kind: 2,
33
+ status_code: 1,
34
+ status_message: null,
35
+ start_time: "2026-06-04T10:00:00.000Z",
36
+ end_time: "2026-06-04T10:00:01.200Z",
37
+ duration_ms: 1200,
38
+ attributes_json: "{}",
39
+ resource_attributes_json: "{}",
40
+ events_json: "[]",
41
+ links_json: "[]",
42
+ received_at: "2026-06-04T10:00:01.200Z",
43
+ expires_at: "2026-06-11T10:00:01.200Z",
44
+ },
45
+ {
46
+ project_id: BENCH_PROJECT_ID,
47
+ trace_id: BENCH_TRACE_ID,
48
+ span_id: "payment",
49
+ parent_span_id: "root",
50
+ service_name: "checkout-api",
51
+ scope_name: null,
52
+ scope_version: null,
53
+ span_name: "payment.authorize",
54
+ span_kind: 3,
55
+ status_code: 2,
56
+ status_message: "Stripe timeout",
57
+ start_time: "2026-06-04T10:00:00.120Z",
58
+ end_time: "2026-06-04T10:00:01.000Z",
59
+ duration_ms: 880,
60
+ attributes_json: "{}",
61
+ resource_attributes_json: "{}",
62
+ events_json: "[]",
63
+ links_json: "[]",
64
+ received_at: "2026-06-04T10:00:01.200Z",
65
+ expires_at: "2026-06-11T10:00:01.200Z",
66
+ },
67
+ ];
68
+
69
+ const makeLogRows = () =>
70
+ Array.from({ length: REPEATED_LOG_COUNT }, (_, index) => ({
71
+ project_id: BENCH_PROJECT_ID,
72
+ log_id: `log-${index + 1}`,
73
+ trace_id: BENCH_TRACE_ID,
74
+ span_id: "payment",
75
+ service_name: "checkout-api",
76
+ severity: "ERROR",
77
+ severity_number: 17,
78
+ logger_name: "checkout",
79
+ message: `GET /api/products/${1000 + index} 404`,
80
+ attributes_json: "{}",
81
+ flags: 0,
82
+ dropped_attributes_count: 0,
83
+ occurred_at: "2026-06-04T10:00:00.500Z",
84
+ received_at: "2026-06-04T10:00:00.500Z",
85
+ expires_at: "2026-06-11T10:00:00.500Z",
86
+ }));
87
+
88
+ const setupBenchmarkApp = () => {
89
+ const spanRows = makeSpanRows();
90
+ const logRows = makeLogRows();
91
+ const db = new MemSqlDb({
92
+ all: (sql, binds) => {
93
+ if (sql.includes("FROM telemetry_spans")) return spanRows;
94
+ if (sql.includes("FROM logs")) {
95
+ const limit = binds.at(-1);
96
+ return typeof limit === "number" ? logRows.slice(0, limit) : logRows;
97
+ }
98
+ if (sql.includes("FROM profile_trace_index")) return [];
99
+ if (
100
+ sql.includes("FROM retrieval_events") ||
101
+ sql.includes("FROM artifacts") ||
102
+ sql.includes("FROM metric_exemplars") ||
103
+ sql.includes("FROM replay")
104
+ ) {
105
+ return [];
106
+ }
107
+ return [];
108
+ },
109
+ first: (sql) => {
110
+ if (sql.includes("FROM logs")) {
111
+ return {
112
+ totalLogs: logRows.length,
113
+ errorLogs: logRows.length,
114
+ warnLogs: 0,
115
+ };
116
+ }
117
+ return null;
118
+ },
119
+ });
120
+ const app = new Hono<{ Bindings: CollectorEnv }>();
121
+ const runtime = new CollectorRuntime(undefined, undefined, () => db);
122
+ evidenceRetrievalRoutesPlugin.register(app, runtime);
123
+ const env: CollectorEnv = { DB: db as unknown as D1Database };
124
+ return { app, env, logRows, spanRows };
125
+ };
126
+
127
+ describe("evidence retrieval CCR benchmark", () => {
128
+ it("compacts repeated logs while preserving the failed-span debugging anchor", async () => {
129
+ const { app, env, logRows, spanRows } = setupBenchmarkApp();
130
+ const bundleRes = await app.request(
131
+ "/internal/evidence/bundle",
132
+ {
133
+ method: "POST",
134
+ body: JSON.stringify({
135
+ anchor: { entityKind: "trace", entityId: BENCH_TRACE_ID },
136
+ intent: "debug_failure",
137
+ budget: { targetTokens: 4000, detailLevel: "standard" },
138
+ }),
139
+ headers: { "content-type": "application/json" },
140
+ },
141
+ env,
142
+ );
143
+ expect(bundleRes.status).toBe(200);
144
+ const bundle = (await bundleRes.json()) as EvidenceBundle;
145
+
146
+ const traceRef = bundle.retrievalRefs.find(
147
+ (ref) => ref.source === "evidence.bundle.trace_detail",
148
+ );
149
+ const logsRef = bundle.retrievalRefs.find(
150
+ (ref) => ref.source === "evidence.bundle.correlated_logs",
151
+ );
152
+ expect(traceRef).toBeDefined();
153
+ expect(logsRef).toBeDefined();
154
+
155
+ const traceRes = await app.request(
156
+ `/internal/evidence/refs/${traceRef?.refId}`,
157
+ { method: "GET" },
158
+ env,
159
+ );
160
+ const logsRes = await app.request(
161
+ `/internal/evidence/refs/${logsRef?.refId}`,
162
+ { method: "GET" },
163
+ env,
164
+ );
165
+ expect(traceRes.status).toBe(200);
166
+ expect(logsRes.status).toBe(200);
167
+ const rawTrace = (await traceRes.json()) as { data: unknown };
168
+ const rawLogs = (await logsRes.json()) as {
169
+ data: { logs: unknown[]; summary: unknown };
170
+ };
171
+ const rawContext = {
172
+ trace: rawTrace.data,
173
+ logs: rawLogs.data.logs,
174
+ logSummary: rawLogs.data.summary,
175
+ };
176
+
177
+ const logCompaction = bundle.compactions.find(
178
+ (compaction) =>
179
+ compaction.kind === "logs" &&
180
+ compaction.strategy === "signature_cluster",
181
+ );
182
+ expect(logCompaction).toEqual(
183
+ expect.objectContaining({
184
+ inputCount: REPEATED_LOG_COUNT,
185
+ outputCount: 3,
186
+ }),
187
+ );
188
+ expect(bundle.findings.map((finding) => finding.title)).toContain(
189
+ "Failed span present",
190
+ );
191
+ expect(
192
+ bundle.evidenceReferences.some(
193
+ (ref) =>
194
+ ref.entityKind === "span" &&
195
+ ref.entityId === `${BENCH_TRACE_ID}:payment`,
196
+ ),
197
+ ).toBe(true);
198
+ expect(logsRef?.compactedFrom?.recordCount).toBe(REPEATED_LOG_COUNT);
199
+ expect(logsRef?.returned?.recordCount).toBe(3);
200
+
201
+ const rawTokens = estimateTokens(rawContext);
202
+ const ccrTokens = estimateTokens(bundle);
203
+ const rawSizeBytes = jsonBytes(rawContext);
204
+ const ccrSizeBytes = jsonBytes(bundle);
205
+ const benchmarkResult = {
206
+ scenario: "trace-repeated-404-burst",
207
+ runDate: "2026-06-04",
208
+ input: {
209
+ traceSpans: spanRows.length,
210
+ rawLogRecords: logRows.length,
211
+ repeatedLogSignature: "GET /api/products/<num> 404",
212
+ },
213
+ rawEvidence: {
214
+ jsonBytes: rawSizeBytes,
215
+ tokenEstimate: rawTokens,
216
+ logRecords: rawLogs.data.logs.length,
217
+ },
218
+ ccrEvidenceBundle: {
219
+ jsonBytes: ccrSizeBytes,
220
+ tokenEstimate: ccrTokens,
221
+ logCompactionInput: logCompaction?.inputCount,
222
+ logCompactionOutput: logCompaction?.outputCount,
223
+ retrievalRefs: bundle.retrievalRefs.length,
224
+ evidenceReferences: bundle.evidenceReferences.length,
225
+ estimatedTokensInBundleBudget: bundle.budget?.estimatedTokens,
226
+ },
227
+ reduction: {
228
+ jsonBytesPct: pctReduction(rawSizeBytes, ccrSizeBytes),
229
+ tokenEstimatePct: pctReduction(rawTokens, ccrTokens),
230
+ rawToCcrTokenRatio: Number((rawTokens / ccrTokens).toFixed(1)),
231
+ },
232
+ preservedSignals: {
233
+ failedSpanFinding: true,
234
+ failedPaymentSpanReference: true,
235
+ },
236
+ };
237
+ console.info(
238
+ `CCR_BENCHMARK_RESULT ${JSON.stringify(benchmarkResult, null, 2)}`,
239
+ );
240
+
241
+ expect(rawLogs.data.logs).toHaveLength(REPEATED_LOG_COUNT);
242
+ expect(rawTokens / ccrTokens).toBeGreaterThan(5);
243
+ expect(rawSizeBytes).toBeGreaterThan(ccrSizeBytes);
244
+ });
245
+ });