@things-factory/headless-twin 10.0.14 → 10.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. package/dist-server/engine/attention-digest.d.ts +4 -4
  2. package/dist-server/engine/attention-digest.js +7 -7
  3. package/dist-server/engine/attention-digest.js.map +1 -1
  4. package/dist-server/engine/canonical-ingest.d.ts +11 -2
  5. package/dist-server/engine/canonical-ingest.js +137 -8
  6. package/dist-server/engine/canonical-ingest.js.map +1 -1
  7. package/dist-server/engine/command-routing.js +1 -1
  8. package/dist-server/engine/command-routing.js.map +1 -1
  9. package/dist-server/engine/declared-stimulus.js +1 -1
  10. package/dist-server/engine/declared-stimulus.js.map +1 -1
  11. package/dist-server/engine/energy-topology.js +1 -1
  12. package/dist-server/engine/energy-topology.js.map +1 -1
  13. package/dist-server/engine/ingest-dedupe.d.ts +38 -0
  14. package/dist-server/engine/ingest-dedupe.js +145 -0
  15. package/dist-server/engine/ingest-dedupe.js.map +1 -0
  16. package/dist-server/engine/ingest-health.d.ts +124 -9
  17. package/dist-server/engine/ingest-health.js +169 -15
  18. package/dist-server/engine/ingest-health.js.map +1 -1
  19. package/dist-server/engine/integration-probes.d.ts +3 -3
  20. package/dist-server/engine/integration-probes.js +6 -6
  21. package/dist-server/engine/integration-probes.js.map +1 -1
  22. package/dist-server/engine/kpi-baseline.js +4 -4
  23. package/dist-server/engine/kpi-baseline.js.map +1 -1
  24. package/dist-server/engine/kpi-fold.d.ts +122 -12
  25. package/dist-server/engine/kpi-fold.js +247 -18
  26. package/dist-server/engine/kpi-fold.js.map +1 -1
  27. package/dist-server/engine/kpi-query.d.ts +11 -11
  28. package/dist-server/engine/kpi-query.js +185 -28
  29. package/dist-server/engine/kpi-query.js.map +1 -1
  30. package/dist-server/engine/live-feed-registry.d.ts +1 -1
  31. package/dist-server/engine/live-feed-registry.js +1 -1
  32. package/dist-server/engine/live-feed-registry.js.map +1 -1
  33. package/dist-server/engine/load-meter.d.ts +1 -1
  34. package/dist-server/engine/load-meter.js.map +1 -1
  35. package/dist-server/engine/local-declarations.d.ts +7 -7
  36. package/dist-server/engine/local-declarations.js +9 -9
  37. package/dist-server/engine/local-declarations.js.map +1 -1
  38. package/dist-server/engine/log.d.ts +1 -1
  39. package/dist-server/engine/log.js +1 -1
  40. package/dist-server/engine/log.js.map +1 -1
  41. package/dist-server/engine/measured-estimator.js +1 -1
  42. package/dist-server/engine/measured-estimator.js.map +1 -1
  43. package/dist-server/engine/measured-yield.js +1 -1
  44. package/dist-server/engine/measured-yield.js.map +1 -1
  45. package/dist-server/engine/model-basis.d.ts +1 -1
  46. package/dist-server/engine/model-basis.js +1 -1
  47. package/dist-server/engine/model-basis.js.map +1 -1
  48. package/dist-server/engine/model-gap.d.ts +3 -3
  49. package/dist-server/engine/model-gap.js +1 -1
  50. package/dist-server/engine/model-gap.js.map +1 -1
  51. package/dist-server/engine/model-vocabulary.js +1 -1
  52. package/dist-server/engine/model-vocabulary.js.map +1 -1
  53. package/dist-server/engine/oee-accumulator.d.ts +4 -4
  54. package/dist-server/engine/oee-accumulator.js +3 -3
  55. package/dist-server/engine/oee-accumulator.js.map +1 -1
  56. package/dist-server/engine/property-effects.js +2 -2
  57. package/dist-server/engine/property-effects.js.map +1 -1
  58. package/dist-server/engine/read-time.d.ts +22 -0
  59. package/dist-server/engine/read-time.js +81 -0
  60. package/dist-server/engine/read-time.js.map +1 -0
  61. package/dist-server/engine/restart-policy.js +1 -1
  62. package/dist-server/engine/restart-policy.js.map +1 -1
  63. package/dist-server/engine/runtime-key.js +1 -1
  64. package/dist-server/engine/runtime-key.js.map +1 -1
  65. package/dist-server/engine/spec-coverage.d.ts +2 -2
  66. package/dist-server/engine/spec-coverage.js +1 -1
  67. package/dist-server/engine/spec-coverage.js.map +1 -1
  68. package/dist-server/engine/stage-path.d.ts +4 -4
  69. package/dist-server/engine/stage-path.js +1 -1
  70. package/dist-server/engine/stage-path.js.map +1 -1
  71. package/dist-server/engine/state-axes.d.ts +1 -1
  72. package/dist-server/engine/state-axes.js +2 -2
  73. package/dist-server/engine/state-axes.js.map +1 -1
  74. package/dist-server/engine/travel-estimator.d.ts +2 -2
  75. package/dist-server/engine/travel-estimator.js +1 -1
  76. package/dist-server/engine/travel-estimator.js.map +1 -1
  77. package/dist-server/engine/twin-engine.d.ts +243 -53
  78. package/dist-server/engine/twin-engine.js +690 -191
  79. package/dist-server/engine/twin-engine.js.map +1 -1
  80. package/dist-server/engine/warm-start.d.ts +2 -2
  81. package/dist-server/engine/warm-start.js +5 -5
  82. package/dist-server/engine/warm-start.js.map +1 -1
  83. package/dist-server/index.js +1 -1
  84. package/dist-server/index.js.map +1 -1
  85. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js +1 -1
  86. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +1 -1
  87. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js +3 -3
  88. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +1 -1
  89. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js +2 -2
  90. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +1 -1
  91. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.d.ts +5 -0
  92. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js +54 -0
  93. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +1 -0
  94. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.d.ts +5 -0
  95. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js +44 -0
  96. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +1 -0
  97. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.d.ts +6 -0
  98. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js +84 -0
  99. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +1 -0
  100. package/dist-server/migrations/index.js +7 -1
  101. package/dist-server/migrations/index.js.map +1 -1
  102. package/dist-server/routes.d.ts +1 -0
  103. package/dist-server/routes.js +69 -5
  104. package/dist-server/routes.js.map +1 -1
  105. package/dist-server/service/index.d.ts +2 -2
  106. package/dist-server/service/index.js +27 -24
  107. package/dist-server/service/index.js.map +1 -1
  108. package/dist-server/service/reference/control-routing.d.ts +1 -1
  109. package/dist-server/service/reference/control-routing.js +1 -1
  110. package/dist-server/service/reference/control-routing.js.map +1 -1
  111. package/dist-server/service/reference/discovery-result.d.ts +2 -2
  112. package/dist-server/service/reference/discovery-result.js +3 -3
  113. package/dist-server/service/reference/discovery-result.js.map +1 -1
  114. package/dist-server/service/reference/hook-contract.d.ts +43 -0
  115. package/dist-server/service/reference/hook-contract.js +59 -0
  116. package/dist-server/service/reference/hook-contract.js.map +1 -0
  117. package/dist-server/service/reference/ingest-space.d.ts +1 -1
  118. package/dist-server/service/reference/ingest-space.js +4 -4
  119. package/dist-server/service/reference/ingest-space.js.map +1 -1
  120. package/dist-server/service/reference/knob-defaults.d.ts +1 -1
  121. package/dist-server/service/reference/knob-defaults.js +4 -4
  122. package/dist-server/service/reference/knob-defaults.js.map +1 -1
  123. package/dist-server/service/reference/reference-adapter.d.ts +140 -8
  124. package/dist-server/service/reference/reference-adapter.js +27 -0
  125. package/dist-server/service/reference/reference-adapter.js.map +1 -1
  126. package/dist-server/service/reference/reference-hook.d.ts +20 -0
  127. package/dist-server/service/reference/reference-hook.js +133 -0
  128. package/dist-server/service/reference/reference-hook.js.map +1 -0
  129. package/dist-server/service/reference/reference-live.d.ts +2 -2
  130. package/dist-server/service/reference/reference-live.js +88 -13
  131. package/dist-server/service/reference/reference-live.js.map +1 -1
  132. package/dist-server/service/reference/reference-master.d.ts +14 -14
  133. package/dist-server/service/reference/reference-master.js +34 -12
  134. package/dist-server/service/reference/reference-master.js.map +1 -1
  135. package/dist-server/service/reference/reference-resolver.d.ts +1 -1
  136. package/dist-server/service/reference/reference-resolver.js +10 -10
  137. package/dist-server/service/reference/reference-resolver.js.map +1 -1
  138. package/dist-server/service/reference/twin-reference.d.ts +3 -3
  139. package/dist-server/service/reference/twin-reference.js.map +1 -1
  140. package/dist-server/service/twin-attention/twin-attention-query.d.ts +1 -1
  141. package/dist-server/service/twin-attention/twin-attention-query.js +1 -1
  142. package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
  143. package/dist-server/service/twin-audit/command-audit.d.ts +1 -1
  144. package/dist-server/service/twin-audit/command-audit.js.map +1 -1
  145. package/dist-server/service/twin-audit/twin-audit-event.d.ts +1 -1
  146. package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -1
  147. package/dist-server/service/twin-audit/twin-audit-query.js +1 -1
  148. package/dist-server/service/twin-audit/twin-audit-query.js.map +1 -1
  149. package/dist-server/service/twin-control/twin-control-mutation.js +1 -1
  150. package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
  151. package/dist-server/service/twin-event/twin-event-keys.d.ts +22 -2
  152. package/dist-server/service/twin-event/twin-event-keys.js +37 -5
  153. package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
  154. package/dist-server/service/twin-event/twin-event-type.d.ts +1 -0
  155. package/dist-server/service/twin-event/twin-event-type.js +8 -1
  156. package/dist-server/service/twin-event/twin-event-type.js.map +1 -1
  157. package/dist-server/service/twin-event/twin-event.d.ts +2 -2
  158. package/dist-server/service/twin-event/twin-event.js +32 -4
  159. package/dist-server/service/twin-event/twin-event.js.map +1 -1
  160. package/dist-server/service/twin-forecast/forecast-metrics.js +1 -1
  161. package/dist-server/service/twin-forecast/forecast-metrics.js.map +1 -1
  162. package/dist-server/service/twin-forecast/gap-analytics.d.ts +1 -1
  163. package/dist-server/service/twin-forecast/gap-analytics.js.map +1 -1
  164. package/dist-server/service/twin-forecast/twin-forecast-query.js +29 -11
  165. package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
  166. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.d.ts +1 -1
  167. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js +1 -1
  168. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +1 -1
  169. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js +1 -1
  170. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +1 -1
  171. package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
  172. package/dist-server/service/twin-journal/event-type-split-shape.d.ts +50 -0
  173. package/dist-server/service/twin-journal/event-type-split-shape.js +111 -0
  174. package/dist-server/service/twin-journal/event-type-split-shape.js.map +1 -0
  175. package/dist-server/service/twin-journal/event-type-split.d.ts +15 -0
  176. package/dist-server/service/twin-journal/event-type-split.js +102 -0
  177. package/dist-server/service/twin-journal/event-type-split.js.map +1 -0
  178. package/dist-server/service/twin-journal/journal-order.d.ts +15 -0
  179. package/dist-server/service/twin-journal/journal-order.js +17 -0
  180. package/dist-server/service/twin-journal/journal-order.js.map +1 -0
  181. package/dist-server/service/twin-journal/twin-journal-query.d.ts +5 -5
  182. package/dist-server/service/twin-journal/twin-journal-query.js +232 -21
  183. package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
  184. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +27 -1
  185. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +59 -3
  186. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
  187. package/dist-server/service/twin-metrics/twin-metrics-query.js +14 -1
  188. package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
  189. package/dist-server/service/twin-model/axis-journal-evidence.js +1 -1
  190. package/dist-server/service/twin-model/axis-journal-evidence.js.map +1 -1
  191. package/dist-server/service/twin-model/epcis-coverage.js +36 -1
  192. package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
  193. package/dist-server/service/twin-model/iec61850-coverage.js +1 -1
  194. package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -1
  195. package/dist-server/service/twin-model/isa95-coverage.d.ts +15 -2
  196. package/dist-server/service/twin-model/isa95-coverage.js +32 -8
  197. package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
  198. package/dist-server/service/twin-model/item-ref.js +2 -2
  199. package/dist-server/service/twin-model/item-ref.js.map +1 -1
  200. package/dist-server/service/twin-model/name-index.d.ts +1 -1
  201. package/dist-server/service/twin-model/name-index.js +1 -1
  202. package/dist-server/service/twin-model/name-index.js.map +1 -1
  203. package/dist-server/service/twin-model/project-structure.d.ts +1 -1
  204. package/dist-server/service/twin-model/project-structure.js +3 -3
  205. package/dist-server/service/twin-model/project-structure.js.map +1 -1
  206. package/dist-server/service/twin-model/twin-equipment.d.ts +1 -1
  207. package/dist-server/service/twin-model/twin-equipment.js +1 -1
  208. package/dist-server/service/twin-model/twin-equipment.js.map +1 -1
  209. package/dist-server/service/twin-model/twin-lineage-query.js +16 -5
  210. package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
  211. package/dist-server/service/twin-model/twin-location.d.ts +1 -1
  212. package/dist-server/service/twin-model/twin-location.js.map +1 -1
  213. package/dist-server/service/twin-model/twin-model-item-query.d.ts +3 -3
  214. package/dist-server/service/twin-model/twin-model-item-query.js +14 -14
  215. package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
  216. package/dist-server/service/twin-model/twin-model-mutation.d.ts +2 -2
  217. package/dist-server/service/twin-model/twin-model-mutation.js +4 -4
  218. package/dist-server/service/twin-model/twin-model-mutation.js.map +1 -1
  219. package/dist-server/service/twin-model/twin-model-query.js +117 -28
  220. package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
  221. package/dist-server/service/twin-model/twin-model-tree-query.js +22 -0
  222. package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
  223. package/dist-server/service/twin-model/twin-operation.d.ts +1 -1
  224. package/dist-server/service/twin-model/twin-operation.js +1 -1
  225. package/dist-server/service/twin-model/twin-operation.js.map +1 -1
  226. package/dist-server/service/twin-space/move-space.js +2 -2
  227. package/dist-server/service/twin-space/move-space.js.map +1 -1
  228. package/dist-server/service/twin-space/representation-role.d.ts +2 -0
  229. package/dist-server/service/twin-space/representation-role.js +18 -0
  230. package/dist-server/service/twin-space/representation-role.js.map +1 -0
  231. package/dist-server/service/twin-space/space-integrity.d.ts +1 -1
  232. package/dist-server/service/twin-space/space-integrity.js.map +1 -1
  233. package/dist-server/service/twin-space/twin-space-representation.d.ts +2 -0
  234. package/dist-server/service/twin-space/twin-space-representation.js +16 -0
  235. package/dist-server/service/twin-space/twin-space-representation.js.map +1 -1
  236. package/dist-server/service/twin-space/twin-space-resolver.d.ts +2 -2
  237. package/dist-server/service/twin-space/twin-space-resolver.js +36 -7
  238. package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
  239. package/dist-server/service/twin-structure/twin-structure.js +3 -3
  240. package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
  241. package/dist-server/service/twin-subject/event-subjects.d.ts +43 -0
  242. package/dist-server/service/twin-subject/event-subjects.js +137 -0
  243. package/dist-server/service/twin-subject/event-subjects.js.map +1 -0
  244. package/dist-server/service/twin-subject/index.d.ts +9 -0
  245. package/dist-server/service/twin-subject/index.js +16 -0
  246. package/dist-server/service/twin-subject/index.js.map +1 -0
  247. package/dist-server/service/twin-subject/item-resolver.d.ts +10 -0
  248. package/dist-server/service/twin-subject/item-resolver.js +93 -0
  249. package/dist-server/service/twin-subject/item-resolver.js.map +1 -0
  250. package/dist-server/service/twin-subject/subject-query-shape.d.ts +22 -0
  251. package/dist-server/service/twin-subject/subject-query-shape.js +88 -0
  252. package/dist-server/service/twin-subject/subject-query-shape.js.map +1 -0
  253. package/dist-server/service/twin-subject/twin-subject-backfill.d.ts +23 -0
  254. package/dist-server/service/twin-subject/twin-subject-backfill.js +159 -0
  255. package/dist-server/service/twin-subject/twin-subject-backfill.js.map +1 -0
  256. package/dist-server/service/twin-subject/twin-subject-event.d.ts +14 -0
  257. package/dist-server/service/twin-subject/twin-subject-event.js +111 -0
  258. package/dist-server/service/twin-subject/twin-subject-event.js.map +1 -0
  259. package/dist-server/service/twin-subject/twin-subject-fast-path.d.ts +39 -0
  260. package/dist-server/service/twin-subject/twin-subject-fast-path.js +295 -0
  261. package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +1 -0
  262. package/dist-server/service/twin-subject/twin-subject-query.d.ts +27 -0
  263. package/dist-server/service/twin-subject/twin-subject-query.js +232 -0
  264. package/dist-server/service/twin-subject/twin-subject-query.js.map +1 -0
  265. package/dist-server/service/twin-subject/twin-subject-writer.d.ts +27 -0
  266. package/dist-server/service/twin-subject/twin-subject-writer.js +166 -0
  267. package/dist-server/service/twin-subject/twin-subject-writer.js.map +1 -0
  268. package/dist-server/service/twin-target/twin-target-resolver.js +1 -1
  269. package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -1
  270. package/dist-shared/entity-delta.d.ts +1 -1
  271. package/dist-shared/entity-delta.js +3 -3
  272. package/dist-shared/entity-delta.js.map +1 -1
  273. package/dist-shared/touched-items.js +2 -2
  274. package/dist-shared/touched-items.js.map +1 -1
  275. package/dist-shared/twin-level.js +1 -1
  276. package/dist-shared/twin-level.js.map +1 -1
  277. package/package.json +3 -3
  278. package/server/engine/attention-digest.ts +9 -9
  279. package/server/engine/canonical-ingest.ts +170 -13
  280. package/server/engine/command-routing.ts +1 -1
  281. package/server/engine/declared-stimulus.ts +1 -1
  282. package/server/engine/energy-topology.ts +1 -1
  283. package/server/engine/ingest-dedupe.ts +149 -0
  284. package/server/engine/ingest-health.ts +234 -20
  285. package/server/engine/integration-probes.ts +7 -7
  286. package/server/engine/kpi-baseline.ts +4 -4
  287. package/server/engine/kpi-fold.ts +395 -27
  288. package/server/engine/kpi-query.ts +212 -46
  289. package/server/engine/live-feed-registry.ts +1 -1
  290. package/server/engine/load-meter.ts +1 -1
  291. package/server/engine/local-declarations.ts +13 -13
  292. package/server/engine/log.ts +1 -1
  293. package/server/engine/measured-estimator.ts +1 -1
  294. package/server/engine/measured-yield.ts +1 -1
  295. package/server/engine/model-basis.ts +2 -2
  296. package/server/engine/model-gap.ts +3 -3
  297. package/server/engine/model-vocabulary.ts +1 -1
  298. package/server/engine/oee-accumulator.ts +4 -4
  299. package/server/engine/property-effects.ts +2 -2
  300. package/server/engine/read-time.ts +77 -0
  301. package/server/engine/restart-policy.ts +1 -1
  302. package/server/engine/runtime-key.ts +1 -1
  303. package/server/engine/spec-coverage.ts +3 -3
  304. package/server/engine/stage-path.ts +5 -5
  305. package/server/engine/state-axes.ts +2 -2
  306. package/server/engine/travel-estimator.ts +2 -2
  307. package/server/engine/twin-engine.ts +764 -201
  308. package/server/engine/warm-start.ts +6 -6
  309. package/server/index.ts +1 -1
  310. package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +1 -1
  311. package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +3 -3
  312. package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +2 -2
  313. package/server/migrations/1786300000000-IndexEventTypeByTime.ts +48 -0
  314. package/server/migrations/1786400000000-IndexEventCreatedAt.ts +38 -0
  315. package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +93 -0
  316. package/server/migrations/index.ts +7 -1
  317. package/server/routes.ts +68 -5
  318. package/server/service/index.ts +4 -1
  319. package/server/service/reference/control-routing.ts +1 -1
  320. package/server/service/reference/discovery-result.ts +3 -3
  321. package/server/service/reference/hook-contract.ts +103 -0
  322. package/server/service/reference/ingest-space.ts +4 -4
  323. package/server/service/reference/knob-defaults.ts +4 -4
  324. package/server/service/reference/reference-adapter.ts +166 -9
  325. package/server/service/reference/reference-hook.ts +145 -0
  326. package/server/service/reference/reference-live.ts +92 -14
  327. package/server/service/reference/reference-master.ts +39 -17
  328. package/server/service/reference/reference-resolver.ts +10 -10
  329. package/server/service/reference/twin-reference.ts +3 -3
  330. package/server/service/twin-attention/twin-attention-query.ts +1 -1
  331. package/server/service/twin-audit/command-audit.ts +1 -1
  332. package/server/service/twin-audit/twin-audit-event.ts +2 -2
  333. package/server/service/twin-audit/twin-audit-query.ts +1 -1
  334. package/server/service/twin-control/twin-control-mutation.ts +1 -1
  335. package/server/service/twin-event/twin-event-keys.ts +37 -5
  336. package/server/service/twin-event/twin-event-type.ts +18 -1
  337. package/server/service/twin-event/twin-event.ts +32 -6
  338. package/server/service/twin-forecast/forecast-metrics.ts +1 -1
  339. package/server/service/twin-forecast/gap-analytics.ts +1 -1
  340. package/server/service/twin-forecast/twin-forecast-query.ts +30 -11
  341. package/server/service/twin-ingest-window/twin-ingest-window-query.ts +1 -1
  342. package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +1 -1
  343. package/server/service/twin-instance/twin-instance.ts +4 -4
  344. package/server/service/twin-journal/event-type-split-shape.ts +112 -0
  345. package/server/service/twin-journal/event-type-split.ts +144 -0
  346. package/server/service/twin-journal/journal-order.ts +20 -0
  347. package/server/service/twin-journal/twin-journal-query.ts +224 -20
  348. package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +57 -3
  349. package/server/service/twin-metrics/twin-metrics-query.ts +14 -1
  350. package/server/service/twin-model/axis-journal-evidence.ts +1 -1
  351. package/server/service/twin-model/epcis-coverage.ts +36 -1
  352. package/server/service/twin-model/iec61850-coverage.ts +1 -1
  353. package/server/service/twin-model/isa95-coverage.ts +45 -8
  354. package/server/service/twin-model/item-ref.ts +2 -2
  355. package/server/service/twin-model/name-index.ts +1 -1
  356. package/server/service/twin-model/project-structure.ts +4 -4
  357. package/server/service/twin-model/twin-equipment.ts +2 -2
  358. package/server/service/twin-model/twin-lineage-query.ts +16 -5
  359. package/server/service/twin-model/twin-location.ts +1 -1
  360. package/server/service/twin-model/twin-model-item-query.ts +14 -14
  361. package/server/service/twin-model/twin-model-mutation.ts +4 -4
  362. package/server/service/twin-model/twin-model-query.ts +117 -28
  363. package/server/service/twin-model/twin-model-tree-query.ts +25 -1
  364. package/server/service/twin-model/twin-operation.ts +2 -2
  365. package/server/service/twin-space/move-space.ts +3 -3
  366. package/server/service/twin-space/representation-role.ts +15 -0
  367. package/server/service/twin-space/space-integrity.ts +1 -1
  368. package/server/service/twin-space/twin-space-representation.ts +37 -0
  369. package/server/service/twin-space/twin-space-resolver.ts +39 -7
  370. package/server/service/twin-structure/twin-structure.ts +3 -3
  371. package/server/service/twin-subject/event-subjects.ts +155 -0
  372. package/server/service/twin-subject/index.ts +10 -0
  373. package/server/service/twin-subject/item-resolver.ts +92 -0
  374. package/server/service/twin-subject/subject-query-shape.ts +84 -0
  375. package/server/service/twin-subject/twin-subject-backfill.ts +196 -0
  376. package/server/service/twin-subject/twin-subject-event.ts +101 -0
  377. package/server/service/twin-subject/twin-subject-fast-path.ts +346 -0
  378. package/server/service/twin-subject/twin-subject-query.ts +210 -0
  379. package/server/service/twin-subject/twin-subject-writer.ts +189 -0
  380. package/server/service/twin-target/twin-target-resolver.ts +1 -1
  381. package/shared/entity-delta.ts +3 -3
  382. package/shared/touched-items.ts +2 -2
  383. package/shared/twin-level.ts +1 -1
  384. package/test/adopt-structure-live.test.ts +3 -3
  385. package/test/aggregate-not-findone.test.ts +112 -0
  386. package/test/attention-digest.test.ts +6 -6
  387. package/test/axis-read.test.ts +2 -2
  388. package/test/boot-resume.test.ts +20 -7
  389. package/test/broadcast-period.test.ts +2 -2
  390. package/test/broadcast-queue-overflow.test.ts +244 -0
  391. package/test/canonical-ingest-vocabularies.test.ts +179 -7
  392. package/test/canonical-quantity-door.test.ts +2 -2
  393. package/test/checkpoint-refuses-empty.test.ts +2 -2
  394. package/test/connector-capability-declaration.test.ts +88 -0
  395. package/test/control-capability.test.ts +1 -1
  396. package/test/cursor-stall-not-read-failure.test.ts +2 -2
  397. package/test/declaration-reaches-model.test.ts +5 -5
  398. package/test/declared-stimulus.test.ts +4 -4
  399. package/test/discovery-result.test.ts +2 -2
  400. package/test/entity-delta.test.ts +4 -4
  401. package/test/event-subjects.test.ts +147 -0
  402. package/test/event-time-column.test.ts +3 -3
  403. package/test/event-type-split.test.ts +155 -0
  404. package/test/fold-gap-speaks.test.ts +53 -0
  405. package/test/forecast-metrics.test.ts +1 -1
  406. package/test/forecast-tuning.test.ts +1 -1
  407. package/test/ingest-health-engine.test.ts +2 -2
  408. package/test/ingest-health-wiring.test.ts +7 -5
  409. package/test/ingest-health.test.ts +6 -6
  410. package/test/ingest-history.test.ts +2 -2
  411. package/test/ingest-idempotent.test.ts +132 -0
  412. package/test/ingest-space.test.ts +3 -3
  413. package/test/integration-probes.test.ts +3 -3
  414. package/test/integration-runner.test.ts +1 -1
  415. package/test/item-ref.test.ts +1 -1
  416. package/test/journal-order-axis.test.ts +80 -0
  417. package/test/journal-read-discipline.test.ts +16 -6
  418. package/test/journal-retention.test.ts +171 -29
  419. package/test/journal-sort-axis.test.ts +1 -1
  420. package/test/journal-write-door.test.ts +3 -3
  421. package/test/journal-write-trend.test.ts +1 -1
  422. package/test/kernel-kind-guard.test.ts +3 -3
  423. package/test/knob-defaults.test.ts +1 -1
  424. package/test/kpi-baseline-db.test.ts +2 -2
  425. package/test/kpi-fold.test.ts +185 -14
  426. package/test/kpi-query-bench.test.ts +3 -3
  427. package/test/live-cursor-wiring.test.ts +6 -6
  428. package/test/live-kernel-facts.test.ts +11 -11
  429. package/test/live-mirror-parity.test.ts +3 -3
  430. package/test/load-meter.test.ts +1 -1
  431. package/test/local-declarations.test.ts +10 -10
  432. package/test/log-stamp.test.ts +1 -1
  433. package/test/measured-yield.test.ts +2 -2
  434. package/test/merge-streams.test.ts +70 -0
  435. package/test/mirror-resumes-from-checkpoint.test.ts +13 -13
  436. package/test/model-basis.test.ts +2 -2
  437. package/test/model-gap.test.ts +4 -4
  438. package/test/model-vocabulary.test.ts +2 -2
  439. package/test/move-space.test.ts +1 -1
  440. package/test/oee-accumulator.test.ts +2 -2
  441. package/test/operational-vocabulary.test.ts +2 -2
  442. package/test/project-structure-db.test.ts +2 -2
  443. package/test/projection-reaches-screen.test.ts +2 -2
  444. package/test/property-effects.test.ts +3 -3
  445. package/test/read-failure-ledger.test.ts +106 -0
  446. package/test/read-failure-visible.test.ts +7 -7
  447. package/test/read-time.test.ts +118 -0
  448. package/test/reference-grounding.test.ts +1 -1
  449. package/test/reference-hook.test.ts +86 -0
  450. package/test/registry-key-guard.test.ts +1 -1
  451. package/test/rename-twin.test.ts +81 -0
  452. package/test/restart-policy.test.ts +3 -3
  453. package/test/resync-origin-site.test.ts +3 -3
  454. package/test/revision-axis.test.ts +14 -8
  455. package/test/runtime-key.test.ts +3 -3
  456. package/test/snapshot-freshness.test.ts +4 -4
  457. package/test/source-outcome-audit.test.ts +1 -1
  458. package/test/space-integrity.test.ts +1 -1
  459. package/test/space-representation.test.ts +66 -0
  460. package/test/space-scope-includes-stopped.test.ts +55 -0
  461. package/test/spec-coverage.test.ts +1 -1
  462. package/test/standard-coverage.test.ts +43 -1
  463. package/test/status-tally.test.ts +1 -1
  464. package/test/structure-revision-db.test.ts +7 -7
  465. package/test/subject-fast-path.test.ts +148 -0
  466. package/test/subject-read-model.test.ts +175 -0
  467. package/test/touched-items.test.ts +2 -2
  468. package/test/twin-audit.test.ts +1 -1
  469. package/test/twin-event-keys.test.ts +49 -3
  470. package/test/twin-model-item-db.test.ts +5 -5
  471. package/test/twin-model-tree-db.test.ts +35 -2
  472. package/test/twin-origin-resync.test.ts +1 -1
  473. package/test/warm-start-seam.test.ts +1 -1
  474. package/test/warm-start.test.ts +6 -6
  475. package/test/withheld-door.test.ts +95 -0
  476. package/test/yield-loop.test.ts +4 -4
  477. package/tsconfig.shared.tsbuildinfo +1 -1
  478. package/tsconfig.tsbuildinfo +1 -1
@@ -19,6 +19,8 @@ exports.computeTwinKpi = computeTwinKpi;
19
19
  * 어떤 기준을 썼는지는 결과의 `window.basis` 로 밝힌다 — 숨기면 사용자가 숫자를 오해한다.
20
20
  */
21
21
  const log_js_1 = require("./log.js");
22
+ const event_type_split_shape_js_1 = require("../service/twin-journal/event-type-split-shape.js");
23
+ const read_time_js_1 = require("./read-time.js");
22
24
  const typeorm_1 = require("typeorm");
23
25
  const twin_kernel_1 = require("@operato/twin-kernel");
24
26
  const shell_1 = require("@things-factory/shell");
@@ -39,16 +41,24 @@ const kpi_baseline_js_1 = require("./kpi-baseline.js");
39
41
  * 나눈 값이라, 두 사실이 한 폴드에 들어와야 창이 어긋날 수 없다. 표본(`energy.measured`)은 읽지 않는다 —
40
42
  * 저널에 없고(§4.1 인제스트 계약) 여기서 필요한 것은 마감된 구간뿐이다.
41
43
  */
42
- const BUSINESS_EVENTS = ['task.status', 'order.status', 'energy.demand.window'];
43
- /** 한 번에 접을 이벤트 상한 — 큰 구간이 서버를 붙잡지 않게. */
44
+ const BUSINESS_EVENTS = ['task.status', 'order.status', 'energy.demand.window', 'energy.generation.period', 'energy.usage.period', 'energy.bill'];
45
+ /** 한 번에 계산할 이벤트 상한 — 큰 구간이 서버를 붙잡지 않게. */
44
46
  const MAX_EVENTS = 20000;
47
+ /**
48
+ * 종류를 나눠 물을 때 만들 갈래 수의 상한 — 대상 × 종류.
49
+ *
50
+ * 갈래마다 상한(`MAX_EVENTS`)까지 받아야 합친 결과가 정확하다(§`mergeStreams`). 그래서 갈래가 늘면
51
+ * 메모리가 그만큼 든다. 셋(종류) × 둘(트윈)까지는 최악에 12만 행이고, 그 이상은 예전 한 질의로 간다 —
52
+ * 느리지만 답은 같다.
53
+ */
54
+ const KPI_MAX_SPLIT = 6;
45
55
  /**
46
56
  * 노드 → 구역 지도. **저널에는 구역이 없다**(노드까지만 있다) — 모델 정의의 `locations[].parentId` 가
47
57
  * 마스터 계층에서 온 소속이다. 기동하지 않은 트윈도 모델은 남아 있어 이 경로가 가장 튼튼하다.
48
58
  *
49
59
  * **한 홉만 보면 안 된다.** `parentId` 는 구역일 수도 있고 중간 단(라인·셀)일 수도 있다. 라인이 끼면
50
60
  * 한 홉짜리 코드는 "구역이 LINE-1" 이라고 답하고, 그 id 는 구역 목록에 없으므로 그 아래 스테이션이
51
- * **집계에서 조용히 빠진다**(에러 없이 틀린 숫자). 그래서 커널 `hierarchyOf` 로 사슬을 끝까지 걷는다 —
61
+ * **집계에서 오류 없이 빠진다**(에러 없이 틀린 숫자). 그래서 커널 `hierarchyOf` 로 사슬을 끝까지 걷는다 —
52
62
  * 걷는 규칙은 커널에 한 벌뿐이고 소비처는 그것만 부른다.
53
63
  */
54
64
  /**
@@ -170,7 +180,7 @@ async function locationAreaMap(domainId, instanceIds, spaceId) {
170
180
  *
171
181
  * 여러 에너지 트윈이 한 현장에 있으면 **처음 선언된 것 하나**를 쓴다. 평균을 내거나 합치지 않는다:
172
182
  * 요금은 계약이고, 두 계약을 섞은 단가는 어느 계약도 아니다. 서로 다른 단가가 선언돼 있으면 그것을
173
- * 밝힌다(조용히 하나를 고르면 왜 그 금액인지 아무도 설명할 수 없다).
183
+ * 밝힌다(알리지 않고 하나를 고르면 왜 그 금액인지 아무도 설명할 수 없다).
174
184
  */
175
185
  async function tariffOf(domainId, instanceIds) {
176
186
  if (!instanceIds.length)
@@ -188,12 +198,15 @@ async function tariffOf(domainId, instanceIds) {
188
198
  };
189
199
  const d = num(twin_kernel_1.EMS_PROPERTY.demandChargePerKW);
190
200
  const e = num(twin_kernel_1.EMS_PROPERTY.energyChargePerKWh);
201
+ /* 요금적용전력 — 있으면 기본요금이 조건부 파생이 아니라 사실이 된다(§`electricityCost`). */
202
+ const b = num(twin_kernel_1.EMS_PROPERTY.billingDemandKW);
191
203
  const cur = props.get(twin_kernel_1.EMS_PROPERTY.currency);
192
204
  if (d === undefined && e === undefined)
193
205
  continue;
194
206
  found.push({
195
207
  ...(d !== undefined ? { demandChargePerKW: d } : {}),
196
208
  ...(e !== undefined ? { energyChargePerKWh: e } : {}),
209
+ ...(b !== undefined ? { billingDemandKW: b } : {}),
197
210
  ...(cur ? { currency: String(cur) } : {})
198
211
  });
199
212
  }
@@ -201,7 +214,10 @@ async function tariffOf(domainId, instanceIds) {
201
214
  if (!found.length)
202
215
  return {};
203
216
  const first = found[0];
204
- const same = found.every(t => t.demandChargePerKW === first.demandChargePerKW && t.energyChargePerKWh === first.energyChargePerKWh && t.currency === first.currency);
217
+ const same = found.every(t => t.demandChargePerKW === first.demandChargePerKW &&
218
+ t.energyChargePerKWh === first.energyChargePerKWh &&
219
+ t.billingDemandKW === first.billingDemandKW &&
220
+ t.currency === first.currency);
205
221
  return { tariff: first, ...(same ? {} : { conflict: true }) };
206
222
  }
207
223
  async function resourceFeederMap(domainId, instanceIds) {
@@ -267,11 +283,33 @@ async function resourceFeederMap(domainId, instanceIds) {
267
283
  return { resourceFeeder, meterFeeder, labels };
268
284
  }
269
285
  /**
270
- * 이 공간의 에너지 트윈들의 계통도 — 모델을 읽어 트리로 접는다.
286
+ * 이 공간의 에너지 트윈들의 계통도 — 모델을 읽어 트리로 처리한다.
271
287
  *
272
288
  * 여러 트윈이 한 공간에 있으면 **각 트윈의 뿌리를 나란히** 둔다(하나로 합치면 없는 결선을 그리게 된다).
273
289
  * 에너지 트윈이 없으면 만들지 않는다 — 빈 그림은 고장으로 읽힌다.
274
290
  */
291
+ /**
292
+ * 이 트윈들이 이어받고 있는 **마지막 청구서** — 요금적용전력의 우선 근거.
293
+ *
294
+ * 저널이 아니라 상태에서 읽는 이유: 청구는 한 달에 한 번이라 하루 창의 조회에는 걸리지 않는다.
295
+ * 그런데 그 값은 **그 창에도 적용된다**(요금 제도가 그렇게 군다). 커널이 재기동에서도 이어받으므로
296
+ * 상태가 그 자리다.
297
+ *
298
+ * 트윈이 여럿이면 가장 늦게 끝난 것을 쓴다 — 현장 하나에 청구가 여럿이면 그것은 다른 문제이고,
299
+ * 여기서 골라 감추지 않는다.
300
+ */
301
+ function latestBillOf(domainId, instanceIds) {
302
+ let best;
303
+ for (const instanceId of instanceIds) {
304
+ const bill = twin_engine_js_1.TwinEngine.snapshot(domainId, instanceId)?.energy?.lastBill;
305
+ if (!bill?.to)
306
+ continue;
307
+ if (!best || Date.parse(bill.to) > Date.parse(best.to)) {
308
+ best = { to: String(bill.to), ...(Number.isFinite(Number(bill.billingDemandKW)) ? { billingDemandKW: Number(bill.billingDemandKW) } : {}) };
309
+ }
310
+ }
311
+ return best;
312
+ }
275
313
  async function energyTopologyOf(domainId, instanceIds, energy) {
276
314
  if (!instanceIds.length)
277
315
  return undefined;
@@ -294,16 +332,46 @@ async function energyTopologyOf(domainId, instanceIds, energy) {
294
332
  }
295
333
  return merged.roots.length ? merged : undefined;
296
334
  }
297
- /** 이 대상들의 마지막 업무 기록 시각(ISO). 기록이 없으면 null. */
335
+ /**
336
+ * 이 대상들의 **마지막 업무 기록 시각**(ISO). 기록이 없으면 null.
337
+ *
338
+ * ── 두 가지를 함께 고쳤다 (2026-08-28 실측) ─────────────────────────────────
339
+ * 예전 판은 이랬다.
340
+ *
341
+ * findOne({ where: { domain: { id }, instanceId: In(…), eventType: In(셋) }, order: { revision: 'DESC' } })
342
+ *
343
+ * **① 관계로 물어 두 단계 질의가 된다.** 안쪽에 상한이 없어 조건에 맞는 행을 **전부 만든 뒤** 하나를
344
+ * 고른다 — 그 트윈의 업무 사실이 97만 행이고 `payload` 까지 실려 온다. 이 파일의 다른 자리가 같은 것을
345
+ * 실측해 적어 두었다(§`twinEvents`: 0.024초 대 12초). 서버 로그에 「느린 질의」로 찍혀 있던 것이
346
+ * 이것이고, 현장 성과 화면의 7.2초가 여기서 나왔다.
347
+ *
348
+ * **② 「마지막 시각」을 원하면서 `revision` 으로 정렬했다.** 그 둘은 어긋난다 — 연결된 시스템을 되읽는
349
+ * 트윈은 원본의 시각을 싣고 도착 순서로 리비전을 받으므로, 리비전이 가장 큰 행이 4월 사실일 수 있다
350
+ * (같은 어긋남을 오늘 저널 목록에서도 고쳤다). 창의 끝을 그 값으로 잡으면 **창 자체가 틀린다.**
351
+ *
352
+ * 지금은 종류마다 `MAX(eventTime)` 을 묻는다. `(domain, instance, event_type, event_time)` 색인의
353
+ * 끝을 집으므로 종류당 한 번이고, 셋 중 가장 늦은 것이 답이다.
354
+ */
298
355
  async function latestEventTime(domainId, instanceIds) {
299
356
  if (instanceIds.length === 0)
300
357
  return null;
301
- const row = await (0, shell_1.getRepository)(twin_event_js_1.TwinEvent).findOne({
302
- where: { domain: { id: domainId }, instanceId: (0, typeorm_1.In)(instanceIds), eventType: (0, typeorm_1.In)(BUSINESS_EVENTS) },
303
- order: { revision: 'DESC' }
304
- });
305
- /* 소비처는 ISO 문자열을 기대한다(시각 축의 공용 표기) — 컬럼이 날짜가 됐으므로 여기서 옮긴다. */
306
- return row?.eventTime ? row.eventTime.toISOString() : null;
358
+ const rows = await Promise.all(instanceIds.flatMap(instanceId => BUSINESS_EVENTS.map(eventType => (0, shell_1.getReadRepository)(twin_event_js_1.TwinEvent)
359
+ .createQueryBuilder('e')
360
+ .select('MAX(e.eventTime)', 'at')
361
+ .where('e.domain = :domainId', { domainId })
362
+ .andWhere('e.instanceId = :instanceId', { instanceId })
363
+ .andWhere('e.eventType = :eventType', { eventType })
364
+ .getRawOne()
365
+ .catch(() => null))));
366
+ /* 드라이버가 준 값은 시간대가 없을 수 있다 — 컬럼 변환을 지나게 한다(§`hydratedDate`). */
367
+ const repo = (0, shell_1.getReadRepository)(twin_event_js_1.TwinEvent);
368
+ let latest = null;
369
+ for (const r of rows) {
370
+ const at = (0, read_time_js_1.hydratedDate)(repo, 'eventTime', r?.at);
371
+ if (at && (!latest || at.getTime() > latest.getTime()))
372
+ latest = at;
373
+ }
374
+ return latest ? latest.toISOString() : null;
307
375
  }
308
376
  /**
309
377
  * 창 구간의 업무 이벤트 — 창보다 `lookbackMs` 만큼 앞까지 읽는다(완료된 작업의 시작이 창 밖일 수 있다).
@@ -369,23 +437,79 @@ async function fetchEvents(domainId, instanceIds, fromMs, toMs, lookbackMs) {
369
437
  * 남는 비용을 감추지 않는다: `payload` 파싱은 여전히 메인 스레드 일이고 연결을 나눠도 나뉘지 않는다
370
438
  * (실측 평균 1.5초). 그것을 더 줄이려면 폴드가 payload 에서 무엇을 쓰는지 좁혀야 한다 — 별 건이다.
371
439
  */
372
- const raw = await (0, shell_1.getReadRepository)(twin_event_js_1.TwinEvent)
440
+ const from = new Date(fromMs - lookbackMs);
441
+ const to = new Date(toMs);
442
+ /**
443
+ * 한 갈래를 묻는다 — **종류를 정확일치로** 준다. 그것이 이 고침의 요점이다.
444
+ */
445
+ const readOne = (instanceId, eventType) => (0, shell_1.getReadRepository)(twin_event_js_1.TwinEvent)
373
446
  .createQueryBuilder('e')
374
- .select(['e.eventType AS "eventType"', 'e.eventTime AS "eventTime"', 'e.payload AS "payload"'])
447
+ .select(['e.eventType AS "eventType"', 'e.eventTime AS "eventTime"', 'e.revision AS "revision"', 'e.payload AS "payload"'])
375
448
  .where('e.domain = :domainId', { domainId })
376
- .andWhere('e.instanceId IN (:...instanceIds)', { instanceIds })
377
- .andWhere('e.eventType IN (:...types)', { types: BUSINESS_EVENTS })
378
- .andWhere('e.eventTime BETWEEN :from AND :to', { from: new Date(fromMs - lookbackMs), to: new Date(toMs) })
449
+ .andWhere('e.instanceId = :instanceId', { instanceId })
450
+ .andWhere('e.eventType = :eventType', { eventType })
451
+ .andWhere('e.eventTime BETWEEN :from AND :to', { from, to })
379
452
  .orderBy('e.eventTime', 'ASC')
380
453
  .addOrderBy('e.revision', 'ASC')
381
454
  .limit(MAX_EVENTS)
382
455
  .getRawMany();
383
- /* 상한에 정확히 닿으면 **더 있었을 수 있다** — 그 사실을 돌려준다. 조용히 잘라내면 숫자가 조용히
456
+ /*
457
+ * ── **종류를 나눠 묻는다** (2026-08-28 실측) ────────────────────────────────
458
+ *
459
+ * 예전에는 `eventType IN (셋)` + 시각 범위 + 시각순이었다. 그 모양에서 sqlite 는 종류 색인을 버리고
460
+ * 시각 색인으로 **구간 전체를 걸으며 행마다 종류를 확인한다.** 승화푸드(저널 3,140만행) 실측:
461
+ *
462
+ * 창 1시간 구간 안 0행 · 쓰는 행 0 40밀리초
463
+ * 창 24시간 구간 안 254,185행 · 쓰는 행 0 3,776밀리초 ← 25만 행을 걸어 0건을 쓴다
464
+ * 창 7일 구간 안 2,070,625행 · 34,143 173밀리초 ← 상한에 일찍 닿아 오히려 빠르다
465
+ *
466
+ * 창을 넓히면 빨라지는 것 자체가 신호였다. 종류를 정확일치로 물으면
467
+ * `ix_twin_event_7 (domain, instance, event_type, event_time)` 이 범위까지 색인에서 준다.
468
+ *
469
+ * 나눌 수를 제한한다 — 갈래마다 상한까지 받아야 정확하므로(§`mergeStreams`) 갈래가 많으면 메모리가
470
+ * 그만큼 든다. 넘으면 예전 한 질의로 간다(느리지만 답은 같다).
471
+ */
472
+ const pairs = instanceIds.length * BUSINESS_EVENTS.length;
473
+ let raw;
474
+ let mergedCapped = false;
475
+ if (pairs && pairs <= KPI_MAX_SPLIT) {
476
+ const streams = await Promise.all(instanceIds.flatMap(instanceId => BUSINESS_EVENTS.map(eventType => readOne(instanceId, eventType))));
477
+ const merged = (0, event_type_split_shape_js_1.mergeStreams)(streams, MAX_EVENTS, (a, b) => {
478
+ const at = String(a.eventTime ?? '').localeCompare(String(b.eventTime ?? ''));
479
+ if (at !== 0)
480
+ return at;
481
+ return Number(a.revision ?? 0) - Number(b.revision ?? 0);
482
+ });
483
+ if (merged.underfilled) {
484
+ /* 있을 수 없는 일이다 — 갈래가 상한보다 많이 받아 왔다. 조용히 쓰지 않고 말한다. */
485
+ (0, log_js_1.twinWarn)('[twin-kpi] a per-type stream returned more than the cap — the merge cannot be trusted; falling back');
486
+ raw = [];
487
+ }
488
+ else {
489
+ raw = merged.items;
490
+ mergedCapped = merged.capped;
491
+ }
492
+ }
493
+ else {
494
+ raw = await (0, shell_1.getReadRepository)(twin_event_js_1.TwinEvent)
495
+ .createQueryBuilder('e')
496
+ .select(['e.eventType AS "eventType"', 'e.eventTime AS "eventTime"', 'e.revision AS "revision"', 'e.payload AS "payload"'])
497
+ .where('e.domain = :domainId', { domainId })
498
+ .andWhere('e.instanceId IN (:...instanceIds)', { instanceIds })
499
+ .andWhere('e.eventType IN (:...types)', { types: BUSINESS_EVENTS })
500
+ .andWhere('e.eventTime BETWEEN :from AND :to', { from, to })
501
+ .orderBy('e.eventTime', 'ASC')
502
+ .addOrderBy('e.revision', 'ASC')
503
+ .limit(MAX_EVENTS)
504
+ .getRawMany();
505
+ mergedCapped = raw.length >= MAX_EVENTS;
506
+ }
507
+ /* 상한에 정확히 닿으면 **더 있었을 수 있다** — 그 사실을 돌려준다. 알리지 않고 잘라내면 숫자가 알리지 않고
384
508
  * 작아지고, 사용자는 그것을 성과 하락으로 읽는다(가장 나쁜 종류의 거짓이다). */
385
509
  return {
386
510
  /* 폴드는 ISO 문자열 축으로 수행된다(kpi-fold) — 경계에서 한 번만 옮긴다. */
387
511
  events: raw.map(r => ({ eventType: r.eventType, eventTime: isoOf(r.eventTime), payload: jsonOf(r.payload) })),
388
- capped: raw.length >= MAX_EVENTS
512
+ capped: mergedCapped
389
513
  };
390
514
  }
391
515
  /**
@@ -425,8 +549,30 @@ function jsonOf(v) {
425
549
  /**
426
550
  * 집계 대상 트윈 목록.
427
551
  *
428
- * 공간이 주어지면 **가동 중이고 운영 목적인** 것만 모은다 — 벤치 사본은 사용자가 보는 현장이 아니다.
552
+ * 공간이 주어지면 그 공간의 **운영 목적** 트윈을 모은다 — 벤치 사본은 사용자가 보는 현장이 아니다.
429
553
  * 트윈이 지정되면 그것 하나. 둘 다 없으면 무엇을 재야 할지 알 수 없으므로 명시 실패한다.
554
+ *
555
+ * ── **「지금 도는가」로 걸러내지 않는다** (2026-08-28 실측) ──────────────────
556
+ * 여기에 `running` 조건이 있었다. 그래서 멈춘 트윈은 현장으로 물었을 때 대상에서 빠졌고, 화면은
557
+ * 「기록이 없습니다」라고 말했다 — **저널에는 있는데.**
558
+ *
559
+ * twinEventList(instanceId: "ems-plant-1") 1,792건
560
+ * twinEventList(spaceId: "ems-plant-1-site") 0건 ← 같은 트윈, 같은 도메인
561
+ *
562
+ * 규모로 보면 이것은 예외가 아니라 기본값이었다: 등록된 트윈 30개 중 **도는 것이 2개, 멈춘 것이 28개**다.
563
+ * 즉 현장으로 묻는 저널 조회가 대부분의 현장에서 거짓 「없다」를 냈다.
564
+ *
565
+ * 지운 이유는 두 가지다.
566
+ *
567
+ * ① **저널은 기록이고, 프로세스가 지금 도는지는 다른 축이다.** 창 안에 있는 사실은 사실이다.
568
+ * 아래 줄이 트윈을 지정한 경우에 대해 이미 그 원칙을 적어 두었다(「정지·비등록이어도 저널은 남아
569
+ * 있을 수 있다」). 같은 원칙이 현장 경로에만 적용되지 않고 있었다.
570
+ * ② 옛 주석이 대는 이유(「벤치 사본은 사용자가 보는 현장이 아니다」)는 **벤치 제외만** 설명한다.
571
+ * `running` 에는 근거가 적혀 있지 않았다.
572
+ *
573
+ * **바뀌는 것**: 현장 KPI 가 이제 멈춘 트윈의 지난 기록도 센다. 창 안의 사실을 세는 것이 그 지표의
574
+ * 정의이므로 그것이 맞다. 「지금 도는 것만」이 필요한 화면이 생기면 그때 **이름 있는 축**으로 받는다 —
575
+ * 여기에 숨겨 두면 두 물음이 한 칸을 쓴다.
430
576
  */
431
577
  async function resolveTargets(input) {
432
578
  if (!input.instanceId && !input.spaceId)
@@ -434,7 +580,7 @@ async function resolveTargets(input) {
434
580
  const list = await twin_engine_js_1.TwinEngine.list(input.domainId);
435
581
  const rows = input.instanceId
436
582
  ? list.filter((x) => x?.instanceId === input.instanceId)
437
- : list.filter((x) => x?.running && x.spaceId === input.spaceId && x.purpose !== 'bench' && x.instanceId);
583
+ : list.filter((x) => x?.spaceId === input.spaceId && x.purpose !== 'bench' && x.instanceId);
438
584
  /* 목록에서 못 찾아도(정지·비등록) 지정된 트윈은 그대로 조회한다 — 저널은 남아 있을 수 있다. */
439
585
  const instanceIds = rows.length ? rows.map((x) => x.instanceId) : input.instanceId ? [input.instanceId] : [];
440
586
  /* 컬럼이 NOT NULL 이라 'unknown' 으로 메울 일이 없다 — 비면 계약이 깨진 것이지 모르는 상태가 아니다. */
@@ -500,12 +646,23 @@ async function computeTwinKpi(input) {
500
646
  const feederMap = await resourceFeederMap(input.domainId, instanceIds);
501
647
  /* 요금 단가도 축과 무관하게 읽는다 — 금액은 같은 창의 전력량·최대수요에서 나오는 사실이다. */
502
648
  const tariffFound = await tariffOf(input.domainId, instanceIds);
649
+ /*
650
+ * ── 요금적용전력은 **청구서가 먼저다** (2026-08-29) ─────────────────────────
651
+ *
652
+ * 그 값은 최근 몇 달의 최고 수요로 **매달 다시 정해진다.** 선언에 박아 두면 매달 사람이 갱신해야
653
+ * 하고, 안 하면 낡은 값으로 기본요금이 조용히 틀린다.
654
+ *
655
+ * 원본이 청구서에 그 값을 실어 매일 보낸다. 창 밖의 청구서는 저널 조회에 걸리지 않으므로
656
+ * **커널 상태에 이어져 있는 마지막 청구서**를 함께 넘긴다(§`EnergyState.lastBill`).
657
+ */
658
+ const lastBill = latestBillOf(input.domainId, instanceIds);
503
659
  const kpi = (0, kpi_fold_js_1.foldKpi)(events, { fromMs, toMs }, {
504
660
  groupBy: input.groupBy,
505
661
  locationArea: areaMap.locationArea,
506
662
  resourceFeeder: feederMap.resourceFeeder,
507
663
  meterFeeder: feederMap.meterFeeder,
508
664
  ...(tariffFound.tariff ? { tariff: tariffFound.tariff } : {}),
665
+ ...(lastBill ? { lastBill } : {}),
509
666
  ...shiftCtx,
510
667
  groupLimit: input.groupLimit
511
668
  });
@@ -514,14 +671,14 @@ async function computeTwinKpi(input) {
514
671
  *
515
672
  * 폴드는 순수하다(로그를 모른다) — 그래서 부르는 쪽이 말한다. 왜 말해야 하나: 이 폴드는 오더의
516
673
  * 완료를 **동의어 목록**으로 판정하고, 그것은 원본의 낱말에서 뜻을 추론하는 것이다. 커넥터가
517
- * 자기 표로 옮기지 않으면 그 낱말이 목록에 없어 **완료가 조용히 0 으로 세어진다.**
674
+ * 자기 표로 옮기지 않으면 그 낱말이 목록에 없어 **완료가 알리지 않고 0 으로 세어진다.**
518
675
  *
519
676
  * 실제로 그랬다: 첫 실 원본의 `FINISHED` 가 목록에 없어 완료 오더가 전부 0 이었고, 사람이 화면을
520
677
  * 보고 의심해서야 드러났다(그 커넥터에는 상태 표가 정의돼 **있으면서** 오더 경로에서 쓰이지 않았다).
521
678
  * 그때 목록에 낱말을 더한 것은 그 사실을 가린 땜빵이었다.
522
679
  *
523
680
  * 그러니 이 로그는 「목록을 늘려라」가 아니라 **「그 커넥터가 옮기지 않았다」**를 뜻한다. 화면을 보지
524
- * 않는 사람도 알아야 하므로 로그에 남긴다 — 조용한 0 이 이 시스템에서 가장 비싼 침묵이다.
681
+ * 않는 사람도 알아야 하므로 로그에 남긴다 — 들어온 것이 없는 0 이 이 시스템에서 가장 비싼 침묵이다.
525
682
  *
526
683
  * 완료가 하나도 없을 때만 말한다: `picking` 처럼 완료가 아닌 낱말이 섞여 있는 것은 정상이고,
527
684
  * 그것까지 매번 경고하면 진짜 신호가 묻힌다.
@@ -561,7 +718,7 @@ async function computeTwinKpi(input) {
561
718
  for (const w of windows) {
562
719
  const { events: pastEvents, capped: pastCapped } = await fetchEvents(input.domainId, instanceIds, w.fromMs, w.toMs, lookbackMs);
563
720
  baseCapped = baseCapped || pastCapped;
564
- /* 축 없이, **같은 교대 정의**로 접는다(정의가 다르면 같은 시간대가 아니게 된다). */
721
+ /* 축 없이, **같은 교대 정의**로 처리한다(정의가 다르면 같은 시간대가 아니게 된다). */
565
722
  const past = (0, kpi_fold_js_1.foldKpi)(pastEvents, w, { locationArea: areaMap.locationArea, ...shiftCtx });
566
723
  const values = {};
567
724
  for (const metric of Object.keys(kpi_target_js_1.TWIN_METRIC))
@@ -599,7 +756,7 @@ async function computeTwinKpi(input) {
599
756
  workP50Ms: b.workP50Ms
600
757
  }))
601
758
  : undefined;
602
- /* 비교 구간 — **같은 길이·같은 규칙**으로 한 번 더 접는다. 기록이 없으면 delta 를 만들지 않는다
759
+ /* 비교 구간 — **같은 길이·같은 규칙**으로 한 번 더 계산한다. 기록이 없으면 delta 를 만들지 않는다
603
760
  * (없는 것을 0 으로 그리면 "변화 없음" 이라는 거짓이 된다). */
604
761
  let comparison;
605
762
  /* 관점 × 비교 교차 — 축별 delta 와 **사라진 축**(전에 있었고 지금 없는 것). */
@@ -609,13 +766,13 @@ async function computeTwinKpi(input) {
609
766
  if (input.compareTo === 'previous' || input.compareTo === 'yesterday') {
610
767
  const w = (0, kpi_fold_js_1.shiftWindow)(fromMs, toMs, input.compareTo);
611
768
  const { events: prevEvents, capped: prevCapped } = await fetchEvents(input.domainId, instanceIds, w.fromMs, w.toMs, lookbackMs);
612
- /* 축이 요청됐으면 비교 구간도 **같은 축**으로 접는다 — 교차("구역별로 어제 대비")를 내기 위해.
769
+ /* 축이 요청됐으면 비교 구간도 **같은 축**으로 처리한다 — 교차("구역별로 어제 대비")를 내기 위해.
613
770
  * 상한은 축이 서로 어긋나지 않게 넉넉히 둔다(현재 축이 상한 안에 있는데 비교 축에서 밀려나면
614
771
  * 있는데 없다고 말하게 된다). */
615
772
  const prev = (0, kpi_fold_js_1.foldKpi)(prevEvents, w, {
616
773
  groupBy: input.groupBy,
617
774
  locationArea: areaMap.locationArea,
618
- ...shiftCtx, // 비교 구간도 **같은 교대 정의**로 접는다(정의가 다르면 축이 어긋난다)
775
+ ...shiftCtx, // 비교 구간도 **같은 교대 정의**로 처리한다(정의가 다르면 축이 어긋난다)
619
776
  groupLimit: input.groupBy ? 100 : undefined
620
777
  });
621
778
  prevCappedFlag = prevCapped;