@things-factory/headless-twin 10.0.14 → 10.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. package/dist-server/engine/attention-digest.d.ts +4 -4
  2. package/dist-server/engine/attention-digest.js +7 -7
  3. package/dist-server/engine/attention-digest.js.map +1 -1
  4. package/dist-server/engine/canonical-ingest.d.ts +11 -2
  5. package/dist-server/engine/canonical-ingest.js +137 -8
  6. package/dist-server/engine/canonical-ingest.js.map +1 -1
  7. package/dist-server/engine/command-routing.js +1 -1
  8. package/dist-server/engine/command-routing.js.map +1 -1
  9. package/dist-server/engine/declared-stimulus.js +1 -1
  10. package/dist-server/engine/declared-stimulus.js.map +1 -1
  11. package/dist-server/engine/energy-topology.js +1 -1
  12. package/dist-server/engine/energy-topology.js.map +1 -1
  13. package/dist-server/engine/ingest-dedupe.d.ts +38 -0
  14. package/dist-server/engine/ingest-dedupe.js +145 -0
  15. package/dist-server/engine/ingest-dedupe.js.map +1 -0
  16. package/dist-server/engine/ingest-health.d.ts +124 -9
  17. package/dist-server/engine/ingest-health.js +169 -15
  18. package/dist-server/engine/ingest-health.js.map +1 -1
  19. package/dist-server/engine/integration-probes.d.ts +3 -3
  20. package/dist-server/engine/integration-probes.js +6 -6
  21. package/dist-server/engine/integration-probes.js.map +1 -1
  22. package/dist-server/engine/kpi-baseline.js +4 -4
  23. package/dist-server/engine/kpi-baseline.js.map +1 -1
  24. package/dist-server/engine/kpi-fold.d.ts +122 -12
  25. package/dist-server/engine/kpi-fold.js +247 -18
  26. package/dist-server/engine/kpi-fold.js.map +1 -1
  27. package/dist-server/engine/kpi-query.d.ts +11 -11
  28. package/dist-server/engine/kpi-query.js +185 -28
  29. package/dist-server/engine/kpi-query.js.map +1 -1
  30. package/dist-server/engine/live-feed-registry.d.ts +1 -1
  31. package/dist-server/engine/live-feed-registry.js +1 -1
  32. package/dist-server/engine/live-feed-registry.js.map +1 -1
  33. package/dist-server/engine/load-meter.d.ts +1 -1
  34. package/dist-server/engine/load-meter.js.map +1 -1
  35. package/dist-server/engine/local-declarations.d.ts +7 -7
  36. package/dist-server/engine/local-declarations.js +9 -9
  37. package/dist-server/engine/local-declarations.js.map +1 -1
  38. package/dist-server/engine/log.d.ts +1 -1
  39. package/dist-server/engine/log.js +1 -1
  40. package/dist-server/engine/log.js.map +1 -1
  41. package/dist-server/engine/measured-estimator.js +1 -1
  42. package/dist-server/engine/measured-estimator.js.map +1 -1
  43. package/dist-server/engine/measured-yield.js +1 -1
  44. package/dist-server/engine/measured-yield.js.map +1 -1
  45. package/dist-server/engine/model-basis.d.ts +1 -1
  46. package/dist-server/engine/model-basis.js +1 -1
  47. package/dist-server/engine/model-basis.js.map +1 -1
  48. package/dist-server/engine/model-gap.d.ts +3 -3
  49. package/dist-server/engine/model-gap.js +1 -1
  50. package/dist-server/engine/model-gap.js.map +1 -1
  51. package/dist-server/engine/model-vocabulary.js +1 -1
  52. package/dist-server/engine/model-vocabulary.js.map +1 -1
  53. package/dist-server/engine/oee-accumulator.d.ts +4 -4
  54. package/dist-server/engine/oee-accumulator.js +3 -3
  55. package/dist-server/engine/oee-accumulator.js.map +1 -1
  56. package/dist-server/engine/property-effects.js +2 -2
  57. package/dist-server/engine/property-effects.js.map +1 -1
  58. package/dist-server/engine/read-time.d.ts +22 -0
  59. package/dist-server/engine/read-time.js +81 -0
  60. package/dist-server/engine/read-time.js.map +1 -0
  61. package/dist-server/engine/restart-policy.js +1 -1
  62. package/dist-server/engine/restart-policy.js.map +1 -1
  63. package/dist-server/engine/runtime-key.js +1 -1
  64. package/dist-server/engine/runtime-key.js.map +1 -1
  65. package/dist-server/engine/spec-coverage.d.ts +2 -2
  66. package/dist-server/engine/spec-coverage.js +1 -1
  67. package/dist-server/engine/spec-coverage.js.map +1 -1
  68. package/dist-server/engine/stage-path.d.ts +4 -4
  69. package/dist-server/engine/stage-path.js +1 -1
  70. package/dist-server/engine/stage-path.js.map +1 -1
  71. package/dist-server/engine/state-axes.d.ts +1 -1
  72. package/dist-server/engine/state-axes.js +2 -2
  73. package/dist-server/engine/state-axes.js.map +1 -1
  74. package/dist-server/engine/travel-estimator.d.ts +2 -2
  75. package/dist-server/engine/travel-estimator.js +1 -1
  76. package/dist-server/engine/travel-estimator.js.map +1 -1
  77. package/dist-server/engine/twin-engine.d.ts +243 -53
  78. package/dist-server/engine/twin-engine.js +690 -191
  79. package/dist-server/engine/twin-engine.js.map +1 -1
  80. package/dist-server/engine/warm-start.d.ts +2 -2
  81. package/dist-server/engine/warm-start.js +5 -5
  82. package/dist-server/engine/warm-start.js.map +1 -1
  83. package/dist-server/index.js +1 -1
  84. package/dist-server/index.js.map +1 -1
  85. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js +1 -1
  86. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +1 -1
  87. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js +3 -3
  88. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +1 -1
  89. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js +2 -2
  90. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +1 -1
  91. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.d.ts +5 -0
  92. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js +54 -0
  93. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +1 -0
  94. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.d.ts +5 -0
  95. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js +44 -0
  96. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +1 -0
  97. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.d.ts +6 -0
  98. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js +84 -0
  99. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +1 -0
  100. package/dist-server/migrations/index.js +7 -1
  101. package/dist-server/migrations/index.js.map +1 -1
  102. package/dist-server/routes.d.ts +1 -0
  103. package/dist-server/routes.js +69 -5
  104. package/dist-server/routes.js.map +1 -1
  105. package/dist-server/service/index.d.ts +2 -2
  106. package/dist-server/service/index.js +27 -24
  107. package/dist-server/service/index.js.map +1 -1
  108. package/dist-server/service/reference/control-routing.d.ts +1 -1
  109. package/dist-server/service/reference/control-routing.js +1 -1
  110. package/dist-server/service/reference/control-routing.js.map +1 -1
  111. package/dist-server/service/reference/discovery-result.d.ts +2 -2
  112. package/dist-server/service/reference/discovery-result.js +3 -3
  113. package/dist-server/service/reference/discovery-result.js.map +1 -1
  114. package/dist-server/service/reference/hook-contract.d.ts +43 -0
  115. package/dist-server/service/reference/hook-contract.js +59 -0
  116. package/dist-server/service/reference/hook-contract.js.map +1 -0
  117. package/dist-server/service/reference/ingest-space.d.ts +1 -1
  118. package/dist-server/service/reference/ingest-space.js +4 -4
  119. package/dist-server/service/reference/ingest-space.js.map +1 -1
  120. package/dist-server/service/reference/knob-defaults.d.ts +1 -1
  121. package/dist-server/service/reference/knob-defaults.js +4 -4
  122. package/dist-server/service/reference/knob-defaults.js.map +1 -1
  123. package/dist-server/service/reference/reference-adapter.d.ts +140 -8
  124. package/dist-server/service/reference/reference-adapter.js +27 -0
  125. package/dist-server/service/reference/reference-adapter.js.map +1 -1
  126. package/dist-server/service/reference/reference-hook.d.ts +20 -0
  127. package/dist-server/service/reference/reference-hook.js +133 -0
  128. package/dist-server/service/reference/reference-hook.js.map +1 -0
  129. package/dist-server/service/reference/reference-live.d.ts +2 -2
  130. package/dist-server/service/reference/reference-live.js +88 -13
  131. package/dist-server/service/reference/reference-live.js.map +1 -1
  132. package/dist-server/service/reference/reference-master.d.ts +14 -14
  133. package/dist-server/service/reference/reference-master.js +34 -12
  134. package/dist-server/service/reference/reference-master.js.map +1 -1
  135. package/dist-server/service/reference/reference-resolver.d.ts +1 -1
  136. package/dist-server/service/reference/reference-resolver.js +10 -10
  137. package/dist-server/service/reference/reference-resolver.js.map +1 -1
  138. package/dist-server/service/reference/twin-reference.d.ts +3 -3
  139. package/dist-server/service/reference/twin-reference.js.map +1 -1
  140. package/dist-server/service/twin-attention/twin-attention-query.d.ts +1 -1
  141. package/dist-server/service/twin-attention/twin-attention-query.js +1 -1
  142. package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
  143. package/dist-server/service/twin-audit/command-audit.d.ts +1 -1
  144. package/dist-server/service/twin-audit/command-audit.js.map +1 -1
  145. package/dist-server/service/twin-audit/twin-audit-event.d.ts +1 -1
  146. package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -1
  147. package/dist-server/service/twin-audit/twin-audit-query.js +1 -1
  148. package/dist-server/service/twin-audit/twin-audit-query.js.map +1 -1
  149. package/dist-server/service/twin-control/twin-control-mutation.js +1 -1
  150. package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
  151. package/dist-server/service/twin-event/twin-event-keys.d.ts +22 -2
  152. package/dist-server/service/twin-event/twin-event-keys.js +37 -5
  153. package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
  154. package/dist-server/service/twin-event/twin-event-type.d.ts +1 -0
  155. package/dist-server/service/twin-event/twin-event-type.js +8 -1
  156. package/dist-server/service/twin-event/twin-event-type.js.map +1 -1
  157. package/dist-server/service/twin-event/twin-event.d.ts +2 -2
  158. package/dist-server/service/twin-event/twin-event.js +32 -4
  159. package/dist-server/service/twin-event/twin-event.js.map +1 -1
  160. package/dist-server/service/twin-forecast/forecast-metrics.js +1 -1
  161. package/dist-server/service/twin-forecast/forecast-metrics.js.map +1 -1
  162. package/dist-server/service/twin-forecast/gap-analytics.d.ts +1 -1
  163. package/dist-server/service/twin-forecast/gap-analytics.js.map +1 -1
  164. package/dist-server/service/twin-forecast/twin-forecast-query.js +29 -11
  165. package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
  166. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.d.ts +1 -1
  167. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js +1 -1
  168. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +1 -1
  169. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js +1 -1
  170. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +1 -1
  171. package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
  172. package/dist-server/service/twin-journal/event-type-split-shape.d.ts +50 -0
  173. package/dist-server/service/twin-journal/event-type-split-shape.js +111 -0
  174. package/dist-server/service/twin-journal/event-type-split-shape.js.map +1 -0
  175. package/dist-server/service/twin-journal/event-type-split.d.ts +15 -0
  176. package/dist-server/service/twin-journal/event-type-split.js +102 -0
  177. package/dist-server/service/twin-journal/event-type-split.js.map +1 -0
  178. package/dist-server/service/twin-journal/journal-order.d.ts +15 -0
  179. package/dist-server/service/twin-journal/journal-order.js +17 -0
  180. package/dist-server/service/twin-journal/journal-order.js.map +1 -0
  181. package/dist-server/service/twin-journal/twin-journal-query.d.ts +5 -5
  182. package/dist-server/service/twin-journal/twin-journal-query.js +232 -21
  183. package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
  184. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +27 -1
  185. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +59 -3
  186. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
  187. package/dist-server/service/twin-metrics/twin-metrics-query.js +14 -1
  188. package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
  189. package/dist-server/service/twin-model/axis-journal-evidence.js +1 -1
  190. package/dist-server/service/twin-model/axis-journal-evidence.js.map +1 -1
  191. package/dist-server/service/twin-model/epcis-coverage.js +36 -1
  192. package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
  193. package/dist-server/service/twin-model/iec61850-coverage.js +1 -1
  194. package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -1
  195. package/dist-server/service/twin-model/isa95-coverage.d.ts +15 -2
  196. package/dist-server/service/twin-model/isa95-coverage.js +32 -8
  197. package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
  198. package/dist-server/service/twin-model/item-ref.js +2 -2
  199. package/dist-server/service/twin-model/item-ref.js.map +1 -1
  200. package/dist-server/service/twin-model/name-index.d.ts +1 -1
  201. package/dist-server/service/twin-model/name-index.js +1 -1
  202. package/dist-server/service/twin-model/name-index.js.map +1 -1
  203. package/dist-server/service/twin-model/project-structure.d.ts +1 -1
  204. package/dist-server/service/twin-model/project-structure.js +3 -3
  205. package/dist-server/service/twin-model/project-structure.js.map +1 -1
  206. package/dist-server/service/twin-model/twin-equipment.d.ts +1 -1
  207. package/dist-server/service/twin-model/twin-equipment.js +1 -1
  208. package/dist-server/service/twin-model/twin-equipment.js.map +1 -1
  209. package/dist-server/service/twin-model/twin-lineage-query.js +16 -5
  210. package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
  211. package/dist-server/service/twin-model/twin-location.d.ts +1 -1
  212. package/dist-server/service/twin-model/twin-location.js.map +1 -1
  213. package/dist-server/service/twin-model/twin-model-item-query.d.ts +3 -3
  214. package/dist-server/service/twin-model/twin-model-item-query.js +14 -14
  215. package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
  216. package/dist-server/service/twin-model/twin-model-mutation.d.ts +2 -2
  217. package/dist-server/service/twin-model/twin-model-mutation.js +4 -4
  218. package/dist-server/service/twin-model/twin-model-mutation.js.map +1 -1
  219. package/dist-server/service/twin-model/twin-model-query.js +117 -28
  220. package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
  221. package/dist-server/service/twin-model/twin-model-tree-query.js +22 -0
  222. package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
  223. package/dist-server/service/twin-model/twin-operation.d.ts +1 -1
  224. package/dist-server/service/twin-model/twin-operation.js +1 -1
  225. package/dist-server/service/twin-model/twin-operation.js.map +1 -1
  226. package/dist-server/service/twin-space/move-space.js +2 -2
  227. package/dist-server/service/twin-space/move-space.js.map +1 -1
  228. package/dist-server/service/twin-space/representation-role.d.ts +2 -0
  229. package/dist-server/service/twin-space/representation-role.js +18 -0
  230. package/dist-server/service/twin-space/representation-role.js.map +1 -0
  231. package/dist-server/service/twin-space/space-integrity.d.ts +1 -1
  232. package/dist-server/service/twin-space/space-integrity.js.map +1 -1
  233. package/dist-server/service/twin-space/twin-space-representation.d.ts +2 -0
  234. package/dist-server/service/twin-space/twin-space-representation.js +16 -0
  235. package/dist-server/service/twin-space/twin-space-representation.js.map +1 -1
  236. package/dist-server/service/twin-space/twin-space-resolver.d.ts +2 -2
  237. package/dist-server/service/twin-space/twin-space-resolver.js +36 -7
  238. package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
  239. package/dist-server/service/twin-structure/twin-structure.js +3 -3
  240. package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
  241. package/dist-server/service/twin-subject/event-subjects.d.ts +43 -0
  242. package/dist-server/service/twin-subject/event-subjects.js +137 -0
  243. package/dist-server/service/twin-subject/event-subjects.js.map +1 -0
  244. package/dist-server/service/twin-subject/index.d.ts +9 -0
  245. package/dist-server/service/twin-subject/index.js +16 -0
  246. package/dist-server/service/twin-subject/index.js.map +1 -0
  247. package/dist-server/service/twin-subject/item-resolver.d.ts +10 -0
  248. package/dist-server/service/twin-subject/item-resolver.js +93 -0
  249. package/dist-server/service/twin-subject/item-resolver.js.map +1 -0
  250. package/dist-server/service/twin-subject/subject-query-shape.d.ts +22 -0
  251. package/dist-server/service/twin-subject/subject-query-shape.js +88 -0
  252. package/dist-server/service/twin-subject/subject-query-shape.js.map +1 -0
  253. package/dist-server/service/twin-subject/twin-subject-backfill.d.ts +23 -0
  254. package/dist-server/service/twin-subject/twin-subject-backfill.js +159 -0
  255. package/dist-server/service/twin-subject/twin-subject-backfill.js.map +1 -0
  256. package/dist-server/service/twin-subject/twin-subject-event.d.ts +14 -0
  257. package/dist-server/service/twin-subject/twin-subject-event.js +111 -0
  258. package/dist-server/service/twin-subject/twin-subject-event.js.map +1 -0
  259. package/dist-server/service/twin-subject/twin-subject-fast-path.d.ts +39 -0
  260. package/dist-server/service/twin-subject/twin-subject-fast-path.js +295 -0
  261. package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +1 -0
  262. package/dist-server/service/twin-subject/twin-subject-query.d.ts +27 -0
  263. package/dist-server/service/twin-subject/twin-subject-query.js +232 -0
  264. package/dist-server/service/twin-subject/twin-subject-query.js.map +1 -0
  265. package/dist-server/service/twin-subject/twin-subject-writer.d.ts +27 -0
  266. package/dist-server/service/twin-subject/twin-subject-writer.js +166 -0
  267. package/dist-server/service/twin-subject/twin-subject-writer.js.map +1 -0
  268. package/dist-server/service/twin-target/twin-target-resolver.js +1 -1
  269. package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -1
  270. package/dist-shared/entity-delta.d.ts +1 -1
  271. package/dist-shared/entity-delta.js +3 -3
  272. package/dist-shared/entity-delta.js.map +1 -1
  273. package/dist-shared/touched-items.js +2 -2
  274. package/dist-shared/touched-items.js.map +1 -1
  275. package/dist-shared/twin-level.js +1 -1
  276. package/dist-shared/twin-level.js.map +1 -1
  277. package/package.json +3 -3
  278. package/server/engine/attention-digest.ts +9 -9
  279. package/server/engine/canonical-ingest.ts +170 -13
  280. package/server/engine/command-routing.ts +1 -1
  281. package/server/engine/declared-stimulus.ts +1 -1
  282. package/server/engine/energy-topology.ts +1 -1
  283. package/server/engine/ingest-dedupe.ts +149 -0
  284. package/server/engine/ingest-health.ts +234 -20
  285. package/server/engine/integration-probes.ts +7 -7
  286. package/server/engine/kpi-baseline.ts +4 -4
  287. package/server/engine/kpi-fold.ts +395 -27
  288. package/server/engine/kpi-query.ts +212 -46
  289. package/server/engine/live-feed-registry.ts +1 -1
  290. package/server/engine/load-meter.ts +1 -1
  291. package/server/engine/local-declarations.ts +13 -13
  292. package/server/engine/log.ts +1 -1
  293. package/server/engine/measured-estimator.ts +1 -1
  294. package/server/engine/measured-yield.ts +1 -1
  295. package/server/engine/model-basis.ts +2 -2
  296. package/server/engine/model-gap.ts +3 -3
  297. package/server/engine/model-vocabulary.ts +1 -1
  298. package/server/engine/oee-accumulator.ts +4 -4
  299. package/server/engine/property-effects.ts +2 -2
  300. package/server/engine/read-time.ts +77 -0
  301. package/server/engine/restart-policy.ts +1 -1
  302. package/server/engine/runtime-key.ts +1 -1
  303. package/server/engine/spec-coverage.ts +3 -3
  304. package/server/engine/stage-path.ts +5 -5
  305. package/server/engine/state-axes.ts +2 -2
  306. package/server/engine/travel-estimator.ts +2 -2
  307. package/server/engine/twin-engine.ts +764 -201
  308. package/server/engine/warm-start.ts +6 -6
  309. package/server/index.ts +1 -1
  310. package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +1 -1
  311. package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +3 -3
  312. package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +2 -2
  313. package/server/migrations/1786300000000-IndexEventTypeByTime.ts +48 -0
  314. package/server/migrations/1786400000000-IndexEventCreatedAt.ts +38 -0
  315. package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +93 -0
  316. package/server/migrations/index.ts +7 -1
  317. package/server/routes.ts +68 -5
  318. package/server/service/index.ts +4 -1
  319. package/server/service/reference/control-routing.ts +1 -1
  320. package/server/service/reference/discovery-result.ts +3 -3
  321. package/server/service/reference/hook-contract.ts +103 -0
  322. package/server/service/reference/ingest-space.ts +4 -4
  323. package/server/service/reference/knob-defaults.ts +4 -4
  324. package/server/service/reference/reference-adapter.ts +166 -9
  325. package/server/service/reference/reference-hook.ts +145 -0
  326. package/server/service/reference/reference-live.ts +92 -14
  327. package/server/service/reference/reference-master.ts +39 -17
  328. package/server/service/reference/reference-resolver.ts +10 -10
  329. package/server/service/reference/twin-reference.ts +3 -3
  330. package/server/service/twin-attention/twin-attention-query.ts +1 -1
  331. package/server/service/twin-audit/command-audit.ts +1 -1
  332. package/server/service/twin-audit/twin-audit-event.ts +2 -2
  333. package/server/service/twin-audit/twin-audit-query.ts +1 -1
  334. package/server/service/twin-control/twin-control-mutation.ts +1 -1
  335. package/server/service/twin-event/twin-event-keys.ts +37 -5
  336. package/server/service/twin-event/twin-event-type.ts +18 -1
  337. package/server/service/twin-event/twin-event.ts +32 -6
  338. package/server/service/twin-forecast/forecast-metrics.ts +1 -1
  339. package/server/service/twin-forecast/gap-analytics.ts +1 -1
  340. package/server/service/twin-forecast/twin-forecast-query.ts +30 -11
  341. package/server/service/twin-ingest-window/twin-ingest-window-query.ts +1 -1
  342. package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +1 -1
  343. package/server/service/twin-instance/twin-instance.ts +4 -4
  344. package/server/service/twin-journal/event-type-split-shape.ts +112 -0
  345. package/server/service/twin-journal/event-type-split.ts +144 -0
  346. package/server/service/twin-journal/journal-order.ts +20 -0
  347. package/server/service/twin-journal/twin-journal-query.ts +224 -20
  348. package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +57 -3
  349. package/server/service/twin-metrics/twin-metrics-query.ts +14 -1
  350. package/server/service/twin-model/axis-journal-evidence.ts +1 -1
  351. package/server/service/twin-model/epcis-coverage.ts +36 -1
  352. package/server/service/twin-model/iec61850-coverage.ts +1 -1
  353. package/server/service/twin-model/isa95-coverage.ts +45 -8
  354. package/server/service/twin-model/item-ref.ts +2 -2
  355. package/server/service/twin-model/name-index.ts +1 -1
  356. package/server/service/twin-model/project-structure.ts +4 -4
  357. package/server/service/twin-model/twin-equipment.ts +2 -2
  358. package/server/service/twin-model/twin-lineage-query.ts +16 -5
  359. package/server/service/twin-model/twin-location.ts +1 -1
  360. package/server/service/twin-model/twin-model-item-query.ts +14 -14
  361. package/server/service/twin-model/twin-model-mutation.ts +4 -4
  362. package/server/service/twin-model/twin-model-query.ts +117 -28
  363. package/server/service/twin-model/twin-model-tree-query.ts +25 -1
  364. package/server/service/twin-model/twin-operation.ts +2 -2
  365. package/server/service/twin-space/move-space.ts +3 -3
  366. package/server/service/twin-space/representation-role.ts +15 -0
  367. package/server/service/twin-space/space-integrity.ts +1 -1
  368. package/server/service/twin-space/twin-space-representation.ts +37 -0
  369. package/server/service/twin-space/twin-space-resolver.ts +39 -7
  370. package/server/service/twin-structure/twin-structure.ts +3 -3
  371. package/server/service/twin-subject/event-subjects.ts +155 -0
  372. package/server/service/twin-subject/index.ts +10 -0
  373. package/server/service/twin-subject/item-resolver.ts +92 -0
  374. package/server/service/twin-subject/subject-query-shape.ts +84 -0
  375. package/server/service/twin-subject/twin-subject-backfill.ts +196 -0
  376. package/server/service/twin-subject/twin-subject-event.ts +101 -0
  377. package/server/service/twin-subject/twin-subject-fast-path.ts +346 -0
  378. package/server/service/twin-subject/twin-subject-query.ts +210 -0
  379. package/server/service/twin-subject/twin-subject-writer.ts +189 -0
  380. package/server/service/twin-target/twin-target-resolver.ts +1 -1
  381. package/shared/entity-delta.ts +3 -3
  382. package/shared/touched-items.ts +2 -2
  383. package/shared/twin-level.ts +1 -1
  384. package/test/adopt-structure-live.test.ts +3 -3
  385. package/test/aggregate-not-findone.test.ts +112 -0
  386. package/test/attention-digest.test.ts +6 -6
  387. package/test/axis-read.test.ts +2 -2
  388. package/test/boot-resume.test.ts +20 -7
  389. package/test/broadcast-period.test.ts +2 -2
  390. package/test/broadcast-queue-overflow.test.ts +244 -0
  391. package/test/canonical-ingest-vocabularies.test.ts +179 -7
  392. package/test/canonical-quantity-door.test.ts +2 -2
  393. package/test/checkpoint-refuses-empty.test.ts +2 -2
  394. package/test/connector-capability-declaration.test.ts +88 -0
  395. package/test/control-capability.test.ts +1 -1
  396. package/test/cursor-stall-not-read-failure.test.ts +2 -2
  397. package/test/declaration-reaches-model.test.ts +5 -5
  398. package/test/declared-stimulus.test.ts +4 -4
  399. package/test/discovery-result.test.ts +2 -2
  400. package/test/entity-delta.test.ts +4 -4
  401. package/test/event-subjects.test.ts +147 -0
  402. package/test/event-time-column.test.ts +3 -3
  403. package/test/event-type-split.test.ts +155 -0
  404. package/test/fold-gap-speaks.test.ts +53 -0
  405. package/test/forecast-metrics.test.ts +1 -1
  406. package/test/forecast-tuning.test.ts +1 -1
  407. package/test/ingest-health-engine.test.ts +2 -2
  408. package/test/ingest-health-wiring.test.ts +7 -5
  409. package/test/ingest-health.test.ts +6 -6
  410. package/test/ingest-history.test.ts +2 -2
  411. package/test/ingest-idempotent.test.ts +132 -0
  412. package/test/ingest-space.test.ts +3 -3
  413. package/test/integration-probes.test.ts +3 -3
  414. package/test/integration-runner.test.ts +1 -1
  415. package/test/item-ref.test.ts +1 -1
  416. package/test/journal-order-axis.test.ts +80 -0
  417. package/test/journal-read-discipline.test.ts +16 -6
  418. package/test/journal-retention.test.ts +171 -29
  419. package/test/journal-sort-axis.test.ts +1 -1
  420. package/test/journal-write-door.test.ts +3 -3
  421. package/test/journal-write-trend.test.ts +1 -1
  422. package/test/kernel-kind-guard.test.ts +3 -3
  423. package/test/knob-defaults.test.ts +1 -1
  424. package/test/kpi-baseline-db.test.ts +2 -2
  425. package/test/kpi-fold.test.ts +185 -14
  426. package/test/kpi-query-bench.test.ts +3 -3
  427. package/test/live-cursor-wiring.test.ts +6 -6
  428. package/test/live-kernel-facts.test.ts +11 -11
  429. package/test/live-mirror-parity.test.ts +3 -3
  430. package/test/load-meter.test.ts +1 -1
  431. package/test/local-declarations.test.ts +10 -10
  432. package/test/log-stamp.test.ts +1 -1
  433. package/test/measured-yield.test.ts +2 -2
  434. package/test/merge-streams.test.ts +70 -0
  435. package/test/mirror-resumes-from-checkpoint.test.ts +13 -13
  436. package/test/model-basis.test.ts +2 -2
  437. package/test/model-gap.test.ts +4 -4
  438. package/test/model-vocabulary.test.ts +2 -2
  439. package/test/move-space.test.ts +1 -1
  440. package/test/oee-accumulator.test.ts +2 -2
  441. package/test/operational-vocabulary.test.ts +2 -2
  442. package/test/project-structure-db.test.ts +2 -2
  443. package/test/projection-reaches-screen.test.ts +2 -2
  444. package/test/property-effects.test.ts +3 -3
  445. package/test/read-failure-ledger.test.ts +106 -0
  446. package/test/read-failure-visible.test.ts +7 -7
  447. package/test/read-time.test.ts +118 -0
  448. package/test/reference-grounding.test.ts +1 -1
  449. package/test/reference-hook.test.ts +86 -0
  450. package/test/registry-key-guard.test.ts +1 -1
  451. package/test/rename-twin.test.ts +81 -0
  452. package/test/restart-policy.test.ts +3 -3
  453. package/test/resync-origin-site.test.ts +3 -3
  454. package/test/revision-axis.test.ts +14 -8
  455. package/test/runtime-key.test.ts +3 -3
  456. package/test/snapshot-freshness.test.ts +4 -4
  457. package/test/source-outcome-audit.test.ts +1 -1
  458. package/test/space-integrity.test.ts +1 -1
  459. package/test/space-representation.test.ts +66 -0
  460. package/test/space-scope-includes-stopped.test.ts +55 -0
  461. package/test/spec-coverage.test.ts +1 -1
  462. package/test/standard-coverage.test.ts +43 -1
  463. package/test/status-tally.test.ts +1 -1
  464. package/test/structure-revision-db.test.ts +7 -7
  465. package/test/subject-fast-path.test.ts +148 -0
  466. package/test/subject-read-model.test.ts +175 -0
  467. package/test/touched-items.test.ts +2 -2
  468. package/test/twin-audit.test.ts +1 -1
  469. package/test/twin-event-keys.test.ts +49 -3
  470. package/test/twin-model-item-db.test.ts +5 -5
  471. package/test/twin-model-tree-db.test.ts +35 -2
  472. package/test/twin-origin-resync.test.ts +1 -1
  473. package/test/warm-start-seam.test.ts +1 -1
  474. package/test/warm-start.test.ts +6 -6
  475. package/test/withheld-door.test.ts +95 -0
  476. package/test/yield-loop.test.ts +4 -4
  477. package/tsconfig.shared.tsbuildinfo +1 -1
  478. package/tsconfig.tsbuildinfo +1 -1
@@ -11,7 +11,7 @@ export interface LocalDeclaration {
11
11
  /**
12
12
  * 단위(UN/CEFACT 코드) — **어떤 값은 단위 없이는 뜻이 없다.**
13
13
  *
14
- * 속도가 그렇다: 단위가 없으면 이동 시간 추정기가 그 설비를 **거절한다**(그러면 공정 시간이 조용히
14
+ * 속도가 그렇다: 단위가 없으면 이동 시간 추정기가 그 설비를 **거절한다**(그러면 공정 시간이 알리지 않고
15
15
  * 사라진다). 명세가 단위를 요구하면(`uomChoices`) 받고, 그 목록에 없는 코드는 거절한다 — 읽는 쪽이
16
16
  * 모르는 단위를 받아 두면 화면은 넣었다고 말하고 계산은 그 값을 버린다.
17
17
  */
@@ -67,7 +67,7 @@ export interface RejectedDeclaration {
67
67
  * 그 이름의 공정을 이 트윈에서 확인할 수 없다 — 커널이 돌린 적도 없고 명세 행에도 없다.
68
68
  *
69
69
  * 공정 선언은 자리·설비와 달리 **모델에 행이 없어도 성립한다**(창고·야드에는 행이 아예 없다).
70
- * 그래서 오타를 잡아 줄 것이 이름 대조뿐이다 — 그마저 없으면 값은 조용히 아무 일도 하지 않는다.
70
+ * 그래서 오타를 잡아 줄 것이 이름 대조뿐이다 — 그마저 없으면 값은 알리지 않고 아무 일도 하지 않는다.
71
71
  */
72
72
  | 'unknown-operation';
73
73
  /** 무엇을 기대했나 — 사람이 고칠 수 있게(명세의 문장). */
@@ -120,7 +120,7 @@ export declare function declarablePropertySpecs(kind: string | undefined): Decla
120
120
  /**
121
121
  * 로컬 선언을 검사한다 — **받은 것과 받지 않은 것을 함께** 돌려준다.
122
122
  *
123
- * 조용히 걸러내지 않는 이유: 사용자는 값을 넣었다고 믿는다. 무엇이 왜 빠졌는지 말해야 고칠 수 있다.
123
+ * 알리지 않고 걸러내지 않는 이유: 사용자는 값을 넣었다고 믿는다. 무엇이 왜 빠졌는지 말해야 고칠 수 있다.
124
124
  */
125
125
  export declare function validateLocalDeclarations(kind: string | undefined, declarations: readonly LocalDeclaration[] | undefined | null, opts?: {
126
126
  /**
@@ -148,7 +148,7 @@ export declare function mergeLocalDeclarations(current: readonly LocalDeclaratio
148
148
  * 고쳐 넣었다는 것은 「원천의 값이 낡았다」는 뜻이다. 다만 그 사실이 보여야 하므로 `source: 'local'` 를
149
149
  * 값에 붙인다 — 화면·감사가 「누가 말한 값인가」를 되짚을 수 있다.
150
150
  *
151
- * 모델에 없는 대상에 얹으려 하면 **조용히 버리지 않고** 그 사실을 돌려준다(구조가 바뀌어 그 설비가
151
+ * 모델에 없는 대상에 얹으려 하면 **알리지 않고 버리지 않고** 그 사실을 돌려준다(구조가 바뀌어 그 설비가
152
152
  * 사라졌을 수 있고, 그때 사용자의 선언은 갈 곳이 없다).
153
153
  */
154
154
  export declare function applyLocalDeclarations<T extends {
@@ -163,7 +163,7 @@ export declare function applyLocalDeclarations<T extends {
163
163
  id: string;
164
164
  }[];
165
165
  /**
166
- * 읽을 수 있는 값으로 바꿀 수 없었던 선언 — **조용히 버리지 않기 위한 자리.**
166
+ * 읽을 수 있는 값으로 바꿀 수 없었던 선언 — **알리지 않고 버리지 않기 위한 자리.**
167
167
  *
168
168
  * 지금은 공정 시간에서만 난다(단위가 없거나 수가 아니면 ISO 8601 기간을 만들 수 없다). 문에서 이미
169
169
  * 검사하므로 정상 경로에서는 비어 있고, 옛 겹이나 손으로 넣은 데이터에서 드러난다.
@@ -188,7 +188,7 @@ export interface RevokedDeclaration extends DeclarationRevocation {
188
188
  /**
189
189
  * 겹에서 선언을 **뺀다** — 값을 넣는 것과 다른 결정이므로 다른 문이다.
190
190
  *
191
- * 없는 것을 뺐다고 말하지 않는다: 겹에 없던 대상·속성은 `notFound` 로 돌려준다(조용히 성공하면
191
+ * 없는 것을 뺐다고 말하지 않는다: 겹에 없던 대상·속성은 `notFound` 로 돌려준다(알리지 않고 성공하면
192
192
  * 사용자는 지웠다고 믿고 값은 그대로 남는다 — 가장 비싼 종류의 침묵이다).
193
193
  *
194
194
  * 속성이 하나도 남지 않은 대상은 겹에서 사라진다(빈 껍데기를 남기지 않는다).
@@ -249,7 +249,7 @@ export declare function applyDeclarationLayers<T extends {
249
249
  target: string;
250
250
  id: string;
251
251
  }[];
252
- /** 읽을 수 있는 값으로 바꿀 수 없던 선언 — 범위와 함께(조용히 버리지 않는다). */
252
+ /** 읽을 수 있는 값으로 바꿀 수 없던 선언 — 범위와 함께(알리지 않고 버리지 않는다). */
253
253
  unreadable: {
254
254
  scope: DeclarationScope;
255
255
  target: string;
@@ -37,7 +37,7 @@ exports.applyDeclarationLayers = applyDeclarationLayers;
37
37
  *
38
38
  * ── 왜 모델에 바로 쓰지 않고 겹으로 두나 ────────────────────────────────────
39
39
  * 모델은 **원천의 사본**이다. 다시 읽기(resync)는 그 사본을 새로 그리므로, 모델에 직접 쓴 값은 다음
40
- * 다시 읽기에서 **조용히 사라진다** — 요금이 사라지면 금액도 사라지고, 사용자는 이유를 알 수 없다.
40
+ * 다시 읽기에서 **오류 없이 사라진다** — 요금이 사라지면 금액도 사라지고, 사용자는 이유를 알 수 없다.
41
41
  * 그래서 로컬 선언은 따로 보관하고, 모델을 그릴 때마다 그 위에 얹는다. 덤으로 「누가 말한 값인가」에
42
42
  * 답할 수 있다 — 이 시스템이 근거를 값과 함께 다니게 하는 규율과 같다(`basis`·`derived` 와 한 결).
43
43
  *
@@ -87,7 +87,7 @@ const COMMON_SPECS = [
87
87
  *
88
88
  * 혼잡 90%·불량 15%는 우리가 정한 수였다. 통로를 95%로 채워 쓰는 창고는 그 기준에서 영구히 「병목」
89
89
  * 이고, 초기 라인은 계속 「불량 과다」로 운다 — 그때 진짜 경보도 함께 묻힌다. 선언이 없으면 기본값을
90
- * 쓰되 주의가 `basis: 'default'` 로 그 사실을 밝힌다(조용히 기본값을 진실로 두지 않는다).
90
+ * 쓰되 주의가 `basis: 'default'` 로 그 사실을 밝힌다(알리지 않고 기본값을 진실로 두지 않는다).
91
91
  */
92
92
  {
93
93
  id: twin_kernel_1.ATTENTION_PROPERTY.congestionRatio,
@@ -163,7 +163,7 @@ const COMMON_SPECS = [
163
163
  * 공정 모수 표에 한 칸 담는다 — **커널이 읽는 표기 그대로**(수율 0..1, 셋업 ISO 8601).
164
164
  *
165
165
  * 표를 통째로 새로 만들어 얹는다: 같은 모델 객체를 여러 겹이 지나므로(현장 → 트윈) 그때마다 새 객체여야
166
- * 앞 겹의 것을 조용히 덮어쓰지 않는다.
166
+ * 앞 겹의 것을 알리지 않고 덮어쓰지 않는다.
167
167
  */
168
168
  function putParam(next, kind, paramId, value) {
169
169
  const table = { ...(next.localParams ?? {}) };
@@ -206,7 +206,7 @@ function declarablePropertySpecs(kind) {
206
206
  /**
207
207
  * 로컬 선언을 검사한다 — **받은 것과 받지 않은 것을 함께** 돌려준다.
208
208
  *
209
- * 조용히 걸러내지 않는 이유: 사용자는 값을 넣었다고 믿는다. 무엇이 왜 빠졌는지 말해야 고칠 수 있다.
209
+ * 알리지 않고 걸러내지 않는 이유: 사용자는 값을 넣었다고 믿는다. 무엇이 왜 빠졌는지 말해야 고칠 수 있다.
210
210
  */
211
211
  function validateLocalDeclarations(kind, declarations, opts) {
212
212
  const accepted = [];
@@ -215,7 +215,7 @@ function validateLocalDeclarations(kind, declarations, opts) {
215
215
  const target = d?.target === 'equipment' ? 'equipment' : d?.target === 'operation' ? 'operation' : 'location';
216
216
  const id = String(d?.id ?? '').trim();
217
217
  if (!id) {
218
- /* 어느 대상인지 모르면 얹을 자리가 없다 — 그 사실을 말한다(조용히 버리지 않는다). */
218
+ /* 어느 대상인지 모르면 얹을 자리가 없다 — 그 사실을 말한다(알리지 않고 버리지 않는다). */
219
219
  rejected.push({ target, id: '', propertyId: '', reason: 'no-target-id' });
220
220
  continue;
221
221
  }
@@ -321,7 +321,7 @@ function mergeLocalDeclarations(current, incoming) {
321
321
  * 고쳐 넣었다는 것은 「원천의 값이 낡았다」는 뜻이다. 다만 그 사실이 보여야 하므로 `source: 'local'` 를
322
322
  * 값에 붙인다 — 화면·감사가 「누가 말한 값인가」를 되짚을 수 있다.
323
323
  *
324
- * 모델에 없는 대상에 얹으려 하면 **조용히 버리지 않고** 그 사실을 돌려준다(구조가 바뀌어 그 설비가
324
+ * 모델에 없는 대상에 얹으려 하면 **알리지 않고 버리지 않고** 그 사실을 돌려준다(구조가 바뀌어 그 설비가
325
325
  * 사라졌을 수 있고, 그때 사용자의 선언은 갈 곳이 없다).
326
326
  */
327
327
  function applyLocalDeclarations(model, declarations) {
@@ -362,7 +362,7 @@ function applyLocalDeclarations(model, declarations) {
362
362
  *
363
363
  * 겹에는 단위를 담고도 모델에 얹을 때 떨어뜨리고 있었다 — 그러면 속도를 넣어도 이동 시간 추정기가
364
364
  * 그 설비를 **버린다**(「speed has no unit of measure」). 화면은 「넣었습니다」라고 말하고 계산은
365
- * 그 값을 못 쓰는, 가장 조용한 종류의 결함이다.
365
+ * 그 값을 못 쓰는, 가장 들어온 것이 없는 종류의 결함이다.
366
366
  */
367
367
  const next = { id: p.id, value: String(p.value), ...(p.uom ? { uom: String(p.uom) } : {}), source: 'local' };
368
368
  if (at >= 0)
@@ -390,7 +390,7 @@ function applyLocalDeclarations(model, declarations) {
390
390
  const uom = String(p.uom ?? '');
391
391
  /*
392
392
  * 값을 **커널이 읽는 표기로** 바꿔 담는다 — 시간은 ISO 8601, 수율은 비율(0..1).
393
- * 바꿀 수 없으면 담지 않고 그 사실을 낸다(조용히 0 이나 빈 값으로 만들지 않는다).
393
+ * 바꿀 수 없으면 담지 않고 그 사실을 낸다(알리지 않고 0 이나 빈 값으로 만들지 않는다).
394
394
  */
395
395
  if (p.id === twin_kernel_1.OPERATION_PROPERTY.duration) {
396
396
  const iso = isoDurationOf(p.value, uom);
@@ -451,7 +451,7 @@ function applyLocalDeclarations(model, declarations) {
451
451
  /**
452
452
  * 겹에서 선언을 **뺀다** — 값을 넣는 것과 다른 결정이므로 다른 문이다.
453
453
  *
454
- * 없는 것을 뺐다고 말하지 않는다: 겹에 없던 대상·속성은 `notFound` 로 돌려준다(조용히 성공하면
454
+ * 없는 것을 뺐다고 말하지 않는다: 겹에 없던 대상·속성은 `notFound` 로 돌려준다(알리지 않고 성공하면
455
455
  * 사용자는 지웠다고 믿고 값은 그대로 남는다 — 가장 비싼 종류의 침묵이다).
456
456
  *
457
457
  * 속성이 하나도 남지 않은 대상은 겹에서 사라진다(빈 껍데기를 남기지 않는다).
@@ -1 +1 @@
1
- {"version":3,"file":"local-declarations.js","sourceRoot":"","sources":["../../server/engine/local-declarations.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 로컬 선언 — **구조는 원천이, 수는 우리가.**\n *\n * ── 낱말부터: `reference` ↔ `local` (2026-08-18 확정) ───────────────────────\n * 이 파일이 다루는 축은 **「이 값을 누가 말했나」** 하나다. 답은 둘뿐이다.\n *\n * · `reference` — 외부 원천 시스템이 준 값(SCADA·WMS·MES 의 마스터). 우리가 고르지 않았다.\n * · `local` — **이 트윈 쪽에서 정한 값.** 원천이 모르는 수를 운영 주체가 넣은 것이다.\n *\n * 「사람이 넣었나」가 아니라 **「권위가 어디 있나」**가 기준이다 — 앞으로 AI 가 사용자 대리로 채워도\n * 여전히 `local` 이다. 표식은 커널 계약의 `PropertySource` 로 선언돼 있고, **표식이 없으면 모델을 그린\n * 쪽**(참조에서 그렸으면 원천, 템플릿으로 만들었으면 그 템플릿)이다.\n *\n * 장소 낱말을 쓰지 않는 이유(옛 이름 `siteDeclarations` 를 버린 이유):\n * · `site` — 이 코드에서는 **외부 시스템이 부르는 사업장**이다(SAP 는 `P3`, Oracle 은 `W01` 로 같은\n * 현장을 부른다). 여기 담기는 것은 그 시스템이 **모르는** 값이므로 이름이 내용과 정반대다.\n * · `facility` — 화면에 나가는 영어(현장)이고, 코드에 남은 것은 남의 API 필드뿐이다(Oracle lgfapi).\n * · `space` — 「어디」에 답하는 낱말이다(트윈이 서는 그릇). 이 축은 「누가」에 답한다. 게다가 공간\n * 수준 선언(한 현장의 여러 트윈이 함께 읽는 요금)은 앞으로 정말 필요할 수 있어 비워 둔다.\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 커넥터로 만든 트윈은 구조를 원 시스템에서 읽어 온다(계통도·계량 지점·설비). 그런데 요금 단가·계약\n * 정보·축전지 충전율은 원 시스템이 모른다 — SCADA 게이트웨이는 우리 전기 계약을 알지 못한다. 그래서 그\n * 값들을 넣을 문이 없었고, 계량이 흐르는 트윈에서도 금액이 영원히 나오지 않았다(실제로 그 벽에 부딪혔다).\n *\n * ── 왜 모델에 바로 쓰지 않고 겹으로 두나 ────────────────────────────────────\n * 모델은 **원천의 사본**이다. 다시 읽기(resync)는 그 사본을 새로 그리므로, 모델에 직접 쓴 값은 다음\n * 다시 읽기에서 **조용히 사라진다** — 요금이 사라지면 금액도 사라지고, 사용자는 이유를 알 수 없다.\n * 그래서 로컬 선언은 따로 보관하고, 모델을 그릴 때마다 그 위에 얹는다. 덤으로 「누가 말한 값인가」에\n * 답할 수 있다 — 이 시스템이 근거를 값과 함께 다니게 하는 규율과 같다(`basis`·`derived` 와 한 결).\n *\n * ── 지어낸 속성을 받지 않는다 ───────────────────────────────────────────────\n * 커널이 읽는 속성만 받는다. 모르는 id 를 받아 두면 사용자는 값을 넣었다고 믿고 화면은 아무것도 바뀌지\n * 않는다 — 그 침묵이 가장 비싼 오류다. 단위·범위도 커널의 명세(`EMS_PROPERTY_SPEC`)로 검사한다:\n * 「SOC 80%」를 `0.8` 로 넣으면 배터리가 0.8% 로 서고, 그 사실을 아무도 모른 채 피크가 깎이지 않는다.\n *\n * **속성 어휘는 사용자가 정의하지 않는다** — 커널이 정한 속성에 값을 넣는 문이다(그래서 이름도\n * 「사용자 정의 속성」이 아니다).\n */\nimport { ATTENTION_PROPERTY, EMS_PROPERTY_SPEC, OPERATION_PROPERTY, OP_PARAM } from '@operato/twin-kernel'\n\nimport { EQUIPMENT_PROPERTY, SPEED_TO_MPS } from './travel-estimator.ts'\nimport { declarationTargetsOf } from './property-effects.ts'\n\n/** 로컬 선언이 겨눌 수 있는 대상 — 자원 둘과 **공정 종류**(자원이 아니라 명세다). */\nexport type DeclarationTarget = 'location' | 'equipment' | 'operation'\n\n/** 한 대상(자리·설비·공정)에 얹을 로컬 선언. */\nexport interface LocalDeclaration {\n target: DeclarationTarget\n /** 대상 id — 모델에 있는 그 이름. */\n id: string\n properties: {\n id: string\n value: string | number\n /**\n * 단위(UN/CEFACT 코드) — **어떤 값은 단위 없이는 뜻이 없다.**\n *\n * 속도가 그렇다: 단위가 없으면 이동 시간 추정기가 그 설비를 **거절한다**(그러면 공정 시간이 조용히\n * 사라진다). 명세가 단위를 요구하면(`uomChoices`) 받고, 그 목록에 없는 코드는 거절한다 — 읽는 쪽이\n * 모르는 단위를 받아 두면 화면은 넣었다고 말하고 계산은 그 값을 버린다.\n */\n uom?: string\n /**\n * 이 선언이 **덮은 원천 값** — 철회하면 이것으로 되돌린다.\n *\n * ── 왜 기억해야 하나 (2026-08-18) ────────────────────────────────────────\n * 선언은 모델에 얹혀 **저장된 모델에 구워진다.** 그래서 겹에서 줄만 지워도 원래 값이 돌아오지\n * 않는다 — 실제로 되돌릴 방법이 없어서, 잘못 넣은 값을 「원래 값을 다시 선언하는」 우회로만\n * 고칠 수 있었다(사용자가 원래 값을 알고 있어야 한다는 뜻이다).\n *\n * · 문자열 — 원천이 그 자리에 갖고 있던 값이다.\n * · `null` — 원천에는 그 속성이 **없었다**(철회하면 속성을 지운다).\n * · 없음 — **우리가 모른다**(이 필드가 생기기 전에 선언된 것). 지어내지 않고 그렇게 말한다.\n *\n * 사용자가 넣는 값이 아니라 얹을 때 우리가 채우는 값이다(매 원천 재읽기마다 새로 채워져 낡지 않는다).\n */\n replaced?: string | null\n }[]\n}\n\nexport interface RejectedDeclaration {\n target: string\n id: string\n propertyId: string\n /**\n * 왜 받지 않았나 — 언어중립 코드(화면이 옮긴다).\n * · `unknown-property` — 커널이 읽지 않는 속성 id 다\n * · `out-of-range` — 명세가 정한 범위 밖이다(단위를 잘못 읽은 값이 대개 여기 걸린다)\n * · `not-a-number` — 수를 요구하는 속성에 수가 아닌 값이 왔다\n * · `empty-value` — 값이 비어 있다(지우려면 그 뜻을 따로 말해야 한다)\n * · `no-property-vocabulary` — 이 종류의 트윈에는 선언 어휘가 아직 없다\n * · `no-target-id` — 어느 대상에 얹을지 말하지 않았다(선언의 모양이 틀렸다)\n * · `no-property-id` — 어느 속성인지 말하지 않았다(선언의 모양이 틀렸다)\n *\n * 뒤의 둘이 있는 이유: 예전에는 대상 id·속성 id 가 비면 **아무 말 없이 건너뛰었다.** 그래서 필드 이름을\n * 틀린 호출이 「받아준 선언이 없다」는 답만 받고 `rejected` 는 비어 있었다 — 무엇이 틀렸는지 알 길이\n * 없는 침묵이다(실제로 그 침묵에 걸렸다).\n */\n reason:\n | 'unknown-property'\n | 'out-of-range'\n | 'not-a-number'\n | 'empty-value'\n | 'no-property-vocabulary'\n | 'no-target-id'\n | 'no-property-id'\n /** 단위를 요구하는 속성인데 오지 않았다(그 값은 읽는 쪽이 버린다). */\n | 'uom-required'\n /** 읽는 쪽이 모르는 단위 코드다(명세가 받는 목록에 없다). */\n | 'unknown-uom'\n /**\n * 이 속성은 그 종류의 대상에 얹히지 않는다 — 읽는 코드가 다른 축을 본다.\n *\n * 예전에는 지게차에 계약전력을 넣을 수 있었다(문은 받고 화면은 「넣었습니다」라고 말하는데 읽는\n * 쪽은 자리만 본다). 어느 축의 속성인지는 효과 등록부가 이미 알고 있으므로 그것으로 거절한다.\n */\n | 'wrong-target'\n /**\n * 그 이름의 공정을 이 트윈에서 확인할 수 없다 — 커널이 돌린 적도 없고 명세 행에도 없다.\n *\n * 공정 선언은 자리·설비와 달리 **모델에 행이 없어도 성립한다**(창고·야드에는 행이 아예 없다).\n * 그래서 오타를 잡아 줄 것이 이름 대조뿐이다 — 그마저 없으면 값은 조용히 아무 일도 하지 않는다.\n */\n | 'unknown-operation'\n /** 무엇을 기대했나 — 사람이 고칠 수 있게(명세의 문장). */\n expected?: string\n}\n\n/** 이 종류의 트윈에서 커널이 읽는 선언 명세. 없으면 그 종류에는 선언 어휘가 없다. */\nfunction specOf(kind: string | undefined, propertyId: string): DeclarablePropertySpec | undefined {\n /* 종류 어휘와 공용 어휘를 함께 본다 — 속도는 어느 트윈에서나 읽히므로 EMS 밖에서도 받아야 한다. */\n return declarablePropertySpecs(kind).find(s => s.id === propertyId)\n}\n\n/** 이 종류의 트윈에 선언 어휘가 있나 — 없으면 검사할 근거가 없으므로 받지 않는다. */\nexport function hasPropertyVocabulary(kind: string | undefined): boolean {\n return kind === 'ems'\n}\n\n/** 이 종류에 선언할 수 있는 것이 하나라도 있나 — 공용 어휘가 있으므로 대개 참이다. */\nexport function hasAnyDeclarable(kind: string | undefined): boolean {\n return declarablePropertySpecs(kind).length > 0\n}\n\n/**\n * 이 종류의 트윈에 **선언할 수 있는 속성 목록** — 화면이 도메인의 모양을 알지 않게.\n *\n * 화면이 「에너지 트윈이면 계약전력·요금 단가…」를 알고 있으면, 도메인이 늘 때마다 화면을 고쳐야 하고\n * 단위·범위가 두 곳에 적힌다(ADR-0031: 카탈로그가 선언하고 화면은 그린다). 그래서 커널의 명세를 그대로\n * 낸다 — 화면은 받은 것을 렌더할 뿐이다.\n *\n * 어휘가 없는 종류는 **빈 목록**이다(「아직 없다」와 「아무 값이나 받는다」는 다르다).\n */\nexport interface DeclarablePropertySpec {\n id: string\n /** 사람이 읽는 단위(명세가 정한 것) — 값에 붙여 보인다. */\n uom?: string\n dataType: string\n range?: [number, number]\n note: string\n /**\n * 값과 **함께 단위를 골라야 하는** 속성 — 그 목록(UN/CEFACT 코드).\n *\n * 어떤 값은 단위 없이는 뜻이 없다: 속도는 단위가 없으면 추정기가 **거절한다**(「speed has no unit of\n * measure」). 그래서 명세가 「단위를 받아야 한다」와 「무엇을 받나」를 함께 말한다 — 화면이 목록을\n * 지어내면 화면이 받아 준 단위를 읽는 쪽이 모르는 일이 생긴다.\n */\n uomChoices?: string[]\n /**\n * 어느 종류의 대상에 얹히나(효과 등록부의 축) — 없으면 **우리가 제한을 모른다**(받아 준다).\n *\n * 화면은 이것으로 속성 목록을 대상에 맞게 좁힌다: 공정 시간을 설비에 넣으려는 선택지를 아예 보이지\n * 않게 하는 것이, 넣게 하고 거절하는 것보다 낫다.\n */\n targets?: string[]\n}\n\n/**\n * **어느 종류에나 있는 선언** — 「읽는 코드가 있는 것」만.\n *\n * 효과 등록부(`PROPERTY_EFFECTS`)가 그 목록의 근거다: 값이 계산을 바꾸는 속성만 여기 온다. 지금은\n * 설비의 `speed` 하나다(이동 시간 추정이 읽는다) — 자재의 `perBaseUnit` 은 자원 축이 아니라 자재\n * 정의에 얹히므로 이 화면의 대상이 아니다.\n *\n * 지어내지 않는다: 새 효과를 코드로 붙이는 사람이 등록부에 한 줄을 더하면 여기에도 따라 나온다.\n */\n/**\n * 시간 단위(UN/CEFACT) → ms. **목록은 여기 하나** — 화면·AI 는 명세의 `uomChoices` 로 받아 간다.\n *\n * 셋만 두는 이유: 현장이 공정 시간을 말할 때 쓰는 단위가 초·분·시간이다. 일 단위는 공정이 아니라 체류에\n * 가깝고, 그때는 그 값을 넣는 사람이 시간으로 환산해 말하는 편이 오해가 없다.\n */\nconst DURATION_UOM_MS: Record<string, number> = { SEC: 1_000, MIN: 60_000, HUR: 3_600_000 }\n\nconst COMMON_SPECS: DeclarablePropertySpec[] = [\n /*\n * 주의 판정 기준 — **경보가 소음이 되지 않게 현장이 정한다.**\n *\n * 혼잡 90%·불량 15%는 우리가 정한 수였다. 통로를 95%로 채워 쓰는 창고는 그 기준에서 영구히 「병목」\n * 이고, 초기 라인은 계속 「불량 과다」로 운다 — 그때 진짜 경보도 함께 묻힌다. 선언이 없으면 기본값을\n * 쓰되 주의가 `basis: 'default'` 로 그 사실을 밝힌다(조용히 기본값을 진실로 두지 않는다).\n */\n {\n id: ATTENTION_PROPERTY.congestionRatio,\n uom: '%',\n dataType: 'xs:double',\n range: [1, 100],\n note: 'congestion threshold as a percentage 1-100 (95 means 95% of capacity) — above it this location is called a bottleneck. default 90.'\n },\n {\n id: ATTENTION_PROPERTY.scrapRate,\n uom: '%',\n dataType: 'xs:double',\n range: [1, 100],\n note: 'scrap threshold as a percentage 1-100 (25 means 25%) — above it this equipment is called high-scrap, and twice it is severe. default 15.'\n },\n {\n id: ATTENTION_PROPERTY.scrapMinSamples,\n dataType: 'xs:double',\n range: [1, 100000],\n note: 'how many pieces must be counted before the scrap rate is judged at all — a ratio over few pieces is not evidence. default 10.'\n },\n {\n id: EQUIPMENT_PROPERTY.speed,\n dataType: 'xs:double',\n range: [0.01, 50],\n note: 'travel speed of this equipment — the estimator turns distance into duration with it. a unit is required.',\n uomChoices: Object.keys(SPEED_TO_MPS)\n },\n /*\n * 공정 소요시간 — **측정이 없을 때 상수를 대신한다.**\n *\n * 대상은 자리·설비가 아니라 **공정 종류**(`putaway`·`pick`·`spot`…)이고, 그 종류에 명세 행이 없어도\n * 성립한다(실측: 창고·야드 트윈에는 행이 0개다). 단위를 반드시 받는다 — 「4」만으로는 4초인지 4분인지\n * 알 수 없고, 짐작하면 예측이 60배 틀린다.\n */\n {\n id: OPERATION_PROPERTY.duration,\n dataType: 'xs:double',\n range: [0.01, 100000],\n note: 'how long this operation takes at this site — the simulation and the forecast use it instead of the built-in constant. a unit is required. history still wins where it exists.',\n uomChoices: Object.keys(DURATION_UOM_MS)\n },\n /*\n * 양품률 — **퍼센트로 받는다**(80 이 80%다).\n *\n * 커널이 읽는 값은 0..1 이지만(ISA-95 `Parameter` 값 그대로) 사람에게 0.8 을 넣으라고 하면 SOC 에서\n * 겪은 함정을 되풀게 된다(0.8 을 0.8% 로 쓴 일이 있다). 그래서 화면·AI 는 퍼센트로 말하고, 비율로\n * 바꾸는 일은 얹는 자리 한 곳에서 한다.\n */\n {\n id: OPERATION_PROPERTY.yield,\n uom: '%',\n dataType: 'xs:double',\n range: [1, 100],\n note: 'good-output rate of this operation as a percentage 1-100 (95 means 95%) — the kernel reads it as a ratio, and this value replaces the built-in default that also drives the high-scrap attention.'\n },\n /* 셋업·체인지오버 — 가동 앞에 붙는 시간. 소요시간과 같은 단위 목록을 쓴다(같은 종류의 값이다). */\n {\n id: OPERATION_PROPERTY.setupDuration,\n dataType: 'xs:double',\n range: [0.01, 100000],\n note: 'changeover time before this operation runs — added in front of the cycle. a unit is required.',\n uomChoices: Object.keys(DURATION_UOM_MS)\n }\n]\n\n/**\n * 값 + 단위 → **ISO 8601 기간** — 커널이 읽는 표기로 바꾼다(커널은 초·분을 모른다, 표준 표기만 안다).\n *\n * 읽을 수 없으면 `undefined` — 부르는 쪽이 그 사실을 거절 사유로 올린다(0 을 만들지 않는다: 소요 0 인\n * 작업은 영원히 끝나지 않는다).\n */\n/**\n * 공정 모수 표에 한 칸 담는다 — **커널이 읽는 표기 그대로**(수율 0..1, 셋업 ISO 8601).\n *\n * 표를 통째로 새로 만들어 얹는다: 같은 모델 객체를 여러 겹이 지나므로(현장 → 트윈) 그때마다 새 객체여야\n * 앞 겹의 것을 조용히 덮어쓰지 않는다.\n */\nfunction putParam(next: any, kind: string, paramId: string, value: string): void {\n const table = { ...(next.localParams ?? {}) }\n table[kind] = { ...(table[kind] ?? {}), [paramId]: value }\n next.localParams = table\n}\n\nexport function isoDurationOf(value: string | number, uom: string): string | undefined {\n const n = Number(value)\n const per = DURATION_UOM_MS[String(uom ?? '').trim()]\n if (!Number.isFinite(n) || n <= 0 || !per) return undefined\n return `PT${(n * per) / 1000}S`\n}\n\n/**\n * 이 종류의 트윈에 **선언할 수 있는 속성 목록** — 화면이 도메인의 모양을 알지 않게.\n *\n * 종류와 무관한 것(효과 등록부에서 온다) + 그 종류의 어휘(지금은 EMS 뿐). 어휘가 없는 종류도 **빈손이\n * 아니다**: 속도는 어느 트윈에서나 읽히므로 창고·야드·생산 트윈에서도 넣을 수 있어야 한다.\n */\nexport function declarablePropertySpecs(kind: string | undefined): DeclarablePropertySpec[] {\n const domain = hasPropertyVocabulary(kind)\n ? Object.entries(EMS_PROPERTY_SPEC as Record<string, any>).map(([id, spec]) => ({\n id,\n ...(spec.uom ? { uom: spec.uom } : {}),\n dataType: spec.dataType,\n ...(spec.range ? { range: spec.range as [number, number] } : {}),\n note: spec.note\n }))\n : []\n /*\n * **어느 대상에 얹히나는 효과 등록부가 답한다** — 명세에 손으로 또 적지 않는다(두 곳에 적으면 한쪽만\n * 바뀐다). 등록부에 없는 속성은 제한을 **모르는** 것이므로 그대로 둔다(속성은 열려 있다).\n */\n const all: DeclarablePropertySpec[] = [...COMMON_SPECS, ...domain]\n return all.map(s => {\n const targets = s.targets ?? declarationTargetsOf(s.id)\n return targets ? { ...s, targets } : s\n })\n}\n\n/**\n * 로컬 선언을 검사한다 — **받은 것과 받지 않은 것을 함께** 돌려준다.\n *\n * 조용히 걸러내지 않는 이유: 사용자는 값을 넣었다고 믿는다. 무엇이 왜 빠졌는지 말해야 고칠 수 있다.\n */\nexport function validateLocalDeclarations(\n kind: string | undefined,\n declarations: readonly LocalDeclaration[] | undefined | null,\n opts?: {\n /**\n * 이 트윈에서 확인된 공정 이름들 — 있으면 그 밖의 이름을 거절한다(오타를 잡는 유일한 수단이다).\n *\n * **모르면 넘기지 않고 모른다고 둔다**: 아직 돌지 않은 트윈은 어떤 공정을 돌릴지 알 수 없으므로\n * 목록을 주지 않고(=undefined) 받아 준다. 부르는 쪽이 「확인하지 못했다」는 사실을 답에 싣는다.\n */\n knownOperations?: readonly string[]\n }\n): { accepted: LocalDeclaration[]; rejected: RejectedDeclaration[] } {\n const accepted: LocalDeclaration[] = []\n const rejected: RejectedDeclaration[] = []\n for (const d of declarations ?? []) {\n const target: DeclarationTarget = d?.target === 'equipment' ? 'equipment' : d?.target === 'operation' ? 'operation' : 'location'\n const id = String(d?.id ?? '').trim()\n if (!id) {\n /* 어느 대상인지 모르면 얹을 자리가 없다 — 그 사실을 말한다(조용히 버리지 않는다). */\n rejected.push({ target, id: '', propertyId: '', reason: 'no-target-id' })\n continue\n }\n const props: { id: string; value: string }[] = []\n for (const p of d?.properties ?? []) {\n const propertyId = String(p?.id ?? '').trim()\n if (!propertyId) {\n rejected.push({ target, id, propertyId: '', reason: 'no-property-id' })\n continue\n }\n const raw = p?.value\n if (raw === undefined || raw === null || String(raw).trim() === '') {\n rejected.push({ target, id, propertyId, reason: 'empty-value' })\n continue\n }\n if (!hasAnyDeclarable(kind)) {\n rejected.push({ target, id, propertyId, reason: 'no-property-vocabulary' })\n continue\n }\n const spec = specOf(kind, propertyId)\n if (!spec) {\n rejected.push({ target, id, propertyId, reason: 'unknown-property' })\n continue\n }\n /*\n * **읽는 쪽이 보는 축에만 얹는다** — 지게차에 계약전력을 넣을 수 있던 구멍을 여기서 막는다.\n * 명세가 제한을 말하지 않으면(등록부에 없는 속성) 받아 준다: 속성은 열려 있고, 효과만 닫혀 있다.\n */\n if (spec.targets?.length && !spec.targets.includes(target)) {\n rejected.push({ target, id, propertyId, reason: 'wrong-target', expected: `one of ${spec.targets.join(', ')}` })\n continue\n }\n /* 공정 이름은 대조로만 걸러진다 — 모델에 행이 없어도 성립하는 선언이라 다른 안전망이 없다. */\n if (target === 'operation' && opts?.knownOperations && !opts.knownOperations.includes(id)) {\n rejected.push({ target, id, propertyId, reason: 'unknown-operation', expected: `one of ${[...opts.knownOperations].join(', ')}` })\n continue\n }\n if (spec.dataType === 'xs:double') {\n const n = Number(raw)\n if (!Number.isFinite(n)) {\n rejected.push({ target, id, propertyId, reason: 'not-a-number', expected: spec.note })\n continue\n }\n if (spec.range && (n < spec.range[0] || n > spec.range[1])) {\n rejected.push({ target, id, propertyId, reason: 'out-of-range', expected: spec.note })\n continue\n }\n }\n /*\n * 단위를 요구하는 속성은 **단위까지 검사한다** — 값만 받아 두면 읽는 쪽이 그 설비를 건너뛴다.\n * 목록은 명세가 든다(`uomChoices`): 화면이 목록을 지어내면 화면이 받아 준 단위를 계산이 모른다.\n */\n const uom = String((p as any)?.uom ?? '').trim()\n if (spec.uomChoices?.length) {\n if (!uom) {\n rejected.push({ target, id, propertyId, reason: 'uom-required', expected: `one of ${spec.uomChoices.join(', ')}` })\n continue\n }\n if (!spec.uomChoices.includes(uom)) {\n rejected.push({ target, id, propertyId, reason: 'unknown-uom', expected: `one of ${spec.uomChoices.join(', ')}` })\n continue\n }\n }\n props.push({ id: propertyId, value: String(raw), ...(uom ? { uom } : {}) })\n }\n if (props.length) accepted.push({ target, id, properties: props })\n }\n return { accepted, rejected }\n}\n\n/**\n * 겹을 합친다 — 같은 대상·같은 속성이면 **새 값이 이긴다**(현장이 값을 고치는 일은 흔하다).\n *\n * 대상이나 속성을 지우는 일은 여기서 하지 않는다: 지움은 「값을 모르는 상태로 되돌린다」는 뜻이고,\n * 그것은 값을 넣는 것과 다른 결정이므로 다른 문으로 말해야 한다.\n */\nexport function mergeLocalDeclarations(\n current: readonly LocalDeclaration[] | undefined | null,\n incoming: readonly LocalDeclaration[]\n): LocalDeclaration[] {\n const key = (t: string, id: string) => `${t}:${id}`\n const out = new Map<string, LocalDeclaration>()\n for (const d of current ?? []) out.set(key(d.target, d.id), { ...d, properties: [...d.properties] })\n for (const d of incoming) {\n const k = key(d.target, d.id)\n const cur = out.get(k)\n if (!cur) {\n out.set(k, { ...d, properties: [...d.properties] })\n continue\n }\n for (const p of d.properties) {\n const at = cur.properties.findIndex(x => x.id === p.id)\n if (at >= 0) cur.properties[at] = { ...p }\n else cur.properties.push({ ...p })\n }\n }\n return [...out.values()]\n}\n\n/**\n * 모델 위에 로컬 선언을 얹는다 — **모델을 바꾸지 않고 새 모델을 돌려준다.**\n *\n * 원천이 같은 속성을 이미 말했으면 **로컬 값이 이긴다**: 계약전력을 게이트웨이가 말하더라도, 우리가\n * 고쳐 넣었다는 것은 「원천의 값이 낡았다」는 뜻이다. 다만 그 사실이 보여야 하므로 `source: 'local'` 를\n * 값에 붙인다 — 화면·감사가 「누가 말한 값인가」를 되짚을 수 있다.\n *\n * 모델에 없는 대상에 얹으려 하면 **조용히 버리지 않고** 그 사실을 돌려준다(구조가 바뀌어 그 설비가\n * 사라졌을 수 있고, 그때 사용자의 선언은 갈 곳이 없다).\n */\nexport function applyLocalDeclarations<\n T extends { locations?: any[]; equipment?: any[]; localDurations?: Record<string, string>; localParams?: Record<string, Record<string, string>> }\n>(\n model: T,\n declarations: readonly LocalDeclaration[] | undefined | null\n): {\n model: T\n missingTargets: { target: string; id: string }[]\n /**\n * 읽을 수 있는 값으로 바꿀 수 없었던 선언 — **조용히 버리지 않기 위한 자리.**\n *\n * 지금은 공정 시간에서만 난다(단위가 없거나 수가 아니면 ISO 8601 기간을 만들 수 없다). 문에서 이미\n * 검사하므로 정상 경로에서는 비어 있고, 옛 겹이나 손으로 넣은 데이터에서 드러난다.\n */\n unreadable: { target: string; id: string; propertyId: string }[]\n layer: LocalDeclaration[]\n} {\n const decls = declarations ?? []\n if (!decls.length) return { model, missingTargets: [], unreadable: [], layer: [] }\n\n const missingTargets: { target: string; id: string }[] = []\n const unreadable: { target: string; id: string; propertyId: string }[] = []\n /* 얹으면서 **무엇을 덮었는지** 함께 적어 돌려준다 — 철회는 그 기억이 있어야 성립한다. */\n const layer: LocalDeclaration[] = decls.map(d => ({ ...d, properties: d.properties.map(p => ({ ...p })) }))\n\n const put = (list: any[] | undefined, d: LocalDeclaration, memo: LocalDeclaration): boolean => {\n const item = (list ?? []).find(x => String(x?.id ?? '') === d.id)\n if (!item) return false\n const props: any[] = Array.isArray(item.properties) ? [...item.properties] : []\n for (let i = 0; i < d.properties.length; i++) {\n const p = d.properties[i]\n const at = props.findIndex(x => String(x?.id ?? '') === p.id)\n const noted = memo.properties[i]\n /*\n * 기억은 **표식이 없는 값일 때만** 갱신한다 — ADR-0034 의 규칙 그대로: 표식이 없으면 모델을 그린\n * 쪽(대개 원천)이 말한 값이고, 표식이 있으면 **누군가 찍은 것**이므로 맨 원천 값이 아니다.\n *\n * 이 함수는 원천에서 새로 그린 모델에도, 이미 선언이 구워진 저장 모델에도 얹힌다. 뒤쪽에서\n * 무턱대고 적으면 **우리 값을 원천 값으로 기억**하게 되고(600 을 원천이 말한 것으로 적는다),\n * 철회해도 같은 값으로 되돌아간다.\n *\n * `!== 'local'` 로 보지 않는 이유: 옛 표식(`'site'`)이 구워진 모델이 남아 있고, 그것을 「표식\n * 아님」으로 읽으면 바로 그 오인이 일어난다. 호환 코드를 두는 것이 아니라 **규칙을 그대로 적는\n * 것**이다 — 표식의 종류를 따지지 않는다.\n */\n if (at < 0) noted.replaced = null\n else if (props[at]?.source === undefined) noted.replaced = String(props[at]?.value ?? '')\n /*\n * **단위도 함께 얹는다** (2026-08-18 실측으로 고침).\n *\n * 겹에는 단위를 담고도 모델에 얹을 때 떨어뜨리고 있었다 — 그러면 속도를 넣어도 이동 시간 추정기가\n * 그 설비를 **버린다**(「speed has no unit of measure」). 화면은 「넣었습니다」라고 말하고 계산은\n * 그 값을 못 쓰는, 가장 조용한 종류의 결함이다.\n */\n const next = { id: p.id, value: String(p.value), ...((p as any).uom ? { uom: String((p as any).uom) } : {}), source: 'local' as const }\n if (at >= 0) props[at] = { ...props[at], ...next }\n else props.push(next)\n }\n item.properties = props\n return true\n }\n\n /**\n * 공정 시간은 **자원 속성이 아니다** — 종류별 표(`localDurations`)에 담는다.\n *\n * 왜 자리·설비처럼 속성으로 담지 않나: 커널의 공정 명세 행(`OperationDef`)은 `label`·`intent` 를\n * 요구하는데(ISA-95 `OperationsSegment`) 창고·야드 트윈에는 그 행이 **아예 없다**(실측: 35개 중\n * WMS·YMS 전부 0개). 행을 지어 만들면 지어낸 `intent` 가 능력 계산까지 오염시킨다 — 「몇 분 걸리나」를\n * 말하려고 「무슨 작업인가」를 발명하지 않는다.\n *\n * 이 표는 **전부 우리가 정한 값**이다(원천이 그리는 자리가 아니다). 그래서 기억(`replaced`)은 언제나\n * `null` 이고, 철회는 「표에서 뺀다」로 끝난다 — 그러면 원천 명세나 커널 상수가 다시 답한다.\n */\n const putOperation = (next: any, d: LocalDeclaration, memo: LocalDeclaration): void => {\n for (let i = 0; i < d.properties.length; i++) {\n const p = d.properties[i]\n const uom = String((p as any).uom ?? '')\n /*\n * 값을 **커널이 읽는 표기로** 바꿔 담는다 — 시간은 ISO 8601, 수율은 비율(0..1).\n * 바꿀 수 없으면 담지 않고 그 사실을 낸다(조용히 0 이나 빈 값으로 만들지 않는다).\n */\n if (p.id === OPERATION_PROPERTY.duration) {\n const iso = isoDurationOf(p.value, uom)\n if (!iso) { unreadable.push({ target: 'operation', id: d.id, propertyId: p.id }); continue }\n next.localDurations = { ...(next.localDurations ?? {}), [d.id]: iso }\n memo.properties[i].replaced = null\n continue\n }\n if (p.id === OPERATION_PROPERTY.setupDuration) {\n const iso = isoDurationOf(p.value, uom)\n if (!iso) { unreadable.push({ target: 'operation', id: d.id, propertyId: p.id }); continue }\n putParam(next, d.id, OP_PARAM.setupDuration, iso)\n memo.properties[i].replaced = null\n continue\n }\n if (p.id === OPERATION_PROPERTY.yield) {\n const pct = Number(p.value)\n if (!Number.isFinite(pct) || pct <= 0 || pct > 100) { unreadable.push({ target: 'operation', id: d.id, propertyId: p.id }); continue }\n /* 퍼센트 → 비율. 이 환산이 있는 자리는 여기 하나다(두 곳에서 나누면 100 배가 두 번 걸린다). */\n putParam(next, d.id, OP_PARAM.yield, String(pct / 100))\n memo.properties[i].replaced = null\n continue\n }\n unreadable.push({ target: 'operation', id: d.id, propertyId: p.id })\n }\n }\n\n /* 얕은 복사로는 안 된다 — 자리·설비 항목을 손대므로 그 배열과 항목까지 새로 만든다. */\n const next: any = {\n ...model,\n ...(model.locations ? { locations: model.locations.map(n => ({ ...n, ...(n?.properties ? { properties: [...n.properties] } : {}) })) } : {}),\n ...(model.equipment ? { equipment: model.equipment.map(e => ({ ...e, ...(e?.properties ? { properties: [...e.properties] } : {}) })) } : {}),\n ...(model.localDurations ? { localDurations: { ...model.localDurations } } : {}),\n ...(model.localParams ? { localParams: Object.fromEntries(Object.entries(model.localParams).map(([k, v]) => [k, { ...v }])) } : {})\n }\n for (let i = 0; i < decls.length; i++) {\n const d = decls[i]\n if (d.target === 'operation') {\n /* 빈 표를 남기지 않는다 — 「선언이 있다」와 「읽을 수 있는 선언이 하나도 없다」는 다른 사실이다\n (`putOperation` 이 담을 것이 있을 때만 표를 만든다). */\n putOperation(next, d, layer[i])\n continue\n }\n const ok = put(d.target === 'equipment' ? next.equipment : next.locations, d, layer[i])\n if (!ok) missingTargets.push({ target: d.target, id: d.id })\n }\n return { model: next as T, missingTargets, unreadable, layer }\n}\n\n/** 철회 요청 하나 — 어느 대상의 어느 속성을 원천으로 되돌리나. */\nexport interface DeclarationRevocation {\n target: DeclarationTarget\n id: string\n propertyId: string\n}\n\n/** 철회된 항목 — 되돌릴 값을 함께 든다(모르면 `replaced` 가 없다). */\nexport interface RevokedDeclaration extends DeclarationRevocation {\n replaced?: string | null\n}\n\n/**\n * 겹에서 선언을 **뺀다** — 값을 넣는 것과 다른 결정이므로 다른 문이다.\n *\n * 없는 것을 뺐다고 말하지 않는다: 겹에 없던 대상·속성은 `notFound` 로 돌려준다(조용히 성공하면\n * 사용자는 지웠다고 믿고 값은 그대로 남는다 — 가장 비싼 종류의 침묵이다).\n *\n * 속성이 하나도 남지 않은 대상은 겹에서 사라진다(빈 껍데기를 남기지 않는다).\n */\nexport function revokeLocalDeclarations(\n current: readonly LocalDeclaration[] | undefined | null,\n revocations: readonly DeclarationRevocation[] | undefined | null\n): { layer: LocalDeclaration[]; revoked: RevokedDeclaration[]; notFound: DeclarationRevocation[] } {\n const layer: LocalDeclaration[] = (current ?? []).map(d => ({ ...d, properties: d.properties.map(p => ({ ...p })) }))\n const revoked: RevokedDeclaration[] = []\n const notFound: DeclarationRevocation[] = []\n\n for (const r of revocations ?? []) {\n const target: DeclarationTarget = r?.target === 'equipment' ? 'equipment' : r?.target === 'operation' ? 'operation' : 'location'\n const id = String(r?.id ?? '').trim()\n const propertyId = String(r?.propertyId ?? '').trim()\n if (!id || !propertyId) {\n notFound.push({ target, id, propertyId })\n continue\n }\n const d = layer.find(x => x.target === target && x.id === id)\n const at = d ? d.properties.findIndex(p => p.id === propertyId) : -1\n if (!d || at < 0) {\n notFound.push({ target, id, propertyId })\n continue\n }\n const [gone] = d.properties.splice(at, 1)\n revoked.push({ target, id, propertyId, ...('replaced' in gone ? { replaced: gone.replaced } : {}) })\n }\n\n return { layer: layer.filter(d => d.properties.length), revoked, notFound }\n}\n\n/**\n * 철회한 속성을 **모델에서 원천 값으로 되돌린다** — 저장된 모델에 값이 구워져 있기 때문이다.\n *\n * · 기억한 값이 있으면 그 값으로 되돌리고 `source: 'local'` 표식을 뗀다(다시 원천이 말한 값이다).\n * · `null` 이면 원천에 없던 속성이므로 **지운다**.\n * · 기억이 없으면(옛 선언) **손대지 않고** `unknownSource` 로 말한다 — 원래 값을 지어내는 것은\n * 잘못 넣은 값을 그대로 두는 것보다 나쁘다(다음 원천 재읽기가 그 자리를 바로잡는다).\n */\nexport function restoreRevoked<\n T extends { locations?: any[]; equipment?: any[]; localDurations?: Record<string, string>; localParams?: Record<string, Record<string, string>> }\n>(\n model: T,\n revoked: readonly RevokedDeclaration[]\n): { model: T; restored: RevokedDeclaration[]; unknownSource: DeclarationRevocation[]; missingTargets: { target: string; id: string }[] } {\n if (!revoked.length) return { model, restored: [], unknownSource: [], missingTargets: [] }\n\n const next: any = {\n ...model,\n ...(model.locations ? { locations: model.locations.map(n => ({ ...n, ...(n?.properties ? { properties: [...n.properties] } : {}) })) } : {}),\n ...(model.equipment ? { equipment: model.equipment.map(e => ({ ...e, ...(e?.properties ? { properties: [...e.properties] } : {}) })) } : {}),\n ...(model.localDurations ? { localDurations: { ...model.localDurations } } : {}),\n ...(model.localParams ? { localParams: Object.fromEntries(Object.entries(model.localParams).map(([k, v]) => [k, { ...v }])) } : {})\n }\n const restored: RevokedDeclaration[] = []\n const unknownSource: DeclarationRevocation[] = []\n const missingTargets: { target: string; id: string }[] = []\n\n for (const r of revoked) {\n /*\n * 공정 시간은 표에서 **뺀다** — 되돌릴 원천 값이 없는 자리다(전부 우리가 정한 값이었다).\n * 빼면 원천 명세(있으면)나 커널 상수가 다시 답한다: 그것이 「선언 이전」의 상태다.\n */\n if (r.target === 'operation') {\n /* 시간은 시간 표에서, 모수(수율·셋업)는 모수 표에서 뺀다 — 담긴 자리에서 빼야 실제로 빠진다. */\n const paramId =\n r.propertyId === OPERATION_PROPERTY.yield\n ? OP_PARAM.yield\n : r.propertyId === OPERATION_PROPERTY.setupDuration\n ? OP_PARAM.setupDuration\n : undefined\n if (paramId) {\n const params: Record<string, Record<string, string>> = next.localParams ?? {}\n if (!params[r.id] || !(paramId in params[r.id])) {\n missingTargets.push({ target: r.target, id: r.id })\n continue\n }\n delete params[r.id][paramId]\n /* 빈 껍데기를 남기지 않는다 — 「선언이 있다」와 「빈 표가 있다」가 구별되지 않는다. */\n if (!Object.keys(params[r.id]).length) delete params[r.id]\n if (!Object.keys(params).length) delete next.localParams\n else next.localParams = params\n restored.push(r)\n continue\n }\n const table: Record<string, string> = next.localDurations ?? {}\n if (!(r.id in table)) {\n missingTargets.push({ target: r.target, id: r.id })\n continue\n }\n delete table[r.id]\n if (!Object.keys(table).length) delete next.localDurations\n else next.localDurations = table\n restored.push(r)\n continue\n }\n const list: any[] = (r.target === 'equipment' ? next.equipment : next.locations) ?? []\n const item = list.find(x => String(x?.id ?? '') === r.id)\n if (!item) {\n missingTargets.push({ target: r.target, id: r.id })\n continue\n }\n if (!('replaced' in r)) {\n unknownSource.push({ target: r.target, id: r.id, propertyId: r.propertyId })\n continue\n }\n const props: any[] = Array.isArray(item.properties) ? [...item.properties] : []\n const at = props.findIndex(x => String(x?.id ?? '') === r.propertyId)\n if (r.replaced === null) {\n if (at >= 0) props.splice(at, 1)\n } else if (at >= 0) {\n const { source: _dropped, ...rest } = props[at] ?? {}\n props[at] = { ...rest, value: String(r.replaced) }\n } else {\n /* 겹은 있었는데 모델에는 없다 — 원천 값을 되살려 넣는다(우리가 지웠을 수 있다). */\n props.push({ id: r.propertyId, value: String(r.replaced) })\n }\n item.properties = props\n restored.push(r)\n }\n\n return { model: next as T, restored, unknownSource, missingTargets }\n}\n\n/** 선언이 어느 범위의 것인가 — 좁은 것이 이긴다(`space` 먼저, `instance` 나중). */\nexport type DeclarationScope = 'space' | 'instance'\n\n/**\n * 두 겹을 **순서대로** 얹는다 — 현장 먼저, 트윈이 나중.\n *\n * ── 왜 순서가 규율인가 (2026-08-18) ─────────────────────────────────────────\n * 요금 단가는 현장의 계약이므로 그 현장의 트윈 전부가 같은 값을 읽어야 한다. 그런데 한 트윈만 다른\n * 값으로 실험하는 일이 있다(인상된 단가로 what-if). 그때 현장 값을 고치게 하면 **나머지 트윈이 함께\n * 흔들린다.** 그래서 더 좁은 선언이 이긴다 — 넓은 값은 그대로 두고 좁은 자리에서만 달라진다.\n *\n * 기억(`replaced`)은 각 겹이 **자기가 덮은 값**을 적는다: 트윈 겹의 기억은 「현장 값을 덮었다」가 되고,\n * 철회하면 현장 값으로 돌아간다(원천까지 가지 않는다). 그것이 사람이 기대하는 되돌림이다.\n */\nexport function applyDeclarationLayers<\n T extends { locations?: any[]; equipment?: any[]; localDurations?: Record<string, string>; localParams?: Record<string, Record<string, string>> }\n>(\n model: T,\n spaceLayer: readonly LocalDeclaration[] | undefined | null,\n instanceLayer: readonly LocalDeclaration[] | undefined | null\n): {\n model: T\n /** 현장 겹을 얹은 결과 — 저장해야 기억이 낡지 않는다. */\n spaceLayerApplied: LocalDeclaration[]\n instanceLayerApplied: LocalDeclaration[]\n /** 갈 곳이 없던 대상 — 어느 범위에서 났는지 함께 낸다(현장 겹은 그 트윈에 없을 수 있다). */\n missingTargets: { scope: DeclarationScope; target: string; id: string }[]\n /** 읽을 수 있는 값으로 바꿀 수 없던 선언 — 범위와 함께(조용히 버리지 않는다). */\n unreadable: { scope: DeclarationScope; target: string; id: string; propertyId: string }[]\n} {\n const first = applyLocalDeclarations(model, spaceLayer)\n const second = applyLocalDeclarations(first.model, instanceLayer)\n return {\n model: second.model,\n spaceLayerApplied: first.layer,\n instanceLayerApplied: second.layer,\n unreadable: [\n ...first.unreadable.map(u => ({ scope: 'space' as const, ...u })),\n ...second.unreadable.map(u => ({ scope: 'instance' as const, ...u }))\n ],\n /*\n * **현장 겹의 「갈 곳 없음」은 오류가 아니다.**\n *\n * 한 현장에는 여러 종류의 트윈이 산다(창고·야드·에너지). 요금을 현장에 선언하면 그 값은 수전 자리를\n * 가진 에너지 트윈에만 얹히고, 창고 트윈에는 얹을 자리가 없다 — 그것이 정상이다. 그래서 범위를 함께\n * 실어 보내고, 부르는 쪽이 현장 겹의 것은 경고로 올리지 않는다.\n */\n missingTargets: [\n ...first.missingTargets.map(t => ({ scope: 'space' as const, ...t })),\n ...second.missingTargets.map(t => ({ scope: 'instance' as const, ...t }))\n ]\n }\n}\n"]}
1
+ {"version":3,"file":"local-declarations.js","sourceRoot":"","sources":["../../server/engine/local-declarations.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 로컬 선언 — **구조는 원천이, 수는 우리가.**\n *\n * ── 낱말부터: `reference` ↔ `local` (2026-08-18 확정) ───────────────────────\n * 이 파일이 다루는 축은 **「이 값을 누가 말했나」** 하나다. 답은 둘뿐이다.\n *\n * · `reference` — 외부 원천 시스템이 준 값(SCADA·WMS·MES 의 마스터). 우리가 고르지 않았다.\n * · `local` — **이 트윈 쪽에서 정한 값.** 원천이 모르는 수를 운영 주체가 넣은 것이다.\n *\n * 「사람이 넣었나」가 아니라 **「권위가 어디 있나」**가 기준이다 — 앞으로 AI 가 사용자 대리로 채워도\n * 여전히 `local` 이다. 표식은 커널 계약의 `PropertySource` 로 선언돼 있고, **표식이 없으면 모델을 그린\n * 쪽**(참조에서 그렸으면 원천, 템플릿으로 만들었으면 그 템플릿)이다.\n *\n * 장소 낱말을 쓰지 않는 이유(옛 이름 `siteDeclarations` 를 버린 이유):\n * · `site` — 이 코드에서는 **외부 시스템이 부르는 사업장**이다(SAP 는 `P3`, Oracle 은 `W01` 로 같은\n * 현장을 부른다). 여기 담기는 것은 그 시스템이 **모르는** 값이므로 이름이 내용과 정반대다.\n * · `facility` — 화면에 나가는 영어(현장)이고, 코드에 남은 것은 남의 API 필드뿐이다(Oracle lgfapi).\n * · `space` — 「어디」에 답하는 낱말이다(트윈이 서는 그릇). 이 축은 「누가」에 답한다. 게다가 공간\n * 수준 선언(한 현장의 여러 트윈이 함께 읽는 요금)은 앞으로 정말 필요할 수 있어 비워 둔다.\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 커넥터로 만든 트윈은 구조를 원 시스템에서 읽어 온다(계통도·계량 지점·설비). 그런데 요금 단가·계약\n * 정보·축전지 충전율은 원 시스템이 모른다 — SCADA 게이트웨이는 우리 전기 계약을 알지 못한다. 그래서 그\n * 값들을 넣을 문이 없었고, 계량이 흐르는 트윈에서도 금액이 영원히 나오지 않았다(실제로 그 벽에 부딪혔다).\n *\n * ── 왜 모델에 바로 쓰지 않고 겹으로 두나 ────────────────────────────────────\n * 모델은 **원천의 사본**이다. 다시 읽기(resync)는 그 사본을 새로 그리므로, 모델에 직접 쓴 값은 다음\n * 다시 읽기에서 **오류 없이 사라진다** — 요금이 사라지면 금액도 사라지고, 사용자는 이유를 알 수 없다.\n * 그래서 로컬 선언은 따로 보관하고, 모델을 그릴 때마다 그 위에 얹는다. 덤으로 「누가 말한 값인가」에\n * 답할 수 있다 — 이 시스템이 근거를 값과 함께 다니게 하는 규율과 같다(`basis`·`derived` 와 한 결).\n *\n * ── 지어낸 속성을 받지 않는다 ───────────────────────────────────────────────\n * 커널이 읽는 속성만 받는다. 모르는 id 를 받아 두면 사용자는 값을 넣었다고 믿고 화면은 아무것도 바뀌지\n * 않는다 — 그 침묵이 가장 비싼 오류다. 단위·범위도 커널의 명세(`EMS_PROPERTY_SPEC`)로 검사한다:\n * 「SOC 80%」를 `0.8` 로 넣으면 배터리가 0.8% 로 서고, 그 사실을 아무도 모른 채 피크가 깎이지 않는다.\n *\n * **속성 어휘는 사용자가 정의하지 않는다** — 커널이 정한 속성에 값을 넣는 문이다(그래서 이름도\n * 「사용자 정의 속성」이 아니다).\n */\nimport { ATTENTION_PROPERTY, EMS_PROPERTY_SPEC, OPERATION_PROPERTY, OP_PARAM } from '@operato/twin-kernel'\n\nimport { EQUIPMENT_PROPERTY, SPEED_TO_MPS } from './travel-estimator.ts'\nimport { declarationTargetsOf } from './property-effects.ts'\n\n/** 로컬 선언이 겨눌 수 있는 대상 — 자원 둘과 **공정 종류**(자원이 아니라 명세다). */\nexport type DeclarationTarget = 'location' | 'equipment' | 'operation'\n\n/** 한 대상(자리·설비·공정)에 얹을 로컬 선언. */\nexport interface LocalDeclaration {\n target: DeclarationTarget\n /** 대상 id — 모델에 있는 그 이름. */\n id: string\n properties: {\n id: string\n value: string | number\n /**\n * 단위(UN/CEFACT 코드) — **어떤 값은 단위 없이는 뜻이 없다.**\n *\n * 속도가 그렇다: 단위가 없으면 이동 시간 추정기가 그 설비를 **거절한다**(그러면 공정 시간이 알리지 않고\n * 사라진다). 명세가 단위를 요구하면(`uomChoices`) 받고, 그 목록에 없는 코드는 거절한다 — 읽는 쪽이\n * 모르는 단위를 받아 두면 화면은 넣었다고 말하고 계산은 그 값을 버린다.\n */\n uom?: string\n /**\n * 이 선언이 **덮은 원천 값** — 철회하면 이것으로 되돌린다.\n *\n * ── 왜 기억해야 하나 (2026-08-18) ────────────────────────────────────────\n * 선언은 모델에 얹혀 **저장된 모델에 구워진다.** 그래서 겹에서 줄만 지워도 원래 값이 돌아오지\n * 않는다 — 실제로 되돌릴 방법이 없어서, 잘못 넣은 값을 「원래 값을 다시 선언하는」 우회로만\n * 고칠 수 있었다(사용자가 원래 값을 알고 있어야 한다는 뜻이다).\n *\n * · 문자열 — 원천이 그 자리에 갖고 있던 값이다.\n * · `null` — 원천에는 그 속성이 **없었다**(철회하면 속성을 지운다).\n * · 없음 — **우리가 모른다**(이 필드가 생기기 전에 선언된 것). 지어내지 않고 그렇게 말한다.\n *\n * 사용자가 넣는 값이 아니라 얹을 때 우리가 채우는 값이다(매 원천 재읽기마다 새로 채워져 낡지 않는다).\n */\n replaced?: string | null\n }[]\n}\n\nexport interface RejectedDeclaration {\n target: string\n id: string\n propertyId: string\n /**\n * 왜 받지 않았나 — 언어중립 코드(화면이 옮긴다).\n * · `unknown-property` — 커널이 읽지 않는 속성 id 다\n * · `out-of-range` — 명세가 정한 범위 밖이다(단위를 잘못 읽은 값이 대개 여기 걸린다)\n * · `not-a-number` — 수를 요구하는 속성에 수가 아닌 값이 왔다\n * · `empty-value` — 값이 비어 있다(지우려면 그 뜻을 따로 말해야 한다)\n * · `no-property-vocabulary` — 이 종류의 트윈에는 선언 어휘가 아직 없다\n * · `no-target-id` — 어느 대상에 얹을지 말하지 않았다(선언의 모양이 틀렸다)\n * · `no-property-id` — 어느 속성인지 말하지 않았다(선언의 모양이 틀렸다)\n *\n * 뒤의 둘이 있는 이유: 예전에는 대상 id·속성 id 가 비면 **아무 말 없이 건너뛰었다.** 그래서 필드 이름을\n * 틀린 호출이 「받아준 선언이 없다」는 답만 받고 `rejected` 는 비어 있었다 — 무엇이 틀렸는지 알 길이\n * 없는 침묵이다(실제로 그 침묵에 걸렸다).\n */\n reason:\n | 'unknown-property'\n | 'out-of-range'\n | 'not-a-number'\n | 'empty-value'\n | 'no-property-vocabulary'\n | 'no-target-id'\n | 'no-property-id'\n /** 단위를 요구하는 속성인데 오지 않았다(그 값은 읽는 쪽이 버린다). */\n | 'uom-required'\n /** 읽는 쪽이 모르는 단위 코드다(명세가 받는 목록에 없다). */\n | 'unknown-uom'\n /**\n * 이 속성은 그 종류의 대상에 얹히지 않는다 — 읽는 코드가 다른 축을 본다.\n *\n * 예전에는 지게차에 계약전력을 넣을 수 있었다(문은 받고 화면은 「넣었습니다」라고 말하는데 읽는\n * 쪽은 자리만 본다). 어느 축의 속성인지는 효과 등록부가 이미 알고 있으므로 그것으로 거절한다.\n */\n | 'wrong-target'\n /**\n * 그 이름의 공정을 이 트윈에서 확인할 수 없다 — 커널이 돌린 적도 없고 명세 행에도 없다.\n *\n * 공정 선언은 자리·설비와 달리 **모델에 행이 없어도 성립한다**(창고·야드에는 행이 아예 없다).\n * 그래서 오타를 잡아 줄 것이 이름 대조뿐이다 — 그마저 없으면 값은 알리지 않고 아무 일도 하지 않는다.\n */\n | 'unknown-operation'\n /** 무엇을 기대했나 — 사람이 고칠 수 있게(명세의 문장). */\n expected?: string\n}\n\n/** 이 종류의 트윈에서 커널이 읽는 선언 명세. 없으면 그 종류에는 선언 어휘가 없다. */\nfunction specOf(kind: string | undefined, propertyId: string): DeclarablePropertySpec | undefined {\n /* 종류 어휘와 공용 어휘를 함께 본다 — 속도는 어느 트윈에서나 읽히므로 EMS 밖에서도 받아야 한다. */\n return declarablePropertySpecs(kind).find(s => s.id === propertyId)\n}\n\n/** 이 종류의 트윈에 선언 어휘가 있나 — 없으면 검사할 근거가 없으므로 받지 않는다. */\nexport function hasPropertyVocabulary(kind: string | undefined): boolean {\n return kind === 'ems'\n}\n\n/** 이 종류에 선언할 수 있는 것이 하나라도 있나 — 공용 어휘가 있으므로 대개 참이다. */\nexport function hasAnyDeclarable(kind: string | undefined): boolean {\n return declarablePropertySpecs(kind).length > 0\n}\n\n/**\n * 이 종류의 트윈에 **선언할 수 있는 속성 목록** — 화면이 도메인의 모양을 알지 않게.\n *\n * 화면이 「에너지 트윈이면 계약전력·요금 단가…」를 알고 있으면, 도메인이 늘 때마다 화면을 고쳐야 하고\n * 단위·범위가 두 곳에 적힌다(ADR-0031: 카탈로그가 선언하고 화면은 그린다). 그래서 커널의 명세를 그대로\n * 낸다 — 화면은 받은 것을 렌더할 뿐이다.\n *\n * 어휘가 없는 종류는 **빈 목록**이다(「아직 없다」와 「아무 값이나 받는다」는 다르다).\n */\nexport interface DeclarablePropertySpec {\n id: string\n /** 사람이 읽는 단위(명세가 정한 것) — 값에 붙여 보인다. */\n uom?: string\n dataType: string\n range?: [number, number]\n note: string\n /**\n * 값과 **함께 단위를 골라야 하는** 속성 — 그 목록(UN/CEFACT 코드).\n *\n * 어떤 값은 단위 없이는 뜻이 없다: 속도는 단위가 없으면 추정기가 **거절한다**(「speed has no unit of\n * measure」). 그래서 명세가 「단위를 받아야 한다」와 「무엇을 받나」를 함께 말한다 — 화면이 목록을\n * 지어내면 화면이 받아 준 단위를 읽는 쪽이 모르는 일이 생긴다.\n */\n uomChoices?: string[]\n /**\n * 어느 종류의 대상에 얹히나(효과 등록부의 축) — 없으면 **우리가 제한을 모른다**(받아 준다).\n *\n * 화면은 이것으로 속성 목록을 대상에 맞게 좁힌다: 공정 시간을 설비에 넣으려는 선택지를 아예 보이지\n * 않게 하는 것이, 넣게 하고 거절하는 것보다 낫다.\n */\n targets?: string[]\n}\n\n/**\n * **어느 종류에나 있는 선언** — 「읽는 코드가 있는 것」만.\n *\n * 효과 등록부(`PROPERTY_EFFECTS`)가 그 목록의 근거다: 값이 계산을 바꾸는 속성만 여기 온다. 지금은\n * 설비의 `speed` 하나다(이동 시간 추정이 읽는다) — 자재의 `perBaseUnit` 은 자원 축이 아니라 자재\n * 정의에 얹히므로 이 화면의 대상이 아니다.\n *\n * 지어내지 않는다: 새 효과를 코드로 붙이는 사람이 등록부에 한 줄을 더하면 여기에도 따라 나온다.\n */\n/**\n * 시간 단위(UN/CEFACT) → ms. **목록은 여기 하나** — 화면·AI 는 명세의 `uomChoices` 로 받아 간다.\n *\n * 셋만 두는 이유: 현장이 공정 시간을 말할 때 쓰는 단위가 초·분·시간이다. 일 단위는 공정이 아니라 체류에\n * 가깝고, 그때는 그 값을 넣는 사람이 시간으로 환산해 말하는 편이 오해가 없다.\n */\nconst DURATION_UOM_MS: Record<string, number> = { SEC: 1_000, MIN: 60_000, HUR: 3_600_000 }\n\nconst COMMON_SPECS: DeclarablePropertySpec[] = [\n /*\n * 주의 판정 기준 — **경보가 소음이 되지 않게 현장이 정한다.**\n *\n * 혼잡 90%·불량 15%는 우리가 정한 수였다. 통로를 95%로 채워 쓰는 창고는 그 기준에서 영구히 「병목」\n * 이고, 초기 라인은 계속 「불량 과다」로 운다 — 그때 진짜 경보도 함께 묻힌다. 선언이 없으면 기본값을\n * 쓰되 주의가 `basis: 'default'` 로 그 사실을 밝힌다(알리지 않고 기본값을 진실로 두지 않는다).\n */\n {\n id: ATTENTION_PROPERTY.congestionRatio,\n uom: '%',\n dataType: 'xs:double',\n range: [1, 100],\n note: 'congestion threshold as a percentage 1-100 (95 means 95% of capacity) — above it this location is called a bottleneck. default 90.'\n },\n {\n id: ATTENTION_PROPERTY.scrapRate,\n uom: '%',\n dataType: 'xs:double',\n range: [1, 100],\n note: 'scrap threshold as a percentage 1-100 (25 means 25%) — above it this equipment is called high-scrap, and twice it is severe. default 15.'\n },\n {\n id: ATTENTION_PROPERTY.scrapMinSamples,\n dataType: 'xs:double',\n range: [1, 100000],\n note: 'how many pieces must be counted before the scrap rate is judged at all — a ratio over few pieces is not evidence. default 10.'\n },\n {\n id: EQUIPMENT_PROPERTY.speed,\n dataType: 'xs:double',\n range: [0.01, 50],\n note: 'travel speed of this equipment — the estimator turns distance into duration with it. a unit is required.',\n uomChoices: Object.keys(SPEED_TO_MPS)\n },\n /*\n * 공정 소요시간 — **측정이 없을 때 상수를 대신한다.**\n *\n * 대상은 자리·설비가 아니라 **공정 종류**(`putaway`·`pick`·`spot`…)이고, 그 종류에 명세 행이 없어도\n * 성립한다(실측: 창고·야드 트윈에는 행이 0개다). 단위를 반드시 받는다 — 「4」만으로는 4초인지 4분인지\n * 알 수 없고, 짐작하면 예측이 60배 틀린다.\n */\n {\n id: OPERATION_PROPERTY.duration,\n dataType: 'xs:double',\n range: [0.01, 100000],\n note: 'how long this operation takes at this site — the simulation and the forecast use it instead of the built-in constant. a unit is required. history still wins where it exists.',\n uomChoices: Object.keys(DURATION_UOM_MS)\n },\n /*\n * 양품률 — **퍼센트로 받는다**(80 이 80%다).\n *\n * 커널이 읽는 값은 0..1 이지만(ISA-95 `Parameter` 값 그대로) 사람에게 0.8 을 넣으라고 하면 SOC 에서\n * 겪은 함정을 되풀게 된다(0.8 을 0.8% 로 쓴 일이 있다). 그래서 화면·AI 는 퍼센트로 말하고, 비율로\n * 바꾸는 일은 얹는 자리 한 곳에서 한다.\n */\n {\n id: OPERATION_PROPERTY.yield,\n uom: '%',\n dataType: 'xs:double',\n range: [1, 100],\n note: 'good-output rate of this operation as a percentage 1-100 (95 means 95%) — the kernel reads it as a ratio, and this value replaces the built-in default that also drives the high-scrap attention.'\n },\n /* 셋업·체인지오버 — 가동 앞에 붙는 시간. 소요시간과 같은 단위 목록을 쓴다(같은 종류의 값이다). */\n {\n id: OPERATION_PROPERTY.setupDuration,\n dataType: 'xs:double',\n range: [0.01, 100000],\n note: 'changeover time before this operation runs — added in front of the cycle. a unit is required.',\n uomChoices: Object.keys(DURATION_UOM_MS)\n }\n]\n\n/**\n * 값 + 단위 → **ISO 8601 기간** — 커널이 읽는 표기로 바꾼다(커널은 초·분을 모른다, 표준 표기만 안다).\n *\n * 읽을 수 없으면 `undefined` — 부르는 쪽이 그 사실을 거절 사유로 올린다(0 을 만들지 않는다: 소요 0 인\n * 작업은 영원히 끝나지 않는다).\n */\n/**\n * 공정 모수 표에 한 칸 담는다 — **커널이 읽는 표기 그대로**(수율 0..1, 셋업 ISO 8601).\n *\n * 표를 통째로 새로 만들어 얹는다: 같은 모델 객체를 여러 겹이 지나므로(현장 → 트윈) 그때마다 새 객체여야\n * 앞 겹의 것을 알리지 않고 덮어쓰지 않는다.\n */\nfunction putParam(next: any, kind: string, paramId: string, value: string): void {\n const table = { ...(next.localParams ?? {}) }\n table[kind] = { ...(table[kind] ?? {}), [paramId]: value }\n next.localParams = table\n}\n\nexport function isoDurationOf(value: string | number, uom: string): string | undefined {\n const n = Number(value)\n const per = DURATION_UOM_MS[String(uom ?? '').trim()]\n if (!Number.isFinite(n) || n <= 0 || !per) return undefined\n return `PT${(n * per) / 1000}S`\n}\n\n/**\n * 이 종류의 트윈에 **선언할 수 있는 속성 목록** — 화면이 도메인의 모양을 알지 않게.\n *\n * 종류와 무관한 것(효과 등록부에서 온다) + 그 종류의 어휘(지금은 EMS 뿐). 어휘가 없는 종류도 **빈손이\n * 아니다**: 속도는 어느 트윈에서나 읽히므로 창고·야드·생산 트윈에서도 넣을 수 있어야 한다.\n */\nexport function declarablePropertySpecs(kind: string | undefined): DeclarablePropertySpec[] {\n const domain = hasPropertyVocabulary(kind)\n ? Object.entries(EMS_PROPERTY_SPEC as Record<string, any>).map(([id, spec]) => ({\n id,\n ...(spec.uom ? { uom: spec.uom } : {}),\n dataType: spec.dataType,\n ...(spec.range ? { range: spec.range as [number, number] } : {}),\n note: spec.note\n }))\n : []\n /*\n * **어느 대상에 얹히나는 효과 등록부가 답한다** — 명세에 손으로 또 적지 않는다(두 곳에 적으면 한쪽만\n * 바뀐다). 등록부에 없는 속성은 제한을 **모르는** 것이므로 그대로 둔다(속성은 열려 있다).\n */\n const all: DeclarablePropertySpec[] = [...COMMON_SPECS, ...domain]\n return all.map(s => {\n const targets = s.targets ?? declarationTargetsOf(s.id)\n return targets ? { ...s, targets } : s\n })\n}\n\n/**\n * 로컬 선언을 검사한다 — **받은 것과 받지 않은 것을 함께** 돌려준다.\n *\n * 알리지 않고 걸러내지 않는 이유: 사용자는 값을 넣었다고 믿는다. 무엇이 왜 빠졌는지 말해야 고칠 수 있다.\n */\nexport function validateLocalDeclarations(\n kind: string | undefined,\n declarations: readonly LocalDeclaration[] | undefined | null,\n opts?: {\n /**\n * 이 트윈에서 확인된 공정 이름들 — 있으면 그 밖의 이름을 거절한다(오타를 잡는 유일한 수단이다).\n *\n * **모르면 넘기지 않고 모른다고 둔다**: 아직 돌지 않은 트윈은 어떤 공정을 돌릴지 알 수 없으므로\n * 목록을 주지 않고(=undefined) 받아 준다. 부르는 쪽이 「확인하지 못했다」는 사실을 답에 싣는다.\n */\n knownOperations?: readonly string[]\n }\n): { accepted: LocalDeclaration[]; rejected: RejectedDeclaration[] } {\n const accepted: LocalDeclaration[] = []\n const rejected: RejectedDeclaration[] = []\n for (const d of declarations ?? []) {\n const target: DeclarationTarget = d?.target === 'equipment' ? 'equipment' : d?.target === 'operation' ? 'operation' : 'location'\n const id = String(d?.id ?? '').trim()\n if (!id) {\n /* 어느 대상인지 모르면 얹을 자리가 없다 — 그 사실을 말한다(알리지 않고 버리지 않는다). */\n rejected.push({ target, id: '', propertyId: '', reason: 'no-target-id' })\n continue\n }\n const props: { id: string; value: string }[] = []\n for (const p of d?.properties ?? []) {\n const propertyId = String(p?.id ?? '').trim()\n if (!propertyId) {\n rejected.push({ target, id, propertyId: '', reason: 'no-property-id' })\n continue\n }\n const raw = p?.value\n if (raw === undefined || raw === null || String(raw).trim() === '') {\n rejected.push({ target, id, propertyId, reason: 'empty-value' })\n continue\n }\n if (!hasAnyDeclarable(kind)) {\n rejected.push({ target, id, propertyId, reason: 'no-property-vocabulary' })\n continue\n }\n const spec = specOf(kind, propertyId)\n if (!spec) {\n rejected.push({ target, id, propertyId, reason: 'unknown-property' })\n continue\n }\n /*\n * **읽는 쪽이 보는 축에만 얹는다** — 지게차에 계약전력을 넣을 수 있던 구멍을 여기서 막는다.\n * 명세가 제한을 말하지 않으면(등록부에 없는 속성) 받아 준다: 속성은 열려 있고, 효과만 닫혀 있다.\n */\n if (spec.targets?.length && !spec.targets.includes(target)) {\n rejected.push({ target, id, propertyId, reason: 'wrong-target', expected: `one of ${spec.targets.join(', ')}` })\n continue\n }\n /* 공정 이름은 대조로만 걸러진다 — 모델에 행이 없어도 성립하는 선언이라 다른 안전망이 없다. */\n if (target === 'operation' && opts?.knownOperations && !opts.knownOperations.includes(id)) {\n rejected.push({ target, id, propertyId, reason: 'unknown-operation', expected: `one of ${[...opts.knownOperations].join(', ')}` })\n continue\n }\n if (spec.dataType === 'xs:double') {\n const n = Number(raw)\n if (!Number.isFinite(n)) {\n rejected.push({ target, id, propertyId, reason: 'not-a-number', expected: spec.note })\n continue\n }\n if (spec.range && (n < spec.range[0] || n > spec.range[1])) {\n rejected.push({ target, id, propertyId, reason: 'out-of-range', expected: spec.note })\n continue\n }\n }\n /*\n * 단위를 요구하는 속성은 **단위까지 검사한다** — 값만 받아 두면 읽는 쪽이 그 설비를 건너뛴다.\n * 목록은 명세가 든다(`uomChoices`): 화면이 목록을 지어내면 화면이 받아 준 단위를 계산이 모른다.\n */\n const uom = String((p as any)?.uom ?? '').trim()\n if (spec.uomChoices?.length) {\n if (!uom) {\n rejected.push({ target, id, propertyId, reason: 'uom-required', expected: `one of ${spec.uomChoices.join(', ')}` })\n continue\n }\n if (!spec.uomChoices.includes(uom)) {\n rejected.push({ target, id, propertyId, reason: 'unknown-uom', expected: `one of ${spec.uomChoices.join(', ')}` })\n continue\n }\n }\n props.push({ id: propertyId, value: String(raw), ...(uom ? { uom } : {}) })\n }\n if (props.length) accepted.push({ target, id, properties: props })\n }\n return { accepted, rejected }\n}\n\n/**\n * 겹을 합친다 — 같은 대상·같은 속성이면 **새 값이 이긴다**(현장이 값을 고치는 일은 흔하다).\n *\n * 대상이나 속성을 지우는 일은 여기서 하지 않는다: 지움은 「값을 모르는 상태로 되돌린다」는 뜻이고,\n * 그것은 값을 넣는 것과 다른 결정이므로 다른 문으로 말해야 한다.\n */\nexport function mergeLocalDeclarations(\n current: readonly LocalDeclaration[] | undefined | null,\n incoming: readonly LocalDeclaration[]\n): LocalDeclaration[] {\n const key = (t: string, id: string) => `${t}:${id}`\n const out = new Map<string, LocalDeclaration>()\n for (const d of current ?? []) out.set(key(d.target, d.id), { ...d, properties: [...d.properties] })\n for (const d of incoming) {\n const k = key(d.target, d.id)\n const cur = out.get(k)\n if (!cur) {\n out.set(k, { ...d, properties: [...d.properties] })\n continue\n }\n for (const p of d.properties) {\n const at = cur.properties.findIndex(x => x.id === p.id)\n if (at >= 0) cur.properties[at] = { ...p }\n else cur.properties.push({ ...p })\n }\n }\n return [...out.values()]\n}\n\n/**\n * 모델 위에 로컬 선언을 얹는다 — **모델을 바꾸지 않고 새 모델을 돌려준다.**\n *\n * 원천이 같은 속성을 이미 말했으면 **로컬 값이 이긴다**: 계약전력을 게이트웨이가 말하더라도, 우리가\n * 고쳐 넣었다는 것은 「원천의 값이 낡았다」는 뜻이다. 다만 그 사실이 보여야 하므로 `source: 'local'` 를\n * 값에 붙인다 — 화면·감사가 「누가 말한 값인가」를 되짚을 수 있다.\n *\n * 모델에 없는 대상에 얹으려 하면 **알리지 않고 버리지 않고** 그 사실을 돌려준다(구조가 바뀌어 그 설비가\n * 사라졌을 수 있고, 그때 사용자의 선언은 갈 곳이 없다).\n */\nexport function applyLocalDeclarations<\n T extends { locations?: any[]; equipment?: any[]; localDurations?: Record<string, string>; localParams?: Record<string, Record<string, string>> }\n>(\n model: T,\n declarations: readonly LocalDeclaration[] | undefined | null\n): {\n model: T\n missingTargets: { target: string; id: string }[]\n /**\n * 읽을 수 있는 값으로 바꿀 수 없었던 선언 — **알리지 않고 버리지 않기 위한 자리.**\n *\n * 지금은 공정 시간에서만 난다(단위가 없거나 수가 아니면 ISO 8601 기간을 만들 수 없다). 문에서 이미\n * 검사하므로 정상 경로에서는 비어 있고, 옛 겹이나 손으로 넣은 데이터에서 드러난다.\n */\n unreadable: { target: string; id: string; propertyId: string }[]\n layer: LocalDeclaration[]\n} {\n const decls = declarations ?? []\n if (!decls.length) return { model, missingTargets: [], unreadable: [], layer: [] }\n\n const missingTargets: { target: string; id: string }[] = []\n const unreadable: { target: string; id: string; propertyId: string }[] = []\n /* 얹으면서 **무엇을 덮었는지** 함께 적어 돌려준다 — 철회는 그 기억이 있어야 성립한다. */\n const layer: LocalDeclaration[] = decls.map(d => ({ ...d, properties: d.properties.map(p => ({ ...p })) }))\n\n const put = (list: any[] | undefined, d: LocalDeclaration, memo: LocalDeclaration): boolean => {\n const item = (list ?? []).find(x => String(x?.id ?? '') === d.id)\n if (!item) return false\n const props: any[] = Array.isArray(item.properties) ? [...item.properties] : []\n for (let i = 0; i < d.properties.length; i++) {\n const p = d.properties[i]\n const at = props.findIndex(x => String(x?.id ?? '') === p.id)\n const noted = memo.properties[i]\n /*\n * 기억은 **표식이 없는 값일 때만** 갱신한다 — ADR-0034 의 규칙 그대로: 표식이 없으면 모델을 그린\n * 쪽(대개 원천)이 말한 값이고, 표식이 있으면 **누군가 찍은 것**이므로 맨 원천 값이 아니다.\n *\n * 이 함수는 원천에서 새로 그린 모델에도, 이미 선언이 구워진 저장 모델에도 얹힌다. 뒤쪽에서\n * 무턱대고 적으면 **우리 값을 원천 값으로 기억**하게 되고(600 을 원천이 말한 것으로 적는다),\n * 철회해도 같은 값으로 되돌아간다.\n *\n * `!== 'local'` 로 보지 않는 이유: 옛 표식(`'site'`)이 구워진 모델이 남아 있고, 그것을 「표식\n * 아님」으로 읽으면 바로 그 오인이 일어난다. 호환 코드를 두는 것이 아니라 **규칙을 그대로 적는\n * 것**이다 — 표식의 종류를 따지지 않는다.\n */\n if (at < 0) noted.replaced = null\n else if (props[at]?.source === undefined) noted.replaced = String(props[at]?.value ?? '')\n /*\n * **단위도 함께 얹는다** (2026-08-18 실측으로 고침).\n *\n * 겹에는 단위를 담고도 모델에 얹을 때 떨어뜨리고 있었다 — 그러면 속도를 넣어도 이동 시간 추정기가\n * 그 설비를 **버린다**(「speed has no unit of measure」). 화면은 「넣었습니다」라고 말하고 계산은\n * 그 값을 못 쓰는, 가장 들어온 것이 없는 종류의 결함이다.\n */\n const next = { id: p.id, value: String(p.value), ...((p as any).uom ? { uom: String((p as any).uom) } : {}), source: 'local' as const }\n if (at >= 0) props[at] = { ...props[at], ...next }\n else props.push(next)\n }\n item.properties = props\n return true\n }\n\n /**\n * 공정 시간은 **자원 속성이 아니다** — 종류별 표(`localDurations`)에 담는다.\n *\n * 왜 자리·설비처럼 속성으로 담지 않나: 커널의 공정 명세 행(`OperationDef`)은 `label`·`intent` 를\n * 요구하는데(ISA-95 `OperationsSegment`) 창고·야드 트윈에는 그 행이 **아예 없다**(실측: 35개 중\n * WMS·YMS 전부 0개). 행을 지어 만들면 지어낸 `intent` 가 능력 계산까지 오염시킨다 — 「몇 분 걸리나」를\n * 말하려고 「무슨 작업인가」를 발명하지 않는다.\n *\n * 이 표는 **전부 우리가 정한 값**이다(원천이 그리는 자리가 아니다). 그래서 기억(`replaced`)은 언제나\n * `null` 이고, 철회는 「표에서 뺀다」로 끝난다 — 그러면 원천 명세나 커널 상수가 다시 답한다.\n */\n const putOperation = (next: any, d: LocalDeclaration, memo: LocalDeclaration): void => {\n for (let i = 0; i < d.properties.length; i++) {\n const p = d.properties[i]\n const uom = String((p as any).uom ?? '')\n /*\n * 값을 **커널이 읽는 표기로** 바꿔 담는다 — 시간은 ISO 8601, 수율은 비율(0..1).\n * 바꿀 수 없으면 담지 않고 그 사실을 낸다(알리지 않고 0 이나 빈 값으로 만들지 않는다).\n */\n if (p.id === OPERATION_PROPERTY.duration) {\n const iso = isoDurationOf(p.value, uom)\n if (!iso) { unreadable.push({ target: 'operation', id: d.id, propertyId: p.id }); continue }\n next.localDurations = { ...(next.localDurations ?? {}), [d.id]: iso }\n memo.properties[i].replaced = null\n continue\n }\n if (p.id === OPERATION_PROPERTY.setupDuration) {\n const iso = isoDurationOf(p.value, uom)\n if (!iso) { unreadable.push({ target: 'operation', id: d.id, propertyId: p.id }); continue }\n putParam(next, d.id, OP_PARAM.setupDuration, iso)\n memo.properties[i].replaced = null\n continue\n }\n if (p.id === OPERATION_PROPERTY.yield) {\n const pct = Number(p.value)\n if (!Number.isFinite(pct) || pct <= 0 || pct > 100) { unreadable.push({ target: 'operation', id: d.id, propertyId: p.id }); continue }\n /* 퍼센트 → 비율. 이 환산이 있는 자리는 여기 하나다(두 곳에서 나누면 100 배가 두 번 걸린다). */\n putParam(next, d.id, OP_PARAM.yield, String(pct / 100))\n memo.properties[i].replaced = null\n continue\n }\n unreadable.push({ target: 'operation', id: d.id, propertyId: p.id })\n }\n }\n\n /* 얕은 복사로는 안 된다 — 자리·설비 항목을 손대므로 그 배열과 항목까지 새로 만든다. */\n const next: any = {\n ...model,\n ...(model.locations ? { locations: model.locations.map(n => ({ ...n, ...(n?.properties ? { properties: [...n.properties] } : {}) })) } : {}),\n ...(model.equipment ? { equipment: model.equipment.map(e => ({ ...e, ...(e?.properties ? { properties: [...e.properties] } : {}) })) } : {}),\n ...(model.localDurations ? { localDurations: { ...model.localDurations } } : {}),\n ...(model.localParams ? { localParams: Object.fromEntries(Object.entries(model.localParams).map(([k, v]) => [k, { ...v }])) } : {})\n }\n for (let i = 0; i < decls.length; i++) {\n const d = decls[i]\n if (d.target === 'operation') {\n /* 빈 표를 남기지 않는다 — 「선언이 있다」와 「읽을 수 있는 선언이 하나도 없다」는 다른 사실이다\n (`putOperation` 이 담을 것이 있을 때만 표를 만든다). */\n putOperation(next, d, layer[i])\n continue\n }\n const ok = put(d.target === 'equipment' ? next.equipment : next.locations, d, layer[i])\n if (!ok) missingTargets.push({ target: d.target, id: d.id })\n }\n return { model: next as T, missingTargets, unreadable, layer }\n}\n\n/** 철회 요청 하나 — 어느 대상의 어느 속성을 원천으로 되돌리나. */\nexport interface DeclarationRevocation {\n target: DeclarationTarget\n id: string\n propertyId: string\n}\n\n/** 철회된 항목 — 되돌릴 값을 함께 든다(모르면 `replaced` 가 없다). */\nexport interface RevokedDeclaration extends DeclarationRevocation {\n replaced?: string | null\n}\n\n/**\n * 겹에서 선언을 **뺀다** — 값을 넣는 것과 다른 결정이므로 다른 문이다.\n *\n * 없는 것을 뺐다고 말하지 않는다: 겹에 없던 대상·속성은 `notFound` 로 돌려준다(알리지 않고 성공하면\n * 사용자는 지웠다고 믿고 값은 그대로 남는다 — 가장 비싼 종류의 침묵이다).\n *\n * 속성이 하나도 남지 않은 대상은 겹에서 사라진다(빈 껍데기를 남기지 않는다).\n */\nexport function revokeLocalDeclarations(\n current: readonly LocalDeclaration[] | undefined | null,\n revocations: readonly DeclarationRevocation[] | undefined | null\n): { layer: LocalDeclaration[]; revoked: RevokedDeclaration[]; notFound: DeclarationRevocation[] } {\n const layer: LocalDeclaration[] = (current ?? []).map(d => ({ ...d, properties: d.properties.map(p => ({ ...p })) }))\n const revoked: RevokedDeclaration[] = []\n const notFound: DeclarationRevocation[] = []\n\n for (const r of revocations ?? []) {\n const target: DeclarationTarget = r?.target === 'equipment' ? 'equipment' : r?.target === 'operation' ? 'operation' : 'location'\n const id = String(r?.id ?? '').trim()\n const propertyId = String(r?.propertyId ?? '').trim()\n if (!id || !propertyId) {\n notFound.push({ target, id, propertyId })\n continue\n }\n const d = layer.find(x => x.target === target && x.id === id)\n const at = d ? d.properties.findIndex(p => p.id === propertyId) : -1\n if (!d || at < 0) {\n notFound.push({ target, id, propertyId })\n continue\n }\n const [gone] = d.properties.splice(at, 1)\n revoked.push({ target, id, propertyId, ...('replaced' in gone ? { replaced: gone.replaced } : {}) })\n }\n\n return { layer: layer.filter(d => d.properties.length), revoked, notFound }\n}\n\n/**\n * 철회한 속성을 **모델에서 원천 값으로 되돌린다** — 저장된 모델에 값이 구워져 있기 때문이다.\n *\n * · 기억한 값이 있으면 그 값으로 되돌리고 `source: 'local'` 표식을 뗀다(다시 원천이 말한 값이다).\n * · `null` 이면 원천에 없던 속성이므로 **지운다**.\n * · 기억이 없으면(옛 선언) **손대지 않고** `unknownSource` 로 말한다 — 원래 값을 지어내는 것은\n * 잘못 넣은 값을 그대로 두는 것보다 나쁘다(다음 원천 재읽기가 그 자리를 바로잡는다).\n */\nexport function restoreRevoked<\n T extends { locations?: any[]; equipment?: any[]; localDurations?: Record<string, string>; localParams?: Record<string, Record<string, string>> }\n>(\n model: T,\n revoked: readonly RevokedDeclaration[]\n): { model: T; restored: RevokedDeclaration[]; unknownSource: DeclarationRevocation[]; missingTargets: { target: string; id: string }[] } {\n if (!revoked.length) return { model, restored: [], unknownSource: [], missingTargets: [] }\n\n const next: any = {\n ...model,\n ...(model.locations ? { locations: model.locations.map(n => ({ ...n, ...(n?.properties ? { properties: [...n.properties] } : {}) })) } : {}),\n ...(model.equipment ? { equipment: model.equipment.map(e => ({ ...e, ...(e?.properties ? { properties: [...e.properties] } : {}) })) } : {}),\n ...(model.localDurations ? { localDurations: { ...model.localDurations } } : {}),\n ...(model.localParams ? { localParams: Object.fromEntries(Object.entries(model.localParams).map(([k, v]) => [k, { ...v }])) } : {})\n }\n const restored: RevokedDeclaration[] = []\n const unknownSource: DeclarationRevocation[] = []\n const missingTargets: { target: string; id: string }[] = []\n\n for (const r of revoked) {\n /*\n * 공정 시간은 표에서 **뺀다** — 되돌릴 원천 값이 없는 자리다(전부 우리가 정한 값이었다).\n * 빼면 원천 명세(있으면)나 커널 상수가 다시 답한다: 그것이 「선언 이전」의 상태다.\n */\n if (r.target === 'operation') {\n /* 시간은 시간 표에서, 모수(수율·셋업)는 모수 표에서 뺀다 — 담긴 자리에서 빼야 실제로 빠진다. */\n const paramId =\n r.propertyId === OPERATION_PROPERTY.yield\n ? OP_PARAM.yield\n : r.propertyId === OPERATION_PROPERTY.setupDuration\n ? OP_PARAM.setupDuration\n : undefined\n if (paramId) {\n const params: Record<string, Record<string, string>> = next.localParams ?? {}\n if (!params[r.id] || !(paramId in params[r.id])) {\n missingTargets.push({ target: r.target, id: r.id })\n continue\n }\n delete params[r.id][paramId]\n /* 빈 껍데기를 남기지 않는다 — 「선언이 있다」와 「빈 표가 있다」가 구별되지 않는다. */\n if (!Object.keys(params[r.id]).length) delete params[r.id]\n if (!Object.keys(params).length) delete next.localParams\n else next.localParams = params\n restored.push(r)\n continue\n }\n const table: Record<string, string> = next.localDurations ?? {}\n if (!(r.id in table)) {\n missingTargets.push({ target: r.target, id: r.id })\n continue\n }\n delete table[r.id]\n if (!Object.keys(table).length) delete next.localDurations\n else next.localDurations = table\n restored.push(r)\n continue\n }\n const list: any[] = (r.target === 'equipment' ? next.equipment : next.locations) ?? []\n const item = list.find(x => String(x?.id ?? '') === r.id)\n if (!item) {\n missingTargets.push({ target: r.target, id: r.id })\n continue\n }\n if (!('replaced' in r)) {\n unknownSource.push({ target: r.target, id: r.id, propertyId: r.propertyId })\n continue\n }\n const props: any[] = Array.isArray(item.properties) ? [...item.properties] : []\n const at = props.findIndex(x => String(x?.id ?? '') === r.propertyId)\n if (r.replaced === null) {\n if (at >= 0) props.splice(at, 1)\n } else if (at >= 0) {\n const { source: _dropped, ...rest } = props[at] ?? {}\n props[at] = { ...rest, value: String(r.replaced) }\n } else {\n /* 겹은 있었는데 모델에는 없다 — 원천 값을 되살려 넣는다(우리가 지웠을 수 있다). */\n props.push({ id: r.propertyId, value: String(r.replaced) })\n }\n item.properties = props\n restored.push(r)\n }\n\n return { model: next as T, restored, unknownSource, missingTargets }\n}\n\n/** 선언이 어느 범위의 것인가 — 좁은 것이 이긴다(`space` 먼저, `instance` 나중). */\nexport type DeclarationScope = 'space' | 'instance'\n\n/**\n * 두 겹을 **순서대로** 얹는다 — 현장 먼저, 트윈이 나중.\n *\n * ── 왜 순서가 규율인가 (2026-08-18) ─────────────────────────────────────────\n * 요금 단가는 현장의 계약이므로 그 현장의 트윈 전부가 같은 값을 읽어야 한다. 그런데 한 트윈만 다른\n * 값으로 실험하는 일이 있다(인상된 단가로 what-if). 그때 현장 값을 고치게 하면 **나머지 트윈이 함께\n * 흔들린다.** 그래서 더 좁은 선언이 이긴다 — 넓은 값은 그대로 두고 좁은 자리에서만 달라진다.\n *\n * 기억(`replaced`)은 각 겹이 **자기가 덮은 값**을 적는다: 트윈 겹의 기억은 「현장 값을 덮었다」가 되고,\n * 철회하면 현장 값으로 돌아간다(원천까지 가지 않는다). 그것이 사람이 기대하는 되돌림이다.\n */\nexport function applyDeclarationLayers<\n T extends { locations?: any[]; equipment?: any[]; localDurations?: Record<string, string>; localParams?: Record<string, Record<string, string>> }\n>(\n model: T,\n spaceLayer: readonly LocalDeclaration[] | undefined | null,\n instanceLayer: readonly LocalDeclaration[] | undefined | null\n): {\n model: T\n /** 현장 겹을 얹은 결과 — 저장해야 기억이 낡지 않는다. */\n spaceLayerApplied: LocalDeclaration[]\n instanceLayerApplied: LocalDeclaration[]\n /** 갈 곳이 없던 대상 — 어느 범위에서 났는지 함께 낸다(현장 겹은 그 트윈에 없을 수 있다). */\n missingTargets: { scope: DeclarationScope; target: string; id: string }[]\n /** 읽을 수 있는 값으로 바꿀 수 없던 선언 — 범위와 함께(알리지 않고 버리지 않는다). */\n unreadable: { scope: DeclarationScope; target: string; id: string; propertyId: string }[]\n} {\n const first = applyLocalDeclarations(model, spaceLayer)\n const second = applyLocalDeclarations(first.model, instanceLayer)\n return {\n model: second.model,\n spaceLayerApplied: first.layer,\n instanceLayerApplied: second.layer,\n unreadable: [\n ...first.unreadable.map(u => ({ scope: 'space' as const, ...u })),\n ...second.unreadable.map(u => ({ scope: 'instance' as const, ...u }))\n ],\n /*\n * **현장 겹의 「갈 곳 없음」은 오류가 아니다.**\n *\n * 한 현장에는 여러 종류의 트윈이 산다(창고·야드·에너지). 요금을 현장에 선언하면 그 값은 수전 자리를\n * 가진 에너지 트윈에만 얹히고, 창고 트윈에는 얹을 자리가 없다 — 그것이 정상이다. 그래서 범위를 함께\n * 실어 보내고, 부르는 쪽이 현장 겹의 것은 경고로 올리지 않는다.\n */\n missingTargets: [\n ...first.missingTargets.map(t => ({ scope: 'space' as const, ...t })),\n ...second.missingTargets.map(t => ({ scope: 'instance' as const, ...t }))\n ]\n }\n}\n"]}
@@ -9,7 +9,7 @@ export declare function stamp(at?: Date): string;
9
9
  * 인자 여럿을 한 줄로 — **어떤 인자도 버리지 않는다.**
10
10
  *
11
11
  * `console.error(msg, err)` 처럼 쓰던 자리가 많다. 프레임워크 로거에 그대로 넘기면 두 번째 인자는
12
- * meta 로 들어가 우리 형식(`message` 만 찍는다)에서 **조용히 사라진다.** 그래서 여기서 문자열로 편다.
12
+ * meta 로 들어가 우리 형식(`message` 만 찍는다)에서 **오류 없이 사라진다.** 그래서 여기서 문자열로 편다.
13
13
  * 오류는 스택까지 — 스택이 없으면 「어디서 났나」를 잃는다.
14
14
  */
15
15
  export declare function line(args: unknown[]): string;
@@ -46,7 +46,7 @@ function stamp(at = new Date()) {
46
46
  * 인자 여럿을 한 줄로 — **어떤 인자도 버리지 않는다.**
47
47
  *
48
48
  * `console.error(msg, err)` 처럼 쓰던 자리가 많다. 프레임워크 로거에 그대로 넘기면 두 번째 인자는
49
- * meta 로 들어가 우리 형식(`message` 만 찍는다)에서 **조용히 사라진다.** 그래서 여기서 문자열로 편다.
49
+ * meta 로 들어가 우리 형식(`message` 만 찍는다)에서 **오류 없이 사라진다.** 그래서 여기서 문자열로 편다.
50
50
  * 오류는 스택까지 — 스택이 없으면 「어디서 났나」를 잃는다.
51
51
  */
52
52
  function line(args) {
@@ -1 +1 @@
1
- {"version":3,"file":"log.js","sourceRoot":"","sources":["../../server/engine/log.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 트윈 로그의 문 — **모든 줄에 시각을 찍는다.** 순수(프레임워크 의존 없음).\n *\n * ── 왜 필요한가 (2026-08-22) ────────────────────────────────────────────────\n * 「서버가 왜 느린가」를 조사하면서 루프가 55초 막힌 것을 계기로 알아냈는데(`loop-lag.ts`),\n * **그 시각에 무슨 일이 있었는지 로그에서 찾을 수 없었다.** 트윈의 로그가 `console.log` 로 나가\n * 시각이 없었기 때문이다. 프레임워크 로그(`2026-08-22T05:54:12+09:00 info: …`)와 섞여 있어서, 어느\n * 줄이 언제 찍혔는지 앞뒤 프레임워크 줄로 짐작해야 했다.\n *\n * ── 왜 프레임워크 로거를 그대로 쓰지 않나 ──────────────────────────────────\n * 재발명을 피하려고 `@things-factory/env` 의 `logger` 를 쓰려 했는데, 실측으로 막혔다: 앱 **밖**에서는\n * 그 로거에 전송지가 하나도 없다(설정이 앱에서 주입된다). 그래서 시험·도구에서 부르면\n *\n * [winston] Attempt to write logs with no transports … {\"message\":\"…\",\"level\":\"info\"}\n *\n * 가 나오고 **메시지 자체가 사라진다.** 트윈 엔진은 시험과 CLI 도구에서도 불려 다니므로, 그 조건에서\n * 로그가 없어지는 것은 바꿔선 안 되는 거동이다(로그가 사라지는 것이 지금 문제의 원인이었다).\n *\n * 그래서 **싱크는 그대로 두고**(console) 빠진 사실 하나만 채운다 — 시각. 형식은 프레임워크와 같은\n * 모양으로 맞춰 한 파일에서 섞여 읽히게 하고, **밀리초까지** 남긴다(계기의 값이 epoch ms 라\n * 초 단위로는 같은 초 안의 순서를 가릴 수 없다).\n */\n\n/** 두 자리·세 자리 0 채우기 — 형식이 흔들리면 정렬해 읽을 수 없다. */\nconst p2 = (n: number) => String(n).padStart(2, '0')\nconst p3 = (n: number) => String(n).padStart(3, '0')\n\n/**\n * 지역 시각 + 오프셋 — `2026-08-22T05:54:12.345+09:00`.\n *\n * UTC 로 찍지 않는다: 이 로그를 읽는 사람은 현장의 시각으로 이야기하고, 프레임워크의 다른 줄도\n * 지역 시각이다. 두 줄이 다른 기준이면 나란히 놓고 읽을 수 없다.\n */\nexport function stamp(at: Date = new Date()): string {\n const off = -at.getTimezoneOffset()\n const sign = off >= 0 ? '+' : '-'\n const abs = Math.abs(off)\n return (\n `${at.getFullYear()}-${p2(at.getMonth() + 1)}-${p2(at.getDate())}` +\n `T${p2(at.getHours())}:${p2(at.getMinutes())}:${p2(at.getSeconds())}.${p3(at.getMilliseconds())}` +\n `${sign}${p2(Math.floor(abs / 60))}:${p2(abs % 60)}`\n )\n}\n\n/**\n * 인자 여럿을 한 줄로 — **어떤 인자도 버리지 않는다.**\n *\n * `console.error(msg, err)` 처럼 쓰던 자리가 많다. 프레임워크 로거에 그대로 넘기면 두 번째 인자는\n * meta 로 들어가 우리 형식(`message` 만 찍는다)에서 **조용히 사라진다.** 그래서 여기서 문자열로 편다.\n * 오류는 스택까지 — 스택이 없으면 「어디서 났나」를 잃는다.\n */\nexport function line(args: unknown[]): string {\n return args\n .map(a => {\n if (typeof a === 'string') return a\n if (a instanceof Error) return a.stack ?? a.message\n if (a === undefined) return 'undefined'\n if (a === null) return 'null'\n try {\n return typeof a === 'object' ? JSON.stringify(a) : String(a)\n } catch {\n /* 순환 참조 등 — 값을 못 적어도 **무엇이 있었다는 사실**은 남긴다. */\n return String(a)\n }\n })\n .join(' ')\n}\n\n/* 수준 표기는 프레임워크와 같은 낱말을 쓴다(`info`·`warn`·`error`) — 같은 파일에서 걸러 읽는다. */\nexport const twinLog = (...args: unknown[]): void => console.log(`${stamp()} info: ${line(args)}`)\nexport const twinWarn = (...args: unknown[]): void => console.warn(`${stamp()} warn: ${line(args)}`)\nexport const twinError = (...args: unknown[]): void => console.error(`${stamp()} error: ${line(args)}`)\n"]}
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../server/engine/log.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AAYH,sBASC;AASD,oBAeC;AA3CD,8CAA8C;AAC9C,MAAM,EAAE,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AACpD,MAAM,EAAE,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAEpD;;;;;GAKG;AACH,SAAgB,KAAK,CAAC,KAAW,IAAI,IAAI,EAAE;IACzC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAA;IACnC,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;IACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACzB,OAAO,CACL,GAAG,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE;QAClE,IAAI,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,EAAE;QACjG,GAAG,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,CACrD,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,IAAI,CAAC,IAAe;IAClC,OAAO,IAAI;SACR,GAAG,CAAC,CAAC,CAAC,EAAE;QACP,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAA;QACnC,IAAI,CAAC,YAAY,KAAK;YAAE,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAA;QACnD,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,WAAW,CAAA;QACvC,IAAI,CAAC,KAAK,IAAI;YAAE,OAAO,MAAM,CAAA;QAC7B,IAAI,CAAC;YACH,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,8CAA8C;YAC9C,OAAO,MAAM,CAAC,CAAC,CAAC,CAAA;QAClB,CAAC;IACH,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CAAA;AACd,CAAC;AAED,sEAAsE;AAC/D,MAAM,OAAO,GAAG,CAAC,GAAG,IAAe,EAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,EAAE,UAAU,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAArF,QAAA,OAAO,WAA8E;AAC3F,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAe,EAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,EAAE,UAAU,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAAvF,QAAA,QAAQ,YAA+E;AAC7F,MAAM,SAAS,GAAG,CAAC,GAAG,IAAe,EAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,EAAE,WAAW,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAA1F,QAAA,SAAS,aAAiF","sourcesContent":["/*\n * 트윈 로그의 문 — **모든 줄에 시각을 찍는다.** 순수(프레임워크 의존 없음).\n *\n * ── 왜 필요한가 (2026-08-22) ────────────────────────────────────────────────\n * 「서버가 왜 느린가」를 조사하면서 루프가 55초 막힌 것을 계기로 알아냈는데(`loop-lag.ts`),\n * **그 시각에 무슨 일이 있었는지 로그에서 찾을 수 없었다.** 트윈의 로그가 `console.log` 로 나가\n * 시각이 없었기 때문이다. 프레임워크 로그(`2026-08-22T05:54:12+09:00 info: …`)와 섞여 있어서, 어느\n * 줄이 언제 찍혔는지 앞뒤 프레임워크 줄로 짐작해야 했다.\n *\n * ── 왜 프레임워크 로거를 그대로 쓰지 않나 ──────────────────────────────────\n * 재발명을 피하려고 `@things-factory/env` 의 `logger` 를 쓰려 했는데, 실측으로 막혔다: 앱 **밖**에서는\n * 그 로거에 전송지가 하나도 없다(설정이 앱에서 주입된다). 그래서 시험·도구에서 부르면\n *\n * [winston] Attempt to write logs with no transports … {\"message\":\"…\",\"level\":\"info\"}\n *\n * 가 나오고 **메시지 자체가 사라진다.** 트윈 엔진은 시험과 CLI 도구에서도 불려 다니므로, 그 조건에서\n * 로그가 없어지는 것은 바꿔선 안 되는 거동이다(로그가 사라지는 것이 지금 문제의 원인이었다).\n *\n * 그래서 **싱크는 그대로 두고**(console) 빠진 사실 하나만 채운다 — 시각. 형식은 프레임워크와 같은\n * 모양으로 맞춰 한 파일에서 섞여 읽히게 하고, **밀리초까지** 남긴다(계기의 값이 epoch ms 라\n * 초 단위로는 같은 초 안의 순서를 가릴 수 없다).\n */\n\n/** 두 자리·세 자리 0 채우기 — 형식이 흔들리면 정렬해 읽을 수 없다. */\nconst p2 = (n: number) => String(n).padStart(2, '0')\nconst p3 = (n: number) => String(n).padStart(3, '0')\n\n/**\n * 지역 시각 + 오프셋 — `2026-08-22T05:54:12.345+09:00`.\n *\n * UTC 로 찍지 않는다: 이 로그를 읽는 사람은 현장의 시각으로 이야기하고, 프레임워크의 다른 줄도\n * 지역 시각이다. 두 줄이 다른 기준이면 나란히 놓고 읽을 수 없다.\n */\nexport function stamp(at: Date = new Date()): string {\n const off = -at.getTimezoneOffset()\n const sign = off >= 0 ? '+' : '-'\n const abs = Math.abs(off)\n return (\n `${at.getFullYear()}-${p2(at.getMonth() + 1)}-${p2(at.getDate())}` +\n `T${p2(at.getHours())}:${p2(at.getMinutes())}:${p2(at.getSeconds())}.${p3(at.getMilliseconds())}` +\n `${sign}${p2(Math.floor(abs / 60))}:${p2(abs % 60)}`\n )\n}\n\n/**\n * 인자 여럿을 한 줄로 — **어떤 인자도 버리지 않는다.**\n *\n * `console.error(msg, err)` 처럼 쓰던 자리가 많다. 프레임워크 로거에 그대로 넘기면 두 번째 인자는\n * meta 로 들어가 우리 형식(`message` 만 찍는다)에서 **오류 없이 사라진다.** 그래서 여기서 문자열로 편다.\n * 오류는 스택까지 — 스택이 없으면 「어디서 났나」를 잃는다.\n */\nexport function line(args: unknown[]): string {\n return args\n .map(a => {\n if (typeof a === 'string') return a\n if (a instanceof Error) return a.stack ?? a.message\n if (a === undefined) return 'undefined'\n if (a === null) return 'null'\n try {\n return typeof a === 'object' ? JSON.stringify(a) : String(a)\n } catch {\n /* 순환 참조 등 — 값을 못 적어도 **무엇이 있었다는 사실**은 남긴다. */\n return String(a)\n }\n })\n .join(' ')\n}\n\n/* 수준 표기는 프레임워크와 같은 낱말을 쓴다(`info`·`warn`·`error`) — 같은 파일에서 걸러 읽는다. */\nexport const twinLog = (...args: unknown[]): void => console.log(`${stamp()} info: ${line(args)}`)\nexport const twinWarn = (...args: unknown[]): void => console.warn(`${stamp()} warn: ${line(args)}`)\nexport const twinError = (...args: unknown[]): void => console.error(`${stamp()} error: ${line(args)}`)\n"]}
@@ -19,7 +19,7 @@
19
19
  * - 표본이 적으면 쓰지 않는다(`minSamples`). 한두 건의 p50 은 그 현장의 값이 아니다.
20
20
  * - 'unknown' 축은 쓰지 않는다 — 종류를 모르는 기록을 특정 종류의 근거로 삼을 수 없다.
21
21
  * - 퍼짐이 성립하지 않으면(p10 ≥ p90, 또는 0) **퍼짐 없이 평균만** 넘긴다. 없는 흔들림을 만들지 않는다.
22
- * - 무엇을 배웠고 무엇을 표본 부족으로 버렸는지 함께 낸다(조용히 일부만 반영하지 않는다).
22
+ * - 무엇을 배웠고 무엇을 표본 부족으로 버렸는지 함께 낸다(알리지 않고 일부만 반영하지 않는다).
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.DEFAULT_MIN_SAMPLES = void 0;
@@ -1 +1 @@
1
- {"version":3,"file":"measured-estimator.js","sourceRoot":"","sources":["../../server/engine/measured-estimator.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AA4BH,wDAyCC;AAhDD,mEAAmE;AACtD,QAAA,mBAAmB,GAAG,CAAC,CAAA;AAEpC;;;GAGG;AACH,SAAgB,sBAAsB,CACpC,MAAkH,EAClH,OAAgC,EAAE;IAElC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,2BAAmB,CAAA;IACzD,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,MAAM,OAAO,GAAqD,EAAE,CAAA;IACpE,MAAM,SAAS,GAAqC,EAAE,CAAA;IACtD,MAAM,OAAO,GAAsC,EAAE,CAAA;IACrD,KAAK,MAAM,CAAC,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;YAAE,SAAQ;QAC5C,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAA;QACpB,MAAM,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,CAAA;QAC3B,MAAM,GAAG,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,CAAA;QACzB,IAAI,KAAK,GAAG,UAAU,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;YAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;YAAC,SAAQ;QAAC,CAAC;QACxF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA;QACrB,6DAA6D;QAC7D,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAA;QACnB,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAA;QACnB,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;YAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC5B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;YACjC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAA;QACzG,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QACrC,CAAC;IACH,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAA;IAClF,OAAO;QACL,OAAO;QACP,OAAO;QACP,OAAO;QACP,UAAU;QACV,SAAS,EAAE;YACT,QAAQ,CAAC,GAAG;gBACV,OAAO,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAC5B,CAAC;SACF;KACF,CAAA;AACH,CAAC","sourcesContent":["/*\n * 실측 소요 추정기 — **이력에서 배운 값이 상수·선언값을 이긴다.**\n *\n * 배경(operato-twin design/plans/simulation-spec.md §5-4): 소요시간을 명세로 받을 수 있게 됐지만\n * 명세는 선언이다 — 그 현장에서 실제로 얼마 걸리는지는 저널이 안다. `gap-analytics` 는 지표 수준의\n * 편향만 배웠고(예측값 보정), **공정별 소요를 추정기로 되먹이는 경로는 없었다.**\n *\n * 여기서 하는 일: KPI 폴드가 작업 종류별로 낸 **작업시간(착수→완료) p50** 을 그 종류의 소요로 쓴다.\n * 리드타임(생성→완료)이 아니라 작업시간을 쓰는 이유: 리드타임에는 **대기가 섞여 있고**, 대기는\n * 시뮬레이션이 자원 경합으로 스스로 만들어야 하는 값이다. 대기를 소요에 넣으면 이중 계산이 된다.\n *\n * **평균만 배우면 대기와 병목을 과소평가한다.** 소요가 늘 정확히 같으면 줄이 생기지 않는다 — 현장의\n * 줄은 흔들림에서 나온다. 그래서 p50 만이 아니라 **관측된 퍼짐(p10..p90)** 을 함께 넘기고, 표본은\n * 커널이 자기 난수로 뽑는다(fork 결정성). 양쪽 꼬리를 다 관측에서 가져오는 이유: p90 만 있으면\n * 아래쪽을 위쪽에서 거울처럼 베껴야 하는데 그건 발명이다.\n *\n * 정직 규율:\n * - 표본이 적으면 쓰지 않는다(`minSamples`). 한두 건의 p50 은 그 현장의 값이 아니다.\n * - 'unknown' 축은 쓰지 않는다 — 종류를 모르는 기록을 특정 종류의 근거로 삼을 수 없다.\n * - 퍼짐이 성립하지 않으면(p10 ≥ p90, 또는 0) **퍼짐 없이 평균만** 넘긴다. 없는 흔들림을 만들지 않는다.\n * - 무엇을 배웠고 무엇을 표본 부족으로 버렸는지 함께 낸다(조용히 일부만 반영하지 않는다).\n */\n\n/** 커널 `DurationEstimate` 와 같은 모양(커널 타입에 의존하지 않기 위해 구조로 맞춘다). */\nexport interface MeasuredEstimate {\n meanMs: number\n spread?: { distribution: 'triangular'; minMs: number; maxMs: number; modeMs: number }\n}\n\nexport interface MeasuredEstimatorResult {\n estimator?: {\n estimate(ctx: { kind: string; fromNode: string; toNode: string; resourceKind?: string }): MeasuredEstimate | undefined\n }\n /** 작업 종류 → 실측 소요(ms). 추정기가 답하는 종류들. */\n learned: Record<string, number>\n /** 퍼짐까지 배운 종류 — 평균만 배운 것과 구별한다(변동 없는 소요는 대기를 과소평가한다). */\n spreads: Record<string, { minMs: number; maxMs: number }>\n /** 표본이 모자라 쓰지 않은 종류와 그 건수 — 배우지 못한 것을 밝힌다. */\n skipped: { kind: string; count: number }[]\n minSamples: number\n}\n\n/** 기본 표본 하한 — 이보다 적으면 그 종류는 배우지 않는다(작은 표본의 p50 을 현장 값으로 승격 금지). */\nexport const DEFAULT_MIN_SAMPLES = 5\n\n/**\n * 작업 종류별 KPI 그룹에서 추정기를 만든다(순수 — DB·커널 모름).\n * `groups` 는 `computeTwinKpi({ groupBy: 'taskKind' })` 의 `groups.items` 를 그대로 넘긴다.\n */\nexport function buildMeasuredEstimator(\n groups: { key: string; workTime?: { count: number; p10Ms?: number; p50Ms: number; p90Ms?: number } }[] | undefined,\n opts: { minSamples?: number } = {}\n): MeasuredEstimatorResult {\n const minSamples = opts.minSamples ?? DEFAULT_MIN_SAMPLES\n const learned: Record<string, number> = {}\n const spreads: Record<string, { minMs: number; maxMs: number }> = {}\n const estimates: Record<string, MeasuredEstimate> = {}\n const skipped: { kind: string; count: number }[] = []\n for (const g of groups ?? []) {\n if (!g?.key || g.key === 'unknown') continue\n const w = g.workTime\n const count = w?.count ?? 0\n const p50 = w?.p50Ms ?? 0\n if (count < minSamples || !(p50 > 0)) { skipped.push({ kind: g.key, count }); continue }\n const mean = Math.round(p50)\n learned[g.key] = mean\n /* 퍼짐은 **관측된 두 꼬리**로만 만든다. 한쪽이라도 없거나 구간이 성립하지 않으면 평균만 넘긴다. */\n const lo = w?.p10Ms\n const hi = w?.p90Ms\n if (typeof lo === 'number' && typeof hi === 'number' && lo > 0 && hi > lo) {\n const minMs = Math.round(lo)\n const maxMs = Math.round(hi)\n spreads[g.key] = { minMs, maxMs }\n estimates[g.key] = { meanMs: mean, spread: { distribution: 'triangular', minMs, maxMs, modeMs: mean } }\n } else {\n estimates[g.key] = { meanMs: mean }\n }\n }\n if (!Object.keys(learned).length) return { learned, spreads, skipped, minSamples }\n return {\n learned,\n spreads,\n skipped,\n minSamples,\n estimator: {\n estimate(ctx) {\n return estimates[ctx.kind]\n }\n }\n }\n}\n"]}
1
+ {"version":3,"file":"measured-estimator.js","sourceRoot":"","sources":["../../server/engine/measured-estimator.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AA4BH,wDAyCC;AAhDD,mEAAmE;AACtD,QAAA,mBAAmB,GAAG,CAAC,CAAA;AAEpC;;;GAGG;AACH,SAAgB,sBAAsB,CACpC,MAAkH,EAClH,OAAgC,EAAE;IAElC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,2BAAmB,CAAA;IACzD,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,MAAM,OAAO,GAAqD,EAAE,CAAA;IACpE,MAAM,SAAS,GAAqC,EAAE,CAAA;IACtD,MAAM,OAAO,GAAsC,EAAE,CAAA;IACrD,KAAK,MAAM,CAAC,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;YAAE,SAAQ;QAC5C,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAA;QACpB,MAAM,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,CAAA;QAC3B,MAAM,GAAG,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,CAAA;QACzB,IAAI,KAAK,GAAG,UAAU,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;YAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;YAAC,SAAQ;QAAC,CAAC;QACxF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA;QACrB,6DAA6D;QAC7D,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAA;QACnB,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAA;QACnB,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;YAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC5B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;YACjC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAA;QACzG,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QACrC,CAAC;IACH,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAA;IAClF,OAAO;QACL,OAAO;QACP,OAAO;QACP,OAAO;QACP,UAAU;QACV,SAAS,EAAE;YACT,QAAQ,CAAC,GAAG;gBACV,OAAO,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAC5B,CAAC;SACF;KACF,CAAA;AACH,CAAC","sourcesContent":["/*\n * 실측 소요 추정기 — **이력에서 배운 값이 상수·선언값을 이긴다.**\n *\n * 배경(operato-twin design/plans/simulation-spec.md §5-4): 소요시간을 명세로 받을 수 있게 됐지만\n * 명세는 선언이다 — 그 현장에서 실제로 얼마 걸리는지는 저널이 안다. `gap-analytics` 는 지표 수준의\n * 편향만 배웠고(예측값 보정), **공정별 소요를 추정기로 되먹이는 경로는 없었다.**\n *\n * 여기서 하는 일: KPI 폴드가 작업 종류별로 낸 **작업시간(착수→완료) p50** 을 그 종류의 소요로 쓴다.\n * 리드타임(생성→완료)이 아니라 작업시간을 쓰는 이유: 리드타임에는 **대기가 섞여 있고**, 대기는\n * 시뮬레이션이 자원 경합으로 스스로 만들어야 하는 값이다. 대기를 소요에 넣으면 이중 계산이 된다.\n *\n * **평균만 배우면 대기와 병목을 과소평가한다.** 소요가 늘 정확히 같으면 줄이 생기지 않는다 — 현장의\n * 줄은 흔들림에서 나온다. 그래서 p50 만이 아니라 **관측된 퍼짐(p10..p90)** 을 함께 넘기고, 표본은\n * 커널이 자기 난수로 뽑는다(fork 결정성). 양쪽 꼬리를 다 관측에서 가져오는 이유: p90 만 있으면\n * 아래쪽을 위쪽에서 거울처럼 베껴야 하는데 그건 발명이다.\n *\n * 정직 규율:\n * - 표본이 적으면 쓰지 않는다(`minSamples`). 한두 건의 p50 은 그 현장의 값이 아니다.\n * - 'unknown' 축은 쓰지 않는다 — 종류를 모르는 기록을 특정 종류의 근거로 삼을 수 없다.\n * - 퍼짐이 성립하지 않으면(p10 ≥ p90, 또는 0) **퍼짐 없이 평균만** 넘긴다. 없는 흔들림을 만들지 않는다.\n * - 무엇을 배웠고 무엇을 표본 부족으로 버렸는지 함께 낸다(알리지 않고 일부만 반영하지 않는다).\n */\n\n/** 커널 `DurationEstimate` 와 같은 모양(커널 타입에 의존하지 않기 위해 구조로 맞춘다). */\nexport interface MeasuredEstimate {\n meanMs: number\n spread?: { distribution: 'triangular'; minMs: number; maxMs: number; modeMs: number }\n}\n\nexport interface MeasuredEstimatorResult {\n estimator?: {\n estimate(ctx: { kind: string; fromNode: string; toNode: string; resourceKind?: string }): MeasuredEstimate | undefined\n }\n /** 작업 종류 → 실측 소요(ms). 추정기가 답하는 종류들. */\n learned: Record<string, number>\n /** 퍼짐까지 배운 종류 — 평균만 배운 것과 구별한다(변동 없는 소요는 대기를 과소평가한다). */\n spreads: Record<string, { minMs: number; maxMs: number }>\n /** 표본이 모자라 쓰지 않은 종류와 그 건수 — 배우지 못한 것을 밝힌다. */\n skipped: { kind: string; count: number }[]\n minSamples: number\n}\n\n/** 기본 표본 하한 — 이보다 적으면 그 종류는 배우지 않는다(작은 표본의 p50 을 현장 값으로 승격 금지). */\nexport const DEFAULT_MIN_SAMPLES = 5\n\n/**\n * 작업 종류별 KPI 그룹에서 추정기를 만든다(순수 — DB·커널 모름).\n * `groups` 는 `computeTwinKpi({ groupBy: 'taskKind' })` 의 `groups.items` 를 그대로 넘긴다.\n */\nexport function buildMeasuredEstimator(\n groups: { key: string; workTime?: { count: number; p10Ms?: number; p50Ms: number; p90Ms?: number } }[] | undefined,\n opts: { minSamples?: number } = {}\n): MeasuredEstimatorResult {\n const minSamples = opts.minSamples ?? DEFAULT_MIN_SAMPLES\n const learned: Record<string, number> = {}\n const spreads: Record<string, { minMs: number; maxMs: number }> = {}\n const estimates: Record<string, MeasuredEstimate> = {}\n const skipped: { kind: string; count: number }[] = []\n for (const g of groups ?? []) {\n if (!g?.key || g.key === 'unknown') continue\n const w = g.workTime\n const count = w?.count ?? 0\n const p50 = w?.p50Ms ?? 0\n if (count < minSamples || !(p50 > 0)) { skipped.push({ kind: g.key, count }); continue }\n const mean = Math.round(p50)\n learned[g.key] = mean\n /* 퍼짐은 **관측된 두 꼬리**로만 만든다. 한쪽이라도 없거나 구간이 성립하지 않으면 평균만 넘긴다. */\n const lo = w?.p10Ms\n const hi = w?.p90Ms\n if (typeof lo === 'number' && typeof hi === 'number' && lo > 0 && hi > lo) {\n const minMs = Math.round(lo)\n const maxMs = Math.round(hi)\n spreads[g.key] = { minMs, maxMs }\n estimates[g.key] = { meanMs: mean, spread: { distribution: 'triangular', minMs, maxMs, modeMs: mean } }\n } else {\n estimates[g.key] = { meanMs: mean }\n }\n }\n if (!Object.keys(learned).length) return { learned, spreads, skipped, minSamples }\n return {\n learned,\n spreads,\n skipped,\n minSamples,\n estimator: {\n estimate(ctx) {\n return estimates[ctx.kind]\n }\n }\n }\n}\n"]}
@@ -19,7 +19,7 @@
19
19
  * 여기서도 `good + scrap` 만 쓴다 — `tasks` 를 분모로 쓰면 이동·체류가 섞여 양품률이 낮게 나온다.
20
20
  * - **0 도 사실이다.** 전부 불량이면 0 을 배운다(그 현장이 그랬다). 「값이 없다」와 구별해 다룬다 —
21
21
  * 커널이 `0` 을 받으면 전량 불량으로 굴러가고, 그것이 관측과 같은 결론이다.
22
- * - 무엇을 배웠고 무엇을 표본 부족으로 버렸는지 함께 낸다(조용히 일부만 반영하지 않는다).
22
+ * - 무엇을 배웠고 무엇을 표본 부족으로 버렸는지 함께 낸다(알리지 않고 일부만 반영하지 않는다).
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.DEFAULT_YIELD_MIN_SAMPLES = void 0;
@@ -1 +1 @@
1
- {"version":3,"file":"measured-yield.js","sourceRoot":"","sources":["../../server/engine/measured-yield.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AA6BH,kDAsCC;AAnDD;;;;;;GAMG;AACU,QAAA,yBAAyB,GAAG,EAAE,CAAA;AAE3C;;;GAGG;AACH,SAAgB,mBAAmB,CACjC,MAAgF,EAChF,OAAgC,EAAE;IAElC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,iCAAyB,CAAA;IAC/D,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,MAAM,OAAO,GAAoD,EAAE,CAAA;IACnE,MAAM,OAAO,GAAuC,EAAE,CAAA;IAEtD,KAAK,MAAM,CAAC,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;YAAE,SAAQ;QAC5C,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,CAAA;QACjC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC,CAAA;QACnC,MAAM,MAAM,GAAG,IAAI,GAAG,KAAK,CAAA;QAC3B,gEAAgE;QAChE,IAAI,MAAM,KAAK,CAAC;YAAE,SAAQ;QAC1B,IAAI,MAAM,GAAG,UAAU,EAAE,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAA;YACrC,SAAQ;QACV,CAAC;QACD,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,MAAM,CAAA;QAC9B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;IAClC,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAClC,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAA;IAEnE,OAAO;QACL;;;WAGG;QACH,SAAS,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACpD,OAAO;QACP,OAAO;QACP,OAAO;QACP,UAAU;KACX,CAAA;AACH,CAAC","sourcesContent":["/*\n * 실측 양품률 추정기 — **이력에서 배운 수율이 선언값·상수를 이긴다.**\n *\n * ── 왜 필요했나 (2026-08-19) ────────────────────────────────────────────────\n * 소요시간에는 이력 층이 있었다(`measured-estimator`): 저널이 「그 현장에서 실제로 얼마 걸렸나」를 안다.\n * 수율에는 그 층이 없었다 — 양품률의 기본값은 커널 소스의 상수(0.8)였고, **그 상수가 불량 과다 주목\n * 신호까지 만들었다.** 초기 라인이든 성숙한 라인이든 같은 수로 판정된다는 뜻이다.\n *\n * 그런데 이력을 만들 재료도 없었다: 양품/불량은 설비 누적 카운터(종류를 모른다)와 EPCIS disposition\n * (종류를 모른다)에만 남았다. 그래서 커널이 **판정을 일어난 자리(작업 완료)에 적게** 했고\n * (`TaskStatusDelta.outcome`), 폴드가 축으로 들고(`TaskFacets.outcome`), KPI 가 종류별로 센다\n * (`KpiGroup.quality`). 이 파일은 그 수를 비율로 바꿔 커널 시임에 넣는다.\n *\n * ── 정직 규율 (소요 추정기와 같은 것) ───────────────────────────────────────\n * - **표본이 적으면 쓰지 않는다**(`minSamples`). 세 건 중 한 건 불량은 「불량률 33%」가 아니다.\n * - `'unknown'` 축은 쓰지 않는다 — 종류를 모르는 기록을 특정 종류의 근거로 삼을 수 없다.\n * - **판정이 없는 작업은 분모에 넣지 않는다.** KPI 가 이미 그렇게 세지만(`quality` 는 판정된 것만),\n * 여기서도 `good + scrap` 만 쓴다 — `tasks` 를 분모로 쓰면 이동·체류가 섞여 양품률이 낮게 나온다.\n * - **0 도 사실이다.** 전부 불량이면 0 을 배운다(그 현장이 그랬다). 「값이 없다」와 구별해 다룬다 —\n * 커널이 `0` 을 받으면 전량 불량으로 굴러가고, 그것이 관측과 같은 결론이다.\n * - 무엇을 배웠고 무엇을 표본 부족으로 버렸는지 함께 낸다(조용히 일부만 반영하지 않는다).\n */\n\n/** 커널 `YieldEstimator` 와 같은 모양(커널 타입에 의존하지 않기 위해 구조로 맞춘다). */\nexport interface YieldEstimatorResult {\n estimator?: {\n estimate(ctx: { kind: string }): number | undefined\n }\n /** 작업 종류 → 실측 양품률(0..1). 추정기가 답하는 종류들. */\n learned: Record<string, number>\n /** 종류별 표본 — 배운 값의 근거 크기(화면·로그가 「몇 건에서 배웠나」를 말할 수 있게). */\n samples: Record<string, { good: number; scrap: number }>\n /** 표본이 모자라 쓰지 않은 종류와 판정 건수 — 배우지 못한 것을 밝힌다. */\n skipped: { kind: string; judged: number }[]\n minSamples: number\n}\n\n/**\n * 기본 표본 하한 — 판정 20건 미만이면 그 종류는 배우지 않는다.\n *\n * 소요(5건)보다 높게 잡는 이유: 비율은 **드문 사건**을 재는 값이다. 불량률 5% 인 공정에서 20건을 봐도\n * 불량은 한 건이고, 그 한 건이 있고 없음으로 비율이 0% 와 5% 사이를 오간다. 낮게 잡으면 추정기가\n * 이력이라는 이름으로 잡음을 실어 보낸다.\n */\nexport const DEFAULT_YIELD_MIN_SAMPLES = 20\n\n/**\n * 작업 종류별 KPI 그룹에서 양품률 추정기를 만든다(순수 — DB·커널 모름).\n * `groups` 는 `computeTwinKpi({ groupBy: 'taskKind' })` 의 `groups.items` 를 그대로 넘긴다.\n */\nexport function buildYieldEstimator(\n groups: { key: string; quality?: { good: number; scrap: number } }[] | undefined,\n opts: { minSamples?: number } = {}\n): YieldEstimatorResult {\n const minSamples = opts.minSamples ?? DEFAULT_YIELD_MIN_SAMPLES\n const learned: Record<string, number> = {}\n const samples: Record<string, { good: number; scrap: number }> = {}\n const skipped: { kind: string; judged: number }[] = []\n\n for (const g of groups ?? []) {\n if (!g?.key || g.key === 'unknown') continue\n const good = g.quality?.good ?? 0\n const scrap = g.quality?.scrap ?? 0\n const judged = good + scrap\n /* 판정이 아예 없는 종류는 **건너뛴 것이 아니다** — 품질을 가리지 않는 공정(이동·체류)이 정상이다. */\n if (judged === 0) continue\n if (judged < minSamples) {\n skipped.push({ kind: g.key, judged })\n continue\n }\n learned[g.key] = good / judged\n samples[g.key] = { good, scrap }\n }\n\n const kinds = Object.keys(learned)\n if (!kinds.length) return { learned, samples, skipped, minSamples }\n\n return {\n /*\n * 시임은 **무상태**다(커널이 fork 할 때 참조를 공유한다 — 소요 추정기와 같은 규약).\n * 배우지 않은 종류에는 `undefined` 를 답한다: 그러면 커널이 선언값 → 상수로 내려간다.\n */\n estimator: { estimate: ({ kind }) => learned[kind] },\n learned,\n samples,\n skipped,\n minSamples\n }\n}\n"]}
1
+ {"version":3,"file":"measured-yield.js","sourceRoot":"","sources":["../../server/engine/measured-yield.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AA6BH,kDAsCC;AAnDD;;;;;;GAMG;AACU,QAAA,yBAAyB,GAAG,EAAE,CAAA;AAE3C;;;GAGG;AACH,SAAgB,mBAAmB,CACjC,MAAgF,EAChF,OAAgC,EAAE;IAElC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,iCAAyB,CAAA;IAC/D,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,MAAM,OAAO,GAAoD,EAAE,CAAA;IACnE,MAAM,OAAO,GAAuC,EAAE,CAAA;IAEtD,KAAK,MAAM,CAAC,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;YAAE,SAAQ;QAC5C,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,CAAA;QACjC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC,CAAA;QACnC,MAAM,MAAM,GAAG,IAAI,GAAG,KAAK,CAAA;QAC3B,gEAAgE;QAChE,IAAI,MAAM,KAAK,CAAC;YAAE,SAAQ;QAC1B,IAAI,MAAM,GAAG,UAAU,EAAE,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAA;YACrC,SAAQ;QACV,CAAC;QACD,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,MAAM,CAAA;QAC9B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;IAClC,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAClC,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAA;IAEnE,OAAO;QACL;;;WAGG;QACH,SAAS,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACpD,OAAO;QACP,OAAO;QACP,OAAO;QACP,UAAU;KACX,CAAA;AACH,CAAC","sourcesContent":["/*\n * 실측 양품률 추정기 — **이력에서 배운 수율이 선언값·상수를 이긴다.**\n *\n * ── 왜 필요했나 (2026-08-19) ────────────────────────────────────────────────\n * 소요시간에는 이력 층이 있었다(`measured-estimator`): 저널이 「그 현장에서 실제로 얼마 걸렸나」를 안다.\n * 수율에는 그 층이 없었다 — 양품률의 기본값은 커널 소스의 상수(0.8)였고, **그 상수가 불량 과다 주목\n * 신호까지 만들었다.** 초기 라인이든 성숙한 라인이든 같은 수로 판정된다는 뜻이다.\n *\n * 그런데 이력을 만들 재료도 없었다: 양품/불량은 설비 누적 카운터(종류를 모른다)와 EPCIS disposition\n * (종류를 모른다)에만 남았다. 그래서 커널이 **판정을 일어난 자리(작업 완료)에 적게** 했고\n * (`TaskStatusDelta.outcome`), 폴드가 축으로 들고(`TaskFacets.outcome`), KPI 가 종류별로 센다\n * (`KpiGroup.quality`). 이 파일은 그 수를 비율로 바꿔 커널 시임에 넣는다.\n *\n * ── 정직 규율 (소요 추정기와 같은 것) ───────────────────────────────────────\n * - **표본이 적으면 쓰지 않는다**(`minSamples`). 세 건 중 한 건 불량은 「불량률 33%」가 아니다.\n * - `'unknown'` 축은 쓰지 않는다 — 종류를 모르는 기록을 특정 종류의 근거로 삼을 수 없다.\n * - **판정이 없는 작업은 분모에 넣지 않는다.** KPI 가 이미 그렇게 세지만(`quality` 는 판정된 것만),\n * 여기서도 `good + scrap` 만 쓴다 — `tasks` 를 분모로 쓰면 이동·체류가 섞여 양품률이 낮게 나온다.\n * - **0 도 사실이다.** 전부 불량이면 0 을 배운다(그 현장이 그랬다). 「값이 없다」와 구별해 다룬다 —\n * 커널이 `0` 을 받으면 전량 불량으로 굴러가고, 그것이 관측과 같은 결론이다.\n * - 무엇을 배웠고 무엇을 표본 부족으로 버렸는지 함께 낸다(알리지 않고 일부만 반영하지 않는다).\n */\n\n/** 커널 `YieldEstimator` 와 같은 모양(커널 타입에 의존하지 않기 위해 구조로 맞춘다). */\nexport interface YieldEstimatorResult {\n estimator?: {\n estimate(ctx: { kind: string }): number | undefined\n }\n /** 작업 종류 → 실측 양품률(0..1). 추정기가 답하는 종류들. */\n learned: Record<string, number>\n /** 종류별 표본 — 배운 값의 근거 크기(화면·로그가 「몇 건에서 배웠나」를 말할 수 있게). */\n samples: Record<string, { good: number; scrap: number }>\n /** 표본이 모자라 쓰지 않은 종류와 판정 건수 — 배우지 못한 것을 밝힌다. */\n skipped: { kind: string; judged: number }[]\n minSamples: number\n}\n\n/**\n * 기본 표본 하한 — 판정 20건 미만이면 그 종류는 배우지 않는다.\n *\n * 소요(5건)보다 높게 잡는 이유: 비율은 **드문 사건**을 재는 값이다. 불량률 5% 인 공정에서 20건을 봐도\n * 불량은 한 건이고, 그 한 건이 있고 없음으로 비율이 0% 와 5% 사이를 오간다. 낮게 잡으면 추정기가\n * 이력이라는 이름으로 잡음을 실어 보낸다.\n */\nexport const DEFAULT_YIELD_MIN_SAMPLES = 20\n\n/**\n * 작업 종류별 KPI 그룹에서 양품률 추정기를 만든다(순수 — DB·커널 모름).\n * `groups` 는 `computeTwinKpi({ groupBy: 'taskKind' })` 의 `groups.items` 를 그대로 넘긴다.\n */\nexport function buildYieldEstimator(\n groups: { key: string; quality?: { good: number; scrap: number } }[] | undefined,\n opts: { minSamples?: number } = {}\n): YieldEstimatorResult {\n const minSamples = opts.minSamples ?? DEFAULT_YIELD_MIN_SAMPLES\n const learned: Record<string, number> = {}\n const samples: Record<string, { good: number; scrap: number }> = {}\n const skipped: { kind: string; judged: number }[] = []\n\n for (const g of groups ?? []) {\n if (!g?.key || g.key === 'unknown') continue\n const good = g.quality?.good ?? 0\n const scrap = g.quality?.scrap ?? 0\n const judged = good + scrap\n /* 판정이 아예 없는 종류는 **건너뛴 것이 아니다** — 품질을 가리지 않는 공정(이동·체류)이 정상이다. */\n if (judged === 0) continue\n if (judged < minSamples) {\n skipped.push({ kind: g.key, judged })\n continue\n }\n learned[g.key] = good / judged\n samples[g.key] = { good, scrap }\n }\n\n const kinds = Object.keys(learned)\n if (!kinds.length) return { learned, samples, skipped, minSamples }\n\n return {\n /*\n * 시임은 **무상태**다(커널이 fork 할 때 참조를 공유한다 — 소요 추정기와 같은 규약).\n * 배우지 않은 종류에는 `undefined` 를 답한다: 그러면 커널이 선언값 → 상수로 내려간다.\n */\n estimator: { estimate: ({ kind }) => learned[kind] },\n learned,\n samples,\n skipped,\n minSamples\n }\n}\n"]}