@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,153 +0,0 @@
1
- /*
2
- * 첫 봉투가 몇 번부터 시작했는지 남기는가.
3
- *
4
- * ── 무엇이 있었나 (2026-09-06 실측, 인티그레이션 레인이 찾음) ────────────────
5
- * plant 아웃박스의 1~20 이 400 으로 거절돼 그 연결에 커서가 없었다. 다음에 온 21번이 첫 봉투로
6
- * 앉으며 커서가 21 로 뛰었다.
7
- *
8
- * 커서가 0 이었다면 같은 21이 구멍이었다. 둘의 차이가 그 결함의 전부다. 스무 건이 구멍으로도 안
9
- * 세어지고, 경고도 없고, backfill 대상도 아니었다 — backfill 은 커서부터 묻는다.
10
- */
11
- import { test } from 'node:test'
12
- import assert from 'node:assert/strict'
13
-
14
- import { firstSeqOf, seqOf, takeInSequence, withSeq } from '../server/service/reference/hook-contract.js'
15
-
16
- const item = (seq: number) => ({ seq, record: { at: '2026-09-06T00:00:00.000Z' } })
17
-
18
- test('★ 커서가 없는 연결에 21번이 오면 그 앞 20건을 못 봤다고 말한다', () => {
19
- const got = takeInSequence({ items: [item(21)], scope: 'EVENTS', lastSeq: undefined })
20
-
21
- assert.equal(got.firstUnseenBefore, 20, '못 본 개수를 안 말하면 그 사실이 아무 데도 안 남는다')
22
- assert.equal(got.advancedSeq, 21)
23
- assert.equal(got.gap, undefined, '첫 봉투를 구멍으로 만들지 않는다 — 거절할 근거가 없다')
24
- })
25
-
26
- test('★ 커서가 0 이면 같은 21번이 구멍이다 — 둘의 차이가 이 결함의 전부다', () => {
27
- const got = takeInSequence({ items: [item(21)], scope: 'EVENTS', lastSeq: 0 })
28
-
29
- assert.equal(got.gap?.expectedSeq, 1)
30
- assert.equal(got.firstUnseenBefore, undefined, '구멍은 첫 봉투가 아니다')
31
- })
32
-
33
- test('1번부터 시작한 연결은 못 본 것이 없다 — 그 칸을 내지 않는다', () => {
34
- const got = takeInSequence({ items: [item(1)], scope: 'EVENTS', lastSeq: undefined })
35
-
36
- assert.equal(got.firstUnseenBefore, undefined, '0 을 내면 화면이 말할 것 없는 줄을 그린다')
37
- assert.equal(got.advancedSeq, 1)
38
- })
39
-
40
- test('늦게 붙은 연결도 수만 말한다 — 손실이라고 하지 않는다', () => {
41
- /*
42
- * 원본이 오래 돌고 있는데 트윈을 나중에 붙이면 seq 4,712 부터 받는 것이 정상이고, 그 아래
43
- * 4,711 개는 애초에 안 받기로 한 것이다. 계약은 둘을 구별할 수 없으므로 수만 낸다.
44
- */
45
- const got = takeInSequence({ items: [item(4712)], scope: 'EVENTS', lastSeq: undefined })
46
- assert.equal(got.firstUnseenBefore, 4711)
47
- assert.equal(got.advancedSeq, 4712, '늦게 붙은 것을 거절하지 않는다')
48
- })
49
-
50
- test('이어받는 봉투에는 그 칸이 없다', () => {
51
- const got = takeInSequence({ items: [item(49)], scope: 'EVENTS', lastSeq: 48 })
52
- assert.equal(got.firstUnseenBefore, undefined)
53
- assert.equal(got.advancedSeq, 49)
54
- })
55
-
56
- test('★ 이 테스트가 실제로 무언가를 잰다 — 견준 것이 0이면 빨강', () => {
57
- const first = takeInSequence({ items: [item(21)], scope: 'EVENTS', lastSeq: undefined })
58
- const cont = takeInSequence({ items: [item(21)], scope: 'EVENTS', lastSeq: 20 })
59
- assert.notEqual(first.firstUnseenBefore, cont.firstUnseenBefore, '첫 봉투와 이어받은 봉투가 같아졌다')
60
- })
61
-
62
- /* ── 몇 번부터 받기 시작했나를 커서에 적는다 ────────────────────────────────── */
63
-
64
- test('★ 첫 봉투의 번호를 커서에 적는다 — 「앞이 비었나」를 나중에 답하려면 이 값이 있어야 한다', () => {
65
- const got = takeInSequence({ items: [item(21)], scope: 'EVENTS', lastSeq: undefined })
66
- assert.equal(got.firstSeq, 21)
67
-
68
- const cursor = withSeq({}, 'mes-line-a::EVENTS', got.advancedSeq!, got.firstSeq)
69
- assert.equal(firstSeqOf(cursor, 'mes-line-a::EVENTS'), 21)
70
- assert.equal(seqOf(cursor, 'mes-line-a::EVENTS'), 21)
71
- })
72
-
73
- test('★ 한 번 적힌 firstSeq 는 안 바뀐다 — 덮으면 「앞이 비었나」를 다시 못 묻는다', () => {
74
- /*
75
- * 커서가 나아간 뒤에 이 값을 만들면 그 번호는 첫 번호가 아니라 그때 마침 온 번호다.
76
- */
77
- let cursor = withSeq({}, 'S', 21, 21)
78
- cursor = withSeq(cursor, 'S', 22, 22)
79
- cursor = withSeq(cursor, 'S', 48, 48)
80
-
81
- assert.equal(firstSeqOf(cursor, 'S'), 21, 'firstSeq 가 덮였다')
82
- assert.equal(seqOf(cursor, 'S'), 48)
83
- })
84
-
85
- test('★ 적힌 적이 없으면 모른다고 말한다 — 0 으로 답하면 1번부터 다 받은 것으로 읽힌다', () => {
86
- const old = withSeq({}, 'S', 48)
87
- assert.equal(firstSeqOf(old, 'S'), undefined)
88
- assert.equal(seqOf(old, 'S'), 48, '옛 커서의 번호는 그대로 읽힌다')
89
- })
90
-
91
- test('이어받는 봉투는 firstSeq 를 안 낸다 — 그 값을 만들 근거가 없다', () => {
92
- const got = takeInSequence({ items: [item(49)], scope: 'EVENTS', lastSeq: 48 })
93
- assert.equal(got.firstSeq, undefined)
94
- })
95
-
96
- test('1번부터 시작한 연결도 firstSeq 를 적는다 — 못 본 것이 없다는 사실도 사실이다', () => {
97
- const got = takeInSequence({ items: [item(1)], scope: 'EVENTS', lastSeq: undefined })
98
- assert.equal(got.firstSeq, 1)
99
- assert.equal(got.firstUnseenBefore, undefined, '못 본 것이 0이면 그 칸은 안 낸다')
100
- })
101
-
102
- test('★ 밑에서부터 다시 받으면 그 번호가 firstSeq 가 된다 — from: 0 은 first 판정이 아니다', () => {
103
- /*
104
- * 실제로 났다(2026-09-06). `fillTwinScope(from: 0)` 으로 잃은 구간을 되찾았는데 커서에
105
- * `firstSeq` 가 안 적혔다. 커서가 0 이라 seq 1 이 `next` 판정이었고 `first` 가 아니었기 때문이다.
106
- *
107
- * 그런데 우리는 실제로 1번부터 갖게 됐다. `lowestSeq` 가 그 사실을 낸다.
108
- */
109
- const got = takeInSequence({ items: [item(1), item(2), item(3)], scope: 'EVENTS', lastSeq: 0 })
110
-
111
- assert.equal(got.firstSeq, undefined, '커서가 있었으므로 첫 봉투가 아니다')
112
- assert.equal(got.lowestSeq, 1, '받아들인 것 중 가장 낮은 번호')
113
- assert.equal(got.advancedSeq, 3)
114
- })
115
-
116
- test('가장 낮은 번호는 하나도 못 받으면 없다', () => {
117
- const got = takeInSequence({ items: [item(9)], scope: 'EVENTS', lastSeq: 4 })
118
- assert.equal(got.lowestSeq, undefined, '구멍이라 아무것도 안 받았다')
119
- assert.equal(got.gap?.expectedSeq, 5)
120
- })
121
-
122
- /* ── 봉투의 시각 ────────────────────────────────────────────────────────────── */
123
-
124
- test('★ 시각을 안 싣는 레코드에 봉투의 시각을 얹는다 — 안 얹으면 다시 받을 때마다 새 사실이 된다', () => {
125
- /*
126
- * [측정함 2026-09-06] `fillTwinScope(from: 0)` 을 두 번 돌렸더니 mes-line-a 저널이 40 → 69건이
127
- * 되고 중복이 27가지 생겼다. 시각을 안 싣는 레코드는 ingest 시각으로 떨어지는데 그 값이 실행할
128
- * 때마다 달라서 fact identity 가 매번 바뀐다. 시각을 말하는 12건만 되풀이로 걸러졌다.
129
- *
130
- * plant 아웃박스는 그 시각을 봉투에 실어 보내고 있었다. 갈 자리가 없어서 버려지던 값이다.
131
- */
132
- const got = takeInSequence({
133
- items: [{ seq: 1, record: { subjectId: 'L', decision: 'scrap' }, at: '2026-09-01T00:00:00.000Z' }],
134
- scope: 'EVENTS',
135
- lastSeq: 0
136
- })
137
- assert.equal((got.records[0] as any).at, '2026-09-01T00:00:00.000Z')
138
- })
139
-
140
- test('★ 레코드가 자기 시각을 말하면 그것이 이긴다 — 봉투는 「언제 보냈나」에 가깝다', () => {
141
- const got = takeInSequence({
142
- items: [{ seq: 1, record: { subjectId: 'L', decision: 'scrap', at: '2026-08-30T00:00:00.000Z' }, at: '2026-09-01T00:00:00.000Z' }],
143
- scope: 'EVENTS',
144
- lastSeq: 0
145
- })
146
- assert.equal((got.records[0] as any).at, '2026-08-30T00:00:00.000Z')
147
- })
148
-
149
- test('봉투가 시각을 안 말하면 레코드를 그대로 둔다 — 없는 값을 만들지 않는다', () => {
150
- const rec = { subjectId: 'L', decision: 'scrap' }
151
- const got = takeInSequence({ items: [{ seq: 1, record: rec }], scope: 'EVENTS', lastSeq: 0 })
152
- assert.equal(got.records[0], rec, '새 객체를 만들 이유가 없다')
153
- })
@@ -1,53 +0,0 @@
1
- /*
2
- * ★ 저장본 뒤의 사건 — 계산했든 못 했든 **말한다**.
3
- *
4
- * ── 무엇이 침묵했나 (2026-08-28 실측) ───────────────────────────────────────
5
- * 미러는 저장본에서 상태를 되세우고 그 뒤의 사건만 따라 계산한다. 그 자리가 이랬다.
6
- *
7
- * 10:34:37 found cached state → revision 3372228
8
- * 10:34:37 resumed from the stored fold seed at revision 3372228 — items 2665 · orders 4794
9
- *
10
- * 그 시각 저널의 머리는 3,374,894였다. 2,666건이 저장본보다 앞서 있었는데 **계산했다는 줄도, 못
11
- * 했다는 줄도 없었다.** 갈래가 셋인데 말하는 갈래가 하나뿐이었기 때문이다.
12
- *
13
- * ① 계산해서 나아갔다 → 말한다
14
- * ② 계산이 오류로 실패했다 → 말한다
15
- * ③ 오류 없이 끝났는데 그대로 → **아무 말도 없었다** ← 여기
16
- * ④ 저널 머리를 못 읽었다 → **아무 말도 없었다** ← 여기
17
- *
18
- * ③④에서 트윈은 그 사건들의 결과 없이 뜨고, 화면은 그것을 「없는 사실」로 그린다.
19
- */
20
- import { test } from 'node:test'
21
- import assert from 'node:assert/strict'
22
- import { readFileSync } from 'node:fs'
23
- import { join } from 'node:path'
24
-
25
- const engine = readFileSync(join(import.meta.dirname, '..', 'server', 'engine', 'twin-engine.ts'), 'utf8')
26
-
27
- test('★ 저널 머리를 못 읽었으면 그 사실을 말하고 그대로 뜬다', () => {
28
- assert.match(engine, /if \(!tip\) \{/, '못 읽은 갈래가 있어야 한다')
29
- assert.match(engine, /could not read the journal tip/, '무엇을 못 했는지 말해야 한다')
30
- assert.match(engine, /without folding what came after it/, '무엇을 잃는지 말해야 한다')
31
- })
32
-
33
- test('★ 계산이 아무 일도 하지 않았으면 그것도 말한다', () => {
34
- assert.match(engine, /\} else if \(!failed\) \{/, '오류가 아닌데 나아가지 않은 갈래가 있어야 한다')
35
- assert.match(engine, /left the seed at revision/)
36
- /* 오류로 실패한 것을 두 번 말하지 않는다 — 이미 위에서 말했다. */
37
- assert.match(engine, /let failed: string \| undefined/)
38
- })
39
-
40
- test('★ 부팅마다 부르는 질의를 관계로 묻지 않는다 — 이 파일이 스스로 금지한 모양이다', () => {
41
- /*
42
- * `findOne({ where: { domain: { id } } })` 는 안쪽에 상한이 없는 두 단계 질의가 된다(실측 12초,
43
- * 저널 375만 행). `tipOf` 는 부팅마다 트윈마다 불리므로 그 한 번이 부팅을 붙잡는다.
44
- */
45
- const from = engine.indexOf('private static async tipOf')
46
- assert.ok(from > 0, 'tipOf 가 있어야 한다')
47
- const tipOf = engine.slice(from, from + 2600)
48
- /* 주석에 인용된 나쁜 모양과 헷갈리지 않게, 실제 코드 모양만 본다. */
49
- assert.doesNotMatch(tipOf, /findOne\(\{ where: \{ domain: \{ id: domainId \}/, '관계로 묻는 모양이 남아 있다')
50
- assert.match(tipOf, /createQueryBuilder\('e'\)/)
51
- assert.match(tipOf, /orderBy\('e\.revision', 'DESC'\)/)
52
- assert.match(tipOf, /\.limit\(1\)/)
53
- })
@@ -1,110 +0,0 @@
1
- /*
2
- * Folding a span that crosses a daylight-saving transition.
3
- *
4
- * ── Why this is worth a test ───────────────────────────────────────────────
5
- * `utcOffsetMinutes` on the model is one number, resolved when the host starts the twin.
6
- * Folding walks a past span, so a year of facts gets read with whatever offset happened to
7
- * be current at start — and a year holds two transitions. Facts after one land an hour off
8
- * and are counted into the neighbouring shift. Nothing errors; shift performance is simply
9
- * wrong, and plausible.
10
- *
11
- * `America/Tijuana` is already in this deployment's data (PST -480 / PDT -420), so this is
12
- * not a hypothetical.
13
- */
14
- import test from 'node:test'
15
- import assert from 'node:assert/strict'
16
-
17
- import { foldKpi } from '../server/engine/kpi-fold.js'
18
-
19
- /* Two shifts that between them cover the day, so a misread hour lands in the other one. */
20
- const SHIFTS = [
21
- { id: 'day', fromTime: '06:00', toTime: '18:00' },
22
- { id: 'night', fromDayOffset: 0, fromTime: '18:00', toDayOffset: 1, toTime: '06:00' }
23
- ] as any[]
24
-
25
- /* Tijuana: PDT (-420) in July, PST (-480) in December. */
26
- const SUMMER = Date.UTC(2026, 6, 15, 13, 30) // 06:30 local under -420 → day shift
27
- const WINTER = Date.UTC(2026, 11, 15, 13, 30) // 05:30 local under -480 → night shift
28
-
29
- /*
30
- * The same rule the host uses (`utcOffsetOf`), written out here because that function lives
31
- * beside code the test runner cannot load. Kept small and checked by its own test below, so a
32
- * drift in it does not quietly turn the rest of this file green.
33
- */
34
- function offsetAt(atMs: number): number | undefined {
35
- const name = new Intl.DateTimeFormat('en-US', { timeZone: 'America/Tijuana', timeZoneName: 'longOffset' })
36
- .formatToParts(new Date(atMs))
37
- .find(p => p.type === 'timeZoneName')?.value
38
-
39
- const m = /^GMT([+-])(\d{1,2}):(\d{2})$/.exec(name ?? '')
40
-
41
- return m ? (m[1] === '-' ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : undefined
42
- }
43
-
44
- /*
45
- * A completed task is a transition, not one row — the fold counts throughput at completion
46
- * and needs the earlier states to have a lead time to measure.
47
- */
48
- const doneAt = (ms: number, id: string): any[] =>
49
- ['created', 'in-progress', 'completed'].map((status, i) => ({
50
- eventType: 'task.status',
51
- eventTime: new Date(ms - (2 - i) * 30_000).toISOString(),
52
- payload: { data: { taskId: id, status } }
53
- }))
54
-
55
- function shiftsOf(events: any[], options: any) {
56
- const out = foldKpi(events, { fromMs: Date.UTC(2026, 0, 1), toMs: Date.UTC(2027, 0, 1) } as any, {
57
- groupBy: 'shift',
58
- shifts: SHIFTS,
59
- ...options
60
- } as any)
61
-
62
- return (out as any)?.groups?.items?.map((g: any) => g.key) ?? []
63
- }
64
-
65
- test('the probe itself reads Tijuana as -420 in July and -480 in December', () => {
66
- /* If this drifts, the rest of the file is measuring nothing. */
67
- assert.equal(offsetAt(SUMMER), -420)
68
- assert.equal(offsetAt(WINTER), -480)
69
- })
70
-
71
- test('a fixed offset puts a December fact in the wrong shift — the defect', () => {
72
- /*
73
- * Both facts are 13:30 UTC. Read with the summer offset the December one looks like
74
- * 06:30 local and lands on the day shift, when locally it was 05:30 and belongs to the
75
- * night shift.
76
- */
77
- const keys = shiftsOf([...doneAt(WINTER, 'w')], { utcOffsetMinutes: -420 })
78
-
79
- assert.deepEqual(keys, ['day'])
80
- })
81
-
82
- test('offsetAt resolves per fact, so the December fact lands on the night shift', () => {
83
- const keys = shiftsOf([...doneAt(WINTER, 'w')], { utcOffsetMinutes: -420, offsetAt })
84
-
85
- assert.deepEqual(keys, ['night'])
86
- })
87
-
88
- test('the same span read per instant splits across both shifts, not one', () => {
89
- const keys = shiftsOf([...doneAt(SUMMER, 's'), ...doneAt(WINTER, 'w')], { utcOffsetMinutes: -420, offsetAt }).sort()
90
-
91
- assert.deepEqual(keys, ['day', 'night'])
92
- })
93
-
94
- test('offsetAt returning undefined falls back to the number rather than to UTC', () => {
95
- /*
96
- * Not knowing the offset for an instant is not the same as the instant being UTC.
97
- * Falling through to the declared number keeps the previous behaviour instead of
98
- * silently asserting Greenwich.
99
- */
100
- const keys = shiftsOf([...doneAt(WINTER, 'w')], { utcOffsetMinutes: -420, offsetAt: () => undefined })
101
-
102
- assert.deepEqual(keys, ['day'])
103
- })
104
-
105
- test('no offset at all still folds, reading times as UTC as the contract states', () => {
106
- const keys = shiftsOf([...doneAt(WINTER, 'w')], {})
107
-
108
- /* 13:30 UTC is inside 06:00-18:00. */
109
- assert.deepEqual(keys, ['day'])
110
- })
@@ -1,157 +0,0 @@
1
- /*
2
- * 예측 지표 — **스냅샷에서 무엇을 세는가.**
3
- *
4
- * 여기서 틀리면 예측 그래프가 알리지 않고 다른 것을 그린다(오류 없이 곡선이 나온다). 특히 에너지 지표는
5
- * 「없음」과 「0」이 같은 그림이 되기 쉬워서, 그 경계를 명시로 잡아 둔다.
6
- */
7
- import { test } from 'node:test'
8
- import assert from 'node:assert/strict'
9
-
10
- import { FORECAST_METRICS, metricsForKind } from '../server/service/twin-forecast/forecast-metrics.ts'
11
-
12
- test('물류 지표 — 기존 여섯은 그대로 센다', () => {
13
- const s = {
14
- items: [1, 2, 3],
15
- locations: [{ occupancy: 2 }, { occupancy: 5 }],
16
- tasks: [{}, {}],
17
- orders: [{ status: 'shipped' }, { status: 'picking' }, { status: 'cancelled' }]
18
- }
19
- assert.equal(FORECAST_METRICS.items(s), 3)
20
- assert.equal(FORECAST_METRICS.occupancy(s), 7)
21
- assert.equal(FORECAST_METRICS.tasks(s), 2)
22
- assert.equal(FORECAST_METRICS.orders(s), 3)
23
- assert.equal(FORECAST_METRICS.shipped(s), 1)
24
- /* 취소는 백로그가 아니다 — 대기와 취소를 같이 세면 「안 빠지는 일감」이 부풀려진다. */
25
- assert.equal(FORECAST_METRICS.backlog(s), 1)
26
- })
27
-
28
- /*
29
- * ── 받는 오더는 흐름 지표에서 빠진다 (2026-09-19, ADR-0076 보탬 ⑥) ──────────
30
- *
31
- * 입고 · 반품 입고는 도착이 세우고 도착이 이행한다. 세면 「나간 것」이 받을 때마다 늘고 백로그는
32
- * 영영 안 줄어든다 — 그래프가 있지도 않은 일을 그린다.
33
- */
34
- test('★ 입고 · 반품 입고는 세지 않는다 — 그 오더는 나가지 않는다', () => {
35
- const s = {
36
- orders: [
37
- { kind: 'outbound', status: 'shipped' },
38
- { kind: 'outbound', status: 'picking' },
39
- { kind: 'inbound', status: 'received', requested: 10, fulfilled: 10 },
40
- { kind: 'return', status: 'received', requested: 3, fulfilled: 3 }
41
- ]
42
- }
43
- assert.equal(FORECAST_METRICS.orders(s), 2, '흐름 오더 둘만 센다')
44
- assert.equal(FORECAST_METRICS.shipped(s), 1)
45
- assert.equal(FORECAST_METRICS.backlog(s), 1, '받은 오더가 백로그에 남으면 곡선이 안 내려간다')
46
- })
47
-
48
- /*
49
- * ── 백로그는 「종결인가」로 묻는다 ──────────────────────────────────────────
50
- *
51
- * 낱말 둘로 물을 때 원본이 `completed` 를 쓰는 미러에서 끝난 오더가 영영 백로그에 남았다. `status` 는
52
- * 열린 축이고 실 시스템은 자기 낱말을 쓴다 — 이 셈은 낱말이 아니라 양을 1차 근거로 본다.
53
- */
54
- test('★ 원본의 낱말로 끝난 오더도 백로그에서 빠진다', () => {
55
- const s = {
56
- orders: [
57
- { kind: 'outbound', status: 'completed', requested: 5, fulfilled: 5 },
58
- { kind: 'outbound', status: 'FINISHED', requested: 2, fulfilled: 2 },
59
- { kind: 'outbound', status: 'picking', requested: 4, fulfilled: 1 }
60
- ]
61
- }
62
- assert.equal(FORECAST_METRICS.backlog(s), 1, '아직 안 끝난 하나만 백로그다')
63
- })
64
-
65
- test('낱말을 말하지 않은 오더는 그대로 센다 — 지금까지의 거동이다', () => {
66
- const s = { orders: [{ status: 'picking' }, { status: 'shipped' }] }
67
- assert.equal(FORECAST_METRICS.orders(s), 2)
68
- assert.equal(FORECAST_METRICS.shipped(s), 1)
69
- })
70
-
71
- test('에너지 지표 — 커널이 낸 값을 그대로 읽는다(다시 계산하지 않는다)', () => {
72
- const s = {
73
- energy: {
74
- peakSince: { kW: 812 },
75
- open: { meanKW: 300, projectedKW: 455, projectionBasis: 'mean-so-far' },
76
- closed: [{ overContract: true }, { overContract: false }, { overContract: true }]
77
- }
78
- }
79
- assert.equal(FORECAST_METRICS.peakKW(s), 812)
80
- assert.equal(FORECAST_METRICS.projectedKW(s), 455)
81
- assert.equal(FORECAST_METRICS.overWindows(s), 2)
82
- })
83
-
84
- test('투영이 아직 없으면 지금까지의 평균으로 — 0 으로 떨어뜨리지 않는다', () => {
85
- assert.equal(FORECAST_METRICS.projectedKW({ energy: { open: { meanKW: 120 } } }), 120)
86
- })
87
-
88
- test('에너지가 없는 스냅샷 — 세 지표 모두 0(그리지 않을 값이지, 관측한 0 이 아니다)', () => {
89
- assert.equal(FORECAST_METRICS.peakKW({ items: [] }), 0)
90
- assert.equal(FORECAST_METRICS.projectedKW({ items: [] }), 0)
91
- assert.equal(FORECAST_METRICS.overWindows({ items: [] }), 0)
92
- })
93
-
94
- test('계약을 모르면 초과 구간은 0 — overContract 가 없는 구간을 초과로 세지 않는다', () => {
95
- const s = { energy: { closed: [{ maxKW: 900 }, { maxKW: 1200 }] } }
96
- assert.equal(FORECAST_METRICS.overWindows(s), 0)
97
- })
98
-
99
- test('종류별 지표 목록 — 에너지 트윈에는 물류 지표가 섞이지 않는다', () => {
100
- const ems = metricsForKind('ems')
101
- assert.deepEqual(ems, ['projectedKW', 'peakKW', 'overWindows'])
102
- assert.equal(ems.includes('items' as any), false)
103
- assert.equal(metricsForKind('wms').includes('items'), true)
104
- /* 종류를 모르면 물류로 — 기존 트윈(kind 미선언)이 지표를 잃지 않는다. */
105
- assert.equal(metricsForKind(undefined).includes('items'), true)
106
- })
107
-
108
- test('목록의 모든 키가 실제로 계산 가능하다 — 화면에 뜨는데 서버가 모르는 지표는 없다', () => {
109
- for (const kind of ['ems', 'wms', 'mes', 'yms']) {
110
- for (const key of metricsForKind(kind)) {
111
- assert.equal(typeof (FORECAST_METRICS as any)[key], 'function', `${kind}:${key}`)
112
- }
113
- }
114
- })
115
-
116
- /*
117
- * ★ Folding does not move the count (ADR-0092 decision 3, ruling 2026-09-24).
118
- * A twin folds finished orders out of its hot list and keeps kind × status counts. Counting the hot list
119
- * alone dropped orders/shipped at every fold, and made the gap trend's actual increase negative.
120
- */
121
- const hot = (orders: any[], foldedOrders?: any) => ({ orders, ...(foldedOrders ? { foldedOrders } : {}) })
122
-
123
- test('folding the finished orders leaves orders and shipped where they were', () => {
124
- const before = hot([
125
- { id: 'o1', kind: 'outbound', status: 'shipped' },
126
- { id: 'o2', kind: 'outbound', status: 'cancelled' },
127
- { id: 'o3', kind: 'outbound', status: 'picking' },
128
- { id: 'r1', kind: 'inbound', status: 'completed' }
129
- ])
130
- const after = hot([{ id: 'o3', kind: 'outbound', status: 'picking' }], {
131
- counts: { outbound: { shipped: 1, cancelled: 1 }, inbound: { completed: 1 } },
132
- boundaryMs: 1
133
- })
134
- for (const m of ['orders', 'shipped', 'backlog'] as const) assert.equal(FORECAST_METRICS[m](after), FORECAST_METRICS[m](before), m)
135
- assert.equal(FORECAST_METRICS.orders(after), 3, 'receiving orders are not counted, folded or not')
136
- })
137
-
138
- test('a fold between T and T+h keeps the actual increase from going negative', () => {
139
- const atT = hot([{ kind: 'outbound', status: 'shipped' }, { kind: 'outbound', status: 'shipped' }, { kind: 'outbound', status: 'picking' }])
140
- /* by T+h: one more shipped, and the three shipped have been folded */
141
- const atTh = hot([{ kind: 'outbound', status: 'picking' }], { counts: { outbound: { shipped: 3 } }, boundaryMs: 2 })
142
- assert.equal(FORECAST_METRICS.shipped(atTh) - FORECAST_METRICS.shipped(atT), 1)
143
- assert.ok(FORECAST_METRICS.orders(atTh) - FORECAST_METRICS.orders(atT) >= 0)
144
- })
145
-
146
- test('a twin with nothing folded counts as before', () => {
147
- const s = hot([{ kind: 'outbound', status: 'shipped' }, { status: 'picking' }])
148
- assert.equal(FORECAST_METRICS.orders(s), 2)
149
- assert.equal(FORECAST_METRICS.shipped(s), 1)
150
- })
151
-
152
- test('a folded order with no kind is a flow order, as it is in the hot list', () => {
153
- const folded = hot([], { counts: { '': { shipped: 2 } }, boundaryMs: 1 })
154
- const unfolded = hot([{ status: 'shipped' }, { status: 'shipped' }])
155
- assert.equal(FORECAST_METRICS.orders(folded), FORECAST_METRICS.orders(unfolded))
156
- assert.equal(FORECAST_METRICS.shipped(folded), FORECAST_METRICS.shipped(unfolded))
157
- })
@@ -1,42 +0,0 @@
1
- /*
2
- * 예측 틱 크기 — **지평선이 정하고, 양끝은 묶인다.**
3
- *
4
- * 이 규칙이 틀리면 예측이 알리지 않고 비싸지거나(1초 고정 시절 9,000틱·28.5초) 알리지 않고 성글어진다
5
- * (짧은 작업이 한 틱에 삼켜진다). 둘 다 화면에는 그럴듯한 숫자로 나오므로 시험이 지킨다.
6
- */
7
- import { test } from 'node:test'
8
- import assert from 'node:assert/strict'
9
-
10
- import { forecastStepMs, TARGET_TICKS_PER_RUN, MAX_STEP_MS, MIN_STEP_MS } from '../dist-server/service/twin-forecast/forecast-tuning.js'
11
-
12
- test('기본 지평선(5분)은 5초 단위 — 실측으로 1초와 답이 같고 5배 싸다', () => {
13
- assert.equal(forecastStepMs(300_000), 5_000)
14
- })
15
-
16
- test('짧은 지평선은 더 잘게 — 1분이면 1초', () => {
17
- assert.equal(forecastStepMs(60_000), 1_000)
18
- })
19
-
20
- test('아주 짧아도 커널 시각 단위 아래로 내려가지 않는다', () => {
21
- assert.equal(forecastStepMs(5_000), MIN_STEP_MS)
22
- assert.equal(forecastStepMs(0), MIN_STEP_MS)
23
- assert.equal(forecastStepMs(Number.NaN), MIN_STEP_MS)
24
- })
25
-
26
- test('아무리 길어도 상한을 넘지 않는다 — 짧은 작업이 한 틱에 삼켜진다', () => {
27
- assert.equal(forecastStepMs(2 * 60 * 60_000), MAX_STEP_MS)
28
- assert.equal(forecastStepMs(24 * 60 * 60_000), MAX_STEP_MS)
29
- })
30
-
31
- test('틱 크기는 초 단위로 떨어진다 — 3,333ms 는 사람도 화면도 설명할 수 없다', () => {
32
- for (const h of [100_000, 200_000, 333_000, 777_000]) {
33
- assert.equal(forecastStepMs(h) % 1000, 0, `${h}ms`)
34
- }
35
- })
36
-
37
- test('상한에 걸리지 않는 구간에서는 목표 틱 수 근처로 돈다', () => {
38
- for (const h of [60_000, 120_000, 300_000, 600_000]) {
39
- const ticks = h / forecastStepMs(h)
40
- assert.ok(ticks >= TARGET_TICKS_PER_RUN * 0.8 && ticks <= TARGET_TICKS_PER_RUN * 1.25, `${h}ms → ${ticks}틱`)
41
- }
42
- })
@@ -1,113 +0,0 @@
1
- /*
2
- * 예측 정확도 진단(gap-analytics) — SPC 두 축(안정성·편향)·정확도·보정의 canonical 계산을 서버에서 단언.
3
- * 클라에서 이전한 순수 함수라 DB 무의존, 결정적.
4
- */
5
- import { test } from 'node:test'
6
- import assert from 'node:assert/strict'
7
-
8
- import { computeSpc, gapMetrics, learnedCalibration, aggregatePoints } from '../server/service/twin-forecast/gap-analytics.ts'
9
-
10
- const pt = (error: number, actual = 100, inBand = true) => ({
11
- t: 0, p50: actual - error, min: 0, max: 0, actual, error, inBand,
12
- naiveAbsError: Math.abs(error) + 1, modelAbsError: Math.abs(error)
13
- })
14
-
15
- test('computeSpc — 표본 부족(n<4)은 insufficient', () => {
16
- assert.equal(computeSpc([1, 2, 3]).insufficient, true)
17
- })
18
-
19
- test('computeSpc — 안정적·무편향: 0 주변 교대 오차', () => {
20
- const spc = computeSpc([1, -1, 1, -1, 1, -1, 1, -1])
21
- assert.equal(spc.stable, true, '이탈·추세 없음 → 안정')
22
- assert.equal(spc.biased, false, '평균 0 → 무편향')
23
- })
24
-
25
- test('computeSpc — 안정적이지만 편향: 계속 +6 (안정성과 편향은 별개 축)', () => {
26
- const spc = computeSpc([6, 6, 6, 6, 6, 6])
27
- assert.equal(spc.stable, true, '오차 폭 일정 → 안정')
28
- assert.equal(spc.biased, true, '평균이 0 에서 크게 벗어남 → 편향')
29
- assert.equal(spc.biasSign, 'under', 'CL>0 = 실제>예측 = 과소예측')
30
- })
31
-
32
- test('computeSpc — 급변(3σ 이탈)은 shift', () => {
33
- const spc = computeSpc([0.1, -0.1, 0.1, -0.1, 0.1, -0.1, 10])
34
- assert.equal(spc.instability, 'shift')
35
- assert.equal(spc.stable, false)
36
- })
37
-
38
- test('gapMetrics — 정확도=1−WMAPE, 커버리지·FVA', () => {
39
- const m = gapMetrics([pt(1, 10, true), pt(-1, 10, false)])
40
- assert.equal(m.accuracy, 90, '오차합2/실제합20=10% → 90%')
41
- assert.equal(m.inBandCount, 1)
42
- assert.equal(m.total, 2)
43
- assert.equal(m.fva, 2, 'Σ(naiveAbs−modelAbs) = (2−1)+(2−1)')
44
- assert.equal(m.fvaPct, 50, 'naive 오차 4 → 모델 2 → 50% 감소(예측 가치)')
45
- })
46
-
47
- test('learnedCalibration — biasShift=평균오차, 편향 제거로 정확도 개선', () => {
48
- const pts = [pt(6, 100), pt(6, 100), pt(6, 100)]
49
- const learn = learnedCalibration(pts, gapMetrics(pts))
50
- assert.equal(learn.biasShift, 6, '학습된 편향 = 평균 오차')
51
- assert.ok(learn.accAfter >= learn.accBefore, '편향 제거 → 정확도 유지·개선')
52
- assert.equal(learn.accAfter, 100, '일정 편향을 완전 제거 → 잔차 0 → 100%')
53
- })
54
-
55
- /*
56
- * ★ Nothing to divide by is not 100% (2026-09-24). With every actual at 0 the card read
57
- * "100% accurate" beside "0 of 6 in band, mean error −20" — the installed build gave exactly that.
58
- */
59
- test('gapMetrics — a window with no actual says the percentages are not measured, and keeps what is', () => {
60
- const idle = Array.from({ length: 6 }, () => ({ t: 0, p50: 20, min: 15, max: 25, actual: 0, error: -20, inBand: false, naiveAbsError: 0, modelAbsError: 20 }))
61
- const m = gapMetrics(idle)
62
- assert.equal(m.accuracy, null, 'WMAPE has no denominator — not 100%')
63
- assert.equal(m.fvaPct, null, 'the naive error is 0 — "adds nothing" would be invented')
64
- /* Measured in that window, so they stay. */
65
- assert.equal(m.mpe, -20)
66
- assert.equal(m.inBandCount, 0)
67
- assert.equal(m.total, 6)
68
- assert.equal(m.fva, -120)
69
- })
70
-
71
- test('learnedCalibration — the bias is still learned with no actual; only the percentages are not', () => {
72
- const idle = Array.from({ length: 4 }, () => pt(-20, 0, false))
73
- const learn = learnedCalibration(idle, gapMetrics(idle))
74
- assert.equal(learn.biasShift, -20, 'forecasting 20 over a standing line is a bias worth removing')
75
- assert.equal(learn.accBefore, null)
76
- assert.equal(learn.accAfter, null, 'no denominator was made up (it used to be `|| 1`)')
77
- })
78
-
79
- /* 같은 격자 위의 점 — vantage 를 명시한다(예전 헬퍼는 t 를 늘 0 으로 두어 시간축을 전혀 시험하지 않았다). */
80
- const at = (t: number, error: number, actual = 100) => ({ ...pt(error, actual), t })
81
-
82
- test('aggregatePoints — 공동배치 합산(공장 총량)', () => {
83
- const a = [at(10, 2, 30), at(20, 4, 30)]
84
- const b = [at(10, 1, 20), at(20, 2, 20)]
85
- const { points, droppedVantages } = aggregatePoints([a, b])
86
- assert.equal(points.length, 2)
87
- assert.equal(droppedVantages, 0)
88
- assert.equal(points[0].actual, 50, '실제 30+20')
89
- assert.equal(points[0].t, 10, '합산점의 시각은 그 vantage 다')
90
- assert.equal(points[0].error, points[0].actual - points[0].p50, 'error = 실제−예측(합산 후 재계산)')
91
- })
92
-
93
- test('한 트윈이 못 잰 vantage 는 통째로 뺀다 — 빠진 트윈을 0 으로 세지 않는다', () => {
94
- /*
95
- * 예전에는 `max` 길이로 돌며 있는 것만 더했다. 그래서 이 경우 t=20 의 공장 총량이 20(b 뿐)으로
96
- * 나왔다 — a 가 그 시각에 아무것도 안 했다는 뜻이 아니라, **못 쟀다**는 뜻인데도.
97
- * 공장 합계 곡선이 꼬리에서 아래로 꺾이고, 그 곡선으로 예측 정확도를 판정했다.
98
- */
99
- const a = [at(10, 2, 30), null]
100
- const b = [at(10, 1, 20), at(20, 2, 20)]
101
- const { points, droppedVantages } = aggregatePoints([a as any, b])
102
- assert.equal(points.length, 1, '모두가 값을 가진 vantage 만 남는다')
103
- assert.equal(points[0].t, 10)
104
- assert.equal(droppedVantages, 1, '뺀 개수를 돌려줘야 화면이 "2점 중 1점" 이라고 말할 수 있다')
105
- })
106
-
107
- test('길이가 다르면 뒤쪽은 뺀다 — 남은 트윈만의 합을 공장 총량이라 부르지 않는다', () => {
108
- const a = [at(10, 2, 30)]
109
- const b = [at(10, 1, 20), at(20, 2, 20)]
110
- const { points, droppedVantages } = aggregatePoints([a, b])
111
- assert.equal(points.length, 1)
112
- assert.equal(droppedVantages, 1)
113
- })