@things-factory/headless-twin 10.0.14 โ†’ 10.0.17

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 (478) hide show
  1. package/dist-server/engine/attention-digest.d.ts +4 -4
  2. package/dist-server/engine/attention-digest.js +7 -7
  3. package/dist-server/engine/attention-digest.js.map +1 -1
  4. package/dist-server/engine/canonical-ingest.d.ts +11 -2
  5. package/dist-server/engine/canonical-ingest.js +137 -8
  6. package/dist-server/engine/canonical-ingest.js.map +1 -1
  7. package/dist-server/engine/command-routing.js +1 -1
  8. package/dist-server/engine/command-routing.js.map +1 -1
  9. package/dist-server/engine/declared-stimulus.js +1 -1
  10. package/dist-server/engine/declared-stimulus.js.map +1 -1
  11. package/dist-server/engine/energy-topology.js +1 -1
  12. package/dist-server/engine/energy-topology.js.map +1 -1
  13. package/dist-server/engine/ingest-dedupe.d.ts +38 -0
  14. package/dist-server/engine/ingest-dedupe.js +145 -0
  15. package/dist-server/engine/ingest-dedupe.js.map +1 -0
  16. package/dist-server/engine/ingest-health.d.ts +124 -9
  17. package/dist-server/engine/ingest-health.js +169 -15
  18. package/dist-server/engine/ingest-health.js.map +1 -1
  19. package/dist-server/engine/integration-probes.d.ts +3 -3
  20. package/dist-server/engine/integration-probes.js +6 -6
  21. package/dist-server/engine/integration-probes.js.map +1 -1
  22. package/dist-server/engine/kpi-baseline.js +4 -4
  23. package/dist-server/engine/kpi-baseline.js.map +1 -1
  24. package/dist-server/engine/kpi-fold.d.ts +122 -12
  25. package/dist-server/engine/kpi-fold.js +247 -18
  26. package/dist-server/engine/kpi-fold.js.map +1 -1
  27. package/dist-server/engine/kpi-query.d.ts +11 -11
  28. package/dist-server/engine/kpi-query.js +185 -28
  29. package/dist-server/engine/kpi-query.js.map +1 -1
  30. package/dist-server/engine/live-feed-registry.d.ts +1 -1
  31. package/dist-server/engine/live-feed-registry.js +1 -1
  32. package/dist-server/engine/live-feed-registry.js.map +1 -1
  33. package/dist-server/engine/load-meter.d.ts +1 -1
  34. package/dist-server/engine/load-meter.js.map +1 -1
  35. package/dist-server/engine/local-declarations.d.ts +7 -7
  36. package/dist-server/engine/local-declarations.js +9 -9
  37. package/dist-server/engine/local-declarations.js.map +1 -1
  38. package/dist-server/engine/log.d.ts +1 -1
  39. package/dist-server/engine/log.js +1 -1
  40. package/dist-server/engine/log.js.map +1 -1
  41. package/dist-server/engine/measured-estimator.js +1 -1
  42. package/dist-server/engine/measured-estimator.js.map +1 -1
  43. package/dist-server/engine/measured-yield.js +1 -1
  44. package/dist-server/engine/measured-yield.js.map +1 -1
  45. package/dist-server/engine/model-basis.d.ts +1 -1
  46. package/dist-server/engine/model-basis.js +1 -1
  47. package/dist-server/engine/model-basis.js.map +1 -1
  48. package/dist-server/engine/model-gap.d.ts +3 -3
  49. package/dist-server/engine/model-gap.js +1 -1
  50. package/dist-server/engine/model-gap.js.map +1 -1
  51. package/dist-server/engine/model-vocabulary.js +1 -1
  52. package/dist-server/engine/model-vocabulary.js.map +1 -1
  53. package/dist-server/engine/oee-accumulator.d.ts +4 -4
  54. package/dist-server/engine/oee-accumulator.js +3 -3
  55. package/dist-server/engine/oee-accumulator.js.map +1 -1
  56. package/dist-server/engine/property-effects.js +2 -2
  57. package/dist-server/engine/property-effects.js.map +1 -1
  58. package/dist-server/engine/read-time.d.ts +22 -0
  59. package/dist-server/engine/read-time.js +81 -0
  60. package/dist-server/engine/read-time.js.map +1 -0
  61. package/dist-server/engine/restart-policy.js +1 -1
  62. package/dist-server/engine/restart-policy.js.map +1 -1
  63. package/dist-server/engine/runtime-key.js +1 -1
  64. package/dist-server/engine/runtime-key.js.map +1 -1
  65. package/dist-server/engine/spec-coverage.d.ts +2 -2
  66. package/dist-server/engine/spec-coverage.js +1 -1
  67. package/dist-server/engine/spec-coverage.js.map +1 -1
  68. package/dist-server/engine/stage-path.d.ts +4 -4
  69. package/dist-server/engine/stage-path.js +1 -1
  70. package/dist-server/engine/stage-path.js.map +1 -1
  71. package/dist-server/engine/state-axes.d.ts +1 -1
  72. package/dist-server/engine/state-axes.js +2 -2
  73. package/dist-server/engine/state-axes.js.map +1 -1
  74. package/dist-server/engine/travel-estimator.d.ts +2 -2
  75. package/dist-server/engine/travel-estimator.js +1 -1
  76. package/dist-server/engine/travel-estimator.js.map +1 -1
  77. package/dist-server/engine/twin-engine.d.ts +243 -53
  78. package/dist-server/engine/twin-engine.js +690 -191
  79. package/dist-server/engine/twin-engine.js.map +1 -1
  80. package/dist-server/engine/warm-start.d.ts +2 -2
  81. package/dist-server/engine/warm-start.js +5 -5
  82. package/dist-server/engine/warm-start.js.map +1 -1
  83. package/dist-server/index.js +1 -1
  84. package/dist-server/index.js.map +1 -1
  85. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js +1 -1
  86. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +1 -1
  87. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js +3 -3
  88. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +1 -1
  89. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js +2 -2
  90. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +1 -1
  91. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.d.ts +5 -0
  92. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js +54 -0
  93. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +1 -0
  94. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.d.ts +5 -0
  95. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js +44 -0
  96. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +1 -0
  97. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.d.ts +6 -0
  98. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js +84 -0
  99. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +1 -0
  100. package/dist-server/migrations/index.js +7 -1
  101. package/dist-server/migrations/index.js.map +1 -1
  102. package/dist-server/routes.d.ts +1 -0
  103. package/dist-server/routes.js +69 -5
  104. package/dist-server/routes.js.map +1 -1
  105. package/dist-server/service/index.d.ts +2 -2
  106. package/dist-server/service/index.js +27 -24
  107. package/dist-server/service/index.js.map +1 -1
  108. package/dist-server/service/reference/control-routing.d.ts +1 -1
  109. package/dist-server/service/reference/control-routing.js +1 -1
  110. package/dist-server/service/reference/control-routing.js.map +1 -1
  111. package/dist-server/service/reference/discovery-result.d.ts +2 -2
  112. package/dist-server/service/reference/discovery-result.js +3 -3
  113. package/dist-server/service/reference/discovery-result.js.map +1 -1
  114. package/dist-server/service/reference/hook-contract.d.ts +43 -0
  115. package/dist-server/service/reference/hook-contract.js +59 -0
  116. package/dist-server/service/reference/hook-contract.js.map +1 -0
  117. package/dist-server/service/reference/ingest-space.d.ts +1 -1
  118. package/dist-server/service/reference/ingest-space.js +4 -4
  119. package/dist-server/service/reference/ingest-space.js.map +1 -1
  120. package/dist-server/service/reference/knob-defaults.d.ts +1 -1
  121. package/dist-server/service/reference/knob-defaults.js +4 -4
  122. package/dist-server/service/reference/knob-defaults.js.map +1 -1
  123. package/dist-server/service/reference/reference-adapter.d.ts +140 -8
  124. package/dist-server/service/reference/reference-adapter.js +27 -0
  125. package/dist-server/service/reference/reference-adapter.js.map +1 -1
  126. package/dist-server/service/reference/reference-hook.d.ts +20 -0
  127. package/dist-server/service/reference/reference-hook.js +133 -0
  128. package/dist-server/service/reference/reference-hook.js.map +1 -0
  129. package/dist-server/service/reference/reference-live.d.ts +2 -2
  130. package/dist-server/service/reference/reference-live.js +88 -13
  131. package/dist-server/service/reference/reference-live.js.map +1 -1
  132. package/dist-server/service/reference/reference-master.d.ts +14 -14
  133. package/dist-server/service/reference/reference-master.js +34 -12
  134. package/dist-server/service/reference/reference-master.js.map +1 -1
  135. package/dist-server/service/reference/reference-resolver.d.ts +1 -1
  136. package/dist-server/service/reference/reference-resolver.js +10 -10
  137. package/dist-server/service/reference/reference-resolver.js.map +1 -1
  138. package/dist-server/service/reference/twin-reference.d.ts +3 -3
  139. package/dist-server/service/reference/twin-reference.js.map +1 -1
  140. package/dist-server/service/twin-attention/twin-attention-query.d.ts +1 -1
  141. package/dist-server/service/twin-attention/twin-attention-query.js +1 -1
  142. package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
  143. package/dist-server/service/twin-audit/command-audit.d.ts +1 -1
  144. package/dist-server/service/twin-audit/command-audit.js.map +1 -1
  145. package/dist-server/service/twin-audit/twin-audit-event.d.ts +1 -1
  146. package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -1
  147. package/dist-server/service/twin-audit/twin-audit-query.js +1 -1
  148. package/dist-server/service/twin-audit/twin-audit-query.js.map +1 -1
  149. package/dist-server/service/twin-control/twin-control-mutation.js +1 -1
  150. package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
  151. package/dist-server/service/twin-event/twin-event-keys.d.ts +22 -2
  152. package/dist-server/service/twin-event/twin-event-keys.js +37 -5
  153. package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
  154. package/dist-server/service/twin-event/twin-event-type.d.ts +1 -0
  155. package/dist-server/service/twin-event/twin-event-type.js +8 -1
  156. package/dist-server/service/twin-event/twin-event-type.js.map +1 -1
  157. package/dist-server/service/twin-event/twin-event.d.ts +2 -2
  158. package/dist-server/service/twin-event/twin-event.js +32 -4
  159. package/dist-server/service/twin-event/twin-event.js.map +1 -1
  160. package/dist-server/service/twin-forecast/forecast-metrics.js +1 -1
  161. package/dist-server/service/twin-forecast/forecast-metrics.js.map +1 -1
  162. package/dist-server/service/twin-forecast/gap-analytics.d.ts +1 -1
  163. package/dist-server/service/twin-forecast/gap-analytics.js.map +1 -1
  164. package/dist-server/service/twin-forecast/twin-forecast-query.js +29 -11
  165. package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
  166. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.d.ts +1 -1
  167. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js +1 -1
  168. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +1 -1
  169. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js +1 -1
  170. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +1 -1
  171. package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
  172. package/dist-server/service/twin-journal/event-type-split-shape.d.ts +50 -0
  173. package/dist-server/service/twin-journal/event-type-split-shape.js +111 -0
  174. package/dist-server/service/twin-journal/event-type-split-shape.js.map +1 -0
  175. package/dist-server/service/twin-journal/event-type-split.d.ts +15 -0
  176. package/dist-server/service/twin-journal/event-type-split.js +102 -0
  177. package/dist-server/service/twin-journal/event-type-split.js.map +1 -0
  178. package/dist-server/service/twin-journal/journal-order.d.ts +15 -0
  179. package/dist-server/service/twin-journal/journal-order.js +17 -0
  180. package/dist-server/service/twin-journal/journal-order.js.map +1 -0
  181. package/dist-server/service/twin-journal/twin-journal-query.d.ts +5 -5
  182. package/dist-server/service/twin-journal/twin-journal-query.js +232 -21
  183. package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
  184. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +27 -1
  185. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +59 -3
  186. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
  187. package/dist-server/service/twin-metrics/twin-metrics-query.js +14 -1
  188. package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
  189. package/dist-server/service/twin-model/axis-journal-evidence.js +1 -1
  190. package/dist-server/service/twin-model/axis-journal-evidence.js.map +1 -1
  191. package/dist-server/service/twin-model/epcis-coverage.js +36 -1
  192. package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
  193. package/dist-server/service/twin-model/iec61850-coverage.js +1 -1
  194. package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -1
  195. package/dist-server/service/twin-model/isa95-coverage.d.ts +15 -2
  196. package/dist-server/service/twin-model/isa95-coverage.js +32 -8
  197. package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
  198. package/dist-server/service/twin-model/item-ref.js +2 -2
  199. package/dist-server/service/twin-model/item-ref.js.map +1 -1
  200. package/dist-server/service/twin-model/name-index.d.ts +1 -1
  201. package/dist-server/service/twin-model/name-index.js +1 -1
  202. package/dist-server/service/twin-model/name-index.js.map +1 -1
  203. package/dist-server/service/twin-model/project-structure.d.ts +1 -1
  204. package/dist-server/service/twin-model/project-structure.js +3 -3
  205. package/dist-server/service/twin-model/project-structure.js.map +1 -1
  206. package/dist-server/service/twin-model/twin-equipment.d.ts +1 -1
  207. package/dist-server/service/twin-model/twin-equipment.js +1 -1
  208. package/dist-server/service/twin-model/twin-equipment.js.map +1 -1
  209. package/dist-server/service/twin-model/twin-lineage-query.js +16 -5
  210. package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
  211. package/dist-server/service/twin-model/twin-location.d.ts +1 -1
  212. package/dist-server/service/twin-model/twin-location.js.map +1 -1
  213. package/dist-server/service/twin-model/twin-model-item-query.d.ts +3 -3
  214. package/dist-server/service/twin-model/twin-model-item-query.js +14 -14
  215. package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
  216. package/dist-server/service/twin-model/twin-model-mutation.d.ts +2 -2
  217. package/dist-server/service/twin-model/twin-model-mutation.js +4 -4
  218. package/dist-server/service/twin-model/twin-model-mutation.js.map +1 -1
  219. package/dist-server/service/twin-model/twin-model-query.js +117 -28
  220. package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
  221. package/dist-server/service/twin-model/twin-model-tree-query.js +22 -0
  222. package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
  223. package/dist-server/service/twin-model/twin-operation.d.ts +1 -1
  224. package/dist-server/service/twin-model/twin-operation.js +1 -1
  225. package/dist-server/service/twin-model/twin-operation.js.map +1 -1
  226. package/dist-server/service/twin-space/move-space.js +2 -2
  227. package/dist-server/service/twin-space/move-space.js.map +1 -1
  228. package/dist-server/service/twin-space/representation-role.d.ts +2 -0
  229. package/dist-server/service/twin-space/representation-role.js +18 -0
  230. package/dist-server/service/twin-space/representation-role.js.map +1 -0
  231. package/dist-server/service/twin-space/space-integrity.d.ts +1 -1
  232. package/dist-server/service/twin-space/space-integrity.js.map +1 -1
  233. package/dist-server/service/twin-space/twin-space-representation.d.ts +2 -0
  234. package/dist-server/service/twin-space/twin-space-representation.js +16 -0
  235. package/dist-server/service/twin-space/twin-space-representation.js.map +1 -1
  236. package/dist-server/service/twin-space/twin-space-resolver.d.ts +2 -2
  237. package/dist-server/service/twin-space/twin-space-resolver.js +36 -7
  238. package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
  239. package/dist-server/service/twin-structure/twin-structure.js +3 -3
  240. package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
  241. package/dist-server/service/twin-subject/event-subjects.d.ts +43 -0
  242. package/dist-server/service/twin-subject/event-subjects.js +137 -0
  243. package/dist-server/service/twin-subject/event-subjects.js.map +1 -0
  244. package/dist-server/service/twin-subject/index.d.ts +9 -0
  245. package/dist-server/service/twin-subject/index.js +16 -0
  246. package/dist-server/service/twin-subject/index.js.map +1 -0
  247. package/dist-server/service/twin-subject/item-resolver.d.ts +10 -0
  248. package/dist-server/service/twin-subject/item-resolver.js +93 -0
  249. package/dist-server/service/twin-subject/item-resolver.js.map +1 -0
  250. package/dist-server/service/twin-subject/subject-query-shape.d.ts +22 -0
  251. package/dist-server/service/twin-subject/subject-query-shape.js +88 -0
  252. package/dist-server/service/twin-subject/subject-query-shape.js.map +1 -0
  253. package/dist-server/service/twin-subject/twin-subject-backfill.d.ts +23 -0
  254. package/dist-server/service/twin-subject/twin-subject-backfill.js +159 -0
  255. package/dist-server/service/twin-subject/twin-subject-backfill.js.map +1 -0
  256. package/dist-server/service/twin-subject/twin-subject-event.d.ts +14 -0
  257. package/dist-server/service/twin-subject/twin-subject-event.js +111 -0
  258. package/dist-server/service/twin-subject/twin-subject-event.js.map +1 -0
  259. package/dist-server/service/twin-subject/twin-subject-fast-path.d.ts +39 -0
  260. package/dist-server/service/twin-subject/twin-subject-fast-path.js +295 -0
  261. package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +1 -0
  262. package/dist-server/service/twin-subject/twin-subject-query.d.ts +27 -0
  263. package/dist-server/service/twin-subject/twin-subject-query.js +232 -0
  264. package/dist-server/service/twin-subject/twin-subject-query.js.map +1 -0
  265. package/dist-server/service/twin-subject/twin-subject-writer.d.ts +27 -0
  266. package/dist-server/service/twin-subject/twin-subject-writer.js +166 -0
  267. package/dist-server/service/twin-subject/twin-subject-writer.js.map +1 -0
  268. package/dist-server/service/twin-target/twin-target-resolver.js +1 -1
  269. package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -1
  270. package/dist-shared/entity-delta.d.ts +1 -1
  271. package/dist-shared/entity-delta.js +3 -3
  272. package/dist-shared/entity-delta.js.map +1 -1
  273. package/dist-shared/touched-items.js +2 -2
  274. package/dist-shared/touched-items.js.map +1 -1
  275. package/dist-shared/twin-level.js +1 -1
  276. package/dist-shared/twin-level.js.map +1 -1
  277. package/package.json +3 -3
  278. package/server/engine/attention-digest.ts +9 -9
  279. package/server/engine/canonical-ingest.ts +170 -13
  280. package/server/engine/command-routing.ts +1 -1
  281. package/server/engine/declared-stimulus.ts +1 -1
  282. package/server/engine/energy-topology.ts +1 -1
  283. package/server/engine/ingest-dedupe.ts +149 -0
  284. package/server/engine/ingest-health.ts +234 -20
  285. package/server/engine/integration-probes.ts +7 -7
  286. package/server/engine/kpi-baseline.ts +4 -4
  287. package/server/engine/kpi-fold.ts +395 -27
  288. package/server/engine/kpi-query.ts +212 -46
  289. package/server/engine/live-feed-registry.ts +1 -1
  290. package/server/engine/load-meter.ts +1 -1
  291. package/server/engine/local-declarations.ts +13 -13
  292. package/server/engine/log.ts +1 -1
  293. package/server/engine/measured-estimator.ts +1 -1
  294. package/server/engine/measured-yield.ts +1 -1
  295. package/server/engine/model-basis.ts +2 -2
  296. package/server/engine/model-gap.ts +3 -3
  297. package/server/engine/model-vocabulary.ts +1 -1
  298. package/server/engine/oee-accumulator.ts +4 -4
  299. package/server/engine/property-effects.ts +2 -2
  300. package/server/engine/read-time.ts +77 -0
  301. package/server/engine/restart-policy.ts +1 -1
  302. package/server/engine/runtime-key.ts +1 -1
  303. package/server/engine/spec-coverage.ts +3 -3
  304. package/server/engine/stage-path.ts +5 -5
  305. package/server/engine/state-axes.ts +2 -2
  306. package/server/engine/travel-estimator.ts +2 -2
  307. package/server/engine/twin-engine.ts +764 -201
  308. package/server/engine/warm-start.ts +6 -6
  309. package/server/index.ts +1 -1
  310. package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +1 -1
  311. package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +3 -3
  312. package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +2 -2
  313. package/server/migrations/1786300000000-IndexEventTypeByTime.ts +48 -0
  314. package/server/migrations/1786400000000-IndexEventCreatedAt.ts +38 -0
  315. package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +93 -0
  316. package/server/migrations/index.ts +7 -1
  317. package/server/routes.ts +68 -5
  318. package/server/service/index.ts +4 -1
  319. package/server/service/reference/control-routing.ts +1 -1
  320. package/server/service/reference/discovery-result.ts +3 -3
  321. package/server/service/reference/hook-contract.ts +103 -0
  322. package/server/service/reference/ingest-space.ts +4 -4
  323. package/server/service/reference/knob-defaults.ts +4 -4
  324. package/server/service/reference/reference-adapter.ts +166 -9
  325. package/server/service/reference/reference-hook.ts +145 -0
  326. package/server/service/reference/reference-live.ts +92 -14
  327. package/server/service/reference/reference-master.ts +39 -17
  328. package/server/service/reference/reference-resolver.ts +10 -10
  329. package/server/service/reference/twin-reference.ts +3 -3
  330. package/server/service/twin-attention/twin-attention-query.ts +1 -1
  331. package/server/service/twin-audit/command-audit.ts +1 -1
  332. package/server/service/twin-audit/twin-audit-event.ts +2 -2
  333. package/server/service/twin-audit/twin-audit-query.ts +1 -1
  334. package/server/service/twin-control/twin-control-mutation.ts +1 -1
  335. package/server/service/twin-event/twin-event-keys.ts +37 -5
  336. package/server/service/twin-event/twin-event-type.ts +18 -1
  337. package/server/service/twin-event/twin-event.ts +32 -6
  338. package/server/service/twin-forecast/forecast-metrics.ts +1 -1
  339. package/server/service/twin-forecast/gap-analytics.ts +1 -1
  340. package/server/service/twin-forecast/twin-forecast-query.ts +30 -11
  341. package/server/service/twin-ingest-window/twin-ingest-window-query.ts +1 -1
  342. package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +1 -1
  343. package/server/service/twin-instance/twin-instance.ts +4 -4
  344. package/server/service/twin-journal/event-type-split-shape.ts +112 -0
  345. package/server/service/twin-journal/event-type-split.ts +144 -0
  346. package/server/service/twin-journal/journal-order.ts +20 -0
  347. package/server/service/twin-journal/twin-journal-query.ts +224 -20
  348. package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +57 -3
  349. package/server/service/twin-metrics/twin-metrics-query.ts +14 -1
  350. package/server/service/twin-model/axis-journal-evidence.ts +1 -1
  351. package/server/service/twin-model/epcis-coverage.ts +36 -1
  352. package/server/service/twin-model/iec61850-coverage.ts +1 -1
  353. package/server/service/twin-model/isa95-coverage.ts +45 -8
  354. package/server/service/twin-model/item-ref.ts +2 -2
  355. package/server/service/twin-model/name-index.ts +1 -1
  356. package/server/service/twin-model/project-structure.ts +4 -4
  357. package/server/service/twin-model/twin-equipment.ts +2 -2
  358. package/server/service/twin-model/twin-lineage-query.ts +16 -5
  359. package/server/service/twin-model/twin-location.ts +1 -1
  360. package/server/service/twin-model/twin-model-item-query.ts +14 -14
  361. package/server/service/twin-model/twin-model-mutation.ts +4 -4
  362. package/server/service/twin-model/twin-model-query.ts +117 -28
  363. package/server/service/twin-model/twin-model-tree-query.ts +25 -1
  364. package/server/service/twin-model/twin-operation.ts +2 -2
  365. package/server/service/twin-space/move-space.ts +3 -3
  366. package/server/service/twin-space/representation-role.ts +15 -0
  367. package/server/service/twin-space/space-integrity.ts +1 -1
  368. package/server/service/twin-space/twin-space-representation.ts +37 -0
  369. package/server/service/twin-space/twin-space-resolver.ts +39 -7
  370. package/server/service/twin-structure/twin-structure.ts +3 -3
  371. package/server/service/twin-subject/event-subjects.ts +155 -0
  372. package/server/service/twin-subject/index.ts +10 -0
  373. package/server/service/twin-subject/item-resolver.ts +92 -0
  374. package/server/service/twin-subject/subject-query-shape.ts +84 -0
  375. package/server/service/twin-subject/twin-subject-backfill.ts +196 -0
  376. package/server/service/twin-subject/twin-subject-event.ts +101 -0
  377. package/server/service/twin-subject/twin-subject-fast-path.ts +346 -0
  378. package/server/service/twin-subject/twin-subject-query.ts +210 -0
  379. package/server/service/twin-subject/twin-subject-writer.ts +189 -0
  380. package/server/service/twin-target/twin-target-resolver.ts +1 -1
  381. package/shared/entity-delta.ts +3 -3
  382. package/shared/touched-items.ts +2 -2
  383. package/shared/twin-level.ts +1 -1
  384. package/test/adopt-structure-live.test.ts +3 -3
  385. package/test/aggregate-not-findone.test.ts +112 -0
  386. package/test/attention-digest.test.ts +6 -6
  387. package/test/axis-read.test.ts +2 -2
  388. package/test/boot-resume.test.ts +20 -7
  389. package/test/broadcast-period.test.ts +2 -2
  390. package/test/broadcast-queue-overflow.test.ts +244 -0
  391. package/test/canonical-ingest-vocabularies.test.ts +179 -7
  392. package/test/canonical-quantity-door.test.ts +2 -2
  393. package/test/checkpoint-refuses-empty.test.ts +2 -2
  394. package/test/connector-capability-declaration.test.ts +88 -0
  395. package/test/control-capability.test.ts +1 -1
  396. package/test/cursor-stall-not-read-failure.test.ts +2 -2
  397. package/test/declaration-reaches-model.test.ts +5 -5
  398. package/test/declared-stimulus.test.ts +4 -4
  399. package/test/discovery-result.test.ts +2 -2
  400. package/test/entity-delta.test.ts +4 -4
  401. package/test/event-subjects.test.ts +147 -0
  402. package/test/event-time-column.test.ts +3 -3
  403. package/test/event-type-split.test.ts +155 -0
  404. package/test/fold-gap-speaks.test.ts +53 -0
  405. package/test/forecast-metrics.test.ts +1 -1
  406. package/test/forecast-tuning.test.ts +1 -1
  407. package/test/ingest-health-engine.test.ts +2 -2
  408. package/test/ingest-health-wiring.test.ts +7 -5
  409. package/test/ingest-health.test.ts +6 -6
  410. package/test/ingest-history.test.ts +2 -2
  411. package/test/ingest-idempotent.test.ts +132 -0
  412. package/test/ingest-space.test.ts +3 -3
  413. package/test/integration-probes.test.ts +3 -3
  414. package/test/integration-runner.test.ts +1 -1
  415. package/test/item-ref.test.ts +1 -1
  416. package/test/journal-order-axis.test.ts +80 -0
  417. package/test/journal-read-discipline.test.ts +16 -6
  418. package/test/journal-retention.test.ts +171 -29
  419. package/test/journal-sort-axis.test.ts +1 -1
  420. package/test/journal-write-door.test.ts +3 -3
  421. package/test/journal-write-trend.test.ts +1 -1
  422. package/test/kernel-kind-guard.test.ts +3 -3
  423. package/test/knob-defaults.test.ts +1 -1
  424. package/test/kpi-baseline-db.test.ts +2 -2
  425. package/test/kpi-fold.test.ts +185 -14
  426. package/test/kpi-query-bench.test.ts +3 -3
  427. package/test/live-cursor-wiring.test.ts +6 -6
  428. package/test/live-kernel-facts.test.ts +11 -11
  429. package/test/live-mirror-parity.test.ts +3 -3
  430. package/test/load-meter.test.ts +1 -1
  431. package/test/local-declarations.test.ts +10 -10
  432. package/test/log-stamp.test.ts +1 -1
  433. package/test/measured-yield.test.ts +2 -2
  434. package/test/merge-streams.test.ts +70 -0
  435. package/test/mirror-resumes-from-checkpoint.test.ts +13 -13
  436. package/test/model-basis.test.ts +2 -2
  437. package/test/model-gap.test.ts +4 -4
  438. package/test/model-vocabulary.test.ts +2 -2
  439. package/test/move-space.test.ts +1 -1
  440. package/test/oee-accumulator.test.ts +2 -2
  441. package/test/operational-vocabulary.test.ts +2 -2
  442. package/test/project-structure-db.test.ts +2 -2
  443. package/test/projection-reaches-screen.test.ts +2 -2
  444. package/test/property-effects.test.ts +3 -3
  445. package/test/read-failure-ledger.test.ts +106 -0
  446. package/test/read-failure-visible.test.ts +7 -7
  447. package/test/read-time.test.ts +118 -0
  448. package/test/reference-grounding.test.ts +1 -1
  449. package/test/reference-hook.test.ts +86 -0
  450. package/test/registry-key-guard.test.ts +1 -1
  451. package/test/rename-twin.test.ts +81 -0
  452. package/test/restart-policy.test.ts +3 -3
  453. package/test/resync-origin-site.test.ts +3 -3
  454. package/test/revision-axis.test.ts +14 -8
  455. package/test/runtime-key.test.ts +3 -3
  456. package/test/snapshot-freshness.test.ts +4 -4
  457. package/test/source-outcome-audit.test.ts +1 -1
  458. package/test/space-integrity.test.ts +1 -1
  459. package/test/space-representation.test.ts +66 -0
  460. package/test/space-scope-includes-stopped.test.ts +55 -0
  461. package/test/spec-coverage.test.ts +1 -1
  462. package/test/standard-coverage.test.ts +43 -1
  463. package/test/status-tally.test.ts +1 -1
  464. package/test/structure-revision-db.test.ts +7 -7
  465. package/test/subject-fast-path.test.ts +148 -0
  466. package/test/subject-read-model.test.ts +175 -0
  467. package/test/touched-items.test.ts +2 -2
  468. package/test/twin-audit.test.ts +1 -1
  469. package/test/twin-event-keys.test.ts +49 -3
  470. package/test/twin-model-item-db.test.ts +5 -5
  471. package/test/twin-model-tree-db.test.ts +35 -2
  472. package/test/twin-origin-resync.test.ts +1 -1
  473. package/test/warm-start-seam.test.ts +1 -1
  474. package/test/warm-start.test.ts +6 -6
  475. package/test/withheld-door.test.ts +95 -0
  476. package/test/yield-loop.test.ts +4 -4
  477. package/tsconfig.shared.tsbuildinfo +1 -1
  478. package/tsconfig.tsbuildinfo +1 -1
@@ -5,12 +5,12 @@
5
5
  * โ”€โ”€ ํ•œ ์งˆ๋ฌธ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
6
6
  * ํ™”๋ฉด์—์„œ ๊ฐ’์ด ๋ณ€ํ•˜์ง€ ์•Š์„ ๋•Œ ์›์ธ์ด ๋„ท์ธ๋ฐ, ์ง€๊ธˆ๊นŒ์ง€ **๋„ท์ด ๋˜‘๊ฐ™์ด ๋ณด์˜€๋‹ค.**
7
7
  * โ‘  ํ”ผ๋“œ๊ฐ€ ๋ถ™์ง€ ์•Š์•˜๋‹ค โ‘ก ๋ถ™์—ˆ๋Š”๋ฐ ์•„๋ฌด๊ฒƒ๋„ ์˜ค์ง€ ์•Š๋Š”๋‹ค
8
- * โ‘ข ์˜ค๋Š”๋ฐ ์ „๋ถ€ ๊ฑฐ๋ถ€๋œ๋‹ค โ‘ฃ ์ •์ƒ์ธ๋ฐ ํ˜„์žฅ์ด ์กฐ์šฉํ•˜๋‹ค
8
+ * โ‘ข ์˜ค๋Š”๋ฐ ์ „๋ถ€ ๊ฑฐ๋ถ€๋œ๋‹ค โ‘ฃ ์ •์ƒ์ธ๋ฐ ํ˜„์žฅ์ด ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†๋‹ค
9
9
  * โ‘ข์€ ํŠนํžˆ ๋‚˜์˜๋‹ค โ€” ์ปค๋„ฅํ„ฐ์˜ ๋งคํ•‘์ด ํ•œ ๊ธ€์ž ํ‹€๋ฆฌ๋ฉด ํŠธ์œˆ์€ ใ€Œ๋นˆ ๊ณต์žฅใ€์ด ๋˜๊ณ , ๊ทธ ์‚ฌ์‹ค์ด ์„œ๋ฒ„ ๋กœ๊ทธ
10
10
  * ํ•œ ์ค„์—๋งŒ ๋‚จ์•˜๋‹ค. ํ˜„์žฅ์— ๋‚˜๊ฐ„ ์‚ฌ๋žŒ์€ ๋กœ๊ทธ๋ฅผ ๋’ค์ ธ์•ผ ํ–ˆ๋‹ค.
11
11
  *
12
12
  * โ”€โ”€ ์™œ ์ €๋„๋กœ๋Š” ๋‹ตํ•  ์ˆ˜ ์—†๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
13
- * **๊ฑฐ๋ถ€๋œ ๊ฒƒ์€ ์ €๋„์— ์—†๋‹ค** โ€” ๊ฑฐ๋ถ€์˜ ๋œป์ด ๊ทธ๊ฒƒ์ด๋‹ค. ์ €๋„์„ ์ ‘์œผ๋ฉด ํ†ต๊ณผํ•œ ์ชฝ๋งŒ ๋‚˜์˜จ๋‹ค. ๊ฑฐ๋ถ€์˜ ์ถ”์ด๋Š”
13
+ * **๊ฑฐ๋ถ€๋œ ๊ฒƒ์€ ์ €๋„์— ์—†๋‹ค** โ€” ๊ฑฐ๋ถ€์˜ ๋œป์ด ๊ทธ๊ฒƒ์ด๋‹ค. ์ €๋„์„ ๊ณ„์‚ฐํ•˜๋ฉด ํ†ต๊ณผํ•œ ์ชฝ๋งŒ ๋‚˜์˜จ๋‹ค. ๊ฑฐ๋ถ€์˜ ์ถ”์ด๋Š”
14
14
  * ์„ธ๋Š” ์ž๋ฆฌ๋ฅผ ๋”ฐ๋กœ ๋‘์–ด์•ผ ๋‚˜์˜จ๋‹ค. ์ด ์žฅ๋ถ€๊ฐ€ ๊ทธ ์ž๋ฆฌ๋‹ค.
15
15
  *
16
16
  * โ”€โ”€ ์™œ ๊ฑด๋งˆ๋‹ค ์ €์žฅํ•˜์ง€ ์•Š๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -24,14 +24,18 @@
24
24
  * โ”€โ”€ ์ง€ํ‚ค๋Š” ๊ทœ์œจ ๋„ท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
25
25
  * โ‘  **๋‹ซํžŒ ์ฐฝ๋งŒ ใ€Œ์ตœ๊ทผใ€์ด๋‹ค** โ€” ์ฑ„์šฐ๋Š” ์ค‘์ธ ์ฐฝ์€ ๊ณผ์†Œ ์ง‘๊ณ„๋‹ค(๋ถ€ํ•˜ ๊ณ„๊ธฐํŒ์—์„œ ๊ฒช์—ˆ๋‹ค).
26
26
  * โ‘ก **0 ๊ณผ ใ€Œ์—†์Œใ€์„ ๊ฐ€๋ฅธ๋‹ค** โ€” ์•ˆ ๋ฐ›์€ ๊ฒƒ์€ `null`, ๋ฐ›์•˜์ง€๋งŒ ๊ฑฐ๋ถ€ 0 ์€ `0`.
27
- * โ‘ข **์กฐ์šฉํžˆ ์ž๋ฅด์ง€ ์•Š๋Š”๋‹ค** โ€” ํ‘œ๋ณธยท์ถ”์ด ์ƒํ•œ์—์„œ ๋ฐ€๋ ค๋‚œ ์ˆ˜๋ฅผ ํ•จ๊ป˜ ๋‚ธ๋‹ค.
27
+ * โ‘ข **์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ž๋ฅด์ง€ ์•Š๋Š”๋‹ค** โ€” ํ‘œ๋ณธยท์ถ”์ด ์ƒํ•œ์—์„œ ๋ฐ€๋ ค๋‚œ ์ˆ˜๋ฅผ ํ•จ๊ป˜ ๋‚ธ๋‹ค.
28
28
  * โ‘ฃ **ํ†ต๊ณผ์œจ์— 1 ์„ ํ•จ๋ถ€๋กœ ์“ฐ์ง€ ์•Š๋Š”๋‹ค** โ€” 1,000๊ฑด ์ค‘ 1๊ฑด ๊ฑฐ๋ถ€๋Š” 0.999 ๋‹ค.
29
29
  */
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
31
  exports.HEALTHY_ACCEPTED_RATIO = exports.RECORD_MAX = exports.SAMPLE_LIMIT = exports.OTHER_REASON = exports.REASON_LIMIT = exports.TREND_LIMIT = exports.STALE_AFTER_MS = exports.INGEST_WINDOW_MS = void 0;
32
32
  exports.recordReadFailure = recordReadFailure;
33
+ exports.recordRecovered = recordRecovered;
33
34
  exports.clearReadFailure = clearReadFailure;
34
35
  exports.recordCursorStall = recordCursorStall;
36
+ exports.recordIngestSource = recordIngestSource;
37
+ exports.recordDuplicates = recordDuplicates;
38
+ exports.recordWithheld = recordWithheld;
35
39
  exports.clearCursorStall = clearCursorStall;
36
40
  exports.newIngestLedger = newIngestLedger;
37
41
  exports.rollIngestWindow = rollIngestWindow;
@@ -46,21 +50,44 @@ exports.ingestHealth = ingestHealth;
46
50
  * ์—ฐ์† ์‹คํŒจ์˜ ์‹œ์ž‘ ์‹œ๊ฐ์„ ์ง€ํ‚ค๋Š” ๊ฒƒ์ด ์ด ํ•จ์ˆ˜์˜ ์š”์ ์ด๋‹ค: ๋งค๋ฒˆ ์ƒˆ๋กœ ์ ์œผ๋ฉด ใ€Œ20๋ถ„์งธ ๋ชป ์ฝ๋Š”๋‹คใ€๋ฅผ
47
51
  * ๋งํ•  ์ˆ˜ ์—†๊ณ , ํ™”๋ฉด์€ ๋Š˜ ใ€Œ๋ฐฉ๊ธˆ ์‹คํŒจํ–ˆ๋‹คใ€๋งŒ ๋ณด๊ฒŒ ๋œ๋‹ค.
48
52
  */
49
- function recordReadFailure(ledger, reason, nowMs, stream) {
53
+ function recordReadFailure(ledger, reason, nowMs, stream,
54
+ /**
55
+ * ๋‹ค์Œ์— ๋‹ค์‹œ ๋ฌผ์„ ๋•Œ๊นŒ์ง€ ๋‚จ์€ ์‹œ๊ฐ„(ms) โ€” ์ปค๋„ฅํ„ฐ๊ฐ€ ๋ฌผ๋Ÿฌ์„ฐ์œผ๋ฉด ๊ทธ ๊ฐ’์„ ์ค€๋‹ค (2026-08-27).
56
+ *
57
+ * ์ด๊ฒƒ์ด ์—†์œผ๋ฉด ํ™”๋ฉด์ด ใ€Œ๋‹ฟ์ง€ ๋ชปํ•˜๊ณ  ์žˆ๋‹คใ€๊นŒ์ง€๋งŒ ๋งํ•˜๊ณ , ์‚ฌ๋žŒ์€ **์–ธ์ œ ๋‹ค์‹œ ์‹œ๋„ํ•˜๋Š”์ง€** ์•Œ ์ˆ˜
58
+ * ์—†๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๊ธฐ๋‹ค๋ ค์•ผ ํ•˜๋Š”์ง€ ์†์„ ์จ์•ผ ํ•˜๋Š”์ง€ ํŒ๋‹จํ•˜์ง€ ๋ชปํ•œ๋‹ค.
59
+ *
60
+ * **์—ฐ๋‹ฌ์•„ ํšŸ์ˆ˜์™€ ์‹œ์ž‘ ์‹œ๊ฐ์€ ์ปค๋„ฅํ„ฐ๊ฐ€ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค** โ€” ์ด ํ•จ์ˆ˜๊ฐ€ ์ด๋ฏธ ์„ผ๋‹ค. ๋‘ ๊ณณ์—์„œ ์„ธ๋ฉด
61
+ * ๊ฐˆ๋ผ์ง€๊ณ , ๊ฐˆ๋ผ์ง€๋ฉด ์–ด๋А ์ชฝ์ด ์‚ฌ์‹ค์ธ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค.
62
+ */
63
+ nextRetryMs) {
50
64
  const prev = ledger.readFailure;
51
65
  ledger.readFailure = {
52
66
  consecutive: (prev?.consecutive ?? 0) + 1,
53
67
  sinceMs: prev?.sinceMs ?? nowMs,
54
68
  lastAtMs: nowMs,
55
69
  reason,
56
- ...(stream ? { stream } : {})
70
+ ...(stream ? { stream } : {}),
71
+ ...(Number.isFinite(nextRetryMs) && nextRetryMs > 0 ? { nextRetryMs: nextRetryMs } : {})
57
72
  };
58
73
  }
74
+ /**
75
+ * **๋Š๊ฒผ๋‹ค๊ฐ€ ๋Œ์•„์™”๋‹ค** โ€” ๊ทธ ์‚ฌ์‹ค์„ ๋‚จ๊ธด๋‹ค (2026-08-27).
76
+ *
77
+ * ์‹คํŒจ ๊ธฐ๋ก๋งŒ ์ง€์šฐ๋ฉด ใ€Œ๋Š๊ธด ์ ์ด ์žˆ์—ˆ๋‹คใ€๊ฐ€ ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์ง„๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ฐค์ƒˆ ๋‘ ์‹œ๊ฐ„ ๋Š๊ฒผ๋˜ ์—ฐ๊ฒฐ๊ณผ ํ•œ
78
+ * ๋ฒˆ๋„ ๋Š๊ธฐ์ง€ ์•Š์€ ์—ฐ๊ฒฐ์ด ์•„์นจ์— ๋˜‘๊ฐ™์ด ๋ณด์ธ๋‹ค. ๋ช‡ ๋ฒˆ ์‹คํŒจํ•œ ๋’ค์— ๋Œ์•„์™”๋Š”์ง€, ์–ผ๋งˆ๋‚˜ ๋Š๊ฒผ๋Š”์ง€๋ฅผ ํ•จ๊ป˜
79
+ * ๋‚จ๊ธด๋‹ค.
80
+ */
81
+ function recordRecovered(ledger, afterFailures, downMs, nowMs) {
82
+ if (ledger.readFailure)
83
+ delete ledger.readFailure;
84
+ ledger.lastRecovery = { atMs: nowMs, afterFailures, downMs };
85
+ }
59
86
  /**
60
87
  * ์ฝ๊ธฐ๊ฐ€ ์„ฑ๊ณตํ–ˆ๋‹ค โ€” ๋‹จ์ ˆ ๊ธฐ๋ก์„ ์ง€์šด๋‹ค.
61
88
  *
62
89
  * **๋นˆ ์ฝ๊ธฐ๋„ ์„ฑ๊ณต์ด๋‹ค**: ์›๋ณธ์ด ใ€Œ์ƒˆ ๊ฒƒ์ด ์—†๋‹คใ€๊ณ  ๋‹ตํ•œ ๊ฒƒ์€ ๋‹ฟ์•˜๋‹ค๋Š” ๋œป์ด๋‹ค. ๊ทธ ๋‘˜์„ ๊ฐ™๊ฒŒ ๋‘๋ฉด
63
- * ์กฐ์šฉํ•œ ์›๋ณธ์ด ๋Š๊ธด ์›๋ณธ์œผ๋กœ ๋ณด์ธ๋‹ค(๊ณ ์น˜๋ ค๋˜ ๊ฒƒ์˜ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ ํ‹€๋ฆฐ๋‹ค).
90
+ * ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†๋Š” ์—ฐ๊ฒฐ์ด ๋Š๊ธด ์›๋ณธ์œผ๋กœ ๋ณด์ธ๋‹ค(๊ณ ์น˜๋ ค๋˜ ๊ฒƒ์˜ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ ํ‹€๋ฆฐ๋‹ค).
64
91
  */
65
92
  function clearReadFailure(ledger) {
66
93
  if (ledger.readFailure)
@@ -83,6 +110,48 @@ function recordCursorStall(ledger, reason, nowMs, stream) {
83
110
  ...(stream ? { stream } : {})
84
111
  };
85
112
  }
113
+ /**
114
+ * **์„ธ์šฐ์ง€ ์•Š์€ ๊ฒƒ์„ ์ ๋Š”๋‹ค** โ€” ์ด์œ ๋ณ„ ๋ˆ„์ .
115
+ *
116
+ * ์ฃผ๊ธฐ๋งˆ๋‹ค ๋‹ค์‹œ ์•Œ๋ ค๋„ ์‹œ๋„๋Ÿฌ์›Œ์ง€์ง€ ์•Š๊ฒŒ **์ด์œ ๋กœ ๋ฌถ์–ด ์„ผ๋‹ค**(์ค„์ด ๋Š˜์ง€ ์•Š๋Š”๋‹ค). ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰ ์‹œ๊ฐ์„
117
+ * ํ•จ๊ป˜ ๋“ ๋‹ค โ€” ใ€Œ์ง€๊ธˆ๋„ ๊ทธ๋Ÿฐ๊ฐ€ใ€์™€ ใ€Œํ•œ๋•Œ ๊ทธ๋žฌ๋‚˜ใ€๋Š” ๋‹ค๋ฅธ ์‚ฌ์‹ค์ด๊ณ , ์‹œ๊ฐ ์—†์ด๋Š” ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์—†๋‹ค.
118
+ *
119
+ * ์ด์œ ๊ฐ€ ๊ฐ™๊ณ  ์ˆ˜๊ฐ€ ๋‹ฌ๋ผ์ง€๋ฉด **๋งˆ์ง€๋ง‰ ์ˆ˜๋กœ ๋ฎ๋Š”๋‹ค**: ์ด๊ฒƒ์€ ๋ˆ„์  ์‚ฌ๊ฑด์ด ์•„๋‹ˆ๋ผ **์ง€๊ธˆ ์„ธ์šฐ์ง€ ์•Š์€ ๊ฒƒ์˜
120
+ * ์ˆ˜**๋‹ค. ์ฃผ๊ธฐ๋งˆ๋‹ค 8๊ฑด์ด๋ฉด 8์ด๊ณ , ๋”ํ•ด์„œ 800์ด ๋˜๋ฉด ๊ฑฐ์ง“์ด๋‹ค.
121
+ */
122
+ /**
123
+ * ์–ด๋–ค ๊ธธ๋กœ ๋ช‡ ๊ฑด์ด ๋“ค์–ด์™”๋‚˜ โ€” ๊ฒฝ๋กœ ์ด๋ฆ„์€ ๋ถ€๋ฅด๋Š” ์ชฝ์ด ์ •ํ•œ๋‹ค.
124
+ *
125
+ * ์ด๋ฆ„์„ ์ž์œ  ๋ฌธ์ž์—ด๋กœ ๋‘๋Š” ์ด์œ : ๊ธธ์ด ๋Š˜์–ด๋‚œ๋‹ค(์ง€๊ธˆ์€ ๋ฌผ์–ด๋ณด๊ธฐยท์‹œ๋‚˜๋ฆฌ์˜คยท๋ถ€ํ•˜ ๋„๊ตฌ, ๋‹ค์Œ์€ ์›นํ›…).
126
+ * ์—ด๊ฑฐ๋กœ ๋ชป ๋ฐ•์œผ๋ฉด ์ƒˆ ๊ธธ์ด ์ƒ๊ธธ ๋•Œ ์ด ํŒŒ์ผ์„ ํ•จ๊ป˜ ๊ณ ์ณ์•ผ ํ•˜๊ณ , ์žŠ์œผ๋ฉด ๊ทธ ๊ธธ์ด ํ†ต๊ณ„์—์„œ ์‚ฌ๋ผ์ง„๋‹ค.
127
+ */
128
+ function recordIngestSource(ledger, source, count, nowMs) {
129
+ const key = String(source ?? '').trim() || 'unknown';
130
+ if (count <= 0)
131
+ return;
132
+ const cur = (ledger.bySource[key] ??= { count: 0, lastAtMs: -1 });
133
+ cur.count += count;
134
+ cur.lastAtMs = nowMs;
135
+ }
136
+ /** ๊ฐ™์€ ์‚ฌ์‹ค์ด ๋‘ ๋ฒˆ ์™€์„œ ๋ฒ„๋ฆฐ ์ˆ˜ โ€” ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. */
137
+ function recordDuplicates(ledger, count, nowMs) {
138
+ if (count <= 0)
139
+ return;
140
+ ledger.duplicates += count;
141
+ ledger.lastDuplicateAtMs = nowMs;
142
+ }
143
+ function recordWithheld(ledger, reason, count, nowMs) {
144
+ if (!reason || !(count > 0))
145
+ return;
146
+ const list = (ledger.withheld ??= []);
147
+ const at = list.find(w => w.reason === reason);
148
+ if (at) {
149
+ at.count = count;
150
+ at.lastAtMs = nowMs;
151
+ return;
152
+ }
153
+ list.push({ reason, count, lastAtMs: nowMs });
154
+ }
86
155
  /** ์ฐฝ์„ ๋„˜๊ฒผ์œผ๋ฉด ์ง€์šด๋‹ค โ€” ํ’€๋ฆฐ ์ •์ฒด๊ฐ€ ํ™”๋ฉด์— ๋‚จ์•„ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ๋„ ๊ฑฐ์ง“์ด๋‹ค. */
87
156
  function clearCursorStall(ledger) {
88
157
  if (ledger.cursorStall)
@@ -106,7 +175,7 @@ exports.TREND_LIMIT = 36;
106
175
  * (`unknown bizStep "PUTAWAY_X17"`, `โ€ฆX18`, โ€ฆ) ํ‚ค๊ฐ€ ๋ฌดํ•œํžˆ ๋Š˜์–ด๋‚œ๋‹ค. ์ดˆ๋‹น 1๋งŒ ๊ฑด ๊ทœ๋ชจ์—์„œ ์ด๊ฒƒ์€
107
176
  * ๋ฏธ๋ฏธํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ˆ„์ˆ˜๋‹ค.
108
177
  *
109
- * ๋„˜์น˜๋ฉด `OTHER_REASON` ํ•œ ์นธ์— ๋ชจ์œผ๊ณ  **๋ช‡ ์ข…์„ ์ ‘์—ˆ๋Š”์ง€ ํ•จ๊ป˜ ๋‚ธ๋‹ค**(์กฐ์šฉํžˆ ์ž๋ฅด์ง€ ์•Š๋Š”๋‹ค).
178
+ * ๋„˜์น˜๋ฉด `OTHER_REASON` ํ•œ ์นธ์— ๋ชจ์œผ๊ณ  **๋ช‡ ์ข…์„ ๋ชจ์•˜๋Š”์ง€ ํ•จ๊ป˜ ๋‚ธ๋‹ค**(์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ž๋ฅด์ง€ ์•Š๋Š”๋‹ค).
110
179
  * 30์ข…์ด๋ฉด ๊ณ ์น  ๊ณณ์„ ์ฐพ๊ธฐ์— ์ถฉ๋ถ„ํ•˜๋‹ค โ€” ๋ชฉ๋ก์„ ํ›‘๋Š” ์ผ์ด ์•„๋‹ˆ๋ผ ๋งคํ•‘ ๋ช‡ ์ค„์„ ๊ณ ์น˜๋Š” ์ผ์ด๋‹ค.
111
180
  */
112
181
  exports.REASON_LIMIT = 30;
@@ -142,6 +211,9 @@ function newIngestLedger() {
142
211
  flushes: 0,
143
212
  lastWriteAtMs: -1,
144
213
  lastUndeliveredAtMs: -1,
214
+ bySource: {},
215
+ duplicates: 0,
216
+ lastDuplicateAtMs: -1,
145
217
  byReason: {},
146
218
  reasonsCapped: false,
147
219
  sampledReasons: new Set(),
@@ -230,7 +302,7 @@ undelivered = 0) {
230
302
  for (const it of items) {
231
303
  const raw = reasonKey(it?.errors);
232
304
  const known = raw in ledger.byReason;
233
- /* ์‚ฌ์œ  ์ข…๋ฅ˜ ์ƒํ•œ โ€” ๋„˜์น˜๋ฉด ํ•œ ์นธ์œผ๋กœ ์ ‘๊ณ , **๋‹ฟ์•˜๋‹ค๋Š” ์‚ฌ์‹ค**์„ ํ‘œ์‹œํ•œ๋‹ค. */
305
+ /* ์‚ฌ์œ  ์ข…๋ฅ˜ ์ƒํ•œ โ€” ๋„˜์น˜๋ฉด ํ•œ ์นธ์œผ๋กœ ๋ชจ์œผ๊ณ , **๋‹ฟ์•˜๋‹ค๋Š” ์‚ฌ์‹ค**์„ ํ‘œ์‹œํ•œ๋‹ค. */
234
306
  let key = raw;
235
307
  if (!known && Object.keys(ledger.byReason).length >= exports.REASON_LIMIT) {
236
308
  key = exports.OTHER_REASON;
@@ -286,14 +358,14 @@ function recordJournalWrite(ledger, rows, nowMs, onClosed) {
286
358
  /*
287
359
  * **`lastAtMs` ๋Š” ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.** ๊ทธ ๊ฐ’์€ ใ€Œ๋งˆ์ง€๋ง‰ ์œ ์ž…ใ€์ด๊ณ , ๋Š๊ธด ํ”ผ๋“œ๋ฅผ ๊ฐ€๋ ค๋‚ด๋Š” ๊ทผ๊ฑฐ๋‹ค. ์“ฐ๊ธฐ๋กœ
288
360
  * ๊ฐฑ์‹ ํ•˜๋ฉด ๋ผ์ด๋ธŒ ํŠธ์œˆ์€ ์œ ์ž…์ด ๋Š๊ฒจ๋„ ํ‹ฑ์ด ๋งŒ๋“  ๋ธํƒ€๋ฅผ ๊ณ„์† ์ ์œผ๋ฏ€๋กœ **์˜์›ํžˆ ใ€Œ๋ฐฉ๊ธˆ ๋ฐ›์•˜๋‹คใ€**๊ฐ€ ๋˜๊ณ ,
289
- * ใ€Œ์˜ค๋ž˜ ๋Š๊ธด ํ”ผ๋“œ์— ์˜› ์ฐฝ์œผ๋กœ ์ดˆ๋ก์„ ๋‚ด์ง€ ์•Š๋Š”๋‹คใ€๊ฐ€ ์กฐ์šฉํžˆ ๋ฌด๋ ฅํ•ด์ง„๋‹ค. ์ถ•์„ ๋”ฐ๋กœ ๋“ ๋‹ค.
361
+ * ใ€Œ์˜ค๋ž˜ ๋Š๊ธด ํ”ผ๋“œ์— ์˜› ์ฐฝ์œผ๋กœ ์ดˆ๋ก์„ ๋‚ด์ง€ ์•Š๋Š”๋‹คใ€๊ฐ€ ์˜ค๋ฅ˜ ํ•˜๋‚˜ ์—†์ด ๋ฌด๋ ฅํ•ด์ง„๋‹ค. ์ถ•์„ ๋”ฐ๋กœ ๋“ ๋‹ค.
290
362
  */
291
363
  ledger.lastWriteAtMs = nowMs;
292
364
  }
293
365
  function syncVerdictOf(args) {
294
366
  /*
295
367
  * โ”€โ”€ ๋ฒ„๋ ค์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ๋จผ์ €๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
296
- * ํŠธ์œˆ์ด ๋ฉˆ์ถ”๋ฉด ํ”ผ๋“œ๋Š” ๋‚จ์•„ ๊ณ„์† ๋ ˆ์ฝ”๋“œ๋ฅผ ๋‚˜๋ฅด๊ณ , ์ปค๋„์€ ๊ทธ๊ฒƒ์„ ์กฐ์šฉํžˆ ๋ฒ„๋ฆฐ๋‹ค. ๊ทธ ์ƒํƒœ๋ฅผ ใ€Œํ•ด๋‹น ์—†์Œใ€
368
+ * ํŠธ์œˆ์ด ๋ฉˆ์ถ”๋ฉด ํ”ผ๋“œ๋Š” ๋‚จ์•„ ๊ณ„์† ๋ ˆ์ฝ”๋“œ๋ฅผ ๋‚˜๋ฅด๊ณ , ์ปค๋„์€ ๊ทธ๊ฒƒ์„ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฐ๋‹ค. ๊ทธ ์ƒํƒœ๋ฅผ ใ€Œํ•ด๋‹น ์—†์Œใ€
297
369
  * ์ด๋ผ ๋ถ€๋ฅด๋ฉด **์‚ฌ์‹ค์ด ์‚ฌ๋ผ์ง€๋Š” ๋™์•ˆ ํ™”๋ฉด์ด ์•ˆ์‹ฌ์‹œํ‚จ๋‹ค** โ€” ์‹ค์ œ๋กœ ๊ทธ๋ ‡๊ฒŒ ๋–ด๋‹ค(๋ฉˆ์ถ˜ ํŠธ์œˆ ์˜†์— ใ€Œ150
298
370
  * ํ†ต๊ณผ ยท 100%ใ€). ๊ทธ๋ž˜์„œ ์‹œ๋ฎฌยท๋ฉˆ์ถค ํŒ์ •๋ณด๋‹ค ๋จผ์ € ๋ณธ๋‹ค.
299
371
  *
@@ -331,8 +403,29 @@ function syncVerdictOf(args) {
331
403
  if (args.nowMs - args.lastAtMs > exports.STALE_AFTER_MS)
332
404
  return 'no-traffic';
333
405
  }
334
- if (!args.recent)
335
- return 'unknown';
406
+ /*
407
+ * โ”€โ”€ **๋‹ฟ์•˜๋Š”๋ฐ ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹ค** ๋ฅผ ใ€Œ๋ชจ๋ฅธ๋‹คใ€๋กœ ๋‹ตํ•˜์ง€ ์•Š๋Š”๋‹ค (2026-08-28) โ”€โ”€โ”€โ”€
408
+ *
409
+ * ์ฐฝ์€ **๋‹ค์Œ ์œ ์ž…์ด ์žˆ์„ ๋•Œ** ๋‹ซํžŒ๋‹ค. ๊ทธ๋ž˜์„œ ์˜ค๋Š” ๊ฒƒ์ด ํ•˜๋‚˜๋„ ์—†๋Š” ํŠธ์œˆ์€ ์ฐฝ์ด ์˜์›ํžˆ ๋‹ซํžˆ์ง€ ์•Š๊ณ ,
410
+ * ์ด ์ž๋ฆฌ๊ฐ€ `unknown` ์„ ๋ƒˆ๋‹ค โ€” ๋ถ™์–ด ์žˆ๊ณ  37์ดˆ๋งˆ๋‹ค ์„ฑ๊ณต์ ์œผ๋กœ ์ฝ๋Š” ํŠธ์œˆ์ด ํ™”๋ฉด์—์„œ ใ€Œ๋ชจ๋ฅธ๋‹คใ€์˜€๋‹ค.
411
+ *
412
+ * ๊ทธ๊ฒƒ์ด ๋“œ๋Ÿฌ๋‚œ ๊ณ„๊ธฐ: ์ปค๋„ฅํ„ฐ๊ฐ€ ์žฌ๊ธฐ๋™๋งˆ๋‹ค ์žฌ๊ณ  2,665๊ฑด์„ ๋‹ค์‹œ ๋งํ•˜๋˜ ๊ฒƒ์„ ์—†์•ด๋‹ค. ๊ทธ๋Ÿฌ์ž ์ •์ƒ ์ƒํƒœ์˜
413
+ * ์œ ์ž…์ด 0 ์ด ๋˜์—ˆ๊ณ , **๊ฑด๊ฐ• ์‹ ํ˜ธ๊ฐ€ ๊ทธ ์†Œ์Œ์„ ํƒ€๊ณ  ์žˆ์—ˆ๋‹ค**๋Š” ๊ฒƒ์ด ๋ณด์˜€๋‹ค.
414
+ *
415
+ * โ”€โ”€ ๊ฐ€๋ฅด๋Š” ์‚ฌ์‹ค์€ **๋ฐ›์€ ๊ฒƒ์ด ์žˆ๋‚˜**๋‹ค (์ฒซ ํŒ์„ ๊ณ ์นจ) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
416
+ * ์ฒ˜์Œ์—๋Š” ๋‹ฟ์€ ์‹œ๊ฐ์œผ๋กœ ๊ฐˆ๋ž๋‹ค. ๊ทธ๊ฒƒ์ด ํ‹€๋ ธ๋‹ค โ€” **๋นˆ ์ฝ๊ธฐ๋„ ๋‹ฟ์€ ๊ฒƒ**์ด๋ผ ๊ทธ ์‹œ๊ฐ์€ ์–ธ์ œ๋‚˜ ์„ ๋‹ค.
417
+ * ๊ทธ๋ž˜์„œ ์ฐฝ์„ ์ฑ„์šฐ๋Š” ์ค‘์ธ ํŠธ์œˆ(์ œ์‹œ 200๊ฑด)๊นŒ์ง€ ใ€Œ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹คใ€๋กœ ๋‹ตํ•˜๊ฒŒ ๋๊ณ , ์‹œํ—˜์ด ๊ทธ๊ฒƒ์„ ์žก์•˜๋‹ค.
418
+ * ๋ฐ›๊ณ  ์žˆ๋Š” ์ค‘์ธ๋ฐ ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์€ **์—†๋Š” ๊ฒƒ๊ณผ ๋ชป ๋ณธ ๊ฒƒ์„ ๋’ค์ง‘๋Š” ๊ฒƒ**์ด๋‹ค.
419
+ *
420
+ * ๋‹ฟ์•˜๊ณ  ยท ์ œ์‹œ๊ฐ€ 0 ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹ค `no-traffic`
421
+ * ๋‹ฟ์•˜๊ณ  ยท ์ œ์‹œ๊ฐ€ ์žˆ๋‹ค ๋ฐ›๋Š” ์ค‘์ด๊ณ  ํŒ์ •ํ•  ์ฐฝ์ด ์•„์ง ์—†๋‹ค `unknown`
422
+ * ํ•œ ๋ฒˆ๋„ ๋‹ฟ์ง€ ์•Š์•˜๋‹ค `unknown`(์œ„ `lastAtMs < 0` ๊ฐˆ๋ž˜)
423
+ */
424
+ if (!args.recent) {
425
+ const touched = typeof args.lastAtMs === 'number' && args.lastAtMs >= 0;
426
+ const nothingOffered = (args.offeredTotal ?? 0) <= 0;
427
+ return touched && nothingOffered ? 'no-traffic' : 'unknown';
428
+ }
336
429
  if (args.recent.offered <= 0)
337
430
  return 'no-traffic';
338
431
  const ratio = (args.recent.offered - args.recent.rejected) / args.recent.offered;
@@ -372,6 +465,8 @@ function ingestHealth(ledger, feedState, threshold = exports.HEALTHY_ACCEPTED_RA
372
465
  recent: recentRaw,
373
466
  lastAtMs: ledger?.lastAtMs ?? -1,
374
467
  lastUndeliveredAtMs: ledger?.lastUndeliveredAtMs ?? -1,
468
+ /* ์ฐฝ์ด ๋‹ซํžˆ์ง€ ์•Š์•˜์„ ๋•Œ ใ€Œ๋ฐ›๋Š” ์ค‘ใ€๊ณผ ใ€Œ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹คใ€๋ฅผ ๊ฐ€๋ฅด๋Š” ์‚ฌ์‹ค. */
469
+ offeredTotal: ledger?.offered ?? 0,
375
470
  nowMs,
376
471
  threshold
377
472
  });
@@ -407,12 +502,22 @@ function ingestHealth(ledger, feedState, threshold = exports.HEALTHY_ACCEPTED_RA
407
502
  since: new Date(ledger.readFailure.sinceMs).toISOString(),
408
503
  lastAt: new Date(ledger.readFailure.lastAtMs).toISOString(),
409
504
  reason: ledger.readFailure.reason,
410
- ...(ledger.readFailure.stream ? { stream: ledger.readFailure.stream } : {})
505
+ ...(ledger.readFailure.stream ? { stream: ledger.readFailure.stream } : {}),
506
+ ...(ledger.readFailure.nextRetryMs ? { nextRetryMs: ledger.readFailure.nextRetryMs } : {})
507
+ }
508
+ }
509
+ : {}),
510
+ ...(ledger?.lastRecovery
511
+ ? {
512
+ lastRecovery: {
513
+ at: new Date(ledger.lastRecovery.atMs).toISOString(),
514
+ afterFailures: ledger.lastRecovery.afterFailures,
515
+ downMs: ledger.lastRecovery.downMs
411
516
  }
412
517
  }
413
518
  : {}),
414
519
  /* ์ปค์„œ ์ •์ฒด๋„ **์ด ๊ฐˆ๋ž˜์—์„œ** ๋‚ด๋ณด๋‚ธ๋‹ค โ€” ์œ ์ž…์ด ์—†๋Š” ์žฅ๋ถ€์—์„œ๋„ ์ •์ฒด๋Š” ์‹ค์žฌํ•œ๋‹ค(์ฝ์—ˆ์œผ๋‚˜ ๋ชป
415
- ๋„˜๊ฒผ์œผ๋ฉด ์œ ์ž…์ด 0์ด๋‹ค). ์—ฌ๊ธฐ์„œ ๋–จ์–ด๋œจ๋ฆฌ๋ฉด ๋ฐ”๋กœ ๊ทธ ์ƒํ™ฉ์—์„œ ์กฐ์šฉํ•ด์ง„๋‹ค. */
520
+ ๋„˜๊ฒผ์œผ๋ฉด ์œ ์ž…์ด 0์ด๋‹ค). ์—ฌ๊ธฐ์„œ ๋–จ์–ด๋œจ๋ฆฌ๋ฉด ๋ฐ”๋กœ ๊ทธ ์ƒํ™ฉ์—์„œ ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†์–ด์ง„๋‹ค. */
416
521
  ...(ledger?.cursorStall
417
522
  ? {
418
523
  cursorStall: {
@@ -423,6 +528,19 @@ function ingestHealth(ledger, feedState, threshold = exports.HEALTHY_ACCEPTED_RA
423
528
  ...(ledger.cursorStall.stream ? { stream: ledger.cursorStall.stream } : {})
424
529
  }
425
530
  }
531
+ : {}),
532
+ /*
533
+ * **์„ธ์šฐ์ง€ ์•Š์€ ๊ฒƒ๋„ ๋‚ธ๋‹ค โ€” ์–‘์ชฝ ๊ฐˆ๋ž˜์—์„œ.** ์œ ์ž…์ด ํ•œ ๊ฑด๋„ ์—†๋Š” ์žฅ๋ถ€์—์„œ๋„ ์ด ์‚ฌ์‹ค์€ ์‹ค์žฌํ•œ๋‹ค
534
+ * (์›๋ณธ์ด ๋งํ•˜๋Š”๋ฐ ์ „๋ถ€ ์•ˆ ๋ฐ›๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค). ์—ฌ๊ธฐ์„œ ๋–จ์–ด๋œจ๋ฆฌ๋ฉด ๋ฐ”๋กœ ๊ทธ ์ƒํ™ฉ์—์„œ ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†์–ด์ง„๋‹ค.
535
+ */
536
+ ...(ledger?.withheld?.length
537
+ ? {
538
+ withheld: ledger.withheld.map(w => ({
539
+ reason: w.reason,
540
+ count: w.count,
541
+ lastAt: new Date(w.lastAtMs).toISOString()
542
+ }))
543
+ }
426
544
  : {})
427
545
  };
428
546
  }
@@ -434,6 +552,31 @@ function ingestHealth(ledger, feedState, threshold = exports.HEALTHY_ACCEPTED_RA
434
552
  recent: recentRaw ? toWindowView(recentRaw, lastAnyMs) : null,
435
553
  trend: ledger.trend.map(w => toWindowView(w, lastAnyMs)),
436
554
  trendDropped: ledger.trendDropped,
555
+ /* ์„ธ์šฐ์ง€ ์•Š์€ ๊ฒƒ โ€” ์–‘์ชฝ ๊ฐˆ๋ž˜์—์„œ ๊ฐ™์€ ๋ชจ์–‘์œผ๋กœ ๋‚ธ๋‹ค(ยง`IngestLedger.withheld`). */
556
+ ...(ledger.withheld?.length
557
+ ? {
558
+ withheld: ledger.withheld.map(w => ({
559
+ reason: w.reason,
560
+ count: w.count,
561
+ lastAt: new Date(w.lastAtMs).toISOString()
562
+ }))
563
+ }
564
+ : {}),
565
+ /*
566
+ * **์–ด๋–ค ๊ธธ๋กœ ๋“ค์–ด์˜ค๊ณ  ์žˆ๋‚˜** โ€” ํ™”๋ฉด์ด ใ€Œ์ด ํŠธ์œˆ์€ ๋ฌด์—‡์„ ํ†ตํ•ด ๊ฐ’์„ ๋ฐ›๋‚˜ใ€๋ฅผ ๋งํ•  ์ˆ˜ ์žˆ๊ฒŒ.
567
+ * ๋น„์–ด ์žˆ์œผ๋ฉด ์นธ์„ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค: ์•„์ง ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๋ฐ›์€ ํŠธ์œˆ๊ณผ ใ€Œ๊ธธ์ด ์—†๋‹คใ€๋ฅผ ๊ตฌ๋ณ„ํ•œ๋‹ค.
568
+ */
569
+ ...(Object.keys(ledger.bySource).length
570
+ ? {
571
+ sources: Object.entries(ledger.bySource)
572
+ .map(([source, v]) => ({ source, count: v.count, lastAt: new Date(v.lastAtMs).toISOString() }))
573
+ .sort((a, b) => b.count - a.count)
574
+ }
575
+ : {}),
576
+ /* ๊ฐ™์€ ์‚ฌ์‹ค์ด ๋‘ ๋ฒˆ ์™€์„œ ๋ฒ„๋ฆฐ ์ˆ˜ โ€” 0 ์ด๋ฉด ์นธ์„ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค(๋ฒ„๋ฆฐ ์ ์ด ์—†๋‹ค๋Š” ๋œป์ด๋‹ค). */
577
+ ...(ledger.duplicates
578
+ ? { duplicates: { count: ledger.duplicates, lastAt: new Date(ledger.lastDuplicateAtMs).toISOString() } }
579
+ : {}),
437
580
  total: {
438
581
  offered: ledger.offered,
439
582
  accepted,
@@ -460,7 +603,18 @@ function ingestHealth(ledger, feedState, threshold = exports.HEALTHY_ACCEPTED_RA
460
603
  since: new Date(ledger.readFailure.sinceMs).toISOString(),
461
604
  lastAt: new Date(ledger.readFailure.lastAtMs).toISOString(),
462
605
  reason: ledger.readFailure.reason,
463
- ...(ledger.readFailure.stream ? { stream: ledger.readFailure.stream } : {})
606
+ ...(ledger.readFailure.stream ? { stream: ledger.readFailure.stream } : {}),
607
+ ...(ledger.readFailure.nextRetryMs ? { nextRetryMs: ledger.readFailure.nextRetryMs } : {})
608
+ }
609
+ }
610
+ : {}),
611
+ /* ๋Š๊ฒผ๋‹ค๊ฐ€ ๋Œ์•„์˜จ ๊ฒƒ โ€” ์ง€๊ธˆ ๋‹ฟ๊ณ  ์žˆ์–ด๋„ ๋‚จ๊ธด๋‹ค(๊ทธ๊ฒƒ์ด ์—†์œผ๋ฉด ๋ฐค์ƒˆ์˜ ๋‹จ์ ˆ์ด ์‚ฌ๋ผ์ง„๋‹ค). */
612
+ ...(ledger.lastRecovery
613
+ ? {
614
+ lastRecovery: {
615
+ at: new Date(ledger.lastRecovery.atMs).toISOString(),
616
+ afterFailures: ledger.lastRecovery.afterFailures,
617
+ downMs: ledger.lastRecovery.downMs
464
618
  }
465
619
  }
466
620
  : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"ingest-health.js","sourceRoot":"","sources":["../../server/engine/ingest-health.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * ๋™๊ธฐํ™” ๊ฑด๊ฐ• โ€” **์ด ํŠธ์œˆ์ด ํ˜„์žฅ๊ณผ ๋งž์ถฐ์ง€๊ณ  ์žˆ๋‚˜.** ์ˆœ์ˆ˜(import ์—†์Œ).\n *\n * โ”€โ”€ ํ•œ ์งˆ๋ฌธ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ํ™”๋ฉด์—์„œ ๊ฐ’์ด ๋ณ€ํ•˜์ง€ ์•Š์„ ๋•Œ ์›์ธ์ด ๋„ท์ธ๋ฐ, ์ง€๊ธˆ๊นŒ์ง€ **๋„ท์ด ๋˜‘๊ฐ™์ด ๋ณด์˜€๋‹ค.**\n * โ‘  ํ”ผ๋“œ๊ฐ€ ๋ถ™์ง€ ์•Š์•˜๋‹ค โ‘ก ๋ถ™์—ˆ๋Š”๋ฐ ์•„๋ฌด๊ฒƒ๋„ ์˜ค์ง€ ์•Š๋Š”๋‹ค\n * โ‘ข ์˜ค๋Š”๋ฐ ์ „๋ถ€ ๊ฑฐ๋ถ€๋œ๋‹ค โ‘ฃ ์ •์ƒ์ธ๋ฐ ํ˜„์žฅ์ด ์กฐ์šฉํ•˜๋‹ค\n * โ‘ข์€ ํŠนํžˆ ๋‚˜์˜๋‹ค โ€” ์ปค๋„ฅํ„ฐ์˜ ๋งคํ•‘์ด ํ•œ ๊ธ€์ž ํ‹€๋ฆฌ๋ฉด ํŠธ์œˆ์€ ใ€Œ๋นˆ ๊ณต์žฅใ€์ด ๋˜๊ณ , ๊ทธ ์‚ฌ์‹ค์ด ์„œ๋ฒ„ ๋กœ๊ทธ\n * ํ•œ ์ค„์—๋งŒ ๋‚จ์•˜๋‹ค. ํ˜„์žฅ์— ๋‚˜๊ฐ„ ์‚ฌ๋žŒ์€ ๋กœ๊ทธ๋ฅผ ๋’ค์ ธ์•ผ ํ–ˆ๋‹ค.\n *\n * โ”€โ”€ ์™œ ์ €๋„๋กœ๋Š” ๋‹ตํ•  ์ˆ˜ ์—†๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * **๊ฑฐ๋ถ€๋œ ๊ฒƒ์€ ์ €๋„์— ์—†๋‹ค** โ€” ๊ฑฐ๋ถ€์˜ ๋œป์ด ๊ทธ๊ฒƒ์ด๋‹ค. ์ €๋„์„ ์ ‘์œผ๋ฉด ํ†ต๊ณผํ•œ ์ชฝ๋งŒ ๋‚˜์˜จ๋‹ค. ๊ฑฐ๋ถ€์˜ ์ถ”์ด๋Š”\n * ์„ธ๋Š” ์ž๋ฆฌ๋ฅผ ๋”ฐ๋กœ ๋‘์–ด์•ผ ๋‚˜์˜จ๋‹ค. ์ด ์žฅ๋ถ€๊ฐ€ ๊ทธ ์ž๋ฆฌ๋‹ค.\n *\n * โ”€โ”€ ์™œ ๊ฑด๋งˆ๋‹ค ์ €์žฅํ•˜์ง€ ์•Š๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ๋ผ์ด๋ธŒ ๊ณ„์ธก์€ ์ดˆ๋‹น ์—ฌ๋Ÿฌ ๊ฑด์ด๋‹ค. ๊ฑฐ๋ถ€๋งˆ๋‹ค ํ–‰์„ ์“ฐ๋ฉด ๊ทธ ์ž์ฒด๊ฐ€ ๋ถ€ํ•˜๊ฐ€ ๋œ๋‹ค(`reference-live.ts` ๊ฐ€\n * ์ด๋ฏธ ๊ทธ๋ ‡๊ฒŒ ์ ์–ด ๋‘์—ˆ๋‹ค). ๊ทธ๋ฆฌ๊ณ  ์ด ํŒจํ‚ค์ง€๋Š” **`integration-base` ๋ฅผ ์•Œ์ง€ ์•Š๋Š”๋‹ค**(์ด์‹์„ฑ ๊ทœ์•ฝ) โ€”\n * ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒฝ๋กœ๊ฐ€ ์“ฐ๋Š” dead-letter ๋ฅผ ์—ฌ๊ธฐ์„œ ๋ถ€๋ฅผ ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ž˜์„œ **์ง‘๊ณ„ํ•œ๋‹ค.**\n *\n * โ”€โ”€ ํ†ต๊ณผ์™€ ๊ฑฐ๋ถ€๋ฅผ ํ•จ๊ป˜ ์„ผ๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ๊ฑฐ๋ถ€๋งŒ ๊ทธ๋ฆฌ๋ฉด ใ€Œ์œ ์ž… ์—†์Œใ€๊ณผ ใ€Œ์ •์ƒใ€์ด **๋‘˜ ๋‹ค ๊ฑฐ๋ถ€ 0** ์œผ๋กœ ๊ฐ™์•„ ๋ณด์ธ๋‹ค. ์ œ์‹œ ์ˆ˜๊ฐ€ ์žˆ์–ด์•ผ ๊ฐˆ๋ฆฐ๋‹ค.\n *\n * โ”€โ”€ ์ง€ํ‚ค๋Š” ๊ทœ์œจ ๋„ท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * โ‘  **๋‹ซํžŒ ์ฐฝ๋งŒ ใ€Œ์ตœ๊ทผใ€์ด๋‹ค** โ€” ์ฑ„์šฐ๋Š” ์ค‘์ธ ์ฐฝ์€ ๊ณผ์†Œ ์ง‘๊ณ„๋‹ค(๋ถ€ํ•˜ ๊ณ„๊ธฐํŒ์—์„œ ๊ฒช์—ˆ๋‹ค).\n * โ‘ก **0 ๊ณผ ใ€Œ์—†์Œใ€์„ ๊ฐ€๋ฅธ๋‹ค** โ€” ์•ˆ ๋ฐ›์€ ๊ฒƒ์€ `null`, ๋ฐ›์•˜์ง€๋งŒ ๊ฑฐ๋ถ€ 0 ์€ `0`.\n * โ‘ข **์กฐ์šฉํžˆ ์ž๋ฅด์ง€ ์•Š๋Š”๋‹ค** โ€” ํ‘œ๋ณธยท์ถ”์ด ์ƒํ•œ์—์„œ ๋ฐ€๋ ค๋‚œ ์ˆ˜๋ฅผ ํ•จ๊ป˜ ๋‚ธ๋‹ค.\n * โ‘ฃ **ํ†ต๊ณผ์œจ์— 1 ์„ ํ•จ๋ถ€๋กœ ์“ฐ์ง€ ์•Š๋Š”๋‹ค** โ€” 1,000๊ฑด ์ค‘ 1๊ฑด ๊ฑฐ๋ถ€๋Š” 0.999 ๋‹ค.\n */\n\n/** ์ถ”์ด ์ฐฝ ํ•˜๋‚˜ โ€” ์ œ์‹œยทํ†ต๊ณผยท๊ฑฐ๋ถ€ยท๋ฏธ์ „๋‹ฌ, ๊ทธ๋ฆฌ๊ณ  **์ ํžŒ ๊ฒƒ**์„ ํ•จ๊ป˜ ๋“ ๋‹ค. */\nexport interface IngestWindow {\n /** ์ฐฝ์ด ์—ด๋ฆฐ ์‹œ๊ฐ(ms). */\n startMs: number\n /** ์ฐฝ์ด ๋‹ซํžŒ ์‹œ๊ฐ(ms). ์•„์ง ์ฑ„์šฐ๋Š” ์ค‘์ด๋ฉด `-1`. */\n endMs: number\n /** ์ด ์ฐฝ์— ์ œ์‹œ๋œ ๋ ˆ์ฝ”๋“œ ์ˆ˜(๊ฑฐ๋ถ€ ์—ฌ๋ถ€ ๋ฌด๊ด€). */\n offered: number\n /** ๊ทธ์ค‘ **๋งคํ•‘์ด ๊ฑฐ๋ถ€ํ•œ** ์ˆ˜ โ€” ์ฒ˜๋ฐฉ์€ ๋งคํ•‘์„ ๊ณ ์น˜๋Š” ๊ฒƒ์ด๋‹ค. */\n rejected: number\n /**\n * ๋งคํ•‘์€ ํ†ต๊ณผํ–ˆ์ง€๋งŒ **ํŠธ์œˆ์— ๋‹ฟ์ง€ ๋ชปํ•œ** ์ˆ˜ โ€” ์ฒ˜๋ฐฉ์€ ํŠธ์œˆ์„ ์„ธ์šฐ๋Š” ๊ฒƒ์ด๋‹ค.\n *\n * โ”€โ”€ ์™œ ๊ฑฐ๋ถ€์™€ ๋”ฐ๋กœ ์„ธ๋‚˜ (2026-08-20์— ๊ฒช์—ˆ๋‹ค) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ํŠธ์œˆ์ด ๋ผ์ด๋ธŒ๋กœ ๋Œ์ง€ ์•Š์œผ๋ฉด ์ปค๋„์ด ๋ ˆ์ฝ”๋“œ๋ฅผ **์กฐ์šฉํžˆ ๋ฒ„๋ฆฐ๋‹ค**. ๊ทธ๋Ÿฐ๋ฐ ์žฅ๋ถ€๋Š” ๊ทธ๊ฒƒ์„ ใ€Œํ†ต๊ณผใ€๋กœ ์…Œ๋‹ค.\n * ํ™”๋ฉด์ด ใ€Œ150 ๋“ค์–ด์˜ด ยท 150 ํ†ต๊ณผ ยท 100%ใ€๋ผ๊ณ  ๋งํ•˜๋Š” ๋™์•ˆ 150๊ฑด์€ ๋ฐ”๋‹ฅ์— ๋ฒ„๋ ค์ง€๊ณ  ์žˆ์—ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์ด\n * ๋ง‰์œผ๋ ค๋˜ ๋ฐ”๋กœ ๊ทธ๊ฒƒ์„ ์ด ๊ธฐ๋Šฅ์ด ์ €์ง€๋ฅธ ์…ˆ์ด๋‹ค.\n *\n * ๊ฑฐ๋ถ€์— ํ•ฉ์น˜๋Š” ๊ฒƒ๋„ ๋‹ต์ด ์•„๋‹ˆ๋‹ค โ€” ๊ฑฐ๋ถ€๋Š” ใ€Œ๋งคํ•‘์„ ๊ณ ์ณ๋ผใ€๋Š” ๋ง์ด๊ณ , ์—ฌ๊ธฐ์„œ ํ•„์š”ํ•œ ๋ง์€ ใ€ŒํŠธ์œˆ์ด ์•ˆ\n * ๋Œ๊ณ  ์žˆ๋‹คใ€๋‹ค. **์ฒ˜๋ฐฉ์ด ๋‹ค๋ฅด๋ฉด ๋‹ค๋ฅธ ์ˆ˜๋กœ ์„ผ๋‹ค.**\n */\n undelivered: number\n /**\n * ์ด ์ฐฝ์— **์ €๋„์— ์ ํžŒ ํ–‰** ์ˆ˜ โ€” ใ€Œ๋ฌด์—‡์ด ๋‚จ์•˜๋‚˜ใ€.\n *\n * ์œ ์ž…๊ณผ ๋‹ค๋ฅธ ์ถ•์ด๋‹ค: ์‹œ๋ฎฌ ํŠธ์œˆ์€ ๋ฐ›๋Š” ๊ฒƒ ์—†์ด ์ž๊ธฐ๊ฐ€ ๋‚ธ ์‚ฌ์‹ค์„ ์ ๋Š”๋‹ค(๊ทธ๋ž˜์„œ `offered` ๋Š” 0 ์ด๊ณ \n * `rows` ๋Š” 0 ์ด ์•„๋‹ˆ๋‹ค). ์ด ์นธ์ด ์—†์œผ๋ฉด ๊ทธ ํŠธ์œˆ์˜ ํ™œ๋™์€ ์–ด๋А ์ถ”์ด์—๋„ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š๋Š”๋‹ค.\n */\n rows: number\n /**\n * ๊ทธ๊ฒƒ์„ ์ ๊ธฐ ์œ„ํ•ด **DB ๋ฅผ ๋‘๋“œ๋ฆฐ ํšŸ์ˆ˜**(flush) โ€” ใ€Œ์šฐ๋ฆฌ๊ฐ€ ๋ช‡ ๋ฒˆ ๊ฐ”๋‚˜ใ€.\n *\n * ํ–‰ ์ˆ˜์™€ ๋”ฐ๋กœ ์„ธ๋Š” ์ด์œ : 2026-08-20 ์— ๊ณ ์นœ ๊ฒƒ์ด ๋ฐ”๋กœ ์ด ๊ฐ’์ด๋‹ค(ํ–‰๋งˆ๋‹ค ํ•œ ๋ฒˆ โ†’ ์ฐฝ๋‹น ํ•œ ๋ฒˆ). ํ–‰๋งŒ\n * ์„ธ๋ฉด ๊ทธ ๊ฐœ์„ ๋„, ๋˜๋Œ์•„๊ฐ€๋Š” ๊ฒƒ๋„ ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค.\n */\n flushes: number\n}\n\n/** ๊ฑฐ๋ถ€ ํ‘œ๋ณธ ํ•˜๋‚˜ โ€” ๋งคํ•‘์„ ๊ณ ์น˜๋ ค๋ฉด ์‹ค์ œ ๋ชจ์–‘์„ ๋ด์•ผ ํ•œ๋‹ค. */\nexport interface RejectSample {\n atMs: number\n /** ์ปค๋„์ด ๋‚ธ ์‚ฌ์œ . ์šฐ๋ฆฌ๊ฐ€ ๋‹ค์‹œ ์“ฐ์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์˜ฎ๊ธด๋‹ค. */\n errors: string[]\n /** ๋ง‰ํžŒ ๋ ˆ์ฝ”๋“œ์˜ ์ง๋ ฌํ™”. ๊ธธ๋ฉด ์ž๋ฅด๊ณ , ์ž˜๋ž๋‹ค๊ณ  ํ‘œ์‹œํ•œ๋‹ค. */\n record: string\n recordTruncated?: boolean\n}\n\nexport interface IngestLedger {\n /** ์ง€๊ธˆ ์ฑ„์šฐ๋Š” ์ค‘์ธ ์ฐฝ. `startMs < 0` ์ด๋ฉด ์•„์ง ์•„๋ฌด๊ฒƒ๋„ ๋ฐ›์ง€ ์•Š์•˜๋‹ค. */\n window: IngestWindow\n /** ๋‹ซํžŒ ์ฐฝ๋“ค โ€” ์˜ค๋ž˜๋œ ๊ฒƒ์ด ์•ž. ์ƒํ•œ์„ ๋„˜์œผ๋ฉด ์•ž์—์„œ ๋ฐ€๋ ค๋‚œ๋‹ค. */\n trend: IngestWindow[]\n /** ์ถ”์ด์—์„œ ๋ฐ€๋ ค๋‚œ ์ฐฝ ์ˆ˜. */\n trendDropped: number\n /** ๋ˆ„์  ์ œ์‹œยท๊ฑฐ๋ถ€ยท๋ฏธ์ „๋‹ฌ. */\n offered: number\n rejected: number\n undelivered: number\n /** ๋ˆ„์  โ€” ์ ํžŒ ํ–‰๊ณผ ๋‘๋“œ๋ฆฐ ํšŸ์ˆ˜. */\n rows: number\n flushes: number\n /**\n * ๋งˆ์ง€๋ง‰์œผ๋กœ **์ ์€** ์‹œ๊ฐ. ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `-1`.\n *\n * ์œ ์ž… ์‹œ๊ฐ(`lastAtMs`)๊ณผ ๊ฐˆ๋ผ ๋‘๋Š” ์ด์œ ๋Š” ์œ„ `recordJournalWrite` ์ฃผ์„์— ์žˆ๋‹ค โ€” ์„ž์œผ๋ฉด ๋Š๊ธด ํ”ผ๋“œ๊ฐ€\n * ์ดˆ๋ก์œผ๋กœ ๋ณด์ธ๋‹ค.\n */\n lastWriteAtMs: number\n /**\n * ๋งˆ์ง€๋ง‰์œผ๋กœ **๋ฒ„๋ ค์ง„** ์‹œ๊ฐ(ms). ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `-1`.\n *\n * ์ฐฝ๊ณผ ๋”ฐ๋กœ ๋‘”๋‹ค: ๋ฏธ์ „๋‹ฌ์€ ์ฐฝ์ด ๋‹ซํžˆ๊ธฐ ์ „์— ์•Œ์•„์•ผ ํ•œ๋‹ค(๊ทธ ์‚ฌ์ด์—๋„ ์‚ฌ์‹ค์€ ๊ณ„์† ์‚ฌ๋ผ์ง€๊ณ  ์žˆ๋‹ค).\n */\n lastUndeliveredAtMs: number\n /** ์‚ฌ์œ ๋ณ„ ๋ˆ„์  ๊ฑด์ˆ˜์™€ **๋งˆ์ง€๋ง‰์œผ๋กœ ๊ทธ ์‚ฌ์œ ๊ฐ€ ๋‚œ ์‹œ๊ฐ** โ€” ๊ณ ์นœ ์‚ฌ์œ ๋Š” ์‹œ๊ฐ์ด ๋ฉˆ์ถ˜๋‹ค. */\n byReason: Record<string, { count: number; lastAtMs: number }>\n /**\n * ์‚ฌ์œ  ์ข…๋ฅ˜๊ฐ€ ์ƒํ•œ์— ๋‹ฟ์•˜๋‚˜.\n *\n * **์ ‘ํžŒ ์ข…๋ฅ˜ ์ˆ˜๋Š” ์„ธ์ง€ ์•Š๋Š”๋‹ค** โ€” ๊ทธ๊ฒƒ์„ ์„ธ๋ ค๋ฉด ์ด๋ฏธ ๋ณธ ์‚ฌ์œ ๋ฅผ ๋ชจ๋‘ ๊ธฐ์–ตํ•ด์•ผ ํ•˜๊ณ , ๊ทธ ์ง‘ํ•ฉ์ด\n * ๋ฐ”๋กœ ์ด ์ƒํ•œ์œผ๋กœ ๋ง‰์œผ๋ ค๋˜ ๋ฌดํ•œํ•œ ์ง‘ํ•ฉ์ด๋‹ค. ์ ‘ํžŒ **๊ฑด์ˆ˜**๋Š” `byReason[OTHER_REASON].count` ๊ฐ€ ๋‹ตํ•œ๋‹ค.\n */\n reasonsCapped: boolean\n /** ํ‘œ๋ณธ์„ ์ด๋ฏธ ๋œฌ ์‚ฌ์œ ๋“ค โ€” ๊ฐ™์€ ์‚ฌ์œ ๋ฅผ ๋‹ค์‹œ ์ง๋ ฌํ™”ํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•œ ์ƒ‰์ธ(O(1) ์กฐํšŒ). */\n sampledReasons: Set<string>\n /** ์ตœ๊ทผ ๊ฑฐ๋ถ€ ํ‘œ๋ณธ. */\n samples: RejectSample[]\n /** ํ‘œ๋ณธ์—์„œ ๋ฐ€๋ ค๋‚œ ๊ฑฐ๋ถ€ ๊ฑด์ˆ˜. */\n sampleDropped: number\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฌด์—‡์ด๋“  ๋ฐ›์€ ์‹œ๊ฐ(ms). **ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `-1`** โ€” 0 ์œผ๋กœ ๋‘๋ฉด ์‹œ๊ฐ 0 ๊ณผ ๊ตฌ๋ณ„๋˜์ง€ ์•Š๋Š”๋‹ค. */\n lastAtMs: number\n /**\n * **์›๋ณธ์— ๋‹ฟ์ง€ ๋ชปํ•œ ์‚ฌ์‹ค** (2026-08-23) โ€” ใ€Œ๋ฐ›์€ ๊ฒƒ์ด ์—†๋‹คใ€์™€ **๋‹ค๋ฅธ ์‚ฌ์‹ค**์ด๋‹ค.\n *\n * โ”€โ”€ ๋ฌด์—‡์ด ์กฐ์šฉํ–ˆ๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์‹ค ์›๋ณธ์ด ๋Š๊ฒผ์„ ๋•Œ(์ ‘์† ์‹œ๊ฐ„ ์ดˆ๊ณผ) ํŠธ์œˆ์˜ **์กฐํšŒ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ ์–ด๋””์—๋„** ๊ทธ ์‚ฌ์‹ค์ด ์—†์—ˆ๋‹ค.\n * `recordIngest` ๋Š” `onRecords` ๊ฐ€ ๋ถˆ๋ฆด ๋•Œ๋งŒ ๋„๋‹ฌํ•˜๊ณ , ์ฝ๊ธฐ๊ฐ€ ์‹คํŒจํ•˜๋ฉด ๊ทธ ๋ฌธ์— ์˜ค์ง€ ์•Š๋Š”๋‹ค.\n * `TwinReference.lastError` ๋Š” ๋งˆ์Šคํ„ฐ ๋™๊ธฐยท์ ‘์† ์‹œํ—˜์—๋งŒ ์ ํžŒ๋‹ค.\n *\n * ๊ทธ๋ž˜์„œ ํ™”๋ฉด์ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ใ€Œ์ƒˆ ์‚ฌ์‹ค์ด ์—†๋‹คใ€๋ฟ์ด์—ˆ๊ณ , ๊ทธ๊ฒƒ์€ **ใ€Œ์›๋ณธ์ด ์กฐ์šฉํ•˜๋‹คใ€์™€ ๊ตฌ๋ณ„๋˜์ง€\n * ์•Š๋Š”๋‹ค.** ์‹ค์ฆ ๋ฐ๋ชจ ์ค‘์— ์›๋ณธ์ด ๋Š๊ธฐ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์›์ธ์„ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค.\n *\n * โ”€โ”€ ์™œ ใ€Œ๋ช‡ ๋ฒˆใ€๊ณผ ใ€Œ์–ธ์ œ๋ถ€ํ„ฐใ€๋ฅผ ํ•จ๊ป˜ ๋‘๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ํ•œ ๋ฒˆ ์‹คํŒจํ•œ ๊ฒƒ๊ณผ 20๋ถ„์งธ ๋ชป ์ฝ๋Š” ๊ฒƒ์€ ๋‹ค๋ฅธ ์‚ฌ์‹ค์ด๋‹ค. ๊ทธ ๊ตฌ๋ณ„์ด ์—†์œผ๋ฉด ํ™”๋ฉด์ด ์ˆœ๊ฐ„์˜ ์‹คํŒจ์— ๋†€๋ผ๊ฑฐ๋‚˜\n * ๊ณ„์†๋œ ๋‹จ์ ˆ์„ ๋Œ€์ˆ˜๋กญ์ง€ ์•Š๊ฒŒ ๋งํ•œ๋‹ค. ๊ทธ๋ž˜์„œ **์—ฐ์† ์‹คํŒจ ์ˆ˜**์™€ **์ฒ˜์Œ ์‹คํŒจํ•œ ์‹œ๊ฐ**์„ ํ•จ๊ป˜ ๋“ ๋‹ค.\n *\n * ์„ฑ๊ณตํ•˜๋ฉด ์ง€์šด๋‹ค โ€” ๊ณ ์ณ์ง„ ๋‹จ์ ˆ์ด ํ™”๋ฉด์— ๋‚จ์•„ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ๋„ ๊ฑฐ์ง“์ด๋‹ค.\n */\n readFailure?: {\n /** ์—ฐ์† ์‹คํŒจ ์ˆ˜ โ€” ์„ฑ๊ณต ํ•œ ๋ฒˆ์— 0 ์œผ๋กœ ๋Œ์•„๊ฐ„๋‹ค. */\n consecutive: number\n /** ์ด ๋‹จ์ ˆ์ด **์ฒ˜์Œ** ์‹œ์ž‘๋œ ์‹œ๊ฐ(ms). ์—ฐ์†์ด ๋Š๊ธฐ๋ฉด ๋‹ค์‹œ ์ •ํ•ด์ง„๋‹ค. */\n sinceMs: number\n /** ๋งˆ์ง€๋ง‰ ์‹คํŒจ ์‹œ๊ฐ(ms). */\n lastAtMs: number\n /** ๋งˆ์ง€๋ง‰ ์‹คํŒจ์˜ ์‚ฌ์œ  โ€” ์‚ฌ๋žŒ์ด ์ฝ๋Š” ํ•œ ์ค„(์›๋ณธ์ด ์ค€ ๋ง ๊ทธ๋Œ€๋กœ). */\n reason: string\n /** ์–ด๋А ํ๋ฆ„์—์„œ ๋‚ฌ๋‚˜(์–ด๋Œ‘ํ„ฐ๊ฐ€ ๋งํ•ด ์ฃผ๋ฉด). ์›๋ณธ๋งˆ๋‹ค ํ๋ฆ„์ด ๋‹ค๋ฅด๋ฏ€๋กœ ์—ด๋ ค ์žˆ๋‹ค. */\n stream?: string\n }\n /**\n * **์ฝ์—ˆ๋Š”๋ฐ ์ฐฝ์„ ๋„˜๊ธธ ์ˆ˜ ์—†๋‹ค** (2026-08-24) โ€” ์œ„์™€ **๋‹ค๋ฅธ ์‚ฌ์‹ค**์ด๋‹ค.\n *\n * โ”€โ”€ ์™œ ๊ฐˆ๋ž๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์ด ์นธ์„ ๋งŒ๋“ค๊ธฐ ์ „์—๋Š” ๋‘ ์‚ฌ์‹ค์ด ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ๋‚˜๊ฐ”๋‹ค.\n *\n * readFailure ์›๋ณธ์— ๋‹ฟ์ง€ ๋ชปํ–ˆ๋‹ค โ†’ **๊ธฐ๋‹ค๋ฆฌ๋ฉด ํ’€๋ฆฐ๋‹ค**\n * cursorStall ์ฝ์—ˆ๋Š”๋ฐ ์ปค์„œ๊ฐ€ ๋ชป ๋„˜์–ด๊ฐ„๋‹ค โ†’ **๊ธฐ๋‹ค๋ ค๋„ ์•ˆ ํ’€๋ฆฐ๋‹ค**\n *\n * ๋‘˜์งธ๋Š” ์ฝ๊ธฐ๊ฐ€ ์„ฑ๊ณตํ•œ ์‹คํŒจ๋‹ค: ํ•œ ์‹œ๊ฐ์— ํ•œ ํŽ˜์ด์ง€๋ณด๋‹ค ๋งŽ์€ ํ–‰์ด ๋ชฐ๋ ค ์ปค์„œ๋ฅผ ๊ทธ ์‹œ๊ฐ ๋ฐ–์œผ๋กœ ์˜ฎ๊ธธ ์ˆ˜\n * ์—†๋‹ค. ํ™”๋ฉด์ด ใ€Œ์›๋ณธ์— ๋‹ฟ์ง€ ๋ชปํ•œ๋‹คใ€๊ณ  ๋งํ•˜๋ฉด ์‚ฌ๋žŒ์€ ์›๋ณธ์„ ์˜์‹ฌํ•˜๊ณ  ๊ธฐ๋‹ค๋ฆฌ๋Š”๋ฐ, ํ•„์š”ํ•œ ์กฐ์น˜๋Š”\n * ํŽ˜์ด์ง€๋ฅผ ํ‚ค์šฐ๊ฑฐ๋‚˜ ๊ฐ™์€ ์‹œ๊ฐ ์•ˆ์—์„œ ์ˆœ์„œ๋ฅผ ์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. **์กฐ์น˜๊ฐ€ ๋ฐ˜๋Œ€์ธ ๋‘ ์‚ฌ์‹ค์„ ํ•œ ์ด๋ฆ„์œผ๋กœ\n * ๋ถ€๋ฅด๋ฉด ๊ทธ ์ด๋ฆ„์€ ์ •๋ณด๊ฐ€ ์•„๋‹ˆ๋ผ ์˜คํ•ด๋‹ค.**\n *\n * ๋ชจ์–‘์€ ์œ„์™€ ๊ฐ™๊ฒŒ ๋‘”๋‹ค โ€” ใ€Œ๋ช‡ ๋ฒˆใ€๊ณผ ใ€Œ์–ธ์ œ๋ถ€ํ„ฐใ€๊ฐ€ ์—ฌ๊ธฐ์„œ๋„ ๊ฐ™์€ ๊ฐ’์„ ํ•œ๋‹ค(ํ•œ ๋ฒˆ์˜ ๋ฐ€๋ฆผ๊ณผ ์‚ฌํ˜์งธ\n * ๋ชป ๋„˜์–ด๊ฐ€๋Š” ๊ฒƒ์€ ๋‹ค๋ฅด๋‹ค). ๋„˜์–ด๊ฐ€๋ฉด ์ง€์šด๋‹ค.\n */\n cursorStall?: {\n /** ์—ฐ์† ์ •์ฒด ์ˆ˜ โ€” ๋„˜์–ด๊ฐ€๋ฉด 0 ์œผ๋กœ ๋Œ์•„๊ฐ„๋‹ค. */\n consecutive: number\n /** ์ด ์ •์ฒด๊ฐ€ **์ฒ˜์Œ** ์‹œ์ž‘๋œ ์‹œ๊ฐ(ms). */\n sinceMs: number\n /** ๋งˆ์ง€๋ง‰ ์ •์ฒด ์‹œ๊ฐ(ms). */\n lastAtMs: number\n /** ์‚ฌ์œ  โ€” ์‚ฌ๋žŒ์ด ์ฝ๋Š” ํ•œ ์ค„(์–ด๋А ์‹œ๊ฐ์—์„œ ๋ง‰ํ˜”๋‚˜๊นŒ์ง€ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ๋งํ•ด ์ฃผ๋ฉด ์ข‹๋‹ค). */\n reason: string\n /** ์–ด๋А ํ๋ฆ„์ธ๊ฐ€. ๋ฐ€๋„๊ฐ€ ๋†’์€ ํ‘œ๋Š” ์›๋ณธ๋งˆ๋‹ค ๋‹ค๋ฅด๋ฏ€๋กœ ์ด ์ด๋ฆ„์ด ์กฐ์น˜์˜ ์ ˆ๋ฐ˜์ด๋‹ค. */\n stream?: string\n }\n}\n\n/**\n * ์ฝ๊ธฐ ์‹คํŒจ๋ฅผ ์žฅ๋ถ€์— ์ ๋Š”๋‹ค โ€” **๋ฐ›์€ ๊ฒƒ์ด ์—†๋Š” ๊ฒƒ๊ณผ ๊ฐ€๋ฅธ๋‹ค.**\n *\n * ์—ฐ์† ์‹คํŒจ์˜ ์‹œ์ž‘ ์‹œ๊ฐ์„ ์ง€ํ‚ค๋Š” ๊ฒƒ์ด ์ด ํ•จ์ˆ˜์˜ ์š”์ ์ด๋‹ค: ๋งค๋ฒˆ ์ƒˆ๋กœ ์ ์œผ๋ฉด ใ€Œ20๋ถ„์งธ ๋ชป ์ฝ๋Š”๋‹คใ€๋ฅผ\n * ๋งํ•  ์ˆ˜ ์—†๊ณ , ํ™”๋ฉด์€ ๋Š˜ ใ€Œ๋ฐฉ๊ธˆ ์‹คํŒจํ–ˆ๋‹คใ€๋งŒ ๋ณด๊ฒŒ ๋œ๋‹ค.\n */\nexport function recordReadFailure(\n ledger: IngestLedger,\n reason: string,\n nowMs: number,\n stream?: string\n): void {\n const prev = ledger.readFailure\n ledger.readFailure = {\n consecutive: (prev?.consecutive ?? 0) + 1,\n sinceMs: prev?.sinceMs ?? nowMs,\n lastAtMs: nowMs,\n reason,\n ...(stream ? { stream } : {})\n }\n}\n\n/**\n * ์ฝ๊ธฐ๊ฐ€ ์„ฑ๊ณตํ–ˆ๋‹ค โ€” ๋‹จ์ ˆ ๊ธฐ๋ก์„ ์ง€์šด๋‹ค.\n *\n * **๋นˆ ์ฝ๊ธฐ๋„ ์„ฑ๊ณต์ด๋‹ค**: ์›๋ณธ์ด ใ€Œ์ƒˆ ๊ฒƒ์ด ์—†๋‹คใ€๊ณ  ๋‹ตํ•œ ๊ฒƒ์€ ๋‹ฟ์•˜๋‹ค๋Š” ๋œป์ด๋‹ค. ๊ทธ ๋‘˜์„ ๊ฐ™๊ฒŒ ๋‘๋ฉด\n * ์กฐ์šฉํ•œ ์›๋ณธ์ด ๋Š๊ธด ์›๋ณธ์œผ๋กœ ๋ณด์ธ๋‹ค(๊ณ ์น˜๋ ค๋˜ ๊ฒƒ์˜ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ ํ‹€๋ฆฐ๋‹ค).\n */\nexport function clearReadFailure(ledger: IngestLedger): void {\n if (ledger.readFailure) delete ledger.readFailure\n}\n\n/**\n * ์ปค์„œ ์ •์ฒด๋ฅผ ์žฅ๋ถ€์— ์ ๋Š”๋‹ค โ€” **๋‹ฟ์ง€ ๋ชปํ•œ ๊ฒƒ๊ณผ ๊ฐ€๋ฅธ๋‹ค.**\n *\n * `recordReadFailure` ์™€ ๊ฐ™์€ ๋ชจ์–‘์ด์ง€๋งŒ **๋‹ค๋ฅธ ์นธ**์ด๋‹ค. ํ•œ ์ฃผ๊ธฐ๊ฐ€ ๋‘ ์ด์œ ๋กœ ์‹คํŒจํ•  ์ˆ˜๋Š” ์—†์œผ๋ฏ€๋กœ\n * (๋จผ์ € ๋‹ฟ์•„์•ผ ์ฝ๋Š”๋‹ค) ๋‘˜์ด ๋™์‹œ์— ์„œ์ง€ ์•Š๋Š”๋‹ค โ€” ๊ทธ๋ž˜์„œ ํ™”๋ฉด์€ ๋‘˜ ์ค‘ ํ•˜๋‚˜๋งŒ ๋ณด๊ฒŒ ๋˜๊ณ , ๊ทธ ํ•˜๋‚˜๊ฐ€\n * ์กฐ์น˜๋ฅผ ์ •ํ•œ๋‹ค.\n */\nexport function recordCursorStall(ledger: IngestLedger, reason: string, nowMs: number, stream?: string): void {\n const prev = ledger.cursorStall\n ledger.cursorStall = {\n consecutive: (prev?.consecutive ?? 0) + 1,\n sinceMs: prev?.sinceMs ?? nowMs,\n lastAtMs: nowMs,\n reason,\n ...(stream ? { stream } : {})\n }\n}\n\n/** ์ฐฝ์„ ๋„˜๊ฒผ์œผ๋ฉด ์ง€์šด๋‹ค โ€” ํ’€๋ฆฐ ์ •์ฒด๊ฐ€ ํ™”๋ฉด์— ๋‚จ์•„ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ๋„ ๊ฑฐ์ง“์ด๋‹ค. */\nexport function clearCursorStall(ledger: IngestLedger): void {\n if (ledger.cursorStall) delete ledger.cursorStall\n}\n\n/** ์ฐฝ ๊ธธ์ด โ€” 10๋ถ„. ๋ถ€ํ•˜ ๊ณ„๊ธฐํŒ(10์ดˆ)๊ณผ ๋‹ค๋ฅธ ๊ฐ’์ด๋ผ ์ด๋ฆ„์„ ๋”ฐ๋กœ ๋‘”๋‹ค(๊ฐ™์€ ์ด๋ฆ„์ด๋ฉด ์„ž์ธ๋‹ค). */\nexport const INGEST_WINDOW_MS = 600_000\n\n/**\n * ์ด๋งŒํผ ์•„๋ฌด๊ฒƒ๋„ ์˜ค์ง€ ์•Š์œผ๋ฉด ใ€Œ์œ ์ž… ์—†์Œใ€์ด๋ผ ๋งํ•œ๋‹ค.\n *\n * ์ฐฝ ํ•˜๋‚˜์™€ ๊ฐ™๊ฒŒ ๋‘”๋‹ค โ€” ํ•œ ์ฐฝ์„ ํ†ต์งธ๋กœ ๋น„์šด ๊ฒƒ์€ ์‹ค์ œ๋กœ ใ€Œ๊ทธ ๊ตฌ๊ฐ„์— ์•„๋ฌด๊ฒƒ๋„ ์˜ค์ง€ ์•Š์•˜๋‹คใ€์ด๊ณ , ์ถ”์ด์˜\n * ๊ทธ ์ž๋ฆฌ๋„ ๋น„๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ฆผ๊ณผ ์•Œ์•ฝ์ด ๊ฐ™์€ ๋ง์„ ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด ๊ฐ™์€ ๊ธธ์ด์—ฌ์•ผ ํ•œ๋‹ค.\n */\nexport const STALE_AFTER_MS = INGEST_WINDOW_MS\n\n/** ์ถ”์ด ๋ณด๊ด€ ์ฐฝ ์ˆ˜ โ€” 36์ฐฝ ร— 10๋ถ„ = 6์‹œ๊ฐ„. ใ€Œ๊ณ ์นœ ๊ฒƒ์ด ๋จนํ˜”๋‚˜ใ€๋ฅผ ๋ณด๊ธฐ์— ์ถฉ๋ถ„ํ•˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ๋Š” ์ˆซ์ž ๋„ทร—36 ์ด๋‹ค. */\nexport const TREND_LIMIT = 36\n\n/**\n * ์‚ฌ์œ  ์ข…๋ฅ˜ ์ƒํ•œ.\n *\n * **์—†์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋์—†์ด ์ž๋ž€๋‹ค.** ์‚ฌ์œ  ๋ฌธ๊ตฌ์— ๋ ˆ์ฝ”๋“œ๋งˆ๋‹ค ๋‹ค๋ฅธ ๊ฐ’์ด ์„ž์ด๋Š” ์ปค๋„ฅํ„ฐ๊ฐ€ ํ•˜๋‚˜๋งŒ ์žˆ์–ด๋„\n * (`unknown bizStep \"PUTAWAY_X17\"`, `โ€ฆX18`, โ€ฆ) ํ‚ค๊ฐ€ ๋ฌดํ•œํžˆ ๋Š˜์–ด๋‚œ๋‹ค. ์ดˆ๋‹น 1๋งŒ ๊ฑด ๊ทœ๋ชจ์—์„œ ์ด๊ฒƒ์€\n * ๋ฏธ๋ฏธํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ˆ„์ˆ˜๋‹ค.\n *\n * ๋„˜์น˜๋ฉด `OTHER_REASON` ํ•œ ์นธ์— ๋ชจ์œผ๊ณ  **๋ช‡ ์ข…์„ ์ ‘์—ˆ๋Š”์ง€ ํ•จ๊ป˜ ๋‚ธ๋‹ค**(์กฐ์šฉํžˆ ์ž๋ฅด์ง€ ์•Š๋Š”๋‹ค).\n * 30์ข…์ด๋ฉด ๊ณ ์น  ๊ณณ์„ ์ฐพ๊ธฐ์— ์ถฉ๋ถ„ํ•˜๋‹ค โ€” ๋ชฉ๋ก์„ ํ›‘๋Š” ์ผ์ด ์•„๋‹ˆ๋ผ ๋งคํ•‘ ๋ช‡ ์ค„์„ ๊ณ ์น˜๋Š” ์ผ์ด๋‹ค.\n */\nexport const REASON_LIMIT = 30\n\n/** ์ƒํ•œ์„ ๋„˜๊ธด ์‚ฌ์œ ๋“ค์ด ๋ชจ์ด๋Š” ์นธ. */\nexport const OTHER_REASON = '(other reasons)'\n\n/**\n * ํ‘œ๋ณธ ์ƒํ•œ โ€” **์‚ฌ์œ ๋งˆ๋‹ค ํ•œ ๊ฑด**๋งŒ ๋“ ๋‹ค.\n *\n * ๊ฐ™์€ ์‚ฌ์œ  20๊ฑด๋ณด๋‹ค **์„œ๋กœ ๋‹ค๋ฅธ ์‚ฌ์œ  1๊ฑด์”ฉ**์ด ๋งคํ•‘์„ ๊ณ ์น˜๋Š” ๋ฐ ์œ ์šฉํ•˜๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ๊ทœ์น™์ด ์„ฑ๋Šฅ์„\n * ์ง€ํ‚จ๋‹ค: ๊ทธ ์‚ฌ์œ ์˜ ํ‘œ๋ณธ์ด ์ด๋ฏธ ์žˆ์œผ๋ฉด `JSON.stringify` ๋ฅผ **์•„์˜ˆ ํ•˜์ง€ ์•Š๋Š”๋‹ค.** ์‹ค์ธก(๋ ˆ์ฝ”๋“œ 241๋ฐ”์ดํŠธ)\n * ์œผ๋กœ ๊ฑด๋‹น 0.24ยตs ์ธ๋ฐ, ์ดˆ๋‹น 1๋งŒ ๊ฑด์ด ์ „๋ถ€ ๊ฑฐ๋ถ€๋˜๋ฉด 9,980๊ฑด์€ ๋งŒ๋“ค์ž๋งˆ์ž ๋ฒ„๋ ค์ง€๋Š” ๋ฌธ์ž์—ด์ด์—ˆ๋‹ค โ€”\n * CPU ๋Š” ๊ฐ๋‹น๋˜์ง€๋งŒ GC ์••๋ฐ•์ด ์ด๋ฒคํŠธ ๋ฃจํ”„๋กœ ๋Œ์•„์˜จ๋‹ค.\n */\nexport const SAMPLE_LIMIT = REASON_LIMIT\n\n/** ํ‘œ๋ณธ ํ•˜๋‚˜์˜ ์ง๋ ฌํ™” ์ƒํ•œ(๋ฌธ์ž). */\nexport const RECORD_MAX = 2000\n\n/**\n * ๋งคํ•‘์ด ๋ง‰๊ณ  ์žˆ๋‹ค๊ณ  ๋ณผ ํ†ต๊ณผ์œจ ๋ฌธํ„ฑ.\n *\n * ๊ฐ’๊ณผ ํ•จ๊ป˜ ๋‚ด๋ณด๋‚ธ๋‹ค โ€” ํ™”๋ฉด์ด ใ€Œ95% ๋ฏธ๋งŒ์ด๋ผ ๋งคํ•‘ ๋ฌธ์ œ๋กœ ๋ดค๋‹คใ€๋ฅผ ๋งํ•  ์ˆ˜ ์žˆ๊ฒŒ. ์ˆจ๊ธด ๋ฌธํ„ฑ์€ ์‚ฌ๋žŒ์ด\n * ํŒ์ •์„ ๋ฏฟ์ง€ ๋ชปํ•œ๋‹ค.\n */\nexport const HEALTHY_ACCEPTED_RATIO = 0.95\n\nexport function newIngestLedger(): IngestLedger {\n return {\n window: { startMs: -1, endMs: -1, offered: 0, rejected: 0, undelivered: 0, rows: 0, flushes: 0 },\n trend: [],\n trendDropped: 0,\n offered: 0,\n rejected: 0,\n undelivered: 0,\n rows: 0,\n flushes: 0,\n lastWriteAtMs: -1,\n lastUndeliveredAtMs: -1,\n byReason: {},\n reasonsCapped: false,\n sampledReasons: new Set<string>(),\n samples: [],\n sampleDropped: 0,\n lastAtMs: -1\n }\n}\n\n/**\n * ์ฐฝ์ด ๋‹ค ์ฐผ์œผ๋ฉด ๋‹ซ์•„ ์ถ”์ด์— ๋„ฃ๊ณ  ์ƒˆ๋กœ ์—ฐ๋‹ค. **๋‹ซํžŒ ์ฐฝ๋งŒ ใ€Œ์ตœ๊ทผใ€์œผ๋กœ ์ฝ๋Š”๋‹ค.**\n *\n * โ”€โ”€ ์˜์†์ด ๋ถ™๋Š” ์œ ์ผํ•œ ์ž๋ฆฌ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ๋‹ซํžŒ ์ฐฝ ํ•˜๋‚˜๊ฐ€ ์˜์†์˜ ๋‹จ์œ„๋‹ค(์ˆซ์ž ๋„ท + ๊ตฌ๊ฐ„). `onClosed` ๋ฅผ ๋ฐ›๋Š” ์ด์œ ๋Š” **์ด ํ•จ์ˆ˜๊ฐ€ ์ฐฝ์ด ๋‹ซํžˆ๋Š”\n * ์œ ์ผํ•œ ์ง€์ **์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค โ€” ์—ฌ๊ธฐ ํ•œ ๊ณณ์— ๋ถ™์ด๋ฉด ๊ธฐ๋ก ๊ฒฝ๋กœ๊ฐ€ ๊ฐˆ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n *\n * ์™œ ๊ฑด๋งˆ๋‹ค๊ฐ€ ์•„๋‹ˆ๋ผ ์ฐฝ๋งˆ๋‹ค์ธ๊ฐ€: ๊ฑฐ๋ถ€๋งˆ๋‹ค ํ–‰์„ ์“ฐ๋ฉด ์ดˆ๋‹น 1๋งŒ ํ–‰์ด๊ณ  ๊ทธ ๋ถ€ํ•˜๊ฐ€ ์ด๋ฒคํŠธ ๋ฃจํ”„๋กœ ๋Œ์•„์˜จ๋‹ค.\n * ์ฐฝ๋งˆ๋‹ค๋ฉด ํŠธ์œˆ 1,000๊ฐœ์— ํ•˜๋ฃจ 14.4๋งŒ ํ–‰(์•ฝ 14MB)์ด๋‹ค.\n *\n * `onClosed` ๋Š” **์˜ค๋ฅ˜๋ฅผ ๋‚ด์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค**(ํ˜ธ์ถœ๋ถ€๊ฐ€ ๊ฐ์‹ผ๋‹ค) โ€” ์˜์†์ด ์‹คํŒจํ•ด๋„ ์žฅ๋ถ€๋Š” ๊ณ„์† ๊ตด๋Ÿฌ์•ผ ํ•œ๋‹ค.\n * ์˜์†์„ ์ง€ํ‚ค๋Š” ๊ฒƒ๊ณผ ๊ด€์ธก์„ ์ง€ํ‚ค๋Š” ๊ฒƒ์€ ๋‹ค๋ฅธ ์ผ์ด๊ณ , ๋’ค์—ฃ๊ฒƒ์ด ์•ž์—ฃ๊ฒƒ ๋•Œ๋ฌธ์— ๋ฉˆ์ถ”๋ฉด ์•ˆ ๋œ๋‹ค.\n */\nexport function rollIngestWindow(\n ledger: IngestLedger,\n nowMs: number,\n windowMs = INGEST_WINDOW_MS,\n onClosed?: (closed: IngestWindow) => void\n): void {\n if (ledger.window.startMs < 0) {\n ledger.window.startMs = nowMs\n return\n }\n if (nowMs - ledger.window.startMs < windowMs) return\n\n const closed: IngestWindow = { ...ledger.window, endMs: nowMs }\n ledger.trend.push(closed)\n ledger.window = { startMs: nowMs, endMs: -1, offered: 0, rejected: 0, undelivered: 0, rows: 0, flushes: 0 }\n\n if (ledger.trend.length > TREND_LIMIT) {\n const drop = ledger.trend.length - TREND_LIMIT\n ledger.trend.splice(0, drop)\n ledger.trendDropped += drop\n }\n\n /* ์˜์†์€ ๊ด€์ธก์„ ๋ง‰์ง€ ์•Š๋Š”๋‹ค โ€” ํ„ฐ์ ธ๋„ ์žฅ๋ถ€๋Š” ์ด๋ฏธ ๊ฐฑ์‹ ๋๋‹ค. */\n if (onClosed) {\n try {\n onClosed(closed)\n } catch {\n /* ํ˜ธ์ถœ๋ถ€๊ฐ€ ์•Œ๋ฆฐ๋‹ค. ์—ฌ๊ธฐ์„œ ์‚ผํ‚ค๋Š” ๊ฒƒ์€ **์žฅ๋ถ€์˜ ์ง„ํ–‰**์„ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋‹ค. */\n }\n }\n}\n\n/** ์‚ฌ์œ  ๋ฌถ์Œ์˜ ํ‚ค. ์‚ฌ์œ ๊ฐ€ ์—ฌ๋Ÿฟ์ด๋ฉด ํ•œ ์ค„๋กœ ์ž‡๊ณ , ์—†์œผ๋ฉด ์ง€์–ด๋‚ด์ง€ ์•Š๊ณ  `unknown`. */\nexport function reasonKey(errors: string[] | undefined): string {\n const list = (errors ?? []).map(e => String(e).trim()).filter(Boolean)\n return list.length ? list.join(' ยท ') : 'unknown'\n}\n\nfunction safeJson(v: unknown): string {\n try {\n const s = JSON.stringify(v)\n return s === undefined ? String(v) : s\n } catch {\n return '(serialization failed)' /* ์ˆœํ™˜ ์ฐธ์กฐ ๋“ฑ โ€” ๋ชจ์–‘์„ ๋ชป ๋ณด์—ฌ ์ฃผ๋Š” ๊ฒƒ๋„ ์‚ฌ์‹ค์ด๋‹ค */\n }\n}\n\n/**\n * ํ•œ ๋ฒˆ์˜ ์ธ์ œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ์ ๋Š”๋‹ค.\n *\n * `offered` ๋ฅผ ํ•จ๊ป˜ ๋ฐ›๋Š” ์ด์œ : ํ†ต๊ณผ์œจ์„ **์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ๊ณ„์ˆ˜๊ธฐ**์—์„œ ๋‚˜๋ˆ  ๊ณ„์‚ฐํ•˜๋ฉด ๋ถ„๋ชจ์™€ ๋ถ„์ž๊ฐ€ ๋‹ค๋ฅธ\n * ๊ฒƒ์„ ์„ธ๊ฒŒ ๋œ๋‹ค. ๋‘˜์„ ํ•œ์ž๋ฆฌ์—์„œ ๋ณธ ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ ์ ๋Š”๋‹ค.\n */\nexport function recordIngest(\n ledger: IngestLedger,\n offered: number,\n rejected: { record?: unknown; errors?: string[] }[] | undefined,\n nowMs: number,\n onClosed?: (closed: IngestWindow) => void,\n /** ๋งคํ•‘์€ ํ†ต๊ณผํ–ˆ์ง€๋งŒ ํŠธ์œˆ์— ๋‹ฟ์ง€ ๋ชปํ•œ ์ˆ˜ โ€” ใ€Œํ†ต๊ณผใ€๋กœ ์„ธ๋ฉด ํ™”๋ฉด์ด ๊ฑฐ์ง“์„ ๋งํ•œ๋‹ค. */\n undelivered = 0\n): void {\n if (!Number.isFinite(offered) || offered < 0) return\n\n rollIngestWindow(ledger, nowMs, INGEST_WINDOW_MS, onClosed)\n\n ledger.offered += offered\n ledger.window.offered += offered\n ledger.lastAtMs = nowMs\n\n if (Number.isFinite(undelivered) && undelivered > 0) {\n ledger.undelivered += undelivered\n ledger.window.undelivered += undelivered\n ledger.lastUndeliveredAtMs = nowMs\n }\n\n const items = rejected ?? []\n if (!items.length) return\n\n ledger.rejected += items.length\n ledger.window.rejected += items.length\n\n for (const it of items) {\n const raw = reasonKey(it?.errors)\n const known = raw in ledger.byReason\n\n /* ์‚ฌ์œ  ์ข…๋ฅ˜ ์ƒํ•œ โ€” ๋„˜์น˜๋ฉด ํ•œ ์นธ์œผ๋กœ ์ ‘๊ณ , **๋‹ฟ์•˜๋‹ค๋Š” ์‚ฌ์‹ค**์„ ํ‘œ์‹œํ•œ๋‹ค. */\n let key = raw\n if (!known && Object.keys(ledger.byReason).length >= REASON_LIMIT) {\n key = OTHER_REASON\n ledger.reasonsCapped = true\n }\n\n const seen = ledger.byReason[key]\n ledger.byReason[key] = { count: (seen?.count ?? 0) + 1, lastAtMs: nowMs }\n\n /*\n * **ํ‘œ๋ณธ์€ ์‚ฌ์œ ๋งˆ๋‹ค ํ•˜๋‚˜.** ์ด๋ฏธ ๊ทธ ์‚ฌ์œ ์˜ ํ‘œ๋ณธ์ด ์žˆ์œผ๋ฉด ์ง๋ ฌํ™”๋ฅผ ์•„์˜ˆ ํ•˜์ง€ ์•Š๋Š”๋‹ค โ€” ์ดˆ๋‹น 1๋งŒ ๊ฑด์ด\n * ์ „๋ถ€ ๊ฑฐ๋ถ€๋  ๋•Œ ๋งŒ๋“ค์ž๋งˆ์ž ๋ฒ„๋ ค์ง€๋Š” ๋ฌธ์ž์—ด์„ ๋งŒ๋“ค์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ๋‹ค(์‹ค์ธก: ๊ฑด๋‹น 0.24ยตs, GC ์••๋ฐ•).\n */\n if (ledger.sampledReasons.has(key)) {\n ledger.sampleDropped += 1\n continue\n }\n\n const text = safeJson(it?.record)\n const truncated = text.length > RECORD_MAX\n ledger.samples.push({\n atMs: nowMs,\n errors: key === OTHER_REASON ? [OTHER_REASON] : it?.errors?.length ? [...it.errors] : ['unknown'],\n record: truncated ? text.slice(0, RECORD_MAX) : text,\n ...(truncated ? { recordTruncated: true } : {})\n })\n\n ledger.sampledReasons.add(key)\n\n if (ledger.samples.length > SAMPLE_LIMIT) {\n const gone = ledger.samples.splice(0, ledger.samples.length - SAMPLE_LIMIT)\n /* ๋ฐ€๋ ค๋‚œ ํ‘œ๋ณธ์˜ ์‚ฌ์œ ๋Š” ์ƒ‰์ธ์—์„œ๋„ ๋นผ์•ผ ๋‹ค์Œ์— ๋‹ค์‹œ ๋œฐ ์ˆ˜ ์žˆ๋‹ค(์•ˆ ๋นผ๋ฉด ๊ทธ ์‚ฌ์œ ๋Š” ์˜์›ํžˆ ํ‘œ๋ณธ ์—†์Œ). */\n for (const g of gone) ledger.sampledReasons.delete(reasonKey(g.errors))\n ledger.sampleDropped += gone.length\n }\n }\n}\n\n/**\n * ์ €๋„์— **์ ์€ ๊ฒƒ**์„ ๊ธฐ๋กํ•œ๋‹ค โ€” ์ฐฝ์„ ๋„˜๊ธธ ๋•Œ๊ฐ€ ๋˜์—ˆ์œผ๋ฉด ๋„˜๊ธฐ๊ณ , ํ–‰ยท์™•๋ณต์„ ๋”ํ•œ๋‹ค.\n *\n * โ”€โ”€ ์™œ ์œ ์ž…๊ณผ ๊ฐ™์€ ์ฐฝ์ธ๊ฐ€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์ƒˆ ์žฅ๋ถ€๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค. ์ด ์ฐฝ์€ ใ€Œ๋‹ซํžŒ ์ฐฝ๋งŒ ์ตœ๊ทผใ€ยทใ€Œ0 ๊ณผ ์—†์Œ์„ ๊ฐ€๋ฅธ๋‹คใ€ยทใ€Œ์ž˜๋ฆฐ ๊ฒƒ์„ ์„ธ์–ด ๋“œ๋Ÿฌ๋‚ธ๋‹คใ€๋ฅผ\n * ์ด๋ฏธ ์ง€ํ‚ค๊ณ , ๋‹ซํž ๋•Œ ์˜์†๊นŒ์ง€ ๋œ๋‹ค. ๊ฐ™์€ ๊ทœ์œจ์„ ๋‘ ๋ฒŒ ๋งŒ๋“ค๋ฉด ํ•œ์ชฝ๋งŒ ๊ณ ์ณ์ง„๋‹ค.\n *\n * โ”€โ”€ ์œ ์ž…์ด ์—†์–ด๋„ ์ฐฝ์€ ๋„˜์–ด๊ฐ„๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์‹œ๋ฎฌ ํŠธ์œˆ์€ ๋ฐ›๋Š” ๊ฒƒ ์—†์ด ์ž๊ธฐ๊ฐ€ ๋‚ธ ์‚ฌ์‹ค์„ ์ ๋Š”๋‹ค. ์ฐฝ์ด **์œ ์ž…์—์„œ๋งŒ** ๋„˜์–ด๊ฐ„๋‹ค๋ฉด ๊ทธ ํŠธ์œˆ์€ ์ถ”์ด์—\n * ์˜์›ํžˆ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š๋Š”๋‹ค โ€” ๊ทธ๋ž˜์„œ ์“ฐ๊ธฐ๋„ ์ฐฝ์„ ๋„˜๊ธด๋‹ค.\n */\nexport function recordJournalWrite(\n ledger: IngestLedger,\n rows: number,\n nowMs: number,\n onClosed?: (closed: IngestWindow) => void\n): void {\n if (!Number.isFinite(rows) || rows <= 0) return\n rollIngestWindow(ledger, nowMs, INGEST_WINDOW_MS, onClosed)\n ledger.rows += rows\n ledger.flushes += 1\n ledger.window.rows += rows\n ledger.window.flushes += 1\n /*\n * **`lastAtMs` ๋Š” ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.** ๊ทธ ๊ฐ’์€ ใ€Œ๋งˆ์ง€๋ง‰ ์œ ์ž…ใ€์ด๊ณ , ๋Š๊ธด ํ”ผ๋“œ๋ฅผ ๊ฐ€๋ ค๋‚ด๋Š” ๊ทผ๊ฑฐ๋‹ค. ์“ฐ๊ธฐ๋กœ\n * ๊ฐฑ์‹ ํ•˜๋ฉด ๋ผ์ด๋ธŒ ํŠธ์œˆ์€ ์œ ์ž…์ด ๋Š๊ฒจ๋„ ํ‹ฑ์ด ๋งŒ๋“  ๋ธํƒ€๋ฅผ ๊ณ„์† ์ ์œผ๋ฏ€๋กœ **์˜์›ํžˆ ใ€Œ๋ฐฉ๊ธˆ ๋ฐ›์•˜๋‹คใ€**๊ฐ€ ๋˜๊ณ ,\n * ใ€Œ์˜ค๋ž˜ ๋Š๊ธด ํ”ผ๋“œ์— ์˜› ์ฐฝ์œผ๋กœ ์ดˆ๋ก์„ ๋‚ด์ง€ ์•Š๋Š”๋‹คใ€๊ฐ€ ์กฐ์šฉํžˆ ๋ฌด๋ ฅํ•ด์ง„๋‹ค. ์ถ•์„ ๋”ฐ๋กœ ๋“ ๋‹ค.\n */\n ledger.lastWriteAtMs = nowMs\n}\n\n/**\n * ํ•œ๋ˆˆ ํŒ์ •.\n *\n * `not-applicable` ๊ณผ `not-connected` ๋ฅผ ์„ž์ง€ ์•Š๊ณ , ์žฌ์ง€ ์•Š์€ ๊ฒƒ์„ ์ •์ƒยท๋น„์ •์ƒ์œผ๋กœ ์ฑ„์šฐ์ง€ ์•Š๋Š”๋‹ค\n * (`unknown`). ์‹œ๋ฎฌ ํŠธ์œˆ์— ใ€Œ๋™๊ธฐํ™” ์•ˆ ๋จใ€์ด๋ผ ๋งํ•˜๋ฉด ๊ฑฐ์ง“ ๊ฒฝ๋ณด์ด๊ณ , **๊ฑฐ์ง“ ๊ฒฝ๋ณด๋Š” ์ฐธ ๊ฒฝ๋ณด๋ฅผ ์ฃฝ์ธ๋‹ค.**\n */\nexport type SyncVerdict =\n | 'syncing' /** ์œ ์ž…์ด ์žˆ๊ณ  ํ†ต๊ณผ์œจ์ด ๋ฌธํ„ฑ ์ด์ƒ */\n | 'mapping-blocked' /** ์œ ์ž…์€ ์žˆ๋Š”๋ฐ ๊ฑฐ๋ถ€๊ฐ€ ๋ฌธํ„ฑ์„ ๋„˜๋Š”๋‹ค โ€” ์‚ฌ์œ ๋ฅผ ๋ณด๊ณ  ๋งคํ•‘์„ ๊ณ ์นœ๋‹ค */\n /**\n * ๋“ค์–ด์˜ค๋Š”๋ฐ **์ปค๋„์ด ๋ฐ›์ง€ ์•Š๋Š”๋‹ค** โ€” ์‚ฌ์‹ค์ด ์‚ฌ๋ผ์ง€๊ณ  ์žˆ๋‹ค.\n *\n * โ”€โ”€ ์ด๋ฆ„์ด ์‚ฌ์‹ค๋ณด๋‹ค ์ข๋‹ค (2026-08-20์— ๋“œ๋Ÿฌ๋‚ฌ๋‹ค) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ๊ด€์ธกํ•œ ๊ฒƒ์€ ใ€Œ์ปค๋„์ด ๋ฐ›์ง€ ์•Š์•˜๋‹คใ€(`ingestLive` ๊ฐ€ 0)์ด๊ณ , **๋ฉˆ์ถ˜ ๊ฒƒ์€ ๊ทธ ์›์ธ ์ค‘ ํ•˜๋‚˜์ผ ๋ฟ**์ด๋‹ค.\n * ๋‹ค๋ฅธ ์›์ธ์ด ์‹ค์ œ๋กœ ์žˆ์—ˆ๋‹ค: ๋ฏธ๋Ÿฌ๋กœ ์„ ์–ธ๋œ ํŠธ์œˆ์„ ์‚ฌ๋žŒ์ด ํ™”๋ฉด์—์„œ ์‹œ์ž‘ํ•˜๋ฉด ์‹œ๋ฎฌ ๊ตฌ๋™์œผ๋กœ ๋–ด๊ณ , ๊ทธ\n * ํŠธ์œˆ์€ **๋Œ๊ณ  ์žˆ์œผ๋ฉด์„œ** ์œ ์ž…์„ ํ•œ ๊ฑด๋„ ๋ฐ›์ง€ ๋ชปํ–ˆ๋‹ค. ๊ทธ๋•Œ ํ™”๋ฉด์€ ํ•œ์ชฝ์— ใ€Œrunningใ€, ๋‹ค๋ฅธ ์ชฝ์—\n * ใ€Œ๋ฉˆ์ถฐใ€๋ผ๊ณ  ์ ์—ˆ๊ณ  ์‚ฌ๋žŒ์€ ใ€Œ์‹œ์ž‘ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋˜๊ฒ ๋„คใ€๋ผ๋Š” ์—‰๋šฑํ•œ ์ฒ˜๋ฐฉ์œผ๋กœ ๊ฐ”๋‹ค.\n *\n * ๊ทธ๋ž˜์„œ **ํ™”๋ฉด ๋ฌธ๊ตฌ๋Š” ๊ด€์ธก ๊ทธ๋Œ€๋กœ** ์ ๋Š”๋‹ค(ใ€ŒํŠธ์œˆ์— ๋‹ฟ์ง€ ์•Š๊ณ  ๋ฒ„๋ ค์ง€๋Š” ์ค‘ใ€). ์ด ํ‚ค ์ด๋ฆ„์€ ์„œ๋ฒ„ยทํ™”๋ฉดยท\n * ์‹œํ—˜์— ๊ฑธ์นœ ๊ณ„์•ฝ ๋ฌธ์ž์—ด์ด๋ผ ๊ทธ๋Œ€๋กœ ๋‘์ง€๋งŒ, ์ด๋ฆ„์„ ์›์ธ์œผ๋กœ ์ฝ์ง€ ์•Š๋Š”๋‹ค.\n */\n | 'twin-not-running'\n | 'no-traffic' /** ํ”ผ๋“œ๋Š” ๋ถ™์—ˆ๋Š”๋ฐ ์ œ์‹œ๊ฐ€ 0 โ€” ์›์ฒœยท๋ง์„ ๋ณธ๋‹ค */\n | 'not-connected' /** ํ”ผ๋“œ ์ž์ฒด๊ฐ€ ์—†๋‹ค โ€” ๊ฒฐ์„ ํ•œ๋‹ค */\n | 'not-applicable' /** ์›๋ณธ์ด ๋จน์ด์ง€ ์•Š๋Š” ํŠธ์œˆ(์‹œ๋ฎฌ)ยท๋ฉˆ์ถ˜ ํŠธ์œˆ โ€” ๋ฌผ์„ ๋Œ€์ƒ์ด ์•„๋‹ˆ๋‹ค */\n | 'unknown' /** ํ•œ ๋ฒˆ๋„ ๋ฐ›์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ์ฐฝ์ด ์•„์ง ๋‹ซํžˆ์ง€ ์•Š์•˜๋‹ค โ€” ๊ธฐ๋‹ค๋ฆฐ๋‹ค */\n\nexport function syncVerdictOf(args: {\n /** `live-feed-registry.liveFeedStateOf` ์˜ ๋‹ต์„ ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค โ€” ํŒ์ • ์–ดํœ˜๋ฅผ ๋‘ ๋ฒŒ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค. */\n feedState: 'attached' | 'detached' | 'not-applicable'\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ **๋‹ซํžŒ** ์ฐฝ. ์—†์œผ๋ฉด `null`. */\n recent: { offered: number; rejected: number } | null\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฌด์—‡์ด๋“  ๋ฐ›์€ ์‹œ๊ฐ. ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `-1` โ€” 0 ์ด ์•„๋‹ˆ๋‹ค(0 ์€ 1970๋…„์ด๋‹ค). */\n lastAtMs?: number\n /** ์ง€๊ธˆ. `lastAtMs` ๋ฅผ ๊ฒฌ์ค„ ๋•Œ๋งŒ ์“ด๋‹ค. */\n nowMs?: number\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ **๋ฒ„๋ ค์ง„** ์‹œ๊ฐ. ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `-1`. */\n lastUndeliveredAtMs?: number\n threshold?: number\n}): SyncVerdict {\n /*\n * โ”€โ”€ ๋ฒ„๋ ค์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ๋จผ์ €๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ํŠธ์œˆ์ด ๋ฉˆ์ถ”๋ฉด ํ”ผ๋“œ๋Š” ๋‚จ์•„ ๊ณ„์† ๋ ˆ์ฝ”๋“œ๋ฅผ ๋‚˜๋ฅด๊ณ , ์ปค๋„์€ ๊ทธ๊ฒƒ์„ ์กฐ์šฉํžˆ ๋ฒ„๋ฆฐ๋‹ค. ๊ทธ ์ƒํƒœ๋ฅผ ใ€Œํ•ด๋‹น ์—†์Œใ€\n * ์ด๋ผ ๋ถ€๋ฅด๋ฉด **์‚ฌ์‹ค์ด ์‚ฌ๋ผ์ง€๋Š” ๋™์•ˆ ํ™”๋ฉด์ด ์•ˆ์‹ฌ์‹œํ‚จ๋‹ค** โ€” ์‹ค์ œ๋กœ ๊ทธ๋ ‡๊ฒŒ ๋–ด๋‹ค(๋ฉˆ์ถ˜ ํŠธ์œˆ ์˜†์— ใ€Œ150\n * ํ†ต๊ณผ ยท 100%ใ€). ๊ทธ๋ž˜์„œ ์‹œ๋ฎฌยท๋ฉˆ์ถค ํŒ์ •๋ณด๋‹ค ๋จผ์ € ๋ณธ๋‹ค.\n *\n * ์‹œ๋ฎฌ ํŠธ์œˆ์—๋Š” ์ด ๊ฐ’์ด ์„œ์ง€ ์•Š๋Š”๋‹ค(๋จน์ด๋Š” ํ”ผ๋“œ๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋ฏธ์ „๋‹ฌ๋„ ์—†๋‹ค). ๊ทธ๋ž˜์„œ ์ด ์ˆœ์„œ๊ฐ€ ์‹œ๋ฎฌ์—\n * ๊ฑฐ์ง“ ๊ฒฝ๋ณด๋ฅผ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.\n */\n if (\n typeof args.lastUndeliveredAtMs === 'number' &&\n args.lastUndeliveredAtMs >= 0 &&\n typeof args.nowMs === 'number' &&\n args.nowMs - args.lastUndeliveredAtMs <= STALE_AFTER_MS\n ) {\n return 'twin-not-running'\n }\n\n if (args.feedState === 'not-applicable') return 'not-applicable'\n if (args.feedState === 'detached') return 'not-connected'\n\n /*\n * โ”€โ”€ ๋Š๊ธด ํ”ผ๋“œ๊ฐ€ ์ดˆ๋ก์œผ๋กœ ๋‚จ์ง€ ์•Š๊ฒŒ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์ฐฝ์€ **๋‹ค์Œ ์œ ์ž…์ด ์žˆ์„ ๋•Œ** ๋‹ซํžŒ๋‹ค. ๊ทธ๋ž˜์„œ ์œ ์ž…์ด ๋ฉˆ์ถ”๋ฉด ์ฐฝ๋„ ๋ฉˆ์ถ”๊ณ , ใ€Œ์ตœ๊ทผ ์ฐฝใ€์€ ๋ฉˆ์ถ”๊ธฐ ์ง์ „์˜\n * ๊ฑด๊ฐ•ํ–ˆ๋˜ ์ฐฝ์œผ๋กœ ๋‚จ๋Š”๋‹ค. ๊ทธ ์ฐฝ๋งŒ ๋ณด๊ณ  ํŒ์ •ํ•˜๋ฉด **์–ด์ œ ์ฃฝ์€ ํ”ผ๋“œ๊ฐ€ ์˜ค๋Š˜๋„ ใ€Œ๋™๊ธฐํ™” ์ค‘ใ€**์ด๋‹ค. ์ด\n * ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ์ด์œ ๊ฐ€ ๋ฐ”๋กœ ๊ทธ ์ƒํƒœ๋ฅผ ๋“œ๋Ÿฌ๋‚ด๋Š” ๊ฒƒ์ด๋ฏ€๋กœ, ๊ทธ๋•Œ ์ดˆ๋ก์„ ๋‚ด๋ฉด ๋ชฉ์ ์„ ๋†“์นœ๋‹ค.\n *\n * ๊ทธ๋ž˜์„œ ์ฐฝ๊ณผ ๋ณ„๊ฐœ๋กœ **์ง€๊ธˆ๊นŒ์ง€์˜ ์นจ๋ฌต**์„ ๋จผ์ € ๋ณธ๋‹ค. ํ•œ ์ฐฝ์„ ํ†ต์งธ๋กœ ์•„๋ฌด๊ฒƒ๋„ ๋ฐ›์ง€ ์•Š์•˜์œผ๋ฉด ๊ทธ๊ฒƒ์€\n * ใ€Œ์œ ์ž… ์—†์Œใ€์ด๋‹ค โ€” ์‹ค์ œ๋กœ ๊ทธ ๊ตฌ๊ฐ„์˜ ์ฐฝ์€ ๋น„์–ด ์žˆ๊ฒŒ ๋œ๋‹ค.\n *\n * ์ด ํŒ๋‹จ์„ ์„œ๋ฒ„์— ๋‘๋Š” ์ด์œ : ์†Œ๋น„์ฒ˜๊ฐ€ ์—ฌ๋Ÿฟ(๋ชฉ๋กยท์—ฐ๊ฒฐ ์ƒ์„ธยท์•ž์œผ๋กœ ์ƒ๊ธธ ๊ฒƒ)์ด๊ณ , ํ™”๋ฉด๋งˆ๋‹ค ๋‹ค์‹œ\n * ํŒ๋‹จํ•˜๋ฉด ๊ทœ์น™์ด ๊ฐˆ๋ผ์ ธ ์–ด๋–ค ํ™”๋ฉด์€ ์ดˆ๋ก, ์–ด๋–ค ํ™”๋ฉด์€ ๊ฒฝ๊ณ ๋ฅผ ๋งํ•œ๋‹ค.\n *\n * ํ•œ๊ณ„ โ€” ํ•œ ์ฐฝ(10๋ถ„)๋ณด๋‹ค **๋А๋ฆฌ๊ฒŒ ๊ธท๋Š” ์›์ฒœ**์€ ๊ธท๋Š” ์‚ฌ์ด์— ใ€Œ์œ ์ž… ์—†์Œใ€์œผ๋กœ ๋ณด์ธ๋‹ค. ์žฅ๋ถ€๋Š” ์›์ฒœ์˜\n * ์ฃผ๊ธฐ๋ฅผ ๋ชจ๋ฅด๋ฏ€๋กœ ์ด๋ณด๋‹ค ์ •ํ™•ํžˆ ๋‹ตํ•  ์ˆ˜ ์—†๋‹ค. ์ฃผ๊ธฐ๋ฅผ ์•Œ๊ฒŒ ๋˜๋ฉด ๊ทธ ๊ฐ’์œผ๋กœ ๊ฒฌ์ฃผ๋Š” ๊ฒƒ์ด ๋งž๋‹ค.\n */\n if (typeof args.lastAtMs === 'number' && typeof args.nowMs === 'number') {\n if (args.lastAtMs < 0) return 'unknown' /* ๋ถ™์—ˆ์ง€๋งŒ ํ•œ ๋ฒˆ๋„ ๋ฐ›์ง€ ์•Š์•˜๋‹ค โ€” ๊ธฐ๋‹ค๋ฆฐ๋‹ค */\n if (args.nowMs - args.lastAtMs > STALE_AFTER_MS) return 'no-traffic'\n }\n\n if (!args.recent) return 'unknown'\n if (args.recent.offered <= 0) return 'no-traffic'\n\n const ratio = (args.recent.offered - args.recent.rejected) / args.recent.offered\n return ratio >= (args.threshold ?? HEALTHY_ACCEPTED_RATIO) ? 'syncing' : 'mapping-blocked'\n}\n\nexport interface IngestWindowView {\n from: string\n to: string\n offered: number\n /** **ํŠธ์œˆ์— ๋‹ฟ์€** ์ˆ˜. ์ œ์‹œ โˆ’ ๊ฑฐ๋ถ€ โˆ’ ๋ฏธ์ „๋‹ฌ. */\n accepted: number\n /** ๋งคํ•‘์ด ๊ฑฐ๋ถ€ํ•œ ์ˆ˜ โ€” ์ฒ˜๋ฐฉ์€ ๋งคํ•‘์„ ๊ณ ์น˜๋Š” ๊ฒƒ. */\n rejected: number\n /** ๋งคํ•‘์€ ํ†ต๊ณผํ–ˆ์œผ๋‚˜ ํŠธ์œˆ์— ๋‹ฟ์ง€ ๋ชปํ•œ ์ˆ˜ โ€” ์ฒ˜๋ฐฉ์€ ํŠธ์œˆ์„ ์„ธ์šฐ๋Š” ๊ฒƒ. */\n undelivered: number\n /** ํ†ต๊ณผ์œจ(0~1). ์ œ์‹œ๊ฐ€ ์—†์œผ๋ฉด `null` โ€” **1 ๋กœ ์ฑ„์šฐ๋ฉด ใ€Œ์ „๋ถ€ ํ†ต๊ณผใ€๋กœ ์ฝํžŒ๋‹ค.** */\n acceptedRatio: number | null\n /** ์ด ์ฐฝ์— ์ ํžŒ ํ–‰. */\n rows: number\n /** ๊ทธ๊ฒƒ์„ ์ ๊ธฐ ์œ„ํ•ด DB ๋ฅผ ๋‘๋“œ๋ฆฐ ํšŸ์ˆ˜ โ€” ํ–‰๋‹น ์™•๋ณต์ด ์ค„์—ˆ๋Š”์ง€ ๋ณด๋Š” ๊ฐ’. */\n flushes: number\n}\n\nexport interface IngestHealthView {\n verdict: SyncVerdict\n /** ํŒ์ •์— ์“ด ๋ฌธํ„ฑ โ€” ํ™”๋ฉด์ด ์™œ ๊ทธ ํŒ์ •์ธ์ง€ ๋งํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•จ๊ป˜ ๋‚ธ๋‹ค. */\n threshold: number\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ ๋‹ซํžŒ ์ฐฝ. ์ฐฝ์ด ํ•œ ๋ฒˆ๋„ ๋‹ซํžˆ์ง€ ์•Š์•˜์œผ๋ฉด `null`. */\n recent: IngestWindowView | null\n /** ๋‹ซํžŒ ์ฐฝ๋“ค, ์˜ค๋ž˜๋œ ๊ฒƒ๋ถ€ํ„ฐ. */\n trend: IngestWindowView[]\n /** ์ถ”์ด ์ƒํ•œ์—์„œ ๋ฐ€๋ ค๋‚œ ์ฐฝ ์ˆ˜. */\n trendDropped: number\n /** ๋ˆ„์ (๊ธฐ๋™ ์ดํ›„). */\n total: {\n offered: number\n accepted: number\n rejected: number\n undelivered: number\n acceptedRatio: number | null\n /** ์ €๋„์— ์ ํžŒ ํ–‰๊ณผ ๊ทธ๊ฒƒ์„ ์œ„ํ•ด DB ๋ฅผ ๋‘๋“œ๋ฆฐ ํšŸ์ˆ˜ โ€” ๊ธฐ๋™ ์ดํ›„ ๋ˆ„์ . */\n rows: number\n flushes: number\n }\n /** ์‚ฌ์œ ๋ณ„ ๋ˆ„์ , ๋งŽ์€ ๊ฒƒ๋ถ€ํ„ฐ. `lastAt` ์ด ๋ฉˆ์ถ˜ ์‚ฌ์œ ๋Š” ์ด๋ฏธ ๊ณ ์ณ์ง„ ๊ฒƒ์ด๋‹ค. */\n reasons: { reason: string; count: number; lastAt: string }[]\n /** ์‚ฌ์œ  ์ข…๋ฅ˜๊ฐ€ ์ƒํ•œ์— ๋‹ฟ์•„ ์ผ๋ถ€๊ฐ€ `(other reasons)` ๋กœ ์ ‘ํ˜”๋‚˜. */\n reasonsCapped: boolean\n samples: RejectSample[]\n sampleDropped: number\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฌด์—‡์ด๋“  ๋ฐ›์€ ์‹œ๊ฐ. ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `null`. */\n lastAt: string | null\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ ์ €๋„์— ์ ์€ ์‹œ๊ฐ. ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `null` โ€” ๋ฐ›์€ ์  ์—†๋Š” ํŠธ์œˆ๋„ ์ด ๊ฐ’์€ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค. */\n lastWriteAt: string | null\n /**\n * **์›๋ณธ์— ๋‹ฟ์ง€ ๋ชปํ•˜๊ณ  ์žˆ๋‹ค** โ€” ์—†์œผ๋ฉด ๋‹ฟ๊ณ  ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค(ยง`IngestLedger.readFailure`).\n *\n * ์ปค์„œ ์ •์ฒด(`cursorStall`)์™€ **์„ž์ด์ง€ ์•Š๋Š”๋‹ค**: ์ด์ชฝ์€ ๊ธฐ๋‹ค๋ฆฌ๋ฉด ํ’€๋ฆฌ๊ณ  ๊ทธ์ชฝ์€ ๊ธฐ๋‹ค๋ ค๋„ ์•ˆ ํ’€๋ฆฐ๋‹ค.\n *\n * ใ€Œ์œ ์ž… ์—†์Œใ€๊ณผ **๋‹ค๋ฅธ ํŒ์ •**์ด๋‹ค: ์œ ์ž… ์—†์Œ์€ ์›๋ณธ์ด ์กฐ์šฉํ•œ ๊ฒƒ์ผ ์ˆ˜ ์žˆ๊ณ , ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ๋ฌป์ง€๋„\n * ๋ชปํ•œ ๊ฒƒ์ด๋‹ค. ํ™”๋ฉด์€ ๋‘ ๋ฌธ์žฅ์„ ๋‹ค๋ฅด๊ฒŒ ๋งํ•ด์•ผ ํ•œ๋‹ค โ€” ์‹ค ์›๋ณธ์ด ๋Š๊ฒผ์„ ๋•Œ ์‚ฌ์šฉ์ž๊ฐ€ ์›์ธ์„ ์ฐพ์„ ์ˆ˜\n * ์žˆ๋Š”์ง€๊ฐ€ ๊ทธ ์ฐจ์ด๋‹ค.\n */\n readFailure?: {\n consecutive: number\n /** ์ด ๋‹จ์ ˆ์ด ์‹œ์ž‘๋œ ์‹œ๊ฐ โ€” ใ€Œ20๋ถ„์งธใ€๋ฅผ ๋งํ•  ์ˆ˜ ์žˆ๊ฒŒ. */\n since: string\n lastAt: string\n reason: string\n stream?: string\n }\n}\n\nfunction toWindowView(w: IngestWindow, fallbackEndMs: number): IngestWindowView {\n /* ใ€Œํ†ต๊ณผใ€๋Š” **ํŠธ์œˆ์— ๋‹ฟ์€** ๊ฒƒ๋งŒ์ด๋‹ค. ๋ฒ„๋ ค์ง„ ๊ฒƒ์„ ํ†ต๊ณผ๋กœ ์„ธ๋ฉด ํ™”๋ฉด์ด ์•ˆ์‹ฌ์‹œํ‚จ๋‹ค. */\n const accepted = w.offered - w.rejected - w.undelivered\n return {\n from: new Date(w.startMs).toISOString(),\n to: new Date(w.endMs >= 0 ? w.endMs : fallbackEndMs).toISOString(),\n offered: w.offered,\n accepted,\n rejected: w.rejected,\n undelivered: w.undelivered,\n acceptedRatio: w.offered > 0 ? round4(accepted / w.offered) : null,\n rows: w.rows,\n flushes: w.flushes\n }\n}\n\n/**\n * ์ฝ๊ธฐ์šฉ ์š”์•ฝ.\n *\n * ์žฅ๋ถ€๊ฐ€ ์—†์–ด๋„(ํ•œ ๋ฒˆ๋„ ๋ฐ›์ง€ ์•Š์€ ํŠธ์œˆ) **ํŒ์ •์€ ๋‚ธ๋‹ค** โ€” ํ”ผ๋“œ ์‚ฌ์‹ค๋งŒ์œผ๋กœ ๋‹ตํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค\n * (`not-applicable`ยท`not-connected`). ๊ทธ ๊ฒฝ์šฐ ์ˆ˜์น˜๋Š” ๋น„์šด๋‹ค: ์—†๋Š” ๊ฐ’์„ 0 ์œผ๋กœ ์ฑ„์šฐ์ง€ ์•Š๋Š”๋‹ค.\n *\n * `nowMs` ๋ฅผ ๋ฐ›๋Š” ์ด์œ : ๋Š๊ธด ํ”ผ๋“œ๋ฅผ ๊ฐ€๋ ค๋‚ด๋ ค๋ฉด ใ€Œ๋งˆ์ง€๋ง‰ ์œ ์ž…์ด ์–ผ๋งˆ๋‚˜ ์ „์ธ๊ฐ€ใ€๋ฅผ ๋ด์•ผ ํ•˜๊ณ , ๊ทธ ํŒ๋‹จ์„\n * ํ™”๋ฉด์ด ์•„๋‹ˆ๋ผ ์—ฌ๊ธฐ์„œ ํ•œ๋‹ค(์†Œ๋น„์ฒ˜๋งˆ๋‹ค ๋‹ค์‹œ ํŒ๋‹จํ•˜๋ฉด ๊ทœ์น™์ด ๊ฐˆ๋ผ์ง„๋‹ค). ์‹œํ—˜์ด ์‹œ๊ฐ์„ ๊ณ ์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก\n * ์ธ์ž๋กœ ๋ฐ›๋Š”๋‹ค.\n */\nexport function ingestHealth(\n ledger: IngestLedger | undefined,\n feedState: 'attached' | 'detached' | 'not-applicable',\n threshold = HEALTHY_ACCEPTED_RATIO,\n nowMs: number = Date.now()\n): IngestHealthView {\n const recentRaw = ledger?.trend.length ? ledger.trend[ledger.trend.length - 1] : null\n /* ์—ด๋ฆฐ ์ฐฝ์˜ ๋ ์‹œ๊ฐ ๋Œ€์ฒด๊ฐ’ โ€” ์œ ์ž…์ด ์—†๋Š” ์žฅ๋ถ€์—์„œ `-1` ์„ ์‹œ๊ฐ์œผ๋กœ ์ฝ์œผ๋ฉด 1969๋…„์ด ํ™”๋ฉด์— ๋œฌ๋‹ค. */\n const lastAnyMs = Math.max(ledger?.lastAtMs ?? -1, ledger?.lastWriteAtMs ?? -1)\n const verdict = syncVerdictOf({\n feedState,\n recent: recentRaw,\n lastAtMs: ledger?.lastAtMs ?? -1,\n lastUndeliveredAtMs: ledger?.lastUndeliveredAtMs ?? -1,\n nowMs,\n threshold\n })\n\n /*\n * **์œ ์ž…์ด ์—†์–ด๋„ ใ€Œ๋นˆ ์žฅ๋ถ€ใ€๊ฐ€ ์•„๋‹ˆ๋‹ค.** ์‹œ๋ฎฌ ํŠธ์œˆ์€ ๋ฐ›๋Š” ๊ฒƒ ์—†์ด ์ ๋Š”๋‹ค. ์œ ์ž… ์‹œ๊ฐ๋งŒ ๋ณด๊ณ  ๋น„์—ˆ๋‹ค๊ณ \n * ํŒ๋‹จํ•˜๋ฉด ๊ทธ ํŠธ์œˆ์˜ ์ฐฝ์€ ๋งŒ๋“ค์–ด์ ธ ์žˆ๋Š”๋ฐ ํ™”๋ฉด์—๋Š” ์•„๋ฌด ์ถ”์ด๋„ ๋‚˜์˜ค์ง€ ์•Š๋Š”๋‹ค(์ด ์ถ•์„ ๋„ฃ์€ ์ด์œ ๊ฐ€ ๊ทธ๊ฒƒ์ด๋‹ค).\n */\n if (!ledger || (ledger.lastAtMs < 0 && ledger.lastWriteAtMs < 0)) {\n return {\n verdict,\n threshold,\n recent: null,\n trend: [],\n trendDropped: 0,\n total: { offered: 0, accepted: 0, rejected: 0, undelivered: 0, acceptedRatio: null, rows: 0, flushes: 0 },\n reasons: [],\n reasonsCapped: false,\n samples: [],\n sampleDropped: 0,\n lastAt: null,\n lastWriteAt: null,\n /*\n * **์ด ์งง์€ ๋‹ต์—๋„ ๋‹จ์ ˆ์€ ์‹ค์–ด์•ผ ํ•œ๋‹ค** โ€” ์ด ์ž๋ฆฌ๊ฐ€ ๋ฐ”๋กœ ๊ทธ ํŠธ์œˆ์˜ ๋ชจ์Šต์ด๋‹ค.\n *\n * ๋ฐ›์€ ๊ฒƒ๋„ ์ ์€ ๊ฒƒ๋„ ์—†๋Š” ์žฅ๋ถ€๋Š” ใ€Œ์•„์ง ์•„๋ฌด ์ผ๋„ ์—†๋‹คใ€๋กœ ์ฝํžˆ๋Š”๋ฐ, ์›๋ณธ์— ๋‹ฟ์ง€ ๋ชปํ•˜๋Š” ํŠธ์œˆ์ด\n * ์ •ํ™•ํžˆ ๊ทธ ๋ชจ์Šต์ด๋‹ค(์ฝ์ง€ ๋ชปํ•˜๋ฉด ๋ฐ›์€ ๊ฒƒ๋„ ์ ์€ ๊ฒƒ๋„ ์—†๋‹ค). ์—ฌ๊ธฐ์„œ ๋–จ์–ด๋œจ๋ฆฌ๋ฉด **๊ฐ€์žฅ ํ•„์š”ํ•œ\n * ์ˆœ๊ฐ„์—** ๊ทธ ์‚ฌ์‹ค์ด ์‚ฌ๋ผ์ง„๋‹ค โ€” ์‹ค์ธก์œผ๋กœ ์›๋ณธ์ด ๋Š๊ธด ํŠธ์œˆ์ด ์ด ๊ฒฝ๋กœ๋กœ ๋‹ตํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.\n */\n ...(ledger?.readFailure\n ? {\n readFailure: {\n consecutive: ledger.readFailure.consecutive,\n since: new Date(ledger.readFailure.sinceMs).toISOString(),\n lastAt: new Date(ledger.readFailure.lastAtMs).toISOString(),\n reason: ledger.readFailure.reason,\n ...(ledger.readFailure.stream ? { stream: ledger.readFailure.stream } : {})\n }\n }\n : {}),\n /* ์ปค์„œ ์ •์ฒด๋„ **์ด ๊ฐˆ๋ž˜์—์„œ** ๋‚ด๋ณด๋‚ธ๋‹ค โ€” ์œ ์ž…์ด ์—†๋Š” ์žฅ๋ถ€์—์„œ๋„ ์ •์ฒด๋Š” ์‹ค์žฌํ•œ๋‹ค(์ฝ์—ˆ์œผ๋‚˜ ๋ชป\n ๋„˜๊ฒผ์œผ๋ฉด ์œ ์ž…์ด 0์ด๋‹ค). ์—ฌ๊ธฐ์„œ ๋–จ์–ด๋œจ๋ฆฌ๋ฉด ๋ฐ”๋กœ ๊ทธ ์ƒํ™ฉ์—์„œ ์กฐ์šฉํ•ด์ง„๋‹ค. */\n ...(ledger?.cursorStall\n ? {\n cursorStall: {\n consecutive: ledger.cursorStall.consecutive,\n since: new Date(ledger.cursorStall.sinceMs).toISOString(),\n lastAt: new Date(ledger.cursorStall.lastAtMs).toISOString(),\n reason: ledger.cursorStall.reason,\n ...(ledger.cursorStall.stream ? { stream: ledger.cursorStall.stream } : {})\n }\n }\n : {})\n }\n }\n\n /* ใ€Œํ†ต๊ณผใ€๋Š” ํŠธ์œˆ์— ๋‹ฟ์€ ๊ฒƒ๋งŒ์ด๋‹ค โ€” ์ฐฝ ๋ทฐ์™€ ๊ฐ™์€ ๊ทœ์น™. */\n const accepted = ledger.offered - ledger.rejected - ledger.undelivered\n return {\n verdict,\n threshold,\n recent: recentRaw ? toWindowView(recentRaw, lastAnyMs) : null,\n trend: ledger.trend.map(w => toWindowView(w, lastAnyMs)),\n trendDropped: ledger.trendDropped,\n total: {\n offered: ledger.offered,\n accepted,\n rejected: ledger.rejected,\n undelivered: ledger.undelivered,\n acceptedRatio: ledger.offered > 0 ? round4(accepted / ledger.offered) : null,\n rows: ledger.rows,\n flushes: ledger.flushes\n },\n reasons: Object.entries(ledger.byReason)\n .map(([reason, v]) => ({ reason, count: v.count, lastAt: new Date(v.lastAtMs).toISOString() }))\n .sort((a, b) => b.count - a.count || a.reason.localeCompare(b.reason)),\n reasonsCapped: ledger.reasonsCapped,\n samples: [...ledger.samples],\n sampleDropped: ledger.sampleDropped,\n /* ใ€Œ๋ฐ›์€ ์  ์—†์Œใ€๊ณผ ใ€Œ๋ฐฉ๊ธˆ ๋ฐ›์Œใ€์„ ๊ฐ€๋ฅธ๋‹ค โ€” ์ ๊ธฐ๋งŒ ํ•˜๋Š” ํŠธ์œˆ์˜ ์œ ์ž… ์‹œ๊ฐ์€ **์—†๋‹ค**. */\n lastAt: ledger.lastAtMs >= 0 ? new Date(ledger.lastAtMs).toISOString() : null,\n lastWriteAt: ledger.lastWriteAtMs >= 0 ? new Date(ledger.lastWriteAtMs).toISOString() : null,\n /* ๋‹ฟ์ง€ ๋ชปํ•˜๊ณ  ์žˆ์œผ๋ฉด ๊ทธ ์‚ฌ์‹ค์„ ๋‚ธ๋‹ค โ€” ์—†์œผ๋ฉด ํ•„๋“œ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค(๋นˆ ๊ฐ’์ด ใ€Œ๋‹ฟ๋Š”๋‹คใ€๋กœ ์ฝํžˆ์ง€ ์•Š๊ฒŒ). */\n ...(ledger.readFailure\n ? {\n readFailure: {\n consecutive: ledger.readFailure.consecutive,\n since: new Date(ledger.readFailure.sinceMs).toISOString(),\n lastAt: new Date(ledger.readFailure.lastAtMs).toISOString(),\n reason: ledger.readFailure.reason,\n ...(ledger.readFailure.stream ? { stream: ledger.readFailure.stream } : {})\n }\n }\n : {}),\n ...(ledger.cursorStall\n ? {\n cursorStall: {\n consecutive: ledger.cursorStall.consecutive,\n since: new Date(ledger.cursorStall.sinceMs).toISOString(),\n lastAt: new Date(ledger.cursorStall.lastAtMs).toISOString(),\n reason: ledger.cursorStall.reason,\n ...(ledger.cursorStall.stream ? { stream: ledger.cursorStall.stream } : {})\n }\n }\n : {})\n }\n}\n\n/** ๋น„์œจ์€ ์ž๋ฆฟ์ˆ˜๋ฅผ ๋„‰๋„‰ํžˆ ์ค€๋‹ค โ€” 0.999 ๊ฐ€ 1 ๋กœ ๋ฐ˜์˜ฌ๋ฆผ๋˜๋ฉด ใ€Œ์ „๋ถ€ ํ†ต๊ณผใ€๋กœ ์ฝํžŒ๋‹ค. */\nconst round4 = (v: number) => Math.round(v * 10000) / 10000\n"]}
1
+ {"version":3,"file":"ingest-health.js","sourceRoot":"","sources":["../../server/engine/ingest-health.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * ๋™๊ธฐํ™” ๊ฑด๊ฐ• โ€” **์ด ํŠธ์œˆ์ด ํ˜„์žฅ๊ณผ ๋งž์ถฐ์ง€๊ณ  ์žˆ๋‚˜.** ์ˆœ์ˆ˜(import ์—†์Œ).\n *\n * โ”€โ”€ ํ•œ ์งˆ๋ฌธ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ํ™”๋ฉด์—์„œ ๊ฐ’์ด ๋ณ€ํ•˜์ง€ ์•Š์„ ๋•Œ ์›์ธ์ด ๋„ท์ธ๋ฐ, ์ง€๊ธˆ๊นŒ์ง€ **๋„ท์ด ๋˜‘๊ฐ™์ด ๋ณด์˜€๋‹ค.**\n * โ‘  ํ”ผ๋“œ๊ฐ€ ๋ถ™์ง€ ์•Š์•˜๋‹ค โ‘ก ๋ถ™์—ˆ๋Š”๋ฐ ์•„๋ฌด๊ฒƒ๋„ ์˜ค์ง€ ์•Š๋Š”๋‹ค\n * โ‘ข ์˜ค๋Š”๋ฐ ์ „๋ถ€ ๊ฑฐ๋ถ€๋œ๋‹ค โ‘ฃ ์ •์ƒ์ธ๋ฐ ํ˜„์žฅ์ด ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†๋‹ค\n * โ‘ข์€ ํŠนํžˆ ๋‚˜์˜๋‹ค โ€” ์ปค๋„ฅํ„ฐ์˜ ๋งคํ•‘์ด ํ•œ ๊ธ€์ž ํ‹€๋ฆฌ๋ฉด ํŠธ์œˆ์€ ใ€Œ๋นˆ ๊ณต์žฅใ€์ด ๋˜๊ณ , ๊ทธ ์‚ฌ์‹ค์ด ์„œ๋ฒ„ ๋กœ๊ทธ\n * ํ•œ ์ค„์—๋งŒ ๋‚จ์•˜๋‹ค. ํ˜„์žฅ์— ๋‚˜๊ฐ„ ์‚ฌ๋žŒ์€ ๋กœ๊ทธ๋ฅผ ๋’ค์ ธ์•ผ ํ–ˆ๋‹ค.\n *\n * โ”€โ”€ ์™œ ์ €๋„๋กœ๋Š” ๋‹ตํ•  ์ˆ˜ ์—†๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * **๊ฑฐ๋ถ€๋œ ๊ฒƒ์€ ์ €๋„์— ์—†๋‹ค** โ€” ๊ฑฐ๋ถ€์˜ ๋œป์ด ๊ทธ๊ฒƒ์ด๋‹ค. ์ €๋„์„ ๊ณ„์‚ฐํ•˜๋ฉด ํ†ต๊ณผํ•œ ์ชฝ๋งŒ ๋‚˜์˜จ๋‹ค. ๊ฑฐ๋ถ€์˜ ์ถ”์ด๋Š”\n * ์„ธ๋Š” ์ž๋ฆฌ๋ฅผ ๋”ฐ๋กœ ๋‘์–ด์•ผ ๋‚˜์˜จ๋‹ค. ์ด ์žฅ๋ถ€๊ฐ€ ๊ทธ ์ž๋ฆฌ๋‹ค.\n *\n * โ”€โ”€ ์™œ ๊ฑด๋งˆ๋‹ค ์ €์žฅํ•˜์ง€ ์•Š๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ๋ผ์ด๋ธŒ ๊ณ„์ธก์€ ์ดˆ๋‹น ์—ฌ๋Ÿฌ ๊ฑด์ด๋‹ค. ๊ฑฐ๋ถ€๋งˆ๋‹ค ํ–‰์„ ์“ฐ๋ฉด ๊ทธ ์ž์ฒด๊ฐ€ ๋ถ€ํ•˜๊ฐ€ ๋œ๋‹ค(`reference-live.ts` ๊ฐ€\n * ์ด๋ฏธ ๊ทธ๋ ‡๊ฒŒ ์ ์–ด ๋‘์—ˆ๋‹ค). ๊ทธ๋ฆฌ๊ณ  ์ด ํŒจํ‚ค์ง€๋Š” **`integration-base` ๋ฅผ ์•Œ์ง€ ์•Š๋Š”๋‹ค**(์ด์‹์„ฑ ๊ทœ์•ฝ) โ€”\n * ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒฝ๋กœ๊ฐ€ ์“ฐ๋Š” dead-letter ๋ฅผ ์—ฌ๊ธฐ์„œ ๋ถ€๋ฅผ ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ž˜์„œ **์ง‘๊ณ„ํ•œ๋‹ค.**\n *\n * โ”€โ”€ ํ†ต๊ณผ์™€ ๊ฑฐ๋ถ€๋ฅผ ํ•จ๊ป˜ ์„ผ๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ๊ฑฐ๋ถ€๋งŒ ๊ทธ๋ฆฌ๋ฉด ใ€Œ์œ ์ž… ์—†์Œใ€๊ณผ ใ€Œ์ •์ƒใ€์ด **๋‘˜ ๋‹ค ๊ฑฐ๋ถ€ 0** ์œผ๋กœ ๊ฐ™์•„ ๋ณด์ธ๋‹ค. ์ œ์‹œ ์ˆ˜๊ฐ€ ์žˆ์–ด์•ผ ๊ฐˆ๋ฆฐ๋‹ค.\n *\n * โ”€โ”€ ์ง€ํ‚ค๋Š” ๊ทœ์œจ ๋„ท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * โ‘  **๋‹ซํžŒ ์ฐฝ๋งŒ ใ€Œ์ตœ๊ทผใ€์ด๋‹ค** โ€” ์ฑ„์šฐ๋Š” ์ค‘์ธ ์ฐฝ์€ ๊ณผ์†Œ ์ง‘๊ณ„๋‹ค(๋ถ€ํ•˜ ๊ณ„๊ธฐํŒ์—์„œ ๊ฒช์—ˆ๋‹ค).\n * โ‘ก **0 ๊ณผ ใ€Œ์—†์Œใ€์„ ๊ฐ€๋ฅธ๋‹ค** โ€” ์•ˆ ๋ฐ›์€ ๊ฒƒ์€ `null`, ๋ฐ›์•˜์ง€๋งŒ ๊ฑฐ๋ถ€ 0 ์€ `0`.\n * โ‘ข **์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ž๋ฅด์ง€ ์•Š๋Š”๋‹ค** โ€” ํ‘œ๋ณธยท์ถ”์ด ์ƒํ•œ์—์„œ ๋ฐ€๋ ค๋‚œ ์ˆ˜๋ฅผ ํ•จ๊ป˜ ๋‚ธ๋‹ค.\n * โ‘ฃ **ํ†ต๊ณผ์œจ์— 1 ์„ ํ•จ๋ถ€๋กœ ์“ฐ์ง€ ์•Š๋Š”๋‹ค** โ€” 1,000๊ฑด ์ค‘ 1๊ฑด ๊ฑฐ๋ถ€๋Š” 0.999 ๋‹ค.\n */\n\n/** ์ถ”์ด ์ฐฝ ํ•˜๋‚˜ โ€” ์ œ์‹œยทํ†ต๊ณผยท๊ฑฐ๋ถ€ยท๋ฏธ์ „๋‹ฌ, ๊ทธ๋ฆฌ๊ณ  **์ ํžŒ ๊ฒƒ**์„ ํ•จ๊ป˜ ๋“ ๋‹ค. */\nexport interface IngestWindow {\n /** ์ฐฝ์ด ์—ด๋ฆฐ ์‹œ๊ฐ(ms). */\n startMs: number\n /** ์ฐฝ์ด ๋‹ซํžŒ ์‹œ๊ฐ(ms). ์•„์ง ์ฑ„์šฐ๋Š” ์ค‘์ด๋ฉด `-1`. */\n endMs: number\n /** ์ด ์ฐฝ์— ์ œ์‹œ๋œ ๋ ˆ์ฝ”๋“œ ์ˆ˜(๊ฑฐ๋ถ€ ์—ฌ๋ถ€ ๋ฌด๊ด€). */\n offered: number\n /** ๊ทธ์ค‘ **๋งคํ•‘์ด ๊ฑฐ๋ถ€ํ•œ** ์ˆ˜ โ€” ์ฒ˜๋ฐฉ์€ ๋งคํ•‘์„ ๊ณ ์น˜๋Š” ๊ฒƒ์ด๋‹ค. */\n rejected: number\n /**\n * ๋งคํ•‘์€ ํ†ต๊ณผํ–ˆ์ง€๋งŒ **ํŠธ์œˆ์— ๋‹ฟ์ง€ ๋ชปํ•œ** ์ˆ˜ โ€” ์ฒ˜๋ฐฉ์€ ํŠธ์œˆ์„ ์„ธ์šฐ๋Š” ๊ฒƒ์ด๋‹ค.\n *\n * โ”€โ”€ ์™œ ๊ฑฐ๋ถ€์™€ ๋”ฐ๋กœ ์„ธ๋‚˜ (2026-08-20์— ๊ฒช์—ˆ๋‹ค) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ํŠธ์œˆ์ด ๋ผ์ด๋ธŒ๋กœ ๋Œ์ง€ ์•Š์œผ๋ฉด ์ปค๋„์ด ๋ ˆ์ฝ”๋“œ๋ฅผ **์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฐ๋‹ค**. ๊ทธ๋Ÿฐ๋ฐ ์žฅ๋ถ€๋Š” ๊ทธ๊ฒƒ์„ ใ€Œํ†ต๊ณผใ€๋กœ ์…Œ๋‹ค.\n * ํ™”๋ฉด์ด ใ€Œ150 ๋“ค์–ด์˜ด ยท 150 ํ†ต๊ณผ ยท 100%ใ€๋ผ๊ณ  ๋งํ•˜๋Š” ๋™์•ˆ 150๊ฑด์€ ๋ฐ”๋‹ฅ์— ๋ฒ„๋ ค์ง€๊ณ  ์žˆ์—ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์ด\n * ๋ง‰์œผ๋ ค๋˜ ๋ฐ”๋กœ ๊ทธ๊ฒƒ์„ ์ด ๊ธฐ๋Šฅ์ด ์ €์ง€๋ฅธ ์…ˆ์ด๋‹ค.\n *\n * ๊ฑฐ๋ถ€์— ํ•ฉ์น˜๋Š” ๊ฒƒ๋„ ๋‹ต์ด ์•„๋‹ˆ๋‹ค โ€” ๊ฑฐ๋ถ€๋Š” ใ€Œ๋งคํ•‘์„ ๊ณ ์ณ๋ผใ€๋Š” ๋ง์ด๊ณ , ์—ฌ๊ธฐ์„œ ํ•„์š”ํ•œ ๋ง์€ ใ€ŒํŠธ์œˆ์ด ์•ˆ\n * ๋Œ๊ณ  ์žˆ๋‹คใ€๋‹ค. **์ฒ˜๋ฐฉ์ด ๋‹ค๋ฅด๋ฉด ๋‹ค๋ฅธ ์ˆ˜๋กœ ์„ผ๋‹ค.**\n */\n undelivered: number\n /**\n * ์ด ์ฐฝ์— **์ €๋„์— ์ ํžŒ ํ–‰** ์ˆ˜ โ€” ใ€Œ๋ฌด์—‡์ด ๋‚จ์•˜๋‚˜ใ€.\n *\n * ์œ ์ž…๊ณผ ๋‹ค๋ฅธ ์ถ•์ด๋‹ค: ์‹œ๋ฎฌ ํŠธ์œˆ์€ ๋ฐ›๋Š” ๊ฒƒ ์—†์ด ์ž๊ธฐ๊ฐ€ ๋‚ธ ์‚ฌ์‹ค์„ ์ ๋Š”๋‹ค(๊ทธ๋ž˜์„œ `offered` ๋Š” 0 ์ด๊ณ \n * `rows` ๋Š” 0 ์ด ์•„๋‹ˆ๋‹ค). ์ด ์นธ์ด ์—†์œผ๋ฉด ๊ทธ ํŠธ์œˆ์˜ ํ™œ๋™์€ ์–ด๋А ์ถ”์ด์—๋„ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š๋Š”๋‹ค.\n */\n rows: number\n /**\n * ๊ทธ๊ฒƒ์„ ์ ๊ธฐ ์œ„ํ•ด **DB ๋ฅผ ๋‘๋“œ๋ฆฐ ํšŸ์ˆ˜**(flush) โ€” ใ€Œ์šฐ๋ฆฌ๊ฐ€ ๋ช‡ ๋ฒˆ ๊ฐ”๋‚˜ใ€.\n *\n * ํ–‰ ์ˆ˜์™€ ๋”ฐ๋กœ ์„ธ๋Š” ์ด์œ : 2026-08-20 ์— ๊ณ ์นœ ๊ฒƒ์ด ๋ฐ”๋กœ ์ด ๊ฐ’์ด๋‹ค(ํ–‰๋งˆ๋‹ค ํ•œ ๋ฒˆ โ†’ ์ฐฝ๋‹น ํ•œ ๋ฒˆ). ํ–‰๋งŒ\n * ์„ธ๋ฉด ๊ทธ ๊ฐœ์„ ๋„, ๋˜๋Œ์•„๊ฐ€๋Š” ๊ฒƒ๋„ ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค.\n */\n flushes: number\n}\n\n/** ๊ฑฐ๋ถ€ ํ‘œ๋ณธ ํ•˜๋‚˜ โ€” ๋งคํ•‘์„ ๊ณ ์น˜๋ ค๋ฉด ์‹ค์ œ ๋ชจ์–‘์„ ๋ด์•ผ ํ•œ๋‹ค. */\nexport interface RejectSample {\n atMs: number\n /** ์ปค๋„์ด ๋‚ธ ์‚ฌ์œ . ์šฐ๋ฆฌ๊ฐ€ ๋‹ค์‹œ ์“ฐ์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์˜ฎ๊ธด๋‹ค. */\n errors: string[]\n /** ๋ง‰ํžŒ ๋ ˆ์ฝ”๋“œ์˜ ์ง๋ ฌํ™”. ๊ธธ๋ฉด ์ž๋ฅด๊ณ , ์ž˜๋ž๋‹ค๊ณ  ํ‘œ์‹œํ•œ๋‹ค. */\n record: string\n recordTruncated?: boolean\n}\n\nexport interface IngestLedger {\n /** ์ง€๊ธˆ ์ฑ„์šฐ๋Š” ์ค‘์ธ ์ฐฝ. `startMs < 0` ์ด๋ฉด ์•„์ง ์•„๋ฌด๊ฒƒ๋„ ๋ฐ›์ง€ ์•Š์•˜๋‹ค. */\n window: IngestWindow\n /** ๋‹ซํžŒ ์ฐฝ๋“ค โ€” ์˜ค๋ž˜๋œ ๊ฒƒ์ด ์•ž. ์ƒํ•œ์„ ๋„˜์œผ๋ฉด ์•ž์—์„œ ๋ฐ€๋ ค๋‚œ๋‹ค. */\n trend: IngestWindow[]\n /** ์ถ”์ด์—์„œ ๋ฐ€๋ ค๋‚œ ์ฐฝ ์ˆ˜. */\n trendDropped: number\n /** ๋ˆ„์  ์ œ์‹œยท๊ฑฐ๋ถ€ยท๋ฏธ์ „๋‹ฌ. */\n offered: number\n rejected: number\n undelivered: number\n /** ๋ˆ„์  โ€” ์ ํžŒ ํ–‰๊ณผ ๋‘๋“œ๋ฆฐ ํšŸ์ˆ˜. */\n rows: number\n flushes: number\n /**\n * ๋งˆ์ง€๋ง‰์œผ๋กœ **์ ์€** ์‹œ๊ฐ. ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `-1`.\n *\n * ์œ ์ž… ์‹œ๊ฐ(`lastAtMs`)๊ณผ ๊ฐˆ๋ผ ๋‘๋Š” ์ด์œ ๋Š” ์œ„ `recordJournalWrite` ์ฃผ์„์— ์žˆ๋‹ค โ€” ์„ž์œผ๋ฉด ๋Š๊ธด ํ”ผ๋“œ๊ฐ€\n * ์ดˆ๋ก์œผ๋กœ ๋ณด์ธ๋‹ค.\n */\n lastWriteAtMs: number\n /**\n * ๋งˆ์ง€๋ง‰์œผ๋กœ **๋ฒ„๋ ค์ง„** ์‹œ๊ฐ(ms). ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `-1`.\n *\n * ์ฐฝ๊ณผ ๋”ฐ๋กœ ๋‘”๋‹ค: ๋ฏธ์ „๋‹ฌ์€ ์ฐฝ์ด ๋‹ซํžˆ๊ธฐ ์ „์— ์•Œ์•„์•ผ ํ•œ๋‹ค(๊ทธ ์‚ฌ์ด์—๋„ ์‚ฌ์‹ค์€ ๊ณ„์† ์‚ฌ๋ผ์ง€๊ณ  ์žˆ๋‹ค).\n */\n lastUndeliveredAtMs: number\n /** ์‚ฌ์œ ๋ณ„ ๋ˆ„์  ๊ฑด์ˆ˜์™€ **๋งˆ์ง€๋ง‰์œผ๋กœ ๊ทธ ์‚ฌ์œ ๊ฐ€ ๋‚œ ์‹œ๊ฐ** โ€” ๊ณ ์นœ ์‚ฌ์œ ๋Š” ์‹œ๊ฐ์ด ๋ฉˆ์ถ˜๋‹ค. */\n /**\n * **์–ด๋–ค ๊ธธ๋กœ ๋“ค์–ด์™”๋‚˜** โ€” ๊ฒฝ๋กœ๋งˆ๋‹ค ๊ฑด์ˆ˜์™€ ๋งˆ์ง€๋ง‰ ์‹œ๊ฐ.\n *\n * โ”€โ”€ ์™œ ํ•„์š”ํ•œ๊ฐ€ (2026-08-26) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ํŠธ์œˆ์ด ๊ฐ’์„ ๋ฐ›๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์€ ๊ณ„๊ธฐ๊ฐ€ ๋งํ•ด ์ฃผ๋Š”๋ฐ, **๋ฌด์—‡์„ ํ†ตํ•ด ๋ฐ›๋Š”์ง€๋Š” ์•„๋ฌด๋„ ๋งํ•˜์ง€ ์•Š์•˜๋‹ค.**\n * ์ปค๋„ฅํ„ฐ๊ฐ€ ๋ฌผ์–ด์„œ ๋ฐ›๋Š”์ง€, ์—ฐ๊ฒฐ๋œ ์‹œ์Šคํ…œ์ด ๋ฐ€์–ด ์ฃผ๋Š”์ง€, ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ๋„ฃ๋Š”์ง€, ๋ถ€ํ•˜ ๋„๊ตฌ๊ฐ€ ๋„ฃ๋Š”์ง€ โ€”\n * ํ™”๋ฉด์—์„œ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์—†์—ˆ๋‹ค. ๊ทธ ๊ตฌ๋ณ„์ด ์—†์œผ๋ฉด ใ€Œํด๋ง์„ ์›นํ›…์œผ๋กœ ๋ฐ”๊ฟจ๋‹คใ€๋ฅผ ํ™•์ธํ•  ๋ฐฉ๋ฒ•๋„ ์—†๋‹ค.\n */\n bySource: Record<string, { count: number; lastAtMs: number }>\n /**\n * **๊ฐ™์€ ์‚ฌ์‹ค์ด ๋‘ ๋ฒˆ ์™€์„œ ๋ฒ„๋ฆฐ ์ˆ˜** โ€” ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n *\n * ์ด ์ˆ˜๊ฐ€ ํฌ๋ฉด ๋ณด๋‚ด๋Š” ์ชฝ์ด ๋˜ํ’€์ด ๋ณด๋‚ด๊ณ  ์žˆ๋‹ค๋Š” ๋œป์ด๊ณ , ๊ทธ๊ฒƒ์€ ๊ณ ์น  ์ผ์ด๋‹ค(ํƒœ์–‘๊ด‘ ์ปค๋„ฅํ„ฐ๊ฐ€ ํ•œ ์ฃผ๊ธฐ์—\n * 76๊ฑด์„ ๋˜ํ’€์ด ๋ณด๋‚ด๋˜ ๊ฒƒ์„ ์ด ์ˆ˜๋กœ ๋ณผ ์ˆ˜ ์žˆ์—ˆ์–ด์•ผ ํ–ˆ๋‹ค).\n */\n duplicates: number\n lastDuplicateAtMs: number\n byReason: Record<string, { count: number; lastAtMs: number }>\n /**\n * ์‚ฌ์œ  ์ข…๋ฅ˜๊ฐ€ ์ƒํ•œ์— ๋‹ฟ์•˜๋‚˜.\n *\n * **๋ชจ์€ ์ข…๋ฅ˜ ์ˆ˜๋Š” ์„ธ์ง€ ์•Š๋Š”๋‹ค** โ€” ๊ทธ๊ฒƒ์„ ์„ธ๋ ค๋ฉด ์ด๋ฏธ ๋ณธ ์‚ฌ์œ ๋ฅผ ๋ชจ๋‘ ๊ธฐ์–ตํ•ด์•ผ ํ•˜๊ณ , ๊ทธ ์ง‘ํ•ฉ์ด\n * ๋ฐ”๋กœ ์ด ์ƒํ•œ์œผ๋กœ ๋ง‰์œผ๋ ค๋˜ ๋ฌดํ•œํ•œ ์ง‘ํ•ฉ์ด๋‹ค. ๋ชจ์€ **๊ฑด์ˆ˜**๋Š” `byReason[OTHER_REASON].count` ๊ฐ€ ๋‹ตํ•œ๋‹ค.\n */\n reasonsCapped: boolean\n /** ํ‘œ๋ณธ์„ ์ด๋ฏธ ๋œฌ ์‚ฌ์œ ๋“ค โ€” ๊ฐ™์€ ์‚ฌ์œ ๋ฅผ ๋‹ค์‹œ ์ง๋ ฌํ™”ํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•œ ์ƒ‰์ธ(O(1) ์กฐํšŒ). */\n sampledReasons: Set<string>\n /** ์ตœ๊ทผ ๊ฑฐ๋ถ€ ํ‘œ๋ณธ. */\n samples: RejectSample[]\n /** ํ‘œ๋ณธ์—์„œ ๋ฐ€๋ ค๋‚œ ๊ฑฐ๋ถ€ ๊ฑด์ˆ˜. */\n sampleDropped: number\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฌด์—‡์ด๋“  ๋ฐ›์€ ์‹œ๊ฐ(ms). **ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `-1`** โ€” 0 ์œผ๋กœ ๋‘๋ฉด ์‹œ๊ฐ 0 ๊ณผ ๊ตฌ๋ณ„๋˜์ง€ ์•Š๋Š”๋‹ค. */\n lastAtMs: number\n /**\n * **์›๋ณธ์— ๋‹ฟ์ง€ ๋ชปํ•œ ์‚ฌ์‹ค** (2026-08-23) โ€” ใ€Œ๋ฐ›์€ ๊ฒƒ์ด ์—†๋‹คใ€์™€ **๋‹ค๋ฅธ ์‚ฌ์‹ค**์ด๋‹ค.\n *\n * โ”€โ”€ ๋ฌด์—‡์ด ์•„๋ฌด ์‹ ํ˜ธ๋„ ๋‚ด์ง€ ์•Š์•˜๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์‹ค ์›๋ณธ์ด ๋Š๊ฒผ์„ ๋•Œ(์ ‘์† ์‹œ๊ฐ„ ์ดˆ๊ณผ) ํŠธ์œˆ์˜ **์กฐํšŒ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ ์–ด๋””์—๋„** ๊ทธ ์‚ฌ์‹ค์ด ์—†์—ˆ๋‹ค.\n * `recordIngest` ๋Š” `onRecords` ๊ฐ€ ๋ถˆ๋ฆด ๋•Œ๋งŒ ๋„๋‹ฌํ•˜๊ณ , ์ฝ๊ธฐ๊ฐ€ ์‹คํŒจํ•˜๋ฉด ๊ทธ ๋ฌธ์— ์˜ค์ง€ ์•Š๋Š”๋‹ค.\n * `TwinReference.lastError` ๋Š” ๋งˆ์Šคํ„ฐ ๋™๊ธฐยท์ ‘์† ์‹œํ—˜์—๋งŒ ์ ํžŒ๋‹ค.\n *\n * ๊ทธ๋ž˜์„œ ํ™”๋ฉด์ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ใ€Œ์ƒˆ ์‚ฌ์‹ค์ด ์—†๋‹คใ€๋ฟ์ด์—ˆ๊ณ , ๊ทธ๊ฒƒ์€ **ใ€Œ์—ฐ๊ฒฐ๋œ ์‹œ์Šคํ…œ์—์„œ ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†๋‹คใ€์™€ ๊ตฌ๋ณ„๋˜์ง€\n * ์•Š๋Š”๋‹ค.** ์‹ค์ฆ ๋ฐ๋ชจ ์ค‘์— ์›๋ณธ์ด ๋Š๊ธฐ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์›์ธ์„ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค.\n *\n * โ”€โ”€ ์™œ ใ€Œ๋ช‡ ๋ฒˆใ€๊ณผ ใ€Œ์–ธ์ œ๋ถ€ํ„ฐใ€๋ฅผ ํ•จ๊ป˜ ๋‘๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ํ•œ ๋ฒˆ ์‹คํŒจํ•œ ๊ฒƒ๊ณผ 20๋ถ„์งธ ๋ชป ์ฝ๋Š” ๊ฒƒ์€ ๋‹ค๋ฅธ ์‚ฌ์‹ค์ด๋‹ค. ๊ทธ ๊ตฌ๋ณ„์ด ์—†์œผ๋ฉด ํ™”๋ฉด์ด ์ˆœ๊ฐ„์˜ ์‹คํŒจ์— ๋†€๋ผ๊ฑฐ๋‚˜\n * ๊ณ„์†๋œ ๋‹จ์ ˆ์„ ๋Œ€์ˆ˜๋กญ์ง€ ์•Š๊ฒŒ ๋งํ•œ๋‹ค. ๊ทธ๋ž˜์„œ **์—ฐ์† ์‹คํŒจ ์ˆ˜**์™€ **์ฒ˜์Œ ์‹คํŒจํ•œ ์‹œ๊ฐ**์„ ํ•จ๊ป˜ ๋“ ๋‹ค.\n *\n * ์„ฑ๊ณตํ•˜๋ฉด ์ง€์šด๋‹ค โ€” ๊ณ ์ณ์ง„ ๋‹จ์ ˆ์ด ํ™”๋ฉด์— ๋‚จ์•„ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ๋„ ๊ฑฐ์ง“์ด๋‹ค.\n */\n /**\n * ๋งˆ์ง€๋ง‰์œผ๋กœ ๋Š๊ฒผ๋‹ค๊ฐ€ ๋Œ์•„์˜จ ๊ฒƒ โ€” ์—†์œผ๋ฉด ๋Š๊ธด ์ ์ด ์—†๊ฑฐ๋‚˜ ์•„์ง ๋Œ์•„์˜ค์ง€ ์•Š์€ ๊ฒƒ์ด๋‹ค.\n *\n * ์‹คํŒจ ๊ธฐ๋ก(`readFailure`)์€ ๋Œ์•„์˜ค๋ฉด ์ง€์›Œ์ง„๋‹ค. ๊ทธ๊ฒƒ๋งŒ ์ง€์šฐ๋ฉด ใ€Œ๋Š๊ธด ์ ์ด ์žˆ์—ˆ๋‹คใ€๊ฐ€ ํ™”๋ฉด์—์„œ\n * ์‚ฌ๋ผ์ง€๋ฏ€๋กœ, ๋Œ์•„์˜จ ์‚ฌ์‹ค์„ ๋”ฐ๋กœ ๋‚จ๊ธด๋‹ค.\n */\n lastRecovery?: { atMs: number; afterFailures: number; downMs: number }\n readFailure?: {\n /** ์—ฐ์† ์‹คํŒจ ์ˆ˜ โ€” ์„ฑ๊ณต ํ•œ ๋ฒˆ์— 0 ์œผ๋กœ ๋Œ์•„๊ฐ„๋‹ค. */\n consecutive: number\n /** ์ด ๋‹จ์ ˆ์ด **์ฒ˜์Œ** ์‹œ์ž‘๋œ ์‹œ๊ฐ(ms). ์—ฐ์†์ด ๋Š๊ธฐ๋ฉด ๋‹ค์‹œ ์ •ํ•ด์ง„๋‹ค. */\n sinceMs: number\n /** ๋งˆ์ง€๋ง‰ ์‹คํŒจ ์‹œ๊ฐ(ms). */\n lastAtMs: number\n /** ๋งˆ์ง€๋ง‰ ์‹คํŒจ์˜ ์‚ฌ์œ  โ€” ์‚ฌ๋žŒ์ด ์ฝ๋Š” ํ•œ ์ค„(์›๋ณธ์ด ์ค€ ๋ง ๊ทธ๋Œ€๋กœ). */\n reason: string\n /** ์–ด๋А ํ๋ฆ„์—์„œ ๋‚ฌ๋‚˜(์–ด๋Œ‘ํ„ฐ๊ฐ€ ๋งํ•ด ์ฃผ๋ฉด). ์›๋ณธ๋งˆ๋‹ค ํ๋ฆ„์ด ๋‹ค๋ฅด๋ฏ€๋กœ ์—ด๋ ค ์žˆ๋‹ค. */\n stream?: string\n /** ๋‹ค์Œ์— ๋‹ค์‹œ ๋ฌผ์„ ๋•Œ๊นŒ์ง€ ๋‚จ์€ ์‹œ๊ฐ„(ms) โ€” ์ปค๋„ฅํ„ฐ๊ฐ€ ๋ฌผ๋Ÿฌ์„ฐ์„ ๋•Œ๋งŒ ์žˆ๋‹ค. */\n nextRetryMs?: number\n }\n /**\n * **์ฝ์—ˆ๋Š”๋ฐ ์ฐฝ์„ ๋„˜๊ธธ ์ˆ˜ ์—†๋‹ค** (2026-08-24) โ€” ์œ„์™€ **๋‹ค๋ฅธ ์‚ฌ์‹ค**์ด๋‹ค.\n *\n * โ”€โ”€ ์™œ ๊ฐˆ๋ž๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์ด ์นธ์„ ๋งŒ๋“ค๊ธฐ ์ „์—๋Š” ๋‘ ์‚ฌ์‹ค์ด ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ๋‚˜๊ฐ”๋‹ค.\n *\n * readFailure ์›๋ณธ์— ๋‹ฟ์ง€ ๋ชปํ–ˆ๋‹ค โ†’ **๊ธฐ๋‹ค๋ฆฌ๋ฉด ํ’€๋ฆฐ๋‹ค**\n * cursorStall ์ฝ์—ˆ๋Š”๋ฐ ์ปค์„œ๊ฐ€ ๋ชป ๋„˜์–ด๊ฐ„๋‹ค โ†’ **๊ธฐ๋‹ค๋ ค๋„ ์•ˆ ํ’€๋ฆฐ๋‹ค**\n *\n * ๋‘˜์งธ๋Š” ์ฝ๊ธฐ๊ฐ€ ์„ฑ๊ณตํ•œ ์‹คํŒจ๋‹ค: ํ•œ ์‹œ๊ฐ์— ํ•œ ํŽ˜์ด์ง€๋ณด๋‹ค ๋งŽ์€ ํ–‰์ด ๋ชฐ๋ ค ์ปค์„œ๋ฅผ ๊ทธ ์‹œ๊ฐ ๋ฐ–์œผ๋กœ ์˜ฎ๊ธธ ์ˆ˜\n * ์—†๋‹ค. ํ™”๋ฉด์ด ใ€Œ์›๋ณธ์— ๋‹ฟ์ง€ ๋ชปํ•œ๋‹คใ€๊ณ  ๋งํ•˜๋ฉด ์‚ฌ๋žŒ์€ ์›๋ณธ์„ ์˜์‹ฌํ•˜๊ณ  ๊ธฐ๋‹ค๋ฆฌ๋Š”๋ฐ, ํ•„์š”ํ•œ ์กฐ์น˜๋Š”\n * ํŽ˜์ด์ง€๋ฅผ ํ‚ค์šฐ๊ฑฐ๋‚˜ ๊ฐ™์€ ์‹œ๊ฐ ์•ˆ์—์„œ ์ˆœ์„œ๋ฅผ ์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. **์กฐ์น˜๊ฐ€ ๋ฐ˜๋Œ€์ธ ๋‘ ์‚ฌ์‹ค์„ ํ•œ ์ด๋ฆ„์œผ๋กœ\n * ๋ถ€๋ฅด๋ฉด ๊ทธ ์ด๋ฆ„์€ ์ •๋ณด๊ฐ€ ์•„๋‹ˆ๋ผ ์˜คํ•ด๋‹ค.**\n *\n * ๋ชจ์–‘์€ ์œ„์™€ ๊ฐ™๊ฒŒ ๋‘”๋‹ค โ€” ใ€Œ๋ช‡ ๋ฒˆใ€๊ณผ ใ€Œ์–ธ์ œ๋ถ€ํ„ฐใ€๊ฐ€ ์—ฌ๊ธฐ์„œ๋„ ๊ฐ™์€ ๊ฐ’์„ ํ•œ๋‹ค(ํ•œ ๋ฒˆ์˜ ๋ฐ€๋ฆผ๊ณผ ์‚ฌํ˜์งธ\n * ๋ชป ๋„˜์–ด๊ฐ€๋Š” ๊ฒƒ์€ ๋‹ค๋ฅด๋‹ค). ๋„˜์–ด๊ฐ€๋ฉด ์ง€์šด๋‹ค.\n */\n /**\n * **์›๋ณธ์ด ๋งํ–ˆ๋Š”๋ฐ ํŠธ์œˆ์— ์„ธ์šฐ์ง€ ์•Š์€ ๊ฒƒ** โ€” ์ด์œ ๋ณ„๋กœ ์„ผ๋‹ค.\n *\n * โ”€โ”€ ์™œ ์ด ์ž๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ๊ฐ€ (2026-08-24) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์ปค๋„ฅํ„ฐ๊ฐ€ ์›๋ณธ์˜ ์‚ฌ์‹ค ์ผ๋ถ€๋ฅผ **์ผ๋ถ€๋Ÿฌ ๋ฐ›์ง€ ์•Š๋Š” ์ผ์ด ์ •์ƒ์ด๋‹ค**: ์ž…์ž๊ฐ€ ๋งž์ง€ ์•Š๊ฑฐ๋‚˜, ๋ฐ›์œผ๋ฉด ์ƒํƒœ๊ฐ€\n * ๊ฑฐ์ง“์ด ๋˜๊ฑฐ๋‚˜, ์ปค๋„ ์–ดํœ˜๋กœ ์˜ฎ๊ธธ ์ˆ˜ ์—†๋‹ค. ๊ทธ๋•Œ ์ปค๋„ฅํ„ฐ๋Š” ๊ฒฝ๊ณ ๋ฅผ ๋‚ธ๋‹ค โ€” **๊ทธ๋Ÿฐ๋ฐ ๊ทธ ๊ฒฝ๊ณ ๊ฐ€ ํ”„๋กœ๋น„์ €๋‹\n * ํ™”๋ฉด์—์„œ ํ•œ ๋ฒˆ ์Šค์ณ ์ง€๋‚˜๊ฐˆ ๋ฟ์ด์—ˆ๋‹ค.** ๊ทธ ๋’ค๋กœ๋Š” ์–ด๋””์—์„œ๋„ ๋ณผ ์ˆ˜ ์—†์—ˆ๋‹ค.\n *\n * ๊ทธ๋ž˜์„œ ์‚ฌ์šฉ์ž๊ฐ€ ใ€Œ์›๋ณธ๋ณด๋‹ค ๊ณ„ํš์ด ์ ๋‹คใ€๋ฅผ ๋ฌผ์œผ๋ฉด **๋‹ต์ด ์–ด๋””์—๋„ ์—†๋‹ค.** ๊ทธ ์‚ฌ์‹ค์€ ํ•œ ๋ฒˆ์˜\n * ์‚ฌ๊ฑด์ด ์•„๋‹ˆ๋ผ **์ƒํƒœ์˜ ์„ฑ์งˆ**์ด๋‹ค โ€” ์„ธ์šฐ์ง€ ์•Š์€ ๊ฒƒ์€ ์ง€๊ธˆ๋„ ํŠธ์œˆ์— ์—†๋‹ค.\n *\n * โ”€โ”€ `unhandled` ์˜ ํ˜•์ œ๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * `unhandled` ๋ฆฌ๋“€์„œ๊ฐ€ **๋ฐ›์•˜๋Š”๋ฐ** ๋‹ด์„ ์ž๋ฆฌ๋ฅผ ๋ชฐ๋ž๋‹ค\n * ์—ฌ๊ธฐ ์ปค๋„ฅํ„ฐ๊ฐ€ **๋ณด์ง€๋งŒ ์ผ๋ถ€๋Ÿฌ ๋ณด๋‚ด์ง€ ์•Š์•˜๋‹ค** โ€” ์ด์œ ์™€ ํ•จ๊ป˜\n *\n * ๋‘˜ ๋‹ค ใ€Œ์‚ฌ์‹ค์ด ์˜ค๋ฅ˜ ์—†์ด ์‚ฌ๋ผ์ง€์ง€ ์•Š๊ฒŒใ€ ์„ธ๋Š” ๊ฒƒ์ด๊ณ , ์ด์œ ๊ฐ€ ๋‹ค๋ฅด๋ฏ€๋กœ ์ž๋ฆฌ๋„ ๋‹ค๋ฅด๋‹ค.\n *\n * ์ด์œ ๋ฅผ **๋ฌธ์ž์—ด๋กœ ๋ฐ›๋Š”๋‹ค**: ๋ฌด์—‡์„ ์™œ ์•ˆ ๋ฐ›๋Š”์ง€๋Š” ์›๋ณธ๋งˆ๋‹ค ๋‹ค๋ฅด๊ณ , ๊ณ„์•ฝ์ด ๊ทธ ๋ชฉ๋ก์„ ๋‹ซ์œผ๋ฉด ์ƒˆ ์›๋ณธ์„\n * ๋ถ™์ผ ๋•Œ๋งˆ๋‹ค ๊ณ„์•ฝ์„ ๊ณ ์น˜๊ฒŒ ๋œ๋‹ค(ํ๋ฆ„ ์—ด์‡ ์™€ ๊ฐ™์€ ๊ทœ์œจ).\n */\n withheld?: { reason: string; count: number; lastAtMs: number }[]\n cursorStall?: {\n /** ์—ฐ์† ์ •์ฒด ์ˆ˜ โ€” ๋„˜์–ด๊ฐ€๋ฉด 0 ์œผ๋กœ ๋Œ์•„๊ฐ„๋‹ค. */\n consecutive: number\n /** ์ด ์ •์ฒด๊ฐ€ **์ฒ˜์Œ** ์‹œ์ž‘๋œ ์‹œ๊ฐ(ms). */\n sinceMs: number\n /** ๋งˆ์ง€๋ง‰ ์ •์ฒด ์‹œ๊ฐ(ms). */\n lastAtMs: number\n /** ์‚ฌ์œ  โ€” ์‚ฌ๋žŒ์ด ์ฝ๋Š” ํ•œ ์ค„(์–ด๋А ์‹œ๊ฐ์—์„œ ๋ง‰ํ˜”๋‚˜๊นŒ์ง€ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ๋งํ•ด ์ฃผ๋ฉด ์ข‹๋‹ค). */\n reason: string\n /** ์–ด๋А ํ๋ฆ„์ธ๊ฐ€. ๋ฐ€๋„๊ฐ€ ๋†’์€ ํ‘œ๋Š” ์›๋ณธ๋งˆ๋‹ค ๋‹ค๋ฅด๋ฏ€๋กœ ์ด ์ด๋ฆ„์ด ์กฐ์น˜์˜ ์ ˆ๋ฐ˜์ด๋‹ค. */\n stream?: string\n }\n}\n\n/**\n * ์ฝ๊ธฐ ์‹คํŒจ๋ฅผ ์žฅ๋ถ€์— ์ ๋Š”๋‹ค โ€” **๋ฐ›์€ ๊ฒƒ์ด ์—†๋Š” ๊ฒƒ๊ณผ ๊ฐ€๋ฅธ๋‹ค.**\n *\n * ์—ฐ์† ์‹คํŒจ์˜ ์‹œ์ž‘ ์‹œ๊ฐ์„ ์ง€ํ‚ค๋Š” ๊ฒƒ์ด ์ด ํ•จ์ˆ˜์˜ ์š”์ ์ด๋‹ค: ๋งค๋ฒˆ ์ƒˆ๋กœ ์ ์œผ๋ฉด ใ€Œ20๋ถ„์งธ ๋ชป ์ฝ๋Š”๋‹คใ€๋ฅผ\n * ๋งํ•  ์ˆ˜ ์—†๊ณ , ํ™”๋ฉด์€ ๋Š˜ ใ€Œ๋ฐฉ๊ธˆ ์‹คํŒจํ–ˆ๋‹คใ€๋งŒ ๋ณด๊ฒŒ ๋œ๋‹ค.\n */\nexport function recordReadFailure(\n ledger: IngestLedger,\n reason: string,\n nowMs: number,\n stream?: string,\n /**\n * ๋‹ค์Œ์— ๋‹ค์‹œ ๋ฌผ์„ ๋•Œ๊นŒ์ง€ ๋‚จ์€ ์‹œ๊ฐ„(ms) โ€” ์ปค๋„ฅํ„ฐ๊ฐ€ ๋ฌผ๋Ÿฌ์„ฐ์œผ๋ฉด ๊ทธ ๊ฐ’์„ ์ค€๋‹ค (2026-08-27).\n *\n * ์ด๊ฒƒ์ด ์—†์œผ๋ฉด ํ™”๋ฉด์ด ใ€Œ๋‹ฟ์ง€ ๋ชปํ•˜๊ณ  ์žˆ๋‹คใ€๊นŒ์ง€๋งŒ ๋งํ•˜๊ณ , ์‚ฌ๋žŒ์€ **์–ธ์ œ ๋‹ค์‹œ ์‹œ๋„ํ•˜๋Š”์ง€** ์•Œ ์ˆ˜\n * ์—†๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๊ธฐ๋‹ค๋ ค์•ผ ํ•˜๋Š”์ง€ ์†์„ ์จ์•ผ ํ•˜๋Š”์ง€ ํŒ๋‹จํ•˜์ง€ ๋ชปํ•œ๋‹ค.\n *\n * **์—ฐ๋‹ฌ์•„ ํšŸ์ˆ˜์™€ ์‹œ์ž‘ ์‹œ๊ฐ์€ ์ปค๋„ฅํ„ฐ๊ฐ€ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค** โ€” ์ด ํ•จ์ˆ˜๊ฐ€ ์ด๋ฏธ ์„ผ๋‹ค. ๋‘ ๊ณณ์—์„œ ์„ธ๋ฉด\n * ๊ฐˆ๋ผ์ง€๊ณ , ๊ฐˆ๋ผ์ง€๋ฉด ์–ด๋А ์ชฝ์ด ์‚ฌ์‹ค์ธ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค.\n */\n nextRetryMs?: number\n): void {\n const prev = ledger.readFailure\n ledger.readFailure = {\n consecutive: (prev?.consecutive ?? 0) + 1,\n sinceMs: prev?.sinceMs ?? nowMs,\n lastAtMs: nowMs,\n reason,\n ...(stream ? { stream } : {}),\n ...(Number.isFinite(nextRetryMs) && (nextRetryMs as number) > 0 ? { nextRetryMs: nextRetryMs as number } : {})\n }\n}\n\n/**\n * **๋Š๊ฒผ๋‹ค๊ฐ€ ๋Œ์•„์™”๋‹ค** โ€” ๊ทธ ์‚ฌ์‹ค์„ ๋‚จ๊ธด๋‹ค (2026-08-27).\n *\n * ์‹คํŒจ ๊ธฐ๋ก๋งŒ ์ง€์šฐ๋ฉด ใ€Œ๋Š๊ธด ์ ์ด ์žˆ์—ˆ๋‹คใ€๊ฐ€ ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์ง„๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ฐค์ƒˆ ๋‘ ์‹œ๊ฐ„ ๋Š๊ฒผ๋˜ ์—ฐ๊ฒฐ๊ณผ ํ•œ\n * ๋ฒˆ๋„ ๋Š๊ธฐ์ง€ ์•Š์€ ์—ฐ๊ฒฐ์ด ์•„์นจ์— ๋˜‘๊ฐ™์ด ๋ณด์ธ๋‹ค. ๋ช‡ ๋ฒˆ ์‹คํŒจํ•œ ๋’ค์— ๋Œ์•„์™”๋Š”์ง€, ์–ผ๋งˆ๋‚˜ ๋Š๊ฒผ๋Š”์ง€๋ฅผ ํ•จ๊ป˜\n * ๋‚จ๊ธด๋‹ค.\n */\nexport function recordRecovered(ledger: IngestLedger, afterFailures: number, downMs: number, nowMs: number): void {\n if (ledger.readFailure) delete ledger.readFailure\n ledger.lastRecovery = { atMs: nowMs, afterFailures, downMs }\n}\n\n/**\n * ์ฝ๊ธฐ๊ฐ€ ์„ฑ๊ณตํ–ˆ๋‹ค โ€” ๋‹จ์ ˆ ๊ธฐ๋ก์„ ์ง€์šด๋‹ค.\n *\n * **๋นˆ ์ฝ๊ธฐ๋„ ์„ฑ๊ณต์ด๋‹ค**: ์›๋ณธ์ด ใ€Œ์ƒˆ ๊ฒƒ์ด ์—†๋‹คใ€๊ณ  ๋‹ตํ•œ ๊ฒƒ์€ ๋‹ฟ์•˜๋‹ค๋Š” ๋œป์ด๋‹ค. ๊ทธ ๋‘˜์„ ๊ฐ™๊ฒŒ ๋‘๋ฉด\n * ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†๋Š” ์—ฐ๊ฒฐ์ด ๋Š๊ธด ์›๋ณธ์œผ๋กœ ๋ณด์ธ๋‹ค(๊ณ ์น˜๋ ค๋˜ ๊ฒƒ์˜ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ ํ‹€๋ฆฐ๋‹ค).\n */\nexport function clearReadFailure(ledger: IngestLedger): void {\n if (ledger.readFailure) delete ledger.readFailure\n}\n\n/**\n * ์ปค์„œ ์ •์ฒด๋ฅผ ์žฅ๋ถ€์— ์ ๋Š”๋‹ค โ€” **๋‹ฟ์ง€ ๋ชปํ•œ ๊ฒƒ๊ณผ ๊ฐ€๋ฅธ๋‹ค.**\n *\n * `recordReadFailure` ์™€ ๊ฐ™์€ ๋ชจ์–‘์ด์ง€๋งŒ **๋‹ค๋ฅธ ์นธ**์ด๋‹ค. ํ•œ ์ฃผ๊ธฐ๊ฐ€ ๋‘ ์ด์œ ๋กœ ์‹คํŒจํ•  ์ˆ˜๋Š” ์—†์œผ๋ฏ€๋กœ\n * (๋จผ์ € ๋‹ฟ์•„์•ผ ์ฝ๋Š”๋‹ค) ๋‘˜์ด ๋™์‹œ์— ์„œ์ง€ ์•Š๋Š”๋‹ค โ€” ๊ทธ๋ž˜์„œ ํ™”๋ฉด์€ ๋‘˜ ์ค‘ ํ•˜๋‚˜๋งŒ ๋ณด๊ฒŒ ๋˜๊ณ , ๊ทธ ํ•˜๋‚˜๊ฐ€\n * ์กฐ์น˜๋ฅผ ์ •ํ•œ๋‹ค.\n */\nexport function recordCursorStall(ledger: IngestLedger, reason: string, nowMs: number, stream?: string): void {\n const prev = ledger.cursorStall\n ledger.cursorStall = {\n consecutive: (prev?.consecutive ?? 0) + 1,\n sinceMs: prev?.sinceMs ?? nowMs,\n lastAtMs: nowMs,\n reason,\n ...(stream ? { stream } : {})\n }\n}\n\n/**\n * **์„ธ์šฐ์ง€ ์•Š์€ ๊ฒƒ์„ ์ ๋Š”๋‹ค** โ€” ์ด์œ ๋ณ„ ๋ˆ„์ .\n *\n * ์ฃผ๊ธฐ๋งˆ๋‹ค ๋‹ค์‹œ ์•Œ๋ ค๋„ ์‹œ๋„๋Ÿฌ์›Œ์ง€์ง€ ์•Š๊ฒŒ **์ด์œ ๋กœ ๋ฌถ์–ด ์„ผ๋‹ค**(์ค„์ด ๋Š˜์ง€ ์•Š๋Š”๋‹ค). ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰ ์‹œ๊ฐ์„\n * ํ•จ๊ป˜ ๋“ ๋‹ค โ€” ใ€Œ์ง€๊ธˆ๋„ ๊ทธ๋Ÿฐ๊ฐ€ใ€์™€ ใ€Œํ•œ๋•Œ ๊ทธ๋žฌ๋‚˜ใ€๋Š” ๋‹ค๋ฅธ ์‚ฌ์‹ค์ด๊ณ , ์‹œ๊ฐ ์—†์ด๋Š” ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์—†๋‹ค.\n *\n * ์ด์œ ๊ฐ€ ๊ฐ™๊ณ  ์ˆ˜๊ฐ€ ๋‹ฌ๋ผ์ง€๋ฉด **๋งˆ์ง€๋ง‰ ์ˆ˜๋กœ ๋ฎ๋Š”๋‹ค**: ์ด๊ฒƒ์€ ๋ˆ„์  ์‚ฌ๊ฑด์ด ์•„๋‹ˆ๋ผ **์ง€๊ธˆ ์„ธ์šฐ์ง€ ์•Š์€ ๊ฒƒ์˜\n * ์ˆ˜**๋‹ค. ์ฃผ๊ธฐ๋งˆ๋‹ค 8๊ฑด์ด๋ฉด 8์ด๊ณ , ๋”ํ•ด์„œ 800์ด ๋˜๋ฉด ๊ฑฐ์ง“์ด๋‹ค.\n */\n/**\n * ์–ด๋–ค ๊ธธ๋กœ ๋ช‡ ๊ฑด์ด ๋“ค์–ด์™”๋‚˜ โ€” ๊ฒฝ๋กœ ์ด๋ฆ„์€ ๋ถ€๋ฅด๋Š” ์ชฝ์ด ์ •ํ•œ๋‹ค.\n *\n * ์ด๋ฆ„์„ ์ž์œ  ๋ฌธ์ž์—ด๋กœ ๋‘๋Š” ์ด์œ : ๊ธธ์ด ๋Š˜์–ด๋‚œ๋‹ค(์ง€๊ธˆ์€ ๋ฌผ์–ด๋ณด๊ธฐยท์‹œ๋‚˜๋ฆฌ์˜คยท๋ถ€ํ•˜ ๋„๊ตฌ, ๋‹ค์Œ์€ ์›นํ›…).\n * ์—ด๊ฑฐ๋กœ ๋ชป ๋ฐ•์œผ๋ฉด ์ƒˆ ๊ธธ์ด ์ƒ๊ธธ ๋•Œ ์ด ํŒŒ์ผ์„ ํ•จ๊ป˜ ๊ณ ์ณ์•ผ ํ•˜๊ณ , ์žŠ์œผ๋ฉด ๊ทธ ๊ธธ์ด ํ†ต๊ณ„์—์„œ ์‚ฌ๋ผ์ง„๋‹ค.\n */\nexport function recordIngestSource(ledger: IngestLedger, source: string, count: number, nowMs: number): void {\n const key = String(source ?? '').trim() || 'unknown'\n if (count <= 0) return\n const cur = (ledger.bySource[key] ??= { count: 0, lastAtMs: -1 })\n cur.count += count\n cur.lastAtMs = nowMs\n}\n\n/** ๊ฐ™์€ ์‚ฌ์‹ค์ด ๋‘ ๋ฒˆ ์™€์„œ ๋ฒ„๋ฆฐ ์ˆ˜ โ€” ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. */\nexport function recordDuplicates(ledger: IngestLedger, count: number, nowMs: number): void {\n if (count <= 0) return\n ledger.duplicates += count\n ledger.lastDuplicateAtMs = nowMs\n}\n\nexport function recordWithheld(ledger: IngestLedger, reason: string, count: number, nowMs: number): void {\n if (!reason || !(count > 0)) return\n const list = (ledger.withheld ??= [])\n const at = list.find(w => w.reason === reason)\n if (at) {\n at.count = count\n at.lastAtMs = nowMs\n return\n }\n list.push({ reason, count, lastAtMs: nowMs })\n}\n\n/** ์ฐฝ์„ ๋„˜๊ฒผ์œผ๋ฉด ์ง€์šด๋‹ค โ€” ํ’€๋ฆฐ ์ •์ฒด๊ฐ€ ํ™”๋ฉด์— ๋‚จ์•„ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ๋„ ๊ฑฐ์ง“์ด๋‹ค. */\nexport function clearCursorStall(ledger: IngestLedger): void {\n if (ledger.cursorStall) delete ledger.cursorStall\n}\n\n/** ์ฐฝ ๊ธธ์ด โ€” 10๋ถ„. ๋ถ€ํ•˜ ๊ณ„๊ธฐํŒ(10์ดˆ)๊ณผ ๋‹ค๋ฅธ ๊ฐ’์ด๋ผ ์ด๋ฆ„์„ ๋”ฐ๋กœ ๋‘”๋‹ค(๊ฐ™์€ ์ด๋ฆ„์ด๋ฉด ์„ž์ธ๋‹ค). */\nexport const INGEST_WINDOW_MS = 600_000\n\n/**\n * ์ด๋งŒํผ ์•„๋ฌด๊ฒƒ๋„ ์˜ค์ง€ ์•Š์œผ๋ฉด ใ€Œ์œ ์ž… ์—†์Œใ€์ด๋ผ ๋งํ•œ๋‹ค.\n *\n * ์ฐฝ ํ•˜๋‚˜์™€ ๊ฐ™๊ฒŒ ๋‘”๋‹ค โ€” ํ•œ ์ฐฝ์„ ํ†ต์งธ๋กœ ๋น„์šด ๊ฒƒ์€ ์‹ค์ œ๋กœ ใ€Œ๊ทธ ๊ตฌ๊ฐ„์— ์•„๋ฌด๊ฒƒ๋„ ์˜ค์ง€ ์•Š์•˜๋‹คใ€์ด๊ณ , ์ถ”์ด์˜\n * ๊ทธ ์ž๋ฆฌ๋„ ๋น„๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ฆผ๊ณผ ์•Œ์•ฝ์ด ๊ฐ™์€ ๋ง์„ ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด ๊ฐ™์€ ๊ธธ์ด์—ฌ์•ผ ํ•œ๋‹ค.\n */\nexport const STALE_AFTER_MS = INGEST_WINDOW_MS\n\n/** ์ถ”์ด ๋ณด๊ด€ ์ฐฝ ์ˆ˜ โ€” 36์ฐฝ ร— 10๋ถ„ = 6์‹œ๊ฐ„. ใ€Œ๊ณ ์นœ ๊ฒƒ์ด ๋จนํ˜”๋‚˜ใ€๋ฅผ ๋ณด๊ธฐ์— ์ถฉ๋ถ„ํ•˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ๋Š” ์ˆซ์ž ๋„ทร—36 ์ด๋‹ค. */\nexport const TREND_LIMIT = 36\n\n/**\n * ์‚ฌ์œ  ์ข…๋ฅ˜ ์ƒํ•œ.\n *\n * **์—†์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋์—†์ด ์ž๋ž€๋‹ค.** ์‚ฌ์œ  ๋ฌธ๊ตฌ์— ๋ ˆ์ฝ”๋“œ๋งˆ๋‹ค ๋‹ค๋ฅธ ๊ฐ’์ด ์„ž์ด๋Š” ์ปค๋„ฅํ„ฐ๊ฐ€ ํ•˜๋‚˜๋งŒ ์žˆ์–ด๋„\n * (`unknown bizStep \"PUTAWAY_X17\"`, `โ€ฆX18`, โ€ฆ) ํ‚ค๊ฐ€ ๋ฌดํ•œํžˆ ๋Š˜์–ด๋‚œ๋‹ค. ์ดˆ๋‹น 1๋งŒ ๊ฑด ๊ทœ๋ชจ์—์„œ ์ด๊ฒƒ์€\n * ๋ฏธ๋ฏธํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ˆ„์ˆ˜๋‹ค.\n *\n * ๋„˜์น˜๋ฉด `OTHER_REASON` ํ•œ ์นธ์— ๋ชจ์œผ๊ณ  **๋ช‡ ์ข…์„ ๋ชจ์•˜๋Š”์ง€ ํ•จ๊ป˜ ๋‚ธ๋‹ค**(์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ž๋ฅด์ง€ ์•Š๋Š”๋‹ค).\n * 30์ข…์ด๋ฉด ๊ณ ์น  ๊ณณ์„ ์ฐพ๊ธฐ์— ์ถฉ๋ถ„ํ•˜๋‹ค โ€” ๋ชฉ๋ก์„ ํ›‘๋Š” ์ผ์ด ์•„๋‹ˆ๋ผ ๋งคํ•‘ ๋ช‡ ์ค„์„ ๊ณ ์น˜๋Š” ์ผ์ด๋‹ค.\n */\nexport const REASON_LIMIT = 30\n\n/** ์ƒํ•œ์„ ๋„˜๊ธด ์‚ฌ์œ ๋“ค์ด ๋ชจ์ด๋Š” ์นธ. */\nexport const OTHER_REASON = '(other reasons)'\n\n/**\n * ํ‘œ๋ณธ ์ƒํ•œ โ€” **์‚ฌ์œ ๋งˆ๋‹ค ํ•œ ๊ฑด**๋งŒ ๋“ ๋‹ค.\n *\n * ๊ฐ™์€ ์‚ฌ์œ  20๊ฑด๋ณด๋‹ค **์„œ๋กœ ๋‹ค๋ฅธ ์‚ฌ์œ  1๊ฑด์”ฉ**์ด ๋งคํ•‘์„ ๊ณ ์น˜๋Š” ๋ฐ ์œ ์šฉํ•˜๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ๊ทœ์น™์ด ์„ฑ๋Šฅ์„\n * ์ง€ํ‚จ๋‹ค: ๊ทธ ์‚ฌ์œ ์˜ ํ‘œ๋ณธ์ด ์ด๋ฏธ ์žˆ์œผ๋ฉด `JSON.stringify` ๋ฅผ **์•„์˜ˆ ํ•˜์ง€ ์•Š๋Š”๋‹ค.** ์‹ค์ธก(๋ ˆ์ฝ”๋“œ 241๋ฐ”์ดํŠธ)\n * ์œผ๋กœ ๊ฑด๋‹น 0.24ยตs ์ธ๋ฐ, ์ดˆ๋‹น 1๋งŒ ๊ฑด์ด ์ „๋ถ€ ๊ฑฐ๋ถ€๋˜๋ฉด 9,980๊ฑด์€ ๋งŒ๋“ค์ž๋งˆ์ž ๋ฒ„๋ ค์ง€๋Š” ๋ฌธ์ž์—ด์ด์—ˆ๋‹ค โ€”\n * CPU ๋Š” ๊ฐ๋‹น๋˜์ง€๋งŒ GC ์••๋ฐ•์ด ์ด๋ฒคํŠธ ๋ฃจํ”„๋กœ ๋Œ์•„์˜จ๋‹ค.\n */\nexport const SAMPLE_LIMIT = REASON_LIMIT\n\n/** ํ‘œ๋ณธ ํ•˜๋‚˜์˜ ์ง๋ ฌํ™” ์ƒํ•œ(๋ฌธ์ž). */\nexport const RECORD_MAX = 2000\n\n/**\n * ๋งคํ•‘์ด ๋ง‰๊ณ  ์žˆ๋‹ค๊ณ  ๋ณผ ํ†ต๊ณผ์œจ ๋ฌธํ„ฑ.\n *\n * ๊ฐ’๊ณผ ํ•จ๊ป˜ ๋‚ด๋ณด๋‚ธ๋‹ค โ€” ํ™”๋ฉด์ด ใ€Œ95% ๋ฏธ๋งŒ์ด๋ผ ๋งคํ•‘ ๋ฌธ์ œ๋กœ ๋ดค๋‹คใ€๋ฅผ ๋งํ•  ์ˆ˜ ์žˆ๊ฒŒ. ์ˆจ๊ธด ๋ฌธํ„ฑ์€ ์‚ฌ๋žŒ์ด\n * ํŒ์ •์„ ๋ฏฟ์ง€ ๋ชปํ•œ๋‹ค.\n */\nexport const HEALTHY_ACCEPTED_RATIO = 0.95\n\nexport function newIngestLedger(): IngestLedger {\n return {\n window: { startMs: -1, endMs: -1, offered: 0, rejected: 0, undelivered: 0, rows: 0, flushes: 0 },\n trend: [],\n trendDropped: 0,\n offered: 0,\n rejected: 0,\n undelivered: 0,\n rows: 0,\n flushes: 0,\n lastWriteAtMs: -1,\n lastUndeliveredAtMs: -1,\n bySource: {},\n duplicates: 0,\n lastDuplicateAtMs: -1,\n byReason: {},\n reasonsCapped: false,\n sampledReasons: new Set<string>(),\n samples: [],\n sampleDropped: 0,\n lastAtMs: -1\n }\n}\n\n/**\n * ์ฐฝ์ด ๋‹ค ์ฐผ์œผ๋ฉด ๋‹ซ์•„ ์ถ”์ด์— ๋„ฃ๊ณ  ์ƒˆ๋กœ ์—ฐ๋‹ค. **๋‹ซํžŒ ์ฐฝ๋งŒ ใ€Œ์ตœ๊ทผใ€์œผ๋กœ ์ฝ๋Š”๋‹ค.**\n *\n * โ”€โ”€ ์˜์†์ด ๋ถ™๋Š” ์œ ์ผํ•œ ์ž๋ฆฌ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ๋‹ซํžŒ ์ฐฝ ํ•˜๋‚˜๊ฐ€ ์˜์†์˜ ๋‹จ์œ„๋‹ค(์ˆซ์ž ๋„ท + ๊ตฌ๊ฐ„). `onClosed` ๋ฅผ ๋ฐ›๋Š” ์ด์œ ๋Š” **์ด ํ•จ์ˆ˜๊ฐ€ ์ฐฝ์ด ๋‹ซํžˆ๋Š”\n * ์œ ์ผํ•œ ์ง€์ **์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค โ€” ์—ฌ๊ธฐ ํ•œ ๊ณณ์— ๋ถ™์ด๋ฉด ๊ธฐ๋ก ๊ฒฝ๋กœ๊ฐ€ ๊ฐˆ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n *\n * ์™œ ๊ฑด๋งˆ๋‹ค๊ฐ€ ์•„๋‹ˆ๋ผ ์ฐฝ๋งˆ๋‹ค์ธ๊ฐ€: ๊ฑฐ๋ถ€๋งˆ๋‹ค ํ–‰์„ ์“ฐ๋ฉด ์ดˆ๋‹น 1๋งŒ ํ–‰์ด๊ณ  ๊ทธ ๋ถ€ํ•˜๊ฐ€ ์ด๋ฒคํŠธ ๋ฃจํ”„๋กœ ๋Œ์•„์˜จ๋‹ค.\n * ์ฐฝ๋งˆ๋‹ค๋ฉด ํŠธ์œˆ 1,000๊ฐœ์— ํ•˜๋ฃจ 14.4๋งŒ ํ–‰(์•ฝ 14MB)์ด๋‹ค.\n *\n * `onClosed` ๋Š” **์˜ค๋ฅ˜๋ฅผ ๋‚ด์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค**(ํ˜ธ์ถœ๋ถ€๊ฐ€ ๊ฐ์‹ผ๋‹ค) โ€” ์˜์†์ด ์‹คํŒจํ•ด๋„ ์žฅ๋ถ€๋Š” ๊ณ„์† ๊ตด๋Ÿฌ์•ผ ํ•œ๋‹ค.\n * ์˜์†์„ ์ง€ํ‚ค๋Š” ๊ฒƒ๊ณผ ๊ด€์ธก์„ ์ง€ํ‚ค๋Š” ๊ฒƒ์€ ๋‹ค๋ฅธ ์ผ์ด๊ณ , ๋’ค์—ฃ๊ฒƒ์ด ์•ž์—ฃ๊ฒƒ ๋•Œ๋ฌธ์— ๋ฉˆ์ถ”๋ฉด ์•ˆ ๋œ๋‹ค.\n */\nexport function rollIngestWindow(\n ledger: IngestLedger,\n nowMs: number,\n windowMs = INGEST_WINDOW_MS,\n onClosed?: (closed: IngestWindow) => void\n): void {\n if (ledger.window.startMs < 0) {\n ledger.window.startMs = nowMs\n return\n }\n if (nowMs - ledger.window.startMs < windowMs) return\n\n const closed: IngestWindow = { ...ledger.window, endMs: nowMs }\n ledger.trend.push(closed)\n ledger.window = { startMs: nowMs, endMs: -1, offered: 0, rejected: 0, undelivered: 0, rows: 0, flushes: 0 }\n\n if (ledger.trend.length > TREND_LIMIT) {\n const drop = ledger.trend.length - TREND_LIMIT\n ledger.trend.splice(0, drop)\n ledger.trendDropped += drop\n }\n\n /* ์˜์†์€ ๊ด€์ธก์„ ๋ง‰์ง€ ์•Š๋Š”๋‹ค โ€” ํ„ฐ์ ธ๋„ ์žฅ๋ถ€๋Š” ์ด๋ฏธ ๊ฐฑ์‹ ๋๋‹ค. */\n if (onClosed) {\n try {\n onClosed(closed)\n } catch {\n /* ํ˜ธ์ถœ๋ถ€๊ฐ€ ์•Œ๋ฆฐ๋‹ค. ์—ฌ๊ธฐ์„œ ์‚ผํ‚ค๋Š” ๊ฒƒ์€ **์žฅ๋ถ€์˜ ์ง„ํ–‰**์„ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋‹ค. */\n }\n }\n}\n\n/** ์‚ฌ์œ  ๋ฌถ์Œ์˜ ํ‚ค. ์‚ฌ์œ ๊ฐ€ ์—ฌ๋Ÿฟ์ด๋ฉด ํ•œ ์ค„๋กœ ์ž‡๊ณ , ์—†์œผ๋ฉด ์ง€์–ด๋‚ด์ง€ ์•Š๊ณ  `unknown`. */\nexport function reasonKey(errors: string[] | undefined): string {\n const list = (errors ?? []).map(e => String(e).trim()).filter(Boolean)\n return list.length ? list.join(' ยท ') : 'unknown'\n}\n\nfunction safeJson(v: unknown): string {\n try {\n const s = JSON.stringify(v)\n return s === undefined ? String(v) : s\n } catch {\n return '(serialization failed)' /* ์ˆœํ™˜ ์ฐธ์กฐ ๋“ฑ โ€” ๋ชจ์–‘์„ ๋ชป ๋ณด์—ฌ ์ฃผ๋Š” ๊ฒƒ๋„ ์‚ฌ์‹ค์ด๋‹ค */\n }\n}\n\n/**\n * ํ•œ ๋ฒˆ์˜ ์ธ์ œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ์ ๋Š”๋‹ค.\n *\n * `offered` ๋ฅผ ํ•จ๊ป˜ ๋ฐ›๋Š” ์ด์œ : ํ†ต๊ณผ์œจ์„ **์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ๊ณ„์ˆ˜๊ธฐ**์—์„œ ๋‚˜๋ˆ  ๊ณ„์‚ฐํ•˜๋ฉด ๋ถ„๋ชจ์™€ ๋ถ„์ž๊ฐ€ ๋‹ค๋ฅธ\n * ๊ฒƒ์„ ์„ธ๊ฒŒ ๋œ๋‹ค. ๋‘˜์„ ํ•œ์ž๋ฆฌ์—์„œ ๋ณธ ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ ์ ๋Š”๋‹ค.\n */\nexport function recordIngest(\n ledger: IngestLedger,\n offered: number,\n rejected: { record?: unknown; errors?: string[] }[] | undefined,\n nowMs: number,\n onClosed?: (closed: IngestWindow) => void,\n /** ๋งคํ•‘์€ ํ†ต๊ณผํ–ˆ์ง€๋งŒ ํŠธ์œˆ์— ๋‹ฟ์ง€ ๋ชปํ•œ ์ˆ˜ โ€” ใ€Œํ†ต๊ณผใ€๋กœ ์„ธ๋ฉด ํ™”๋ฉด์ด ๊ฑฐ์ง“์„ ๋งํ•œ๋‹ค. */\n undelivered = 0\n): void {\n if (!Number.isFinite(offered) || offered < 0) return\n\n rollIngestWindow(ledger, nowMs, INGEST_WINDOW_MS, onClosed)\n\n ledger.offered += offered\n ledger.window.offered += offered\n ledger.lastAtMs = nowMs\n\n if (Number.isFinite(undelivered) && undelivered > 0) {\n ledger.undelivered += undelivered\n ledger.window.undelivered += undelivered\n ledger.lastUndeliveredAtMs = nowMs\n }\n\n const items = rejected ?? []\n if (!items.length) return\n\n ledger.rejected += items.length\n ledger.window.rejected += items.length\n\n for (const it of items) {\n const raw = reasonKey(it?.errors)\n const known = raw in ledger.byReason\n\n /* ์‚ฌ์œ  ์ข…๋ฅ˜ ์ƒํ•œ โ€” ๋„˜์น˜๋ฉด ํ•œ ์นธ์œผ๋กœ ๋ชจ์œผ๊ณ , **๋‹ฟ์•˜๋‹ค๋Š” ์‚ฌ์‹ค**์„ ํ‘œ์‹œํ•œ๋‹ค. */\n let key = raw\n if (!known && Object.keys(ledger.byReason).length >= REASON_LIMIT) {\n key = OTHER_REASON\n ledger.reasonsCapped = true\n }\n\n const seen = ledger.byReason[key]\n ledger.byReason[key] = { count: (seen?.count ?? 0) + 1, lastAtMs: nowMs }\n\n /*\n * **ํ‘œ๋ณธ์€ ์‚ฌ์œ ๋งˆ๋‹ค ํ•˜๋‚˜.** ์ด๋ฏธ ๊ทธ ์‚ฌ์œ ์˜ ํ‘œ๋ณธ์ด ์žˆ์œผ๋ฉด ์ง๋ ฌํ™”๋ฅผ ์•„์˜ˆ ํ•˜์ง€ ์•Š๋Š”๋‹ค โ€” ์ดˆ๋‹น 1๋งŒ ๊ฑด์ด\n * ์ „๋ถ€ ๊ฑฐ๋ถ€๋  ๋•Œ ๋งŒ๋“ค์ž๋งˆ์ž ๋ฒ„๋ ค์ง€๋Š” ๋ฌธ์ž์—ด์„ ๋งŒ๋“ค์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ๋‹ค(์‹ค์ธก: ๊ฑด๋‹น 0.24ยตs, GC ์••๋ฐ•).\n */\n if (ledger.sampledReasons.has(key)) {\n ledger.sampleDropped += 1\n continue\n }\n\n const text = safeJson(it?.record)\n const truncated = text.length > RECORD_MAX\n ledger.samples.push({\n atMs: nowMs,\n errors: key === OTHER_REASON ? [OTHER_REASON] : it?.errors?.length ? [...it.errors] : ['unknown'],\n record: truncated ? text.slice(0, RECORD_MAX) : text,\n ...(truncated ? { recordTruncated: true } : {})\n })\n\n ledger.sampledReasons.add(key)\n\n if (ledger.samples.length > SAMPLE_LIMIT) {\n const gone = ledger.samples.splice(0, ledger.samples.length - SAMPLE_LIMIT)\n /* ๋ฐ€๋ ค๋‚œ ํ‘œ๋ณธ์˜ ์‚ฌ์œ ๋Š” ์ƒ‰์ธ์—์„œ๋„ ๋นผ์•ผ ๋‹ค์Œ์— ๋‹ค์‹œ ๋œฐ ์ˆ˜ ์žˆ๋‹ค(์•ˆ ๋นผ๋ฉด ๊ทธ ์‚ฌ์œ ๋Š” ์˜์›ํžˆ ํ‘œ๋ณธ ์—†์Œ). */\n for (const g of gone) ledger.sampledReasons.delete(reasonKey(g.errors))\n ledger.sampleDropped += gone.length\n }\n }\n}\n\n/**\n * ์ €๋„์— **์ ์€ ๊ฒƒ**์„ ๊ธฐ๋กํ•œ๋‹ค โ€” ์ฐฝ์„ ๋„˜๊ธธ ๋•Œ๊ฐ€ ๋˜์—ˆ์œผ๋ฉด ๋„˜๊ธฐ๊ณ , ํ–‰ยท์™•๋ณต์„ ๋”ํ•œ๋‹ค.\n *\n * โ”€โ”€ ์™œ ์œ ์ž…๊ณผ ๊ฐ™์€ ์ฐฝ์ธ๊ฐ€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์ƒˆ ์žฅ๋ถ€๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค. ์ด ์ฐฝ์€ ใ€Œ๋‹ซํžŒ ์ฐฝ๋งŒ ์ตœ๊ทผใ€ยทใ€Œ0 ๊ณผ ์—†์Œ์„ ๊ฐ€๋ฅธ๋‹คใ€ยทใ€Œ์ž˜๋ฆฐ ๊ฒƒ์„ ์„ธ์–ด ๋“œ๋Ÿฌ๋‚ธ๋‹คใ€๋ฅผ\n * ์ด๋ฏธ ์ง€ํ‚ค๊ณ , ๋‹ซํž ๋•Œ ์˜์†๊นŒ์ง€ ๋œ๋‹ค. ๊ฐ™์€ ๊ทœ์œจ์„ ๋‘ ๋ฒŒ ๋งŒ๋“ค๋ฉด ํ•œ์ชฝ๋งŒ ๊ณ ์ณ์ง„๋‹ค.\n *\n * โ”€โ”€ ์œ ์ž…์ด ์—†์–ด๋„ ์ฐฝ์€ ๋„˜์–ด๊ฐ„๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์‹œ๋ฎฌ ํŠธ์œˆ์€ ๋ฐ›๋Š” ๊ฒƒ ์—†์ด ์ž๊ธฐ๊ฐ€ ๋‚ธ ์‚ฌ์‹ค์„ ์ ๋Š”๋‹ค. ์ฐฝ์ด **์œ ์ž…์—์„œ๋งŒ** ๋„˜์–ด๊ฐ„๋‹ค๋ฉด ๊ทธ ํŠธ์œˆ์€ ์ถ”์ด์—\n * ์˜์›ํžˆ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š๋Š”๋‹ค โ€” ๊ทธ๋ž˜์„œ ์“ฐ๊ธฐ๋„ ์ฐฝ์„ ๋„˜๊ธด๋‹ค.\n */\nexport function recordJournalWrite(\n ledger: IngestLedger,\n rows: number,\n nowMs: number,\n onClosed?: (closed: IngestWindow) => void\n): void {\n if (!Number.isFinite(rows) || rows <= 0) return\n rollIngestWindow(ledger, nowMs, INGEST_WINDOW_MS, onClosed)\n ledger.rows += rows\n ledger.flushes += 1\n ledger.window.rows += rows\n ledger.window.flushes += 1\n /*\n * **`lastAtMs` ๋Š” ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.** ๊ทธ ๊ฐ’์€ ใ€Œ๋งˆ์ง€๋ง‰ ์œ ์ž…ใ€์ด๊ณ , ๋Š๊ธด ํ”ผ๋“œ๋ฅผ ๊ฐ€๋ ค๋‚ด๋Š” ๊ทผ๊ฑฐ๋‹ค. ์“ฐ๊ธฐ๋กœ\n * ๊ฐฑ์‹ ํ•˜๋ฉด ๋ผ์ด๋ธŒ ํŠธ์œˆ์€ ์œ ์ž…์ด ๋Š๊ฒจ๋„ ํ‹ฑ์ด ๋งŒ๋“  ๋ธํƒ€๋ฅผ ๊ณ„์† ์ ์œผ๋ฏ€๋กœ **์˜์›ํžˆ ใ€Œ๋ฐฉ๊ธˆ ๋ฐ›์•˜๋‹คใ€**๊ฐ€ ๋˜๊ณ ,\n * ใ€Œ์˜ค๋ž˜ ๋Š๊ธด ํ”ผ๋“œ์— ์˜› ์ฐฝ์œผ๋กœ ์ดˆ๋ก์„ ๋‚ด์ง€ ์•Š๋Š”๋‹คใ€๊ฐ€ ์˜ค๋ฅ˜ ํ•˜๋‚˜ ์—†์ด ๋ฌด๋ ฅํ•ด์ง„๋‹ค. ์ถ•์„ ๋”ฐ๋กœ ๋“ ๋‹ค.\n */\n ledger.lastWriteAtMs = nowMs\n}\n\n/**\n * ํ•œ๋ˆˆ ํŒ์ •.\n *\n * `not-applicable` ๊ณผ `not-connected` ๋ฅผ ์„ž์ง€ ์•Š๊ณ , ์žฌ์ง€ ์•Š์€ ๊ฒƒ์„ ์ •์ƒยท๋น„์ •์ƒ์œผ๋กœ ์ฑ„์šฐ์ง€ ์•Š๋Š”๋‹ค\n * (`unknown`). ์‹œ๋ฎฌ ํŠธ์œˆ์— ใ€Œ๋™๊ธฐํ™” ์•ˆ ๋จใ€์ด๋ผ ๋งํ•˜๋ฉด ๊ฑฐ์ง“ ๊ฒฝ๋ณด์ด๊ณ , **๊ฑฐ์ง“ ๊ฒฝ๋ณด๋Š” ์ฐธ ๊ฒฝ๋ณด๋ฅผ ์ฃฝ์ธ๋‹ค.**\n */\nexport type SyncVerdict =\n | 'syncing' /** ์œ ์ž…์ด ์žˆ๊ณ  ํ†ต๊ณผ์œจ์ด ๋ฌธํ„ฑ ์ด์ƒ */\n | 'mapping-blocked' /** ์œ ์ž…์€ ์žˆ๋Š”๋ฐ ๊ฑฐ๋ถ€๊ฐ€ ๋ฌธํ„ฑ์„ ๋„˜๋Š”๋‹ค โ€” ์‚ฌ์œ ๋ฅผ ๋ณด๊ณ  ๋งคํ•‘์„ ๊ณ ์นœ๋‹ค */\n /**\n * ๋“ค์–ด์˜ค๋Š”๋ฐ **์ปค๋„์ด ๋ฐ›์ง€ ์•Š๋Š”๋‹ค** โ€” ์‚ฌ์‹ค์ด ์‚ฌ๋ผ์ง€๊ณ  ์žˆ๋‹ค.\n *\n * โ”€โ”€ ์ด๋ฆ„์ด ์‚ฌ์‹ค๋ณด๋‹ค ์ข๋‹ค (2026-08-20์— ๋“œ๋Ÿฌ๋‚ฌ๋‹ค) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ๊ด€์ธกํ•œ ๊ฒƒ์€ ใ€Œ์ปค๋„์ด ๋ฐ›์ง€ ์•Š์•˜๋‹คใ€(`ingestLive` ๊ฐ€ 0)์ด๊ณ , **๋ฉˆ์ถ˜ ๊ฒƒ์€ ๊ทธ ์›์ธ ์ค‘ ํ•˜๋‚˜์ผ ๋ฟ**์ด๋‹ค.\n * ๋‹ค๋ฅธ ์›์ธ์ด ์‹ค์ œ๋กœ ์žˆ์—ˆ๋‹ค: ๋ฏธ๋Ÿฌ๋กœ ์„ ์–ธ๋œ ํŠธ์œˆ์„ ์‚ฌ๋žŒ์ด ํ™”๋ฉด์—์„œ ์‹œ์ž‘ํ•˜๋ฉด ์‹œ๋ฎฌ ๊ตฌ๋™์œผ๋กœ ๋–ด๊ณ , ๊ทธ\n * ํŠธ์œˆ์€ **๋Œ๊ณ  ์žˆ์œผ๋ฉด์„œ** ์œ ์ž…์„ ํ•œ ๊ฑด๋„ ๋ฐ›์ง€ ๋ชปํ–ˆ๋‹ค. ๊ทธ๋•Œ ํ™”๋ฉด์€ ํ•œ์ชฝ์— ใ€Œrunningใ€, ๋‹ค๋ฅธ ์ชฝ์—\n * ใ€Œ๋ฉˆ์ถฐใ€๋ผ๊ณ  ์ ์—ˆ๊ณ  ์‚ฌ๋žŒ์€ ใ€Œ์‹œ์ž‘ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋˜๊ฒ ๋„คใ€๋ผ๋Š” ์—‰๋šฑํ•œ ์ฒ˜๋ฐฉ์œผ๋กœ ๊ฐ”๋‹ค.\n *\n * ๊ทธ๋ž˜์„œ **ํ™”๋ฉด ๋ฌธ๊ตฌ๋Š” ๊ด€์ธก ๊ทธ๋Œ€๋กœ** ์ ๋Š”๋‹ค(ใ€ŒํŠธ์œˆ์— ๋‹ฟ์ง€ ์•Š๊ณ  ๋ฒ„๋ ค์ง€๋Š” ์ค‘ใ€). ์ด ํ‚ค ์ด๋ฆ„์€ ์„œ๋ฒ„ยทํ™”๋ฉดยท\n * ์‹œํ—˜์— ๊ฑธ์นœ ๊ณ„์•ฝ ๋ฌธ์ž์—ด์ด๋ผ ๊ทธ๋Œ€๋กœ ๋‘์ง€๋งŒ, ์ด๋ฆ„์„ ์›์ธ์œผ๋กœ ์ฝ์ง€ ์•Š๋Š”๋‹ค.\n */\n | 'twin-not-running'\n | 'no-traffic' /** ํ”ผ๋“œ๋Š” ๋ถ™์—ˆ๋Š”๋ฐ ์ œ์‹œ๊ฐ€ 0 โ€” ์›์ฒœยท๋ง์„ ๋ณธ๋‹ค */\n | 'not-connected' /** ํ”ผ๋“œ ์ž์ฒด๊ฐ€ ์—†๋‹ค โ€” ๊ฒฐ์„ ํ•œ๋‹ค */\n | 'not-applicable' /** ์›๋ณธ์ด ๋จน์ด์ง€ ์•Š๋Š” ํŠธ์œˆ(์‹œ๋ฎฌ)ยท๋ฉˆ์ถ˜ ํŠธ์œˆ โ€” ๋ฌผ์„ ๋Œ€์ƒ์ด ์•„๋‹ˆ๋‹ค */\n | 'unknown' /** ํ•œ ๋ฒˆ๋„ ๋ฐ›์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ์ฐฝ์ด ์•„์ง ๋‹ซํžˆ์ง€ ์•Š์•˜๋‹ค โ€” ๊ธฐ๋‹ค๋ฆฐ๋‹ค */\n\nexport function syncVerdictOf(args: {\n /** `live-feed-registry.liveFeedStateOf` ์˜ ๋‹ต์„ ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค โ€” ํŒ์ • ์–ดํœ˜๋ฅผ ๋‘ ๋ฒŒ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค. */\n feedState: 'attached' | 'detached' | 'not-applicable'\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ **๋‹ซํžŒ** ์ฐฝ. ์—†์œผ๋ฉด `null`. */\n recent: { offered: number; rejected: number } | null\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฌด์—‡์ด๋“  ๋ฐ›์€ ์‹œ๊ฐ. ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `-1` โ€” 0 ์ด ์•„๋‹ˆ๋‹ค(0 ์€ 1970๋…„์ด๋‹ค). */\n lastAtMs?: number\n /** ์ง€๊ธˆ. `lastAtMs` ๋ฅผ ๊ฒฌ์ค„ ๋•Œ๋งŒ ์“ด๋‹ค. */\n nowMs?: number\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ **๋ฒ„๋ ค์ง„** ์‹œ๊ฐ. ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `-1`. */\n lastUndeliveredAtMs?: number\n /**\n * ์ง€๊ธˆ๊นŒ์ง€ ์ œ์‹œ๋œ ๋ˆ„์  ์ˆ˜ โ€” **ใ€Œ๋ฐ›๊ณ  ์žˆ๋Š” ์ค‘ใ€๊ณผ ใ€Œ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹คใ€๋ฅผ ๊ฐ€๋ฅด๋Š” ์‚ฌ์‹ค**์ด๋‹ค.\n *\n * ๋‹ฟ์€ ์‹œ๊ฐ(`lastAtMs`)์œผ๋กœ๋Š” ๊ฐ€๋ฅผ ์ˆ˜ ์—†๋‹ค: ๋นˆ ์ฝ๊ธฐ๋„ ๋‹ฟ์€ ๊ฒƒ์ด๋ฏ€๋กœ ๊ทธ ์‹œ๊ฐ์ด ์„ ๋‹ค. ์ฐฝ์ด ์•„์ง ๋‹ซํžˆ์ง€\n * ์•Š์•˜์„ ๋•Œ ์ด ์ˆ˜๊ฐ€ 0 ์ด๋ฉด ์˜ค๋Š” ๊ฒƒ์ด ์—†๋Š” ๊ฒƒ์ด๊ณ , 0 ์ด ์•„๋‹ˆ๋ฉด ๋ฐ›๊ณ  ์žˆ๋Š” ์ค‘์ด๋‹ค.\n */\n offeredTotal?: number\n threshold?: number\n}): SyncVerdict {\n /*\n * โ”€โ”€ ๋ฒ„๋ ค์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ๋จผ์ €๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ํŠธ์œˆ์ด ๋ฉˆ์ถ”๋ฉด ํ”ผ๋“œ๋Š” ๋‚จ์•„ ๊ณ„์† ๋ ˆ์ฝ”๋“œ๋ฅผ ๋‚˜๋ฅด๊ณ , ์ปค๋„์€ ๊ทธ๊ฒƒ์„ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฐ๋‹ค. ๊ทธ ์ƒํƒœ๋ฅผ ใ€Œํ•ด๋‹น ์—†์Œใ€\n * ์ด๋ผ ๋ถ€๋ฅด๋ฉด **์‚ฌ์‹ค์ด ์‚ฌ๋ผ์ง€๋Š” ๋™์•ˆ ํ™”๋ฉด์ด ์•ˆ์‹ฌ์‹œํ‚จ๋‹ค** โ€” ์‹ค์ œ๋กœ ๊ทธ๋ ‡๊ฒŒ ๋–ด๋‹ค(๋ฉˆ์ถ˜ ํŠธ์œˆ ์˜†์— ใ€Œ150\n * ํ†ต๊ณผ ยท 100%ใ€). ๊ทธ๋ž˜์„œ ์‹œ๋ฎฌยท๋ฉˆ์ถค ํŒ์ •๋ณด๋‹ค ๋จผ์ € ๋ณธ๋‹ค.\n *\n * ์‹œ๋ฎฌ ํŠธ์œˆ์—๋Š” ์ด ๊ฐ’์ด ์„œ์ง€ ์•Š๋Š”๋‹ค(๋จน์ด๋Š” ํ”ผ๋“œ๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋ฏธ์ „๋‹ฌ๋„ ์—†๋‹ค). ๊ทธ๋ž˜์„œ ์ด ์ˆœ์„œ๊ฐ€ ์‹œ๋ฎฌ์—\n * ๊ฑฐ์ง“ ๊ฒฝ๋ณด๋ฅผ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.\n */\n if (\n typeof args.lastUndeliveredAtMs === 'number' &&\n args.lastUndeliveredAtMs >= 0 &&\n typeof args.nowMs === 'number' &&\n args.nowMs - args.lastUndeliveredAtMs <= STALE_AFTER_MS\n ) {\n return 'twin-not-running'\n }\n\n if (args.feedState === 'not-applicable') return 'not-applicable'\n if (args.feedState === 'detached') return 'not-connected'\n\n /*\n * โ”€โ”€ ๋Š๊ธด ํ”ผ๋“œ๊ฐ€ ์ดˆ๋ก์œผ๋กœ ๋‚จ์ง€ ์•Š๊ฒŒ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์ฐฝ์€ **๋‹ค์Œ ์œ ์ž…์ด ์žˆ์„ ๋•Œ** ๋‹ซํžŒ๋‹ค. ๊ทธ๋ž˜์„œ ์œ ์ž…์ด ๋ฉˆ์ถ”๋ฉด ์ฐฝ๋„ ๋ฉˆ์ถ”๊ณ , ใ€Œ์ตœ๊ทผ ์ฐฝใ€์€ ๋ฉˆ์ถ”๊ธฐ ์ง์ „์˜\n * ๊ฑด๊ฐ•ํ–ˆ๋˜ ์ฐฝ์œผ๋กœ ๋‚จ๋Š”๋‹ค. ๊ทธ ์ฐฝ๋งŒ ๋ณด๊ณ  ํŒ์ •ํ•˜๋ฉด **์–ด์ œ ์ฃฝ์€ ํ”ผ๋“œ๊ฐ€ ์˜ค๋Š˜๋„ ใ€Œ๋™๊ธฐํ™” ์ค‘ใ€**์ด๋‹ค. ์ด\n * ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ์ด์œ ๊ฐ€ ๋ฐ”๋กœ ๊ทธ ์ƒํƒœ๋ฅผ ๋“œ๋Ÿฌ๋‚ด๋Š” ๊ฒƒ์ด๋ฏ€๋กœ, ๊ทธ๋•Œ ์ดˆ๋ก์„ ๋‚ด๋ฉด ๋ชฉ์ ์„ ๋†“์นœ๋‹ค.\n *\n * ๊ทธ๋ž˜์„œ ์ฐฝ๊ณผ ๋ณ„๊ฐœ๋กœ **์ง€๊ธˆ๊นŒ์ง€์˜ ์นจ๋ฌต**์„ ๋จผ์ € ๋ณธ๋‹ค. ํ•œ ์ฐฝ์„ ํ†ต์งธ๋กœ ์•„๋ฌด๊ฒƒ๋„ ๋ฐ›์ง€ ์•Š์•˜์œผ๋ฉด ๊ทธ๊ฒƒ์€\n * ใ€Œ์œ ์ž… ์—†์Œใ€์ด๋‹ค โ€” ์‹ค์ œ๋กœ ๊ทธ ๊ตฌ๊ฐ„์˜ ์ฐฝ์€ ๋น„์–ด ์žˆ๊ฒŒ ๋œ๋‹ค.\n *\n * ์ด ํŒ๋‹จ์„ ์„œ๋ฒ„์— ๋‘๋Š” ์ด์œ : ์†Œ๋น„์ฒ˜๊ฐ€ ์—ฌ๋Ÿฟ(๋ชฉ๋กยท์—ฐ๊ฒฐ ์ƒ์„ธยท์•ž์œผ๋กœ ์ƒ๊ธธ ๊ฒƒ)์ด๊ณ , ํ™”๋ฉด๋งˆ๋‹ค ๋‹ค์‹œ\n * ํŒ๋‹จํ•˜๋ฉด ๊ทœ์น™์ด ๊ฐˆ๋ผ์ ธ ์–ด๋–ค ํ™”๋ฉด์€ ์ดˆ๋ก, ์–ด๋–ค ํ™”๋ฉด์€ ๊ฒฝ๊ณ ๋ฅผ ๋งํ•œ๋‹ค.\n *\n * ํ•œ๊ณ„ โ€” ํ•œ ์ฐฝ(10๋ถ„)๋ณด๋‹ค **๋А๋ฆฌ๊ฒŒ ๊ธท๋Š” ์›์ฒœ**์€ ๊ธท๋Š” ์‚ฌ์ด์— ใ€Œ์œ ์ž… ์—†์Œใ€์œผ๋กœ ๋ณด์ธ๋‹ค. ์žฅ๋ถ€๋Š” ์›์ฒœ์˜\n * ์ฃผ๊ธฐ๋ฅผ ๋ชจ๋ฅด๋ฏ€๋กœ ์ด๋ณด๋‹ค ์ •ํ™•ํžˆ ๋‹ตํ•  ์ˆ˜ ์—†๋‹ค. ์ฃผ๊ธฐ๋ฅผ ์•Œ๊ฒŒ ๋˜๋ฉด ๊ทธ ๊ฐ’์œผ๋กœ ๊ฒฌ์ฃผ๋Š” ๊ฒƒ์ด ๋งž๋‹ค.\n */\n if (typeof args.lastAtMs === 'number' && typeof args.nowMs === 'number') {\n if (args.lastAtMs < 0) return 'unknown' /* ๋ถ™์—ˆ์ง€๋งŒ ํ•œ ๋ฒˆ๋„ ๋ฐ›์ง€ ์•Š์•˜๋‹ค โ€” ๊ธฐ๋‹ค๋ฆฐ๋‹ค */\n if (args.nowMs - args.lastAtMs > STALE_AFTER_MS) return 'no-traffic'\n }\n\n /*\n * โ”€โ”€ **๋‹ฟ์•˜๋Š”๋ฐ ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹ค** ๋ฅผ ใ€Œ๋ชจ๋ฅธ๋‹คใ€๋กœ ๋‹ตํ•˜์ง€ ์•Š๋Š”๋‹ค (2026-08-28) โ”€โ”€โ”€โ”€\n *\n * ์ฐฝ์€ **๋‹ค์Œ ์œ ์ž…์ด ์žˆ์„ ๋•Œ** ๋‹ซํžŒ๋‹ค. ๊ทธ๋ž˜์„œ ์˜ค๋Š” ๊ฒƒ์ด ํ•˜๋‚˜๋„ ์—†๋Š” ํŠธ์œˆ์€ ์ฐฝ์ด ์˜์›ํžˆ ๋‹ซํžˆ์ง€ ์•Š๊ณ ,\n * ์ด ์ž๋ฆฌ๊ฐ€ `unknown` ์„ ๋ƒˆ๋‹ค โ€” ๋ถ™์–ด ์žˆ๊ณ  37์ดˆ๋งˆ๋‹ค ์„ฑ๊ณต์ ์œผ๋กœ ์ฝ๋Š” ํŠธ์œˆ์ด ํ™”๋ฉด์—์„œ ใ€Œ๋ชจ๋ฅธ๋‹คใ€์˜€๋‹ค.\n *\n * ๊ทธ๊ฒƒ์ด ๋“œ๋Ÿฌ๋‚œ ๊ณ„๊ธฐ: ์ปค๋„ฅํ„ฐ๊ฐ€ ์žฌ๊ธฐ๋™๋งˆ๋‹ค ์žฌ๊ณ  2,665๊ฑด์„ ๋‹ค์‹œ ๋งํ•˜๋˜ ๊ฒƒ์„ ์—†์•ด๋‹ค. ๊ทธ๋Ÿฌ์ž ์ •์ƒ ์ƒํƒœ์˜\n * ์œ ์ž…์ด 0 ์ด ๋˜์—ˆ๊ณ , **๊ฑด๊ฐ• ์‹ ํ˜ธ๊ฐ€ ๊ทธ ์†Œ์Œ์„ ํƒ€๊ณ  ์žˆ์—ˆ๋‹ค**๋Š” ๊ฒƒ์ด ๋ณด์˜€๋‹ค.\n *\n * โ”€โ”€ ๊ฐ€๋ฅด๋Š” ์‚ฌ์‹ค์€ **๋ฐ›์€ ๊ฒƒ์ด ์žˆ๋‚˜**๋‹ค (์ฒซ ํŒ์„ ๊ณ ์นจ) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * ์ฒ˜์Œ์—๋Š” ๋‹ฟ์€ ์‹œ๊ฐ์œผ๋กœ ๊ฐˆ๋ž๋‹ค. ๊ทธ๊ฒƒ์ด ํ‹€๋ ธ๋‹ค โ€” **๋นˆ ์ฝ๊ธฐ๋„ ๋‹ฟ์€ ๊ฒƒ**์ด๋ผ ๊ทธ ์‹œ๊ฐ์€ ์–ธ์ œ๋‚˜ ์„ ๋‹ค.\n * ๊ทธ๋ž˜์„œ ์ฐฝ์„ ์ฑ„์šฐ๋Š” ์ค‘์ธ ํŠธ์œˆ(์ œ์‹œ 200๊ฑด)๊นŒ์ง€ ใ€Œ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹คใ€๋กœ ๋‹ตํ•˜๊ฒŒ ๋๊ณ , ์‹œํ—˜์ด ๊ทธ๊ฒƒ์„ ์žก์•˜๋‹ค.\n * ๋ฐ›๊ณ  ์žˆ๋Š” ์ค‘์ธ๋ฐ ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์€ **์—†๋Š” ๊ฒƒ๊ณผ ๋ชป ๋ณธ ๊ฒƒ์„ ๋’ค์ง‘๋Š” ๊ฒƒ**์ด๋‹ค.\n *\n * ๋‹ฟ์•˜๊ณ  ยท ์ œ์‹œ๊ฐ€ 0 ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹ค `no-traffic`\n * ๋‹ฟ์•˜๊ณ  ยท ์ œ์‹œ๊ฐ€ ์žˆ๋‹ค ๋ฐ›๋Š” ์ค‘์ด๊ณ  ํŒ์ •ํ•  ์ฐฝ์ด ์•„์ง ์—†๋‹ค `unknown`\n * ํ•œ ๋ฒˆ๋„ ๋‹ฟ์ง€ ์•Š์•˜๋‹ค `unknown`(์œ„ `lastAtMs < 0` ๊ฐˆ๋ž˜)\n */\n if (!args.recent) {\n const touched = typeof args.lastAtMs === 'number' && args.lastAtMs >= 0\n const nothingOffered = (args.offeredTotal ?? 0) <= 0\n return touched && nothingOffered ? 'no-traffic' : 'unknown'\n }\n if (args.recent.offered <= 0) return 'no-traffic'\n\n const ratio = (args.recent.offered - args.recent.rejected) / args.recent.offered\n return ratio >= (args.threshold ?? HEALTHY_ACCEPTED_RATIO) ? 'syncing' : 'mapping-blocked'\n}\n\nexport interface IngestWindowView {\n from: string\n to: string\n offered: number\n /** **ํŠธ์œˆ์— ๋‹ฟ์€** ์ˆ˜. ์ œ์‹œ โˆ’ ๊ฑฐ๋ถ€ โˆ’ ๋ฏธ์ „๋‹ฌ. */\n accepted: number\n /** ๋งคํ•‘์ด ๊ฑฐ๋ถ€ํ•œ ์ˆ˜ โ€” ์ฒ˜๋ฐฉ์€ ๋งคํ•‘์„ ๊ณ ์น˜๋Š” ๊ฒƒ. */\n rejected: number\n /** ๋งคํ•‘์€ ํ†ต๊ณผํ–ˆ์œผ๋‚˜ ํŠธ์œˆ์— ๋‹ฟ์ง€ ๋ชปํ•œ ์ˆ˜ โ€” ์ฒ˜๋ฐฉ์€ ํŠธ์œˆ์„ ์„ธ์šฐ๋Š” ๊ฒƒ. */\n undelivered: number\n /** ํ†ต๊ณผ์œจ(0~1). ์ œ์‹œ๊ฐ€ ์—†์œผ๋ฉด `null` โ€” **1 ๋กœ ์ฑ„์šฐ๋ฉด ใ€Œ์ „๋ถ€ ํ†ต๊ณผใ€๋กœ ์ฝํžŒ๋‹ค.** */\n acceptedRatio: number | null\n /** ์ด ์ฐฝ์— ์ ํžŒ ํ–‰. */\n rows: number\n /** ๊ทธ๊ฒƒ์„ ์ ๊ธฐ ์œ„ํ•ด DB ๋ฅผ ๋‘๋“œ๋ฆฐ ํšŸ์ˆ˜ โ€” ํ–‰๋‹น ์™•๋ณต์ด ์ค„์—ˆ๋Š”์ง€ ๋ณด๋Š” ๊ฐ’. */\n flushes: number\n}\n\nexport interface IngestHealthView {\n verdict: SyncVerdict\n /** ํŒ์ •์— ์“ด ๋ฌธํ„ฑ โ€” ํ™”๋ฉด์ด ์™œ ๊ทธ ํŒ์ •์ธ์ง€ ๋งํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•จ๊ป˜ ๋‚ธ๋‹ค. */\n threshold: number\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ ๋‹ซํžŒ ์ฐฝ. ์ฐฝ์ด ํ•œ ๋ฒˆ๋„ ๋‹ซํžˆ์ง€ ์•Š์•˜์œผ๋ฉด `null`. */\n recent: IngestWindowView | null\n /** ๋‹ซํžŒ ์ฐฝ๋“ค, ์˜ค๋ž˜๋œ ๊ฒƒ๋ถ€ํ„ฐ. */\n trend: IngestWindowView[]\n /** ์ถ”์ด ์ƒํ•œ์—์„œ ๋ฐ€๋ ค๋‚œ ์ฐฝ ์ˆ˜. */\n trendDropped: number\n /** ๋ˆ„์ (๊ธฐ๋™ ์ดํ›„). */\n total: {\n offered: number\n accepted: number\n rejected: number\n undelivered: number\n acceptedRatio: number | null\n /** ์ €๋„์— ์ ํžŒ ํ–‰๊ณผ ๊ทธ๊ฒƒ์„ ์œ„ํ•ด DB ๋ฅผ ๋‘๋“œ๋ฆฐ ํšŸ์ˆ˜ โ€” ๊ธฐ๋™ ์ดํ›„ ๋ˆ„์ . */\n rows: number\n flushes: number\n }\n /** ์‚ฌ์œ ๋ณ„ ๋ˆ„์ , ๋งŽ์€ ๊ฒƒ๋ถ€ํ„ฐ. `lastAt` ์ด ๋ฉˆ์ถ˜ ์‚ฌ์œ ๋Š” ์ด๋ฏธ ๊ณ ์ณ์ง„ ๊ฒƒ์ด๋‹ค. */\n reasons: { reason: string; count: number; lastAt: string }[]\n /** ์‚ฌ์œ  ์ข…๋ฅ˜๊ฐ€ ์ƒํ•œ์— ๋‹ฟ์•„ ์ผ๋ถ€๊ฐ€ `(other reasons)` ๋กœ ์ ‘ํ˜”๋‚˜. */\n reasonsCapped: boolean\n samples: RejectSample[]\n sampleDropped: number\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฌด์—‡์ด๋“  ๋ฐ›์€ ์‹œ๊ฐ. ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `null`. */\n lastAt: string | null\n /** ๋งˆ์ง€๋ง‰์œผ๋กœ ์ €๋„์— ์ ์€ ์‹œ๊ฐ. ํ•œ ๋ฒˆ๋„ ์—†์œผ๋ฉด `null` โ€” ๋ฐ›์€ ์  ์—†๋Š” ํŠธ์œˆ๋„ ์ด ๊ฐ’์€ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค. */\n lastWriteAt: string | null\n /**\n * **์›๋ณธ์— ๋‹ฟ์ง€ ๋ชปํ•˜๊ณ  ์žˆ๋‹ค** โ€” ์—†์œผ๋ฉด ๋‹ฟ๊ณ  ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค(ยง`IngestLedger.readFailure`).\n *\n * ์ปค์„œ ์ •์ฒด(`cursorStall`)์™€ **์„ž์ด์ง€ ์•Š๋Š”๋‹ค**: ์ด์ชฝ์€ ๊ธฐ๋‹ค๋ฆฌ๋ฉด ํ’€๋ฆฌ๊ณ  ๊ทธ์ชฝ์€ ๊ธฐ๋‹ค๋ ค๋„ ์•ˆ ํ’€๋ฆฐ๋‹ค.\n *\n * ใ€Œ์œ ์ž… ์—†์Œใ€๊ณผ **๋‹ค๋ฅธ ํŒ์ •**์ด๋‹ค: ์œ ์ž… ์—†์Œ์€ ์—ฐ๊ฒฐ๋œ ์‹œ์Šคํ…œ์—์„œ ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†๋Š” ๊ฒƒ์ผ ์ˆ˜ ์žˆ๊ณ , ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ๋ฌป์ง€๋„\n * ๋ชปํ•œ ๊ฒƒ์ด๋‹ค. ํ™”๋ฉด์€ ๋‘ ๋ฌธ์žฅ์„ ๋‹ค๋ฅด๊ฒŒ ๋งํ•ด์•ผ ํ•œ๋‹ค โ€” ์‹ค ์›๋ณธ์ด ๋Š๊ฒผ์„ ๋•Œ ์‚ฌ์šฉ์ž๊ฐ€ ์›์ธ์„ ์ฐพ์„ ์ˆ˜\n * ์žˆ๋Š”์ง€๊ฐ€ ๊ทธ ์ฐจ์ด๋‹ค.\n */\n readFailure?: {\n consecutive: number\n /** ์ด ๋‹จ์ ˆ์ด ์‹œ์ž‘๋œ ์‹œ๊ฐ โ€” ใ€Œ20๋ถ„์งธใ€๋ฅผ ๋งํ•  ์ˆ˜ ์žˆ๊ฒŒ. */\n since: string\n lastAt: string\n reason: string\n stream?: string\n /** ๋‹ค์Œ์— ๋‹ค์‹œ ๋ฌผ์„ ๋•Œ๊นŒ์ง€ ๋‚จ์€ ์‹œ๊ฐ„(ms) โ€” ์ปค๋„ฅํ„ฐ๊ฐ€ ๋ฌผ๋Ÿฌ์„ฐ์„ ๋•Œ๋งŒ ์žˆ๋‹ค. */\n nextRetryMs?: number\n }\n /**\n * ๋งˆ์ง€๋ง‰์œผ๋กœ ๋Š๊ฒผ๋‹ค๊ฐ€ ๋Œ์•„์˜จ ๊ฒƒ โ€” ์—†์œผ๋ฉด ๋Š๊ธด ์ ์ด ์—†๊ฑฐ๋‚˜ ์•„์ง ๋Œ์•„์˜ค์ง€ ์•Š์€ ๊ฒƒ์ด๋‹ค (2026-08-27).\n *\n * `readFailure` ๋Š” ๋Œ์•„์˜ค๋ฉด ์‚ฌ๋ผ์ง„๋‹ค. ์ด ์นธ์ด ์—†์œผ๋ฉด ๋ฐค์ƒˆ ๋Š๊ฒผ๋˜ ์—ฐ๊ฒฐ๊ณผ ํ•œ ๋ฒˆ๋„ ๋Š๊ธฐ์ง€ ์•Š์€ ์—ฐ๊ฒฐ์ด\n * ์•„์นจ์— ๋˜‘๊ฐ™์ด ๋ณด์ธ๋‹ค.\n */\n lastRecovery?: { at: string; afterFailures: number; downMs: number }\n}\n\nfunction toWindowView(w: IngestWindow, fallbackEndMs: number): IngestWindowView {\n /* ใ€Œํ†ต๊ณผใ€๋Š” **ํŠธ์œˆ์— ๋‹ฟ์€** ๊ฒƒ๋งŒ์ด๋‹ค. ๋ฒ„๋ ค์ง„ ๊ฒƒ์„ ํ†ต๊ณผ๋กœ ์„ธ๋ฉด ํ™”๋ฉด์ด ์•ˆ์‹ฌ์‹œํ‚จ๋‹ค. */\n const accepted = w.offered - w.rejected - w.undelivered\n return {\n from: new Date(w.startMs).toISOString(),\n to: new Date(w.endMs >= 0 ? w.endMs : fallbackEndMs).toISOString(),\n offered: w.offered,\n accepted,\n rejected: w.rejected,\n undelivered: w.undelivered,\n acceptedRatio: w.offered > 0 ? round4(accepted / w.offered) : null,\n rows: w.rows,\n flushes: w.flushes\n }\n}\n\n/**\n * ์ฝ๊ธฐ์šฉ ์š”์•ฝ.\n *\n * ์žฅ๋ถ€๊ฐ€ ์—†์–ด๋„(ํ•œ ๋ฒˆ๋„ ๋ฐ›์ง€ ์•Š์€ ํŠธ์œˆ) **ํŒ์ •์€ ๋‚ธ๋‹ค** โ€” ํ”ผ๋“œ ์‚ฌ์‹ค๋งŒ์œผ๋กœ ๋‹ตํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค\n * (`not-applicable`ยท`not-connected`). ๊ทธ ๊ฒฝ์šฐ ์ˆ˜์น˜๋Š” ๋น„์šด๋‹ค: ์—†๋Š” ๊ฐ’์„ 0 ์œผ๋กœ ์ฑ„์šฐ์ง€ ์•Š๋Š”๋‹ค.\n *\n * `nowMs` ๋ฅผ ๋ฐ›๋Š” ์ด์œ : ๋Š๊ธด ํ”ผ๋“œ๋ฅผ ๊ฐ€๋ ค๋‚ด๋ ค๋ฉด ใ€Œ๋งˆ์ง€๋ง‰ ์œ ์ž…์ด ์–ผ๋งˆ๋‚˜ ์ „์ธ๊ฐ€ใ€๋ฅผ ๋ด์•ผ ํ•˜๊ณ , ๊ทธ ํŒ๋‹จ์„\n * ํ™”๋ฉด์ด ์•„๋‹ˆ๋ผ ์—ฌ๊ธฐ์„œ ํ•œ๋‹ค(์†Œ๋น„์ฒ˜๋งˆ๋‹ค ๋‹ค์‹œ ํŒ๋‹จํ•˜๋ฉด ๊ทœ์น™์ด ๊ฐˆ๋ผ์ง„๋‹ค). ์‹œํ—˜์ด ์‹œ๊ฐ์„ ๊ณ ์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก\n * ์ธ์ž๋กœ ๋ฐ›๋Š”๋‹ค.\n */\nexport function ingestHealth(\n ledger: IngestLedger | undefined,\n feedState: 'attached' | 'detached' | 'not-applicable',\n threshold = HEALTHY_ACCEPTED_RATIO,\n nowMs: number = Date.now()\n): IngestHealthView {\n const recentRaw = ledger?.trend.length ? ledger.trend[ledger.trend.length - 1] : null\n /* ์—ด๋ฆฐ ์ฐฝ์˜ ๋ ์‹œ๊ฐ ๋Œ€์ฒด๊ฐ’ โ€” ์œ ์ž…์ด ์—†๋Š” ์žฅ๋ถ€์—์„œ `-1` ์„ ์‹œ๊ฐ์œผ๋กœ ์ฝ์œผ๋ฉด 1969๋…„์ด ํ™”๋ฉด์— ๋œฌ๋‹ค. */\n const lastAnyMs = Math.max(ledger?.lastAtMs ?? -1, ledger?.lastWriteAtMs ?? -1)\n const verdict = syncVerdictOf({\n feedState,\n recent: recentRaw,\n lastAtMs: ledger?.lastAtMs ?? -1,\n lastUndeliveredAtMs: ledger?.lastUndeliveredAtMs ?? -1,\n /* ์ฐฝ์ด ๋‹ซํžˆ์ง€ ์•Š์•˜์„ ๋•Œ ใ€Œ๋ฐ›๋Š” ์ค‘ใ€๊ณผ ใ€Œ์˜ค๋Š” ๊ฒƒ์ด ์—†๋‹คใ€๋ฅผ ๊ฐ€๋ฅด๋Š” ์‚ฌ์‹ค. */\n offeredTotal: ledger?.offered ?? 0,\n nowMs,\n threshold\n })\n\n /*\n * **์œ ์ž…์ด ์—†์–ด๋„ ใ€Œ๋นˆ ์žฅ๋ถ€ใ€๊ฐ€ ์•„๋‹ˆ๋‹ค.** ์‹œ๋ฎฌ ํŠธ์œˆ์€ ๋ฐ›๋Š” ๊ฒƒ ์—†์ด ์ ๋Š”๋‹ค. ์œ ์ž… ์‹œ๊ฐ๋งŒ ๋ณด๊ณ  ๋น„์—ˆ๋‹ค๊ณ \n * ํŒ๋‹จํ•˜๋ฉด ๊ทธ ํŠธ์œˆ์˜ ์ฐฝ์€ ๋งŒ๋“ค์–ด์ ธ ์žˆ๋Š”๋ฐ ํ™”๋ฉด์—๋Š” ์•„๋ฌด ์ถ”์ด๋„ ๋‚˜์˜ค์ง€ ์•Š๋Š”๋‹ค(์ด ์ถ•์„ ๋„ฃ์€ ์ด์œ ๊ฐ€ ๊ทธ๊ฒƒ์ด๋‹ค).\n */\n if (!ledger || (ledger.lastAtMs < 0 && ledger.lastWriteAtMs < 0)) {\n return {\n verdict,\n threshold,\n recent: null,\n trend: [],\n trendDropped: 0,\n total: { offered: 0, accepted: 0, rejected: 0, undelivered: 0, acceptedRatio: null, rows: 0, flushes: 0 },\n reasons: [],\n reasonsCapped: false,\n samples: [],\n sampleDropped: 0,\n lastAt: null,\n lastWriteAt: null,\n /*\n * **์ด ์งง์€ ๋‹ต์—๋„ ๋‹จ์ ˆ์€ ์‹ค์–ด์•ผ ํ•œ๋‹ค** โ€” ์ด ์ž๋ฆฌ๊ฐ€ ๋ฐ”๋กœ ๊ทธ ํŠธ์œˆ์˜ ๋ชจ์Šต์ด๋‹ค.\n *\n * ๋ฐ›์€ ๊ฒƒ๋„ ์ ์€ ๊ฒƒ๋„ ์—†๋Š” ์žฅ๋ถ€๋Š” ใ€Œ์•„์ง ์•„๋ฌด ์ผ๋„ ์—†๋‹คใ€๋กœ ์ฝํžˆ๋Š”๋ฐ, ์›๋ณธ์— ๋‹ฟ์ง€ ๋ชปํ•˜๋Š” ํŠธ์œˆ์ด\n * ์ •ํ™•ํžˆ ๊ทธ ๋ชจ์Šต์ด๋‹ค(์ฝ์ง€ ๋ชปํ•˜๋ฉด ๋ฐ›์€ ๊ฒƒ๋„ ์ ์€ ๊ฒƒ๋„ ์—†๋‹ค). ์—ฌ๊ธฐ์„œ ๋–จ์–ด๋œจ๋ฆฌ๋ฉด **๊ฐ€์žฅ ํ•„์š”ํ•œ\n * ์ˆœ๊ฐ„์—** ๊ทธ ์‚ฌ์‹ค์ด ์‚ฌ๋ผ์ง„๋‹ค โ€” ์‹ค์ธก์œผ๋กœ ์›๋ณธ์ด ๋Š๊ธด ํŠธ์œˆ์ด ์ด ๊ฒฝ๋กœ๋กœ ๋‹ตํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.\n */\n ...(ledger?.readFailure\n ? {\n readFailure: {\n consecutive: ledger.readFailure.consecutive,\n since: new Date(ledger.readFailure.sinceMs).toISOString(),\n lastAt: new Date(ledger.readFailure.lastAtMs).toISOString(),\n reason: ledger.readFailure.reason,\n ...(ledger.readFailure.stream ? { stream: ledger.readFailure.stream } : {}),\n ...(ledger.readFailure.nextRetryMs ? { nextRetryMs: ledger.readFailure.nextRetryMs } : {})\n }\n }\n : {}),\n ...(ledger?.lastRecovery\n ? {\n lastRecovery: {\n at: new Date(ledger.lastRecovery.atMs).toISOString(),\n afterFailures: ledger.lastRecovery.afterFailures,\n downMs: ledger.lastRecovery.downMs\n }\n }\n : {}),\n /* ์ปค์„œ ์ •์ฒด๋„ **์ด ๊ฐˆ๋ž˜์—์„œ** ๋‚ด๋ณด๋‚ธ๋‹ค โ€” ์œ ์ž…์ด ์—†๋Š” ์žฅ๋ถ€์—์„œ๋„ ์ •์ฒด๋Š” ์‹ค์žฌํ•œ๋‹ค(์ฝ์—ˆ์œผ๋‚˜ ๋ชป\n ๋„˜๊ฒผ์œผ๋ฉด ์œ ์ž…์ด 0์ด๋‹ค). ์—ฌ๊ธฐ์„œ ๋–จ์–ด๋œจ๋ฆฌ๋ฉด ๋ฐ”๋กœ ๊ทธ ์ƒํ™ฉ์—์„œ ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†์–ด์ง„๋‹ค. */\n ...(ledger?.cursorStall\n ? {\n cursorStall: {\n consecutive: ledger.cursorStall.consecutive,\n since: new Date(ledger.cursorStall.sinceMs).toISOString(),\n lastAt: new Date(ledger.cursorStall.lastAtMs).toISOString(),\n reason: ledger.cursorStall.reason,\n ...(ledger.cursorStall.stream ? { stream: ledger.cursorStall.stream } : {})\n }\n }\n : {}),\n /*\n * **์„ธ์šฐ์ง€ ์•Š์€ ๊ฒƒ๋„ ๋‚ธ๋‹ค โ€” ์–‘์ชฝ ๊ฐˆ๋ž˜์—์„œ.** ์œ ์ž…์ด ํ•œ ๊ฑด๋„ ์—†๋Š” ์žฅ๋ถ€์—์„œ๋„ ์ด ์‚ฌ์‹ค์€ ์‹ค์žฌํ•œ๋‹ค\n * (์›๋ณธ์ด ๋งํ•˜๋Š”๋ฐ ์ „๋ถ€ ์•ˆ ๋ฐ›๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค). ์—ฌ๊ธฐ์„œ ๋–จ์–ด๋œจ๋ฆฌ๋ฉด ๋ฐ”๋กœ ๊ทธ ์ƒํ™ฉ์—์„œ ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†์–ด์ง„๋‹ค.\n */\n ...(ledger?.withheld?.length\n ? {\n withheld: ledger.withheld.map(w => ({\n reason: w.reason,\n count: w.count,\n lastAt: new Date(w.lastAtMs).toISOString()\n }))\n }\n : {})\n }\n }\n\n /* ใ€Œํ†ต๊ณผใ€๋Š” ํŠธ์œˆ์— ๋‹ฟ์€ ๊ฒƒ๋งŒ์ด๋‹ค โ€” ์ฐฝ ๋ทฐ์™€ ๊ฐ™์€ ๊ทœ์น™. */\n const accepted = ledger.offered - ledger.rejected - ledger.undelivered\n return {\n verdict,\n threshold,\n recent: recentRaw ? toWindowView(recentRaw, lastAnyMs) : null,\n trend: ledger.trend.map(w => toWindowView(w, lastAnyMs)),\n trendDropped: ledger.trendDropped,\n /* ์„ธ์šฐ์ง€ ์•Š์€ ๊ฒƒ โ€” ์–‘์ชฝ ๊ฐˆ๋ž˜์—์„œ ๊ฐ™์€ ๋ชจ์–‘์œผ๋กœ ๋‚ธ๋‹ค(ยง`IngestLedger.withheld`). */\n ...(ledger.withheld?.length\n ? {\n withheld: ledger.withheld.map(w => ({\n reason: w.reason,\n count: w.count,\n lastAt: new Date(w.lastAtMs).toISOString()\n }))\n }\n : {}),\n /*\n * **์–ด๋–ค ๊ธธ๋กœ ๋“ค์–ด์˜ค๊ณ  ์žˆ๋‚˜** โ€” ํ™”๋ฉด์ด ใ€Œ์ด ํŠธ์œˆ์€ ๋ฌด์—‡์„ ํ†ตํ•ด ๊ฐ’์„ ๋ฐ›๋‚˜ใ€๋ฅผ ๋งํ•  ์ˆ˜ ์žˆ๊ฒŒ.\n * ๋น„์–ด ์žˆ์œผ๋ฉด ์นธ์„ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค: ์•„์ง ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๋ฐ›์€ ํŠธ์œˆ๊ณผ ใ€Œ๊ธธ์ด ์—†๋‹คใ€๋ฅผ ๊ตฌ๋ณ„ํ•œ๋‹ค.\n */\n ...(Object.keys(ledger.bySource).length\n ? {\n sources: Object.entries(ledger.bySource)\n .map(([source, v]) => ({ source, count: v.count, lastAt: new Date(v.lastAtMs).toISOString() }))\n .sort((a, b) => b.count - a.count)\n }\n : {}),\n /* ๊ฐ™์€ ์‚ฌ์‹ค์ด ๋‘ ๋ฒˆ ์™€์„œ ๋ฒ„๋ฆฐ ์ˆ˜ โ€” 0 ์ด๋ฉด ์นธ์„ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค(๋ฒ„๋ฆฐ ์ ์ด ์—†๋‹ค๋Š” ๋œป์ด๋‹ค). */\n ...(ledger.duplicates\n ? { duplicates: { count: ledger.duplicates, lastAt: new Date(ledger.lastDuplicateAtMs).toISOString() } }\n : {}),\n total: {\n offered: ledger.offered,\n accepted,\n rejected: ledger.rejected,\n undelivered: ledger.undelivered,\n acceptedRatio: ledger.offered > 0 ? round4(accepted / ledger.offered) : null,\n rows: ledger.rows,\n flushes: ledger.flushes\n },\n reasons: Object.entries(ledger.byReason)\n .map(([reason, v]) => ({ reason, count: v.count, lastAt: new Date(v.lastAtMs).toISOString() }))\n .sort((a, b) => b.count - a.count || a.reason.localeCompare(b.reason)),\n reasonsCapped: ledger.reasonsCapped,\n samples: [...ledger.samples],\n sampleDropped: ledger.sampleDropped,\n /* ใ€Œ๋ฐ›์€ ์  ์—†์Œใ€๊ณผ ใ€Œ๋ฐฉ๊ธˆ ๋ฐ›์Œใ€์„ ๊ฐ€๋ฅธ๋‹ค โ€” ์ ๊ธฐ๋งŒ ํ•˜๋Š” ํŠธ์œˆ์˜ ์œ ์ž… ์‹œ๊ฐ์€ **์—†๋‹ค**. */\n lastAt: ledger.lastAtMs >= 0 ? new Date(ledger.lastAtMs).toISOString() : null,\n lastWriteAt: ledger.lastWriteAtMs >= 0 ? new Date(ledger.lastWriteAtMs).toISOString() : null,\n /* ๋‹ฟ์ง€ ๋ชปํ•˜๊ณ  ์žˆ์œผ๋ฉด ๊ทธ ์‚ฌ์‹ค์„ ๋‚ธ๋‹ค โ€” ์—†์œผ๋ฉด ํ•„๋“œ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค(๋นˆ ๊ฐ’์ด ใ€Œ๋‹ฟ๋Š”๋‹คใ€๋กœ ์ฝํžˆ์ง€ ์•Š๊ฒŒ). */\n ...(ledger.readFailure\n ? {\n readFailure: {\n consecutive: ledger.readFailure.consecutive,\n since: new Date(ledger.readFailure.sinceMs).toISOString(),\n lastAt: new Date(ledger.readFailure.lastAtMs).toISOString(),\n reason: ledger.readFailure.reason,\n ...(ledger.readFailure.stream ? { stream: ledger.readFailure.stream } : {}),\n ...(ledger.readFailure.nextRetryMs ? { nextRetryMs: ledger.readFailure.nextRetryMs } : {})\n }\n }\n : {}),\n /* ๋Š๊ฒผ๋‹ค๊ฐ€ ๋Œ์•„์˜จ ๊ฒƒ โ€” ์ง€๊ธˆ ๋‹ฟ๊ณ  ์žˆ์–ด๋„ ๋‚จ๊ธด๋‹ค(๊ทธ๊ฒƒ์ด ์—†์œผ๋ฉด ๋ฐค์ƒˆ์˜ ๋‹จ์ ˆ์ด ์‚ฌ๋ผ์ง„๋‹ค). */\n ...(ledger.lastRecovery\n ? {\n lastRecovery: {\n at: new Date(ledger.lastRecovery.atMs).toISOString(),\n afterFailures: ledger.lastRecovery.afterFailures,\n downMs: ledger.lastRecovery.downMs\n }\n }\n : {}),\n ...(ledger.cursorStall\n ? {\n cursorStall: {\n consecutive: ledger.cursorStall.consecutive,\n since: new Date(ledger.cursorStall.sinceMs).toISOString(),\n lastAt: new Date(ledger.cursorStall.lastAtMs).toISOString(),\n reason: ledger.cursorStall.reason,\n ...(ledger.cursorStall.stream ? { stream: ledger.cursorStall.stream } : {})\n }\n }\n : {})\n }\n}\n\n/** ๋น„์œจ์€ ์ž๋ฆฟ์ˆ˜๋ฅผ ๋„‰๋„‰ํžˆ ์ค€๋‹ค โ€” 0.999 ๊ฐ€ 1 ๋กœ ๋ฐ˜์˜ฌ๋ฆผ๋˜๋ฉด ใ€Œ์ „๋ถ€ ํ†ต๊ณผใ€๋กœ ์ฝํžŒ๋‹ค. */\nconst round4 = (v: number) => Math.round(v * 10000) / 10000\n"]}
@@ -27,7 +27,7 @@ export interface ProbeResult {
27
27
  *
28
28
  * ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ํŠธ์œˆ์— ๋ผ์ด๋ธŒ ํ”„๋กœ๋ธŒ๋ฅผ ๋“ค์ด๋Œ€๋ฉด ๋นจ๊ฐ„ ์นธ์ด ์ƒ๊ธฐ๋Š”๋ฐ ๊ทธ๊ฑด ๊ฒฐํ•จ์ด ์•„๋‹ˆ๋‹ค โ€” ์‹œ๋ฎฌ์€ ์›์ฒœ์—์„œ
29
29
  * ๋ฐ›๋Š” ๊ฒƒ์ด ์—†๋‹ค. ๋ ˆํผ๋Ÿฐ์Šค ์—†์ด ํ…œํ”Œ๋ฆฟ์œผ๋กœ ์„ธ์šด ํŠธ์œˆ์—๋Š” ์˜ฎ๊ฒจ ์˜จ ๋งˆ์Šคํ„ฐ๊ฐ€ ์—†์œผ๋ฏ€๋กœ ใ€Œ๋ชป ์˜ฎ๊ธด ๊ฐ’ใ€๋„ ์—†๋‹ค.
30
- * ์ด๊ฑธ ํ†ต๊ณผ๋กœ ์ ‘์œผ๋ฉด ์—†๋Š” ๋Šฅ๋ ฅ์„ ์ฃผ์žฅํ•˜๊ณ , ๋ฏธ์ธก์ •์œผ๋กœ ์ ‘์œผ๋ฉด ์˜์›ํžˆ ๋ฐฐ์„ ์„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.
30
+ * ์ด๊ฑธ ํ†ต๊ณผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด ์—†๋Š” ๋Šฅ๋ ฅ์„ ์ฃผ์žฅํ•˜๊ณ , ๋ฏธ์ธก์ •์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด ์˜์›ํžˆ ๋ฐฐ์„ ์„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.
31
31
  */
32
32
  applicable?: boolean;
33
33
  /** ๋Œ๋ ธ์„ ๋•Œ์˜ ํŒ์ •. ๋Œ๋ฆฌ์ง€ ์•Š์•˜์œผ๋ฉด ๋ฌด์˜๋ฏธํ•˜๋‹ค. */
@@ -43,9 +43,9 @@ export interface ProbeResult {
43
43
  detail?: string;
44
44
  }
45
45
  /**
46
- * ํ”„๋กœ๋ธŒ ๊ฒฐ๊ณผ๋ฅผ **์ฆ๊ฑฐ**๋กœ ์ ‘๋Š”๋‹ค.
46
+ * ํ”„๋กœ๋ธŒ ๊ฒฐ๊ณผ๋ฅผ **์ฆ๊ฑฐ**๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
47
47
  *
48
- * ์ ‘๋Š” ๊ทœ์น™์˜ ์ •์ˆ˜: **๋Œ๋ฆฌ์ง€ ์•Š์€ ํ”„๋กœ๋ธŒ๋Š” `null`** ๋กœ ๊ฐ„๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํŒ์ •์ด ๊ทธ ๋‹จ๊ณ„๋ฅผ ๋‹ฌ์„ฑ์œผ๋กœ
48
+ * ๊ณ„์‚ฐ ๊ทœ์น™์˜ ์ •์ˆ˜: **๋Œ๋ฆฌ์ง€ ์•Š์€ ํ”„๋กœ๋ธŒ๋Š” `null`** ๋กœ ๊ฐ„๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํŒ์ •์ด ๊ทธ ๋‹จ๊ณ„๋ฅผ ๋‹ฌ์„ฑ์œผ๋กœ
49
49
  * ๋งŒ๋“ค์ง€ ์•Š๊ณ  `unmeasured` ๋กœ ๋งํ•œ๋‹ค โ€” ์Šค์œ„ํŠธ๋ฅผ ์ ˆ๋ฐ˜๋งŒ ๋Œ๋ฆฌ๊ณ  ์ดˆ๋ก์„ ์ฃผ์žฅํ•  ๊ธธ์ด ์—†๋‹ค.
50
50
  */
51
51
  export declare function foldProbeResults(results: readonly ProbeResult[]): StageEvidence;