@things-factory/headless-twin 10.1.277 → 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,235 +0,0 @@
1
- /*
2
- * **놓친 구간을 backfill 해 채운다** — 그 길이 없었다.
3
- *
4
- * ── 무엇이 있었나 (2026-08-31, 인티그레이션 레인 지적) ────────────────────
5
- * 번호로 「빠졌다」를 알아채게 만들었는데, 알아챈 뒤 **그 사이를 채우는 길이 없었다.** 설계가 적어 둔
6
- * 길이고 양쪽이 다 읽었는데 만들지 않았다.
7
- *
8
- * ── 되풀이해 요청하는 경로에서 무너지는 것 ─────────────────────────────────────
9
- * 행복한 길은 짧다. 무너지는 것은 **끝나지 않는 것**이다.
10
- *
11
- * 빈 답에 물어본 번호를 그대로 돌려주는 원본 → 무한히 요청한다
12
- * 구멍 뒤를 이어 받는다 → 커서가 그 번호를 지나쳐 적히고 빠진 사실은 안 온다
13
- * 상한에 닿았는데 조용히 끝낸다 → 사람은 다 받은 줄로 읽는다
14
- *
15
- * 셋 다 오류가 나지 않는다. 그래서 시험이 셋을 만든다.
16
- */
17
- import { test } from 'node:test'
18
- import assert from 'node:assert/strict'
19
-
20
- import { fillSince } from '../server/service/reference/reference-fill.js'
21
- import { pushCursorKey, seqOf } from '../server/service/reference/hook-contract.js'
22
- import type { InboundBatch } from '../server/service/reference/reference-adapter.js'
23
-
24
- const CFG = { baseUrl: 'x' }
25
- const SITE = { siteId: 's-1' }
26
- const SCOPE = 'EVENTS'
27
- const KEY = pushCursorKey('twin-a', SCOPE)
28
-
29
- /** 번호가 붙은 봉투 한 쪽. */
30
- const page = (seqs: number[], scope: string = SCOPE): InboundBatch => ({
31
- scope,
32
- items: seqs.map(seq => ({ seq, record: { moverId: `eq-${seq}`, kind: 'press', status: 'idle' } }))
33
- })
34
-
35
- /** 저장본 하나 — 프로세스가 아니라 여기 산다. */
36
- function harness(initial?: unknown) {
37
- const state = { cursor: initial as unknown, saves: 0, ingested: [] as unknown[][] }
38
- return {
39
- state,
40
- saveCursor: async (c: Record<string, unknown>) => {
41
- state.saves++
42
- state.cursor = JSON.parse(JSON.stringify(c))
43
- },
44
- ingest: (records: unknown[]) => {
45
- state.ingested.push(records)
46
- return { applied: records.length, duplicates: 0, rejected: [] }
47
- }
48
- }
49
- }
50
-
51
- const run = (h: ReturnType<typeof harness>, fetch: any, maxPages?: number) =>
52
- fillSince({
53
- cfg: CFG,
54
- site: SITE,
55
- scope: SCOPE,
56
- cursorKey: KEY,
57
- cursor: h.state.cursor,
58
- fetch,
59
- ingest: h.ingest,
60
- saveCursor: h.saveCursor,
61
- ...(maxPages ? { maxPages } : {})
62
- })
63
-
64
- /* ── 채운다 ───────────────────────────────────────────────────────────────── */
65
-
66
- test('★ 커서 다음부터 요청하고 받은 데까지 커서를 올린다', async () => {
67
- const h = harness({ streams: { [KEY]: { seq: 4 } } })
68
- const asked: number[] = []
69
- const out = await run(h, async (_c: any, _s: any, _sc: string, since: number) => {
70
- asked.push(since)
71
- return since === 4 ? page([5, 6]) : page([])
72
- })
73
-
74
- assert.deepEqual(asked, [4, 6], '받은 데부터 다시 요청한다')
75
- assert.equal(out.applied, 2)
76
- assert.equal(out.stopped, 'done')
77
- assert.equal(seqOf(h.state.cursor, KEY), 6)
78
- })
79
-
80
- test('★ 커서가 없으면 0부터 요청한다. 처음 채우는 것이다', async () => {
81
- const h = harness()
82
- const asked: number[] = []
83
- await run(h, async (_c: any, _s: any, _sc: string, since: number) => {
84
- asked.push(since)
85
- return since === 0 ? page([1, 2, 3]) : page([])
86
- })
87
- assert.equal(asked[0], 0)
88
- assert.equal(seqOf(h.state.cursor, KEY), 3)
89
- })
90
-
91
- test('여러 쪽을 이어 받는다', async () => {
92
- const h = harness()
93
- const out = await run(h, async (_c: any, _s: any, _sc: string, since: number) => {
94
- if (since === 0) return page([1, 2])
95
- if (since === 2) return page([3, 4])
96
- return page([])
97
- })
98
- assert.equal(out.applied, 4)
99
- assert.equal(out.pages >= 2, true)
100
- assert.equal(seqOf(h.state.cursor, KEY), 4)
101
- })
102
-
103
- /* ── 끝나지 않는 것을 막는다 ──────────────────────────────────────────────── */
104
-
105
- test('★ 번호가 나아가지 않으면 멈춘다 — 같은 것을 다시 주는 원본에 무한히 요청하지 않는다', async () => {
106
- /*
107
- * 아웃박스 계약이 「빈 묶음에는 물어본 번호를 그대로 돌려준다」이므로, 나아감을 보지 않고 요청하면
108
- * 영원히 돈다. 실제로 그 계약을 쓰는 상대가 있다.
109
- */
110
- const h = harness({ streams: { [KEY]: { seq: 4 } } })
111
- let calls = 0
112
- const out = await run(h, async () => {
113
- calls++
114
- /* 이미 받은 번호만 돌려준다 — 커서가 나아가지 않는다. */
115
- return page([3, 4])
116
- })
117
-
118
- assert.equal(out.stopped, 'no-progress')
119
- assert.ok(calls <= 2, `무한히 요청했다 — ${calls}번`)
120
- assert.match(String(out.detail), /나아가지 않았다/)
121
- })
122
-
123
- test('★ 빈 답은 끝이고 오류가 아니다', async () => {
124
- const h = harness({ streams: { [KEY]: { seq: 9 } } })
125
- const out = await run(h, async () => page([]))
126
- assert.equal(out.stopped, 'done')
127
- assert.equal(out.applied, 0)
128
- assert.equal(out.detail, undefined)
129
- })
130
-
131
- /* ── 구멍 ─────────────────────────────────────────────────────────────────── */
132
-
133
- test('★ 구멍이 있으면 그 앞까지만 넣고 멈춘다 — 이어 받으면 빠진 사실이 다시 오지 않는다', async () => {
134
- const h = harness({ streams: { [KEY]: { seq: 4 } } })
135
- const out = await run(h, async (_c: any, _s: any, _sc: string, since: number) =>
136
- since === 4 ? page([5, 6, 9, 10]) : page([])
137
- )
138
-
139
- assert.equal(out.stopped, 'gap')
140
- assert.equal(out.applied, 2, '5·6 만 넣는다')
141
- assert.equal(seqOf(h.state.cursor, KEY), 6, '커서가 구멍을 지나치지 않는다')
142
- assert.match(String(out.detail), /6 다음이 7/)
143
- })
144
-
145
- test('★ 첫 봉투부터 구멍이면 아무것도 넣지 않고 커서도 그대로다', async () => {
146
- const h = harness({ streams: { [KEY]: { seq: 4 } } })
147
- const out = await run(h, async () => page([9, 10]))
148
-
149
- assert.equal(out.stopped, 'gap')
150
- assert.equal(out.applied, 0)
151
- assert.equal(seqOf(h.state.cursor, KEY), 4)
152
- assert.equal(h.state.ingested.length, 0, '넣을 것이 없는데 유입을 불렀다')
153
- })
154
-
155
- /* ── 상한 ─────────────────────────────────────────────────────────────────── */
156
-
157
- test('★ 쪽 상한에 닿으면 말한다 — 조용히 자르면 사람은 다 받은 줄로 읽는다', async () => {
158
- const h = harness()
159
- let n = 0
160
- const out = await run(h, async () => page([++n]), 3)
161
-
162
- assert.equal(out.stopped, 'page-limit')
163
- assert.equal(out.pages, 3)
164
- assert.match(String(out.detail), /남은 것이 있을 수 있다/)
165
- })
166
-
167
- /* ── 거절 ─────────────────────────────────────────────────────────────────── */
168
-
169
- test('★ backfill 이 실패하면 이유를 남기고 멈춘다 — 커서는 받은 데까지다', async () => {
170
- const h = harness({ streams: { [KEY]: { seq: 4 } } })
171
- const out = await run(h, async (_c: any, _s: any, _sc: string, since: number) => {
172
- if (since === 4) return page([5])
173
- throw new Error('MES 가 응답하지 않는다')
174
- })
175
-
176
- assert.equal(out.stopped, 'refused')
177
- assert.match(String(out.detail), /응답하지 않는다/)
178
- assert.equal(seqOf(h.state.cursor, KEY), 5, '받은 것은 지킨다')
179
- })
180
-
181
- test('★ 번호가 섞인 쪽은 받지 않는다 — 밀어 받는 길과 같은 검사를 지난다', async () => {
182
- const h = harness()
183
- const out = await run(h, async () => ({
184
- scope: SCOPE,
185
- items: [{ seq: 1, record: {} }, { record: {} }]
186
- }))
187
-
188
- assert.equal(out.stopped, 'refused')
189
- assert.match(String(out.detail), /mixed batch/)
190
- assert.equal(h.state.ingested.length, 0)
191
- })
192
-
193
- test('★ 커서 열쇠에 트윈이 들어간다 — 한 연결의 두 공장이 계수기를 함께 쓰지 않는다', async () => {
194
- const other = pushCursorKey('twin-b', SCOPE)
195
- const h = harness({ streams: { [KEY]: { seq: 4 }, [other]: { seq: 100 } } })
196
- await run(h, async (_c: any, _s: any, _sc: string, since: number) => (since === 4 ? page([5]) : page([])))
197
-
198
- assert.equal(seqOf(h.state.cursor, KEY), 5)
199
- assert.equal(seqOf(h.state.cursor, other), 100, '다른 공장의 번호를 건드렸다')
200
- })
201
-
202
- /* ── 원본이 가진 번호 범위 ──────────────────────────────────────────────────── */
203
-
204
- test('★ 원본이 말한 번호 범위를 그대로 낸다 — 커서와 견주려면 이 값이 있어야 한다', async () => {
205
- const got = await fillSince({
206
- cfg: {},
207
- site: { siteId: 's' },
208
- scope: 'EVENTS',
209
- cursorKey: 'i::EVENTS',
210
- cursor: {},
211
- fetch: async () => ({ items: [{ seq: 1, record: {} }], scope: 'EVENTS', sourceRange: { first: 1, last: 20 } }),
212
- ingest: () => ({ applied: 1, duplicates: 0, rejected: [] }),
213
- saveCursor: async () => {}
214
- })
215
- assert.deepEqual(got.sourceRange, { first: 1, last: 20 })
216
- })
217
-
218
- test('★ 원본이 안 말하면 그 칸을 안 낸다 — 0 으로 채우면 「다 받았다」가 된다', async () => {
219
- /*
220
- * 0 을 넣으면 「원본이 1번부터 다 갖고 있다」로 읽히고, 그것이 거짓이면 「우리가 다 받았다」는
221
- * 잘못된 결론이 나온다. 견주는 쪽은 「맞다」가 아니라 「모른다」로 답해야 한다.
222
- */
223
- const got = await fillSince({
224
- cfg: {},
225
- site: { siteId: 's' },
226
- scope: 'EVENTS',
227
- cursorKey: 'i::EVENTS',
228
- cursor: {},
229
- fetch: async () => ({ items: [{ seq: 1, record: {} }], scope: 'EVENTS' }),
230
- ingest: () => ({ applied: 1, duplicates: 0, rejected: [] }),
231
- saveCursor: async () => {}
232
- })
233
- assert.equal(got.sourceRange, undefined)
234
- assert.equal('sourceRange' in got, false, '없는 것을 undefined 로 실으면 화면이 칸을 그린다')
235
- })
@@ -1,70 +0,0 @@
1
- /*
2
- * ★ 커넥터의 **근거 수준** — 목으로 확인한 것을 확인으로 부르지 않는다.
3
- *
4
- * ── 왜 이 축이 생겼나 (2026-08-23, 토대 릴리즈 준비) ────────────────────────
5
- * 등록된 커넥터 열넷 중 실 시스템에 붙어 본 것은 **하나**다(첫 실 연동). 나머지는 목에 대고 만들었다.
6
- * 그런데 화면의 커넥터 목록에 그 구분이 없어서 **목으로 확인한 것이 확인으로 보인다.**
7
- *
8
- * 이 저장소는 같은 모양을 능력 축에서 이미 거절했다 — 「선언만 있는 능력은 사용자에게 거짓말이 된다」
9
- * (§`capabilitiesOf`). 그 규율을 근거에도 세운다.
10
- *
11
- * ── 무엇을 검증할 수 있고 무엇을 못 하나 ────────────────────────────────────
12
- * 능력은 구현이 있는지 보아 선언을 검증할 수 있다. **근거는 볼 수 없다** — 사람이 아는 사실이다.
13
- * 그래서 검증할 수 있는 하나만 검증한다: **실 시스템을 주장하면 무엇에 대고인지 말해야 한다.**
14
- * 가리키는 대상이 없는 주장은 주장이 아니고, 그런 주장은 낡아도 아무도 알 수 없다.
15
- */
16
- import { test } from 'node:test'
17
- import assert from 'node:assert/strict'
18
- import { groundingOf, type ReferenceAdapter } from '../server/service/reference/reference-adapter.ts'
19
-
20
- const adapter = (grounding?: any) => ({ type: 't', grounding } as unknown as ReferenceAdapter)
21
-
22
- test('선언이 없으면 「모른다」다 — 「목이다」가 아니다', () => {
23
- /*
24
- * 둘을 같게 두면 아직 선언하지 않은 새 커넥터가 목으로 낙인찍힌다 — 그것도 없는 사실이다.
25
- * 화면은 `unknown` 을 「아직 밝히지 않았다」로 말해야 한다.
26
- */
27
- assert.deepEqual(groundingOf(adapter()), { level: 'unknown' })
28
- assert.deepEqual(groundingOf(undefined), { level: 'unknown' })
29
- })
30
-
31
- test('실 시스템을 주장하면 무엇에 대고인지 말해야 한다 — 아니면 주장이 내려간다', () => {
32
- /* ★ 이 가드의 핵심. 대상 없는 주장은 낡아도 아무도 모른다. */
33
- for (const level of ['instance', 'product'] as const) {
34
- const g = groundingOf(adapter({ level }))
35
- assert.equal(g.level, 'unknown', `${level} 주장이 대상 없이 통과하면 안 된다`)
36
- assert.match(g.downgraded ?? '', /does not say what it was verified against/)
37
- assert.match(g.downgraded ?? '', new RegExp(level), '무엇을 주장했는지 함께 말한다')
38
- }
39
- /* 여백만 있는 것도 비어 있는 것이다 — 형식을 채워 통과하지 못하게. */
40
- assert.equal(groundingOf(adapter({ level: 'instance', verifiedAgainst: ' ' })).level, 'unknown')
41
- })
42
-
43
- test('대상을 말하면 그 주장이 선다', () => {
44
- const g = groundingOf(adapter({ level: 'instance', verifiedAgainst: 'chef v1 · jejoup / pocheon' }))
45
- assert.equal(g.level, 'instance')
46
- assert.equal(g.verifiedAgainst, 'chef v1 · jejoup / pocheon')
47
- assert.equal(g.downgraded, undefined)
48
- })
49
-
50
- test('목과 가상은 대상 없이도 선다 — 그 자체가 사실이다', () => {
51
- /*
52
- * `mock` 은 「실 시스템에 붙어 본 적이 없다」는 사실이고 그것을 말하는 데 대상이 필요하지 않다.
53
- * `synthetic` 은 한계가 아니라 **성질**이다 — 가상 원본은 스스로 만들어 낸다.
54
- */
55
- assert.equal(groundingOf(adapter({ level: 'mock' })).level, 'mock')
56
- assert.equal(groundingOf(adapter({ level: 'synthetic' })).level, 'synthetic')
57
- })
58
-
59
- test('instance 와 product 를 가른다 — 그 구분이 이 어휘의 요점이다', () => {
60
- /*
61
- * 첫 실 연동에서 그 배포만의 성질이 넷 드러났는데(관계 리솔버가 다른 테넌트의 행을 답한다 · 모르는
62
- * 필터를 알리지 않고 무시한다 · 지워진 품목의 BOM 이 남아 있다 · 사실상 무한을 뜻하는 표식) **그것이
63
- * 제품의 성질인지 그 인스턴스의 성질인지 아무도 모른다.** 두 번째 배포가 붙는 날 갈린다.
64
- *
65
- * 그래서 하나로 확인한 것을 `product` 라 부르면 안 되고, 어휘가 그 구분을 담는다.
66
- */
67
- const one = groundingOf(adapter({ level: 'instance', verifiedAgainst: '한 배포' }))
68
- const many = groundingOf(adapter({ level: 'product', verifiedAgainst: '세 배포' }))
69
- assert.notEqual(one.level, many.level, '두 수준이 같은 값으로 뭉개지지 않는다')
70
- })
@@ -1,177 +0,0 @@
1
- /*
2
- * ★ 연결된 시스템이 밀어 주는 사실을 받는 자리 — 규약을 시험이 지킨다.
3
- *
4
- * ── 왜 만들었나 (2026-08-26 · 사용자 지시) ──────────────────────────────────
5
- * 지금 모든 커넥터가 주기적으로 물어봅니다. 그러면 연결된 시스템이 값을 갱신하는 주기보다 자주 묻게
6
- * 되고, 그쪽 서버에 그만큼 부담을 줍니다. 태양광 발전소에 10초마다 물으면서 10분마다 바뀌는 값을
7
- * 받아 왔고, 그날 그쪽 서버가 부하로 멈췄습니다.
8
- *
9
- * ── 무엇을 지키나 ───────────────────────────────────────────────────────────
10
- * 응답 코드가 규약입니다. 밀어 주는 쪽은 그 코드로 재시도를 정하므로, 우리가 아무렇게나 주면 고칠 수
11
- * 없는 것을 영원히 다시 보내거나, 잠깐의 문제로 사라진 사실을 아무도 모릅니다.
12
- */
13
- import { test } from 'node:test'
14
- import assert from 'node:assert/strict'
15
-
16
- import { WEBHOOK_STATUS, webhookSenderAction } from '@operato/ops-contract'
17
- import { signWebhook, WEBHOOK_HEADER } from '@operato/ops-contract/webhook'
18
-
19
- import {
20
- authorizeHook,
21
- producedInstance,
22
- secretFromHeaders,
23
- secretMatches,
24
- seqOf,
25
- withSeq
26
- } from '../server/service/reference/hook-contract.js'
27
-
28
- test('★ 비밀값을 두 가지 방식으로 받는다 — 밀어 주는 쪽의 관습이 갈린다', () => {
29
- assert.equal(secretFromHeaders({ 'x-twin-hook-secret': 'abc' }), 'abc')
30
- assert.equal(secretFromHeaders({ authorization: 'Bearer abc' }), 'abc')
31
- assert.equal(secretFromHeaders({ authorization: 'bearer abc ' }), 'abc')
32
- assert.equal(secretFromHeaders({}), '')
33
- assert.equal(secretFromHeaders(undefined), '')
34
- })
35
-
36
- test('★ 전용 헤더가 먼저다 — 둘 다 오면 헷갈리지 않는다', () => {
37
- assert.equal(secretFromHeaders({ 'x-twin-hook-secret': 'first', authorization: 'Bearer second' }), 'first')
38
- })
39
-
40
- test('★ 빈 비밀값은 어떤 것과도 맞지 않는다 — 선언하지 않은 연결은 훅을 받지 않는다', () => {
41
- assert.equal(secretMatches('', ''), false)
42
- assert.equal(secretMatches('anything', ''), false)
43
- assert.equal(secretMatches('', 'expected'), false)
44
- })
45
-
46
- test('★ 비밀값 비교는 길이가 같으면 전부를 본다 — 응답 시간으로 알아내지 못하게', () => {
47
- assert.equal(secretMatches('secret-value', 'secret-value'), true)
48
- assert.equal(secretMatches('secret-valuX', 'secret-value'), false)
49
- assert.equal(secretMatches('Xecret-value', 'secret-value'), false)
50
- assert.equal(secretMatches('short', 'secret-value'), false)
51
- })
52
-
53
- test('★ 그 연결이 만든 트윈만 받는다 — 남의 트윈에 밀어 넣지 못한다', () => {
54
- const ref = {
55
- scopeSpec: {
56
- produced: [
57
- { siteId: 'site-a', instanceId: 'twin-a' },
58
- { siteId: 'site-b', instanceId: 'twin-b' }
59
- ]
60
- }
61
- }
62
- assert.deepEqual(producedInstance(ref, 'twin-b'), { siteId: 'site-b' })
63
- assert.equal(producedInstance(ref, 'twin-c'), undefined)
64
- assert.equal(producedInstance(null, 'twin-a'), undefined)
65
- assert.equal(producedInstance({}, 'twin-a'), undefined)
66
- })
67
-
68
- test('★ 응답 코드 표가 계약에 있다 — 여기에 두 번째 벌을 만들지 않는다', () => {
69
- /*
70
- * 예전에는 이 표가 훅 규약 파일에 있었고 보내는 쪽(operato-plant)에도 있었다. 그래서 같은 409 를 양쪽이
71
- * 다른 뜻으로 썼다 — 여기서는 「트윈이 안 돈다」, 저쪽에서는 「번호가 비었다」였다.
72
- *
73
- * 값을 여기서 다시 단정하지 않는다. 계약이 정본이고 그 시험은 계약 쪽에 있다. 여기서 지키는 것은
74
- * **이 파일이 계약을 쓰고 있는가**다.
75
- */
76
- assert.equal(WEBHOOK_STATUS.sequenceGap, 409, '409 는 번호가 비었다는 뜻이다')
77
- assert.equal(WEBHOOK_STATUS.notLive, 503, '트윈이 안 도는 것은 503 이다')
78
- })
79
-
80
- test('★ 트윈이 돌지 않으면 200 을 주지 않는다 — 그쪽이 그 사실을 버린다', () => {
81
- /*
82
- * 200 은 「받아서 반영했다」는 뜻이다. 트윈이 실시간으로 돌지 않아 한 건도 반영되지 않았는데 200 을
83
- * 주면, 밀어 주는 쪽은 전달된 줄 알고 그 사실을 버린다.
84
- *
85
- * 그렇다고 4xx 를 주면 「보낸 요청이 잘못됐다」는 뜻이라 그쪽이 포기할 근거가 된다. 트윈은 누가
86
- * 띄우면 다시 도므로 들고 기다리는 것이 맞다.
87
- */
88
- assert.notEqual(WEBHOOK_STATUS.notLive, WEBHOOK_STATUS.ok)
89
- assert.equal(webhookSenderAction(WEBHOOK_STATUS.notLive), 'wait')
90
- })
91
-
92
- /* ── 인증 — 비밀값과 서명 ─────────────────────────────────────────────────── */
93
-
94
- const SECRET = 'shared-secret'
95
- const NOW = Date.parse('2026-08-31T02:00:00.000Z')
96
- const TS = '2026-08-31T02:00:00.000Z'
97
- const BODY = '{"batch":[{"seq":1,"scope":"EVENTS","record":{}}]}'
98
-
99
- test('★ 서명이 오면 서명을 본다', () => {
100
- const headers = {
101
- [WEBHOOK_HEADER.timestamp]: TS,
102
- [WEBHOOK_HEADER.signature]: signWebhook(SECRET, TS, BODY)
103
- }
104
- assert.deepEqual(authorizeHook({ headers, rawBody: BODY, secret: SECRET, nowMs: NOW }), {
105
- ok: true,
106
- method: 'signature'
107
- })
108
- })
109
-
110
- test('★ 서명이 없으면 비밀값을 본다 — 이미 붙어 있는 커넥터가 그 방식이다', () => {
111
- const got = authorizeHook({ headers: { 'x-twin-hook-secret': SECRET }, rawBody: BODY, secret: SECRET, nowMs: NOW })
112
- assert.deepEqual(got, { ok: true, method: 'secret' })
113
- })
114
-
115
- test('★ 서명이 왔는데 받은 바이트가 없으면 거절한다 — 확인할 수 없는 것을 통과시키지 않는다', () => {
116
- /*
117
- * 파싱한 객체를 다시 문자열로 만들어 확인하고 싶어지는데, 키 순서와 공백이 달라져 어차피 맞지 않는다.
118
- * 맞지 않는 것을 통과시키면 서명이 있으나 마나 한 것이 된다.
119
- */
120
- const headers = {
121
- [WEBHOOK_HEADER.timestamp]: TS,
122
- [WEBHOOK_HEADER.signature]: signWebhook(SECRET, TS, BODY)
123
- }
124
- const got = authorizeHook({ headers, rawBody: undefined, secret: SECRET, nowMs: NOW })
125
- assert.equal(got.ok, false)
126
- assert.match((got as any).reason, /raw body/)
127
- })
128
-
129
- test('★ 서명이 왔으면 비밀값 헤더로 넘어가지 않는다 — 맞는 비밀값을 함께 실어도 서명이 틀리면 거절이다', () => {
130
- const headers = {
131
- 'x-twin-hook-secret': SECRET,
132
- [WEBHOOK_HEADER.timestamp]: TS,
133
- [WEBHOOK_HEADER.signature]: 'deadbeef'
134
- }
135
- const got = authorizeHook({ headers, rawBody: BODY, secret: SECRET, nowMs: NOW })
136
- assert.equal(got.ok, false)
137
- })
138
-
139
- test('거절 사유가 무엇을 고칠지 말한다', () => {
140
- const stale = new Date(NOW - 10 * 60_000).toISOString()
141
- const headers = {
142
- [WEBHOOK_HEADER.timestamp]: stale,
143
- [WEBHOOK_HEADER.signature]: signWebhook(SECRET, stale, BODY)
144
- }
145
- const got = authorizeHook({ headers, rawBody: BODY, secret: SECRET, nowMs: NOW })
146
- assert.deepEqual(got, { ok: false, reason: 'signature expired' })
147
- })
148
-
149
- /* ── 커서에 번호를 적는 자리 ──────────────────────────────────────────────── */
150
-
151
- test('★ 번호는 폴링이 쓰는 커서와 같은 자리에 산다 — 진행 위치를 두 벌로 두지 않는다', () => {
152
- const before = { streams: { EVENTS: { since: '2026-08-30T00:00:00.000Z', seen: ['a'] } }, firstAttachedAt: 'x' }
153
- const after = withSeq(before, 'EVENTS', 918342)
154
-
155
- assert.equal(seqOf(after, 'EVENTS'), 918342)
156
- assert.equal((after as any).streams.EVENTS.since, before.streams.EVENTS.since, 'since 를 지우지 않는다')
157
- assert.deepEqual((after as any).streams.EVENTS.seen, ['a'], 'seen 을 지우지 않는다')
158
- assert.equal((after as any).firstAttachedAt, 'x', '커서의 다른 항목을 지우지 않는다')
159
- })
160
-
161
- test('★ 다른 흐름의 번호를 건드리지 않는다 — 한 단위를 올리면서 다른 단위를 되돌리면 그 구간이 다시 온다', () => {
162
- const before = { streams: { EVENTS: { seq: 10 }, MASTER: { seq: 4 } } }
163
- const after = withSeq(before, 'EVENTS', 11)
164
- assert.equal(seqOf(after, 'EVENTS'), 11)
165
- assert.equal(seqOf(after, 'MASTER'), 4)
166
- })
167
-
168
- test('번호가 없는 흐름은 「모른다」다 — 0 으로 메우지 않는다', () => {
169
- assert.equal(seqOf(undefined, 'EVENTS'), undefined)
170
- assert.equal(seqOf({}, 'EVENTS'), undefined)
171
- assert.equal(seqOf({ streams: {} }, 'EVENTS'), undefined)
172
- assert.equal(seqOf({ streams: { EVENTS: { since: 'x' } } }, 'EVENTS'), undefined)
173
- })
174
-
175
- test('커서가 없던 연결에도 번호를 적을 수 있다', () => {
176
- assert.equal(seqOf(withSeq(undefined, 'EVENTS', 1), 'EVENTS'), 1)
177
- })
@@ -1,56 +0,0 @@
1
- /*
2
- * 원본에만 있는 관계를 화면에 낼 수 있는지 판정하는 부분.
3
- *
4
- * 조회 자체는 DB 와 어댑터를 물어야 돌아가므로 테스트가 다루지 않는다. 여기서 확인하는 것은
5
- * 원본이 준 값을 그대로 믿지 않는다는 것이다.
6
- */
7
- import { test } from 'node:test'
8
- import assert from 'node:assert/strict'
9
-
10
- import { usableLinkGroups } from '../server/service/reference/reference-link-groups.js'
11
-
12
- test('정상 그룹은 그대로 남는다', () => {
13
- const got = usableLinkGroups([
14
- { via: 'mes.rel.report', from: 'productionReports', tense: 'past', ids: ['PR-1'], total: 2 }
15
- ])
16
- assert.equal(got.length, 1)
17
- assert.equal(got[0].tense, 'past')
18
- })
19
-
20
- test('★ 전체 수가 없는 그룹은 버린다 — 화면이 목록 길이를 전체 수로 쓰게 된다', () => {
21
- /*
22
- * 원본이 20개만 보내고 전체 수를 빼면, 실제 200건이 화면에 20건으로 표시된다. 오류가 나지
23
- * 않으므로 아무도 알아채지 못한다.
24
- */
25
- const got = usableLinkGroups([{ via: 'mes.rel.report', from: 'x', ids: ['a', 'b'] } as any])
26
- assert.equal(got.length, 0)
27
- })
28
-
29
- test('전체 수가 0 인 그룹은 버린다 — 원본이 그 축을 취급하지 않는다는 뜻이다', () => {
30
- assert.equal(usableLinkGroups([{ via: 'v', from: 'f', ids: [], total: 0 }]).length, 0)
31
- })
32
-
33
- test('이름이 없는 그룹은 버린다 — 화면에 이름 없는 줄이 된다', () => {
34
- const got = usableLinkGroups([
35
- { via: '', from: 'f', ids: ['a'], total: 1 },
36
- { via: ' ', from: 'f', ids: ['a'], total: 1 },
37
- { from: 'f', ids: ['a'], total: 1 } as any
38
- ])
39
- assert.equal(got.length, 0)
40
- })
41
-
42
- test('원본이 아무것도 안 주거나 목록에 빈 값이 섞여도 견딘다', () => {
43
- assert.deepEqual(usableLinkGroups(undefined), [])
44
- assert.deepEqual(usableLinkGroups(null), [])
45
- assert.equal(usableLinkGroups([null as any, undefined as any]).length, 0)
46
- })
47
-
48
- test('★ 이 테스트가 실제로 무언가를 확인한다 — 통과와 탈락이 둘 다 있어야 한다', () => {
49
- const got = usableLinkGroups([
50
- { via: 'ok', from: 'f', ids: ['a'], total: 1 },
51
- { via: 'no-total', from: 'f', ids: ['a'] } as any,
52
- { via: '', from: 'f', ids: ['a'], total: 5 }
53
- ])
54
- assert.equal(got.length, 1, '통과한 것이 하나여야 한다')
55
- assert.equal(got[0].via, 'ok')
56
- })
@@ -1,39 +0,0 @@
1
- import assert from 'node:assert/strict'
2
- import test from 'node:test'
3
- import { readFileSync } from 'node:fs'
4
- import { join } from 'node:path'
5
- import { reconcileReferenceSequence } from '../server/service/reference/reference-reconciliation.ts'
6
-
7
- test('reports work waiting after the Twin cursor', () => {
8
- const got = reconcileReferenceSequence({ source: { first: 1, last: 48 }, cursorSeq: 30, firstSeqSeen: 1 })
9
- assert.equal(got.status, 'gap')
10
- assert.equal(got.behind, 18)
11
- assert.equal(got.headMissing, undefined)
12
- })
13
-
14
- test('reports the leading range only when the first received sequence proves it', () => {
15
- const got = reconcileReferenceSequence({ source: { first: 1, last: 48 }, cursorSeq: 48, firstSeqSeen: 21 })
16
- assert.equal(got.status, 'gap')
17
- assert.equal(got.headMissing, 20)
18
- })
19
-
20
- test('does not claim alignment when the source range or first received sequence is unknown', () => {
21
- assert.equal(reconcileReferenceSequence({ source: { first: 1 }, cursorSeq: 1, firstSeqSeen: 1 }).status, 'unknown')
22
- assert.equal(reconcileReferenceSequence({ source: { first: 1, last: 1 }, cursorSeq: 1 }).status, 'unknown')
23
- })
24
-
25
- test('reports alignment only with both source bounds and a recorded first sequence', () => {
26
- const got = reconcileReferenceSequence({ source: { first: 1, last: 48 }, cursorSeq: 48, firstSeqSeen: 1 })
27
- assert.equal(got.status, 'aligned')
28
- })
29
-
30
- test('registers the durable reconciliation table and its migration', () => {
31
- const root = join(import.meta.dirname, '..', 'server')
32
- const migration = readFileSync(join(root, 'migrations', '1786800000000-CreateTwinReferenceReconciliations.ts'), 'utf8')
33
- const list = readFileSync(join(root, 'migrations', 'index.ts'), 'utf8')
34
- const referenceIndex = readFileSync(join(root, 'service', 'reference', 'index.ts'), 'utf8')
35
- assert.match(migration, /twin_reference_reconciliations/)
36
- assert.match(migration, /uq_twin_reference_reconciliation_0/)
37
- assert.match(list, /CreateTwinReferenceReconciliations1786800000000/)
38
- assert.match(referenceIndex, /TwinReferenceReconciliation/)
39
- })
@@ -1,80 +0,0 @@
1
- /*
2
- * 런타임 레지스트리 색인 가드 — **엔진 밖에서 맵을 직접 만지지 않는다.**
3
- *
4
- * 계기(2026-08-06): `TwinEngine.instances` 가 `instanceId` 하나로 키를 잡던 시절, 엔진 밖 네 곳이 그 맵을
5
- * **직접 색인**했다(`TwinEngine.instances[instanceId]`, 심지어 `(TwinEngine as any).instances?.[…]`).
6
- * 그래서 도메인 확인이 자리마다 제각각이었고 한 곳은 아예 없었다.
7
- *
8
- * 키를 `(domainId, instanceId)` 로 고쳤지만, **직접 색인이 남아 있으면 같은 부류가 다시 자란다** — 밖에서
9
- * `instances[id]` 를 쓰면 이제는 항상 `undefined` 가 나와 알리지 않고 "안 돌고 있다" 로 읽힌다(더 나쁘다:
10
- * 오류가 아니라 거짓 사실이 된다). 그래서 접근자(`owns`·`runtime`·`kernel`·`metrics`·`snapshot`)만 쓴다.
11
- *
12
- * 이 부류는 이미 한 번 물었다 — `twin-lifecycle-mutation.ts` 주석이 증언한다("과거엔
13
- * `Object.keys(TwinEngine.instances)` 로 전역 반환 → 다른 도메인 트윈이 새던 버그"). 그때는 그 자리만
14
- * 고쳤다. 이번에는 기계가 지킨다.
15
- */
16
- import { test } from 'node:test'
17
- import assert from 'node:assert/strict'
18
- import { readdirSync, readFileSync, statSync } from 'node:fs'
19
- import { join } from 'node:path'
20
-
21
- const SERVER = new URL('../server', import.meta.url).pathname
22
-
23
- /** 맵을 소유한 자리 — 여기서는 색인해야 한다. */
24
- const OWNERS = ['engine/twin-engine.ts', 'engine/runtime-key.ts']
25
-
26
- function walk(dir: string, out: string[] = []): string[] {
27
- for (const entry of readdirSync(dir)) {
28
- const p = join(dir, entry)
29
- if (statSync(p).isDirectory()) walk(p, out)
30
- else if (p.endsWith('.ts') && !p.endsWith('.d.ts')) out.push(p)
31
- }
32
- return out
33
- }
34
-
35
- /** 주석 줄은 뺀다 — 이 결함을 설명하는 주석이 여러 곳에 있고, 그것을 위반으로 셀 수 없다. */
36
- const isComment = (line: string) => /^\s*(\/\/|\/\*|\*)/.test(line)
37
-
38
- test('엔진 밖에서 런타임 레지스트리를 직접 색인하지 않는다', () => {
39
- const offenders: string[] = []
40
-
41
- for (const file of walk(SERVER)) {
42
- const rel = file.slice(SERVER.length + 1)
43
- if (OWNERS.includes(rel)) continue
44
-
45
- readFileSync(file, 'utf-8')
46
- .split('\n')
47
- .forEach((line, i) => {
48
- if (isComment(line)) return
49
- /*
50
- * **엔진 참조가 같은 줄에 있을 때만** 위반이다(`as any` 우회 포함). `.instances[` 만 보면
51
- * 무관한 배열(응답·지역 변수)까지 잡아 정당한 코드를 막는다.
52
- */
53
- if (/TwinEngine\s*(as\s+any\s*)?\)?\s*\.\s*(instances|recovered)/.test(line)) {
54
- offenders.push(`${rel}:${i + 1} ${line.trim().slice(0, 100)}`)
55
- }
56
- })
57
- }
58
-
59
- assert.deepEqual(
60
- offenders,
61
- [],
62
- `레지스트리를 직접 색인하는 곳 ${offenders.length}곳 — 접근자(owns·runtime·kernel·metrics·snapshot)를 쓴다:\n` +
63
- offenders.join('\n')
64
- )
65
- })
66
-
67
- test('엔진이 맵을 만질 때는 항상 키 함수를 거친다', () => {
68
- const src = readFileSync(join(SERVER, 'engine/twin-engine.ts'), 'utf-8')
69
- const bad: string[] = []
70
-
71
- src.split('\n').forEach((line, i) => {
72
- if (isComment(line)) return
73
- /* 색인하는 줄에는 `runtimeKey(` 또는 미리 만들어 둔 `key` 변수가 있어야 한다. */
74
- if (!/this\.(instances|recovered)\s*\[/.test(line)) return
75
- if (/runtimeKey\(/.test(line) || /\[key\]/.test(line)) return
76
- bad.push(`engine/twin-engine.ts:${i + 1} ${line.trim().slice(0, 100)}`)
77
- })
78
-
79
- assert.deepEqual(bad, [], `키 함수를 거치지 않고 색인하는 곳 ${bad.length}곳:\n${bad.join('\n')}`)
80
- })