@things-factory/headless-twin 10.1.278 → 10.1.280

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,209 +0,0 @@
1
- /*
2
- * ★ 다단 전개(BOM) — **자재가 자재를 품는 트리가 아니다.**
3
- *
4
- * 표준(ISA-95)이 택한 모양은 자재와 레시피가 번갈아 나오는 그래프다:
5
- * 완제품 ←산출— 레시피 —투입→ 부품, 그리고 그 부품이 또 어느 레시피의 산출이면 거기서 다시 내려간다.
6
- * 우리 선언(`ops.rel.input`·`ops.rel.output`)이 이미 그 두 홉이므로 **새 선언 없이** 재귀가 된다.
7
- *
8
- * 여기서 지키는 것:
9
- * · 패턴을 코드가 알지 않는다 — 선언 그래프에서 자기 축으로 돌아오는 순환을 찾는다
10
- * · **알리지 않고 고르지 않는다** — 순환이 여럿이면 후보를 내고 멈춘다(방향이 뒤집힌 전개는
11
- * 틀린 답이 아니라 **반대의 답**이라 화면만 보고는 구별되지 않는다)
12
- * · 수량은 곱해 내려가되, **모르면 곱하지 않는다**(1 로 메우면 윗단 합계가 통째로 거짓이 된다)
13
- * · 사이클을 만나면 다시 파지 않고 말한다
14
- * · 상한에서 끊기면 끊겼다고 말한다
15
- */
16
- import { test, before, after } from 'node:test'
17
- import assert from 'node:assert/strict'
18
- import { createRequire } from 'node:module'
19
- import type { DataSource } from 'typeorm'
20
-
21
- const req = createRequire(import.meta.url)
22
- const { NamingStrategy, addDataSource, entities: shellEntities, Domain } = req('@things-factory/shell')
23
- const { entities: authEntities } = req('@things-factory/auth-base')
24
- const { entities: twinEntities } = req('../dist-server/service/index.js')
25
- const { TwinEngine } = req('../dist-server/engine/index.js')
26
- const { TwinModelTreeQuery } = req('../dist-server/service/twin-model/twin-model-tree-query.js')
27
- const { DataSource: TypeOrmDataSource } = req('typeorm')
28
-
29
- let ds: DataSource
30
- let domainId: string
31
- let q: any
32
- let ctx: any
33
-
34
- const INSTANCE = 'bom-probe'
35
-
36
- /**
37
- * 3단 BOM — 완제품 → 하위조립 → 원자재.
38
- * trailer ← trailer-line ← { frame ×1, axle ×2 }
39
- * frame ← frame-line ← { steel ×4 }
40
- */
41
- const master = (extra: any = {}): any => ({
42
- source: INSTANCE,
43
- system: 'mes',
44
- siteName: 'BOM 공장',
45
- space: { width: 60, depth: 30, unit: 'm' },
46
- locations: [{ id: 'line', type: 'weld-station', role: 'location', capacity: 10 }],
47
- equipment: [{ id: 'welder-1', kind: 'welder', homeLocationId: 'line' }],
48
- productionSpec: {
49
- definition: {
50
- materials: [{ id: 'trailer' }, { id: 'frame' }, { id: 'axle' }, { id: 'steel' }],
51
- recipes: [
52
- { key: 'trailer-line', label: '트레일러', inputs: [{ material: 'frame', qty: 1 }, { material: 'axle', qty: 2 }], outputs: [{ material: 'trailer', qty: 1 }] },
53
- { key: 'frame-line', label: '프레임', inputs: [{ material: 'steel', qty: 4 }], outputs: [{ material: 'frame', qty: 1 }] }
54
- ],
55
- ...extra
56
- }
57
- }
58
- })
59
-
60
- const explode = (itemId: string, opts: any = {}) =>
61
- q.twinModelTree(INSTANCE, 'materials', itemId, ctx, 'ops.rel.output', 'ops.rel.input', opts.maxDepth, opts.maxItems)
62
-
63
- before(async () => {
64
- ds = new TypeOrmDataSource({
65
- type: 'sqlite',
66
- database: ':memory:',
67
- synchronize: true,
68
- dropSchema: true,
69
- namingStrategy: new NamingStrategy(),
70
- entities: [...shellEntities, ...authEntities, ...twinEntities],
71
- logging: false
72
- })
73
- await ds.initialize()
74
- addDataSource('default', ds)
75
- const d = await ds.getRepository(Domain).save({ name: 'bom-test', subdomain: 'bom-test' } as any)
76
- domainId = d.id
77
- q = new TwinModelTreeQuery()
78
- ctx = { state: { domain: { id: domainId } } }
79
- await TwinEngine.ingestMaster(domainId, master(), 'reset')
80
- })
81
-
82
- after(async () => {
83
- await TwinEngine.stop(domainId, INSTANCE).catch(() => {})
84
- await ds?.destroy()
85
- })
86
-
87
- test('다단으로 내려간다 — 자재가 자재를 품지 않아도 전개된다', async () => {
88
- const t = await explode('trailer')
89
- const byDepth = (n: number) => t.items.filter((x: any) => x.depth === n).map((x: any) => x.id).sort()
90
-
91
- assert.deepEqual(byDepth(0), ['trailer'])
92
- assert.deepEqual(byDepth(1), ['axle', 'frame'], '레시피를 거쳐 1단이 나온다')
93
- assert.deepEqual(byDepth(2), ['steel'], '프레임이 또 어느 레시피의 산출이므로 거기서 더 내려간다')
94
- })
95
-
96
- test('수량을 곱해 내려간다 — 그리고 모르면 곱하지 않는다', async () => {
97
- const t = await explode('trailer')
98
- const qty = (id: string) => t.items.find((n: any) => n.id === id)?.qty
99
- assert.equal(qty('frame'), 1)
100
- assert.equal(qty('axle'), 2)
101
- assert.equal(qty('steel'), 4, '프레임 1개당 강재 4 → 트레일러 1대당 4')
102
- assert.equal(t.qtyIncomplete, false)
103
-
104
- /* 수량이 빠진 선언이 섞이면 그 아래 합계는 성립하지 않는다 — 1 로 메우지 않고 그렇게 말한다. */
105
- const noQty = master({
106
- recipes: [
107
- { key: 'trailer-line', label: 't', inputs: [{ material: 'frame' }], outputs: [{ material: 'trailer', qty: 1 }] },
108
- { key: 'frame-line', label: 'f', inputs: [{ material: 'steel', qty: 4 }], outputs: [{ material: 'frame', qty: 1 }] }
109
- ]
110
- })
111
- await TwinEngine.ingestMaster(domainId, noQty, 'reset')
112
- const t2 = await explode('trailer')
113
- assert.equal(t2.items.find((n: any) => n.id === 'frame').qty, null, '모르는 수량을 1 로 메우지 않는다')
114
- assert.equal(t2.items.find((n: any) => n.id === 'steel').qty, null, '모르는 값 아래는 전부 모르는 값이다')
115
- assert.equal(t2.qtyIncomplete, true, '합계가 성립하지 않는다는 사실을 낸다')
116
-
117
- await TwinEngine.ingestMaster(domainId, master(), 'reset')
118
- })
119
-
120
- test('역전개도 같은 선언으로 — 방향만 뒤집는다', async () => {
121
- /* "이 강재를 쓰는 것은 무엇인가" — 투입으로 들어와 산출로 나간다. */
122
- const up = await q.twinModelTree(INSTANCE, 'materials', 'steel', ctx, 'ops.rel.input', 'ops.rel.output')
123
- assert.deepEqual(up.items.filter((n: any) => n.depth === 1).map((n: any) => n.id), ['frame'])
124
- assert.deepEqual(up.items.filter((n: any) => n.depth === 2).map((n: any) => n.id), ['trailer'])
125
- })
126
-
127
- test('방향을 안 고르면 아무것도 펼치지 않는다 — 뒤집힌 전개는 틀린 답이 아니라 반대의 답이다', async () => {
128
- const t = await q.twinModelTree(INSTANCE, 'materials', 'trailer', ctx)
129
- assert.equal(t.reason, 'ambiguous-pattern')
130
- assert.equal(t.items.length, 0, '한쪽을 집으면 아무도 모른 채 반대 답을 본다')
131
- assert.ok(t.patterns.length >= 2, '고를 수 있게 후보를 낸다')
132
- })
133
-
134
- test('어떤 순환을 따라 펼쳤는지 밝힌다 — 화면만 보고는 방향을 구별할 수 없다', async () => {
135
- const t = await explode('trailer')
136
- assert.equal(t.pattern.inVia, 'ops.rel.output')
137
- assert.equal(t.pattern.outVia, 'ops.rel.input')
138
- assert.equal(t.pattern.through, 'recipes')
139
- })
140
-
141
- test('사이클은 다시 탐색하지 않고 알린다', async () => {
142
- /* 강재가 트레일러에서 나온다고 선언하면(재활용) 그래프가 순환한다. 파고 들면 돌아 나오지 못한다. */
143
- const loop = master({
144
- recipes: [
145
- { key: 'trailer-line', label: 't', inputs: [{ material: 'frame', qty: 1 }], outputs: [{ material: 'trailer', qty: 1 }] },
146
- { key: 'frame-line', label: 'f', inputs: [{ material: 'trailer', qty: 1 }], outputs: [{ material: 'frame', qty: 1 }] }
147
- ]
148
- })
149
- await TwinEngine.ingestMaster(domainId, loop, 'reset')
150
- const t = await explode('trailer')
151
- assert.ok(t.items.length < 10, '무한히 자라지 않는다')
152
- assert.ok(t.cycles.length > 0, '사이클을 만났다는 사실을 말한다')
153
- await TwinEngine.ingestMaster(domainId, master(), 'reset')
154
- })
155
-
156
- test('상한에서 끊기면 끊겼다고 알린다 — 알리지 않고 자르지 않는다', async () => {
157
- const shallow = await explode('trailer', { maxDepth: 1 })
158
- assert.equal(shallow.truncated.depth, true)
159
- assert.deepEqual(shallow.items.filter((n: any) => n.depth === 2), [], '깊이 상한 아래는 안 판다')
160
- /* 너비 우선이라 **얕은 층은 온전하다** — 깊이 우선이면 한 가지만 깊게 파고 끊긴다. */
161
- assert.deepEqual(shallow.items.filter((n: any) => n.depth === 1).map((n: any) => n.id).sort(), ['axle', 'frame'])
162
- })
163
-
164
- test('선언에 없는 자재를 참조하면 그 사실이 드러난다', async () => {
165
- const ghost = master({
166
- recipes: [
167
- { key: 'trailer-line', label: 't', inputs: [{ material: 'no-such-part', qty: 1 }], outputs: [{ material: 'trailer', qty: 1 }] }
168
- ]
169
- })
170
- await TwinEngine.ingestMaster(domainId, ghost, 'reset')
171
- const t = await explode('trailer')
172
- const child = t.items.find((n: any) => n.id === 'no-such-part')
173
- assert.ok(child, '가리켜진 것은 그리되')
174
- assert.equal(child.known, false, '선언에 없다는 사실을 함께 낸다(있는 척하지 않는다)')
175
- await TwinEngine.ingestMaster(domainId, master(), 'reset')
176
- })
177
-
178
- test('★ 선언에 없는 축을 거치면 터지지 않고 **왜 못 하는지 말한다**', async () => {
179
- /*
180
- * ── 실측으로 터진 것 (2026-08-24) ────────────────────────────────────────
181
- * 이 전개는 **트윈 모델의 선언**을 읽는다(`axisInfo(...).path` 로 모델 안을 찾아 들어간다). 그런데
182
- * 커널 카탈로그의 축 중 셋은 **선언되는 것이 아니라 일어나는 것**이다 — `orders`·`tasks`·`items`
183
- * 는 사건이 만들고 끝나면 사라진다. 모델이 그 목록을 가질 수 없으므로 `path` 가 비어 있다.
184
- *
185
- * Cannot read properties of undefined (reading 'split')
186
- *
187
- * ★ **터진 것은 요청한 축이 아니라 「거쳐 가는 축」이다.** `locations` 를 물으면 그 순환이 `tasks` 를
188
- * 거치는데(작업이 `from`·`at` 으로 자리를 두 번 가리킨다) 그 `tasks` 에 경로가 없다. 그래서 요청한
189
- * 축만 지켜서는 막히지 않는다 — 처음에 그렇게 고쳤다가 이 시험이 잡았다.
190
- *
191
- * 그리고 빈 트리로 답하면 화면이 「이어진 것이 없다」로 읽는데 그것은 거짓이다 — 작업은 자리를
192
- * 가리키고 있고, 다만 이 질의가 그것을 아는 곳이 아니다.
193
- */
194
- const q = new TwinModelTreeQuery()
195
- const ctx = { state: { domain: { id: domainId } } } as any
196
-
197
- /* 두 홉을 지정해야 순환이 하나로 정해진다 — 안 지정하면 앞 갈래(모호함)에서 답한다. */
198
- const loc = await q.twinModelTree(INSTANCE, 'locations', 'st-a', ctx, 'ops.rel.from', 'ops.rel.at')
199
- assert.ok(loc, 'locations: 터지지 않는다')
200
- assert.deepEqual(loc.items, [], 'locations: 펼친 것이 없다')
201
- assert.equal(loc.reason, 'not-declared-in-model', 'locations: 왜 못 하는지 말한다(거쳐 갈 축이 선언에 없다)')
202
-
203
- /* 자기참조 순환이 아예 없는 축은 그 사실을 말한다 — 이유가 다르면 낱말도 달라야 한다. */
204
- for (const axis of ['orders', 'tasks', 'items']) {
205
- const r = await q.twinModelTree(INSTANCE, axis, 'anything', ctx)
206
- assert.ok(r, `${axis}: 터지지 않는다`)
207
- assert.equal(r.reason, 'no-recursive-relation', `${axis}: 순환이 없다는 사실을 말한다`)
208
- }
209
- })
@@ -1,133 +0,0 @@
1
- /*
2
- * ★ 트윈은 자기 원본을 기억한다 — **기억하지 않으면 다시 읽을 수 없다.**
3
- *
4
- * 트윈은 원본을 비출 뿐이므로(ADR-0028) 원본이 달라지면 다시 읽어야 한다. 그런데 트윈은
5
- * "내가 무엇에서 나왔나" 를 남기지 않았다 — 어댑터로 들어온 트윈에는 `TwinReference` 행이
6
- * 있었지만 템플릿으로 세운 트윈에는 아무 표식도 없었다. 그 트윈은 **첫날의 모습에 영원히
7
- * 머문다.** 실제로 이 앱의 트윈 둘이 그 상태였다(`twinReferences` 도 비었고 `source` 도 null).
8
- *
9
- * 여기서 지키는 것 셋:
10
- * · 인제스트가 원천을 각인한다 — 원천이 무엇이든 한 자리에
11
- * · 원천을 모르는 재프로비전이 **이미 아는 것을 지우지 않는다** — 한 번 지우면 다시 못 읽는다
12
- * · 못 읽는 트윈은 **못 읽는다고 말한다** — 짐작으로 아무 원본이나 집지 않는다
13
- *
14
- * Since 2026-09-24 (chief architect ruling, engine/producer-guard.ts) a re-provision that does not come
15
- * from the twin's own producer is **refused** rather than applied: a silent one used to keep the origin
16
- * but replace the structure, and a different producer used to replace both — the second is how the
17
- * plant mirror `miratek` got the warehouse's structure. The two tests below now pin the refusal, and
18
- * that the refused twin's origin and structure are untouched.
19
- */
20
- import { test, before, after } from 'node:test'
21
- import assert from 'node:assert/strict'
22
- import { createRequire } from 'node:module'
23
- import type { DataSource } from 'typeorm'
24
-
25
- const req = createRequire(import.meta.url)
26
- const { NamingStrategy, addDataSource, entities: shellEntities, Domain } = req('@things-factory/shell')
27
- const { entities: authEntities } = req('@things-factory/auth-base')
28
- const { entities: twinEntities } = req('../dist-server/service/index.js')
29
- const { TwinEngine } = req('../dist-server/engine/index.js')
30
- const { TwinInstance } = req('../dist-server/service/twin-instance/twin-instance.js')
31
- const { DataSource: TypeOrmDataSource } = req('typeorm')
32
-
33
- let ds: DataSource
34
- let domainId: string
35
-
36
- const BORN = 'origin-probe'
37
- const ORPHAN = 'orphan-probe'
38
-
39
- const master = (source: string, bins: number, origin?: any): any => ({
40
- source,
41
- system: 'wms',
42
- siteName: '원천 창고',
43
- space: { width: 40, depth: 20, unit: 'm' },
44
- locations: [
45
- { id: 'dock-recv', type: 'dock', role: 'location', name: '입고 도크', capacity: 100 },
46
- ...Array.from({ length: bins }, (_, i) => ({ id: `bin-${i + 1}`, type: 'storage', role: 'location', name: `빈${i + 1}`, capacity: 20 }))
47
- ],
48
- equipment: [{ id: 'fk-1', kind: 'forklift', homeLocationId: 'dock-recv' }],
49
- ...(origin ? { origin } : {})
50
- })
51
-
52
- const originOf = async (instanceId: string) =>
53
- (await ds.getRepository(TwinInstance).findOne({ where: { instanceId } }))?.origin
54
-
55
- before(async () => {
56
- ds = new TypeOrmDataSource({
57
- type: 'sqlite',
58
- database: ':memory:',
59
- synchronize: true,
60
- dropSchema: true,
61
- namingStrategy: new NamingStrategy(),
62
- entities: [...shellEntities, ...authEntities, ...twinEntities],
63
- logging: false
64
- })
65
- await ds.initialize()
66
- addDataSource('default', ds)
67
- const d = await ds.getRepository(Domain).save({ name: 'origin-test', subdomain: 'origin-test' } as any)
68
- domainId = d.id
69
- })
70
-
71
- after(async () => {
72
- for (const id of [BORN, ORPHAN]) await TwinEngine.stop(domainId, id).catch(() => {})
73
- await ds?.destroy()
74
- })
75
-
76
- test('인제스트가 원천을 각인한다 — 원천이 무엇이든 한 자리에', async () => {
77
- await TwinEngine.ingestMaster(domainId, master(BORN, 3, { kind: 'template', templateId: 'wh-unit-load', params: { bins: 3 } }), 'reset')
78
- assert.deepEqual(await originOf(BORN), { kind: 'template', templateId: 'wh-unit-load', params: { bins: 3 } })
79
- })
80
-
81
- const TEMPLATE = { kind: 'template', templateId: 'wh-unit-load', params: { bins: 3 } }
82
- const locationsOf = async (instanceId: string) =>
83
- (await ds.getRepository(TwinInstance).findOne({ where: { instanceId } }))?.model?.locations?.length
84
-
85
- test('원천을 모르는 재프로비전은 거절된다 — 원천도 구조도 그대로다', async () => {
86
- /*
87
- * 이 경로는 실제로 있다 — 원천을 모르는 호출자(스크립트·복구·옛 코드)가 같은 트윈을 다시 인제스트한다.
88
- * Its origin used to be kept while the structure was replaced by whatever that caller had. It is refused
89
- * now (ruling (b)): the twin's own producer re-reads it; anything else deletes it first.
90
- */
91
- await assert.rejects(TwinEngine.ingestMaster(domainId, master(BORN, 5), 'reset'), (e: any) => {
92
- assert.equal(e.code, 'instance-taken-by-another-source')
93
- assert.deepEqual(e.params, { instance: BORN, producer: 'wh-unit-load' })
94
- return true
95
- })
96
- assert.deepEqual(await originOf(BORN), TEMPLATE)
97
- assert.equal(await locationsOf(BORN), 4, 'the refused master did not land (dock 1 + bins 3)')
98
- })
99
-
100
- test('다른 생산자는 거절된다 — 이름을 대고, 원천도 구조도 그대로다', async () => {
101
- await assert.rejects(
102
- TwinEngine.ingestMaster(domainId, master(BORN, 5, { kind: 'reference', source: 'oracle-wms' }), 'reset'),
103
- (e: any) => e.code === 'instance-taken-by-another-source' && e.params?.producer === 'wh-unit-load'
104
- )
105
- assert.deepEqual(await originOf(BORN), TEMPLATE)
106
- assert.equal(await locationsOf(BORN), 4)
107
- })
108
-
109
- test('같은 생산자가 다시 읽으면 지나간다 — 노브가 달라도', async () => {
110
- const again = { ...TEMPLATE, params: { bins: 5 } }
111
- await TwinEngine.ingestMaster(domainId, master(BORN, 5, again), 'reset')
112
- assert.deepEqual(await originOf(BORN), again)
113
- assert.equal(await locationsOf(BORN), 6, 'its own template re-run rebuilds it (dock 1 + bins 5)')
114
- })
115
-
116
- test('원천 없이 태어난 트윈은 원천이 없다 — 짐작해 채우지 않는다', async () => {
117
- await TwinEngine.ingestMaster(domainId, master(ORPHAN, 2), 'reset')
118
- assert.equal(await originOf(ORPHAN), null, '모르는 것을 아는 척하면 엉뚱한 원본으로 현장을 덮는다')
119
- })
120
-
121
- test('조회가 다시 읽을 수 있는지를 먼저 답한다 — 못 하는 것을 못 한다고 알린다', async () => {
122
- const { TwinModelQuery } = req('../dist-server/service/twin-model/twin-model-query.js')
123
- const q = new TwinModelQuery()
124
- const ctx = { state: { domain: { id: domainId } } } as any
125
-
126
- const born = await q.twinModelSummary(BORN, ctx)
127
- assert.equal(born.canResync, true)
128
- assert.deepEqual(born.origin, { ...TEMPLATE, params: { bins: 5 } })
129
-
130
- const orphan = await q.twinModelSummary(ORPHAN, ctx)
131
- assert.equal(orphan.canResync, false, '버튼을 그대로 내면 누를 때마다 실패한다')
132
- assert.equal(orphan.origin, null)
133
- })
@@ -1,111 +0,0 @@
1
- /*
2
- * ★ Two tenants, one twin name — each subscriber receives its own tenant's messages and nothing else.
3
- *
4
- * The same instanceId (and the same reference source) in two tenants is the expected case: source
5
- * names such as `sap-ewm-1710` are vocabulary tenants share (`runtime-key.ts`). Until 2026-09-24 both
6
- * channels filtered on that name alone, so tenant B's subscriber received A's deltas, and the snapshot
7
- * sent when A subscribed went to B as well.
8
- *
9
- * This counts what arrives. A filter test alone would not see a sender that builds its own payload
10
- * without a domain, so the last two tests read the source: every sender goes through the channel's
11
- * payload builder, and nothing else names the topic.
12
- */
13
- import { test } from 'node:test'
14
- import assert from 'node:assert/strict'
15
- import { readdirSync, readFileSync, statSync } from 'node:fs'
16
- import { dirname, join, relative } from 'node:path'
17
- import { fileURLToPath } from 'node:url'
18
-
19
- import { TWIN_STATE_TOPIC, twinStateFilter, twinStatePayload } from '../server/engine/twin-state-channel.ts'
20
- import {
21
- REFERENCE_PROGRESS_TOPIC,
22
- referenceProgressFilter,
23
- referenceProgressPayload
24
- } from '../server/service/reference/reference-progress-channel.ts'
25
-
26
- const A = 'domain-a'
27
- const B = 'domain-b'
28
- const SHARED = 'sap-ewm-1710'
29
-
30
- /* A pubsub reduced to what matters here: every published payload, and a subscriber's filter over them. */
31
- function bus() {
32
- const sent: { topic: string; payload: any }[] = []
33
- return {
34
- publish: (topic: string, payload: any) => void sent.push({ topic, payload }),
35
- received: (topic: string, keep: (p: any) => boolean) => sent.filter(m => m.topic === topic).map(m => m.payload).filter(keep)
36
- }
37
- }
38
-
39
- test('twin-state: B receives none of A\'s snapshot or deltas for a twin of the same name', () => {
40
- const b = bus()
41
- /* A subscribes (its snapshot goes out), then both tenants' twins tick. */
42
- b.publish(TWIN_STATE_TOPIC, twinStatePayload(A, { instanceId: SHARED, kind: 'snapshot', revision: 7, payload: { owner: 'A' } }))
43
- b.publish(TWIN_STATE_TOPIC, twinStatePayload(A, { instanceId: SHARED, kind: 'delta', revision: 8 }))
44
- b.publish(TWIN_STATE_TOPIC, twinStatePayload(B, { instanceId: SHARED, kind: 'delta', revision: 3 }))
45
- b.publish(TWIN_STATE_TOPIC, twinStatePayload(B, { instanceId: 'other-twin', kind: 'delta', revision: 1 }))
46
-
47
- const toB = b.received(TWIN_STATE_TOPIC, twinStateFilter(B, SHARED))
48
- assert.deepEqual(toB.map(p => [p.domainId, p.twinState.kind, p.twinState.revision]), [[B, 'delta', 3]])
49
-
50
- const toA = b.received(TWIN_STATE_TOPIC, twinStateFilter(A, SHARED))
51
- assert.deepEqual(toA.map(p => [p.twinState.kind, p.twinState.revision]), [['snapshot', 7], ['delta', 8]])
52
- })
53
-
54
- test('twin-reference-progress: B receives none of A\'s import progress for a source of the same name', () => {
55
- const b = bus()
56
- b.publish(REFERENCE_PROGRESS_TOPIC, referenceProgressPayload(A, { source: SHARED, step: 'master', done: 1, error: 'A: connection refused' }))
57
- b.publish(REFERENCE_PROGRESS_TOPIC, referenceProgressPayload(B, { source: SHARED, step: 'master', done: 2 }))
58
-
59
- const toB = b.received(REFERENCE_PROGRESS_TOPIC, referenceProgressFilter(B, SHARED))
60
- assert.deepEqual(toB.map(p => [p.domainId, p.twinReferenceProgress.done]), [[B, 2]])
61
- })
62
-
63
- test('a payload cannot be built without a domain', () => {
64
- assert.throws(() => twinStatePayload('', { instanceId: SHARED, kind: 'delta' }), /domainId is required/)
65
- assert.throws(() => referenceProgressPayload('', { source: SHARED }), /domainId is required/)
66
- })
67
-
68
- /* ── every sender goes through the channel ────────────────────────────────── */
69
-
70
- const SERVER = join(dirname(fileURLToPath(import.meta.url)), '..', 'server')
71
- const CHANNELS = new Set(['engine/twin-state-channel.ts', 'service/reference/reference-progress-channel.ts'])
72
-
73
- function serverSources(): { path: string; code: string }[] {
74
- const out: { path: string; code: string }[] = []
75
- const walk = (dir: string) => {
76
- for (const name of readdirSync(dir)) {
77
- const p = join(dir, name)
78
- if (statSync(p).isDirectory()) walk(p)
79
- else if (p.endsWith('.ts')) {
80
- /* Comments describe the old shape; count code only. */
81
- const code = readFileSync(p, 'utf8').replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^:])\/\/.*$/gm, '$1')
82
- out.push({ path: relative(SERVER, p), code })
83
- }
84
- }
85
- }
86
- walk(SERVER)
87
- return out
88
- }
89
-
90
- test('no sender builds a channel payload by hand, and nothing names the topics but the channels', () => {
91
- const offenders: string[] = []
92
- for (const { path, code } of serverSources()) {
93
- if (CHANNELS.has(path)) continue
94
- if (/\btwinState\s*:\s*\{/.test(code)) offenders.push(`${path}: builds { twinState: … } by hand`)
95
- if (/\btwinReferenceProgress\s*:\s*\{/.test(code)) offenders.push(`${path}: builds { twinReferenceProgress: … } by hand`)
96
- if (/['"`]twin-state['"`]/.test(code)) offenders.push(`${path}: names 'twin-state'`)
97
- if (/['"`]twin-reference-progress['"`]/.test(code)) offenders.push(`${path}: names 'twin-reference-progress'`)
98
- }
99
- assert.deepEqual(offenders, [])
100
- })
101
-
102
- test('the senders are really there — the scan above is not passing by finding nothing', () => {
103
- const callers = serverSources()
104
- .filter(s => !CHANNELS.has(s.path))
105
- .map(s => s.code)
106
- .join('\n')
107
- /* engine: runtime subscription + flush; the subscription's first snapshot. */
108
- assert.equal((callers.match(/twinStatePayload\(/g) ?? []).length, 3, 'twinStatePayload call sites')
109
- /* resync and import. */
110
- assert.equal((callers.match(/referenceProgressPayload\(/g) ?? []).length, 2, 'referenceProgressPayload call sites')
111
- })
@@ -1,43 +0,0 @@
1
- /*
2
- * 어휘 가드 — 규칙은 커널이 한 벌 들고 있고(`@operato/twin-kernel` `retiredVocabularyIn`),
3
- * 이 테스트는 **자기 패키지 소스를 훑는다.** 판정이 패키지마다 갈릴 수 없다.
4
- *
5
- * 계기(2026-08-01): 개명을 손으로 쓸다 같은 실수를 세 번 반복했다. 단어 경계로 찾으면 파생 식별자가
6
- * 빠지고(`moverCount`·`locationType`), 빠진 것은 다음 파도에서야 드러난다. 그 사이 코드에는 두 어휘가
7
- * 섞여 있고 다음 사람은 어느 쪽을 믿을지 모른다.
8
- *
9
- * 실패하면 위반 목록이 그대로 나온다 — 고치거나, 정당하면 그 줄에 `vocabulary-guard: allow <이유>` 를
10
- * 남긴다(예외가 코드에 이유와 함께 남는다).
11
- */
12
- import { test } from 'node:test'
13
- import assert from 'node:assert/strict'
14
- import { readdirSync, readFileSync, statSync } from 'node:fs'
15
- import { join } from 'node:path'
16
-
17
- import { retiredVocabularyIn } from '@operato/ops-contract'
18
-
19
- const ROOTS = ['server', 'client'].map(d => new URL(`../${d}`, import.meta.url).pathname)
20
-
21
- function walk(dir: string, out: string[] = []): string[] {
22
- let entries: string[]
23
- try { entries = readdirSync(dir) } catch { return out }
24
- for (const e of entries) {
25
- const p = join(dir, e)
26
- if (statSync(p).isDirectory()) walk(p, out)
27
- else if (p.endsWith('.ts') && !p.endsWith('.d.ts')) out.push(p)
28
- }
29
- return out
30
- }
31
-
32
- test('소스에 은퇴한 어휘(location·mover)가 남아 있지 않다', () => {
33
- const bad: string[] = []
34
- for (const root of ROOTS) {
35
- for (const f of walk(root)) {
36
- readFileSync(f, 'utf-8').split('\n').forEach((line, i) => {
37
- const hits = retiredVocabularyIn(line)
38
- if (hits.length) bad.push(`${f.split('/').slice(-2).join('/')}:${i + 1} ${[...new Set(hits)].join(', ')}`)
39
- })
40
- }
41
- }
42
- assert.deepEqual(bad, [], `은퇴 어휘 ${bad.length}곳:\n${bad.slice(0, 60).join('\n')}${bad.length > 60 ? `\n… ${bad.length - 60} more` : ''}`)
43
- })
@@ -1,162 +0,0 @@
1
- /*
2
- * ★ 웜스타트 이음매 검증 — 판정이 만든 씨앗이 **실제 커널에 심기는지**.
3
- *
4
- * ── 왜 따로 있나 ────────────────────────────────────────────────────────────
5
- * `warm-start.test.ts` 는 순수 판정만 본다("무엇을 씨앗에 담기로 했나"). 그것만으로는 부족했다:
6
- * 재기동 후 주문이 사라진 결함은 판정과 커널 **사이**에 있었기 때문이다. 판정은 오더를 버렸고,
7
- * 커널은 받을 준비가 되어 있었다. 양쪽 테스트가 각각 초록이어도 이음매는 끊겨 있었다.
8
- *
9
- * 특히 이 테스트가 겨누는 것은 **`snap.orders` 경로**다. 커널은 오더를 두 가지로 받는다 —
10
- * 두 번째 인자(`OrderStatusDelta[]`)와 스냅샷 안(`snap.orders`). 커널 테스트는 두 번째 인자를
11
- * 쓰고, 호스트 웜스타트는 스냅샷 안을 쓴다. 즉 **호스트가 의존하는 쪽이 검증되지 않은 상태**였다.
12
- *
13
- * ── 무엇을 단언하나 ────────────────────────────────────────────────────────
14
- * 스냅샷의 오더가 남은 데맨드(requested − fulfilled)로 커널에 서고, 이행이 끝난 오더는 서지 않고,
15
- * 원값이 없는 오더는 알리지 않고 서지 않는다는 것. 숫자를 지어내지 않는다는 규칙이 이음매 끝까지 지켜지는가.
16
- */
17
- import { test } from 'node:test'
18
- import assert from 'node:assert/strict'
19
-
20
- import { WmsKernel } from '@operato/twin-kernel'
21
-
22
- import { planWarmStart } from '../server/engine/warm-start.ts'
23
- import { withLiveAttentions } from '../server/engine/live-attentions.ts'
24
-
25
- const GTIN = 'urn:epc:idpat:sgtin:0614141.012345.*'
26
-
27
- /*
28
- * 보드 — WMS 출고 흐름(피킹 → 스테이징 → 출하)이 실제로 굴러갈 최소 구성.
29
- * `staging` 을 빼고 짜 봤더니 오더가 서기만 하고 진행이 0 이었다. 이음매가 아니라 픽스처 문제였다 —
30
- * 이 테스트가 흐름 요건까지 시험하려는 것이 아니므로 커널 테스트와 같은 네 자리를 쓴다.
31
- */
32
- const model = () => ({
33
- /* 이 픽스처가 식별자를 만드는 이름공간 — **픽스처마다 다르게** 둔다(같은 접두 아래에서 서로 다른
34
- 시험의 번호가 부딪치면 표준의 유일성 요구를 깬다, CBV 2.0 §8.2.4). 커널은 선언 없이 팔레트·문서를
35
- 만들지 않는다(0.7.45 에서 날조 프리픽스를 지웠다). */
36
- identity: { namespaces: ['https://twin.test/warm-start-seam'] },
37
- locations: [
38
- { id: 'dock-recv', type: 'dock', capacity: 100 },
39
- { id: 'bin-1', type: 'storage', capacity: 200 },
40
- { id: 'staging', type: 'staging', capacity: 100 },
41
- { id: 'dock-ship', type: 'dock-ship', capacity: 100 }
42
- ],
43
- equipment: [
44
- { id: 'fk-1', kind: 'forklift', homeLocation: 'dock-recv' },
45
- { id: 'fk-2', kind: 'forklift', homeLocation: 'dock-recv' }
46
- ]
47
- })
48
-
49
- /** 관측 스냅샷 흉내가 아니라, 스냅샷이 실제로 들고 오는 모양(일곱 축 중 필요한 것). */
50
- const observed = (orders: unknown[]) => ({
51
- locations: model().locations.map(n => ({ ...n, occupancy: 0, status: 'idle' })),
52
- items: Array.from({ length: 20 }, (_, i) => ({
53
- epc: `urn:epc:id:sgtin:0614141.012345.${i}`,
54
- location: 'bin-1',
55
- gtin: GTIN,
56
- qty: 1
57
- })),
58
- equipment: model().equipment.map(m => ({ ...m, location: 'dock-recv', status: 'idle' })),
59
- tasks: [],
60
- orders
61
- })
62
-
63
- /** 판정 → 주입. 호스트 `warmStart` 가 하는 일과 같은 순서로 잇는다. */
64
- function seedKernel(state: ReturnType<typeof observed>) {
65
- const plan = planWarmStart(state, undefined, true)
66
- assert.equal(plan.action, 'hydrate', '심기로 판정해야 한다')
67
- if (plan.action !== 'hydrate') throw new Error('unreachable')
68
- const k = new WmsKernel('t')
69
- k.loadTwinModel(model() as any)
70
- ;(k as any).hydrateObserved(plan.seed)
71
- return { k, plan }
72
- }
73
-
74
- test('스냅샷 안의 오더가 커널에 선다 — 남은 데맨드로', () => {
75
- const { k, plan } = seedKernel(
76
- observed([{ id: 'SO-1', kind: 'outbound', status: 'in-progress', requested: 20, fulfilled: 5, lines: [{ gtin: GTIN, requested: 20, fulfilled: 5 }] }])
77
- )
78
- assert.equal(plan.orderCount, 1)
79
- const o = (k as any).orders.get('SO-1')
80
- assert.ok(o, '재기동 후 진행 중 주문이 커널에 있어야 한다 — 사라지던 결함의 회귀 방지')
81
- assert.equal(o.requested, 15, '남은 데맨드 15(20−5)로 서야 한다 — 원값이 아니라 남은 양이다')
82
- })
83
-
84
- test('재고가 있으면 복원된 오더가 실제로 이행된다 — 서 있기만 하면 의미가 없다', () => {
85
- const { k } = seedKernel(
86
- observed([{ id: 'SO-1', kind: 'outbound', status: 'in-progress', requested: 8, fulfilled: 0, lines: [{ gtin: GTIN, requested: 8, fulfilled: 0 }] }])
87
- )
88
- const fc = (k as any).fork()
89
- for (let t = 0; t < 1200; t++) fc.tick(1000)
90
- const o = fc.getSnapshot().orders.find((x: any) => x.id === 'SO-1')
91
- assert.ok(o, '예측에도 오더가 있어야 한다')
92
- assert.ok(o.progress > 0, `복원된 오더가 진행돼야 한다(progress=${o.progress})`)
93
- })
94
-
95
- test('이미 이행된 오더는 서지 않는다 — 다 끝난 일을 다시 계획하지 않는다', () => {
96
- const { k, plan } = seedKernel(
97
- observed([{ id: 'SO-DONE', kind: 'outbound', status: 'completed', requested: 10, fulfilled: 10, lines: [{ gtin: GTIN, requested: 10, fulfilled: 10 }] }])
98
- )
99
- assert.equal(plan.orderCount, 1, '판정은 원값이 있으므로 넘긴다')
100
- assert.equal((k as any).orders.get('SO-DONE'), undefined, '커널이 남은 양 0 으로 보고 세우지 않는다')
101
- })
102
-
103
- test('원값이 없는 오더는 커널에 서지 않고, 몇 건인지 남는다', () => {
104
- const state = observed([{ id: 'SO-P', kind: 'outbound', status: 'in-progress', progress: 0.5 }])
105
- const plan = planWarmStart(state, undefined, true)
106
- assert.equal(plan.action, 'hydrate')
107
- if (plan.action !== 'hydrate') return
108
- assert.equal(plan.orderCount, 0)
109
- assert.equal(plan.ordersWithoutDemand, 1, '뺀 사실이 숫자로 남아야 로그가 말할 수 있다')
110
-
111
- const k = new WmsKernel('t')
112
- k.loadTwinModel(model() as any)
113
- ;(k as any).hydrateObserved(plan.seed)
114
- assert.equal((k as any).orders.size, 0, 'progress 만으로는 남은 양을 알 수 없으므로 지어내지 않는다')
115
- })
116
-
117
- test('재고도 함께 살아난다 — 오더를 넣다가 기존 복원을 깨지 않았는지', () => {
118
- const { k } = seedKernel(observed([{ id: 'SO-1', requested: 5, fulfilled: 0, kind: 'outbound', status: 'in-progress' }]))
119
- const snap = (k as any).getSnapshot()
120
- assert.equal(snap.items.length, 20, '재고 20건이 그대로 복원돼야 한다')
121
- assert.equal(snap.equipment.length, 2)
122
- })
123
-
124
- test('라이브 상태의 확인 처리가 신호에 반영된다 — 라이브에서는 전혀 반영되지 않던 것', () => {
125
- /*
126
- * `withLiveAttentions` 가 확인 목록을 넘기지 않아, 라이브(미러)에서는 사람이 확인해도 신호가
127
- * 계속 활성으로 나갔다. 저널에서 되살린 확인 처리가 여기까지 닿는지 본다.
128
- */
129
- const state = {
130
- locations: [{ id: 'bin-tight', type: 'storage', capacity: 1, occupancy: 1 }],
131
- equipment: [],
132
- orders: [],
133
- tasks: [],
134
- acked: [] as string[]
135
- }
136
- const before = withLiveAttentions(state).attentions ?? []
137
- const signal = before[0]
138
- assert.ok(signal, '픽스처 전제 — 병목 신호가 서야 한다')
139
- /* 확인 전에는 `state` 를 채우지 않는다(확인된 것만 표시한다) — 없음과 'acknowledged' 를 가른다. */
140
- assert.notEqual(signal.state, 'acknowledged', '확인하지 않았는데 확인된 것으로 나오면 안 된다')
141
-
142
- const after = withLiveAttentions({ ...state, acked: [signal.id] }).attentions ?? []
143
- assert.equal(after.find((a: any) => a.id === signal.id)?.state, 'acknowledged', '확인 처리가 라이브 신호에 반영돼야 한다')
144
- })
145
-
146
- /*
147
- * ★ Folded orders survive a restart (ADR-0092). Their counts are the only record of the finished orders
148
- * taken out of the hot list, and the seed used to leave them out — a restart after a fold lost them all.
149
- */
150
- test('the folded side comes back with the twin — nothing re-sends those counts', () => {
151
- const foldedOrders = { counts: { outbound: { shipped: 40, cancelled: 2 }, inbound: { completed: 55 } }, boundaryMs: 1_700_000_000_000 }
152
- const { k, plan } = seedKernel({ ...observed([]), foldedOrders } as any)
153
- assert.equal(plan.foldedOrderCount, 97)
154
- assert.deepEqual((k as any).getSnapshot().foldedOrders?.counts, foldedOrders.counts)
155
- })
156
-
157
- test('a value without counts is not a folded side, and a twin that folded nothing carries none', () => {
158
- const junk = planWarmStart({ ...observed([]), foldedOrders: { boundaryMs: 1 } } as any, undefined, true)
159
- assert.equal(junk.action === 'hydrate' && 'foldedOrders' in junk.seed, false)
160
- const none = planWarmStart(observed([]), undefined, true)
161
- assert.equal(none.action === 'hydrate' && none.foldedOrderCount, 0)
162
- })