@things-factory/headless-twin 10.1.278 → 10.1.279

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 (614) hide show
  1. package/package.json +21 -7
  2. package/dist-server/engine/attention-digest.js.map +0 -1
  3. package/dist-server/engine/canonical-ingest.js.map +0 -1
  4. package/dist-server/engine/checkpoint-fold.js.map +0 -1
  5. package/dist-server/engine/command-routing.js.map +0 -1
  6. package/dist-server/engine/declared-stimulus.js.map +0 -1
  7. package/dist-server/engine/energy-topology.js.map +0 -1
  8. package/dist-server/engine/event-time.js.map +0 -1
  9. package/dist-server/engine/index.js.map +0 -1
  10. package/dist-server/engine/ingest-dedupe.js.map +0 -1
  11. package/dist-server/engine/ingest-health.js.map +0 -1
  12. package/dist-server/engine/integration-coverage.js.map +0 -1
  13. package/dist-server/engine/integration-probes.js.map +0 -1
  14. package/dist-server/engine/integration-runner.js.map +0 -1
  15. package/dist-server/engine/integration-target-profile.js.map +0 -1
  16. package/dist-server/engine/kpi-baseline.js.map +0 -1
  17. package/dist-server/engine/kpi-fold.js.map +0 -1
  18. package/dist-server/engine/kpi-query.js.map +0 -1
  19. package/dist-server/engine/kpi-target.js.map +0 -1
  20. package/dist-server/engine/live-attentions.js.map +0 -1
  21. package/dist-server/engine/live-feed-registry.js.map +0 -1
  22. package/dist-server/engine/load-meter.js.map +0 -1
  23. package/dist-server/engine/local-declarations.js.map +0 -1
  24. package/dist-server/engine/log.js.map +0 -1
  25. package/dist-server/engine/loop-lag.js.map +0 -1
  26. package/dist-server/engine/measured-estimator.js.map +0 -1
  27. package/dist-server/engine/measured-yield.js.map +0 -1
  28. package/dist-server/engine/model-basis.js.map +0 -1
  29. package/dist-server/engine/model-gap.js.map +0 -1
  30. package/dist-server/engine/model-refusal.js.map +0 -1
  31. package/dist-server/engine/model-vocabulary.js.map +0 -1
  32. package/dist-server/engine/oee-accumulator.js.map +0 -1
  33. package/dist-server/engine/operation-basis.js.map +0 -1
  34. package/dist-server/engine/producer-guard.js.map +0 -1
  35. package/dist-server/engine/property-effects.js.map +0 -1
  36. package/dist-server/engine/read-time.js.map +0 -1
  37. package/dist-server/engine/restart-policy.js.map +0 -1
  38. package/dist-server/engine/runtime-key.js.map +0 -1
  39. package/dist-server/engine/spec-coverage.js.map +0 -1
  40. package/dist-server/engine/stage-path.js.map +0 -1
  41. package/dist-server/engine/state-axes.js.map +0 -1
  42. package/dist-server/engine/structure-diff.js.map +0 -1
  43. package/dist-server/engine/travel-estimator.js.map +0 -1
  44. package/dist-server/engine/twin-engine.js.map +0 -1
  45. package/dist-server/engine/twin-state-channel.js.map +0 -1
  46. package/dist-server/engine/warm-start.js.map +0 -1
  47. package/dist-server/index.js.map +0 -1
  48. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +0 -1
  49. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +0 -1
  50. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +0 -1
  51. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +0 -1
  52. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +0 -1
  53. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +0 -1
  54. package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.js.map +0 -1
  55. package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.js.map +0 -1
  56. package/dist-server/migrations/1786800000000-CreateTwinReferenceReconciliations.js.map +0 -1
  57. package/dist-server/migrations/index.js.map +0 -1
  58. package/dist-server/routes.js.map +0 -1
  59. package/dist-server/service/actuation/actuation-advisor.js.map +0 -1
  60. package/dist-server/service/actuation/actuation-outcome.js.map +0 -1
  61. package/dist-server/service/actuation/actuation-rule-resolver.js.map +0 -1
  62. package/dist-server/service/actuation/actuation-rule.js.map +0 -1
  63. package/dist-server/service/actuation/approval-gateway.js.map +0 -1
  64. package/dist-server/service/actuation/catch-up-effects.js.map +0 -1
  65. package/dist-server/service/actuation/command-dispatcher.js.map +0 -1
  66. package/dist-server/service/actuation/command-store.js.map +0 -1
  67. package/dist-server/service/actuation/dispatch-after-approval.js.map +0 -1
  68. package/dist-server/service/actuation/effect-match.js.map +0 -1
  69. package/dist-server/service/actuation/index.js.map +0 -1
  70. package/dist-server/service/actuation/retry-plan.js.map +0 -1
  71. package/dist-server/service/actuation/rule-evaluate.js.map +0 -1
  72. package/dist-server/service/actuation/rule-loop.js.map +0 -1
  73. package/dist-server/service/actuation/rule-runner.js.map +0 -1
  74. package/dist-server/service/actuation/rule-wiring.js.map +0 -1
  75. package/dist-server/service/actuation/settle-effects.js.map +0 -1
  76. package/dist-server/service/actuation/sweep-dispatch.js.map +0 -1
  77. package/dist-server/service/actuation/twin-command-resolver.js.map +0 -1
  78. package/dist-server/service/actuation/twin-command.js.map +0 -1
  79. package/dist-server/service/actuation/twin-now.js.map +0 -1
  80. package/dist-server/service/index.js.map +0 -1
  81. package/dist-server/service/reference/actuation-routing.js.map +0 -1
  82. package/dist-server/service/reference/actuation-target.js.map +0 -1
  83. package/dist-server/service/reference/actuation-verification.js.map +0 -1
  84. package/dist-server/service/reference/attention-lane.js.map +0 -1
  85. package/dist-server/service/reference/connection-portability-resolver.js.map +0 -1
  86. package/dist-server/service/reference/connection-portability.js.map +0 -1
  87. package/dist-server/service/reference/control-routing.js.map +0 -1
  88. package/dist-server/service/reference/discovery-result.js.map +0 -1
  89. package/dist-server/service/reference/fill-cadence.js.map +0 -1
  90. package/dist-server/service/reference/fill-dedupe.js.map +0 -1
  91. package/dist-server/service/reference/fill-loop.js.map +0 -1
  92. package/dist-server/service/reference/fill-span.js.map +0 -1
  93. package/dist-server/service/reference/hook-contract.js.map +0 -1
  94. package/dist-server/service/reference/hook-rejected.js.map +0 -1
  95. package/dist-server/service/reference/hook-store.js.map +0 -1
  96. package/dist-server/service/reference/index.js.map +0 -1
  97. package/dist-server/service/reference/ingest-space.js.map +0 -1
  98. package/dist-server/service/reference/knob-defaults.js.map +0 -1
  99. package/dist-server/service/reference/live-cadence.js.map +0 -1
  100. package/dist-server/service/reference/live-feed-lease.js.map +0 -1
  101. package/dist-server/service/reference/live-ingest.js.map +0 -1
  102. package/dist-server/service/reference/reference-adapter.js.map +0 -1
  103. package/dist-server/service/reference/reference-assessment.js.map +0 -1
  104. package/dist-server/service/reference/reference-fill-run.js.map +0 -1
  105. package/dist-server/service/reference/reference-fill.js.map +0 -1
  106. package/dist-server/service/reference/reference-hook.js.map +0 -1
  107. package/dist-server/service/reference/reference-link-groups.js.map +0 -1
  108. package/dist-server/service/reference/reference-links.js.map +0 -1
  109. package/dist-server/service/reference/reference-live.js.map +0 -1
  110. package/dist-server/service/reference/reference-master.js.map +0 -1
  111. package/dist-server/service/reference/reference-probe.js.map +0 -1
  112. package/dist-server/service/reference/reference-progress-channel.js.map +0 -1
  113. package/dist-server/service/reference/reference-progress-subscription.js.map +0 -1
  114. package/dist-server/service/reference/reference-progress.js.map +0 -1
  115. package/dist-server/service/reference/reference-reconciliation-query.js.map +0 -1
  116. package/dist-server/service/reference/reference-reconciliation-writer.js.map +0 -1
  117. package/dist-server/service/reference/reference-reconciliation.js.map +0 -1
  118. package/dist-server/service/reference/reference-registry.js.map +0 -1
  119. package/dist-server/service/reference/reference-resolver.js.map +0 -1
  120. package/dist-server/service/reference/template-registry.js.map +0 -1
  121. package/dist-server/service/reference/twin-reference-reconciliation.js.map +0 -1
  122. package/dist-server/service/reference/twin-reference.js.map +0 -1
  123. package/dist-server/service/twin-attention/index.js.map +0 -1
  124. package/dist-server/service/twin-attention/twin-attention-query.js.map +0 -1
  125. package/dist-server/service/twin-audit/command-audit.js.map +0 -1
  126. package/dist-server/service/twin-audit/index.js.map +0 -1
  127. package/dist-server/service/twin-audit/twin-audit-event.js.map +0 -1
  128. package/dist-server/service/twin-audit/twin-audit-query.js.map +0 -1
  129. package/dist-server/service/twin-backfill/backfill-period-facts.js.map +0 -1
  130. package/dist-server/service/twin-backfill/backfill-shape.js.map +0 -1
  131. package/dist-server/service/twin-backfill/index.js.map +0 -1
  132. package/dist-server/service/twin-backfill/twin-backfill-resolver.js.map +0 -1
  133. package/dist-server/service/twin-control/index.js.map +0 -1
  134. package/dist-server/service/twin-control/twin-control-mutation.js.map +0 -1
  135. package/dist-server/service/twin-event/index.js.map +0 -1
  136. package/dist-server/service/twin-event/journal-count.js.map +0 -1
  137. package/dist-server/service/twin-event/twin-event-keys.js.map +0 -1
  138. package/dist-server/service/twin-event/twin-event-type.js.map +0 -1
  139. package/dist-server/service/twin-event/twin-event.js.map +0 -1
  140. package/dist-server/service/twin-forecast/forecast-metrics.js.map +0 -1
  141. package/dist-server/service/twin-forecast/forecast-tuning.js.map +0 -1
  142. package/dist-server/service/twin-forecast/gap-analytics.js.map +0 -1
  143. package/dist-server/service/twin-forecast/index.js.map +0 -1
  144. package/dist-server/service/twin-forecast/twin-forecast-query.js.map +0 -1
  145. package/dist-server/service/twin-ingest-window/index.js.map +0 -1
  146. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +0 -1
  147. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +0 -1
  148. package/dist-server/service/twin-ingest-window/twin-ingest-window.js.map +0 -1
  149. package/dist-server/service/twin-instance/index.js.map +0 -1
  150. package/dist-server/service/twin-instance/twin-instance.js.map +0 -1
  151. package/dist-server/service/twin-journal/event-type-split-shape.js.map +0 -1
  152. package/dist-server/service/twin-journal/event-type-split.js.map +0 -1
  153. package/dist-server/service/twin-journal/index.js.map +0 -1
  154. package/dist-server/service/twin-journal/journal-order.js.map +0 -1
  155. package/dist-server/service/twin-journal/twin-journal-query.js.map +0 -1
  156. package/dist-server/service/twin-lifecycle/domain-catalog.js.map +0 -1
  157. package/dist-server/service/twin-lifecycle/index.js.map +0 -1
  158. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +0 -1
  159. package/dist-server/service/twin-metrics/index.js.map +0 -1
  160. package/dist-server/service/twin-metrics/twin-metrics-query.js.map +0 -1
  161. package/dist-server/service/twin-model/axis-journal-evidence.js.map +0 -1
  162. package/dist-server/service/twin-model/choose-recipe.js.map +0 -1
  163. package/dist-server/service/twin-model/epcis-coverage.js.map +0 -1
  164. package/dist-server/service/twin-model/external-resolver.js.map +0 -1
  165. package/dist-server/service/twin-model/iec61850-coverage.js.map +0 -1
  166. package/dist-server/service/twin-model/index.js.map +0 -1
  167. package/dist-server/service/twin-model/isa95-coverage.js.map +0 -1
  168. package/dist-server/service/twin-model/item-ref.js.map +0 -1
  169. package/dist-server/service/twin-model/name-index.js.map +0 -1
  170. package/dist-server/service/twin-model/project-structure.js.map +0 -1
  171. package/dist-server/service/twin-model/standard-coverage.js.map +0 -1
  172. package/dist-server/service/twin-model/status-tally.js.map +0 -1
  173. package/dist-server/service/twin-model/twin-equipment.js.map +0 -1
  174. package/dist-server/service/twin-model/twin-lineage-query.js.map +0 -1
  175. package/dist-server/service/twin-model/twin-location.js.map +0 -1
  176. package/dist-server/service/twin-model/twin-model-item-query.js.map +0 -1
  177. package/dist-server/service/twin-model/twin-model-mutation.js.map +0 -1
  178. package/dist-server/service/twin-model/twin-model-query.js.map +0 -1
  179. package/dist-server/service/twin-model/twin-model-tree-query.js.map +0 -1
  180. package/dist-server/service/twin-model/twin-operation.js.map +0 -1
  181. package/dist-server/service/twin-readiness/index.js.map +0 -1
  182. package/dist-server/service/twin-readiness/twin-readiness-query.js.map +0 -1
  183. package/dist-server/service/twin-space/area-resolver.js.map +0 -1
  184. package/dist-server/service/twin-space/index.js.map +0 -1
  185. package/dist-server/service/twin-space/move-space.js.map +0 -1
  186. package/dist-server/service/twin-space/representation-role.js.map +0 -1
  187. package/dist-server/service/twin-space/space-integrity.js.map +0 -1
  188. package/dist-server/service/twin-space/twin-area.js.map +0 -1
  189. package/dist-server/service/twin-space/twin-space-area.js.map +0 -1
  190. package/dist-server/service/twin-space/twin-space-representation.js.map +0 -1
  191. package/dist-server/service/twin-space/twin-space-resolver.js.map +0 -1
  192. package/dist-server/service/twin-space/twin-space.js.map +0 -1
  193. package/dist-server/service/twin-state/index.js.map +0 -1
  194. package/dist-server/service/twin-state/twin-state-subscription.js.map +0 -1
  195. package/dist-server/service/twin-structure/index.js.map +0 -1
  196. package/dist-server/service/twin-structure/twin-structure.js.map +0 -1
  197. package/dist-server/service/twin-subject/event-subjects.js.map +0 -1
  198. package/dist-server/service/twin-subject/index.js.map +0 -1
  199. package/dist-server/service/twin-subject/item-resolver.js.map +0 -1
  200. package/dist-server/service/twin-subject/subject-query-shape.js.map +0 -1
  201. package/dist-server/service/twin-subject/twin-subject-backfill.js.map +0 -1
  202. package/dist-server/service/twin-subject/twin-subject-event.js.map +0 -1
  203. package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +0 -1
  204. package/dist-server/service/twin-subject/twin-subject-query.js.map +0 -1
  205. package/dist-server/service/twin-subject/twin-subject-writer.js.map +0 -1
  206. package/dist-server/service/twin-target/index.js.map +0 -1
  207. package/dist-server/service/twin-target/twin-target-resolver.js.map +0 -1
  208. package/dist-server/service/twin-target/twin-target.js.map +0 -1
  209. package/dist-shared/axis-read.js.map +0 -1
  210. package/dist-shared/entity-delta.js.map +0 -1
  211. package/dist-shared/kpi-broadcast.js.map +0 -1
  212. package/dist-shared/touched-items.js.map +0 -1
  213. package/dist-shared/twin-level.js.map +0 -1
  214. package/server/engine/attention-digest.ts +0 -102
  215. package/server/engine/canonical-ingest.ts +0 -511
  216. package/server/engine/checkpoint-fold.ts +0 -12
  217. package/server/engine/command-routing.ts +0 -67
  218. package/server/engine/declared-stimulus.ts +0 -66
  219. package/server/engine/energy-topology.ts +0 -189
  220. package/server/engine/event-time.ts +0 -96
  221. package/server/engine/index.ts +0 -51
  222. package/server/engine/ingest-dedupe.ts +0 -24
  223. package/server/engine/ingest-health.ts +0 -24
  224. package/server/engine/integration-coverage.ts +0 -147
  225. package/server/engine/integration-probes.ts +0 -144
  226. package/server/engine/integration-runner.ts +0 -95
  227. package/server/engine/integration-target-profile.ts +0 -103
  228. package/server/engine/kpi-baseline.ts +0 -202
  229. package/server/engine/kpi-fold.ts +0 -1516
  230. package/server/engine/kpi-query.ts +0 -1210
  231. package/server/engine/kpi-target.ts +0 -248
  232. package/server/engine/live-attentions.ts +0 -30
  233. package/server/engine/live-feed-registry.ts +0 -59
  234. package/server/engine/load-meter.ts +0 -393
  235. package/server/engine/local-declarations.ts +0 -779
  236. package/server/engine/log.ts +0 -72
  237. package/server/engine/loop-lag.ts +0 -120
  238. package/server/engine/measured-estimator.ts +0 -91
  239. package/server/engine/measured-yield.ts +0 -89
  240. package/server/engine/model-basis.ts +0 -162
  241. package/server/engine/model-gap.ts +0 -168
  242. package/server/engine/model-refusal.ts +0 -31
  243. package/server/engine/model-vocabulary.ts +0 -82
  244. package/server/engine/oee-accumulator.ts +0 -195
  245. package/server/engine/operation-basis.ts +0 -131
  246. package/server/engine/producer-guard.ts +0 -64
  247. package/server/engine/property-effects.ts +0 -216
  248. package/server/engine/read-time.ts +0 -77
  249. package/server/engine/restart-policy.ts +0 -55
  250. package/server/engine/runtime-key.ts +0 -58
  251. package/server/engine/spec-coverage.ts +0 -105
  252. package/server/engine/stage-path.ts +0 -172
  253. package/server/engine/state-axes.ts +0 -60
  254. package/server/engine/structure-diff.ts +0 -88
  255. package/server/engine/travel-estimator.ts +0 -138
  256. package/server/engine/twin-engine.ts +0 -4979
  257. package/server/engine/twin-state-channel.ts +0 -39
  258. package/server/engine/warm-start.ts +0 -265
  259. package/server/index.ts +0 -90
  260. package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +0 -43
  261. package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +0 -54
  262. package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +0 -53
  263. package/server/migrations/1786300000000-IndexEventTypeByTime.ts +0 -48
  264. package/server/migrations/1786400000000-IndexEventCreatedAt.ts +0 -38
  265. package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +0 -93
  266. package/server/migrations/1786600000000-RenameOperatoMesAdapterType.ts +0 -66
  267. package/server/migrations/1786700000000-AddDispatchRetryColumns.ts +0 -82
  268. package/server/migrations/1786800000000-CreateTwinReferenceReconciliations.ts +0 -39
  269. package/server/migrations/index.ts +0 -25
  270. package/server/routes.ts +0 -75
  271. package/server/service/actuation/actuation-advisor.ts +0 -131
  272. package/server/service/actuation/actuation-outcome.ts +0 -97
  273. package/server/service/actuation/actuation-rule-resolver.ts +0 -147
  274. package/server/service/actuation/actuation-rule.ts +0 -139
  275. package/server/service/actuation/approval-gateway.ts +0 -44
  276. package/server/service/actuation/catch-up-effects.ts +0 -152
  277. package/server/service/actuation/command-dispatcher.ts +0 -474
  278. package/server/service/actuation/command-store.ts +0 -236
  279. package/server/service/actuation/dispatch-after-approval.ts +0 -71
  280. package/server/service/actuation/effect-match.ts +0 -124
  281. package/server/service/actuation/index.ts +0 -27
  282. package/server/service/actuation/retry-plan.ts +0 -145
  283. package/server/service/actuation/rule-evaluate.ts +0 -352
  284. package/server/service/actuation/rule-loop.ts +0 -160
  285. package/server/service/actuation/rule-runner.ts +0 -216
  286. package/server/service/actuation/rule-wiring.ts +0 -84
  287. package/server/service/actuation/settle-effects.ts +0 -115
  288. package/server/service/actuation/sweep-dispatch.ts +0 -179
  289. package/server/service/actuation/twin-command-resolver.ts +0 -387
  290. package/server/service/actuation/twin-command.ts +0 -232
  291. package/server/service/actuation/twin-now.ts +0 -97
  292. package/server/service/index.ts +0 -100
  293. package/server/service/reference/actuation-routing.ts +0 -192
  294. package/server/service/reference/actuation-target.ts +0 -163
  295. package/server/service/reference/actuation-verification.ts +0 -85
  296. package/server/service/reference/attention-lane.ts +0 -21
  297. package/server/service/reference/connection-portability-resolver.ts +0 -171
  298. package/server/service/reference/connection-portability.ts +0 -329
  299. package/server/service/reference/control-routing.ts +0 -62
  300. package/server/service/reference/discovery-result.ts +0 -95
  301. package/server/service/reference/fill-cadence.ts +0 -29
  302. package/server/service/reference/fill-dedupe.ts +0 -66
  303. package/server/service/reference/fill-loop.ts +0 -153
  304. package/server/service/reference/fill-span.ts +0 -31
  305. package/server/service/reference/hook-contract.ts +0 -364
  306. package/server/service/reference/hook-rejected.ts +0 -53
  307. package/server/service/reference/hook-store.ts +0 -28
  308. package/server/service/reference/index.ts +0 -18
  309. package/server/service/reference/ingest-space.ts +0 -104
  310. package/server/service/reference/knob-defaults.ts +0 -59
  311. package/server/service/reference/live-cadence.ts +0 -129
  312. package/server/service/reference/live-feed-lease.ts +0 -56
  313. package/server/service/reference/live-ingest.ts +0 -94
  314. package/server/service/reference/reference-adapter.ts +0 -985
  315. package/server/service/reference/reference-assessment.ts +0 -250
  316. package/server/service/reference/reference-fill-run.ts +0 -179
  317. package/server/service/reference/reference-fill.ts +0 -223
  318. package/server/service/reference/reference-hook.ts +0 -479
  319. package/server/service/reference/reference-link-groups.ts +0 -21
  320. package/server/service/reference/reference-links.ts +0 -77
  321. package/server/service/reference/reference-live.ts +0 -420
  322. package/server/service/reference/reference-master.ts +0 -999
  323. package/server/service/reference/reference-probe.ts +0 -264
  324. package/server/service/reference/reference-progress-channel.ts +0 -26
  325. package/server/service/reference/reference-progress-subscription.ts +0 -78
  326. package/server/service/reference/reference-progress.ts +0 -95
  327. package/server/service/reference/reference-reconciliation-query.ts +0 -28
  328. package/server/service/reference/reference-reconciliation-writer.ts +0 -38
  329. package/server/service/reference/reference-reconciliation.ts +0 -55
  330. package/server/service/reference/reference-registry.ts +0 -17
  331. package/server/service/reference/reference-resolver.ts +0 -969
  332. package/server/service/reference/template-registry.ts +0 -85
  333. package/server/service/reference/twin-reference-reconciliation.ts +0 -27
  334. package/server/service/reference/twin-reference.ts +0 -190
  335. package/server/service/twin-attention/index.ts +0 -3
  336. package/server/service/twin-attention/twin-attention-query.ts +0 -70
  337. package/server/service/twin-audit/command-audit.ts +0 -114
  338. package/server/service/twin-audit/index.ts +0 -5
  339. package/server/service/twin-audit/twin-audit-event.ts +0 -158
  340. package/server/service/twin-audit/twin-audit-query.ts +0 -72
  341. package/server/service/twin-backfill/backfill-period-facts.ts +0 -168
  342. package/server/service/twin-backfill/backfill-shape.ts +0 -86
  343. package/server/service/twin-backfill/index.ts +0 -3
  344. package/server/service/twin-backfill/twin-backfill-resolver.ts +0 -35
  345. package/server/service/twin-control/index.ts +0 -3
  346. package/server/service/twin-control/twin-control-mutation.ts +0 -150
  347. package/server/service/twin-event/index.ts +0 -3
  348. package/server/service/twin-event/journal-count.ts +0 -53
  349. package/server/service/twin-event/twin-event-keys.ts +0 -215
  350. package/server/service/twin-event/twin-event-type.ts +0 -68
  351. package/server/service/twin-event/twin-event.ts +0 -250
  352. package/server/service/twin-forecast/forecast-metrics.ts +0 -106
  353. package/server/service/twin-forecast/forecast-tuning.ts +0 -40
  354. package/server/service/twin-forecast/gap-analytics.ts +0 -139
  355. package/server/service/twin-forecast/index.ts +0 -3
  356. package/server/service/twin-forecast/twin-forecast-query.ts +0 -486
  357. package/server/service/twin-ingest-window/index.ts +0 -7
  358. package/server/service/twin-ingest-window/twin-ingest-window-query.ts +0 -125
  359. package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +0 -57
  360. package/server/service/twin-ingest-window/twin-ingest-window.ts +0 -113
  361. package/server/service/twin-instance/index.ts +0 -3
  362. package/server/service/twin-instance/twin-instance.ts +0 -199
  363. package/server/service/twin-journal/event-type-split-shape.ts +0 -112
  364. package/server/service/twin-journal/event-type-split.ts +0 -144
  365. package/server/service/twin-journal/index.ts +0 -3
  366. package/server/service/twin-journal/journal-order.ts +0 -20
  367. package/server/service/twin-journal/twin-journal-query.ts +0 -615
  368. package/server/service/twin-lifecycle/domain-catalog.ts +0 -51
  369. package/server/service/twin-lifecycle/index.ts +0 -3
  370. package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +0 -285
  371. package/server/service/twin-metrics/index.ts +0 -3
  372. package/server/service/twin-metrics/twin-metrics-query.ts +0 -172
  373. package/server/service/twin-model/axis-journal-evidence.ts +0 -57
  374. package/server/service/twin-model/choose-recipe.ts +0 -31
  375. package/server/service/twin-model/epcis-coverage.ts +0 -251
  376. package/server/service/twin-model/external-resolver.ts +0 -173
  377. package/server/service/twin-model/iec61850-coverage.ts +0 -111
  378. package/server/service/twin-model/index.ts +0 -25
  379. package/server/service/twin-model/isa95-coverage.ts +0 -320
  380. package/server/service/twin-model/item-ref.ts +0 -80
  381. package/server/service/twin-model/name-index.ts +0 -94
  382. package/server/service/twin-model/project-structure.ts +0 -213
  383. package/server/service/twin-model/standard-coverage.ts +0 -35
  384. package/server/service/twin-model/status-tally.ts +0 -37
  385. package/server/service/twin-model/twin-equipment.ts +0 -191
  386. package/server/service/twin-model/twin-lineage-query.ts +0 -327
  387. package/server/service/twin-model/twin-location.ts +0 -147
  388. package/server/service/twin-model/twin-model-item-query.ts +0 -1029
  389. package/server/service/twin-model/twin-model-mutation.ts +0 -503
  390. package/server/service/twin-model/twin-model-query.ts +0 -792
  391. package/server/service/twin-model/twin-model-tree-query.ts +0 -211
  392. package/server/service/twin-model/twin-operation.ts +0 -157
  393. package/server/service/twin-readiness/index.ts +0 -3
  394. package/server/service/twin-readiness/twin-readiness-query.ts +0 -96
  395. package/server/service/twin-space/area-resolver.ts +0 -75
  396. package/server/service/twin-space/index.ts +0 -8
  397. package/server/service/twin-space/move-space.ts +0 -262
  398. package/server/service/twin-space/representation-role.ts +0 -15
  399. package/server/service/twin-space/space-integrity.ts +0 -293
  400. package/server/service/twin-space/twin-area.ts +0 -63
  401. package/server/service/twin-space/twin-space-area.ts +0 -66
  402. package/server/service/twin-space/twin-space-representation.ts +0 -99
  403. package/server/service/twin-space/twin-space-resolver.ts +0 -582
  404. package/server/service/twin-space/twin-space.ts +0 -148
  405. package/server/service/twin-state/index.ts +0 -3
  406. package/server/service/twin-state/twin-state-subscription.ts +0 -62
  407. package/server/service/twin-structure/index.ts +0 -3
  408. package/server/service/twin-structure/twin-structure.ts +0 -96
  409. package/server/service/twin-subject/event-subjects.ts +0 -162
  410. package/server/service/twin-subject/index.ts +0 -10
  411. package/server/service/twin-subject/item-resolver.ts +0 -92
  412. package/server/service/twin-subject/subject-query-shape.ts +0 -84
  413. package/server/service/twin-subject/twin-subject-backfill.ts +0 -196
  414. package/server/service/twin-subject/twin-subject-event.ts +0 -101
  415. package/server/service/twin-subject/twin-subject-fast-path.ts +0 -346
  416. package/server/service/twin-subject/twin-subject-query.ts +0 -210
  417. package/server/service/twin-subject/twin-subject-writer.ts +0 -189
  418. package/server/service/twin-target/index.ts +0 -5
  419. package/server/service/twin-target/twin-target-resolver.ts +0 -142
  420. package/server/service/twin-target/twin-target.ts +0 -97
  421. package/server/types/global.d.ts +0 -22
  422. package/shared/axis-read.ts +0 -28
  423. package/shared/entity-delta.ts +0 -335
  424. package/shared/kpi-broadcast.ts +0 -13
  425. package/shared/touched-items.ts +0 -73
  426. package/shared/twin-level.ts +0 -48
  427. package/test/ack-shape-alignment.test.ts +0 -97
  428. package/test/actuation-advisor.test.ts +0 -55
  429. package/test/actuation-approval-door.test.ts +0 -411
  430. package/test/actuation-dispatch.test.ts +0 -209
  431. package/test/actuation-effect.test.ts +0 -162
  432. package/test/actuation-outcome.test.ts +0 -152
  433. package/test/actuation-rule-runner.test.ts +0 -281
  434. package/test/actuation-rule.test.ts +0 -266
  435. package/test/actuation-seam.test.ts +0 -255
  436. package/test/actuation-settle.test.ts +0 -164
  437. package/test/actuation-target.test.ts +0 -145
  438. package/test/actuation-verification-state.test.ts +0 -139
  439. package/test/adopt-structure-live.test.ts +0 -133
  440. package/test/aggregate-not-findone.test.ts +0 -112
  441. package/test/area-resolver-db.test.ts +0 -110
  442. package/test/attention-digest.test.ts +0 -161
  443. package/test/axis-declaration-passthrough-db.test.ts +0 -152
  444. package/test/axis-journal-evidence.test.ts +0 -71
  445. package/test/axis-read.test.ts +0 -188
  446. package/test/backfill-shape.test.ts +0 -89
  447. package/test/boot-resume.test.ts +0 -287
  448. package/test/broadcast-cost-baseline.test.ts +0 -201
  449. package/test/broadcast-period.test.ts +0 -58
  450. package/test/broadcast-queue-overflow.test.ts +0 -244
  451. package/test/canonical-ingest-vocabularies.test.ts +0 -451
  452. package/test/canonical-quantity-door.test.ts +0 -61
  453. package/test/capability-mapping.test.ts +0 -89
  454. package/test/checkpoint-refuses-empty.test.ts +0 -103
  455. package/test/choose-recipe.test.ts +0 -117
  456. package/test/column-type-portability.test.ts +0 -122
  457. package/test/command-routing.test.ts +0 -61
  458. package/test/command-store-writes-columns.test.ts +0 -126
  459. package/test/connection-portability-doors.test.ts +0 -87
  460. package/test/connector-capability-declaration.test.ts +0 -195
  461. package/test/contract-layer-guard.test.ts +0 -48
  462. package/test/control-capability.test.ts +0 -98
  463. package/test/cursor-stall-not-read-failure.test.ts +0 -126
  464. package/test/declaration-reaches-model.test.ts +0 -226
  465. package/test/declared-location-types.test.ts +0 -89
  466. package/test/declared-stimulus.test.ts +0 -88
  467. package/test/dedupe-transformation.test.ts +0 -112
  468. package/test/discovery-result.test.ts +0 -75
  469. package/test/dispatch-after-approval-source.test.ts +0 -81
  470. package/test/duration-estimators.test.ts +0 -144
  471. package/test/energy-topology.test.ts +0 -113
  472. package/test/entity-delta.test.ts +0 -352
  473. package/test/equipment-identity-reaches.test.ts +0 -57
  474. package/test/event-subjects.test.ts +0 -148
  475. package/test/event-time-column.test.ts +0 -67
  476. package/test/event-type-split.test.ts +0 -178
  477. package/test/external-resolver.test.ts +0 -177
  478. package/test/fact-scope-wiring.test.ts +0 -96
  479. package/test/fill-dedupe.test.ts +0 -59
  480. package/test/fill-loop.test.ts +0 -35
  481. package/test/first-envelope.test.ts +0 -153
  482. package/test/fold-gap-speaks.test.ts +0 -53
  483. package/test/fold-shift-dst.test.ts +0 -110
  484. package/test/forecast-metrics.test.ts +0 -157
  485. package/test/forecast-tuning.test.ts +0 -42
  486. package/test/gap-analytics.test.ts +0 -113
  487. package/test/generated-not-counted.test.ts +0 -99
  488. package/test/guard-roots.ts +0 -76
  489. package/test/home-in-area.test.ts +0 -90
  490. package/test/hook-address.test.ts +0 -28
  491. package/test/hook-rejected-shape.test.ts +0 -86
  492. package/test/hook-response-sum.test.ts +0 -193
  493. package/test/hook-sequence.test.ts +0 -333
  494. package/test/ingest-bench.test.ts +0 -70
  495. package/test/ingest-dedupe.test.ts +0 -56
  496. package/test/ingest-expected-quiet.test.ts +0 -111
  497. package/test/ingest-health-engine.test.ts +0 -248
  498. package/test/ingest-health-wiring.test.ts +0 -120
  499. package/test/ingest-health.test.ts +0 -306
  500. package/test/ingest-history.test.ts +0 -247
  501. package/test/ingest-idempotent.test.ts +0 -211
  502. package/test/ingest-reconcile-four-ways.test.ts +0 -97
  503. package/test/ingest-rules-header.test.ts +0 -100
  504. package/test/ingest-running-guard.test.ts +0 -136
  505. package/test/ingest-space.test.ts +0 -78
  506. package/test/ingest-window-rolls-unobserved.test.ts +0 -72
  507. package/test/ingest-wiring-guard.test.ts +0 -197
  508. package/test/instance-cache-lifecycle.test.ts +0 -110
  509. package/test/intake-mapping-parity.test.ts +0 -175
  510. package/test/integration-probes.test.ts +0 -103
  511. package/test/integration-runner.test.ts +0 -95
  512. package/test/item-ref.test.ts +0 -78
  513. package/test/journal-order-axis.test.ts +0 -80
  514. package/test/journal-read-discipline.test.ts +0 -186
  515. package/test/journal-retention.test.ts +0 -275
  516. package/test/journal-sort-axis.test.ts +0 -142
  517. package/test/journal-write-door.test.ts +0 -110
  518. package/test/journal-write-trend.test.ts +0 -115
  519. package/test/kernel-kind-guard.test.ts +0 -98
  520. package/test/knob-defaults.test.ts +0 -72
  521. package/test/kpi-baseline-db.test.ts +0 -216
  522. package/test/kpi-baseline.test.ts +0 -196
  523. package/test/kpi-fold.test.ts +0 -1456
  524. package/test/kpi-query-bench.test.ts +0 -130
  525. package/test/lens-own-places.test.ts +0 -50
  526. package/test/lineage-survives-restart.test.ts +0 -234
  527. package/test/live-cadence.test.ts +0 -170
  528. package/test/live-cursor-wiring.test.ts +0 -87
  529. package/test/live-feed-lease.test.ts +0 -63
  530. package/test/live-feed-registry.test.ts +0 -75
  531. package/test/live-kernel-facts.test.ts +0 -161
  532. package/test/live-mirror-parity.test.ts +0 -286
  533. package/test/load-meter.test.ts +0 -328
  534. package/test/local-declarations.test.ts +0 -662
  535. package/test/log-stamp.test.ts +0 -59
  536. package/test/loop-lag.test.ts +0 -82
  537. package/test/master-to-twin.test.ts +0 -279
  538. package/test/measured-yield.test.ts +0 -90
  539. package/test/merge-streams.test.ts +0 -70
  540. package/test/mirror-resumes-from-checkpoint.test.ts +0 -192
  541. package/test/model-basis.test.ts +0 -141
  542. package/test/model-gap.test.ts +0 -153
  543. package/test/model-refusal.test.ts +0 -75
  544. package/test/model-vocabulary.test.ts +0 -114
  545. package/test/move-space.test.ts +0 -149
  546. package/test/mutation-gate.test.ts +0 -122
  547. package/test/oee-accumulator.test.ts +0 -204
  548. package/test/oee-site-calendar.test.ts +0 -146
  549. package/test/open-doors.test.ts +0 -197
  550. package/test/operation-basis.test.ts +0 -127
  551. package/test/operational-vocabulary.test.ts +0 -108
  552. package/test/operations-capability-db.test.ts +0 -165
  553. package/test/order-and-transaction-split.test.ts +0 -107
  554. package/test/order-fold-host.test.ts +0 -112
  555. package/test/producer-guard.test.ts +0 -66
  556. package/test/project-structure-db.test.ts +0 -226
  557. package/test/projection-reaches-screen.test.ts +0 -277
  558. package/test/projection-reads-declared.test.ts +0 -109
  559. package/test/property-effects.test.ts +0 -128
  560. package/test/provenance-promotion.test.ts +0 -99
  561. package/test/read-failure-ledger.test.ts +0 -106
  562. package/test/read-failure-visible.test.ts +0 -145
  563. package/test/read-time.test.ts +0 -118
  564. package/test/reference-fill.test.ts +0 -235
  565. package/test/reference-grounding.test.ts +0 -70
  566. package/test/reference-hook.test.ts +0 -177
  567. package/test/reference-links.test.ts +0 -56
  568. package/test/reference-reconciliation.test.ts +0 -39
  569. package/test/registry-key-guard.test.ts +0 -80
  570. package/test/rejection-carries-event-id.test.ts +0 -101
  571. package/test/rename-twin.test.ts +0 -89
  572. package/test/resolve-ts-siblings.mjs +0 -52
  573. package/test/restart-policy.test.ts +0 -111
  574. package/test/resync-origin-site.test.ts +0 -83
  575. package/test/retry-plan.test.ts +0 -139
  576. package/test/revision-axis.test.ts +0 -110
  577. package/test/runtime-key.test.ts +0 -66
  578. package/test/save-intake-collision.test.ts +0 -91
  579. package/test/scale-twin-bench.test.ts +0 -103
  580. package/test/secrets-at-rest.test.ts +0 -135
  581. package/test/snapshot-freshness.test.ts +0 -60
  582. package/test/source-outcome-audit.test.ts +0 -104
  583. package/test/space-integrity.test.ts +0 -223
  584. package/test/space-representation.test.ts +0 -66
  585. package/test/space-scope-includes-stopped.test.ts +0 -55
  586. package/test/spec-coverage.test.ts +0 -113
  587. package/test/stage-path.test.ts +0 -95
  588. package/test/standard-coverage.test.ts +0 -148
  589. package/test/state-axes.test.ts +0 -74
  590. package/test/status-tally.test.ts +0 -55
  591. package/test/streamline-e2e.test.ts +0 -76
  592. package/test/structure-revision-db.test.ts +0 -315
  593. package/test/subject-fast-path.test.ts +0 -148
  594. package/test/subject-read-model.test.ts +0 -175
  595. package/test/sweep-dispatch.test.ts +0 -191
  596. package/test/tenant-registry-db.test.ts +0 -149
  597. package/test/time-range.test.ts +0 -152
  598. package/test/touched-items.test.ts +0 -61
  599. package/test/twin-audit.test.ts +0 -83
  600. package/test/twin-event-keys.test.ts +0 -193
  601. package/test/twin-model-item-db.test.ts +0 -319
  602. package/test/twin-model-parent-relation-db.test.ts +0 -125
  603. package/test/twin-model-tree-db.test.ts +0 -209
  604. package/test/twin-origin-resync.test.ts +0 -133
  605. package/test/twin-state-channel.test.ts +0 -111
  606. package/test/vocabulary-guard.test.ts +0 -43
  607. package/test/warm-start-seam.test.ts +0 -162
  608. package/test/warm-start.test.ts +0 -297
  609. package/test/withheld-door.test.ts +0 -95
  610. package/test/yield-loop.test.ts +0 -199
  611. package/tsconfig.json +0 -15
  612. package/tsconfig.shared.json +0 -23
  613. package/tsconfig.shared.tsbuildinfo +0 -1
  614. package/tsconfig.tsbuildinfo +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"kpi-fold.js","sourceRoot":"","sources":["../../server/engine/kpi-fold.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 업무 KPI 폴드 — **이벤트 저널을 계산해** 시간창 지표를 만든다(순수).\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 지금 트윈이 답할 수 있는 것은 \"이 순간의 값\"(점유 92%)과 \"인프라 부하\"(초당 이벤트 수)뿐이다.\n * 운영이 묻는 것은 다르다 — **지난 한 시간에 몇 건 처리했나, 한 건에 얼마나 걸리나, 자원은 얼마나\n * 일했나.** 그 값이 없으면 AI 는 \"점유 92%\" 는 말해도 \"목표보다 나쁜가\" 는 답할 수 없다.\n *\n * ── 원료는 이미 있다 ────────────────────────────────────────────────────────\n * 저널(`TwinEvent`)에 `task.status`·`order.status`·`equipment.status` 전이가 `eventTime`(ISO)과 함께\n * append-only 로 쌓인다. 그래서 새 계측을 심을 필요가 없다 — **계산만 하면 된다.**\n * 같은 모양의 선례가 이 폴더에 있다(`oee-accumulator.ts` — equipment 전이를 계산해 OEE 카운터 생산).\n *\n * ── 이 파일이 하지 않는 것 ──────────────────────────────────────────────────\n * · 목표와 비교하지 않는다. \"95% 목표 대비\" 는 목표를 저장하는 별개 층(경영 KPI)의 몫이고,\n * 이 층은 **사실만** 생산한다. 두 층을 섞으면 목표를 정의하지 않은 현장은 사실조차 못 본다.\n * · 체류시간(dwell)은 아직 내지 않는다. 물건이 노드에 머문 시간은 item 단위 이벤트(epcis.*)를\n * 짝지어야 하고, 그 짝맞춤 규칙이 도메인마다 다르다. **없는 것을 0 으로 꾸미지 않는다.**\n * · DB·커널을 모른다. 입력은 평범한 배열이고 출력은 평범한 객체다(테스트가 값싸다).\n */\n\n/*\n * 짝맞춤은 **커널이 소유한다**(`foldTaskRecords`). 완료는 작업당 하나·나중 것이 사실, 착수는 처음 것,\n * 종류·오더는 어느 전이에서든 — 그 규칙은 커널 상태 기계에서 나오는 지식이다. 여기서 다시 적었더니\n * 공정 타임라인과 **다른 답**을 냈다(재전송된 완료를 두 건으로 세어 처리량·점유가 부풀려졌다).\n * 이 파일은 이제 \"계산한 기록 → 창 지표\" 만 한다.\n */\nimport { attributeEnergy, electricityCost, energyIntensity, energyOfWindows, foldTaskRecords, type ElectricityCost, type TariffDeclaration, type TaskFacets } from '@operato/twin-kernel'\nimport { type WorkCalendarEntry } from '@operato/ops-contract'\nimport { ENERGY_EVENT, isFlowOrder, isOrderTerminal, activeShiftAt } from '@operato/ops-contract'\n\n/** 저널 한 줄 — 필요한 것만(엔티티·typeorm 비의존). */\nexport interface KpiEvent {\n eventType?: string\n /** ISO 시각. 파싱 불가하면 그 줄은 버린다(알리지 않고 0 으로 세지 않는다). */\n eventTime?: string\n payload?: any\n}\n\nexport interface KpiWindow {\n /** 창의 시작·끝(epoch ms). 완료 시각이 이 안에 든 것만 성과로 센다. */\n fromMs: number\n toMs: number\n}\n\nexport interface DurationStats {\n /** 짝이 맞아 계산된 건수. */\n count: number\n /**\n * 하위 꼬리 — **분포를 양쪽에서 계산하지하기 위해** 낸다.\n * p50·p90 만 있으면 아래쪽 퍼짐을 위쪽에서 거울처럼 베껴 쓸 수밖에 없다(발명). 관측이 말하게 한다.\n */\n p10Ms: number\n p50Ms: number\n p90Ms: number\n avgMs: number\n}\n\n/**\n * 관점 축 — **같은 사실을 다른 각으로** 본다.\n *\n * 고정된 한 렌즈(자원별)만 낼 수 있으면 사용자가 \"구역별로는?\" 이라고 물어도 답이 없다. 사실 계산이\n * 축을 못 내면 AI 도 못 낸다 — 그래서 축은 계산 층의 능력이어야 한다.\n *\n * resource 자원별(누가 했나) taskKind 작업 종류별(무엇을 했나)\n * location 도착 지점별(어디서) order 오더별(무엇을 위해)\n * area 구역별 — 노드→구역 지도가 필요하다(이벤트에 없다. 호출부가 준다)\n * shift 교대별(언제) — 완료 시각 + 현장의 시각 기준에서 **계산한다**(이벤트에 없다)\n */\nexport type KpiGroupBy = 'resource' | 'taskKind' | 'location' | 'order' | 'area' | 'shift'\n\nexport interface KpiGroup {\n /** 축의 값(자원 id·작업 종류·노드 id·오더 id·구역 id). 값이 없던 기록은 'unknown'. */\n key: string\n /** 이 축에서 완료된 건수. */\n tasks: number\n leadTime: DurationStats\n workTime: DurationStats\n waitTime: DurationStats\n /** 이 축에 붙어 있던 시간의 합(자원 축에서 특히 의미 있다). */\n busyMs: number\n /**\n * **품질 판정이 있었던 건수만** 센다 (2026-08-19).\n *\n * 판정이 없는 작업(이동·체류·중간 스테이션)은 어느 쪽에도 세지 않는다 — 모름을 양품으로 세면 양품률이\n * 알리지 않고 100% 로 올라간다. 그래서 `good + scrap` 은 `tasks` 보다 작을 수 있고, 그것이 정상이다.\n *\n * 판정이 하나도 없으면 **필드를 만들지 않는다**(0/0 을 「양품률 0%」로 읽히게 두지 않는다).\n */\n quality?: { good: number; scrap: number }\n}\n\nexport interface KpiResult {\n window: { fromMs: number; toMs: number }\n /** 창 안에 **완료된** 건수 — 성과는 완료 시점으로 센다(시작 시점으로 세면 진행 중인 것이 섞인다). */\n throughput: { tasks: number; orders: number }\n /**\n * 에너지 — **이 창의 전력량과 원단위.** 에너지 트윈이 없으면 `undefined`(0 이 아니다).\n *\n * ── 왜 KPI 폴드 안에 있나 (2026-08-14, §10 7단계) ─────────────────────────\n * 원단위는 분자(전력량)와 분모(산출)를 **같은 창**에서 나눈 값이다. 질의를 따로 두면 두 창을 맞추는\n * 일이 소비처의 몫이 되고, 어긋난 두 사실을 나눈 숫자는 아무것도 뜻하지 않는다(커널의 규칙이 그것을\n * 거절한다). 같은 폴드 안에 두면 창이 하나뿐이라 어긋날 수 없다.\n *\n * 분자는 **저널의 구간 마감 사건**에서 온다(`energy.demand.window`) — 커널 상태가 아니라 저널이므로\n * 멈춘 트윈·과거 구간에도 답할 수 있다.\n */\n energy?: {\n /** 이 창의 전력량. 셀 수 있는 구간이 없으면 `undefined`. */\n kWh?: number\n /**\n * 이 창의 **최대수요**(kW)와 그것이 난 구간의 시작.\n *\n * 전력량과 다른 질문이다: 전력량은 요금의 사용량 부분이고, 최대수요는 **기본요금**과 계약 위반을\n * 정한다. 평균으로 갈음할 수 없어서 커널이 구간의 최대들 중 가장 큰 값을 따로 낸다.\n */\n peakKW?: number\n peakStartMs?: number\n /**\n * 그 피크 구간에서 **설비가 끌어당긴** 최대(발전·방전 차감 전)와 그 차이(깎인 양).\n *\n * 요금은 계통 수요(`peakKW`)로 매겨지지만, 「무엇이 그 수요를 만들었나」와 「무엇이 덜어냈나」는\n * 이 두 수의 간격이 답한다. 발전·축전을 선언하지 않은 현장에서는 없다(차감할 것이 없다).\n */\n peakGrossKW?: number\n shavedKW?: number\n /**\n * 이 창의 **발전량** — 커널이 마감한 기간 사실(`energy.generation.period`)에서 온다.\n *\n * 부하(`kWh`)와 더하지 않는다: 발전은 설비가 만든 양이고 부하는 계량 지점을 지난 양이다. 두 수를\n * 더하면 어느 쪽도 아닌 숫자가 된다.\n *\n * 성과지표(ISO 50001 EnPI)와 태양광 성능비가 이 값을 분자로 쓴다. 발전 사실이 없으면 `undefined`\n * 다(0 이 아니다 — 「발전이 없다」와 「발전 설비가 없다」는 다르다).\n */\n generated?: {\n /** 창에 온전히 든 기간들의 합(kWh). */\n kWh: number\n /** 센 기간 수. */\n counted: number\n /**\n * 그중 **관측이 기간을 다 덮지 못한** 기간 수.\n *\n * 커널이 기간마다 관측 구간을 함께 낸다. 마지막 표본이 기간 끝보다 이르면 그만큼 재지 않은\n * 것이고, 그 수를 감추면 화면이 덜 잰 합을 온전한 합으로 말한다.\n */\n partial: number\n /** 설비별 몫 — 이것은 **측정**이다(배분이 아니다). 각 설비의 적산에서 왔다. */\n byEquipment: { id: string; kWh: number }[]\n /**\n * **이 창에 다 들어오지 않아 더하지 못한 기간** — 조용히 버리지 않는다.\n *\n * 더하는 것은 창에 온전히 든 기간뿐이다(걸친 기간을 더하면 다른 날 값이 이 창의 값인 척한다).\n * 그런데 하루 단위 기간은 「지금부터 24시간 전」 창에 온전히 드는 일이 거의 없다 — 창이 한낮에서\n * 시작하기 때문이다. 그래서 어제 발전량이 저널에 멀쩡히 있는데 화면이 비었고, 사용자는 그것을\n * 「발전량이 없다」로 읽었다(2026-08-30 실측).\n *\n * 이 값이 있으면 화면은 **「이 구간에 다 들어오지 않은 기간이 있다」**를 말해야 한다.\n */\n notCounted?: { periods: number; kWh: number; earliestStart: string; latestEnd: string }\n /**\n * 이용률 — 만든 양 ÷ (정격 × 그 기간의 시간).\n *\n * **어느 정격으로 나눴는지를 함께 낸다.** 변환기를 낀 발전은 직류 정격이 교류보다 크고,\n * 직류로 나눠 보여 주는 시스템이 실제로 있다. 밝히지 않으면 두 화면의 수가 두 배 달라도\n * 왜 다른지 말할 수 없다.\n *\n * 창 전체가 아니라 **센 기간들의 길이**로 나눈다 — 하루치 하나만 센 24시간 창에서 창 길이로\n * 나누면 그 하루가 창 전체에 걸쳐 있었던 것처럼 작아진다.\n */\n capacityFactor?: { value: number; ratedKW: number; basis: 'ac' | 'dc'; from: 'equipment' | 'location'; hours: number }\n /**\n * 낸 것의 값 — 기간마다 그때의 단가로 매긴다.\n *\n * **모든 기간에 단가가 있을 때만** 금액을 만든다. 일부만 매기고 더하면 그 합이 창 전체의 값인\n * 척한다 — 못 매긴 기간 수를 함께 낸다.\n */\n revenue?: { amount: number; currency: string; priced: number; unpriced: number }\n }\n /**\n * `kWh` 가 어느 회계에서 왔나.\n *\n * 'demand-window' 우리가 표본에서 마감한 구간 (계량이 오는 현장)\n * 'usage-period' 밖에서 마감되어 온 구간 (계량 표본이 없는 현장)\n *\n * 둘을 더하지 않는다 — 같은 전기를 두 번 센다. 둘 다 있으면 계량 쪽을 쓰고 그 사실을 낸다.\n */\n kWhBasis?: 'demand-window' | 'usage-period'\n /**\n * 기본요금을 매긴 **요금적용전력과 그 근거**.\n *\n * 이 값은 매달 다시 정해진다. 선언에 박아 두면 사람이 갱신해야 하고, 안 하면 낡은 값으로\n * 기본요금이 조용히 틀린다. 그래서 **청구서가 먼저다** — 원본이 매일 실어 보낸다.\n *\n * 'bill' 이 창을 덮는 청구서가 말한 값\n * 'carried-forward' 가장 최근 청구서의 값을 이어 쓴다(요금 제도가 실제로 그렇게 군다)\n * 'declaration' 청구가 오지 않는 현장 — 모델의 선언\n *\n * `declaredKW` 와 다르면 **선언이 낡았다**(`stale`). 그 사실을 감추면 아무도 다시 읽지 않는다.\n */\n billingDemand?: {\n kW: number\n from: 'tariff-basis' | 'bill' | 'carried-forward' | 'declaration'\n declaredKW?: number\n stale?: boolean\n /**\n * 이 창에서 관측한 최고 수요가 그 기준을 넘었다 — **다음 주기의 기본요금이 오른다.**\n *\n * 계약 초과와 다른 값이다. 계약 초과는 지금 줄이면 되고, 이것은 이미 정해진 결과다.\n *\n * `observedTo` 는 이 최고값이 언제까지의 것인지다. 최고 수요는 구간이 마감되어야 오므로\n * 넘은 것을 뒤늦게 안다 — 그 한계를 화면이 말할 수 있어야 한다.\n */\n exceeded?: { peakKW: number; overKW: number; observedTo: string }\n }\n /**\n * 밖에서 마감되어 온 사용 구간 — 계량 표본이 없는 현장의 유일한 근거다.\n *\n * `basis` 별 건수를 함께 낸다: 같은 kWh 라도 계량기가 잰 것과 공급자 화면에서 긁어온 것은 무게가\n * 다르고, 실제로 긁어온 값이 망가져 있는 것을 본 적이 있다.\n */\n usage?: {\n kWh: number\n counted: number\n /** 구간마다 제 단가로 매긴 사용량 요금 — 모든 구간이 단가를 실었을 때만. */\n charge?: number\n currency?: string\n /** 단가가 없어 요금을 매기지 못한 구간 수 — 조용히 빼지 않는다. */\n unpriced: number\n byBasis: { basis: string; counted: number }[]\n }\n /**\n * 이 창에 온전히 든 **청구서** — 공급자가 확정한 금액.\n *\n * 우리 계산을 덮지 않는다. 창이 청구 주기와 정확히 같을 때만 견줄 수 있으므로 그 사실을\n * `matchesWindow` 로 함께 낸다 — 다른 창의 두 수를 빼면 그 차이는 아무것도 뜻하지 않는다.\n */\n bill?: {\n from: string\n to: string\n total?: number\n energyCharge?: number\n demandCharge?: number\n currency?: string\n billingDemandKW?: number\n matchesWindow: boolean\n /**\n * 창이 같을 때, **우리 계산에서 청구액을 뺀 값** — 0 이 아니면 우리 계산이 틀렸다는 뜻이다.\n *\n * 항목별로도 낸다(2026-08-29). 총액만 견주면 원본이 총액을 주지 않는 청구서에서는 비교가\n * 영영 나오지 않는다 — 실제로 첫 청구서에 기본요금만 있었다. 양쪽에 있는 항목만 견준다.\n */\n diff?: { total?: number; demandCharge?: number; energyCharge?: number }\n }\n /** 파생의 근거 — 구간 평균부하 × 길이(`mean-kw`). */\n basis: string\n /** 센 구간 수 · 못 센 구간 수(계측이 끊긴 구간) — 알리지 않고 자르지 않는다. */\n counted: number\n skipped: number\n /** 완료 오더당 전력량 — 답할 수 없으면 `null` 과 이유. */\n intensity: { value: number | null; unit?: string; reason?: string }\n /**\n * 배전 계통의 **자리별 몫** — 「이 분기가 얼마 썼나」.\n *\n * ── 이것은 측정이 아니라 배분이다 ──────────────────────────────────────\n * 저널에 있는 에너지 사실은 **현장 총량 한 줄**이다(뿌리 계량기 합). 계량기별 구간 사실이 없으므로\n * 자리별 몫은 **나눈 것**이고, 무엇으로 나눴는지(`weightKind`)를 값과 함께 낸다. 근거를 감추면\n * 나눈 숫자가 잰 숫자와 같은 무게로 읽힌다 — CBAM·ESG 보고에서 첫 질문에 깨지는 지점이다.\n *\n * 가중치는 **작업 가동시간**이다. 정격(kW)으로 나누지 않는다: 시뮬 트윈의 총부하가 정격의 합으로\n * 파생되므로, 그 총량을 다시 정격으로 나누면 넣은 값이 그대로 나오는 동어반복이 된다.\n *\n * 그래서 이 값에는 **가정이 하나 실려 있다**: 전기가 가동시간에 비례한다. 공조·조명처럼 공정에\n * 붙지 않는 부하도 그 비율에 섞인다 — 화면이 그 문장을 함께 말해야 한다.\n */\n byFeeder?: {\n /** 자리(분기·수전) id. 자리를 모르는 자원은 `unknown` 으로 모인다(알리지 않고 버리지 않는다). */\n key: string\n kWh: number\n /**\n * 이 몫의 근거 — **커널이 정한다.**\n *\n * 선언된 부하가 여럿이면 배분(`apportioned`)이다. 선언된 부하가 **하나뿐인** 현장에서는 커널이\n * 「그 계량기는 그것만 잰다」고 읽어 `measured` 를 낸다 — 모델이 그렇게 말했으므로 그것도 사실이다.\n * 우리가 어느 쪽으로도 낙인찍지 않는다.\n */\n basis: 'apportioned' | 'measured'\n /**\n * 무엇으로 나눴나 — **배분일 때만** 있다.\n *\n * 분기마다 계량기가 있는 현장에서는 나누지 않는다(그 값이 곧 계량이다). 그때 이 칸을\n * `runtimeMs` 로 채우면 「가동시간으로 나눴다」는 없는 사실을 말하게 된다.\n */\n weightKind?: 'runtimeMs'\n /** 이 자리의 몫이 전체의 얼마였나 — 배분일 때만. */\n weightShare?: number\n /**\n * **피크가 찍힌 순간** 이 분기가 얼마였나(kW) — 요금이 걸린 수다.\n *\n * 창의 평균이나 합이 아니다: 최대수요를 낸 구간의 그 순간 값이다(다른 순간을 섞으면 「어느 분기를\n * 깎아야 피크가 내려가나」의 답이 흐려진다). 그 구간에 이 분기의 표본이 없었으면 **없다**.\n */\n peakKW?: number\n }[]\n /**\n * 나누지 못한 양과 그 이유 — **알리지 않고 0 으로 만들지 않는다.**\n * `no-weights` = 그 창에 돌아간 자원이 없다(가동시간이 전부 0). 그러면 총량은 그대로 남는다.\n */\n unattributedKWh?: number\n /**\n * 왜 나누지 못했나(커널 코드) — `no-weights`(가동시간이 전부 0) · `no-consumers`(덮는 대상 미선언) ·\n * `not-submetered`(뿌리에서 재었으나 하위 계량이 덮지 않는 부분 — 결손이 아니라 계량 범위의 사실).\n */\n unattributedReason?: string\n /**\n * 분기 계량의 합이 뿌리 계량보다 **큰** 양 — 두 계량기의 회계가 다르다는 사실이다.\n *\n * 값을 깎아 맞추지 않는다: 그것은 계량을 우리가 고쳐 쓰는 일이고, 어느 쪽이 맞는지는 현장이 안다.\n */\n submeterExcessKWh?: number\n /**\n * 배분에서 **빠진 자원 수** — 그 창에 가동 기록이 없는 설비다. 0kWh 를 주지 않는 대신 몇 대가\n * 빠졌는지 센다(대기전력이 있는 설비에서 0 은 곧 거짓이다).\n */\n excludedResources?: number\n /**\n * 이 창의 전기요금 — **청구서가 아니다.** 계산과 그 한계(TOU·래칫·최소청구액 미모델)는 커널\n * (`electricityCost`)이 갖고 여기서는 나른다. 단가가 없으면 `reason: 'no-tariff'` 로 온다.\n */\n cost?: ElectricityCost\n }\n /** 리드타임 — 생성(created)에서 완료까지. 사용자가 체감하는 소요 시간. */\n leadTime: DurationStats\n /** 작업시간 — 착수(in-progress)에서 완료까지. 대기를 뺀 실작업. */\n workTime: DurationStats\n /** 대기시간 — 생성에서 착수까지. 자원이 부족하면 여기가 늘어난다. */\n waitTime: DurationStats\n /** 자원별 점유 — 작업에 붙어 있던 시간의 합과 창 대비 비율(작업으로 설명되는 부분만). */\n utilization: { resourceId: string; busyMs: number; ratio: number }[]\n /**\n * **셈에서 빠진 것** — 완료는 창 안이지만 시작 이벤트를 못 찾은 건수.\n * 조회 구간이 짧아 시작이 잘렸거나 저널이 유실된 경우다. 이 값을 숨기면 평균이 알리지 않고 거짓이 된다.\n */\n unpaired: { leadTime: number; workTime: number }\n /**\n * **완료로 세지 않은 오더 상태 낱말의 인구조사** — 낱말 → 창 안에서 그 낱말로 본 건수.\n *\n * 「못 읽었다」가 **아니다.** `picking` 처럼 완료가 아닌 낱말이 여기 오는 것은 정상이다. 이 값은\n * 판정이 아니라 **재료**다: 완료가 0 인데 여기 `{ finished: 2780 }` 이 있으면 원인이 곧 보인다.\n *\n * ── 왜 필요한가, 그리고 무엇의 대체가 아닌가 ────────────────────────────────\n * 이 폴드는 완료를 **동의어 목록**으로 판정한다(`completed`·`fulfilled`·`done`·`finished`). 그 방식은\n * 원본의 낱말에서 뜻을 **추론**하는 것이고, 이 저장소가 다른 자리에서 거절하는 그것이다\n * (「선언이 있으면 변환이지만 없으면 발명이다」).\n *\n * 실제로 그 대가를 치렀다: `finished` 가 목록에 없어 첫 실 시스템의 완료 오더가 전부 0 이었다.\n * 그런데 **빈 자리는 이 목록이 아니라 커넥터였다** — 그 커넥터에 상태 표(`WORK_ORDER_STATE`)가\n * 정의돼 있으면서 오더 경로에서 쓰이지 않았고(로트 경로는 썼다), 원본 낱말이 그대로 흘렀다.\n * 목록에 `finished` 를 더한 것은 그 사실을 가린 땜빵이었다.\n *\n * **정석은 커넥터가 자기 표로 옮기고, 옮길 수 없는 낱말은 세고 넣지 않는 것이다**(재고 처분이 이미\n * 그 규율로 선다: 「표현할 길이 없는 것은 넣지 않는다」). 그러면 이 폴드는 추론하지 않아도 된다.\n *\n * 그러므로 이 값은 **동의어 목록을 늘리는 근거가 아니다.** 목록은 커넥터들이 옮기기 시작하면\n * 닫힌 어휘로 좁혀야 하고, 그때까지 이 값이 어긋남을 말해 준다. 순서가 중요하다 — 목록을 먼저\n * 좁히면 아직 옮기지 않는 원본의 완료가 그 순간 0 이 된다.\n *\n * 상태를 말하지 않은 사건은 여기 오지 않는다. 그것은 「모르는 낱말」이 아니라 다른 결함이고\n * (유입 문이 상태를 필수로 받는다) 그 진단은 그 문의 몫이다.\n */\n orderStatusNotCounted: Record<string, number>\n /**\n * 소비한 이벤트 수 — **넘겨받은 배열 전체**(창 앞 lookback 포함). 진단용이다.\n *\n * \"측정된 기록이 있는가\" 의 판단에는 쓰지 말 것 — 호출부는 창보다 앞까지 읽어 넘기므로(완료된 작업의\n * 시작이 창 밖일 수 있다) 창 안이 비어 있어도 이 값은 0 이 아니다. 그 판단은 `eventsInWindow` 로 한다.\n */\n eventsSeen: number\n /**\n * **창 안에** 들어온 이벤트 수 — \"이 구간에 측정할 기록이 있었는가\" 의 정답.\n *\n * 이것이 0 이면 \"처리량 0\" 이 아니라 **\"기록이 없다\"** 다. 두 상황을 섞으면 사용자가 잘못된 결론을\n * 낸다(앞 구간의 기록만 읽어 놓고 \"0건 처리\" 라고 말하는 일이 실제로 가능했다).\n */\n eventsInWindow: number\n /** 요청한 관점으로 쪼갠 값(요청했을 때만). */\n groups?: { by: KpiGroupBy; items: KpiGroup[]; truncated?: number; truncatedTasks?: number }\n}\n\n/**\n * 창의 전력량을 **배전 계통의 자리별로 나눈다** — 근거를 함께.\n *\n * ── 왜 커널 함수를 쓰나 ─────────────────────────────────────────────────────\n * 「나눌 근거가 없으면 나누지 않는다」·「합은 보존된다」·「몫이 0 이면 0kWh 를 주지 않는다」는 규칙이\n * 커널의 `attributeEnergy` 에 이미 있다. 여기서 다시 쓰면 두 벌이 되고, 그때부터 두 화면이 다른 합을\n * 말한다. 이 함수가 하는 일은 **재료를 모아 넘기고 답을 자리별로 묶는 것**뿐이다.\n *\n * ── 가중치는 가동시간이다 ───────────────────────────────────────────────────\n * 정격(kW)으로 나누지 않는다: 시뮬 트윈의 총부하가 정격의 합에서 파생되므로 그 총량을 다시 정격으로\n * 나누면 넣은 값이 그대로 나온다(새 정보 0의 동어반복). 가동시간은 **저널이 관측한 사실**이다.\n *\n * 자리를 모르는 자원은 `unknown` 으로 모인다 — 빼면 합이 줄고, 아무 자리에 넣으면 거짓이 된다.\n */\nfunction feederShares(\n kWh: number | undefined,\n utilization: { resourceId: string; busyMs: number }[],\n resourceFeeder?: Record<string, string>\n): Partial<NonNullable<KpiResult['energy']>> {\n /* 선언이 없으면 분해하지 않는다 — 자리를 모르는 채 나눈 숫자는 아무 것의 몫도 아니다. */\n if (!resourceFeeder || !Object.keys(resourceFeeder).length) return {}\n if (kWh === undefined || !(kWh > 0)) return {}\n\n /*\n * ── 풀이 **무엇을 덮는지 사실대로** 말한다 (2026-08-17) ────────────────────\n *\n * 처음에는 「그 창에 돌아간 자원」만 덮는다고 넘겼다. 그러자 돌아간 자원이 하나인 창에서 커널이\n * 그것을 **`measured`(전용 계량)** 로 판정했다 — 커널의 규칙은 옳다(「덮는 소비처가 하나면 그\n * 계량기는 그것만 잰다는 선언이다」). 틀린 것은 내 선언이었다: 현장 계량기는 돌아간 것만 재지 않는다.\n *\n * 그래서 **선언된 부하 전부**를 덮는다고 말한다(`resourceFeeder` 의 키가 곧 그 선언이다).\n * 그러면 그 창에 돌지 않은 설비는 몫 0 으로 **배분에서 빠지고 그 수가 보고된다** — 0kWh 를 주는\n * 대신 「이 설비는 이 창에 가동 기록이 없다」를 말하는 쪽이다.\n */\n /*\n * 덮는 대상은 **선언된 부하 ∪ 그 창에 돌아간 자원**이다.\n *\n * 선언만 쓰면, 돌았는데 전기 계통에 선언되지 않은 설비의 몫이 나머지 설비에 얹힌다 — 그 설비가\n * 안 쓴 전기를 쓴 것으로 만든다. 돌아간 것만 쓰면, 선언됐지만 쉰 설비가 사라져 「이 창에 안 돌았다」를\n * 말할 수 없다. 둘을 합해야 두 사실이 다 남는다(자리를 모르는 몫은 `unknown` 으로 모인다).\n */\n const busyOf = new Map(utilization.map(u => [u.resourceId, u.busyMs]))\n const ids = new Set<string>([...Object.keys(resourceFeeder), ...busyOf.keys()])\n const consumers = [...ids].map(id => ({ id, weight: busyOf.get(id) ?? 0 }))\n if (!consumers.length) return {}\n\n const r = attributeEnergy({\n pools: [{ meterId: 'site', kWh, consumerIds: consumers.map(c => c.id) }],\n consumers,\n weightKind: 'runtimeMs'\n })\n\n const byKey = new Map<string, { kWh: number; weightShare: number; apportioned: boolean }>()\n for (const sh of r.shares) {\n /* 자리를 모르면 `unknown` — 그 몫이 있다는 사실은 남기고, 어디의 것인지는 지어내지 않는다. */\n const key = resourceFeeder[sh.consumerId] ?? 'unknown'\n const cur = byKey.get(key) ?? { kWh: 0, weightShare: 0, apportioned: false }\n cur.kWh += sh.kWh\n cur.weightShare += sh.weightShare ?? 0\n /* **근거는 커널이 정한다.** 여기서 낙인찍으면 커널이 「쟀다」고 한 것을 「나눴다」고 바꿔 말하거나\n 그 반대가 된다. 섞이면 약한 쪽(배분)으로 적는다 — 강한 주장을 올려 잡지 않는다. */\n if (sh.basis === 'apportioned') cur.apportioned = true\n byKey.set(key, cur)\n }\n\n const byFeeder = [...byKey.entries()]\n .map(([key, v]) => ({\n key,\n kWh: v.kWh,\n basis: (v.apportioned ? 'apportioned' : 'measured') as 'apportioned' | 'measured',\n weightKind: 'runtimeMs' as const,\n weightShare: v.weightShare\n }))\n .sort((a, b) => b.kWh - a.kWh)\n\n const un = r.unattributed.reduce((sum, u) => sum + u.kWh, 0)\n return {\n ...(byFeeder.length ? { byFeeder } : {}),\n ...(un > 0 ? { unattributedKWh: un, unattributedReason: r.unattributed[0]?.reason } : {}),\n ...(r.excluded.length ? { excludedResources: r.excluded.length } : {})\n }\n}\n\n/**\n * 분기별 전력량을 **계량으로** 읽는다 — 계량이 있으면 배분하지 않는다.\n *\n * ── 왜 필요했나 (2026-08-18) ────────────────────────────────────────────────\n * 실 게이트웨이는 분기마다 계량기를 보낸다. 그런데 화면은 「나누지 못함」으로 답하고 있었다 — 여기에\n * 가동시간 배분 경로만 있었기 때문이다. 계량이 있는데 배분으로 말하면 **근거를 실제보다 약하게** 보고하는\n * 것이고, 그 위에 세운 SEU 판단이 무거운 값을 놓친다. 미러 트윈에는 설비 가동 기록조차 없어서\n * (원천이 계량만 준다) 그 경로로는 분기별 값이 영원히 나오지 않았다.\n *\n * 값의 출처는 커널이 구간 마감 사실에 실어 주는 **지점별 몫**이다(적산의 뺄셈). 표본을 직접 읽지 않는\n * 이유는 조회 상한이다 — 계측은 초당 여러 건이라 표본으로는 창을 채우기 전에 상한에 걸린다.\n *\n * 나머지(뿌리 − 분기 합)는 **버리지도 얹지도 않는다**: 수전 직결 부하·손실·계량되지 않은 회로가 그\n * 차이이므로, 어느 분기에 얹으면 그 분기가 쓰지 않은 전기를 쓴 것으로 만든다. 커널의 `remainder` 풀로\n * 넘겨 「하위 계량이 덮지 않는다」로 남긴다.\n *\n * 분기 계량 합이 뿌리보다 크면(회계가 다른 두 계량기) **그 사실을 낸다** — 값을 깎아 맞추면 계량을\n * 우리가 고쳐 쓰는 것이다.\n */\n/**\n * 창의 **발전량** — 커널이 마감한 기간 사실을 합한다.\n *\n * ── 왜 합이 전부인가 ───────────────────────────────────────────────────────\n * 기간 발전량은 커널이 이미 확정한 사실이다(적산의 종류마다 다른 규칙으로 얻는다). 여기서 다시\n * 계산하면 규칙이 두 벌이 되고, 그때부터 두 화면이 다른 발전량을 말한다. 이 함수가 하는 일은\n * **창에 든 것을 고르고, 설비별로 묶고, 근거가 얼마나 덮였는지 세는 것**이다.\n *\n * ── 걸친 기간은 세지 않는다 ─────────────────────────────────────────────────\n * 구간 마감과 같은 규칙이다. 하루의 절반이 창에 들었다고 절반을 세면 그것은 관측이 아니라 우리가 나눈\n * 값이고, 합이 창을 넘는다.\n *\n * ── 덜 덮인 기간을 센다 ─────────────────────────────────────────────────────\n * 기간의 끝이 자정인데 마지막 표본이 저녁이었으면 그 사이는 재지 않은 것이다. 그 수를 함께 내야 화면이\n * 「이 합은 이만큼 덜 잰 것」이라고 말할 수 있다. 얼마나 어긋나면 문제인지는 현장이 정한다.\n */\nfunction generatedOfPeriods(\n periods: readonly {\n equipmentId: string\n kWh: number\n startMs: number\n endMs: number\n observedFromMs: number\n observedToMs: number\n }[],\n range: { startMs: number; endMs: number },\n /** 이용률의 분모 — 없으면 이용률을 내지 않는다(0 이나 짐작을 만들지 않는다). */\n rated?: { kW: number; basis: 'ac' | 'dc'; from: 'equipment' | 'location' },\n /** 기간마다 적용할 단가 — 기간을 덮는 것을 쓴다. 없으면 금액을 만들지 않는다. */\n prices?: readonly { startMs: number; endMs: number; unitPrice: number; currency: string }[]\n): Partial<NonNullable<KpiResult['energy']>> {\n const inside = periods.filter(g => g.startMs >= range.startMs && g.endMs <= range.endMs)\n\n /*\n * ── **걸친 기간을 조용히 버리지 않는다** (2026-08-30) ──────────────────────\n *\n * 더하는 것은 창에 **온전히 들어온 기간**뿐이다. 그것이 맞는 셈이다 — 걸친 기간을 더하면 다른 날의\n * 값이 이 창의 값인 척한다.\n *\n * 그런데 버린 것을 말하지 않았다. 하루 단위 기간은 「지금부터 24시간 전」 창에 온전히 들어오는 일이\n * 거의 없다(창이 한낮에서 시작하므로). 그래서 어제 발전량이 저널에 멀쩡히 있는데 화면이 비었고,\n * 사용자는 그것을 **「발전량이 없다」**로 읽었다. 실측으로 그 일이 났다.\n *\n * 이제 걸친 것을 세어 함께 낸다. 화면이 「이 창에 다 들어오지 않은 기간이 있다」를 말할 수 있다.\n */\n const straddling = periods.filter(\n g => !(g.startMs >= range.startMs && g.endMs <= range.endMs) && g.endMs > range.startMs && g.startMs < range.endMs\n )\n const notCounted = straddling.length\n ? {\n periods: straddling.length,\n kWh: straddling.reduce((a, g) => a + g.kWh, 0),\n earliestStart: new Date(Math.min(...straddling.map(g => g.startMs))).toISOString(),\n latestEnd: new Date(Math.max(...straddling.map(g => g.endMs))).toISOString()\n }\n : undefined\n\n if (!inside.length) {\n /* 온전히 들어온 것이 없다 — 그래도 걸친 것이 있으면 그 사실을 낸다(빈 값과 「없음」은 다르다). */\n return notCounted ? { generated: { kWh: 0, counted: 0, partial: 0, byEquipment: [], notCounted } } : {}\n }\n\n const byEquipment = new Map<string, number>()\n let partial = 0\n for (const g of inside) {\n byEquipment.set(g.equipmentId, (byEquipment.get(g.equipmentId) ?? 0) + g.kWh)\n const coveredFrom = Number.isFinite(g.observedFromMs) ? g.observedFromMs <= g.startMs : false\n const coveredTo = Number.isFinite(g.observedToMs) ? g.observedToMs >= g.endMs : false\n if (!coveredFrom || !coveredTo) partial++\n }\n\n const kWh = inside.reduce((a, g) => a + g.kWh, 0)\n /*\n * 이용률의 분모는 **센 기간들의 길이**다. 창 길이로 나누면, 하루치 하나만 센 24시간 창에서 그 하루가\n * 창 전체에 걸쳐 있었던 것처럼 작아진다.\n */\n const hours = inside.reduce((a, g) => a + (g.endMs - g.startMs) / 3_600_000, 0)\n const capacityFactor =\n rated && rated.kW > 0 && hours > 0\n ? { value: kWh / (rated.kW * hours), ratedKW: rated.kW, basis: rated.basis, from: rated.from, hours }\n : undefined\n\n /*\n * 낸 것의 값 — 기간마다 **그 기간을 덮는 단가**로 매긴다. 걸친 단가는 쓰지 않는다: 하루 단가로\n * 이틀치를 매기면 그 절반은 다른 날의 값이 된다.\n *\n * 모든 기간에 단가가 있을 때만 금액을 만든다. 일부만 매기고 더하면 그 합이 창 전체의 값인 척한다.\n */\n let amount = 0\n let priced = 0\n let currency: string | undefined\n for (const g of inside) {\n const p = (prices ?? []).find(x => x.startMs <= g.startMs && x.endMs >= g.endMs)\n if (!p) continue\n amount += g.kWh * p.unitPrice\n priced++\n currency = currency ?? p.currency\n }\n const unpriced = inside.length - priced\n const revenue = priced > 0 && unpriced === 0 && currency ? { amount, currency, priced, unpriced } : undefined\n\n return {\n generated: {\n kWh,\n counted: inside.length,\n partial,\n byEquipment: [...byEquipment.entries()]\n .map(([id, kWh]) => ({ id, kWh }))\n .sort((a, b) => b.kWh - a.kWh),\n ...(capacityFactor ? { capacityFactor } : {}),\n ...(revenue ? { revenue } : {}),\n /* 이 창에 다 들어오지 않아 더하지 못한 기간 — 있으면 화면이 그것을 말한다. */\n ...(notCounted ? { notCounted } : {})\n }\n }\n}\n\n/**\n * 밖에서 마감되어 온 사용 구간을 합한다 — **구간 마감과 더하지 않는다.**\n *\n * 두 회계가 같은 전기를 말한다. 더하면 두 번 세고, 둘 다 있는 현장에서는 계량 쪽이 강한 근거이므로\n * 그쪽을 쓴다. 여기서는 이쪽 몫만 계산하고, 무엇을 쓸지는 부르는 쪽이 정한다.\n *\n * 단가는 **모든 구간이 실었을 때만** 요금을 만든다. 일부만 실었는데 있는 것만 더하면 그 합이 창의\n * 요금인 척한다 — 못 매긴 구간 수를 함께 낸다.\n */\nfunction usageOfPeriods(\n periods: readonly { meterId: string; kWh: number; startMs: number; endMs: number; unitPrice: number; currency?: string; basis: string }[],\n range: { startMs: number; endMs: number }\n): NonNullable<KpiResult['energy']>['usage'] | undefined {\n const inside = periods.filter(u => u.startMs >= range.startMs && u.endMs <= range.endMs)\n if (!inside.length) return undefined\n\n const byBasis = new Map<string, number>()\n let kWh = 0\n let charge = 0\n let unpriced = 0\n let currency: string | undefined\n for (const u of inside) {\n kWh += u.kWh\n byBasis.set(u.basis, (byBasis.get(u.basis) ?? 0) + 1)\n if (Number.isFinite(u.unitPrice)) {\n charge += u.kWh * u.unitPrice\n if (!currency) currency = u.currency\n } else unpriced++\n }\n\n return {\n kWh,\n counted: inside.length,\n ...(unpriced === 0 && currency ? { charge, currency } : {}),\n unpriced,\n byBasis: [...byBasis.entries()].map(([basis, counted]) => ({ basis, counted })).sort((a, b) => b.counted - a.counted)\n }\n}\n\n/**\n * **요금적용전력을 고른다 — 청구서가 먼저다.**\n *\n * 순서에 뜻이 있다. 청구서는 그 기간에 실제로 매겨진 값이고, 선언은 사람이 옮겨 적은 값이다.\n * 그 값이 매달 바뀌므로 옮겨 적은 것은 곧 낡는다.\n */\nfunction billingDemandOf(\n billsInWindow: readonly { endMs: number; billingDemandKW: number }[],\n tariffBasis: { billingDemandKW?: number } | undefined,\n lastBill: { billingDemandKW?: number } | undefined,\n declaredKW: number | undefined,\n /** 이 창에서 관측한 최고 수요와 그 값이 언제까지의 것인지 — 사용 구간의 `maxKW` 에서 온다. */\n observedPeak?: { kW: number; toMs: number }\n): NonNullable<KpiResult['energy']>['billingDemand'] | undefined {\n const declared = Number.isFinite(Number(declaredKW)) ? Number(declaredKW) : undefined\n const withValue = billsInWindow.filter(b => Number.isFinite(b.billingDemandKW))\n const fromBill = withValue.length ? withValue.reduce((a, x) => (x.endMs > a.endMs ? x : a)).billingDemandKW : undefined\n const carried = Number.isFinite(Number(lastBill?.billingDemandKW)) ? Number(lastBill?.billingDemandKW) : undefined\n /* 이 주기에 적용되는 기준이 가장 먼저다 — 청구서는 끝난 기간의 것이다. */\n const fromBasis = Number.isFinite(Number(tariffBasis?.billingDemandKW)) ? Number(tariffBasis?.billingDemandKW) : undefined\n\n const picked =\n fromBasis !== undefined\n ? { kW: fromBasis, from: 'tariff-basis' as const }\n : fromBill !== undefined\n ? { kW: fromBill, from: 'bill' as const }\n : carried !== undefined\n ? { kW: carried, from: 'carried-forward' as const }\n : declared !== undefined\n ? { kW: declared, from: 'declaration' as const }\n : undefined\n if (!picked) return undefined\n\n /* 선언이 있는데 청구서와 다르면 그 사실을 낸다 — 낡은 선언은 스스로 말해야 한다. */\n const stale = picked.from !== 'declaration' && declared !== undefined && declared !== picked.kW\n const exceeded =\n observedPeak && observedPeak.kW > picked.kW\n ? {\n peakKW: observedPeak.kW,\n overKW: observedPeak.kW - picked.kW,\n observedTo: new Date(observedPeak.toMs).toISOString()\n }\n : undefined\n\n return {\n ...picked,\n ...(declared !== undefined ? { declaredKW: declared } : {}),\n ...(stale ? { stale: true } : {}),\n ...(exceeded ? { exceeded } : {})\n }\n}\n\n/**\n * 창에 온전히 든 청구서 하나 — 가장 늦게 끝난 것.\n *\n * **창이 청구 주기와 같을 때만 견준다.** 한 달 청구서를 하루 창의 값과 빼면 그 차이는 아무것도 뜻하지\n * 않는다. 그래서 같은지를 값과 함께 낸다.\n */\nfunction billOfWindow(\n bills: readonly {\n from: string\n to: string\n startMs: number\n endMs: number\n total: number\n energyCharge: number\n demandCharge: number\n currency?: string\n billingDemandKW: number\n }[],\n range: { startMs: number; endMs: number },\n ours?: { total?: number; demandCharge?: number; energyCharge?: number }\n): NonNullable<KpiResult['energy']>['bill'] | undefined {\n const inside = bills.filter(b => b.startMs >= range.startMs && b.endMs <= range.endMs)\n if (!inside.length) return undefined\n const b = inside.reduce((a, x) => (x.endMs > a.endMs ? x : a))\n const matchesWindow = b.startMs === range.startMs && b.endMs === range.endMs\n /* 양쪽에 있는 항목만 견준다 — 한쪽이 없는 것을 0 으로 두면 그 차이가 통째로 어긋남처럼 보인다. */\n const gap = (mine: unknown, billed: number): number | undefined =>\n Number.isFinite(Number(mine)) && Number.isFinite(billed) ? Number(mine) - billed : undefined\n const diff = matchesWindow\n ? {\n ...(gap(ours?.total, b.total) !== undefined ? { total: gap(ours?.total, b.total) as number } : {}),\n ...(gap(ours?.demandCharge, b.demandCharge) !== undefined\n ? { demandCharge: gap(ours?.demandCharge, b.demandCharge) as number }\n : {}),\n ...(gap(ours?.energyCharge, b.energyCharge) !== undefined\n ? { energyCharge: gap(ours?.energyCharge, b.energyCharge) as number }\n : {})\n }\n : {}\n return {\n from: b.from,\n to: b.to,\n ...(Number.isFinite(b.total) ? { total: b.total } : {}),\n ...(Number.isFinite(b.energyCharge) ? { energyCharge: b.energyCharge } : {}),\n ...(Number.isFinite(b.demandCharge) ? { demandCharge: b.demandCharge } : {}),\n ...(b.currency ? { currency: b.currency } : {}),\n ...(Number.isFinite(b.billingDemandKW) ? { billingDemandKW: b.billingDemandKW } : {}),\n matchesWindow,\n ...(Object.keys(diff).length ? { diff } : {})\n }\n}\n\nfunction measuredFeeders(\n windows: readonly {\n startMs: number\n endMs: number\n maxKW?: number\n points: { id: string; kWh: number; root: boolean }[]\n pointsAtPeak?: { id: string; kW: number }[]\n }[],\n range: { startMs: number; endMs: number },\n totalKWh: number | undefined,\n meterFeeder?: Record<string, string>\n): Partial<NonNullable<KpiResult['energy']>> | undefined {\n if (!meterFeeder || !Object.keys(meterFeeder).length) return undefined\n if (totalKWh === undefined || !(totalKWh > 0)) return undefined\n\n /* 창에 온전히 든 구간만 — 전체 전력량과 같은 규칙이어야 몫과 합이 같은 창을 말한다. */\n const byFeederKWh = new Map<string, number>()\n let sawPoints = false\n for (const w of windows) {\n if (!(w.startMs >= range.startMs && w.endMs <= range.endMs)) continue\n for (const p of w.points ?? []) {\n const feeder = meterFeeder[p.id]\n if (!feeder) continue // 뿌리 계량기·모르는 지점 — 분기의 몫이 아니다\n sawPoints = true\n byFeederKWh.set(feeder, (byFeederKWh.get(feeder) ?? 0) + p.kWh)\n }\n }\n if (!sawPoints) return undefined // 계량이 없으면 이 경로를 쓰지 않는다(배분으로 돌아간다)\n\n const feeders = [...byFeederKWh.entries()].filter(([, kWh]) => kWh > 0)\n if (!feeders.length) return undefined\n const submetered = feeders.reduce((sum, [, kWh]) => sum + kWh, 0)\n const remainder = totalKWh - submetered\n\n const r = attributeEnergy({\n pools: [\n ...feeders.map(([feeder, kWh]) => ({ meterId: `${feeder}:meter`, kWh, consumerIds: [feeder] })),\n ...(remainder > 0 ? [{ meterId: 'root:remainder', kWh: remainder, consumerIds: [], remainder: true }] : [])\n ],\n consumers: feeders.map(([feeder]) => ({ id: feeder }))\n })\n\n /*\n * ── 피크를 만든 분기 (2026-08-18) ─────────────────────────────────────────\n *\n * 요금은 kW 로 매겨지므로 「어느 분기를 깎아야 피크가 내려가나」가 실무의 질문이다. 그 답은 **피크가\n * 찍힌 구간의 그 순간** 값이고, 창 안의 여러 구간을 더하거나 평균하면 뜻이 사라진다(다른 순간의 값을\n * 섞는 것이다). 그래서 **최대수요를 낸 구간 하나**만 골라 그 순간의 지점 값을 쓴다.\n */\n const peakWindow = windows\n .filter(w => w.startMs >= range.startMs && w.endMs <= range.endMs && Number.isFinite(Number(w.maxKW)))\n .reduce<(typeof windows)[number] | undefined>((best, w) => (!best || Number(w.maxKW) > Number(best.maxKW) ? w : best), undefined)\n const peakOf = new Map<string, number>()\n for (const p of peakWindow?.pointsAtPeak ?? []) {\n const feeder = meterFeeder[p.id]\n if (!feeder) continue // 뿌리 계량기·모르는 지점 — 분기의 몫이 아니다\n peakOf.set(feeder, (peakOf.get(feeder) ?? 0) + p.kW)\n }\n\n const byFeeder = r.shares\n .map(sh => ({\n key: sh.consumerId,\n kWh: sh.kWh,\n basis: sh.basis,\n weightKind: undefined,\n weightShare: undefined,\n /* 피크 순간의 값이 없으면 만들지 않는다 — 그 구간에 이 분기의 표본이 없었다는 뜻이다. */\n ...(peakOf.has(sh.consumerId) ? { peakKW: peakOf.get(sh.consumerId) } : {})\n }))\n .sort((a, b) => b.kWh - a.kWh) as NonNullable<KpiResult['energy']>['byFeeder']\n\n const un = r.unattributed.reduce((sum, u) => sum + u.kWh, 0)\n return {\n ...(byFeeder?.length ? { byFeeder } : {}),\n ...(un > 0 ? { unattributedKWh: un, unattributedReason: r.unattributed[0]?.reason } : {}),\n /* 분기 합이 뿌리를 넘었다 — 두 계량기의 회계가 다르다는 사실이다(깎아 맞추지 않는다). */\n ...(remainder < 0 ? { submeterExcessKWh: -remainder } : {})\n }\n}\n\n/** 폴드 옵션 — 관점 축과, 그 축을 만들기 위해 이벤트 밖에서 와야 하는 지도. */\n/*\n * ── 같은 사실이 두 벌 있어도 한 번만 센다 (2026-08-30) ──────────────────────\n *\n * 마감된 구간 사실은 정체성이 내용에 있다(커널 0.7.81). 그런데 저널에 이미 여러 벌 적힌 것이 있다 —\n * 쓰는 자리에서 막기 전에 들어간 것들이다(실측: 어느 날의 발전 단가가 세 벌).\n *\n * 지난 기록을 지우지 않는다 — 저널은 일어난 일의 기록이다. 대신 **읽을 때 한 번만 센다.**\n * 그러지 않으면 그 구간의 수익·사용량이 그 횟수만큼 세어진다.\n */\nfunction onceById<T extends { payload?: any }>(rows: readonly T[]): T[] {\n const seen = new Set<string>()\n return rows.filter(r => {\n const id = (r as any)?.payload?.eventId\n if (typeof id !== 'string' || !id) return true\n if (seen.has(id)) return false\n seen.add(id)\n return true\n })\n}\n\nexport interface KpiFoldOptions {\n groupBy?: KpiGroupBy\n /**\n * 이 트윈이 이어받고 있는 **마지막 청구서** — 요금적용전력의 우선 근거(§`billingDemandOf`).\n *\n * 창 밖의 청구서는 저널 조회에 걸리지 않는다. 그런데 그 값은 이 창에도 적용되므로 호출부가\n * 커널 상태에서 읽어 넘긴다.\n */\n lastBill?: { to: string; billingDemandKW?: number }\n /**\n * 이 트윈이 이어받고 있는 **요금 기준** — 이 주기에 적용되는 값(§`ENERGY_EVENT.tariffBasis`).\n *\n * 청구서보다 먼저다. 청구서는 끝난 기간의 정산이라 이번 주기의 기준을 말하지 않는다.\n */\n tariffBasis?: { from: string; billingDemandKW?: number }\n /**\n * 이 트윈의 **발전 정격**과 그 근거 — 이용률의 분모(§ 커널 `generationRated`).\n *\n * 모델의 선언에서 오므로 호출부가 읽어 넘긴다. 폴드는 저널만 본다.\n */\n generationRated?: { kW: number; basis: 'ac' | 'dc'; from: 'equipment' | 'location' }\n /**\n * 이 트윈이 이어받고 있는 **발전 단가** — 창 밖의 단가도 이 창에 적용될 수 있다.\n *\n * 창 안의 단가 사실이 먼저다. 이것은 그 창에 단가가 없을 때 쓴다.\n */\n generationPrice?: { from: string; to: string; unitPrice: number; currency: string }\n /**\n * 노드 → 구역 지도. **이벤트에는 구역이 없다**(노드까지만 있다) — 스냅샷·마스터가 아는 값이라\n * 호출부가 넘긴다. 없으면 구역 축은 'unknown' 으로 모인다(알리지 않고 다른 축으로 바꾸지 않는다).\n */\n locationArea?: Record<string, string>\n /**\n * 자원 → **배전 계통의 자리**(분기·수전) 지도.\n *\n * ── 왜 밖에서 오나 ───────────────────────────────────────────────────────\n * 이벤트에는 전기 계통이 없다. 「이 설비가 어느 분기에서 먹는가」는 에너지 트윈의 모델이 아는\n * 선언이고(설비 하나에 사실 둘 — 공장에서는 자원, 전기에서는 부하), 그 선언을 읽는 것은 호출부의 일이다.\n *\n * 없으면 분기 분해를 **하지 않는다** — 자리를 모르는 채 나누면 그 숫자는 아무 것의 몫도 아니다.\n */\n resourceFeeder?: Record<string, string>\n /**\n * **계량 지점 → 그것이 재는 분기** 지도.\n *\n * 있으면 분기별 전력량을 계량으로 읽는다(배분보다 강한 근거). 이 지도는 모델의 선언에서 오고\n * (계량기의 자리), 그것을 읽는 것은 호출부의 일이다 — 폴드는 저널만 본다.\n */\n meterFeeder?: Record<string, string>\n /**\n * 선언된 **전기요금 단가** — 수를 금액으로 바꾸는 유일한 입력.\n *\n * 이벤트에는 요금이 없다. 단가는 현장의 계약이고(에너지 트윈 모델의 수전 자리에 선언한다), 그것을\n * 읽는 것은 호출부의 일이다. 없으면 금액을 만들지 않는다 — 짐작한 단가로 낸 금액은 숫자가 있다는\n * 것만으로 사람을 결정으로 밀어붙인다.\n */\n tariff?: TariffDeclaration\n /**\n * 교대 선언 — **이벤트에는 교대가 없다**(시각만 있다). 현장 마스터가 아는 값이라 호출부가 넘긴다.\n * 없으면 교대 축은 'unknown' 으로 모인다(다른 축으로 알리지 않고 바꾸지 않는다).\n */\n shifts?: WorkCalendarEntry[]\n /**\n * 현장의 시각 기준(분) — 교대 축이 `HH:MM` 을 어느 기준으로 읽을지. **공간이 갖는 값**이다\n * (테넌트가 아니다: 한 테넌트가 다른 시간대의 공장을 함께 가질 수 있다). 없으면 UTC.\n *\n * ⚠ 한 수다. 접기는 지난 구간을 훑으므로 **일광절약시간 전환을 넘을 수 없다** — 그때는\n * `offsetAt` 을 넘긴다.\n */\n utcOffsetMinutes?: number\n /**\n * 그 순간의 시각 기준을 푸는 함수 — **`utcOffsetMinutes` 보다 먼저 본다**.\n *\n * ── 왜 수가 아니라 함수인가 (2026-09-07) ─────────────────────────────────\n * 접기는 지난 구간을 훑는다. 1년치를 교대별로 접으면 그 1년 안에 일광절약시간 전환이 두 번 들어\n * 있는데, 고정 오프셋 하나로 읽으면 전환 뒤의 사실이 **한 시간 밀려 다른 교대로 세어진다.**\n * 오류가 나지 않으므로 교대별 성과가 조용히 거짓이 된다. `America/Tijuana`(PST −480 / PDT −420)가\n * 이미 자료에 있다.\n *\n * 이 파일은 시간대 데이터를 들지 않는다(커널과 같은 자세다) — 부르는 쪽이 푸는 법을 준다. 그래서\n * 이 인자가 함수이고, 시험은 여전히 평범한 값으로 돈다.\n *\n * `undefined` 를 돌려주면 그 순간은 기준을 모르는 것이다 — 0(UTC)으로 채우지 않고\n * `utcOffsetMinutes` 로 떨어진다.\n */\n offsetAt?: (atMs: number) => number | undefined\n /**\n * 축의 상한(기본 20). 넘치면 잘라내고 **잘린 축의 수와 그 안의 건수를 함께 알린다**.\n *\n * 축의 수만 알리면 부족하다 — \"그 외 89개\" 로는 그것이 3건인지 300건인지 모른다. 보여준 축이 전부인\n * 것처럼 읽히면 사용자가 꼬리를 놓친다.\n */\n groupLimit?: number\n}\n\n/* ── 관점 축의 재료 ─────────────────────────────────────────────────────────\n * 축의 값은 이벤트 payload 에 이미 들어 있다(`TaskStatusDelta`: kind·orderId·fromNode·toNode·\n * resourceRef). 새 계측을 심을 필요가 없다 — **이미 쌓인 저널을 다르게 묶기만** 한다.\n */\n/** 창 안에 완료된 한 건의 기록 — 통계의 원료이자 관점 축의 원료. */\ninterface CompletionRecord {\n taskId: string\n at: number\n facets: TaskFacets\n leadMs?: number\n workMs?: number\n waitMs?: number\n}\n\n/** 이 기록이 요청한 축에서 어느 값에 속하는가. 값이 없으면 'unknown'(알리지 않고 버리지 않는다). */\nfunction facetKey(rec: CompletionRecord, options: KpiFoldOptions): string {\n const f = rec.facets\n switch (options.groupBy) {\n case 'resource':\n return f.resource ?? 'unknown'\n case 'taskKind':\n return f.kind ?? 'unknown'\n case 'location':\n return f.location ?? 'unknown'\n case 'order':\n return f.order ?? 'unknown'\n case 'area':\n /* 구역은 이벤트 밖의 지식이다 — 지도가 없으면 없다고 말한다(노드 id 로 대체하면 사용자가\n * 그것을 구역으로 오해한다). */\n return (f.location && options.locationArea?.[f.location]) ?? 'unknown'\n case 'shift':\n /*\n * 교대는 **저널에 찍혀 있지 않다 — 계산한다.**\n *\n * 이벤트에 교대 이름을 스탬프하면 두 가지가 무너진다: ① 같은 사실이 두 곳에 생기고,\n * ② 나중에 교대 시간을 고치면 **과거가 옛 구분으로 굳어** 다시 셀 수 없다. 교대는\n * `완료 시각 + 현장의 시각 기준 + 교대 선언`에서 나오는 순수 파생이므로 여기서 낸다 —\n * 라이브·히스토리·예측이 **커널과 같은 함수**(`activeShiftOf`)를 쓴다.\n *\n * 선언이 없으면 'unknown' 이다. 교대를 나눠 놓지 않은 현장에 이름을 지어내지 않는다.\n */\n if (!options.shifts?.length) return 'unknown'\n /* 시각까지 넘긴다 — **휴일에 일어난 일은 어느 교대에도 속하지 않는다.** 되풀이만 보면 그날 서지도\n 않은 교대에 집계돼, 교대별 성과가 알리지 않고 거짓이 된다. */\n /* 그 순간의 기준을 먼저 묻는다 — 없으면 한 수로 떨어진다(§`offsetAt`). */\n const atMs = typeof rec.at === 'number' ? rec.at : Date.parse(String(rec.at))\n const offset =\n (Number.isFinite(atMs) ? options.offsetAt?.(atMs) : undefined) ?? options.utcOffsetMinutes\n return activeShiftAt(options.shifts, rec.at, offset) ?? 'unknown'\n default:\n return 'unknown'\n }\n}\n\n/**\n * 이 축의 품질 — **판정이 있었던 것만** 센다(없으면 필드를 만들지 않는다).\n *\n * 양품률의 이력이 여기서 나온다: 커널이 작업 완료에 결과를 적고(`TaskStatusDelta.outcome`), 폴드가 축으로\n * 들고(`TaskFacets.outcome`), 여기서 종류별로 센다. 그 사슬이 없으면 「이 공정의 양품률」은 저널에\n * 남지 않는다(설비 누적 카운터는 종류를 모른다).\n */\nfunction qualityOf(rows: CompletionRecord[]): { quality?: { good: number; scrap: number } } {\n let good = 0\n let scrap = 0\n for (const r of rows) {\n const o = r.facets.outcome\n if (o === 'good') good++\n else if (o === 'scrap') scrap++\n }\n return good + scrap > 0 ? { quality: { good, scrap } } : {}\n}\n\n/**\n * 기록을 축으로 묶어 축별 통계를 낸다 — **전체와 같은 규칙**으로(같은 stats 함수, 같은 창).\n *\n * 축의 값이 아주 많을 수 있다(오더 축은 수천 개). 그래서 건수 큰 것부터 상한까지만 돌려주고\n * **잘라낸 수를 함께 알린다** — 들어온 것이 없는 절단은 \"이게 전부\" 라는 거짓을 만든다.\n */\nfunction groupRecords(\n records: CompletionRecord[],\n options: KpiFoldOptions\n): { by: KpiGroupBy; items: KpiGroup[]; truncated?: number; truncatedTasks?: number } {\n const by = options.groupBy as KpiGroupBy\n const bins = new Map<string, CompletionRecord[]>()\n for (const rec of records) {\n const key = facetKey(rec, options)\n const bin = bins.get(key)\n if (bin) bin.push(rec)\n else bins.set(key, [rec])\n }\n\n const all = [...bins.entries()]\n .map(([key, rows]) => ({\n key,\n tasks: rows.length,\n leadTime: stats(rows.map(r => r.leadMs).filter((v): v is number => v !== undefined)),\n workTime: stats(rows.map(r => r.workMs).filter((v): v is number => v !== undefined)),\n waitTime: stats(rows.map(r => r.waitMs).filter((v): v is number => v !== undefined)),\n busyMs: rows.reduce((a, r) => a + (r.workMs ?? 0), 0),\n ...qualityOf(rows)\n }))\n .sort((a, b) => b.tasks - a.tasks || (a.key < b.key ? -1 : 1))\n\n const limit = Math.max(1, Math.min(100, options.groupLimit ?? 20))\n const items = all.slice(0, limit)\n const dropped = all.slice(limit)\n /* 축별로 \"창 대비 비율\" 은 내지 않는다 — 자원 아닌 축(오더·작업종류)에서 그 비율은 의미가 없고,\n * 의미 없는 숫자를 내면 사용자가 그것으로 판단한다. 자원 점유는 `utilization` 이 이미 낸다. */\n return {\n by,\n items,\n ...(dropped.length\n ? { truncated: dropped.length, truncatedTasks: dropped.reduce((a, g) => a + g.tasks, 0) }\n : {})\n }\n}\n\nfunction ms(value: string | undefined): number | null {\n if (!value) return null\n const t = Date.parse(value)\n return Number.isFinite(t) ? t : null\n}\n\n/** 분위수 — 정렬된 값에서 선형 보간 없이 가장 가까운 아래 값(작은 표본에서 과장하지 않는다). */\nfunction quantile(sorted: number[], q: number): number {\n if (sorted.length === 0) return 0\n const idx = Math.min(sorted.length - 1, Math.max(0, Math.floor(q * sorted.length)))\n return sorted[idx]\n}\n\nfunction stats(values: number[]): DurationStats {\n if (values.length === 0) return { count: 0, p10Ms: 0, p50Ms: 0, p90Ms: 0, avgMs: 0 }\n const sorted = [...values].sort((a, b) => a - b)\n const sum = sorted.reduce((a, b) => a + b, 0)\n return {\n count: sorted.length,\n p10Ms: quantile(sorted, 0.1),\n p50Ms: quantile(sorted, 0.5),\n p90Ms: quantile(sorted, 0.9),\n avgMs: Math.round(sum / sorted.length)\n }\n}\n\n/**\n * 이벤트를 계산해 시간창 KPI 를 만든다.\n *\n * **호출부 규약**: `events` 는 창보다 **넉넉히 앞까지** 담아 넘긴다(예: 창 시작 − 리드타임 상한).\n * 완료된 작업의 시작 이벤트가 창 밖에 있을 수 있기 때문이다. 못 찾은 것은 `unpaired` 로 알린다 —\n * 평균에서 오류 없이 빠지면 지표가 거짓이 된다.\n *\n * 시간축은 `eventTime`(ISO) 차분이다. sim 클록이 실제 시각과 오프셋이 있어도 **차분은 정확**하다\n * (고정 오프셋은 상쇄된다). 파싱 불가한 줄은 버린다.\n */\nexport function foldKpi(events: KpiEvent[], window: KpiWindow, options: KpiFoldOptions = {}): KpiResult {\n /* 작업 짝맞춤은 **커널 규칙**으로(중복 구현 금지 — 두 곳에 적으면 같은 저널로 다른 답이 나온다). */\n const { records: tasks } = foldTaskRecords(events.filter(e => e.eventType === 'task.status'))\n let completedOrders = 0\n /* 완료로 읽지 못한 오더 상태 낱말 → 건수(§`orderStatusNotCounted`). */\n const orderStatusNotCounted = new Map<string, number>()\n /* 이미 완료로 센 오더 — 폴링 원본이 같은 종결을 다시 보내도 한 번만 센다. */\n const completedOrderIds = new Set<string>()\n let seen = 0\n let inWindow = 0\n\n for (const e of events) {\n const at = ms(e.eventTime)\n if (at === null) continue\n seen++\n if (at >= window.fromMs && at <= window.toMs) inWindow++\n const d = e.payload?.data ?? e.payload ?? {}\n\n if (e.eventType === 'order.status') {\n /*\n * **끝났나는 커널이 답한다** (2026-08-23) — `isOrderTerminal`.\n *\n * 예전에는 여기서 **동의어 목록**으로 추론했다(`completed`·`fulfilled`·`done`·`finished`).\n * 그 방식이 낸 대가가 둘이었고 둘 다 들어온 것이 없었다.\n *\n * ① 원본의 낱말이 목록에 없으면 완료가 0 이 된다. 실제로 그랬다 — 첫 실 시스템의 `FINISHED`\n * 가 빠져 있었고, 그때 목록에 낱말을 더한 것은 진짜 빈 자리(그 커넥터가 자기 상태 표를\n * 안 쓰고 있었다)를 가린 땜빵이었다.\n * ② **시뮬 트윈에서는 언제나 0 이었다.** 시뮬은 오더를 이행하며 `fulfilled` 만 올리고\n * `status` 는 바꾸지 않는다 — 낱말로 묻는 판정은 시뮬의 완료를 한 건도 세지 못했다.\n *\n * 표준이 그 축을 갈라 두었다: `orders.status` ↔ `RequestState`(표준도 열거하지 않는다)이고,\n * 「됐나」는 **실적**에서 읽는다(`JobResponse`, 요구는 `SegmentRequirement.Quantity`). 그래서\n * 판정의 1차 근거는 **양**이고 낱말은 양으로 말할 수 없는 종결(취소·중도 종료)에만 쓴다.\n */\n /*\n * ── 받는 오더는 처리량이 아니다 (2026-09-19, ADR-0076 보탬 ⑥) ────────────\n *\n * 입고 · 반품 입고는 세우는 순간 `fulfilled >= requested` 다 — 받았다는 사실이 곧 그 오더의\n * 완료이기 때문이다. 그래서 세면 **받을 때마다 처리량이 한 건씩 는다.** 판정은 계약의\n * `isFlowOrder` 한 곳이 한다(여기서 `kind` 를 다시 적지 않는다).\n */\n if (!isFlowOrder(d)) continue\n\n const status = String(d.status ?? '').toLowerCase()\n const done = isOrderTerminal({ status: d.status, requested: d.requested, fulfilled: d.fulfilled })\n if (at >= window.fromMs && at <= window.toMs) {\n /*\n * **오더당 한 번만 센다.** 폴링하는 원본은 같은 오더의 종결을 여러 주기에 걸쳐 다시 보낼 수\n * 있고(그것은 결함이 아니라 폴링의 성질이다), 사건마다 세면 처리량이 알리지 않고 부풀려진다.\n * 작업 쪽은 커널의 짝맞춤이 이미 그 규율을 갖고 있다(「완료는 작업당 하나」) — 오더만 없었다.\n */\n if (done) {\n const id = String(d.orderId ?? '')\n if (id && !completedOrderIds.has(id)) {\n completedOrderIds.add(id)\n completedOrders++\n } else if (!id) completedOrders++ // 정체를 모르면 셈에서 빼지 않는다(알리지 않고 줄이지 않는다)\n }\n /*\n * **완료로 세지 않은 낱말의 인구조사.** 「못 읽었다」가 아니다 — `picking` 처럼 완료가 아닌\n * 낱말이 여기 오는 것은 정상이다. 완료가 0 인데 여기 낱말이 쌓여 있으면 원인이 곧 보인다.\n *\n * 빈 낱말은 세지 않는다: 상태를 말하지 않은 사건은 다른 결함이고(유입 문이 상태를 필수로\n * 받는다) 여기서 섞으면 이 값의 뜻이 흐려진다.\n */\n else if (status) orderStatusNotCounted.set(status, (orderStatusNotCounted.get(status) ?? 0) + 1)\n }\n }\n }\n\n /*\n * 에너지 — 창에 **온전히 든** 구간 마감 사건만 센다(걸친 구간은 커널 규칙이 뺀다).\n * 사건이 하나도 없으면 이 트윈에는 에너지가 없는 것이므로 블록 자체를 내지 않는다.\n */\n const demandWindows = events\n .filter(e => e.eventType === ENERGY_EVENT.demandWindow)\n .map(e => {\n const d = e.payload?.data ?? e.payload ?? {}\n /* 최대(`maxKW`)도 함께 넘긴다 — 요금이 걸리는 수는 평균이 아니라 최대다.\n 총부하(`grossMaxKW`)는 「무엇이 깎였나」를 되짚는 축이다(발전·방전 차감 전). */\n return {\n startMs: Number(d.startMs),\n endMs: Number(d.endMs),\n meanKW: Number(d.meanKW),\n maxKW: Number(d.maxKW),\n grossMaxKW: Number(d.grossMaxKW),\n /* 지점별 몫 — 계량이 분기에 있으면 그 값은 **측정**이다(배분이 아니다). 커널이 계층 판정\n (`root`)까지 실어 주므로 여기서 같은 규칙을 다시 쓰지 않는다. */\n points: (Array.isArray(d.points) ? d.points : [])\n .map((p: any) => ({ id: String(p?.id ?? ''), kWh: Number(p?.kWh), root: p?.root === true }))\n .filter((p: any) => p.id && Number.isFinite(p.kWh)),\n /* 피크가 찍힌 순간의 지점별 kW — 요금이 걸린 수는 kW 다(전력량이 큰 분기와 피크를 만든 분기는 다르다). */\n pointsAtPeak: (Array.isArray(d.pointsAtPeak) ? d.pointsAtPeak : [])\n .map((p: any) => ({ id: String(p?.id ?? ''), kW: Number(p?.kW) }))\n .filter((p: any) => p.id && Number.isFinite(p.kW))\n }\n })\n .filter(w => Number.isFinite(w.startMs) && Number.isFinite(w.endMs))\n\n /*\n * 발전 기간 사실 — 커널이 기간을 마감할 때 낸 것이다. 구간 마감과 같은 규칙으로 **창에 온전히 든\n * 것만** 센다(걸친 기간을 반만 세면 합이 창을 넘는다).\n *\n * 관측 구간(`observedFrom`·`observedTo`)을 함께 읽는다 — 값의 근거가 얼마나 덮였는지가 그 둘에 있다.\n */\n const generationPeriods = onceById(events.filter(e => e.eventType === ENERGY_EVENT.generationPeriod))\n .map(e => {\n const d = e.payload?.data ?? e.payload ?? {}\n return {\n equipmentId: String(d.equipmentId ?? ''),\n kWh: Number(d.kWh),\n startMs: Date.parse(String(d.periodStart ?? '')),\n endMs: Date.parse(String(d.periodEnd ?? '')),\n observedFromMs: Date.parse(String(d.observedFrom ?? '')),\n observedToMs: Date.parse(String(d.observedTo ?? ''))\n }\n })\n .filter(g => g.equipmentId && Number.isFinite(g.kWh) && Number.isFinite(g.startMs) && Number.isFinite(g.endMs))\n\n /*\n * 밖에서 마감되어 온 사용 구간 — 구간 마감과 같은 규칙으로 **창에 온전히 든 것만** 센다.\n */\n const usagePeriods = onceById(events.filter(e => e.eventType === ENERGY_EVENT.usagePeriod))\n .map(e => {\n const d = e.payload?.data ?? e.payload ?? {}\n return {\n meterId: String(d.meterId ?? ''),\n kWh: Number(d.kWh),\n startMs: Date.parse(String(d.from ?? '')),\n endMs: Date.parse(String(d.to ?? '')),\n unitPrice: Number(d.unitPrice),\n maxKW: Number(d.maxKW),\n currency: d.currency ? String(d.currency) : undefined,\n basis: d.basis ? String(d.basis) : 'unstated'\n }\n })\n .filter(u => u.meterId && Number.isFinite(u.kWh) && Number.isFinite(u.startMs) && Number.isFinite(u.endMs))\n\n /* 청구서 — 창에 온전히 든 것 중 가장 늦게 끝난 것 하나. */\n const bills = onceById(events.filter(e => e.eventType === ENERGY_EVENT.bill))\n .map(e => {\n const d = e.payload?.data ?? e.payload ?? {}\n return {\n from: String(d.from ?? ''),\n to: String(d.to ?? ''),\n startMs: Date.parse(String(d.from ?? '')),\n endMs: Date.parse(String(d.to ?? '')),\n total: Number(d.total),\n energyCharge: Number(d.energyCharge),\n demandCharge: Number(d.demandCharge),\n currency: d.currency ? String(d.currency) : undefined,\n billingDemandKW: Number(d.billingDemandKW)\n }\n })\n .filter(b => Number.isFinite(b.startMs) && Number.isFinite(b.endMs))\n\n /* 발전 단가 — 기간마다 그때의 값이다(날마다 바뀐다). */\n const generationPrices = onceById(events.filter(e => e.eventType === ENERGY_EVENT.generationPrice))\n .map(e => {\n const d = e.payload?.data ?? e.payload ?? {}\n return {\n startMs: Date.parse(String(d.from ?? '')),\n endMs: Date.parse(String(d.to ?? '')),\n unitPrice: Number(d.unitPrice),\n currency: String(d.currency ?? '')\n }\n })\n .filter(p => Number.isFinite(p.startMs) && Number.isFinite(p.endMs) && Number.isFinite(p.unitPrice) && p.currency)\n\n const leads: number[] = []\n const works: number[] = []\n const waits: number[] = []\n const busyByResource = new Map<string, number>()\n /* 창 안에 완료된 건들의 **기록** — 통계를 낸 뒤에도 버리지 않는다. 관점 축은 같은 기록을 다시\n * 묶는 것일 뿐이므로, 기록을 남기면 축을 하나 더 붙이는 값이 거의 0 이 된다. */\n const records: CompletionRecord[] = []\n let tasksInWindow = 0\n let unpairedLead = 0\n let unpairedWork = 0\n\n for (const t of tasks) {\n const completed = t.completedMs\n if (completed === undefined) continue // 아직 완료되지 않은 작업 — 성과로 세지 않는다\n if (completed < window.fromMs || completed > window.toMs) continue // 성과는 완료 시점으로 센다\n tasksInWindow++\n const c = t.createdMs\n const s = t.startedMs\n const rec: CompletionRecord = { taskId: t.taskId, at: completed, facets: t.facets }\n const resource = rec.facets.resource\n if (c !== undefined && completed >= c) leads.push((rec.leadMs = completed - c))\n else unpairedLead++\n if (s !== undefined && completed >= s) {\n const busy = completed - s\n works.push((rec.workMs = busy))\n if (resource) busyByResource.set(resource, (busyByResource.get(resource) ?? 0) + busy)\n } else {\n unpairedWork++\n }\n if (c !== undefined && s !== undefined && s >= c) waits.push((rec.waitMs = s - c))\n records.push(rec)\n }\n\n const span = Math.max(1, window.toMs - window.fromMs)\n const utilization = [...busyByResource.entries()]\n .map(([resourceId, busyMs]) => ({ resourceId, busyMs, ratio: Math.min(1, busyMs / span) }))\n .sort((a, b) => b.busyMs - a.busyMs)\n\n return {\n window: { fromMs: window.fromMs, toMs: window.toMs },\n throughput: { tasks: tasksInWindow, orders: completedOrders },\n /* 발전만 있는 현장(계량 없는 발전소)도 답이 나와야 한다 — 둘 중 하나만 있어도 블록을 낸다. */\n ...(demandWindows.length || generationPeriods.length || usagePeriods.length || bills.length\n ? (() => {\n const e = energyOfWindows(demandWindows, { startMs: window.fromMs, endMs: window.toMs })\n const usage = usageOfPeriods(usagePeriods, { startMs: window.fromMs, endMs: window.toMs })\n /*\n * 어느 회계를 쓸지 정한다 — **더하지 않는다.** 계량 표본에서 우리가 마감한 값이 있으면 그것을\n * 쓰고(강한 근거), 없으면 밖에서 마감되어 온 값을 쓴다. 무엇을 썼는지 함께 낸다.\n */\n const usedKWh = e.kWh !== undefined ? e.kWh : usage?.kWh\n const kWhBasis = e.kWh !== undefined ? 'demand-window' : usage ? 'usage-period' : undefined\n /* 분모는 **완료 오더**다(대당 전력). 창에 완료가 없으면 커널 규칙이 「정의되지 않음」으로 답한다. */\n const i = energyIntensity({\n kWh: usedKWh,\n energyWindow: e.window,\n denominator: { kind: 'output', value: completedOrders, window: e.window }\n })\n /*\n * 요금은 **구간마다 제 단가로** 매긴 값이 있으면 그것을 쓴다. 단일 단가로 접으면 시간대마다\n * 다른 단가가 사라지고, 「피크를 깎아 얼마를 아끼나」가 그 차이 위에 선다.\n */\n /* 요금적용전력은 청구서가 먼저다 — 선언은 청구가 오지 않는 현장의 몫이다. */\n /* 이 창의 최고 수요 — 사용 구간이 실어 온 값 중 가장 큰 것(원본 계량기가 잰 값이다). */\n const peaks = usagePeriods\n .filter(u => u.startMs >= window.fromMs && u.endMs <= window.toMs && Number.isFinite(u.maxKW))\n .map(u => ({ kW: u.maxKW as number, toMs: u.endMs }))\n const observedPeak = peaks.length ? peaks.reduce((a, x) => (x.kW > a.kW ? x : a)) : undefined\n const billingDemand = billingDemandOf(\n bills.filter(b => Number.isFinite(b.billingDemandKW)).map(b => ({ endMs: b.endMs, billingDemandKW: b.billingDemandKW })),\n options.tariffBasis,\n options.lastBill,\n options.tariff?.billingDemandKW,\n observedPeak\n )\n const cost = options.tariff\n ? electricityCost({\n kWh: usedKWh,\n peakKW: e.peakKW,\n tariff: { ...options.tariff, ...(billingDemand ? { billingDemandKW: billingDemand.kW } : {}) },\n ...(usage?.charge !== undefined ? { energyChargeFromPeriods: usage.charge } : {})\n })\n : undefined\n return {\n energy: {\n ...(usedKWh !== undefined ? { kWh: usedKWh } : {}),\n ...(kWhBasis ? { kWhBasis } : {}),\n ...(billingDemand ? { billingDemand } : {}),\n ...(usage ? { usage } : {}),\n /* 이 창의 최대수요 — 계약 대비 판정이 보는 수다(전력량과 같은 구간 집합에서 왔다). */\n ...(e.peakKW !== undefined ? { peakKW: e.peakKW, peakStartMs: e.peakStartMs } : {}),\n /* 깎인 양은 **같은 구간의 두 수**의 차이다 — 여기서 한 번만 계산해 두 곳이 갈라지지 않게. */\n ...(e.peakGrossKW !== undefined && e.peakKW !== undefined\n ? { peakGrossKW: e.peakGrossKW, ...(e.peakGrossKW > e.peakKW ? { shavedKW: e.peakGrossKW - e.peakKW } : {}) }\n : {}),\n basis: e.basis,\n counted: e.counted,\n skipped: e.skipped,\n intensity:\n i.value === null\n ? { value: null, reason: (i as any).reason }\n : { value: i.value, unit: (i as any).unit },\n ...(measuredFeeders(demandWindows, { startMs: window.fromMs, endMs: window.toMs }, e.kWh, options.meterFeeder) ??\n feederShares(e.kWh, utilization, options.resourceFeeder)),\n /* 금액은 같은 창의 전력량·최대수요에서 나온다 — 창이 하나뿐이라 어긋날 수 없다. */\n ...(cost ? { cost } : {}),\n ...generatedOfPeriods(\n generationPeriods,\n { startMs: window.fromMs, endMs: window.toMs },\n options.generationRated,\n /* 창 안의 단가 사실이 먼저이고, 없으면 이어받은 마지막 단가를 쓴다. */\n generationPrices.length\n ? generationPrices\n : options.generationPrice\n ? [\n {\n startMs: Date.parse(options.generationPrice.from),\n endMs: Date.parse(options.generationPrice.to),\n unitPrice: options.generationPrice.unitPrice,\n currency: options.generationPrice.currency\n }\n ]\n : []\n ),\n /* 청구서는 사실이다 — 우리 계산을 덮지 않고 나란히 둔다(§`billOfWindow`). */\n ...(() => {\n const b = billOfWindow(bills, { startMs: window.fromMs, endMs: window.toMs }, cost as any)\n return b ? { bill: b } : {}\n })()\n }\n }\n })()\n : {}),\n leadTime: stats(leads),\n workTime: stats(works),\n waitTime: stats(waits),\n utilization,\n unpaired: { leadTime: unpairedLead, workTime: unpairedWork },\n /* 못 읽은 오더 상태 낱말 — 비면 빈 객체다(§`orderStatusNotCounted`). */\n orderStatusNotCounted: Object.fromEntries(orderStatusNotCounted),\n eventsSeen: seen,\n eventsInWindow: inWindow,\n ...(options.groupBy ? { groups: groupRecords(records, options) } : {})\n }\n}\n\n/**\n * 관점 × 비교 교차 — **\"구역별로 어제 대비 어떤가\"**.\n *\n * 축(무엇을)과 비교(언제 대비)를 따로 보면 사용자가 머릿속에서 두 표를 대조해야 한다. 운영에서 가장\n * 값 있는 질문은 그 교차다: 전체는 비슷한데 **한 구역만 나빠진** 경우를 전체 숫자로는 절대 못 본다.\n *\n * 정직성 규율:\n * · 두 구간에 **모두 있는** 축만 delta 를 낸다.\n * · 지금만 있는 축은 `isNew`(이전에 없던 일) — delta 를 건수와 같게 적으면 \"늘었다\" 로 오독된다.\n * · **사라진 축**(전에 있었고 지금 없는 것)은 따로 돌려준다. 표에서 그냥 빠지면 **멈춘 구역이\n * 눈에 보이지 않는다** — 운영에서는 이것이 가장 중요한 신호일 수 있다.\n */\nexport interface CrossedGroup extends KpiGroup {\n /** 비교 구간의 건수(두 구간에 모두 있을 때만). */\n prevTasks?: number\n /** 현재 − 비교구간 건수(두 구간에 모두 있을 때만). */\n deltaTasks?: number\n /** 비교 구간 대비 작업시간 중앙값 차이(둘 다 측정된 때만). */\n deltaWorkP50Ms?: number\n /** 비교 구간에는 없던 축 — delta 대신 이 표식을 준다. */\n isNew?: boolean\n}\n\nexport function crossGroups(\n now: KpiGroup[],\n prev: KpiGroup[]\n): { items: CrossedGroup[]; disappeared: { key: string; tasks: number }[] } {\n const before = new Map(prev.map(g => [g.key, g]))\n const items: CrossedGroup[] = now.map(g => {\n const p = before.get(g.key)\n if (!p) return { ...g, isNew: true }\n return {\n ...g,\n prevTasks: p.tasks,\n deltaTasks: g.tasks - p.tasks,\n ...(deltaP50(g.workTime, p.workTime) !== undefined ? { deltaWorkP50Ms: deltaP50(g.workTime, p.workTime) } : {})\n }\n })\n const present = new Set(now.map(g => g.key))\n const disappeared = prev.filter(g => !present.has(g.key)).map(g => ({ key: g.key, tasks: g.tasks }))\n return { items, disappeared }\n}\n\n/* ── 시간 비교 ──────────────────────────────────────────────────────────────\n * 숫자 하나로는 좋아졌는지 나빠졌는지 알 수 없다(\"29초\" 가 개선인지 악화인지). 같은 길이의 창을 옮겨\n * 한 번 더 계산하면 되므로(폴드는 순수) 값싸다. 창 옮기기와 차이 계산은 계산 규칙이라 여기 있다 —\n * 조회 층에 두면 테스트가 DB 를 끌고 와야 한다.\n */\n\n/**\n * 비교 구간의 창 — 같은 **길이**로 옮긴다.\n *\n * previous: 바로 앞 구간(경계가 맞물리게 — 겹치거나 벌어지면 구간의 합이 어긋난다).\n * yesterday: 정확히 24시간 전 같은 자리. 시뮬 트윈의 클록이 실제 시각과 달라도 **차분은 정확**하다.\n */\nexport function shiftWindow(fromMs: number, toMs: number, basis: 'previous' | 'yesterday'): KpiWindow {\n const span = toMs - fromMs\n const back = basis === 'yesterday' ? 24 * 60 * 60_000 : span\n return { fromMs: fromMs - back, toMs: toMs - back }\n}\n\n/**\n * 중앙값 차이(현재 − 비교구간) — **둘 다 측정된 것만** 낸다.\n *\n * 한쪽이 결측이면 차이는 **없는 것**이다(0 이 아니다). 0 으로 내면 \"변화 없음\" 이라는 거짓이 된다.\n * 좋고 나쁨은 판단하지 않는다 — 처리량은 클수록, 소요는 작을수록 좋다는 해석은 소비처의 몫이다.\n */\nexport function deltaP50(now: DurationStats, prev: DurationStats): number | undefined {\n return now.count > 0 && prev.count > 0 ? now.p50Ms - prev.p50Ms : undefined\n}\n\n/** 구간 하나의 최소 요약 — 추세를 그릴 정보만(전체 KPI 를 구간마다 실어 보내지 않는다). */\nexport interface KpiBucket {\n fromMs: number\n toMs: number\n tasks: number\n orders: number\n /** 그 구간의 작업시간 중앙값 — 추세에서 \"느려지고 있는가\" 를 보는 값. */\n workP50Ms: number\n}\n\n/**\n * 창을 N 등분해 구간별로 처리한다 — **같은 이벤트를 창만 바꿔** 다시 계산한다(조회 추가 없음).\n *\n * 마지막 구간은 나머지를 흡수한다(부동소수 나눗셈으로 끝이 밀리면 마지막 완료가 빠진다).\n * `count <= 1` 이면 쪼갤 것이 없으므로 빈 배열 — 호출부가 \"추세 없음\" 으로 다룬다.\n */\nexport function foldKpiBuckets(events: KpiEvent[], window: KpiWindow, count: number): KpiBucket[] {\n const n = Math.max(0, Math.min(48, Math.floor(count)))\n if (n <= 1) return []\n const step = (window.toMs - window.fromMs) / n\n const out: KpiBucket[] = []\n for (let i = 0; i < n; i++) {\n const fromMs = window.fromMs + step * i\n const toMs = i === n - 1 ? window.toMs : window.fromMs + step * (i + 1)\n const b = foldKpi(events, { fromMs, toMs })\n out.push({ fromMs, toMs, tasks: b.throughput.tasks, orders: b.throughput.orders, workP50Ms: b.workTime.p50Ms })\n }\n return out\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kpi-query.js","sourceRoot":"","sources":["../../server/engine/kpi-query.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 업무 KPI 조회 — **창 해소 + 저널 조회 + 폴드**를 한 곳에.\n *\n * 왜 한 곳인가: 처음에 GraphQL 질의와 AI 도구가 각각 같은 조회를 적었다. 그러면 창 규칙이 두 곳에서\n * 갈라지고(한쪽만 고치면 화면과 AI 가 다른 숫자를 말한다) 조회 상한·테넌트 조건도 복제된다.\n * 계산은 `kpi-fold`(순수)가 소유하고, 이 파일은 **무엇을 읽을지**만 정한다.\n *\n * ── 창의 기준 시각은 \"트윈의 시계\" 다 ──────────────────────────────────────\n * 시뮬 트윈은 자기 클록으로 산다(예: 저널의 eventTime 이 2026-01-01T00:25Z 인데 실제 지금은 7월).\n * 그런 트윈에 \"최근 1시간\" 을 **실제 시각**으로 물으면 그 구간에는 아무 기록이 없어 항상\n * \"측정된 기록 없음\" 이 된다 — 폴드는 정상인데 답이 쓸모없다.\n *\n * 그래서 호출부가 끝 시각을 주지 않으면 **그 트윈의 마지막 기록 시각**을 끝으로 잡는다. 커널의 시각\n * 상수를 앱에서 재현하지 않고(무방언), 라이브 트윈에서도 자연스럽다(마지막 기록 ≈ 지금).\n * 어떤 기준을 썼는지는 결과의 `window.basis` 로 밝힌다 — 숨기면 사용자가 숫자를 오해한다.\n */\nimport { twinWarn } from './log.js'\nimport { mergeStreams } from '../service/twin-journal/event-type-split-shape.js'\nimport { hydratedDate } from './read-time.js'\n/* 시간대 이름을 순간마다 푸는 함수 — engine 이 service 를 들이는 것은 이 파일들의 관행이다(§`twin-engine.ts`). */\nimport { utcOffsetOf } from '../service/reference/reference-master.js'\nimport { Between, In } from 'typeorm'\n\nimport { type TariffDeclaration } from '@operato/twin-kernel'\nimport { hierarchyOf, levelOfLocationType, electricalUpstreamOf, EMS_LOCATION_TYPES, EMS_PROPERTY } from '@operato/ops-contract'\n\nimport { getReadRepository, getRepository } from '@things-factory/shell'\n\nimport { TwinEvent } from '../service/twin-event/twin-event.js'\nimport { TwinInstance } from '../service/twin-instance/twin-instance.js'\nimport { TwinArea } from '../service/twin-space/twin-area.js'\nimport { TwinSpace } from '../service/twin-space/twin-space.js'\nimport { TwinTarget } from '../service/twin-target/twin-target.js'\nimport { TWIN_METRIC, judgeTargets, judgeGroupTargets, scopedTargets, type ScopedTargets, type TargetVerdict, type TwinMetricId } from './kpi-target.js'\nimport { TwinEngine } from './twin-engine.js'\nimport { buildEnergyTopology, type EnergyTopology } from './energy-topology.js'\nimport {\n crossGroups,\n deltaP50,\n foldKpi,\n foldKpiBuckets,\n shiftWindow,\n type CrossedGroup,\n type KpiEvent,\n type KpiGroupBy,\n type KpiResult\n} from './kpi-fold.js'\nimport { MAX_BASELINE_PERIODS, baselineWindows, summarizeBaseline, type BaselineStride, type MetricBaseline } from './kpi-baseline.js'\n\n/**\n * 업무 전이만 읽는다 — 설비 상태(equipment.status)는 OEE 누적기의 몫이고 여기선 쓰지 않는다.\n *\n * **에너지 구간 마감**은 함께 읽는다(2026-08-14): 원단위는 분자(전력량)와 분모(산출)를 **같은 창**에서\n * 나눈 값이라, 두 사실이 한 폴드에 들어와야 창이 어긋날 수 없다. 표본(`energy.measured`)은 읽지 않는다 —\n * 저널에 없고(§4.1 인제스트 계약) 여기서 필요한 것은 마감된 구간뿐이다.\n */\nconst BUSINESS_EVENTS = ['task.status', 'order.status', 'energy.demand.window', 'energy.generation.period', 'energy.usage.period', 'energy.bill', 'energy.generation.price']\n\n/** 한 번에 계산할 이벤트 상한 — 큰 구간이 서버를 붙잡지 않게. */\nconst MAX_EVENTS = 20000\n\n/**\n * 종류를 나눠 물을 때 만들 갈래 수의 상한 — 대상 × 종류.\n *\n * 갈래마다 상한(`MAX_EVENTS`)까지 받아야 합친 결과가 정확하다(§`mergeStreams`). 그래서 갈래가 늘면\n * 메모리가 그만큼 든다. 셋(종류) × 둘(트윈)까지는 최악에 12만 행이고, 그 이상은 예전 한 질의로 간다 —\n * 느리지만 답은 같다.\n */\n/*\n * ── 나눠 물을 수 있는 갈래의 최대 (2026-08-30 실측) ──────────────────────────\n *\n * 6 이었다. 그런데 **한 현장에 트윈이 여럿인 것이 이 제품의 정상**이다(창고·공정·야드가 한 현실을\n * 각각 본다). 트윈 3개 × 종류 6가지 = 18 이라 조건에 안 걸렸고, 한 질의로 떨어졌다.\n *\n * 같은 창·같은 행으로 두 모양을 쟀다(hatio-us, order.status 345만 건이 있는 하루):\n *\n * 한 질의(종류 IN + 시각순) ix_twin_event_1 로 그 날 전체를 걷는다 25.987초 · 20,000건(잘림)\n * 갈래 하나(종류 정확일치) ix_twin_event_7 이 순서까지 준다 0.286초\n *\n * 91배다. 그리고 25초를 기다린 끝에 나오는 값이 **잘린 값**이다 — 사용자는 그 사실을 25초 뒤에 본다.\n *\n * ── 늘리는 대가 ────────────────────────────────────────────────────────────\n * 갈래마다 상한까지 받아야 합칠 때 정확하므로(§`mergeStreams`), 갈래가 많으면 그만큼 든다.\n * 최악은 `갈래 수 × MAX_EVENTS` 행이다. 다만 그 최악은 **모든 갈래가 창을 가득 채울 때**이고,\n * 그때는 어차피 결과가 잘린다(그 사실은 `capped` 로 나간다).\n *\n * 32 는 트윈 4개 × 종류 8가지를 덮는다. 어휘가 더 늘면 이 수를 다시 재고 정한다.\n */\nconst KPI_MAX_SPLIT = 32\n\nexport interface TwinKpiInput {\n domainId: string\n /** 한 트윈. `spaceId` 와 둘 중 하나는 있어야 한다. */\n instanceId?: string\n /**\n * 한 **공간** — 그 공간에서 가동 중인 트윈 전부를 **한 번에 계산한다**.\n *\n * 트윈별로 계산해 평균을 다시 평균하면 분위수가 거짓이 된다(p90 의 평균은 p90 이 아니다).\n * 그래서 이벤트를 모아 **한 번** 계산한다. 사용자는 \"부산 창고\" 를 보고, 그 안에 트윈이 몇 개든\n * 합쳐서 본다(헤더 트윈 셀렉터를 없앤 결정과 같은 규율).\n */\n spaceId?: string\n /** 창의 끝(ISO). 미지정이면 **그 트윈의 마지막 기록 시각**. */\n toTime?: string\n /** 창의 시작(ISO). 미지정이면 끝에서 windowMinutes 만큼 뒤로. */\n fromTime?: string\n /** 창 길이(분). fromTime 이 있으면 무시. 기본 60, 1~1440. */\n windowMinutes?: number\n /** 시작 이벤트를 찾기 위해 창보다 앞으로 더 읽는 시간(분). 기본 60, 0~1440. */\n lookbackMinutes?: number\n /**\n * 창을 N 등분해 **구간별 값도** 함께 낸다(추세용). 1~48, 기본 0(쪼개지 않음).\n * 조회는 한 번이고 계산만 N 번 반복한다 — 폴드가 순수하므로 값싸다.\n */\n buckets?: number\n /**\n * 같은 사실을 **다른 관점으로** 쪼갠다: 자원별·작업종류별·도착지점별·오더별·구역별.\n *\n * 고정된 한 렌즈만 낼 수 있으면 \"구역별로는 어떤가\" 라는 질문에 화면도 AI 도 답할 수 없다.\n * 축은 조회 인자 하나이고, 계산는 순수 함수라 축을 하나 더 보는 값이 거의 없다.\n */\n groupBy?: KpiGroupBy\n /** 축의 상한(기본 20, 최대 100). 넘치면 잘라내고 잘린 수를 함께 알린다. */\n groupLimit?: number\n /**\n * **무엇과 비교할 것인가** — 같은 길이의 앞 구간(previous)이나 하루 전 같은 시간(yesterday).\n *\n * 숫자 하나로는 좋아졌는지 나빠졌는지 알 수 없다(\"29초\" 가 개선인지 악화인지). 창만 옮겨 한 번 더\n * 계산하면 되므로(폴드는 순수) 값싸다. 비교 구간에 기록이 없으면 **delta 를 만들지 않는다** —\n * 없는 것을 0% 로 그리면 \"변화 없음\" 이라는 거짓이 된다.\n */\n compareTo?: 'previous' | 'yesterday'\n /**\n * **여러 구간 동시 비교** — 최근 N 구간의 같은 시간대를 함께 계산해 **분포**와 그 안의 자리를 낸다.\n *\n * `compareTo` 는 한 구간만 봐서 판단이 \"어제보다 나아졌다\" 에서 멈춘다 — 어제도 나빴으면 쓸모가 없다.\n * 최근 며칠의 같은 시간대를 보면 목표 없이도 \"지난 7일 중 가장 낮다\" 를 말할 수 있다.\n *\n * `stride: 'week'` 는 같은 요일을 본다(주말·평일이 섞이지 않게). `periods` 는 1~14\n * (창마다 조회가 한 번 더 붙는다 — 값을 공짜로 말하지 않는다).\n *\n * `compareTo` 와 함께 쓸 수 있다: 다른 질문이기 때문이다(직전 한 구간 vs 최근 분포).\n */\n baseline?: { periods: number; stride?: BaselineStride }\n}\n\nexport interface TwinKpiOutput extends Omit<Partial<KpiResult>, 'window'> {\n /** 사람이 읽는 창(ISO) + 기준. 폴드의 raw 창(ms)은 여기에 흡수한다. */\n window: {\n fromTime: string\n toTime: string\n minutes: number\n /** 끝 시각을 어디서 얻었나 — 'given'(호출부 지정) | 'latest-event'(트윈의 마지막 기록). */\n basis: 'given' | 'latest-event'\n }\n /**\n * 조회가 **상한에 걸렸는가**(구간이 너무 커서 일부만 읽었을 수 있다).\n *\n * true 면 이 구간의 숫자는 **하한**이다 — 실제로는 더 있었을 수 있다. 알리지 않고 잘라내면 숫자가 알리지 않고\n * 작아지고 사용자는 그것을 성과 하락으로 읽는다. 구간을 좁혀 다시 물어야 한다.\n */\n eventsCapped?: boolean\n /**\n * 이 구간에 **측정할 기록이 있었는가**. false 는 \"처리량 0\" 이 아니라 **\"기록이 없다\"** 는 뜻이다 —\n * 두 상황은 완전히 다르고, 섞으면 사용자가 잘못된 결론을 낸다.\n */\n measured: boolean\n note?: string\n /** 무엇을 집계했나 — 공간이면 합쳐 계산한 트윈 목록을 밝힌다(숫자의 출처를 숨기지 않는다). */\n /**\n * 무엇을 집계했나 — 공간이면 합쳐 계산한 트윈 목록.\n *\n * `restartPolicies` 는 **이 숫자가 무엇의 성과인지**를 말한다: mirror(실제 시스템을 반영) ·\n * `resume`(이어지는 가상 세계) · `reset`(재현 실험 사본). 같은 \"345건\" 이 현실의 실적일 수도, 시뮬레이션의\n * 산출일 수도 있다 — 그 구별 없이 숫자만 보여주면 사용자가 시뮬 결과를 실적으로 착각한다.\n */\n scope: { spaceId?: string; instanceIds: string[]; restartPolicies: string[] }\n /** 구간별 값(요청했을 때만) — 추세를 그리기 위한 최소 정보. */\n buckets?: { fromTime: string; toTime: string; tasks: number; orders: number; workP50Ms: number }[]\n /**\n * **목표 대비 판정** — 숫자를 판단으로 바꾸는 값(§kpi-target).\n *\n * 목표가 걸린 지표만 들어온다. 목표가 없거나 그 구간에 값이 없는 지표는 **여기 나타나지 않는다** —\n * 전부 \"목표 없음\" 으로 채우면 화면이 회색 노이즈가 되고, 없는 값을 0 으로 놓으면 \"목표 크게 미달\"\n * 이라는 없는 사실이 만들어진다.\n */\n targets?: TargetVerdict[]\n /**\n * 비교 구간(요청했을 때만) — 같은 규칙·같은 길이로 계산한 앞 구간과의 차이.\n *\n * `measured:false` 면 그 구간에 기록이 없다는 뜻이고 `delta` 는 없다(0 이 아니다).\n * delta 는 **현재 − 비교구간** 이다. 좋고 나쁨은 판단하지 않는다 — 처리량은 클수록 좋고 소요는\n * 작을수록 좋다는 해석은 소비처(화면·AI)의 몫이고, 계산 층이 판단을 섞으면 목표 없이 판정하게 된다.\n */\n comparison?: {\n basis: 'previous' | 'yesterday'\n window: { fromTime: string; toTime: string }\n measured: boolean\n throughput?: { tasks: number; orders: number }\n workTime?: KpiResult['workTime']\n leadTime?: KpiResult['leadTime']\n waitTime?: KpiResult['waitTime']\n delta?: { tasks: number; orders: number; workP50Ms?: number; leadP50Ms?: number; waitP50Ms?: number }\n }\n /**\n * **여러 구간 분포**(요청했을 때만) — 지표별로 최근 N 구간의 분포와 지금의 자리.\n *\n * `samples` 는 **요청한 창 전부**다(기록 없는 창도 `measured:false` 로 남는다) — 빼고 말하면\n * \"지난 7일\" 이 실제로는 3일이었다는 것을 아무도 모른다. 자리를 말할 수 없으면 `noPosition` 이\n * 이유를 밝힌다(표본 부족·현재 값 없음). 방향은 지표의 성질이라 `standing` 이 그것을 적용한 읽기다.\n */\n baseline?: {\n stride: BaselineStride\n /** 요청한 과거 구간 수(상한 적용 후). */\n periods: number\n metrics: MetricBaseline[]\n /** 과거 창 조회 중 하나라도 상한에 걸렸나 — 그 창의 값은 하한이라 분포가 낮게 보일 수 있다. */\n capped?: boolean\n }\n /**\n * 관점별 값(요청했을 때만). `key` 는 식별자, `label` 은 사람이 읽는 이름(있을 때만 — 구역은 이름이\n * 따로 저장되어 있다). `truncated` 는 상한 때문에 빠진 축의 수다.\n */\n groups?: {\n by: KpiGroupBy\n items: (KpiGroupOut & { label?: string })[]\n /**\n * 비교 구간에는 일이 있었는데 **지금은 없는** 축. 표에서 그냥 빠지면 멈춘 구역·대기 중인 자원이\n * 눈에 보이지 않는다 — 운영에서는 이것이 가장 중요한 신호일 수 있다.\n */\n disappeared?: { key: string; label?: string; tasks: number }[]\n /** 상한 때문에 빠진 축의 수와, 그 축들이 안고 있던 건수(둘 다 알려야 꼬리를 놓치지 않는다). */\n truncated?: number\n truncatedTasks?: number\n note?: string\n }\n}\n\n/** 축 하나의 값 — 폴드의 KpiGroup 과 같은 모양(시각 변환이 없어 그대로 통과한다). */\ntype KpiGroupOut = NonNullable<KpiResult['groups']>['items'][number] &\n Partial<Pick<CrossedGroup, 'prevTasks' | 'deltaTasks' | 'deltaWorkP50Ms' | 'isNew'>>\n\n/**\n * 노드 → 구역 지도. **저널에는 구역이 없다**(노드까지만 있다) — 모델 정의의 `locations[].parentId` 가\n * 마스터 계층에서 온 소속이다. 기동하지 않은 트윈도 모델은 남아 있어 이 경로가 가장 튼튼하다.\n *\n * **한 홉만 보면 안 된다.** `parentId` 는 구역일 수도 있고 중간 단(라인·셀)일 수도 있다. 라인이 끼면\n * 한 홉짜리 코드는 \"구역이 LINE-1\" 이라고 답하고, 그 id 는 구역 목록에 없으므로 그 아래 스테이션이\n * **집계에서 오류 없이 빠진다**(에러 없이 틀린 숫자). 그래서 커널 `hierarchyOf` 로 사슬을 끝까지 걷는다 —\n * 걷는 규칙은 커널에 한 벌뿐이고 소비처는 그것만 부른다.\n */\n/**\n * 교대 축의 재료 — **교대 선언과 현장의 시각 기준.**\n *\n * 저널에는 시각만 있고 교대가 없다(일부러 그렇게 뒀다 — 교대는 파생이라 찍어 두면 나중에 교대 시간을\n * 고쳤을 때 과거가 옛 구분으로 굳는다). 그래서 폴드가 계산할 재료를 여기서 모은다.\n *\n * 선언의 출처는 **모델의 설비**다. 여러 설비가 서로 다른 교대를 가질 수 있지만, 축은 하나여야 하므로\n * **가장 많이 쓰이는 선언 한 벌**을 쓴다 — 3교대 공장은 전 설비가 같은 교대이므로 이것이 곧 정답이고,\n * 자원마다 교대가 다른 현장에서는 이 축이 거칠다는 뜻이다(그 사실을 여기 적어 둔다).\n *\n * 시각 기준은 모델에 실려 있다(공간이 갖는 값을 기동 시 호스트가 풀어 넣는다). 없으면 UTC 로 읽고,\n * 그 기본값은 커널 계약에 밝혀져 있다.\n *\n * **이름이 있으면 이름을 쓴다**(`timezone`). 접기는 지난 구간을 훑으므로 고정 오프셋으로는 일광절약시간\n * 전환을 넘을 수 없다 — 1년치를 한 오프셋으로 읽으면 그 안의 전환 두 번이 사실을 다른 교대로 세게 하고,\n * 그 어긋남은 오류를 내지 않는다(2026-09-07).\n */\n/**\n * 이 범위에 걸린 운영 목표 — **좁은 선언이 넓은 선언을 덮는다.**\n *\n * 공간 전체 목표 위에 인스턴스 목표를 얹을 수 있어야 예외를 표현할 수 있다(\"이 라인만 다르다\").\n * 같은 지표에 둘 다 있으면 인스턴스가 이긴다.\n */\nasync function loadTargets(\n domainId: string,\n spaceId: string | undefined,\n instanceIds: string[],\n axis?: string\n): Promise<ScopedTargets> {\n /* 조회는 **가져오기만** 한다 — 어느 목표가 이기는지는 순수 함수(`scopedTargets`)가 정한다.\n 그 규칙이 DB 접근 뒤에 있으면 화면을 눌러 봐야만 확인된다(그리고 실제로 확인되지 않았다). */\n const rows = await getRepository(TwinTarget).find({ where: { domain: { id: domainId } } }).catch(() => [])\n return scopedTargets(rows, { spaceId, instanceIds, axis })\n}\n\nasync function shiftContext(\n domainId: string,\n instanceIds: string[]\n): Promise<{ shifts?: any[]; utcOffsetMinutes?: number; offsetAt?: (atMs: number) => number | undefined }> {\n if (!instanceIds.length) return {}\n const rows = await getRepository(TwinInstance).find({ where: { domain: { id: domainId }, instanceId: In(instanceIds) } })\n const tally = new Map<string, { decl: any[]; n: number }>()\n let utcOffsetMinutes: number | undefined\n let timezone: string | undefined\n for (const row of rows) {\n const model = row.model as any\n if (utcOffsetMinutes === undefined && typeof model?.utcOffsetMinutes === 'number') utcOffsetMinutes = model.utcOffsetMinutes\n if (timezone === undefined && typeof model?.timezone === 'string' && model.timezone) timezone = model.timezone\n for (const e of (model?.equipment ?? []) as any[]) {\n const decl = e?.workCalendar\n if (!Array.isArray(decl) || !decl.length) continue\n const key = JSON.stringify(decl)\n const hit = tally.get(key)\n if (hit) hit.n++\n else tally.set(key, { decl, n: 1 })\n }\n }\n let best: { decl: any[]; n: number } | undefined\n for (const v of tally.values()) if (!best || v.n > best.n) best = v\n /*\n * 이름이 있으면 **푸는 함수**로 바꿔 넘긴다. 접기가 레코드마다 부르므로 그 순간의 오프셋이 나오고,\n * 일광절약시간 전환이 든 구간도 맞게 세어진다(§`FoldOptions.offsetAt`).\n *\n * 수(`utcOffsetMinutes`)도 함께 남긴다 — 이름이 없는 공간에서는 그것이 유일한 답이고, 이름이 있어도\n * 그 순간을 못 푸는 경우의 마지막 기댈 자리다.\n */\n return {\n ...(best ? { shifts: best.decl } : {}),\n ...(utcOffsetMinutes !== undefined ? { utcOffsetMinutes } : {}),\n ...(timezone ? { offsetAt: (atMs: number) => utcOffsetOf(timezone, new Date(atMs)) } : {})\n }\n}\n\nasync function locationAreaMap(\n domainId: string,\n instanceIds: string[],\n spaceId?: string\n): Promise<{ locationArea: Record<string, string>; labels: Record<string, string> }> {\n const locationArea: Record<string, string> = {}\n if (instanceIds.length) {\n const rows = await getRepository(TwinInstance).find({\n where: { domain: { id: domainId }, instanceId: In(instanceIds) }\n })\n for (const row of rows) {\n const locations = (((row.model as any)?.locations ?? []) as any[]).filter(n => n?.id)\n if (!locations.length) continue\n /* 순환은 hierarchyOf 가 오류를 낸다 — 한 트윈의 잘못된 계층이 다른 트윈의 집계까지 죽이지 않게 여기서 가둔다. */\n let h\n try {\n /*\n * 단 해석기를 함께 넘긴다 — 자리가 `level` 을 적지 않으면 **타입이 답한다**(카탈로그 선언).\n * 이 집계는 지금 구역 롤업(`rollupOf`)만 쓰므로 결과가 달라지지 않는다. 그래도 여기서\n * 잇는다: 단으로 묻는 질의(`ancestorOfLevel`)가 생길 때 배선을 다시 찾지 않게 한다.\n */\n h = hierarchyOf({ locations }, t => levelOfLocationType(t, row.kind))\n } catch (e: any) {\n twinWarn(`[twin-kpi] \"${row.instanceId}\": location hierarchy is broken — area rollup skipped for this twin: ${e?.message}`)\n continue\n }\n for (const n of locations) {\n const area = h.rollupOf(String(n.id))\n if (area) locationArea[String(n.id)] = area // 구역 미상은 넣지 않는다 — 없는 구역을 만들지 않는다\n }\n }\n }\n\n const labels: Record<string, string> = {}\n const sid = spaceId\n if (sid) {\n const space = await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId: sid } })\n if (space) {\n const areas = await getRepository(TwinArea).find({\n where: { domain: { id: domainId } as any, space: { id: space.id } as any }\n })\n for (const a of areas) if (a.areaId && a.name) labels[a.areaId] = a.name\n }\n }\n return { locationArea, labels }\n}\n\n/**\n * 자원 → **배전 계통의 자리**(분기·수전) 지도와 그 자리의 이름.\n *\n * ── 무엇을 읽나 ─────────────────────────────────────────────────────────────\n * 에너지 트윈의 모델이 「이 설비가 어느 자리에 매달렸나」를 선언한다(`homeLocation`). 그 자리가 분기가\n * 아니라 하위 자리라면 부모를 타고 올라가 **전기 계통의 자리**를 찾는다 — 전기의 계통은 물리 구역과\n * 다른 나무이므로 구역 롤업(`locationAreaMap`)을 쓸 수 없다.\n *\n * ── 왜 에너지 트윈만 읽나 ───────────────────────────────────────────────────\n * 같은 설비가 공장 트윈에서는 자원(작업을 한다)이고 에너지 트윈에서는 부하(전기를 먹는다)다 — 사실 둘,\n * id 하나. 가동시간은 공장 트윈의 저널에서 오고, 전기 계통의 소속은 이 모델에서 온다. 그 둘을 id 로\n * 잇는 것이 이 지도의 일이다.\n *\n * 자리를 말하지 않는 설비는 **넣지 않는다** — 없는 소속을 만들면 그 몫이 엉뚱한 분기의 것이 된다.\n */\n/**\n * 선언된 **전기요금 단가** — 에너지 트윈의 자리 속성에서 읽는다(계약전력과 같은 자리).\n *\n * 여러 에너지 트윈이 한 현장에 있으면 **처음 선언된 것 하나**를 쓴다. 평균을 내거나 합치지 않는다:\n * 요금은 계약이고, 두 계약을 섞은 단가는 어느 계약도 아니다. 서로 다른 단가가 선언돼 있으면 그것을\n * 밝힌다(알리지 않고 하나를 고르면 왜 그 금액인지 아무도 설명할 수 없다).\n */\nasync function tariffOf(\n domainId: string,\n instanceIds: string[]\n): Promise<{ tariff?: TariffDeclaration; conflict?: boolean }> {\n if (!instanceIds.length) return {}\n const rows = await getRepository(TwinInstance).find({ where: { domain: { id: domainId }, instanceId: In(instanceIds) } })\n const found: TariffDeclaration[] = []\n for (const row of rows) {\n if (row.kind !== 'ems') continue\n for (const loc of ((row.model as any)?.locations ?? []) as any[]) {\n const props = new Map<string, any>(((loc?.properties ?? []) as any[]).map(p => [String(p?.id), p?.value]))\n const num = (id: string): number | undefined => {\n const v = Number(props.get(id))\n return Number.isFinite(v) && v > 0 ? v : undefined\n }\n const d = num(EMS_PROPERTY.demandChargePerKW)\n const e = num(EMS_PROPERTY.energyChargePerKWh)\n /* 요금적용전력 — 있으면 기본요금이 조건부 파생이 아니라 사실이 된다(§`electricityCost`). */\n const b = num(EMS_PROPERTY.billingDemandKW)\n const cur = props.get(EMS_PROPERTY.currency)\n if (d === undefined && e === undefined) continue\n found.push({\n ...(d !== undefined ? { demandChargePerKW: d } : {}),\n ...(e !== undefined ? { energyChargePerKWh: e } : {}),\n ...(b !== undefined ? { billingDemandKW: b } : {}),\n ...(cur ? { currency: String(cur) } : {})\n })\n }\n }\n if (!found.length) return {}\n const first = found[0]\n const same = found.every(\n t =>\n t.demandChargePerKW === first.demandChargePerKW &&\n t.energyChargePerKWh === first.energyChargePerKWh &&\n t.billingDemandKW === first.billingDemandKW &&\n t.currency === first.currency\n )\n return { tariff: first, ...(same ? {} : { conflict: true }) }\n}\n\nasync function resourceFeederMap(\n domainId: string,\n instanceIds: string[]\n): Promise<{ resourceFeeder: Record<string, string>; meterFeeder: Record<string, string>; labels: Record<string, string> }> {\n const resourceFeeder: Record<string, string> = {}\n /** 계량 지점 → 그것이 재는 분기. 있으면 그 분기 값은 **측정**이다(배분보다 강한 근거). */\n const meterFeeder: Record<string, string> = {}\n const labels: Record<string, string> = {}\n if (!instanceIds.length) return { resourceFeeder, meterFeeder, labels }\n\n const rows = await getRepository(TwinInstance).find({ where: { domain: { id: domainId }, instanceId: In(instanceIds) } })\n for (const row of rows) {\n if (row.kind !== 'ems') continue // 전기 계통을 선언하는 것은 에너지 트윈이다\n const model: any = row.model ?? {}\n const locations: any[] = (model.locations ?? []).filter((n: any) => n?.id)\n const byId = new Map(locations.map((n: any) => [String(n.id), n]))\n const electrical = new Set(EMS_LOCATION_TYPES as readonly string[])\n\n /**\n * 이 자리에서 **전기 상류**로 올라가 처음 만나는 계통의 자리. 없으면 undefined.\n *\n * 상류의 뜻은 커널이 읽는다(`electricalUpstreamOf`) — 공간 부모(공장·구역)를 따라 올라가면 전기적으로\n * 뜻이 없는 길을 걷게 되고, 옛 세대(분기의 `parentId` 에 수전)와 새 세대(`upstreamId`)가 섞여 있으므로\n * 그 판정을 여기서 다시 쓰면 두 벌이 된다.\n */\n const feederOf = (startId: string): string | undefined => {\n const seen = new Set<string>()\n let cur = byId.get(startId)\n while (cur && !seen.has(String(cur.id))) {\n seen.add(String(cur.id)) // 잘못된 모델의 순환이 여기서 멈춘다\n if (electrical.has(String(cur.type))) return String(cur.id)\n const up = electricalUpstreamOf(locations as any, String(cur.id))\n cur = up ? byId.get(up) : cur.parentId ? byId.get(String(cur.parentId)) : undefined\n }\n return undefined\n }\n\n for (const e of (model.equipment ?? []) as any[]) {\n const home = e?.homeLocation ?? e?.homeLocationId\n if (!e?.id || !home) continue\n /*\n * 계량기는 부하가 아니다 — 그것의 「몫」을 세면 총량이 이중으로 나뉜다. 대신 **어디를 재는지**를\n * 따로 모은다: 계량기가 분기에 있으면 그 분기 값은 배분이 아니라 **측정**이다(가장 강한 근거).\n */\n if (e.kind === 'meter') {\n const at = feederOf(String(home))\n /* 뿌리(수전)를 재는 계량기는 분기의 것이 아니다 — 그 값은 총량이고, 나머지는 뺄셈으로 나온다. */\n if (at && String(byId.get(at)?.type) !== 'incoming') meterFeeder[String(e.id)] = at\n continue\n }\n /* 차단기·태양광·축전지도 부하가 아니다. */\n if (e.kind === 'breaker' || e.kind === 'pv-array' || e.kind === 'battery') continue\n const feeder = feederOf(String(home))\n if (feeder) resourceFeeder[String(e.id)] = feeder\n }\n for (const n of locations) if (electrical.has(String(n.type)) && n.name) labels[String(n.id)] = String(n.name)\n }\n return { resourceFeeder, meterFeeder, labels }\n}\n\n/**\n * 이 공간의 에너지 트윈들의 계통도 — 모델을 읽어 트리로 처리한다.\n *\n * 여러 트윈이 한 공간에 있으면 **각 트윈의 뿌리를 나란히** 둔다(하나로 합치면 없는 결선을 그리게 된다).\n * 에너지 트윈이 없으면 만들지 않는다 — 빈 그림은 고장으로 읽힌다.\n */\n/**\n * 이 트윈들이 이어받고 있는 **마지막 청구서** — 요금적용전력의 우선 근거.\n *\n * 저널이 아니라 상태에서 읽는 이유: 청구는 한 달에 한 번이라 하루 창의 조회에는 걸리지 않는다.\n * 그런데 그 값은 **그 창에도 적용된다**(요금 제도가 그렇게 군다). 커널이 재기동에서도 이어받으므로\n * 상태가 그 자리다.\n *\n * 트윈이 여럿이면 가장 늦게 끝난 것을 쓴다 — 현장 하나에 청구가 여럿이면 그것은 다른 문제이고,\n * 여기서 골라 감추지 않는다.\n */\nfunction latestBillOf(domainId: string, instanceIds: string[]): { to: string; billingDemandKW?: number } | undefined {\n let best: { to: string; billingDemandKW?: number } | undefined\n for (const instanceId of instanceIds) {\n const bill = (TwinEngine.snapshot(domainId, instanceId) as any)?.energy?.lastBill\n if (!bill?.to) continue\n if (!best || Date.parse(bill.to) > Date.parse(best.to)) {\n best = { to: String(bill.to), ...(Number.isFinite(Number(bill.billingDemandKW)) ? { billingDemandKW: Number(bill.billingDemandKW) } : {}) }\n }\n }\n return best\n}\n\n/** 이 트윈들이 이어받고 있는 **발전 단가** — 창 안에 단가 사실이 없을 때 쓴다. */\nfunction latestGenerationPriceOf(\n domainId: string,\n instanceIds: string[]\n): { from: string; to: string; unitPrice: number; currency: string } | undefined {\n let best: { from: string; to: string; unitPrice: number; currency: string } | undefined\n for (const instanceId of instanceIds) {\n const p = (TwinEngine.snapshot(domainId, instanceId) as any)?.energy?.generationPrice\n if (!p?.from || !Number.isFinite(Number(p.unitPrice)) || !p.currency) continue\n if (!best || Date.parse(p.from) > Date.parse(best.from)) {\n best = { from: String(p.from), to: String(p.to), unitPrice: Number(p.unitPrice), currency: String(p.currency) }\n }\n }\n return best\n}\n\n/**\n * 이 트윈들의 **발전 정격** — 이용률의 분모.\n *\n * 커널이 고른다(설비 합 → 자리 합계, 교류 → 직류). 여러 트윈이면 합한다 — 한 현장의 발전은\n * 트윈마다 나뉘어 있어도 같은 현장의 발전이다. 근거가 갈리면 약한 쪽(직류·자리)으로 적는다.\n */\nfunction generationRatedOf(\n domainId: string,\n instanceIds: string[]\n): { kW: number; basis: 'ac' | 'dc'; from: 'equipment' | 'location' } | undefined {\n let kW = 0\n let basis: 'ac' | 'dc' = 'ac'\n let from: 'equipment' | 'location' = 'equipment'\n for (const instanceId of instanceIds) {\n /* 스냅샷에서 읽는다 — 커널이 고른 값이다. 여기서 모델을 다시 뒤지면 고르는 규칙이 두 벌이 된다. */\n const r = (TwinEngine.snapshot(domainId, instanceId) as any)?.energy?.generationRated\n if (!r || !(Number(r.kW) > 0)) continue\n kW += Number(r.kW)\n if (r.basis === 'dc') basis = 'dc'\n if (r.from === 'location') from = 'location'\n }\n return kW > 0 ? { kW, basis, from } : undefined\n}\n\n/** 이 트윈들이 이어받고 있는 **요금 기준** — 이 주기에 적용되는 값(청구서보다 먼저다). */\nfunction latestTariffBasisOf(domainId: string, instanceIds: string[]): { from: string; billingDemandKW?: number } | undefined {\n let best: { from: string; billingDemandKW?: number } | undefined\n for (const instanceId of instanceIds) {\n const b = (TwinEngine.snapshot(domainId, instanceId) as any)?.energy?.tariffBasis\n if (!b?.from) continue\n if (!best || Date.parse(b.from) > Date.parse(best.from)) {\n best = { from: String(b.from), ...(Number.isFinite(Number(b.billingDemandKW)) ? { billingDemandKW: Number(b.billingDemandKW) } : {}) }\n }\n }\n return best\n}\n\nasync function energyTopologyOf(\n domainId: string,\n instanceIds: string[],\n energy: { byFeeder?: { key: string; kWh: number; basis?: 'measured' | 'apportioned'; peakKW?: number }[] } | undefined\n): Promise<EnergyTopology | undefined> {\n if (!instanceIds.length) return undefined\n const rows = await getRepository(TwinInstance).find({ where: { domain: { id: domainId }, instanceId: In(instanceIds) } })\n const ems = rows.filter(r => r.kind === 'ems' && r.model)\n if (!ems.length) return undefined\n const merged: EnergyTopology = { roots: [], unplaced: [], unmetered: [], loadsOnIncoming: 0 }\n for (const row of ems) {\n /*\n * 지금의 설비 상태를 함께 넘긴다 — 발전·저장의 **지금 값**은 구간 폴드가 아니라 스냅샷이 안다.\n * 없으면(정지된 트윈) 구조만 그린다.\n */\n const snap = TwinEngine.snapshot(domainId, row.instanceId)\n const t = buildEnergyTopology(row.model, energy, snap?.equipment)\n merged.roots.push(...t.roots)\n merged.unplaced.push(...t.unplaced)\n merged.unmetered.push(...t.unmetered)\n merged.loadsOnIncoming += t.loadsOnIncoming\n }\n return merged.roots.length ? merged : undefined\n}\n\n/**\n * 이 대상들의 **마지막 업무 기록 시각**(ISO). 기록이 없으면 null.\n *\n * ── 두 가지를 함께 고쳤다 (2026-08-28 실측) ─────────────────────────────────\n * 예전 버전은 이랬다.\n *\n * findOne({ where: { domain: { id }, instanceId: In(…), eventType: In(셋) }, order: { revision: 'DESC' } })\n *\n * **① 관계로 물어 두 단계 질의가 된다.** 안쪽에 상한이 없어 조건에 맞는 행을 **전부 만든 뒤** 하나를\n * 고른다 — 그 트윈의 업무 사실이 97만 행이고 `payload` 까지 실려 온다. 이 파일의 다른 자리가 같은 것을\n * 실측해 적어 두었다(§`twinEvents`: 0.024초 대 12초). 서버 로그에 「느린 질의」로 찍혀 있던 것이\n * 이것이고, 현장 성과 화면의 7.2초가 여기서 나왔다.\n *\n * **② 「마지막 시각」을 원하면서 `revision` 으로 정렬했다.** 그 둘은 어긋난다 — 연결된 시스템을 되읽는\n * 트윈은 원본의 시각을 싣고 도착 순서로 리비전을 받으므로, 리비전이 가장 큰 행이 4월 사실일 수 있다\n * (같은 어긋남을 오늘 저널 목록에서도 고쳤다). 창의 끝을 그 값으로 잡으면 **창 자체가 틀린다.**\n *\n * 지금은 종류마다 `MAX(eventTime)` 을 묻는다. `(domain, instance, event_type, event_time)` 색인의\n * 끝을 집으므로 종류당 한 번이고, 셋 중 가장 늦은 것이 답이다.\n */\nasync function latestEventTime(domainId: string, instanceIds: string[]): Promise<string | null> {\n if (instanceIds.length === 0) return null\n const rows = await Promise.all(\n instanceIds.flatMap(instanceId =>\n BUSINESS_EVENTS.map(eventType =>\n getReadRepository(TwinEvent)\n .createQueryBuilder('e')\n .select('MAX(e.eventTime)', 'at')\n .where('e.domain = :domainId', { domainId })\n .andWhere('e.instanceId = :instanceId', { instanceId })\n .andWhere('e.eventType = :eventType', { eventType })\n .getRawOne<{ at: unknown }>()\n .catch(() => null)\n )\n )\n )\n /* 드라이버가 준 값은 시간대가 없을 수 있다 — 컬럼 변환을 지나게 한다(§`hydratedDate`). */\n const repo = getReadRepository(TwinEvent)\n let latest: Date | null = null\n for (const r of rows) {\n const at = hydratedDate(repo, 'eventTime', r?.at)\n if (at && (!latest || at.getTime() > latest.getTime())) latest = at\n }\n return latest ? latest.toISOString() : null\n}\n\n/**\n * 창 구간의 업무 이벤트 — 창보다 `lookbackMs` 만큼 앞까지 읽는다(완료된 작업의 시작이 창 밖일 수 있다).\n *\n * eventTime 은 날짜 컬럼이라 DB 가 시각으로 비교한다 — 예전 문자열 컬럼 시절의 \"UTC ISO 만 들어오면\n * 사전식 비교가 맞는다\" 는 전제가 필요 없다.\n */\nasync function fetchEvents(domainId: string, instanceIds: string[], fromMs: number, toMs: number, lookbackMs: number) {\n if (instanceIds.length === 0) return { events: [] as KpiEvent[], capped: false }\n /*\n * ── 필요한 세 컬럼만, 그리고 **읽기 연결로** (2026-08-22 실측) ─────────────\n * 이 읽기가 서버를 붙잡고 있었다. 실측(개발 DB 16.3GB · 저널 1,134만 행): 2만 행을 실어 오는 동안\n * 다른 가벼운 읽기가 **47.6초** 기다렸다. 로그에서 이 자리 뒤로 사용자·도메인 조회까지 30.9초로\n * 찍혔다 — TypeORM sqlite 드라이버는 연결을 하나만 들기 때문이다.\n *\n * **행 단위로 읽는 것 자체는 정당하다**: 이 폴드는 `p10·p50·p90` 을 낸다(§`kpi-fold`). 분위수는\n * 카운터로 만들 수 없고 트윈별 사전집계로도 만들 수 없다(p90 의 평균은 p90 이 아니다). 개별 값이\n * 있어야 한다.\n *\n * 정당하지 않은 것은 **읽는 방식**이었다. 세 가지를 고친다.\n *\n * ① **엔티티를 만들지 않는다.** 폴드가 쓰는 것은 `eventType`·`eventTime`·`payload` 뿐인데\n * `find()` 는 열여섯 컬럼을 실어 엔티티로 만들고 관계 로더를 지난다. CPU 프로파일에서\n * `RawSqlResultsToEntityTransformer` 와 `stringToSimpleJson` 이 상위였다.\n * ② **읽기 연결로 보낸다**(`getReadRepository`). 순수 읽기이므로 쓰기와 순서를 다툴 것이 없다.\n * 실측: 같은 2만 행 읽기에서 다른 읽기의 대기가 47.6초 → 2.8초.\n * ③ **정렬 축을 거르는 축과 맞춘다.** 여기가 이 자리의 진짜 비용이었다.\n *\n * ── `ORDER BY revision` 이 `LIMIT` 을 무력화했다 (실측) ────────────────────\n * 거르는 축은 `event_time`(창)이고 정렬 축은 `revision` 이었다. 그러면 sqlite 는 창에 맞는 행을\n * **전부** 찾아 `revision` 으로 **전부 정렬한 뒤** 상한을 적용한다 — 상한이 정렬 뒤에 걸리므로\n * 아무것도 줄여 주지 않는다. 실측이 그것을 그대로 보였다(인스턴스 하나가 375만 행):\n *\n * ORDER BY revision LIMIT 20000 → 32.2초 · LIMIT 5000 → 28.3초 · LIMIT 2000 → 28.2초\n * ORDER BY eventTime, revision LIMIT 20000 → 0.41초 (78배)\n *\n * 계획도 그렇게 말한다: 앞은 `USE TEMP B-TREE FOR ORDER BY`(전체 정렬), 뒤는\n * `USE TEMP B-TREE FOR LAST TERM OF ORDER BY` — 시각은 인덱스(`ix_twin_event_1`)에서 순서대로\n * 나오고 **동시각만** 임시 정렬한다. 즉 범위를 훑다가 상한에서 멈춘다.\n *\n * ── 순서의 뜻은 **완전히 같지는 않다** (측정해서 고친 문장) ────────────────\n * 처음에 「순서의 뜻은 같다」고 적었다. 재 보니 아니었다 — `revision` 순서와 `eventTime` 순서가\n * 어긋나는 자리가 실재한다(hatio-mx2: 426만 행 중 **7건** · rosarito-mes: 11만 중 8건). 미러는\n * 원본의 시각을 싣고 도착 순서로 리비전을 받으므로, 늦게 도착한 사건이 그 어긋남을 만든다.\n *\n * **그러나 폴드의 수치는 같다.** `foldTaskRecords`(커널 `task-fold.ts`)가 스트림 위치가 아니라\n * **시각으로** 판정한다:\n *\n * startedMs = Math.min(startedMs, at)\n * completedMs = Math.max(completedMs, at)\n *\n * `min`·`max` 이므로 행이 어떤 순서로 와도 답이 같다. 소요시간·분위수·처리량은 순서와 무관하다.\n *\n * 순서에 민감한 자리는 **하나**다: 그 폴드의 `location` 이 「마지막에 본 것」이다. 같은 밀리초에 같은\n * 작업의 자리가 두 번 바뀌면 어느 것이 남는지가 순서로 갈린다(라이브 미러가 같은 밀리초에 여러 행을\n * 보내는 경우다). 수치가 아니라 **표시되는 자리 하나**이고, 그래서 이 바꿈의 값이 그 위험보다 크다.\n *\n * `revision` 을 둘째 축으로 남기는 이유도 그것이다 — 동시각의 순서가 실행마다 달라지지 않게 한다.\n *\n * **상한을 낮추는 것으로는 고쳐지지 않는다**(위 숫자가 그것을 말한다). 정렬 축이 원인이다.\n *\n * 남는 비용을 감추지 않는다: `payload` 파싱은 여전히 메인 스레드 일이고 연결을 나눠도 나뉘지 않는다\n * (실측 평균 1.5초). 그것을 더 줄이려면 폴드가 payload 에서 무엇을 쓰는지 좁혀야 한다 — 별 건이다.\n */\n const from = new Date(fromMs - lookbackMs)\n const to = new Date(toMs)\n\n /**\n * 한 갈래를 묻는다 — **종류를 정확일치로** 준다. 그것이 이 고침의 요점이다.\n */\n const readOne = (instanceId: string, eventType: string) =>\n getReadRepository(TwinEvent)\n .createQueryBuilder('e')\n .select(['e.eventType AS \"eventType\"', 'e.eventTime AS \"eventTime\"', 'e.revision AS \"revision\"', 'e.payload AS \"payload\"'])\n .where('e.domain = :domainId', { domainId })\n .andWhere('e.instanceId = :instanceId', { instanceId })\n .andWhere('e.eventType = :eventType', { eventType })\n .andWhere('e.eventTime BETWEEN :from AND :to', { from, to })\n .orderBy('e.eventTime', 'ASC')\n .addOrderBy('e.revision', 'ASC')\n .limit(MAX_EVENTS)\n .getRawMany<{ eventType: string; eventTime: unknown; revision: unknown; payload: unknown }>()\n\n /*\n * ── **종류를 나눠 묻는다** (2026-08-28 실측) ────────────────────────────────\n *\n * 예전에는 `eventType IN (셋)` + 시각 범위 + 시각순이었다. 그 모양에서 sqlite 는 종류 색인을 버리고\n * 시각 색인으로 **구간 전체를 걸으며 행마다 종류를 확인한다.** 승화푸드(저널 3,140만행) 실측:\n *\n * 창 1시간 구간 안 0행 · 쓰는 행 0 40밀리초\n * 창 24시간 구간 안 254,185행 · 쓰는 행 0 3,776밀리초 ← 25만 행을 걸어 0건을 쓴다\n * 창 7일 구간 안 2,070,625행 · 34,143 173밀리초 ← 상한에 일찍 닿아 오히려 빠르다\n *\n * 창을 넓히면 빨라지는 것 자체가 신호였다. 종류를 정확일치로 물으면\n * `ix_twin_event_7 (domain, instance, event_type, event_time)` 이 범위까지 색인에서 준다.\n *\n * 나눌 수를 제한한다 — 갈래마다 상한까지 받아야 정확하므로(§`mergeStreams`) 갈래가 많으면 메모리가\n * 그만큼 든다. 넘으면 예전 한 질의로 간다(느리지만 답은 같다).\n */\n const pairs = instanceIds.length * BUSINESS_EVENTS.length\n let raw: { eventType: string; eventTime: unknown; revision: unknown; payload: unknown }[]\n let mergedCapped = false\n if (pairs && pairs <= KPI_MAX_SPLIT) {\n const streams = await Promise.all(\n instanceIds.flatMap(instanceId => BUSINESS_EVENTS.map(eventType => readOne(instanceId, eventType)))\n )\n const merged = mergeStreams(streams, MAX_EVENTS, (a, b) => {\n const at = String(a.eventTime ?? '').localeCompare(String(b.eventTime ?? ''))\n if (at !== 0) return at\n return Number(a.revision ?? 0) - Number(b.revision ?? 0)\n })\n if (merged.underfilled) {\n /* 있을 수 없는 일이다 — 갈래가 상한보다 많이 받아 왔다. 조용히 쓰지 않고 말한다. */\n twinWarn('[twin-kpi] a per-type stream returned more than the cap — the merge cannot be trusted; falling back')\n raw = []\n } else {\n raw = merged.items\n mergedCapped = merged.capped\n }\n } else {\n raw = await getReadRepository(TwinEvent)\n .createQueryBuilder('e')\n .select(['e.eventType AS \"eventType\"', 'e.eventTime AS \"eventTime\"', 'e.revision AS \"revision\"', 'e.payload AS \"payload\"'])\n .where('e.domain = :domainId', { domainId })\n .andWhere('e.instanceId IN (:...instanceIds)', { instanceIds })\n .andWhere('e.eventType IN (:...types)', { types: BUSINESS_EVENTS })\n .andWhere('e.eventTime BETWEEN :from AND :to', { from, to })\n .orderBy('e.eventTime', 'ASC')\n .addOrderBy('e.revision', 'ASC')\n .limit(MAX_EVENTS)\n .getRawMany<{ eventType: string; eventTime: unknown; revision: unknown; payload: unknown }>()\n mergedCapped = raw.length >= MAX_EVENTS\n }\n\n /* 상한에 정확히 닿으면 **더 있었을 수 있다** — 그 사실을 돌려준다. 알리지 않고 잘라내면 숫자가 알리지 않고\n * 작아지고, 사용자는 그것을 성과 하락으로 읽는다(가장 나쁜 종류의 거짓이다). */\n return {\n /* 폴드는 ISO 문자열 축으로 수행된다(kpi-fold) — 경계에서 한 번만 옮긴다. */\n events: raw.map(r => ({ eventType: r.eventType, eventTime: isoOf(r.eventTime), payload: jsonOf(r.payload) })),\n capped: mergedCapped\n }\n}\n\n/**\n * 원시 결과의 시각을 ISO 로 — **드라이버마다 모양이 다르다.**\n *\n * 엔티티 경로에서는 TypeORM 이 `Date` 로 바꿔 주지만, 원시 결과는 드라이버가 준 그대로다(sqlite 는\n * `'2026-08-07 11:00:00.000'`, pg 는 `Date`). 경계에서 한 번 정규화한다 — 폴드는 ISO 문자열만 안다.\n */\nfunction isoOf(v: unknown): string | undefined {\n if (v == null) return undefined\n if (v instanceof Date) return v.toISOString()\n const s = String(v)\n /* sqlite 의 공백 구분 형식을 ISO 로 — 그대로 `Date` 에 주면 시간대 해석이 갈린다. */\n const d = new Date(s.includes('T') ? s : s.replace(' ', 'T') + (s.endsWith('Z') ? '' : 'Z'))\n return Number.isNaN(d.getTime()) ? undefined : d.toISOString()\n}\n\n/**\n * 원시 결과의 `payload` 를 객체로 — **엔티티 경로의 `simple-json` 변환을 여기서 한다.**\n *\n * 깨진 JSON 은 **버리지 않고 비운다**: 그 사건이 있었다는 사실은 남고, 안을 읽을 수 없다는 것만\n * 폴드에 전해진다(폴드는 없는 필드를 건너뛴다). 던지면 창 하나가 통째로 사라진다.\n */\nfunction jsonOf(v: unknown): any {\n if (v == null) return undefined\n if (typeof v !== 'string') return v\n try {\n return JSON.parse(v)\n } catch {\n return undefined\n }\n}\n\n/**\n * 집계 대상 트윈 목록.\n *\n * 공간이 주어지면 그 공간의 **운영 목적** 트윈을 모은다 — 벤치 사본은 사용자가 보는 현장이 아니다.\n * 트윈이 지정되면 그것 하나. 둘 다 없으면 무엇을 재야 할지 알 수 없으므로 명시 실패한다.\n *\n * ── **「지금 도는가」로 걸러내지 않는다** (2026-08-28 실측) ──────────────────\n * 여기에 `running` 조건이 있었다. 그래서 멈춘 트윈은 현장으로 물었을 때 대상에서 빠졌고, 화면은\n * 「기록이 없습니다」라고 말했다 — **저널에는 있는데.**\n *\n * twinEventList(instanceId: \"ems-plant-1\") 1,792건\n * twinEventList(spaceId: \"ems-plant-1-site\") 0건 ← 같은 트윈, 같은 도메인\n *\n * 규모로 보면 이것은 예외가 아니라 기본값이었다: 등록된 트윈 30개 중 **도는 것이 2개, 멈춘 것이 28개**다.\n * 즉 현장으로 묻는 저널 조회가 대부분의 현장에서 거짓 「없다」를 냈다.\n *\n * 지운 이유는 두 가지다.\n *\n * ① **저널은 기록이고, 프로세스가 지금 도는지는 다른 축이다.** 창 안에 있는 사실은 사실이다.\n * 아래 줄이 트윈을 지정한 경우에 대해 이미 그 원칙을 적어 두었다(「정지·비등록이어도 저널은 남아\n * 있을 수 있다」). 같은 원칙이 현장 경로에만 적용되지 않고 있었다.\n * ② 옛 주석이 대는 이유(「벤치 사본은 사용자가 보는 현장이 아니다」)는 **벤치 제외만** 설명한다.\n * `running` 에는 근거가 적혀 있지 않았다.\n *\n * **바뀌는 것**: 현장 KPI 가 이제 멈춘 트윈의 지난 기록도 센다. 창 안의 사실을 세는 것이 그 지표의\n * 정의이므로 그것이 맞다. 「지금 도는 것만」이 필요한 화면이 생기면 그때 **이름 있는 축**으로 받는다 —\n * 여기에 숨겨 두면 두 물음이 한 칸을 쓴다.\n */\nasync function resolveTargets(input: TwinKpiInput): Promise<{ instanceIds: string[]; restartPolicies: string[] }> {\n if (!input.instanceId && !input.spaceId) throw new Error('either instanceId or spaceId is required')\n const list = await TwinEngine.list(input.domainId)\n const rows = input.instanceId\n ? list.filter((x: any) => x?.instanceId === input.instanceId)\n : list.filter((x: any) => x?.spaceId === input.spaceId && x.purpose !== 'bench' && x.instanceId)\n /* 목록에서 못 찾아도(정지·비등록) 지정된 트윈은 그대로 조회한다 — 저널은 남아 있을 수 있다. */\n const instanceIds = rows.length ? rows.map((x: any) => x.instanceId as string) : input.instanceId ? [input.instanceId] : []\n /* 컬럼이 NOT NULL 이라 'unknown' 으로 메울 일이 없다 — 비면 계약이 깨진 것이지 모르는 상태가 아니다. */\n const restartPolicies = [...new Set(rows.map((x: any) => String(x.restartPolicy)))]\n return { instanceIds, restartPolicies }\n}\n\n/**\n * 대상 해소 공개 창구 — **저널 목록 질의도 KPI 와 같은 규칙을 써야 한다.**\n *\n * 화면에서 \"이 공간\" 은 하나의 뜻이어야 한다. 성과 화면과 원장 화면이 각자 co-located 트윈을\n * 추리면 같은 공간을 보면서 다른 대상 집합을 세게 되고, 두 숫자가 어긋나는 이유를 아무도 설명하지 못한다.\n */\nexport async function resolveTwinTargets(domainId: string, instanceId?: string, spaceId?: string): Promise<string[]> {\n const { instanceIds } = await resolveTargets({ domainId, instanceId, spaceId } as TwinKpiInput)\n return instanceIds\n}\n\n/**\n * 시간창 업무 KPI. 창 해소 → 저널 조회 → 순수 폴드.\n *\n * 테넌트 조건은 호출부가 준 `domainId` 로만 좁힌다(클라이언트 값을 그대로 쓰지 않는 것은 호출부 책임).\n */\nexport async function computeTwinKpi(input: TwinKpiInput): Promise<TwinKpiOutput> {\n const minutes = Math.max(1, Math.min(24 * 60, Number(input.windowMinutes) || 60))\n const lookbackMs = Math.max(0, Math.min(24 * 60, input.lookbackMinutes ?? 60)) * 60_000\n\n const { instanceIds, restartPolicies } = await resolveTargets(input)\n const scope = { spaceId: input.spaceId, instanceIds, restartPolicies }\n\n /* 끝 시각 — 지정이 없으면 대상들의 마지막 기록. 그것도 없으면 측정할 것이 없다. */\n let basis: 'given' | 'latest-event' = 'given'\n let toMs = input.toTime ? Date.parse(input.toTime) : NaN\n if (!Number.isFinite(toMs)) {\n const latest = await latestEventTime(input.domainId, instanceIds)\n if (!latest) {\n const now = Date.now()\n return {\n window: {\n fromTime: new Date(now - minutes * 60_000).toISOString(),\n toTime: new Date(now).toISOString(),\n minutes,\n basis: 'latest-event'\n },\n measured: false,\n scope,\n note:\n instanceIds.length === 0\n ? 'no running twin in this space — nothing to measure.'\n : 'no operational events in the journal yet — there is nothing to measure (this is NOT \"zero throughput\").'\n }\n }\n /*\n * ── **미래를 성과 구간으로 삼지 않는다** (2026-08-30) ──────────────────────\n *\n * 마지막 기록이 미래일 수 있다. 한 원본이 **끝나지 않은 이번 달**의 요금 정보를 청구서로 보냈고,\n * 청구서의 사건 시각은 기간의 끝이라 그 사실이 **내일** 일어난 것으로 적혔다. 그러자 구간이\n * `내일 00:00 → 모레 00:00` 이 되어 그 현장의 화면에서 **어제 발전량이 사라졌다.** 오류는 나지\n * 않았고 화면만 비어 있었다.\n *\n * 커널이 이제 그런 사실을 받지 않는다. 그래도 여기서 막는다 — **이미 적힌 것은 사라지지 않고**,\n * 저널은 지우지 않기 때문이다. 그리고 성과는 지나간 일이다. 아직 오지 않은 구간의 성과라는 것은\n * 없다.\n */\n toMs = Math.min(Date.parse(latest), Date.now())\n basis = 'latest-event'\n }\n\n const fromMs = input.fromTime ? Date.parse(input.fromTime) : toMs - minutes * 60_000\n if (!Number.isFinite(fromMs) || !Number.isFinite(toMs) || fromMs >= toMs) {\n throw new Error('invalid window: fromTime must be a valid time before toTime')\n }\n\n const { events, capped } = await fetchEvents(input.domainId, instanceIds, fromMs, toMs, lookbackMs)\n\n /* 구역·교대 축은 **이벤트 밖의 지식**을 필요로 한다 — 다른 축은 payload 에서 바로 나온다. */\n const areaMap =\n input.groupBy === 'area' ? await locationAreaMap(input.domainId, instanceIds, input.spaceId) : { locationArea: {}, labels: {} }\n const shiftCtx = input.groupBy === 'shift' ? await shiftContext(input.domainId, instanceIds) : {}\n /*\n * 전기 계통 소속은 **축과 무관하게** 읽는다 — 분기별 몫은 관점 축이 아니라 에너지 사실의 일부라\n * (같은 창의 총량을 나눈 것) 어느 축을 보고 있든 함께 나가야 한다.\n */\n const feederMap = await resourceFeederMap(input.domainId, instanceIds)\n /* 요금 단가도 축과 무관하게 읽는다 — 금액은 같은 창의 전력량·최대수요에서 나오는 사실이다. */\n const tariffFound = await tariffOf(input.domainId, instanceIds)\n /*\n * ── 요금적용전력은 **청구서가 먼저다** (2026-08-29) ─────────────────────────\n *\n * 그 값은 최근 몇 달의 최고 수요로 **매달 다시 정해진다.** 선언에 박아 두면 매달 사람이 갱신해야\n * 하고, 안 하면 낡은 값으로 기본요금이 조용히 틀린다.\n *\n * 원본이 청구서에 그 값을 실어 매일 보낸다. 창 밖의 청구서는 저널 조회에 걸리지 않으므로\n * **커널 상태에 이어져 있는 마지막 청구서**를 함께 넘긴다(§`EnergyState.lastBill`).\n */\n const lastBill = latestBillOf(input.domainId, instanceIds)\n const tariffBasis = latestTariffBasisOf(input.domainId, instanceIds)\n /* 이용률의 분모 — 커널이 모델의 선언에서 고른다(설비 합 → 자리 합계, 교류 → 직류). */\n const generationRated = generationRatedOf(input.domainId, instanceIds)\n const generationPrice = latestGenerationPriceOf(input.domainId, instanceIds)\n const kpi = foldKpi(events, { fromMs, toMs }, {\n groupBy: input.groupBy,\n locationArea: areaMap.locationArea,\n resourceFeeder: feederMap.resourceFeeder,\n meterFeeder: feederMap.meterFeeder,\n ...(tariffFound.tariff ? { tariff: tariffFound.tariff } : {}),\n ...(lastBill ? { lastBill } : {}),\n ...(tariffBasis ? { tariffBasis } : {}),\n ...(generationRated ? { generationRated } : {}),\n ...(generationPrice ? { generationPrice } : {}),\n ...shiftCtx,\n groupLimit: input.groupLimit\n })\n /*\n * **완료로 세지 않은 오더 상태 낱말을 말한다** (2026-08-23).\n *\n * 폴드는 순수하다(로그를 모른다) — 그래서 부르는 쪽이 말한다. 왜 말해야 하나: 이 폴드는 오더의\n * 완료를 **동의어 목록**으로 판정하고, 그것은 원본의 낱말에서 뜻을 추론하는 것이다. 커넥터가\n * 자기 표로 옮기지 않으면 그 낱말이 목록에 없어 **완료가 알리지 않고 0 으로 세어진다.**\n *\n * 실제로 그랬다: 첫 실 원본의 `FINISHED` 가 목록에 없어 완료 오더가 전부 0 이었고, 사람이 화면을\n * 보고 의심해서야 드러났다(그 커넥터에는 상태 표가 정의돼 **있으면서** 오더 경로에서 쓰이지 않았다).\n * 그때 목록에 낱말을 더한 것은 그 사실을 가린 땜빵이었다.\n *\n * 그러니 이 로그는 「목록을 늘려라」가 아니라 **「그 커넥터가 옮기지 않았다」**를 뜻한다. 화면을 보지\n * 않는 사람도 알아야 하므로 로그에 남긴다 — 들어온 것이 없는 0 이 이 시스템에서 가장 비싼 침묵이다.\n *\n * 완료가 하나도 없을 때만 말한다: `picking` 처럼 완료가 아닌 낱말이 섞여 있는 것은 정상이고,\n * 그것까지 매번 경고하면 진짜 신호가 묻힌다.\n */\n const notCounted = Object.entries(kpi.orderStatusNotCounted ?? {})\n if (notCounted.length && kpi.throughput.orders === 0) {\n twinWarn(\n `[twin-kpi] no order counted as completed in this window, but these order status words were seen: ` +\n `${notCounted.map(([w, n]) => `${w}×${n}`).join(' · ')} — the connector may not be translating them ` +\n `to the kernel's vocabulary (see its own status table).`\n )\n }\n /*\n * 목표를 **폴드 뒤, 그룹 조립 앞**에 읽는다 — 전체 판정과 축별 판정이 같은 목록에서 나와야\n * 두 곳의 임계가 갈라지지 않는다. 폴드 자체는 목표를 모른다(사실 층과 판정 층의 분리).\n */\n const scoped = await loadTargets(input.domainId, input.spaceId, instanceIds, input.groupBy)\n const verdicts = judgeTargets(kpi, scoped.overall)\n const groupTargets = scoped.byAxisKey\n\n const window = {\n fromTime: new Date(fromMs).toISOString(),\n toTime: new Date(toMs).toISOString(),\n minutes: Math.round((toMs - fromMs) / 60_000),\n basis\n }\n /**\n * 여러 구간 분포를 만든다 — **현재 창에 기록이 없어도 낸다.**\n *\n * 그때가 오히려 가장 알고 싶은 순간이다(\"지금 아무 기록이 없는데 평소엔 60건이었다\" = 멈춘 신호).\n * 그래서 조기 반환보다 앞에 두고 두 경로가 함께 쓴다.\n */\n const computeBaseline = async (): Promise<TwinKpiOutput['baseline']> => {\n if (!input.baseline || !(input.baseline.periods > 0)) return undefined\n const stride = input.baseline.stride ?? 'day'\n const windows = baselineWindows(fromMs, toMs, input.baseline.periods, stride)\n let baseCapped = false\n const samples: { fromMs: number; toMs: number; values: Partial<Record<TwinMetricId, number | undefined>> }[] = []\n\n for (const w of windows) {\n const { events: pastEvents, capped: pastCapped } = await fetchEvents(input.domainId, instanceIds, w.fromMs, w.toMs, lookbackMs)\n baseCapped = baseCapped || pastCapped\n /* 축 없이, **같은 교대 정의**로 처리한다(정의가 다르면 같은 시간대가 아니게 된다). */\n const past = foldKpi(pastEvents, w, { locationArea: areaMap.locationArea, ...shiftCtx })\n const values: Partial<Record<TwinMetricId, number | undefined>> = {}\n for (const metric of Object.keys(TWIN_METRIC) as TwinMetricId[]) values[metric] = TWIN_METRIC[metric].of(past)\n samples.push({ fromMs: w.fromMs, toMs: w.toMs, values })\n }\n\n const current: Partial<Record<TwinMetricId, number | undefined>> = {}\n for (const metric of Object.keys(TWIN_METRIC) as TwinMetricId[]) current[metric] = TWIN_METRIC[metric].of(kpi)\n\n return {\n stride,\n periods: windows.length,\n metrics: summarizeBaseline(TWIN_METRIC, current, samples),\n ...(baseCapped ? { capped: true } : {})\n }\n }\n\n\n if (kpi.eventsInWindow === 0) {\n const baseline = await computeBaseline()\n return {\n window,\n measured: false,\n scope,\n note: 'no operational events in this window — nothing to measure (this is NOT \"zero throughput\").',\n ...(baseline ? { baseline } : {})\n }\n }\n\n /* 구간별 값 — 쪼개기 규칙은 순수 모듈이 소유한다(테스트 가능). 여기서는 시각을 ISO 로 옮기기만. */\n const rawBuckets = foldKpiBuckets(events, { fromMs, toMs }, Number(input.buckets) || 0)\n const buckets = rawBuckets.length\n ? rawBuckets.map(b => ({\n fromTime: new Date(b.fromMs).toISOString(),\n toTime: new Date(b.toMs).toISOString(),\n tasks: b.tasks,\n orders: b.orders,\n workP50Ms: b.workP50Ms\n }))\n : undefined\n\n /* 비교 구간 — **같은 길이·같은 규칙**으로 한 번 더 계산한다. 기록이 없으면 delta 를 만들지 않는다\n * (없는 것을 0 으로 그리면 \"변화 없음\" 이라는 거짓이 된다). */\n let comparison: TwinKpiOutput['comparison']\n /* 관점 × 비교 교차 — 축별 delta 와 **사라진 축**(전에 있었고 지금 없는 것). */\n let crossed: ReturnType<typeof crossGroups> | undefined\n /* 비교 구간 조회도 상한에 걸릴 수 있다 — 그 경우 delta 가 실제보다 작아 보인다. */\n let prevCappedFlag = false\n if (input.compareTo === 'previous' || input.compareTo === 'yesterday') {\n const w = shiftWindow(fromMs, toMs, input.compareTo)\n const { events: prevEvents, capped: prevCapped } = await fetchEvents(\n input.domainId,\n instanceIds,\n w.fromMs,\n w.toMs,\n lookbackMs\n )\n /* 축이 요청됐으면 비교 구간도 **같은 축**으로 처리한다 — 교차(\"구역별로 어제 대비\")를 내기 위해.\n * 상한은 축이 서로 어긋나지 않게 넉넉히 둔다(현재 축이 상한 안에 있는데 비교 축에서 밀려나면\n * 있는데 없다고 말하게 된다). */\n const prev = foldKpi(prevEvents, w, {\n groupBy: input.groupBy,\n locationArea: areaMap.locationArea,\n ...shiftCtx, // 비교 구간도 **같은 교대 정의**로 처리한다(정의가 다르면 축이 어긋난다)\n groupLimit: input.groupBy ? 100 : undefined\n })\n prevCappedFlag = prevCapped\n if (input.groupBy && kpi.groups && prev.groups && prev.eventsInWindow > 0) {\n crossed = crossGroups(kpi.groups.items, prev.groups.items)\n }\n const base = {\n basis: input.compareTo,\n window: { fromTime: new Date(w.fromMs).toISOString(), toTime: new Date(w.toMs).toISOString() }\n }\n comparison =\n prev.eventsInWindow === 0\n ? { ...base, measured: false }\n : {\n ...base,\n measured: true,\n throughput: prev.throughput,\n workTime: prev.workTime,\n leadTime: prev.leadTime,\n waitTime: prev.waitTime,\n delta: {\n tasks: kpi.throughput.tasks - prev.throughput.tasks,\n orders: kpi.throughput.orders - prev.throughput.orders,\n workP50Ms: deltaP50(kpi.workTime, prev.workTime),\n leadP50Ms: deltaP50(kpi.leadTime, prev.leadTime),\n waitP50Ms: deltaP50(kpi.waitTime, prev.waitTime)\n }\n }\n }\n\n /*\n * 여러 구간 동시 비교 — **같은 시간대를 며칠 뒤로** 옮겨 분포를 만든다(성과 맵 ③).\n *\n * 창마다 조회가 한 번 더 붙지만 각 창은 현재 창과 같은 길이라(보통 1~24h) 값싸다. 축(groupBy)은\n * 넘기지 않는다 — 분포는 **전체 지표**의 질문이고, 축별 분포까지 내면 응답이 축 수만큼 부푼다\n * (필요해지면 그때 축을 지정해 다시 묻는 것이 정직하다).\n *\n * 지표 어휘는 `TWIN_METRIC` 이 계속 소유하고, 여기서는 **값만 꺼내** 분포 모듈에 넘긴다.\n */\n /* 관점 값은 시각 변환이 없어 그대로 통과한다. 붙이는 것은 ① 사람이 읽는 이름과 ② 축이 통째로\n * 비었을 때의 이유다 — \"구역별 0건\" 과 \"노드에 구역이 안 붙어 있다\" 는 완전히 다른 상황이다. */\n const groups = kpi.groups\n ? {\n by: kpi.groups.by,\n items: (crossed?.items ?? kpi.groups.items).map(g => {\n /* 축의 이 값에 목표가 걸려 있으면 판정을 함께 낸다 — \"이 구역만 목표가 다르다\" 를\n 표에서 바로 볼 수 있게. 목표가 없는 축값에는 필드를 만들지 않는다. */\n const gt = judgeGroupTargets(g as any, groupTargets[g.key])\n return {\n ...g,\n ...(areaMap.labels[g.key] ? { label: areaMap.labels[g.key] } : {}),\n ...(gt.length ? { targets: gt } : {})\n }\n }),\n ...(crossed?.disappeared.length\n ? {\n disappeared: crossed.disappeared.map(d => ({\n ...d,\n ...(areaMap.labels[d.key] ? { label: areaMap.labels[d.key] } : {})\n }))\n }\n : {}),\n ...(kpi.groups.truncated\n ? { truncated: kpi.groups.truncated, truncatedTasks: kpi.groups.truncatedTasks ?? 0 }\n : {}),\n ...(input.groupBy === 'area' &&\n Object.keys(areaMap.locationArea).length === 0 &&\n kpi.groups.items.some(g => g.key === 'unknown')\n ? { note: 'locations carry no area (parentId) in this twin — cannot break down by area. this is NOT \"no work in areas\".' }\n : {})\n }\n : undefined\n\n /*\n * 목표 대비 판정 — **사실 계산 뒤에 얹는다.**\n *\n * 폴드는 목표를 모른다(사실만 낸다). 목표를 아는 순간 \"세는 규칙\" 과 \"잘했나 판정하는 규칙\" 이\n * 한 함수에 섞이고, 목표가 바뀔 때마다 사실 계산을 건드리게 된다. 그래서 조회 층에서 목표를\n * 실어 오고, 판정은 순수 함수(`judgeTargets`)가 한다.\n *\n * **목표가 없으면 판정하지 않는다** — 필드 자체를 만들지 않는다. 전부 \"목표 없음\" 으로 채우면\n * 화면이 회색 노이즈가 되고, 목표가 있는 지표가 묻힌다.\n */\n /* 분포는 **한 번만** 계산한다 — 창마다 조회가 붙으므로 두 번 부르면 조회가 두 배가 된다. */\n const baseline = await computeBaseline()\n\n /* 폴드가 돌려준 raw 창(ms)은 버린다 — 위의 ISO 창이 사람이 읽을 정본이다. */\n const { window: _rawWindow, groups: _rawGroups, ...rest } = kpi\n /*\n * 자리에 **부르는 이름**을 붙인다 — `feeder-2` 는 id 이고 사람은 「가공 라인」으로 부른다.\n * 이름을 모르는 자리는 id 그대로 둔다(지어내지 않는다).\n */\n /* 단가가 갈려 있으면 밝힌다 — 우리가 고른 하나로 낸 금액이라는 사실을 화면이 말할 수 있게. */\n if (rest.energy?.cost && tariffFound.conflict) rest.energy = { ...rest.energy, tariffConflict: true } as any\n if (rest.energy?.byFeeder?.length) {\n rest.energy = {\n ...rest.energy,\n byFeeder: rest.energy.byFeeder.map(row => ({ ...row, ...(feederMap.labels[row.key] ? { label: feederMap.labels[row.key] } : {}) }))\n }\n }\n /*\n * 계통도 — **선언된 관계와 계량이 덮는 범위**를 함께 낸다.\n *\n * 값은 여기서 다시 계산하지 않는다(폴드가 낸 분기별 몫을 그대로 노드에 얹는다). 계통은 모델이 아는\n * 사실이므로 저널만 보는 폴드가 아니라 여기서 조립한다 — 화면이 모델을 따로 또 조회하지 않아도 되고,\n * AI 도 같은 값을 읽는다(규칙 한 벌).\n */\n if (rest.energy) {\n const topo = await energyTopologyOf(input.domainId, instanceIds, rest.energy)\n if (topo) rest.energy = { ...rest.energy, topology: topo } as any\n }\n return {\n window,\n measured: true,\n scope,\n ...(capped || prevCappedFlag ? { eventsCapped: true } : {}),\n ...rest,\n ...(buckets ? { buckets } : {}),\n ...(groups ? { groups } : {}),\n ...(verdicts.length ? { targets: verdicts } : {}),\n ...(comparison ? { comparison } : {}),\n ...(baseline ? { baseline } : {})\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kpi-target.js","sourceRoot":"","sources":["../../server/engine/kpi-target.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 성과 목표 — **숫자를 판단으로 바꾸는 층.**\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 화면과 AI 는 \"처리량 345건 · 작업시간 29초\" 를 정확히 말한다. 그런데 **그것이 좋은지 나쁜지는 말할 수\n * 없다.** 판단 근거가 앞 구간 비교뿐이라 \"어제보다 나아졌다\" 까지가 한계이고, 어제도 나빴으면 그 말은\n * 쓸모가 없다. 목표가 붙는 순간 같은 숫자가 **정보에서 판단으로** 바뀐다.\n *\n * ── 층을 섞지 않는다 ────────────────────────────────────────────────────────\n * 폴드(`kpi-fold`)는 **사실만** 낸다 — 목표를 모른다. 목표를 아는 순간 \"사실을 세는 규칙\" 과 \"잘했나\n * 판정하는 규칙\" 이 한 함수에 섞이고, 목표가 바뀔 때마다 사실 계산을 건드리게 된다. 그래서 판정은\n * 여기, **순수 함수**로 따로 둔다(폴드처럼 테스트가 값싸다).\n *\n * ── 목표의 집 (사용자 결정 2026-08-02) ──────────────────────────────────────\n * **트윈은 운영 목표를 갖고, `@things-factory/kpi` 는 경영 목표를 갖는다.** 경계:\n *\n * 트윈(TwinTarget) 이 구간·이 교대·이 공간의 **즉시 판단**. 현장 관리자가 트윈 화면에서 고친다.\n * 값은 저널 폴드에서 **그때그때 계산**된다(저장된 값을 읽지 않는다).\n * packages/kpi **기간 평가**(조직·분기·점수·가중치·알림). 값은 저장돼 있어야 한다.\n *\n * 이 경계를 지키는 규칙 하나: **트윈은 목표를 복제하지 않는다.** 경영 목표가 필요하면 트윈의 사실을\n * 내보내 그쪽이 소비하고, 그쪽 목표를 여기로 베껴 오지 않는다 — 베끼는 순간 두 개의 진실이 된다.\n */\n/*\n * ── 운영 임계(커널 attention)와의 경계 ─────────────────────────────────────\n *\n * **attention 은 개체 하나의 지금 상태**다 — 앵커(설비·자리·오더)와 할 수 있는 조치를 갖는다.\n * **target(여기)은 구간의 집계**다 — 앵커도 조치도 없고 추세를 판단한다.\n *\n * 그래서 창 단위 집계 판정을 attention 으로 넣지 않는다: \"가동률 저하\"(창 평균) attention 을 만들면\n * `utilization.ratio` 목표와 **같은 사실을 두 층이 다른 임계로 판정**하게 된다.\n * (design/map/performance.md §5.0-4 — 현재 다섯 attention 과 이 어휘는 겹치지 않는다.)\n */\nimport type { DurationStats, KpiResult } from './kpi-fold.js'\n\n/** 측정된 기록이 있을 때만 값으로 인정한다 — 없으면 모름(0 이 아니다). */\nconst measured = (k: KpiResult, v: number): number | undefined => (k.eventsInWindow > 0 ? v : undefined)\n\n/** 분위수는 표본이 있을 때만 값이다 — `count === 0` 의 `p50Ms: 0` 은 \"0 밀리초\" 가 아니라 \"없음\" 이다. */\nconst statOf = (s: DurationStats, pick: (d: DurationStats) => number): number | undefined =>\n s.count > 0 ? pick(s) : undefined\n\n/**\n * 지표 어휘 — **이름을 한 곳에서 정한다.**\n *\n * 목표와 사실이 서로 다른 이름을 쓰면 그 사이에 매핑 표가 생기고, 그 표가 곧 방언이 된다. 그래서\n * 목표가 가리킬 수 있는 지표는 여기 열거된 것뿐이고, 값을 꺼내는 방법도 여기 함께 있다.\n *\n * **`direction` 은 지표의 성질이지 사용자의 선택이 아니다.** 처리량은 높을수록 좋고 대기시간은 낮을수록\n * 좋다 — 이것을 입력으로 받으면 반대로 적은 목표가 만들어지고, 그 뒤 판정이 전부 뒤집힌다.\n */\nexport const TWIN_METRIC = {\n /* 기록이 없는 구간은 처리량 0 이 아니라 **모름**이다 — `eventsInWindow` 가 그것을 구분한다.\n 0 으로 판정하면 \"측정 못 한 구간\" 이 \"일을 안 한 구간\" 으로 보고된다. */\n 'throughput.tasks': { direction: 'higher', unit: 'count', of: (k: KpiResult) => measured(k, k.throughput.tasks) },\n 'throughput.orders': { direction: 'higher', unit: 'count', of: (k: KpiResult) => measured(k, k.throughput.orders) },\n /* 소요시간 분위수는 `count === 0` 이면 **0 이 아니라 없다.** 이것을 0 으로 읽으면 시간 지표는\n \"낮을수록 좋다\" 라서 **일이 없던 구간이 목표를 완벽히 달성한 것으로** 보고된다 — 미달을 놓치는\n 것보다 나쁘다(고칠 곳이 있는데 잘하고 있다고 말한다). 테스트가 이것을 잡았다. */\n 'leadTime.p50Ms': { direction: 'lower', unit: 'ms', of: (k: KpiResult) => statOf(k.leadTime, d => d.p50Ms) },\n 'leadTime.p90Ms': { direction: 'lower', unit: 'ms', of: (k: KpiResult) => statOf(k.leadTime, d => d.p90Ms) },\n 'workTime.p50Ms': { direction: 'lower', unit: 'ms', of: (k: KpiResult) => statOf(k.workTime, d => d.p50Ms) },\n 'waitTime.p50Ms': { direction: 'lower', unit: 'ms', of: (k: KpiResult) => statOf(k.waitTime, d => d.p50Ms) },\n /*\n * ── 에너지 (2026-08-17) ────────────────────────────────────────────────────\n *\n * 값은 폴드가 이미 내고 있었는데 **목표를 걸 수 있는 지표 목록에 없었다.** 그래서 화면은 「이번 창에\n * 1,240kWh」를 보여 주면서도 그것이 좋은지 나쁜지는 말하지 못했다 — 에너지야말로 계약이라는 딱딱한\n * 기준선이 있는 영역인데도 그랬다.\n *\n * 셋 다 방향은 **낮을수록 좋다**로 못 박는다. 「많이 쓸수록 좋다」는 에너지 목표는 없다. 산출이 늘어\n * 전력량이 느는 경우는 원단위(`energy.intensity`)가 답할 자리이지, 방향을 뒤집을 이유가 아니다.\n *\n * 값이 없는 창(에너지 트윈이 아니거나 계측이 끊긴 창)은 `undefined` 로 남긴다 — 0 으로 읽으면\n * 시간 지표에서 이미 겪은 그 결함이 그대로 재현된다(안 쓴 것이 아니라 못 잰 것인데 만점이 된다).\n */\n 'energy.kWh': { direction: 'lower', unit: 'kWh', of: (k: KpiResult) => k.energy?.kWh },\n /** 최대수요 — 계약 상한을 목표로 적는 자리다(요금의 기본요금 부분이 여기서 정해진다). */\n 'energy.peakKW': { direction: 'lower', unit: 'kW', of: (k: KpiResult) => k.energy?.peakKW },\n /** 원단위 — 완료 오더당 kWh. 답할 수 없으면(`null`) 판정하지 않는다. */\n 'energy.intensity': {\n direction: 'lower',\n unit: 'kWh/order',\n of: (k: KpiResult) => (typeof k.energy?.intensity?.value === 'number' ? k.energy.intensity.value : undefined)\n },\n /** 자원 점유 — 개별 자원이 아니라 **창 전체의 평균 비율**(자원별은 관점 축의 일이다). */\n 'utilization.ratio': {\n direction: 'higher',\n unit: 'ratio',\n of: (k: KpiResult) =>\n k.utilization.length ? k.utilization.reduce((s, u) => s + u.ratio, 0) / k.utilization.length : undefined\n }\n} as const\n\nexport type TwinMetricId = keyof typeof TWIN_METRIC\nexport const isTwinMetric = (id: string): id is TwinMetricId => id in TWIN_METRIC\n\n/**\n * 저장된 목표 행에서 **이 범위에 걸린 목표**를 고른다 — 좁은 선언이 넓은 선언을 덮는다.\n *\n * **왜 순수 함수인가**: 이 규칙(공간 목표 위에 인스턴스 목표를 얹는다)은 지금까지 DB 접근 뒤에 숨어\n * 있어서 **화면을 눌러 봐야만** 확인할 수 있었다. 그런데 이 프로젝트는 로그인 뒤 화면을 자동으로\n * 검증할 방법이 없어, 실제로 여러 기능이 \"실화면 미검증\" 으로 쌓였다. 규칙을 밖으로 빼면 테스트가\n * 값싸진다 — 조회는 행을 가져오기만 하고, **판단은 여기서** 한다.\n */\n/**\n * 축 목표까지 담은 목록 — 전체 목표와 **축의 한 값**에 걸린 목표를 함께 든다.\n *\n * 축 목표는 전체 목표를 **덮지 않는다.** 다른 질문이기 때문이다: \"현장 전체가 100건\" 과\n * \"용접 구역이 30건\" 은 동시에 참일 수 있다. 덮게 만들면 축 하나에 목표를 거는 순간 전체 판정이\n * 사라져 사용자가 \"목표를 지웠나?\" 를 묻게 된다.\n */\nexport interface ScopedTargets {\n /** 창 전체에 걸린 목표. */\n overall: TwinTargetSpec[]\n /** 축 → (그 축의 값 → 목표들). 요청한 축만 채운다. */\n byAxisKey: Record<string, TwinTargetSpec[]>\n}\n\n/**\n * 축의 각 값에 대해 목표 대비 판정 — **그룹 항목에 그대로 붙일 형태.**\n *\n * 그룹의 지표는 전체보다 좁다(주문 수·자원 점유는 축 단위로 뜻이 다르거나 없다). 없는 지표에 목표가\n * 걸려 있으면 **판정하지 않는다** — 여기서 0 으로 읽으면 시간 지표가 \"완벽 달성\" 으로 뒤집힌다\n * (전체 판정에서 이미 겪은 결함이다).\n */\nexport function judgeGroupTargets(\n group: { key: string; tasks: number; leadTime: { count: number; p50Ms: number }; workTime: { count: number; p50Ms: number }; waitTime: { count: number; p50Ms: number } },\n targets: readonly TwinTargetSpec[] | undefined\n): TargetVerdict[] {\n const value = (metric: TwinMetricId): number | undefined => {\n switch (metric) {\n case 'throughput.tasks':\n return group.tasks\n case 'leadTime.p50Ms':\n return group.leadTime.count > 0 ? group.leadTime.p50Ms : undefined\n case 'workTime.p50Ms':\n return group.workTime.count > 0 ? group.workTime.p50Ms : undefined\n case 'waitTime.p50Ms':\n return group.waitTime.count > 0 ? group.waitTime.p50Ms : undefined\n default:\n return undefined // 축 단위로 뜻이 없는 지표(주문 수·자원 점유) — 지어내지 않는다\n }\n }\n const out: TargetVerdict[] = []\n for (const t of targets ?? []) {\n const spec = TWIN_METRIC[t.metric]\n const actual = value(t.metric)\n if (!spec || typeof actual !== 'number' || !Number.isFinite(actual) || !Number.isFinite(t.target)) continue\n out.push(verdictOf(t.metric, actual, t.target, spec.direction))\n }\n return out\n}\n\nexport function mergeTargets(\n rows: readonly TargetRow[],\n scope: { spaceId?: string; instanceIds: readonly string[]; axis?: string }\n): TwinTargetSpec[] {\n return scopedTargets(rows, scope).overall\n}\n\n/** 저장된 목표 행 — 판단에 필요한 것만(엔티티 전체를 요구하지 않는다: 테스트가 값싸진다). */\nexport interface TargetRow {\n metric: string\n target: number\n spaceId?: string | null\n instanceId?: string | null\n axis?: string | null\n axisKey?: string | null\n}\n\n/** 전체 목표와 축 목표를 함께 고른다 — 좁은 선언(인스턴스)이 넓은 선언(공간)을 덮는 규칙은 양쪽 같다. */\nexport function scopedTargets(\n rows: readonly TargetRow[],\n scope: { spaceId?: string; instanceIds: readonly string[]; axis?: string }\n): ScopedTargets {\n /* 같은 자리(지표 또는 축값+지표)에 공간·인스턴스 목표가 함께 있으면 인스턴스가 이긴다. */\n const pick = new Map<string, { target: number; fromInstance: boolean; axisKey?: string; metric: TwinMetricId }>()\n for (const r of rows) {\n if (!isTwinMetric(r.metric) || typeof r.target !== 'number' || !Number.isFinite(r.target)) continue\n const inSpace = !!scope.spaceId && r.spaceId === scope.spaceId\n const inInstance = !!r.instanceId && scope.instanceIds.includes(r.instanceId)\n if (!inSpace && !inInstance) continue\n if (r.axis) {\n if (!scope.axis || r.axis !== scope.axis || !r.axisKey) continue // 지금 보는 축만\n }\n const slot = `${r.axis ?? ''}\\u0000${r.axisKey ?? ''}\\u0000${r.metric}`\n const prev = pick.get(slot)\n if (prev?.fromInstance && !inInstance) continue // 인스턴스 선언을 공간 선언이 덮지 않는다\n pick.set(slot, { target: r.target, fromInstance: inInstance, axisKey: r.axisKey ?? undefined, metric: r.metric })\n }\n const overall: TwinTargetSpec[] = []\n const byAxisKey: Record<string, TwinTargetSpec[]> = {}\n for (const [slot, v] of pick) {\n const spec = { metric: v.metric, target: v.target }\n if (slot.startsWith('\\u0000')) overall.push(spec)\n else (byAxisKey[v.axisKey as string] ??= []).push(spec)\n }\n return { overall, byAxisKey }\n}\n\n/** 목표 하나 — 어느 지표를, 얼마로. 방향은 지표가 안다(§TWIN_METRIC). */\nexport interface TwinTargetSpec {\n metric: TwinMetricId\n target: number\n}\n\nexport interface TargetVerdict {\n metric: TwinMetricId\n /** 이 구간의 실제 값. */\n actual: number\n target: number\n direction: 'higher' | 'lower'\n /** 목표를 만족했나. */\n met: boolean\n /**\n * 목표 대비 비율 — **항상 \"1 이상이면 좋다\"** 로 정규화한다(방향이 다른 지표를 한 화면에서 비교할 수\n * 있게). 처리량은 `actual/target`, 시간은 `target/actual`.\n */\n ratio: number\n}\n\n/**\n * 목표 대비 판정 — **목표가 없는 지표는 판정하지 않는다.**\n *\n * 값이 없는 지표(그 구간에 기록이 없어 `undefined`)도 판정하지 않는다. 없는 것을 0 으로 놓으면\n * \"목표 크게 미달\" 이라는 **없는 사실**이 만들어지고, 사용자는 멈춘 적 없는 라인을 고치러 간다.\n * 목표를 0 으로 준 경우도 비율을 내지 않는다(0 으로 나누지 않는다) — 만족 여부만 답한다.\n */\nexport function judgeTargets(kpi: KpiResult, targets: readonly TwinTargetSpec[] | undefined): TargetVerdict[] {\n const out: TargetVerdict[] = []\n for (const t of targets ?? []) {\n const spec = TWIN_METRIC[t.metric]\n if (!spec) continue // 모르는 지표 이름 — 짐작해 매핑하지 않는다\n const actual = spec.of(kpi)\n if (typeof actual !== 'number' || !Number.isFinite(actual)) continue // 값이 없다 ≠ 값이 0\n if (!Number.isFinite(t.target)) continue\n out.push(verdictOf(t.metric, actual, t.target, spec.direction))\n }\n return out\n}\n\n/** 판정 산수 한 곳 — 전체 판정과 축 판정이 **같은 규칙**을 쓴다(두 벌이면 어긋난다). */\nfunction verdictOf(metric: TwinMetricId, actual: number, target: number, direction: 'higher' | 'lower'): TargetVerdict {\n const met = direction === 'higher' ? actual >= target : actual <= target\n const ratio = direction === 'higher' ? (target > 0 ? actual / target : Number.NaN) : actual > 0 ? target / actual : Number.NaN\n return { metric, actual, target, direction, met, ratio }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"live-attentions.js","sourceRoot":"","sources":["../../server/engine/live-attentions.ts"],"names":[],"mappings":";;AAQA,gDAqBC;AA7BD;;;;;GAKG;AACH,sDAAuD;AAEvD,SAAgB,kBAAkB,CAChC,KAAQ;IAER,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,OAAO;QACL,GAAG,KAAK;QACR;;;WAGG;QACH,UAAU,EAAE,IAAA,8BAAgB,EAC1B,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE;QAC5H;;;;WAIG;QACH,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EACtD,KAAK,CAAC,OAAO,CACd;KACF,CAAA;AACH,CAAC","sourcesContent":["/*\n * live State attention 보강 — projector 미러 State(관측)에 커널 deriveAttentions(계산)를 채운다.\n * live 스냅샷이 sim 동형(attentions 포함)이 되어 attention 질의/스트림이 live 에서도 신호를 준다.\n * 순수(입력 불변). face2-inbound-live §1.1 \"State-순수 파생\"(입력이 미러 State 에 있어 재적용 가능).\n * deriveAttentions 는 커널 공유 공식(sim/live 동일 임계값·규칙).\n */\nimport { deriveAttentions } from '@operato/twin-kernel'\n\nexport function withLiveAttentions<S extends { equipment?: any[]; locations?: any[]; orders?: any[]; tasks?: any[]; attentions?: any[]; acked?: string[]; nowTime?: string }>(\n state: S\n): S {\n if (!state) return state\n return {\n ...state,\n /*\n * 입력을 **시뮬 쪽과 똑같이** 넣는다. 한쪽만 빠지면 같은 공장이 한쪽에서만 막힌 것처럼 보이고,\n * 그 어긋남은 규칙이 아니라 배선 때문이라 아무 데서도 안 잡힌다(`tasks` 를 빠뜨려 실제로 그럴 뻔했다).\n */\n attentions: deriveAttentions(\n { equipment: state.equipment ?? [], locations: state.locations ?? [], orders: state.orders ?? [], tasks: state.tasks ?? [] },\n /*\n * **확인(ack)해 둔 것을 함께 넘긴다.** 예전에는 여기에 undefined 를 넘겨서, 라이브에서는\n * 확인 처리가 화면에 전혀 반영되지 않았다 — 사람이 확인해도 신호가 계속 활성으로 나갔다.\n * 확인 사실은 이제 저널에 남고(`attention.acked`) 프로젝터가 상태에 실어 오므로 여기서 받는다.\n */\n state.acked?.length ? new Set(state.acked) : undefined,\n state.nowTime\n )\n }\n}\n"]}