@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,188 +0,0 @@
1
- /*
2
- * 축의 값을 어디서 읽나 — **선언이 말한 자리에서** (§10 6.5단계)
3
- *
4
- * ── 왜 있나 ────────────────────────────────────────────────────────────────
5
- * 개념 지도는 상태 축을 `state[축이름]` 으로 읽었다. 에너지의 수요 구간은 `state.energy.closed` 에
6
- * 살아서 그 규칙으로는 **언제나 `undefined`** 다 — 카드가 「관측 없음」으로 굳는다. 축은 선언돼 있고
7
- * 상태에도 값이 있는데 이름이 안 맞을 뿐이라, 화면도 서버도 그 원인을 말해 주지 않는다.
8
- *
9
- * 이 시험이 지키는 것은 **모름과 없음의 구별**이다: 경로 중간이 비면 `undefined`(모른다)이고, 배열이
10
- * 비면 빈 배열(없다)이다. 그 둘을 섞으면 「아직 안 온 것」과 「하나도 없는 것」이 같아진다.
11
- */
12
- import { test } from 'node:test'
13
- import assert from 'node:assert/strict'
14
-
15
- import { atPath, axisValueOf } from '../shared/axis-read.ts'
16
-
17
- test('경로가 없는 축은 이름으로 읽는다 — 지금까지의 거동', () => {
18
- const state = { orders: [{ id: 'o1' }], tasks: [] }
19
- assert.deepEqual(axisValueOf(state, { axis: 'orders' }), [{ id: 'o1' }])
20
- assert.deepEqual(axisValueOf(state, { axis: 'tasks' }), [], '빈 배열은 「없다」다 — 모른다가 아니다')
21
- })
22
-
23
- test('경로가 있으면 그 경로로 읽는다 — 중첩된 축이 오류 없이 사라지지 않게', () => {
24
- const state = { energy: { closed: [{ startMs: 1 }, { startMs: 2 }] } }
25
- const value = axisValueOf(state, { axis: 'demandWindows', path: 'energy.closed' })
26
- assert.equal(Array.isArray(value), true)
27
- assert.equal(value.length, 2)
28
- /* 축 이름으로 읽으면 없다 — 그것이 이 함수가 생긴 이유다. */
29
- assert.equal((state as any).demandWindows, undefined)
30
- })
31
-
32
- test('경로 중간이 비면 모른다 — 빈 배열로 메우지 않는다', () => {
33
- assert.equal(axisValueOf({}, { axis: 'demandWindows', path: 'energy.closed' }), undefined)
34
- assert.equal(axisValueOf({ energy: {} }, { axis: 'demandWindows', path: 'energy.closed' }), undefined)
35
- /* 에너지 트윈이 아직 표본을 못 받았을 때가 이 경우다 — 「구간 0」이 아니라 「모름」이다. */
36
- })
37
-
38
- test('상태가 없으면 모른다 — 커널이 멈춰 있으면 0 이 아니다', () => {
39
- assert.equal(axisValueOf(null, { axis: 'orders' }), undefined)
40
- assert.equal(axisValueOf(undefined, { axis: 'demandWindows', path: 'energy.closed' }), undefined)
41
- })
42
-
43
- test('경로 따라가기는 중간 부재에서 멈춘다 — 오류를 내지 않는다', () => {
44
- assert.equal(atPath({ a: { b: 1 } }, 'a.b'), 1)
45
- assert.equal(atPath({ a: null }, 'a.b'), undefined)
46
- assert.equal(atPath(undefined, 'a.b'), undefined)
47
- })
48
-
49
- test('커널 선언과 상태의 모양이 맞는다 — 선언한 경로에 정말 값이 있다', async () => {
50
- /*
51
- * 선언(`path`)과 실제 상태의 모양이 어긋나면 이 배선은 알리지 않고 빈 카드를 만든다. 그래서 **커널이 낸
52
- * 상태로** 확인한다 — 선언을 읽는 쪽과 값을 만드는 쪽이 같은 모양을 쓰는지 보는 것이 요점이다.
53
- */
54
- const { EmsKernel, DEMAND_WINDOW_MS } = await import('@operato/twin-kernel')
55
- const { axisInfo, ENERGY_EVENT } = await import('@operato/ops-contract')
56
- const info = axisInfo('demandWindows')!
57
- assert.ok(info, '커널이 그 축을 선언해야 한다')
58
- assert.equal(info.path, 'energy.closed')
59
-
60
- const k = new EmsKernel('t')
61
- k.loadTwinModel({ locations: [{ id: 'i', type: 'incoming', capacity: 0 }], equipment: [] })
62
- const at = Date.parse('2026-08-14T05:00:00.000Z')
63
- const sample = (whenMs: number, kW: number) => ({
64
- eventId: `e${whenMs}`,
65
- eventType: ENERGY_EVENT.measured,
66
- eventTime: new Date(whenMs).toISOString(),
67
- tenantId: 't',
68
- data: { meterId: 'm', kW, at: new Date(whenMs).toISOString() }
69
- })
70
-
71
- /*
72
- * 표본이 오기 전에는 **빈 목록**이다 — 그리고 그것이 맞다.
73
- *
74
- * 처음에는 `undefined`(모름)를 기대했다가 이 시험에 걸렸다. 판단해 보면 빈 목록이 옳다: 커널은
75
- * 관측 중이고, 수요 구간은 15분이 지나야 닫힌다. 「아직 하나도 마감되지 않았다」는 **참인 사실**이고
76
- * 개념 지도가 그것을 `none-yet` 으로 그린다.
77
- *
78
- * 「모른다」는 다른 경우다 — 커널이 멈춰 있어 상태 자체가 없을 때(위 시험). 그 둘을 섞지 않는 것이
79
- * 이 배선의 요점이다.
80
- */
81
- assert.deepEqual(axisValueOf(k.getSnapshot(), info), [], '관측 중인데 아직 마감이 없으면 빈 목록이다')
82
-
83
- k.apply(sample(at + 60_000, 100) as any)
84
- k.apply(sample(at + DEMAND_WINDOW_MS + 60_000, 120) as any) // 앞 구간 마감
85
- const windows = axisValueOf(k.getSnapshot(), info)
86
- assert.equal(Array.isArray(windows), true, '선언한 경로에 목록이 있어야 한다')
87
- assert.equal(windows.length, 1, '마감된 구간 하나')
88
- })
89
-
90
- /*
91
- * ── 규칙의 집이 하나인지 **기계가 지킨다** (2026-08-21) ──────────────────────
92
- *
93
- * 위 단위 시험은 처음부터 초록이었다. 함수는 옳았고, **호출부 하나가 그 함수를 지나지 않았다** —
94
- * `twin-model-item-query.ts` 가 `(snap as any)[axis]` 로 축 이름을 직접 읽었다. 그래서 개념 지도의
95
- * 카드는 수요 구간을 **42** 로 세는데 그 개념을 열면 **0건**이 나왔다. 사용자에게는 「트윈이 비어
96
- * 있다」로 읽힌다.
97
- *
98
- * 그 함정은 형제 파일 주석에 이미 적혀 있었다(「축 이름으로만 읽으면 중첩된 축이 언제나 관측 없음이
99
- * 된다 — 에너지의 수요 구간이 그렇다」). **적어 두는 것으로는 막히지 않았다.** 그래서 센다.
100
- */
101
- import { readFileSync, readdirSync } from 'node:fs'
102
- import { createRequire } from 'node:module'
103
- import { join } from 'node:path'
104
- import { fileURLToPath } from 'node:url'
105
-
106
- const SVC = fileURLToPath(new URL('../server/service/twin-model', import.meta.url))
107
-
108
- /**
109
- * 상태에서 축을 **이름으로** 읽는 모양 — `snap[axis]` · `(state as any)[axis]` 류.
110
- *
111
- * 앞에 `\b` 를 둔다: 처음에는 없어서 `const` 안의 `st` 를 잡아 `ROW_BACKED[axis]`(조회표이지 상태
112
- * 읽기가 아니다)를 위반으로 냈다. **거짓 빨강을 내는 가드는 없는 것보다 나쁘다** — 이름이 근거다.
113
- */
114
- const BY_NAME = /\(?\s*\b(?:snap|state)\b[^)\n]*\)?\s*\[\s*axis\s*\]/g
115
-
116
- test('축을 이름으로 읽는 자리가 없다 — 선언된 경로를 지나야 중첩된 축이 보인다', () => {
117
- const offenders: string[] = []
118
- for (const f of readdirSync(SVC).filter(n => n.endsWith('.ts'))) {
119
- const src = readFileSync(join(SVC, f), 'utf8')
120
- for (const line of src.split('\n')) {
121
- /* 주석은 규칙이 아니라 설명이다 — 함정을 적어 둔 줄이 스스로 위반으로 잡히면 신호가 묻힌다. */
122
- const code = line.replace(/\/\*.*?\*\//g, '').replace(/\/\/.*$/, '')
123
- if (code.trimStart().startsWith('*')) continue
124
- if (BY_NAME.test(code)) offenders.push(`${f}: ${line.trim()}`)
125
- BY_NAME.lastIndex = 0
126
- }
127
- }
128
- assert.deepEqual(
129
- offenders,
130
- [],
131
- `축을 이름으로 읽는 자리가 있다 — \`axisValueOf(state, 축선언)\` 을 쓰십시오.\n${offenders.join('\n')}`
132
- )
133
- })
134
-
135
- test('수요 구간은 실제로 중첩된 축이다 — 이 가드가 지키는 대상이 있다', () => {
136
- /* 가드가 지킬 것이 없으면 그 가드는 초록으로 거짓을 말한다. 대상이 실재하는지 함께 본다. */
137
- const req = createRequire(import.meta.url)
138
- const { TWIN_AXES } = req('@operato/ops-contract')
139
- const nested = (TWIN_AXES as any[]).filter(a => a.path)
140
-
141
- assert.ok(nested.length > 0, '경로를 가진 축이 하나도 없으면 이 가드는 의미가 없다')
142
- assert.ok(
143
- nested.some(a => a.axis === 'demandWindows'),
144
- `수요 구간이 경로 축이어야 한다 — 실제 경로 축: ${nested.map(a => a.axis).join(', ')}`
145
- )
146
- })
147
-
148
- /*
149
- * ── 무엇이 항목을 가리키는지도 **선언이 말한다** (2026-08-21) ─────────────────
150
- *
151
- * 개념 지도의 수요 구간을 열면 여섯 구간이 모두 「식별자 없음 — 참조할 수 없는 항목입니다」로 보였다.
152
- * 값(구간 시작·종료·최대수요)은 실재하는데 **가리킬 수 없다고 말한 것**이다.
153
- *
154
- * 원인은 축이 사는 자리를 짐작했던 것과 **같은 부류**다: 소비처가 `id`·`key`·`gtin` 을 순서대로
155
- * 짐작했고, 수요 구간에는 그 셋이 없다. 그것은 결함이 아니다 — 구간의 정체성은 **언제 시작했나**이고
156
- * 커널도 그것으로 키를 만든다(`contract-projected-over:<startMs>`).
157
- */
158
- test('id 가 없는 상태 축은 무엇이 항목을 가리키는지 선언한다', () => {
159
- const req2 = createRequire(import.meta.url)
160
- const { TWIN_AXES } = req2('@operato/ops-contract')
161
- const dw = (TWIN_AXES as any[]).find(a => a.axis === 'demandWindows')
162
-
163
- assert.ok(dw, '수요 구간 축이 있어야 한다')
164
- assert.equal(dw.idField, 'startMs', '구간의 정체성은 시작 시각이다')
165
- })
166
-
167
- test('항목 질의가 짐작하지 않는다 — 선언된 자리를 읽는다', () => {
168
- /*
169
- * 기계가 지킨다. 주석에 적어 두는 것으로는 막히지 않는 것을 오늘 이미 겪었다(축이 사는 자리를
170
- * 짐작한 자리 셋이 형제 파일 주석의 경고에도 남아 있었다).
171
- */
172
- /*
173
- * ── **파일 이름에 묶지 않는다** (2026-08-22) ────────────────────────────────
174
- * 예전에는 `twin-model-item-query.ts` 하나만 읽었다. 다른 레인이 `idOf` 를 `name-index.ts` 로
175
- * 옮기자(기능은 배열 차례까지 온전했다) 이 가드가 **거짓 빨강**을 냈다. 가드가 지켜야 하는 것은
176
- * 「그 파일에 그 낱말이 있나」가 아니라 「**이 서비스 어딘가에서 선언된 식별 자리를 읽나**」다.
177
- *
178
- * 그래서 서비스 폴더를 훑는다 — 옮겨도 따라간다. 옮길 자유를 막는 가드는 사람을 가드와 싸우게 만든다.
179
- */
180
- const sources = readdirSync(SVC, { recursive: true } as any)
181
- .filter((f: any) => String(f).endsWith('.ts'))
182
- .map((f: any) => readFileSync(join(SVC, String(f)), 'utf8'))
183
- const all = sources.join('\n')
184
-
185
- assert.match(all, /idField/, '선언된 식별 자리를 읽어야 한다(이 서비스 어딘가에서)')
186
- /* `x?.id === itemId` 로 찾던 자리가 남아 있으면 그 축의 항목은 열 수 없다. */
187
- assert.doesNotMatch(all, /find\(\(x: any\) => x\?\.id === itemId\)/, 'id 로만 찾는 자리가 남지 않았다')
188
- })
@@ -1,89 +0,0 @@
1
- /*
2
- * 지난 기록 채우기의 판단 — **무엇을 받고 무엇을 거절하나.**
3
- *
4
- * ── 왜 저널에만 쓰나 (2026-08-30) ──────────────────────────────────────────
5
- * 커널의 상태는 「지금」이다. 늦게 온 옛 표본은 가드가 막는다(그렇게 만들었다). 지난 기록을 라이브
6
- * 문으로 넣으면 가드에 막히거나 트윈의 시계가 과거로 끌린다. 그래서 이 길은 저널에만 쓰고,
7
- * **상태를 바꾸는 사실은 아예 받지 않는다.**
8
- *
9
- * ── 보존 안에서만 (사용자 결정) ────────────────────────────────────────────
10
- * 보존보다 오래된 것을 채우면 다음 정리에서 지워진다. 조용히 넣고 지워지면 아무도 모르므로
11
- * 거절하고 이유를 말한다.
12
- */
13
- import { test } from 'node:test'
14
- import assert from 'node:assert/strict'
15
-
16
- import { judgeBackfill, splitAlreadyThere, PERIOD_FACTS } from '../server/service/twin-backfill/backfill-shape.ts'
17
-
18
- const NOW = Date.parse('2026-08-30T00:00:00.000Z')
19
- const env = (eventType: string, eventTime: string, eventId = `${eventType}|${eventTime}`) => ({ eventType, eventTime, eventId })
20
-
21
- test('★ 마감된 구간 사실을 받는다', () => {
22
- const r = judgeBackfill([env('energy.usage.period', '2026-08-29T00:00:00.000Z')], { nowMs: NOW, retentionDays: 7 })
23
- assert.equal(r.keep.length, 1)
24
- assert.equal(r.refused.length, 0)
25
- })
26
-
27
- test('★ 상태를 바꾸는 사실은 받지 않는다 — 저널에만 넣으면 상태와 저널이 갈라진다', () => {
28
- const r = judgeBackfill(
29
- [env('energy.measured', '2026-08-29T00:00:00.000Z'), env('energy.generated', '2026-08-29T00:00:00.000Z')],
30
- { nowMs: NOW, retentionDays: 7 }
31
- )
32
- assert.equal(r.keep.length, 0)
33
- assert.equal(r.refused.length, 2)
34
- assert.match(r.refused[0].reason, /라이브 문으로 보내라/)
35
- })
36
-
37
- test('★ 보존보다 오래된 것은 거절하고 이유를 말한다 — 넣어도 지워진다', () => {
38
- const r = judgeBackfill([env('energy.usage.period', '2026-08-01T00:00:00.000Z')], { nowMs: NOW, retentionDays: 7 })
39
- assert.equal(r.keep.length, 0)
40
- assert.match(r.refused[0].reason, /보존 기간\(7일\)보다 오래됐다/)
41
- assert.match(r.refused[0].reason, /보존 기간을 늘려야 한다/, '무엇을 해야 하는지 함께 말한다')
42
- })
43
-
44
- test('보존 안쪽 경계는 받는다', () => {
45
- const justInside = new Date(NOW - 7 * 86_400_000 + 1000).toISOString()
46
- const r = judgeBackfill([env('energy.usage.period', justInside)], { nowMs: NOW, retentionDays: 7 })
47
- assert.equal(r.keep.length, 1)
48
- })
49
-
50
- test('보존 정책이 없으면 오래됨을 판정하지 않는다 — 없는 기준으로 거절하지 않는다', () => {
51
- const r = judgeBackfill([env('energy.usage.period', '2020-01-01T00:00:00.000Z')], { nowMs: NOW })
52
- assert.equal(r.keep.length, 1)
53
- })
54
-
55
- test('★ 아직 오지 않은 시각은 거절한다 — 지난 기록이 아니다', () => {
56
- const r = judgeBackfill([env('energy.usage.period', '2026-09-01T00:00:00.000Z')], { nowMs: NOW, retentionDays: 7 })
57
- assert.equal(r.keep.length, 0)
58
- assert.match(r.refused[0].reason, /아직 오지 않은 시각/)
59
- })
60
-
61
- test('시각을 읽을 수 없으면 거절한다', () => {
62
- const r = judgeBackfill([env('energy.usage.period', 'n/a')], { nowMs: NOW, retentionDays: 7 })
63
- assert.equal(r.keep.length, 0)
64
- assert.match(r.refused[0].reason, /읽을 수 없다/)
65
- })
66
-
67
- test('받는 종류 다섯이 모두 마감된 구간 사실이다', () => {
68
- for (const t of PERIOD_FACTS) {
69
- const r = judgeBackfill([env(t, '2026-08-29T00:00:00.000Z')], { nowMs: NOW, retentionDays: 7 })
70
- assert.equal(r.keep.length, 1, `${t} 를 받아야 한다`)
71
- }
72
- })
73
-
74
- /* ── 같은 기간을 두 번 넣지 않는다 ────────────────────────────────────────── */
75
-
76
- test('★ 이미 있는 것은 건너뛴다 — 다시 읽어도 중복이 쌓이지 않는다', () => {
77
- const a = env('energy.usage.period', '2026-08-29T00:00:00.000Z')
78
- const b = env('energy.usage.period', '2026-08-29T01:00:00.000Z')
79
- const r = splitAlreadyThere([a, b], new Set([a.eventId]))
80
- assert.equal(r.fresh.length, 1)
81
- assert.equal(r.fresh[0].eventId, b.eventId)
82
- assert.equal(r.skipped, 1)
83
- })
84
-
85
- test('id 가 없으면 넣지 않는다 — 같은 사실인지 판단할 근거가 없다', () => {
86
- const r = splitAlreadyThere([{ eventType: 'energy.usage.period', eventTime: '2026-08-29T00:00:00.000Z' }], new Set())
87
- assert.equal(r.fresh.length, 0)
88
- assert.equal(r.skipped, 1)
89
- })
@@ -1,287 +0,0 @@
1
- /*
2
- * 재기동을 넘어 **도는 트윈이 계속 돈다** — 그리고 되살리지 못하면 그렇게 말한다.
3
- *
4
- * ── 실측 (2026-08-14) ───────────────────────────────────────────────────────
5
- * 서버를 한 번 재기동하면 등록부는 `running` 이라 말하는데 **아무 커널도 돌지 않았다**.
6
- * `bootstrap()` 은 상태만 되찾아 `recovered` 에 담았고, 커널을 세우는 것은 명시 mutation 뿐이었다.
7
- * 미러 트윈에서는 계측까지 끊겼다 — `startReferenceLiveFeed` 를 부르는 곳이 `importTwinReference`
8
- * 하나뿐이어서, 붙어 있던 커넥터가 알리지 않고 떨어졌다(오류도 로그도 없이).
9
- *
10
- * 이 시험이 지키는 것 넷:
11
- * ① 부팅이 런타임까지 되살린다(상태 복구만으로는 도는 트윈이 아니다)
12
- * ② 모드를 지어내지 않는다 — 미러였던 것은 미러로(시뮬로 되살리면 없던 움직임을 만든다)
13
- * ③ 되살리지 못한 행은 `stopped` 로 적는다(우리가 고치려던 거짓말이 남지 않게)
14
- * ④ 부팅 훅이 **엔진 다음에** 피드를 다시 붙인다
15
- *
16
- * 엔진과 레퍼런스 계층은 DB·shell 을 물고 있어 단위 시험이 불러올 수 없다. 그래서 **배선의 모양**을
17
- * 소스로 지킨다 — 이 부류의 결함은 「부르는 곳이 없다」는 모양이고, 그건 소스에서 보인다.
18
- */
19
- import { test } from 'node:test'
20
- import assert from 'node:assert/strict'
21
- import { readFileSync } from 'node:fs'
22
- import { fileURLToPath } from 'node:url'
23
-
24
- const engine = readFileSync(fileURLToPath(new URL('../server/engine/twin-engine.ts', import.meta.url)), 'utf-8')
25
- const live = readFileSync(fileURLToPath(new URL('../server/service/reference/reference-live.ts', import.meta.url)), 'utf-8')
26
- const boot = readFileSync(fileURLToPath(new URL('../server/index.ts', import.meta.url)), 'utf-8')
27
-
28
- /** 그 정적 메서드의 본문만 — 다음 메서드 선언까지. */
29
- function methodOf(src: string, name: string): string {
30
- const at = [`static ${name}(`, `static async ${name}(`].map(sig => src.indexOf(sig)).filter(i => i >= 0).sort((a, b) => a - b)[0]
31
- assert.ok(at !== undefined, `${name} 가 없다 — 이름이 바뀌었으면 이 검사도 함께 고친다`)
32
- const ends = ['\n static ', '\n private static ', '\n /**'].map(m => src.indexOf(m, at + 10)).filter(i => i >= 0)
33
- const end = ends.sort((a, b) => a - b)[0]
34
- return src.slice(at, end === undefined ? undefined : end)
35
- }
36
-
37
- test('부팅이 런타임까지 되살린다 — 상태 복구만으로는 도는 트윈이 아니다', () => {
38
- const body = methodOf(engine, 'bootstrap')
39
- assert.match(body, /resumeRow\(row\)/, 'bootstrap 이 행마다 재개를 시도해야 한다')
40
- /* 예전 모양: 스냅샷을 `recovered` 에 담고 `continue` 로 끝났다 — 그것으로 되돌아가지 않게.
41
- 한 줄로 못 박지 않는다(2026-08-21 에 되살리기 사이 양보가 들어와 줄 모양이 바뀌었다) —
42
- **행 전부를 돌며 재개한다**는 사실만 본다. */
43
- assert.match(body, /for \(const row of rows\)[\s\S]{0,400}?await this\.resumeRow\(row\)/, '복구 뒤 재개 루프가 있어야 한다')
44
- /* 되살리기 사이에 루프를 비워 준다 — 없으면 부팅 내내 HTTP 가 서지 않는다. */
45
- assert.match(body, /BOOT_YIELD_MS/, '되살리기 사이에 양보가 있어야 한다')
46
- })
47
-
48
- test('모드를 지어내지 않는다 — 미러였던 것은 미러로 되살린다', () => {
49
- /*
50
- * ── 갈림은 **문 안**에 있다 (2026-08-20) ──────────────────────────────────
51
- * 예전에는 이 판단이 `resumeRow` 에 있었다. 그래서 부팅으로 살아난 미러는 관측 구동이었지만, 사람이
52
- * 화면에서 시작한 미러는 **시뮬로 떴다** — 그 문(`startFromRegistry`)에는 갈림이 없었기 때문이다.
53
- * 시뮬로 뜬 미러는 유입을 한 건도 받지 못하고(`ingestLive` 가 0 을 돌려준다), 화면에는 「running」이라
54
- * 적힌다. 같은 선언이 두 결과를 내지 않도록 갈림을 문 안으로 옮겼고, 이 시험이 그 자리를 지킨다.
55
- */
56
- const resumeRow = methodOf(engine, 'resumeRow')
57
- assert.match(resumeRow, /await this\.startFromRegistry\(domainId, instanceId\)/, '부팅도 같은 문을 지난다')
58
-
59
- const door = methodOf(engine, 'startFromRegistry')
60
- assert.match(door, /const policy = restartPolicy \?\? readRestartPolicy\(/, '선언된 값을 엄격히 읽는다')
61
- assert.match(door, /if \(policy === 'resync'\)/, '문이 선언된 모드를 보고 갈라야 한다')
62
- /*
63
- * 미러 가지 안을 그대로 떠서 본다 — 미러를 시뮬로 세우면 관측 트윈이 **스스로 물건을 옮긴다**
64
- * (없던 움직임을 만드는 것이다). 단어의 거리로 재면 로그·주석에 걸려 헛돈다.
65
- */
66
- const at = door.indexOf("if (policy === 'resync')")
67
- const mirrorBranch = door.slice(at, door.indexOf('\n }', at))
68
- assert.match(mirrorBranch, /return this\.startLive\(/, '미러 가지는 관측 구동으로 세우고 거기서 끝난다')
69
- assert.equal(mirrorBranch.includes('this.start('), false, '미러 가지에서 시뮬 기동을 부른다 — 없던 움직임을 만든다')
70
- /* 미러도 공간의 시각 기준을 받는다 — 교대의 HH:MM 을 어느 기준으로 읽나(부팅과 같은 규칙). */
71
- assert.match(mirrorBranch, /withSpaceTimeBase\(/)
72
- })
73
-
74
- test('reset 은 선언대로 저널을 비운다 — 씨앗 재현은 백지에서 시작한다', () => {
75
- /*
76
- * 「seed 재현」은 백지에서 다시 시작한다는 뜻인데, 예전에는 그 선언이 구현되지 않아 `reset` 트윈이
77
- * 사실상 이어졌다. 사실을 지우는 동작이라 순서를 지켜 켰다: 먼저 사람이 정책을 다시 선언할 문을 만들고
78
- * (`setTwinRestartPolicy`), 이력을 두고 볼 트윈을 `resume` 으로 옮긴 뒤에 켰다.
79
- */
80
- const door = methodOf(engine, 'startFromRegistry')
81
- const at = door.indexOf("if (policy === 'reset')")
82
- assert.ok(at > 0, '문이 reset 을 갈라야 한다')
83
- const branch = door.slice(at, door.indexOf('\n }', at))
84
- assert.match(branch, /resetJournal\(domainId, instanceId\)/, '저널을 비운다')
85
- /* 벤치 사본은 예외다 — 누군가 지켜보는 실험의 기록을 기동이 지우면 실험이 사라진다. */
86
- assert.match(branch, /purpose !== 'bench'/, '벤치 실험의 기록은 지우지 않는다')
87
- /* 웜스타트보다 **먼저** 지워야 한다 — 뒤에 지우면 이미 옛 상태를 읽어 실은 뒤다. */
88
- assert.ok(at < door.indexOf('this.recovered[key]'), '지우기가 웜스타트보다 앞에 있어야 한다')
89
- })
90
-
91
- test('저널만 지우면 씨앗 재현이 아니다 — 체크포인트·사슬도 무효로 만든다', () => {
92
- /*
93
- * 웜스타트는 체크포인트를 **먼저** 본다. 저널만 지우면 백지로 시작한 트윈에 옛 상태가 되살아나
94
- * 「저널은 비었는데 재고가 있는」 트윈이 된다. 시간여행이 딛는 사슬 캐시도 지워진 리비전을 가리킨다.
95
- */
96
- const body = methodOf(engine, 'resetJournal')
97
- assert.match(body, /SNAPSHOT_CACHE_ID[\s\S]{0,120}state: null/, '체크포인트를 「없음」으로 덮는다')
98
- assert.match(body, /CHAIN_INDEX_CACHE_ID[\s\S]{0,80}revisions: \[\]/, '사슬 색인도 비운다')
99
- /* 실패를 삼키지 않는다 — 무효화가 실패하면 다음 기동이 옛 상태를 물려받는다. */
100
- assert.match(body, /catch[\s\S]{0,120}twinWarn/)
101
- })
102
-
103
- test('정책을 다시 선언하는 문이 있다 — 없으면 선언과 쓰임이 영원히 어긋난다', () => {
104
- const src = readFileSync(
105
- fileURLToPath(new URL('../server/service/twin-lifecycle/twin-lifecycle-mutation.ts', import.meta.url)),
106
- 'utf-8'
107
- )
108
- assert.match(src, /async setTwinRestartPolicy\(/, '만든 뒤에 고칠 길이 있어야 한다')
109
- /* 값은 엄격히 읽는다 — 모르는 말을 기본값으로 메우면 그 트윈의 이력이 남을지가 알리지 않고 결정된다. */
110
- assert.match(src, /readRestartPolicy\(restartPolicy, /)
111
- /* 도는 런타임을 알리지 않고 갈아 끼우지 않는다 — 다르다는 사실을 답에 담는다. */
112
- assert.match(src, /appliesAtNextStart/)
113
- })
114
-
115
- test('되살리지 못한 행은 stopped 로 적는다 — 「도는 중」이라 말하는 채로 두지 않는다', () => {
116
- const resume = methodOf(engine, 'resumeRow')
117
- assert.match(resume, /catch[\s\S]{0,80}markStopped/, '실패를 삼키면 등록부가 계속 거짓을 말한다')
118
- const mark = methodOf(engine, 'markStopped')
119
- assert.match(mark, /status: 'stopped'/, '등록부에 실제로 적어야 한다')
120
- assert.match(mark, /twin(Warn|Error)/, '되살리지 못한 사실은 로그에도 남아야 한다')
121
- })
122
-
123
- test('벤치 사본은 무인으로 되살아나지 않는다 — 실험은 재기동을 넘기지 못한다', () => {
124
- const body = methodOf(engine, 'resumeRow')
125
- assert.match(body, /purpose === 'bench'/, '격리 사본을 갈라야 한다')
126
- assert.match(body, /bench[\s\S]{0,200}markStopped/, '되살리지 않았으면 등록부도 그렇게 적어야 한다')
127
- })
128
-
129
- test('피드 재개는 등록부가 「도는 미러」라고 말하는 것만 — 멈춘 것을 다시 켜지 않는다', () => {
130
- assert.match(live, /export async function resumeReferenceLiveFeeds/, '재개 함수가 있어야 한다')
131
- assert.match(live, /r\.status === 'running' && r\.restartPolicy === 'resync'/, '도는 미러만 골라야 한다')
132
- assert.match(live, /discoverSites/, '사이트 서술자는 다시 찾아서 얻는다(hint 를 남기지 않았다)')
133
- /* 붙지 않은 미러는 「도는데 아무것도 오지 않는」 트윈이다 — 알리지 않고 넘기지 않는다. */
134
- assert.match(live, /NOT reattached/, '못 붙인 것은 이유와 함께 남겨야 한다')
135
- })
136
-
137
- test('부팅 훅이 엔진 다음에 피드를 붙인다 — 순서가 뒤집히면 없는 커널에 계측을 붓는다', () => {
138
- assert.match(boot, /resumeReferenceLiveFeeds/, '부팅 훅이 재개를 불러야 한다(부르는 곳이 없으면 없는 기능이다)')
139
- const engineAt = boot.indexOf('TwinEngine.bootstrap()')
140
- const feedAt = boot.indexOf('resumeReferenceLiveFeeds()')
141
- assert.ok(engineAt >= 0 && feedAt > engineAt, '엔진 복구가 피드 재개보다 앞이어야 한다')
142
- })
143
-
144
- /*
145
- * ── 부팅 재개가 드러낸 것 (2026-08-14 실측) ─────────────────────────────────
146
- * 트윈 14개가 실제로 돌기 시작하자 호스트가 죽었다. 원인은 재개가 아니라 **브로드캐스팅 두 가지**였다:
147
- * ① 시뮬은 delta 마다 곧바로 전 상태를 투영해 브로드캐스팅했다 — 한 tick 이 사실을 수백 개 내면 그 반복이
148
- * 이벤트 루프를 35초 막았고(단계 합은 32ms 였다), 그 사이 구독자는 아무것도 빼내지 못했다.
149
- * ② 밀린 push 가 1024를 넘자 pubsub 이 오류를 냈고, 타이머 콜백을 타고 올라온 그 예외가 프로세스를 끝냈다.
150
- * 둘 다 재개 없이도 있던 결함이다 — 재개가 그것을 매일 아침 만나게 만들었을 뿐이다.
151
- */
152
- test('시뮬 브로드캐스팅도 모아서 한 번 — delta 마다 전 상태를 투영하지 않는다', () => {
153
- const at = engine.indexOf("if (msg.kind === 'delta')")
154
- assert.ok(at >= 0, 'sim delta 처리 자리가 없다')
155
- const body = engine.slice(at, at + 2600)
156
- assert.match(body, /inst\.dirty = true/, 'dirty 로 표시하고')
157
- assert.match(body, /ensureBroadcastCoalescer\(\)/, '주기 flush 에 맡겨야 한다')
158
- assert.equal(/this\.publishEntityData\(inst\)/.test(body), false, 'delta 마다 곧바로 브로드캐스팅하는 모양으로 되돌아갔다')
159
- })
160
-
161
- test('flush 가 시뮬도 본다 — 브로드캐스팅과 저널은 둘 다, state 채널은 라이브만', () => {
162
- /*
163
- * 창의 본문은 `flushBroadcastWindow` 로 옮겼다(2026-08-27). `flushLiveBroadcasts` 는 겹쳐 도는 것을
164
- * 막는 문만 남았다 — 발행이 청크로 나뉘어 한 창이 주기를 넘길 수 있게 됐기 때문이다.
165
- */
166
- const body = methodOf(engine, 'flushBroadcastWindow')
167
- assert.equal(/mode !== 'live'\) continue/.test(body), false, '시뮬을 건너뛰는 모양으로 되돌아갔다')
168
- assert.match(body, /const isLive = inst\.mode === 'live'/, '모드는 가르되 둘 다 브로드캐스팅해야 한다')
169
- /*
170
- * **저널은 두 구동이 같은 문을 쓴다** (2026-08-20, ingest-scale §7.1). 예전에는 시뮬이 델타마다 단건
171
- * 저장을 했고 그것이 규모에서 호스트를 먹었다(CPU 100%·질의 8~14초). 그래서 flush 가 시뮬의 저널도
172
- * 흘린다 — 그 호출이 `if (!isLive) continue` **앞**에 있어야 시뮬 몫이 빠지지 않는다.
173
- */
174
- const flushAt = body.indexOf('this.flushJournal(inst)')
175
- const liveOnlyAt = body.indexOf('if (!isLive) continue')
176
- assert.ok(flushAt >= 0, '저널을 흘리는 자리가 없다')
177
- assert.ok(liveOnlyAt >= 0, 'state 채널을 라이브로 가르는 자리가 없다')
178
- assert.ok(flushAt < liveOnlyAt, '저널 flush 가 라이브 전용 구간 뒤로 밀리면 시뮬의 사실이 쌓인 채 남는다')
179
- /* state 채널은 시뮬이 자기 콜백에서 이미 보낸다 — 여기서 또 보내면 같은 신호가 두 번 간다. */
180
- /* The payload is built by the channel since 2026-09-24 (it carries the domain); look for that call. */
181
- const stateSendAt = body.indexOf('twinStatePayload(inst.domainId, { instanceId: inst.id')
182
- assert.ok(stateSendAt >= 0, 'state 채널로 보내는 자리가 없다')
183
- assert.ok(stateSendAt > liveOnlyAt, 'state 채널 브로드캐스팅은 라이브 구간에 있어야 한다')
184
- })
185
-
186
- test('구독자 하나가 호스트를 죽이지 못한다 — 그리고 못 보낸 것을 보낸 것으로 적지 않는다', () => {
187
- /* 직접 브로드캐스팅은 **가드 안에 하나**여야 한다 — 그 밖의 호출은 예외가 그대로 올라가는 길이다. */
188
- const guardBody = methodOf(engine, 'publishGuarded')
189
- assert.equal((engine.match(/pubsub\.publish\(/g) ?? []).length, 1, '가드를 거치지 않는 직접 브로드캐스팅이 남아 있다')
190
- assert.match(guardBody, /pubsub\.publish\(/, '그 하나는 가드 안에 있어야 한다')
191
- /*
192
- * ── 이 가드가 무엇을 못 하는지 (2026-08-27 측정으로 확인) ───────────────────
193
- * 이 try/catch 는 **대기열 넘침을 막지 못한다.** 그 예외는 `EventTarget` 이 부른 리스너 안에서 나고
194
- * 런타임은 그것을 호출자에게 주지 않는다(uncaughtException 으로 간다). 그것을 막는 것은 한 차례에
195
- * 발행하는 수를 상한 아래로 두는 것이고, 그 규율은 `broadcast-queue-overflow.test.ts` 가 지킨다.
196
- *
197
- * 그래도 남긴다: 직렬화 실패처럼 `publish` 가 직접 던지는 경우를 잡고, 못 보낸 태그의 시그니처를
198
- * 되돌린다(남겨 두면 그 태그가 오류 없이 영원히 낡는다).
199
- */
200
- assert.match(guardBody, /catch/, 'publish 가 직접 던지는 경우는 잡아야 한다')
201
- assert.match(guardBody, /twinWarn/, '못 보낸 사실은 남아야 한다(삼키면 「보냈는데 낡았다」가 된다)')
202
- /* 시그니처를 남겨 두면 다음 주기에 「무변화」로 건너뛰고 그 태그는 영원히 낡는다. */
203
- assert.match(engine, /if \(!this\.publishGuarded\('data'[\s\S]{0,80}sigs\.delete\(tag\)/, '실패한 태그의 시그니처를 되돌려야 한다')
204
- })
205
-
206
- /*
207
- * ── 굶김 안전망 (2026-08-14) ─────────────────────────────────────────────────
208
- * 시뮬 틱은 메인 이벤트 루프에서 돈다 — 한 트윈의 틱이 길어지면 그 시간만큼 호스트 전체가 멈춘다
209
- * (실측 34.9초). 브로드캐스팅 반복은 걷어냈지만 커널 틱 자체는 여전히 메인 루프에 있고, 근본 해결(분산)은
210
- * 이연됐다. 그 사이의 안전망이 이것이다: **호스트를 굶기는 트윈은 멈춘다.**
211
- */
212
- test('굶김은 예산과 다른 문턱으로 판정한다 — 조금 느린 트윈을 내리지 않는다', () => {
213
- const body = methodOf(engine, 'guardStarvation')
214
- assert.match(body, /this\.TICK_MS \* this\.STARVE_FACTOR/, '굶김 문턱은 틱 간격의 배수다')
215
- /* 예산(500ms)으로 내리면 계기판이 말해야 할 「조금 느림」에 트윈이 죽는다. */
216
- assert.equal(/budgetMs/.test(body), false, '예산으로 내리는 모양 — 경고와 정지를 섞지 않는다')
217
- assert.ok(/STARVE_FACTOR = \d+/.test(engine) && /STARVE_STREAK = \d+/.test(engine), '문턱과 연속 횟수가 상수로 서 있어야 한다')
218
- })
219
-
220
- test('한 번으로 내리지 않고, 한 번 내려오면 연속이 끊긴다', () => {
221
- const body = methodOf(engine, 'guardStarvation')
222
- assert.match(body, /streak < this\.STARVE_STREAK\) return/, '연속이 차기 전에는 멈추지 않는다')
223
- /* 웜스타트 직후 첫 틱은 원래 무겁다(복구한 상태를 처음 계산한다) — 그것으로 내리면 재개가 무의미해진다. */
224
- assert.match(body, /this\.starveStreak\.delete\(key\)[\s\S]{0,40}return/, '문턱 아래면 연속을 끊어야 한다')
225
- })
226
-
227
- test('멈추는 길은 하나 — 타이머만 끄면 등록부가 계속 「도는 중」이라 말한다', () => {
228
- const guard = methodOf(engine, 'stopWithNote')
229
- assert.match(guard, /this\.stop\(domainId, id\)/, '공용 stop 을 써야 한다(스냅샷·피드 정리·등록부가 그 안에 있다)')
230
- /* 예전 tick 실패 경로는 `clearInterval` 만 했다 — 그 모양으로 되돌아가지 않게. */
231
- const tick = methodOf(engine, 'tickGuarded')
232
- assert.equal(/clearInterval\(inst\.timer\)/.test(tick), false, '타이머만 끄는 모양으로 되돌아갔다')
233
- assert.match(tick, /stopWithNote\(/, '실패도 이유와 함께 멈춰야 한다')
234
- })
235
-
236
- test('왜 멈췄는지 코드로 남고, 다시 세우면 지운다', () => {
237
- assert.match(engine, /export interface StopNote/, '이유는 문장이 아니라 코드다(화면이 옮긴다)')
238
- assert.match(engine, /code: 'starved', params: \{ streak/, '굶김은 근거 수치와 함께')
239
- assert.match(engine, /code: 'tick-failed', params: \{ reason/, '틱 실패는 원문 그대로(옮길 어휘가 아니다)')
240
- assert.match(methodOf(engine, 'list'), /stopNote: this\.stopNotes\.get\(/, '목록이 그 사실을 함께 내야 한다')
241
- /* 남겨 두면 다시 돌고 있는 트윈이 「굶겨서 멈췄다」고 말한다. */
242
- assert.match(engine, /this\.stopNotes\.delete\(key\)/, '다시 세울 때 지워야 한다')
243
- })
244
-
245
- /*
246
- * ── 같은 현장 이웃 설비 상태 전달 (2026-08-14) ──────────────────────────────
247
- * 시뮬 에너지 트윈은 계측이 없으므로 스스로 부하를 만든다. 그 재료 하나가 「무엇이 돌고 있나」인데,
248
- * 그것은 같은 현장 공장 트윈이 아는 사실이다. 호스트는 **누가 이웃인지**만 알려 주고, 무엇을 읽어
249
- * 어떻게 부하로 바꿀지는 커널이 정한다 — 호스트가 kW 를 계산해 주면 규칙이 어디에도 안 적힌 채
250
- * 트윈마다 갈라진다.
251
- */
252
- test('이웃 상태는 커널이 요청할 때만, 같은 현장에서만 전달한다', () => {
253
- const body = methodOf(engine, 'feedPeerEquipment')
254
- assert.match(body, /observePeerEquipment !== 'function'\) return/, '이웃을 읽지 않는 커널에는 넘기지 않는다')
255
- assert.match(body, /other\.spaceId !== inst\.spaceId\) continue/, '같은 현장만 이웃이다')
256
- assert.match(body, /other === inst/, '자기 자신은 이웃이 아니다')
257
- /* 상태만 넘긴다 — 스냅샷을 통째로 넘기면 매 tick 상태 크기에 비례한 비용을 치른다. */
258
- assert.equal(/snapshot\(\)/.test(body), false, '스냅샷을 넘기는 모양으로 되돌아갔다')
259
- assert.match(body, /status: String/, '상태만 실어 보낸다')
260
- })
261
-
262
- test('현장이 없으면 이웃 목록을 비운다 — 옛 상태가 남아 부하를 만들지 않게', () => {
263
- const body = methodOf(engine, 'feedPeerEquipment')
264
- assert.match(body, /!inst\.spaceId[\s\S]{0,120}observePeerEquipment\(\[\]\)/, '빈 목록으로 덮어야 한다')
265
- })
266
-
267
- test('tick 이 이웃 상태를 먼저 싣는다 — 커널이 그것을 읽어 부하를 만든다', () => {
268
- const tick = methodOf(engine, 'tickGuarded')
269
- const feedAt = tick.indexOf('feedPeerEquipment')
270
- const tickAt = tick.indexOf('runtime.tick(')
271
- assert.ok(feedAt >= 0 && tickAt > feedAt, '이웃 상태를 실은 뒤에 tick 해야 한다')
272
- })
273
-
274
- /*
275
- * ── 시뮬 트윈의 시각축 (2026-08-15) ─────────────────────────────────────────
276
- * 커널의 기본 기준점은 상수(2026-01-01)다. 호스트가 그것을 실제 시각으로 옮기지 않으면 시뮬 트윈의
277
- * 저널 시각이 **재기동마다 되감긴다** — 며칠 치 사건이 전부 같은 분에 몰리고, 실 시각으로 창을 자르는
278
- * 성과·이력 질의에는 그 트윈이 보이지 않는다. 수요 구간(15분)도 가짜 시각 위에 서게 된다.
279
- */
280
- test('시뮬 기동이 커널의 시각 기준점을 지금으로 맞춘다', () => {
281
- const body = methodOf(engine, 'start')
282
- assert.match(body, /setClockOrigin\?\.\(Date\.now\(\)\)/, '기동 순간의 실제 시각을 줘야 한다')
283
- /* 커널을 세운 **직후**여야 한다 — 시계가 흐른 뒤에 옮기면 그전 사실과 시간축이 어긋난다. */
284
- const at = body.indexOf('setClockOrigin')
285
- const runtimeAt = body.indexOf('new TwinRuntime(')
286
- assert.ok(at >= 0 && runtimeAt > at, '런타임을 세우기 전에 기준점을 맞춘다')
287
- })