@things-factory/headless-twin 10.0.12 → 10.0.13

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 (312) hide show
  1. package/dist-server/engine/canonical-ingest.d.ts +57 -2
  2. package/dist-server/engine/canonical-ingest.js +43 -2
  3. package/dist-server/engine/canonical-ingest.js.map +1 -1
  4. package/dist-server/engine/command-routing.js +1 -1
  5. package/dist-server/engine/command-routing.js.map +1 -1
  6. package/dist-server/engine/index.d.ts +3 -0
  7. package/dist-server/engine/index.js +4 -0
  8. package/dist-server/engine/index.js.map +1 -1
  9. package/dist-server/engine/ingest-health.d.ts +335 -0
  10. package/dist-server/engine/ingest-health.js +434 -0
  11. package/dist-server/engine/ingest-health.js.map +1 -0
  12. package/dist-server/engine/integration-coverage.d.ts +76 -0
  13. package/dist-server/engine/integration-coverage.js +73 -0
  14. package/dist-server/engine/integration-coverage.js.map +1 -0
  15. package/dist-server/engine/integration-probes.d.ts +65 -0
  16. package/dist-server/engine/integration-probes.js +100 -0
  17. package/dist-server/engine/integration-probes.js.map +1 -0
  18. package/dist-server/engine/integration-runner.d.ts +45 -0
  19. package/dist-server/engine/integration-runner.js +59 -0
  20. package/dist-server/engine/integration-runner.js.map +1 -0
  21. package/dist-server/engine/integration-target-profile.d.ts +57 -0
  22. package/dist-server/engine/integration-target-profile.js +79 -0
  23. package/dist-server/engine/integration-target-profile.js.map +1 -0
  24. package/dist-server/engine/kpi-fold.d.ts +27 -0
  25. package/dist-server/engine/kpi-fold.js +47 -4
  26. package/dist-server/engine/kpi-fold.js.map +1 -1
  27. package/dist-server/engine/kpi-query.js +128 -14
  28. package/dist-server/engine/kpi-query.js.map +1 -1
  29. package/dist-server/engine/live-feed-registry.js +2 -16
  30. package/dist-server/engine/live-feed-registry.js.map +1 -1
  31. package/dist-server/engine/load-meter.d.ts +1 -1
  32. package/dist-server/engine/load-meter.js +12 -3
  33. package/dist-server/engine/load-meter.js.map +1 -1
  34. package/dist-server/engine/log.d.ts +18 -0
  35. package/dist-server/engine/log.js +80 -0
  36. package/dist-server/engine/log.js.map +1 -0
  37. package/dist-server/engine/loop-lag.d.ts +54 -0
  38. package/dist-server/engine/loop-lag.js +87 -0
  39. package/dist-server/engine/loop-lag.js.map +1 -0
  40. package/dist-server/engine/model-gap.d.ts +108 -0
  41. package/dist-server/engine/model-gap.js +95 -0
  42. package/dist-server/engine/model-gap.js.map +1 -0
  43. package/dist-server/engine/restart-policy.d.ts +3 -3
  44. package/dist-server/engine/restart-policy.js +4 -4
  45. package/dist-server/engine/restart-policy.js.map +1 -1
  46. package/dist-server/engine/runtime-key.js +1 -1
  47. package/dist-server/engine/runtime-key.js.map +1 -1
  48. package/dist-server/engine/stage-path.d.ts +83 -0
  49. package/dist-server/engine/stage-path.js +118 -0
  50. package/dist-server/engine/stage-path.js.map +1 -0
  51. package/dist-server/engine/twin-engine.d.ts +352 -16
  52. package/dist-server/engine/twin-engine.js +1037 -166
  53. package/dist-server/engine/twin-engine.js.map +1 -1
  54. package/dist-server/index.js +34 -2
  55. package/dist-server/index.js.map +1 -1
  56. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.d.ts +5 -0
  57. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js +58 -0
  58. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +1 -0
  59. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.d.ts +5 -0
  60. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js +55 -0
  61. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +1 -0
  62. package/dist-server/migrations/index.js +7 -1
  63. package/dist-server/migrations/index.js.map +1 -1
  64. package/dist-server/service/index.d.ts +5 -2
  65. package/dist-server/service/index.js +18 -7
  66. package/dist-server/service/index.js.map +1 -1
  67. package/dist-server/service/reference/discovery-result.d.ts +1 -1
  68. package/dist-server/service/reference/discovery-result.js +1 -1
  69. package/dist-server/service/reference/discovery-result.js.map +1 -1
  70. package/dist-server/service/reference/index.d.ts +4 -1
  71. package/dist-server/service/reference/index.js +6 -1
  72. package/dist-server/service/reference/index.js.map +1 -1
  73. package/dist-server/service/reference/reference-adapter.d.ts +184 -3
  74. package/dist-server/service/reference/reference-adapter.js +28 -4
  75. package/dist-server/service/reference/reference-adapter.js.map +1 -1
  76. package/dist-server/service/reference/reference-assessment.d.ts +68 -0
  77. package/dist-server/service/reference/reference-assessment.js +136 -0
  78. package/dist-server/service/reference/reference-assessment.js.map +1 -0
  79. package/dist-server/service/reference/reference-live.d.ts +12 -1
  80. package/dist-server/service/reference/reference-live.js +116 -11
  81. package/dist-server/service/reference/reference-live.js.map +1 -1
  82. package/dist-server/service/reference/reference-master.d.ts +47 -1
  83. package/dist-server/service/reference/reference-master.js +34 -4
  84. package/dist-server/service/reference/reference-master.js.map +1 -1
  85. package/dist-server/service/reference/reference-probe.d.ts +92 -0
  86. package/dist-server/service/reference/reference-probe.js +186 -0
  87. package/dist-server/service/reference/reference-probe.js.map +1 -0
  88. package/dist-server/service/reference/reference-progress-subscription.d.ts +17 -0
  89. package/dist-server/service/reference/reference-progress-subscription.js +94 -0
  90. package/dist-server/service/reference/reference-progress-subscription.js.map +1 -0
  91. package/dist-server/service/reference/reference-progress.d.ts +38 -0
  92. package/dist-server/service/reference/reference-progress.js +71 -0
  93. package/dist-server/service/reference/reference-progress.js.map +1 -0
  94. package/dist-server/service/reference/reference-resolver.js +210 -43
  95. package/dist-server/service/reference/reference-resolver.js.map +1 -1
  96. package/dist-server/service/reference/twin-reference.d.ts +32 -0
  97. package/dist-server/service/reference/twin-reference.js +10 -0
  98. package/dist-server/service/reference/twin-reference.js.map +1 -1
  99. package/dist-server/service/twin-control/twin-control-mutation.js +4 -3
  100. package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
  101. package/dist-server/service/twin-event/journal-count.d.ts +21 -0
  102. package/dist-server/service/twin-event/journal-count.js +26 -0
  103. package/dist-server/service/twin-event/journal-count.js.map +1 -0
  104. package/dist-server/service/twin-event/twin-event-keys.d.ts +9 -0
  105. package/dist-server/service/twin-event/twin-event-keys.js +15 -18
  106. package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
  107. package/dist-server/service/twin-event/twin-event-type.d.ts +3 -1
  108. package/dist-server/service/twin-event/twin-event-type.js +18 -1
  109. package/dist-server/service/twin-event/twin-event-type.js.map +1 -1
  110. package/dist-server/service/twin-event/twin-event.d.ts +1 -0
  111. package/dist-server/service/twin-event/twin-event.js +9 -1
  112. package/dist-server/service/twin-event/twin-event.js.map +1 -1
  113. package/dist-server/service/twin-forecast/twin-forecast-query.js +2 -1
  114. package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
  115. package/dist-server/service/twin-ingest-window/index.d.ts +5 -0
  116. package/dist-server/service/twin-ingest-window/index.js +10 -0
  117. package/dist-server/service/twin-ingest-window/index.js.map +1 -0
  118. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.d.ts +12 -0
  119. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js +123 -0
  120. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +1 -0
  121. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.d.ts +1 -0
  122. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js +51 -0
  123. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +1 -0
  124. package/dist-server/service/twin-ingest-window/twin-ingest-window.d.ts +16 -0
  125. package/dist-server/service/twin-ingest-window/twin-ingest-window.js +108 -0
  126. package/dist-server/service/twin-ingest-window/twin-ingest-window.js.map +1 -0
  127. package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
  128. package/dist-server/service/twin-journal/twin-journal-query.js +58 -5
  129. package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
  130. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +14 -0
  131. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +72 -2
  132. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
  133. package/dist-server/service/twin-metrics/twin-metrics-query.d.ts +1 -0
  134. package/dist-server/service/twin-metrics/twin-metrics-query.js +78 -3
  135. package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
  136. package/dist-server/service/twin-model/axis-journal-evidence.d.ts +2 -0
  137. package/dist-server/service/twin-model/axis-journal-evidence.js +89 -0
  138. package/dist-server/service/twin-model/axis-journal-evidence.js.map +1 -0
  139. package/dist-server/service/twin-model/epcis-coverage.d.ts +1 -1
  140. package/dist-server/service/twin-model/epcis-coverage.js +19 -7
  141. package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
  142. package/dist-server/service/twin-model/iec61850-coverage.d.ts +1 -1
  143. package/dist-server/service/twin-model/iec61850-coverage.js +18 -7
  144. package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -1
  145. package/dist-server/service/twin-model/isa95-coverage.d.ts +14 -0
  146. package/dist-server/service/twin-model/isa95-coverage.js +23 -9
  147. package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
  148. package/dist-server/service/twin-model/item-ref.d.ts +24 -0
  149. package/dist-server/service/twin-model/item-ref.js +88 -0
  150. package/dist-server/service/twin-model/item-ref.js.map +1 -0
  151. package/dist-server/service/twin-model/name-index.d.ts +36 -0
  152. package/dist-server/service/twin-model/name-index.js +116 -0
  153. package/dist-server/service/twin-model/name-index.js.map +1 -0
  154. package/dist-server/service/twin-model/project-structure.js +1 -1
  155. package/dist-server/service/twin-model/project-structure.js.map +1 -1
  156. package/dist-server/service/twin-model/status-tally.d.ts +9 -0
  157. package/dist-server/service/twin-model/status-tally.js +37 -0
  158. package/dist-server/service/twin-model/status-tally.js.map +1 -0
  159. package/dist-server/service/twin-model/twin-lineage-query.js +40 -12
  160. package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
  161. package/dist-server/service/twin-model/twin-model-item-query.js +38 -39
  162. package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
  163. package/dist-server/service/twin-model/twin-model-query.js +156 -4
  164. package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
  165. package/dist-server/service/twin-model/twin-model-tree-query.js +7 -0
  166. package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
  167. package/dist-server/service/twin-readiness/index.d.ts +2 -0
  168. package/dist-server/service/twin-readiness/index.js +6 -0
  169. package/dist-server/service/twin-readiness/index.js.map +1 -0
  170. package/dist-server/service/twin-readiness/twin-readiness-query.d.ts +3 -0
  171. package/dist-server/service/twin-readiness/twin-readiness-query.js +103 -0
  172. package/dist-server/service/twin-readiness/twin-readiness-query.js.map +1 -0
  173. package/dist-server/service/twin-space/twin-space-resolver.d.ts +2 -2
  174. package/dist-server/service/twin-space/twin-space-resolver.js +5 -4
  175. package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
  176. package/dist-shared/entity-delta.d.ts +23 -3
  177. package/dist-shared/entity-delta.js +12 -8
  178. package/dist-shared/entity-delta.js.map +1 -1
  179. package/dist-shared/kpi-broadcast.js +1 -1
  180. package/dist-shared/kpi-broadcast.js.map +1 -1
  181. package/dist-shared/touched-items.d.ts +7 -0
  182. package/dist-shared/touched-items.js +80 -0
  183. package/dist-shared/touched-items.js.map +1 -0
  184. package/package.json +7 -7
  185. package/server/engine/canonical-ingest.ts +92 -7
  186. package/server/engine/command-routing.ts +1 -1
  187. package/server/engine/index.ts +4 -0
  188. package/server/engine/ingest-health.ts +704 -0
  189. package/server/engine/integration-coverage.ts +147 -0
  190. package/server/engine/integration-probes.ts +144 -0
  191. package/server/engine/integration-runner.ts +95 -0
  192. package/server/engine/integration-target-profile.ts +103 -0
  193. package/server/engine/kpi-fold.ts +72 -4
  194. package/server/engine/kpi-query.ts +129 -15
  195. package/server/engine/live-feed-registry.ts +2 -1
  196. package/server/engine/load-meter.ts +12 -3
  197. package/server/engine/log.ts +72 -0
  198. package/server/engine/loop-lag.ts +120 -0
  199. package/server/engine/model-gap.ts +168 -0
  200. package/server/engine/restart-policy.ts +4 -4
  201. package/server/engine/runtime-key.ts +1 -1
  202. package/server/engine/stage-path.ts +172 -0
  203. package/server/engine/twin-engine.ts +1135 -162
  204. package/server/index.ts +35 -2
  205. package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +54 -0
  206. package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +53 -0
  207. package/server/migrations/index.ts +7 -1
  208. package/server/service/index.ts +11 -0
  209. package/server/service/reference/discovery-result.ts +1 -1
  210. package/server/service/reference/index.ts +6 -1
  211. package/server/service/reference/reference-adapter.ts +197 -5
  212. package/server/service/reference/reference-assessment.ts +215 -0
  213. package/server/service/reference/reference-live.ts +123 -11
  214. package/server/service/reference/reference-master.ts +64 -4
  215. package/server/service/reference/reference-probe.ts +264 -0
  216. package/server/service/reference/reference-progress-subscription.ts +73 -0
  217. package/server/service/reference/reference-progress.ts +95 -0
  218. package/server/service/reference/reference-resolver.ts +200 -12
  219. package/server/service/reference/twin-reference.ts +39 -1
  220. package/server/service/twin-control/twin-control-mutation.ts +4 -3
  221. package/server/service/twin-event/journal-count.ts +53 -0
  222. package/server/service/twin-event/twin-event-keys.ts +16 -1
  223. package/server/service/twin-event/twin-event-type.ts +26 -2
  224. package/server/service/twin-event/twin-event.ts +7 -0
  225. package/server/service/twin-forecast/twin-forecast-query.ts +2 -1
  226. package/server/service/twin-ingest-window/index.ts +7 -0
  227. package/server/service/twin-ingest-window/twin-ingest-window-query.ts +125 -0
  228. package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +57 -0
  229. package/server/service/twin-ingest-window/twin-ingest-window.ts +113 -0
  230. package/server/service/twin-instance/twin-instance.ts +1 -1
  231. package/server/service/twin-journal/twin-journal-query.ts +59 -5
  232. package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +77 -3
  233. package/server/service/twin-metrics/twin-metrics-query.ts +81 -3
  234. package/server/service/twin-model/axis-journal-evidence.ts +57 -0
  235. package/server/service/twin-model/epcis-coverage.ts +2 -8
  236. package/server/service/twin-model/iec61850-coverage.ts +2 -8
  237. package/server/service/twin-model/isa95-coverage.ts +22 -9
  238. package/server/service/twin-model/item-ref.ts +80 -0
  239. package/server/service/twin-model/name-index.ts +94 -0
  240. package/server/service/twin-model/project-structure.ts +1 -1
  241. package/server/service/twin-model/status-tally.ts +37 -0
  242. package/server/service/twin-model/twin-lineage-query.ts +38 -9
  243. package/server/service/twin-model/twin-model-item-query.ts +26 -27
  244. package/server/service/twin-model/twin-model-query.ts +155 -4
  245. package/server/service/twin-model/twin-model-tree-query.ts +7 -0
  246. package/server/service/twin-readiness/index.ts +3 -0
  247. package/server/service/twin-readiness/twin-readiness-query.ts +96 -0
  248. package/server/service/twin-space/twin-space-resolver.ts +5 -4
  249. package/shared/entity-delta.ts +31 -7
  250. package/shared/kpi-broadcast.ts +1 -1
  251. package/shared/touched-items.ts +73 -0
  252. package/test/axis-journal-evidence.test.ts +71 -0
  253. package/test/axis-read.test.ts +101 -1
  254. package/test/boot-resume.test.ts +92 -26
  255. package/test/broadcast-cost-baseline.test.ts +200 -0
  256. package/test/broadcast-period.test.ts +58 -0
  257. package/test/canonical-ingest-vocabularies.test.ts +36 -1
  258. package/test/canonical-quantity-door.test.ts +61 -0
  259. package/test/command-routing.test.ts +1 -1
  260. package/test/declared-location-types.test.ts +89 -0
  261. package/test/discovery-result.test.ts +1 -1
  262. package/test/duration-estimators.test.ts +1 -1
  263. package/test/entity-delta.test.ts +2 -2
  264. package/test/ingest-bench.test.ts +3 -3
  265. package/test/ingest-health-engine.test.ts +248 -0
  266. package/test/ingest-health-wiring.test.ts +119 -0
  267. package/test/ingest-health.test.ts +306 -0
  268. package/test/ingest-history.test.ts +247 -0
  269. package/test/integration-probes.test.ts +103 -0
  270. package/test/integration-runner.test.ts +95 -0
  271. package/test/item-ref.test.ts +78 -0
  272. package/test/journal-read-discipline.test.ts +177 -0
  273. package/test/journal-retention.test.ts +133 -0
  274. package/test/journal-sort-axis.test.ts +142 -0
  275. package/test/journal-write-door.test.ts +110 -0
  276. package/test/journal-write-trend.test.ts +115 -0
  277. package/test/kernel-kind-guard.test.ts +4 -4
  278. package/test/kpi-fold.test.ts +90 -3
  279. package/test/kpi-query-bench.test.ts +2 -2
  280. package/test/lineage-survives-restart.test.ts +17 -1
  281. package/test/live-cursor-wiring.test.ts +87 -0
  282. package/test/live-feed-registry.test.ts +9 -1
  283. package/test/live-kernel-facts.test.ts +7 -7
  284. package/test/live-mirror-parity.test.ts +6 -0
  285. package/test/load-meter.test.ts +29 -15
  286. package/test/local-declarations.test.ts +1 -1
  287. package/test/log-stamp.test.ts +59 -0
  288. package/test/loop-lag.test.ts +82 -0
  289. package/test/model-gap.test.ts +153 -0
  290. package/test/oee-accumulator.test.ts +4 -0
  291. package/test/operational-vocabulary.test.ts +4 -4
  292. package/test/read-failure-visible.test.ts +145 -0
  293. package/test/reference-grounding.test.ts +70 -0
  294. package/test/resolve-ts-siblings.mjs +52 -0
  295. package/test/restart-policy.test.ts +7 -7
  296. package/test/revision-axis.test.ts +93 -0
  297. package/test/runtime-key.test.ts +2 -2
  298. package/test/scale-twin-bench.test.ts +2 -2
  299. package/test/spec-coverage.test.ts +1 -1
  300. package/test/stage-path.test.ts +95 -0
  301. package/test/standard-coverage.test.ts +15 -3
  302. package/test/status-tally.test.ts +55 -0
  303. package/test/structure-revision-db.test.ts +4 -0
  304. package/test/tenant-registry-db.test.ts +1 -1
  305. package/test/time-range.test.ts +152 -0
  306. package/test/touched-items.test.ts +61 -0
  307. package/test/twin-event-keys.test.ts +10 -2
  308. package/test/twin-model-item-db.test.ts +2 -0
  309. package/test/warm-start-seam.test.ts +4 -0
  310. package/test/yield-loop.test.ts +57 -2
  311. package/tsconfig.shared.tsbuildinfo +1 -1
  312. package/tsconfig.tsbuildinfo +1 -1
@@ -2,8 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TwinReferenceResolver = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const type_graphql_1 = require("type-graphql");
6
5
  const shell_1 = require("@things-factory/shell");
6
+ const reference_progress_js_1 = require("./reference-progress.js");
7
+ const reference_assessment_js_1 = require("./reference-assessment.js");
8
+ const type_graphql_1 = require("type-graphql");
9
+ const shell_2 = require("@things-factory/shell");
7
10
  const index_js_1 = require("../../engine/index.js");
8
11
  const restart_policy_js_1 = require("../../engine/restart-policy.js");
9
12
  const twin_kernel_1 = require("@operato/twin-kernel");
@@ -85,7 +88,7 @@ async function resolveSiteEquipmentKnobs(knobs, effective, domainId, spaceId) {
85
88
  continue;
86
89
  }
87
90
  if (ids === undefined) {
88
- const rows = await (0, shell_1.getRepository)(twin_instance_js_1.TwinInstance).find({ where: { domain: { id: domainId }, spaceId } });
91
+ const rows = await (0, shell_2.getRepository)(twin_instance_js_1.TwinInstance).find({ where: { domain: { id: domainId }, spaceId } });
89
92
  const seen = new Map();
90
93
  for (const r of rows) {
91
94
  for (const e of (0, twin_kernel_1.readBoardEquipment)(r.model ?? {})) {
@@ -107,7 +110,7 @@ let TwinReferenceResolver = class TwinReferenceResolver {
107
110
  const domainId = context.state.domain.id;
108
111
  const out = [];
109
112
  for (const m of (0, reference_registry_js_1.listReferences)()) {
110
- const reg = await (0, shell_1.getRepository)(twin_instance_js_1.TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId: m.source } });
113
+ const reg = await (0, shell_2.getRepository)(twin_instance_js_1.TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId: m.source } });
111
114
  out.push({
112
115
  source: m.source,
113
116
  system: m.system,
@@ -160,7 +163,7 @@ let TwinReferenceResolver = class TwinReferenceResolver {
160
163
  */
161
164
  async resyncTwin(instanceId, context) {
162
165
  const domainId = context.state.domain.id;
163
- const inst = await (0, shell_1.getRepository)(twin_instance_js_1.TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } });
166
+ const inst = await (0, shell_2.getRepository)(twin_instance_js_1.TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } });
164
167
  if (!inst)
165
168
  return { ok: false, error: `unknown twin: ${instanceId}` };
166
169
  const origin = inst.origin;
@@ -169,8 +172,35 @@ let TwinReferenceResolver = class TwinReferenceResolver {
169
172
  ok: false,
170
173
  error: `twin "${instanceId}" does not remember where it came from — it cannot be re-read. Re-create it from its template or reference so the origin is recorded.`
171
174
  };
175
+ /*
176
+ * 진행을 알리는 통로 — 만들 때와 **같은 것**이다. 레퍼런스에서 온 트윈만 알린다(템플릿에서 온
177
+ * 트윈은 원본을 읽지 않으므로 알릴 단계가 없다).
178
+ *
179
+ * 분모는 그 어댑터가 선언한 단계 수다(`masterSteps`) + 호스트 단계. 선언하지 않은 어댑터는
180
+ * `total` 이 없고, 그때 화면은 퍼센트를 만들지 않는다 — 모르는 진행률을 지어내지 않는다.
181
+ */
182
+ const progressSource = origin.kind === 'reference' ? origin.source : undefined;
183
+ const refRow = progressSource
184
+ ? await (0, shell_2.getRepository)(twin_reference_js_1.TwinReference).findOne({ where: { domain: { id: domainId }, source: progressSource } })
185
+ : null;
186
+ const adapterForSteps = refRow ? (0, reference_adapter_js_1.getAdapter)(refRow.adapterType) : undefined;
187
+ const declaredSteps = adapterForSteps?.masterSteps?.length;
188
+ const stepTotal = declaredSteps === undefined ? undefined : declaredSteps + reference_progress_js_1.HOST_STEPS.length;
189
+ const siteId = origin.siteId ?? instanceId;
190
+ let lastDone = 0;
191
+ let lastStep = (adapterForSteps?.masterSteps?.[0] ?? 'provision');
192
+ const emit = (p) => {
193
+ if (!progressSource)
194
+ return;
195
+ lastDone = p.done;
196
+ lastStep = p.step;
197
+ shell_1.pubsub.publish('twin-reference-progress', {
198
+ twinReferenceProgress: { ...p, siteId, siteIndex: 0, siteCount: 1, total: p.total ?? stepTotal, source: progressSource }
199
+ });
200
+ };
172
201
  try {
173
- const master = await this.masterFromOrigin(domainId, origin, instanceId);
202
+ const master = await this.masterFromOrigin(domainId, origin, instanceId, st => emit({ step: st.key, done: st.done, total: stepTotal, detail: st.detail }));
203
+ emit({ step: 'provision', done: declaredSteps ?? 0, total: stepTotal });
174
204
  /*
175
205
  * **어느 현장의 트윈이었나를 지킨다** — 다시 읽기는 구조만 새로 그리는 일이고, 현장 소속은 그때
176
206
  * 사람이 고른 것이다. 기억이 없는 옛 트윈은 **지금 있는 현장**을 그대로 쓴다(빈손으로 두면 템플릿
@@ -179,14 +209,44 @@ let TwinReferenceResolver = class TwinReferenceResolver {
179
209
  const keepSpace = origin.spaceId || inst.spaceId;
180
210
  /* 다시 읽기는 **구조만** 새로 그린다 — 재기동 정책은 그 트윈의 선언이므로 그대로 지킨다. */
181
211
  const res = await index_js_1.TwinEngine.ingestMaster(domainId, master, (0, restart_policy_js_1.readRestartPolicy)(inst.restartPolicy, `resync of twin "${instanceId}"`), keepSpace ? { spaceId: keepSpace } : undefined, context.state.user);
212
+ emit({ step: 'start', done: (declaredSteps ?? 0) + 1, total: stepTotal });
213
+ /*
214
+ * **총평도 새로 남긴다.** 다시 읽었는데 총평이 옛것이면 화면이 지난 읽기를 현재로 보여 준다 —
215
+ * 그 표를 보고 사람이 원본을 고치러 간다.
216
+ */
217
+ if (refRow && progressSource) {
218
+ const prevSpec = refRow.scopeSpec ?? {};
219
+ await (0, shell_2.getRepository)(twin_reference_js_1.TwinReference).update({ id: refRow.id }, {
220
+ scopeSpec: { ...prevSpec, assessments: { ...(prevSpec.assessments ?? {}), [siteId]: (0, reference_assessment_js_1.assessMaster)(master, siteId) } }
221
+ });
222
+ }
182
223
  return { ok: true, instanceId: res.instanceId, spaceId: res.spaceId, warnings: res.warnings };
183
224
  }
184
225
  catch (e) {
226
+ /* 막힌 자리를 그대로 알린다 — 「끝낸 수」의 다음 단계다(만들 때와 같은 규칙). */
227
+ emit({
228
+ step: (adapterForSteps?.masterSteps?.[lastDone] ?? lastStep),
229
+ done: lastDone,
230
+ total: stepTotal,
231
+ failed: true,
232
+ error: e?.message ?? String(e)
233
+ });
185
234
  return refusal(context, e, 'resync failed');
186
235
  }
187
236
  }
188
237
  /** 기억해 둔 원천에서 마스터를 다시 얻는다 — 어댑터든 템플릿이든 이 한 곳에서 갈린다. */
189
- async masterFromOrigin(domainId, origin, instanceId) {
238
+ async masterFromOrigin(domainId, origin, instanceId,
239
+ /*
240
+ * **다시 읽기도 진행을 보여야 한다** (2026-08-22).
241
+ *
242
+ * 만들 때는 8단계가 화면에 흐르는데 다시 읽을 때는 아무것도 없었다 — 사용자가 버튼을 누르고
243
+ * 「진행되고 있는지 확인할 수가 없다」고 했다. 실제로 chef 는 원본 읽기가 14~21초이고 그 사이
244
+ * 화면에 아무 변화가 없다.
245
+ *
246
+ * 통로는 만들 때와 **같은 것**이다(`twin-reference-progress`). 화면도 같은 표시를 쓴다 —
247
+ * 두 벌을 만들면 한쪽만 고쳐지고 사람은 같은 일이 다르게 보이는 것을 결함으로 읽는다.
248
+ */
249
+ onStep) {
190
250
  if (origin.kind === 'template') {
191
251
  const tpl = (0, template_registry_js_1.getTemplate)(origin.templateId);
192
252
  if (!tpl)
@@ -202,7 +262,7 @@ let TwinReferenceResolver = class TwinReferenceResolver {
202
262
  const inMemory = (0, reference_registry_js_1.getReference)(origin.source);
203
263
  if (inMemory)
204
264
  return { ...inMemory, origin };
205
- const ref = await (0, shell_1.getRepository)(twin_reference_js_1.TwinReference).findOne({ where: { domain: { id: domainId }, source: origin.source } });
265
+ const ref = await (0, shell_2.getRepository)(twin_reference_js_1.TwinReference).findOne({ where: { domain: { id: domainId }, source: origin.source } });
206
266
  if (!ref)
207
267
  throw new Error(`reference "${origin.source}" is no longer registered — nothing to re-read from`);
208
268
  const adapter = (0, reference_adapter_js_1.getAdapter)(ref.adapterType);
@@ -225,7 +285,7 @@ let TwinReferenceResolver = class TwinReferenceResolver {
225
285
  */
226
286
  const picked = (0, reference_master_js_1.pickOriginSite)(sites, origin);
227
287
  if (picked.site)
228
- return { ...(await adapter.fetchMaster(cfg, picked.site)), origin };
288
+ return { ...(await adapter.fetchMaster(cfg, picked.site, onStep)), origin };
229
289
  if (picked.reason === 'site-gone')
230
290
  throw new Error(`reference "${origin.source}" no longer reports site "${origin.siteId}" — refusing to overwrite this twin with a different site`);
231
291
  for (const site of sites) {
@@ -299,9 +359,16 @@ let TwinReferenceResolver = class TwinReferenceResolver {
299
359
  */
300
360
  async twinReferences(context) {
301
361
  const domainId = context.state.domain.id;
302
- const rows = await (0, shell_1.getRepository)(twin_reference_js_1.TwinReference).find({ where: { domain: { id: domainId } } });
303
- // provenance 라이브 대조 — 지워진 트윈은 producedCount 에서 자동 제외(상태 동기의 lazy 방식).
304
- const liveIds = new Set((await (0, shell_1.getRepository)(twin_instance_js_1.TwinInstance).find({ where: { domain: { id: domainId } } })).map(i => i.instanceId));
362
+ const rows = await (0, shell_2.getRepository)(twin_reference_js_1.TwinReference).find({ where: { domain: { id: domainId } } });
363
+ /*
364
+ * provenance 라이브 대조 지워진 트윈은 producedCount 에서 자동 제외(상태 동기의 lazy 방식).
365
+ *
366
+ * **id 만 읽는다** (2026-08-21). 예전에는 인스턴스 행 전체를 읽었고, 그 행에는 **모델 JSON** 이
367
+ * 들어 있다(자리 수백 개 + 발자국 좌표). 그래서 이 목록을 여는 데 수 메가바이트를 읽고 파싱했고,
368
+ * 화면은 그동안 「아직 연결된 시스템이 없습니다」를 보이고 있었다 — 사용자에게는 「없다」는 답으로
369
+ * 읽힌다. 여기서 필요한 것은 살아 있는 id 집합 하나다.
370
+ */
371
+ const liveIds = new Set((await (0, shell_2.getRepository)(twin_instance_js_1.TwinInstance).find({ where: { domain: { id: domainId } }, select: ['instanceId'] })).map(i => i.instanceId));
305
372
  return rows.map(r => {
306
373
  const produced = (r.scopeSpec?.produced ?? []).filter(p => liveIds.has(p.instanceId));
307
374
  return {
@@ -321,8 +388,8 @@ let TwinReferenceResolver = class TwinReferenceResolver {
321
388
  */
322
389
  async twinConnections(context) {
323
390
  const domainId = context.state.domain.id;
324
- const rows = await (0, shell_1.getRepository)(twin_reference_js_1.TwinReference).find({ where: { domain: { id: domainId } } });
325
- const liveIds = new Set((await (0, shell_1.getRepository)(twin_instance_js_1.TwinInstance).find({ where: { domain: { id: domainId } } })).map(i => i.instanceId));
391
+ const rows = await (0, shell_2.getRepository)(twin_reference_js_1.TwinReference).find({ where: { domain: { id: domainId } } });
392
+ const liveIds = new Set((await (0, shell_2.getRepository)(twin_instance_js_1.TwinInstance).find({ where: { domain: { id: domainId } } })).map(i => i.instanceId));
326
393
  const groups = new Map();
327
394
  for (const r of rows) {
328
395
  const connectorType = r.scopeSpec?.connectorType ?? r.adapterType;
@@ -342,11 +409,11 @@ let TwinReferenceResolver = class TwinReferenceResolver {
342
409
  /** 한 Connection 그룹 노드 상세 — 그 아래 생성된 트윈 인스턴스(라이브 대조·상태·기동여부). */
343
410
  async twinConnection(source, context) {
344
411
  const domainId = context.state.domain.id;
345
- const r = await (0, shell_1.getRepository)(twin_reference_js_1.TwinReference).findOne({ where: { domain: { id: domainId }, source } });
412
+ const r = await (0, shell_2.getRepository)(twin_reference_js_1.TwinReference).findOne({ where: { domain: { id: domainId }, source } });
346
413
  if (!r)
347
414
  return null;
348
415
  const produced = (r.scopeSpec?.produced ?? []);
349
- const instMap = new Map((await (0, shell_1.getRepository)(twin_instance_js_1.TwinInstance).find({ where: { domain: { id: domainId } } })).map(i => [i.instanceId, i]));
416
+ const instMap = new Map((await (0, shell_2.getRepository)(twin_instance_js_1.TwinInstance).find({ where: { domain: { id: domainId } } })).map(i => [i.instanceId, i]));
350
417
  const instances = produced
351
418
  .filter(p => instMap.has(p.instanceId))
352
419
  .map(p => {
@@ -366,24 +433,44 @@ let TwinReferenceResolver = class TwinReferenceResolver {
366
433
  }
367
434
  /** 단건 상세(뷰/편집) — connectionConfig 포함, 어댑터 스키마의 secret 필드는 마스킹(있으면 '••••••'). */
368
435
  async twinReference(source, context) {
369
- const r = await (0, shell_1.getRepository)(twin_reference_js_1.TwinReference).findOne({ where: { domain: { id: context.state.domain.id }, source } });
436
+ const r = await (0, shell_2.getRepository)(twin_reference_js_1.TwinReference).findOne({ where: { domain: { id: context.state.domain.id }, source } });
370
437
  if (!r)
371
438
  return null;
372
439
  const secretKeys = new Set(((0, reference_adapter_js_1.getAdapter)(r.adapterType)?.meta?.configSchema ?? []).filter(f => f.secret).map(f => f.key));
373
440
  const cfg = {};
374
441
  for (const [k, v] of Object.entries(r.connectionConfig ?? {}))
375
442
  cfg[k] = secretKeys.has(k) ? (v ? '••••••' : '') : v;
443
+ /*
444
+ * **이 연결이 만든 트윈 id 를 함께 낸다.** 상세 화면이 「이 연결이 잘 동기화되고 있나」를 물으려면
445
+ * 어느 트윈을 물어야 하는지 알아야 한다. 예전에는 개수(`producedCount`)만 있어서 화면이 id 를
446
+ * 알 방법이 없었다.
447
+ */
448
+ const producedInstanceIds = [
449
+ ...new Set((r.scopeSpec?.produced ?? [])
450
+ .map((p) => p?.instanceId)
451
+ .filter((x) => typeof x === 'string' && !!x))
452
+ ];
376
453
  return {
377
454
  source: r.source, system: r.system, siteName: r.siteName, description: r.description,
378
- adapterType: r.adapterType, connectionConfig: cfg, status: r.status, lastSyncedAt: r.lastSyncedAt, lastError: r.lastError
455
+ adapterType: r.adapterType, connectionConfig: cfg, status: r.status, lastSyncedAt: r.lastSyncedAt, lastError: r.lastError,
456
+ producedInstanceIds
379
457
  };
380
458
  }
381
459
  referenceAdapters() {
382
- return (0, reference_adapter_js_1.listAdapters)().map(a => ({ type: a.type, ...(a.meta ?? {}) }));
460
+ /*
461
+ * **능력을 함께 낸다** (2026-08-21).
462
+ *
463
+ * `capabilities` 는 어댑터 뿌리에 있고 `meta` 에는 없었다. 그래서 연결 화면이 「이 원본이 라이브를
464
+ * 낼 수 있나」를 알 수 없었고, 트윈을 만들 때 `live` 를 말하지 못해 **재기동 정책이 없다고 거절**됐다
465
+ * (실 연동에서 그것에 막혔다 — 미러는 정의상 `resync` 이므로 `live` 하나로 정해진다).
466
+ *
467
+ * 화면이 그 사실을 알아야 정책을 사람에게 물을지 스스로 정할지 가를 수 있다.
468
+ */
469
+ return (0, reference_adapter_js_1.listAdapters)().map(a => ({ type: a.type, ...(a.meta ?? {}), capabilities: a.capabilities ?? [] }));
383
470
  }
384
471
  async saveTwinReference(source, patch, context) {
385
472
  const domainId = context.state.domain.id;
386
- const repo = (0, shell_1.getRepository)(twin_reference_js_1.TwinReference);
473
+ const repo = (0, shell_2.getRepository)(twin_reference_js_1.TwinReference);
387
474
  const existing = await repo.findOne({ where: { domain: { id: domainId }, source } });
388
475
  const saved = await repo.save(repo.create({
389
476
  ...(existing ?? {}),
@@ -406,7 +493,7 @@ let TwinReferenceResolver = class TwinReferenceResolver {
406
493
  /** 연결 해제 — removeTwins=true 면 이 레퍼런스가 만든 트윈(provenance)도 함께 제거(라이프사이클 연쇄). 기본은 트윈 보존(분리). */
407
494
  async deleteTwinReference(source, context, removeTwins) {
408
495
  const domainId = context.state.domain.id;
409
- const repo = (0, shell_1.getRepository)(twin_reference_js_1.TwinReference);
496
+ const repo = (0, shell_2.getRepository)(twin_reference_js_1.TwinReference);
410
497
  const ref = await repo.findOne({ where: { domain: { id: domainId }, source } });
411
498
  if (removeTwins && ref) {
412
499
  for (const p of (ref.scopeSpec?.produced ?? [])) {
@@ -422,7 +509,7 @@ let TwinReferenceResolver = class TwinReferenceResolver {
422
509
  return true;
423
510
  }
424
511
  async testReferenceConnection(source, context) {
425
- const repo = (0, shell_1.getRepository)(twin_reference_js_1.TwinReference);
512
+ const repo = (0, shell_2.getRepository)(twin_reference_js_1.TwinReference);
426
513
  const ref = await repo.findOne({ where: { domain: { id: context.state.domain.id }, source } });
427
514
  if (!ref)
428
515
  return { ok: false, error: `unknown reference: ${source}` };
@@ -450,7 +537,7 @@ let TwinReferenceResolver = class TwinReferenceResolver {
450
537
  * 연결에서 가장 자주 막히는 자리인데 자격이 틀렸는지 주소가 틀렸는지 알 수 없었다.
451
538
  * 판정과 문장 형태는 `discovery-result`(순수)가 갖는다.
452
539
  */
453
- const ref = await (0, shell_1.getRepository)(twin_reference_js_1.TwinReference).findOne({ where: { domain: { id: context.state.domain.id }, source } });
540
+ const ref = await (0, shell_2.getRepository)(twin_reference_js_1.TwinReference).findOne({ where: { domain: { id: context.state.domain.id }, source } });
454
541
  if (!ref)
455
542
  return (0, discovery_result_js_1.failed)((0, discovery_result_js_1.referenceNotFound)(source));
456
543
  const adapter = (0, reference_adapter_js_1.getAdapter)(ref.adapterType);
@@ -486,7 +573,7 @@ let TwinReferenceResolver = class TwinReferenceResolver {
486
573
  */
487
574
  async importTwinReference(source, context, siteIds, start, live, spaceId, restartPolicy) {
488
575
  const domainId = context.state.domain.id;
489
- const repo = (0, shell_1.getRepository)(twin_reference_js_1.TwinReference);
576
+ const repo = (0, shell_2.getRepository)(twin_reference_js_1.TwinReference);
490
577
  const ref = await repo.findOne({ where: { domain: { id: domainId }, source } });
491
578
  if (!ref)
492
579
  return { ok: false, error: `unknown reference: ${source}` };
@@ -504,9 +591,55 @@ let TwinReferenceResolver = class TwinReferenceResolver {
504
591
  return { ok: false, error: refusal.message, errorCode: refusal.code, errorParams: refusal.params };
505
592
  const instances = [];
506
593
  const failed = [];
507
- for (const site of sites) {
594
+ /*
595
+ * **진행을 구독으로 흘린다** (2026-08-21).
596
+ *
597
+ * 마스터 읽기는 원본에 여러 질의를 보내는 긴 작업이고, 화면은 그 사이 아무것도 알 수 없었다.
598
+ * 단계를 아는 것은 어댑터뿐이므로 어댑터가 말하고(표준 낱말), 호스트가 자기 두 단계(생성·기동)를
599
+ * 더해 흘린다.
600
+ *
601
+ * **분모는 어댑터가 선언한 것**이다(`masterSteps`). 선언하지 않은 어댑터는 `total` 이 없고, 그때
602
+ * 화면은 퍼센트를 만들지 않는다 — 모르는 진행률을 지어내지 않는다.
603
+ */
604
+ const declared = adapter.masterSteps?.length;
605
+ const stepTotal = declared === undefined ? undefined : declared + reference_progress_js_1.HOST_STEPS.length;
606
+ const emit = (p) => shell_1.pubsub.publish('twin-reference-progress', {
607
+ twinReferenceProgress: { ...p, siteCount: sites.length, total: p.total ?? stepTotal, source }
608
+ });
609
+ /*
610
+ * **읽은 뒤의 총평을 사이트마다 남긴다** (2026-08-22).
611
+ *
612
+ * 총평은 읽는 순간에만 있었다 — 대화상자를 닫으면 사라지고, 다시 보려면 같은 읽기를 또 해야 했다.
613
+ * 그런데 사람이 그것을 묻는 자리는 「모델 살펴보기」다(트윈을 보다가 「이건 왜 비었나」를 묻는다).
614
+ * 그래서 레퍼런스 행에 남겨 그 화면이 언제든 읽게 한다.
615
+ *
616
+ * 사실만 담는다(`assessMaster`) — 문장은 화면이 만들고, 더 알고 싶을 때 AI 가 이 사실만 근거로
617
+ * 서술한다. 다시 읽으면 그 사이트의 총평이 새것으로 바뀐다(지난 읽기의 총평을 현재로 보이게
618
+ * 두지 않는다).
619
+ */
620
+ const assessments = {};
621
+ for (const [siteIndex, site] of sites.entries()) {
622
+ /*
623
+ * **실패를 실제로 막힌 단계에 적는다** (2026-08-22).
624
+ *
625
+ * 예전에는 실패를 무조건 `provision` 으로 적었다. 그래서 원본을 읽다 끊긴 것도 화면에는
626
+ * 「트윈 생성에서 실패」로 떴다 — 사람이 원인을 엉뚱한 자리에서 찾는다(실제로 그렇게 됐다).
627
+ *
628
+ * 규약이 답을 준다: `done` 은 **끝낸 단계 수**다(`ReferenceProgress.done`). 그러면 지금 막힌 것은
629
+ * 그 다음 단계, 즉 `masterSteps[done]` 이다. 그 표를 넘어서면(호스트 단계) 마지막으로 알린 것을 쓴다.
630
+ *
631
+ * 이 규칙은 두 가지 보고 방식에 함께 맞는다 — 단계 **시작**에 알리는 어댑터(`done` 이 아직 0)와
632
+ * 단계 **완료**에 알리는 어댑터(`done` 이 1 늘어난다) 모두 「끝낸 수」만 지키면 된다.
633
+ */
634
+ let lastDone = 0;
635
+ let lastStep = (adapter.masterSteps?.[0] ?? 'provision');
636
+ const track = (p) => {
637
+ lastDone = p.done;
638
+ lastStep = p.step;
639
+ emit(p);
640
+ };
508
641
  try {
509
- const master = await adapter.fetchMaster(cfg, site);
642
+ const master = await adapter.fetchMaster(cfg, site, st => track({ siteId: site.siteId, siteIndex, step: st.key, done: st.done, total: stepTotal, detail: st.detail }));
510
643
  /* 어느 레퍼런스의 **어느 사이트**에서 왔는지 남긴다 — 다시 읽기가 같은 어댑터·같은 사이트로 돌아오려면
511
644
  사이트 정체가 있어야 한다(`source` 는 참조 이름이라 사이트 전부에 같다). */
512
645
  /*
@@ -514,11 +647,18 @@ let TwinReferenceResolver = class TwinReferenceResolver {
514
647
  * 미러는 정의상 `resync` 다(외부가 진실이므로 우리 과거를 이어 붙이지 않는다). 시뮬로 띄우려면
515
648
  * 스스로 선언해야 한다 — 그 자리에서 우리가 고르면 재기동마다 이력을 지우는 트윈이 조용히 생긴다.
516
649
  */
650
+ assessments[site.siteId] = (0, reference_assessment_js_1.assessMaster)(master, site.siteId);
517
651
  const policy = (0, restart_policy_js_1.readRestartPolicy)(restartPolicy ?? (live ? 'resync' : undefined), `importTwinReference("${source}")`);
652
+ track({ siteId: site.siteId, siteIndex, step: 'provision', done: declared ?? 0, total: stepTotal });
518
653
  const res = await index_js_1.TwinEngine.ingestMaster(domainId, { ...master, origin: { kind: 'reference', source, siteId: site.siteId, ...(spaceId ? { spaceId } : {}) } }, policy, { spaceId }, context.state.user);
654
+ track({ siteId: site.siteId, siteIndex, step: 'start', done: (declared ?? 0) + 1, total: stepTotal });
519
655
  if (start !== false) {
520
656
  // live=true + 어댑터가 openLiveFeed 지원 → mirror(실 이벤트 미러). 아니면 sim 기동(fallback).
521
- const wentLive = live ? await (0, reference_live_js_1.startReferenceLiveFeed)(domainId, ref.adapterType, cfg, site, res.instanceId) : false;
657
+ /* 읽기 커서를 저장할 자리를 함께 넘긴다 레퍼런스의 이름은 호스트가 붙이는 것이고
658
+ 커넥터는 그것을 알 수 없다(§`startReferenceLiveFeed`). */
659
+ const wentLive = live
660
+ ? await (0, reference_live_js_1.startReferenceLiveFeed)(domainId, ref.adapterType, cfg, site, res.instanceId, ref.source)
661
+ : false;
522
662
  if (!wentLive)
523
663
  await index_js_1.TwinEngine.startFromRegistry(domainId, res.instanceId);
524
664
  instances.push({ siteId: site.siteId, instanceId: res.instanceId, spaceId: res.spaceId, mode: wentLive ? 'live' : 'sim', warnings: res.warnings });
@@ -528,6 +668,12 @@ let TwinReferenceResolver = class TwinReferenceResolver {
528
668
  }
529
669
  }
530
670
  catch (e) {
671
+ /*
672
+ * **실패한 단계를 그대로 알린다.** 화면이 진행 표시를 지우지 않고 그 자리를 붉게 두어야
673
+ * 사람이 「어디까지 갔는지」를 안다. 막힌 자리는 「끝낸 수」의 다음 단계다(위 주석).
674
+ */
675
+ const at = (adapter.masterSteps?.[lastDone] ?? lastStep);
676
+ emit({ siteId: site.siteId, siteIndex, step: at, done: lastDone, total: stepTotal, failed: true, error: e?.message ?? String(e) });
531
677
  failed.push({ siteId: site.siteId, error: e?.message ?? 'failed' });
532
678
  }
533
679
  }
@@ -537,6 +683,10 @@ let TwinReferenceResolver = class TwinReferenceResolver {
537
683
  await repo.update({ id: ref.id }, {
538
684
  status: 'connected',
539
685
  lastSyncedAt: new Date().toISOString(),
686
+ /* 못 옮긴 값을 **세어 남긴다**. 아무 인스턴스도 보고하지 않았으면 `null` — 「없었다」로 위장하지 않는다. */
687
+ lastWarningCount: instances.some((i) => Array.isArray(i.warnings))
688
+ ? instances.reduce((n, i) => n + (Array.isArray(i.warnings) ? i.warnings.length : 0), 0)
689
+ : null,
540
690
  /*
541
691
  * **영어 canonical 로 남긴다.** 이 값은 DB 에 저장되고 연결 화면이 그대로 뿌린다 — 예전에는
542
692
  * 한글 산문(`N개 사이트 실패`)이라 다섯 언어 화면에 한글이 새어 나갔다. 어댑터가 주는 사유도
@@ -544,7 +694,24 @@ let TwinReferenceResolver = class TwinReferenceResolver {
544
694
  * 어느 사이트가 왜 실패했는지는 `failed[]` 가 답한다 — 화면이 그것을 보여 준다.
545
695
  */
546
696
  lastError: failed.length ? `${failed.length} of ${sites.length} site(s) failed: ${failed.map((f) => f.siteId).join(', ')}` : null,
547
- scopeSpec: { ...(ref.scopeSpec ?? {}), produced: [...prev, ...producedNew] }
697
+ /*
698
+ * ── 실패 사유를 **남긴다** (2026-08-21) ────────────────────────────────
699
+ *
700
+ * 사유는 대화상자에만 있었다. 닫으면 사라지고, 다시 보려면 같은 실패를 다시 만들어야 했다 —
701
+ * 실제로 「승화푸드」 연결이 그 상태였다(요약 한 줄만 남고 원인은 어디에도 없었다).
702
+ *
703
+ * `scopeSpec` 은 이미 이 레퍼런스의 부속 사실을 담는 자리이므로(`produced`) 스키마를 늘리지 않고
704
+ * 여기 적는다. 성공한 회차에는 **비운다** — 지나간 실패를 현재 상태처럼 보이게 두지 않는다.
705
+ */
706
+ scopeSpec: {
707
+ ...(ref.scopeSpec ?? {}),
708
+ produced: [...prev, ...producedNew],
709
+ /* 이번에 읽은 사이트의 총평만 갈아 끼운다 — 읽지 않은 사이트의 것은 그대로 둔다. */
710
+ assessments: { ...((ref.scopeSpec ?? {}).assessments ?? {}), ...assessments },
711
+ ...(failed.length
712
+ ? { lastFailures: failed.map((f) => ({ siteId: f.siteId, error: f.error, at: new Date().toISOString() })) }
713
+ : { lastFailures: [] })
714
+ }
548
715
  });
549
716
  return { ok: instances.length > 0, instances, failed };
550
717
  }
@@ -557,7 +724,7 @@ let TwinReferenceResolver = class TwinReferenceResolver {
557
724
  };
558
725
  exports.TwinReferenceResolver = TwinReferenceResolver;
559
726
  tslib_1.__decorate([
560
- (0, type_graphql_1.Query)(returns => [shell_1.ScalarObject], { description: 'List registered reference sources (virtual or real adapters) available to ingest.' }),
727
+ (0, type_graphql_1.Query)(returns => [shell_2.ScalarObject], { description: 'List registered reference sources (virtual or real adapters) available to ingest.' }),
561
728
  tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
562
729
  tslib_1.__metadata("design:type", Function),
563
730
  tslib_1.__metadata("design:paramtypes", [Object]),
@@ -565,7 +732,7 @@ tslib_1.__decorate([
565
732
  ], TwinReferenceResolver.prototype, "referenceSources", null);
566
733
  tslib_1.__decorate([
567
734
  (0, type_graphql_1.Directive)('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)'),
568
- (0, type_graphql_1.Mutation)(returns => shell_1.ScalarObject, {
735
+ (0, type_graphql_1.Mutation)(returns => shell_2.ScalarObject, {
569
736
  description: "Ingest a registered reference source into a twin (space + instance). Optionally start it. restartPolicy declares how the new twin treats its own past on reboot: 'resync' (read the source again) | 'resume' (continue its journal) | 'reset' (wipe and replay from the seed). It has no default — a twin cannot exist without the declaration."
570
737
  }),
571
738
  tslib_1.__param(0, (0, type_graphql_1.Arg)('source')),
@@ -578,7 +745,7 @@ tslib_1.__decorate([
578
745
  ], TwinReferenceResolver.prototype, "importFromReference", null);
579
746
  tslib_1.__decorate([
580
747
  (0, type_graphql_1.Directive)('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)'),
581
- (0, type_graphql_1.Mutation)(returns => shell_1.ScalarObject, {
748
+ (0, type_graphql_1.Mutation)(returns => shell_2.ScalarObject, {
582
749
  description: "Re-read a twin's structure from the source it remembers (TwinInstance.origin). Works for both reference- and template-born twins. A running mirror adopts the new structure without stopping; a running simulation is refused."
583
750
  }),
584
751
  tslib_1.__param(0, (0, type_graphql_1.Arg)('instanceId')),
@@ -588,7 +755,7 @@ tslib_1.__decorate([
588
755
  tslib_1.__metadata("design:returntype", Promise)
589
756
  ], TwinReferenceResolver.prototype, "resyncTwin", null);
590
757
  tslib_1.__decorate([
591
- (0, type_graphql_1.Query)(returns => [shell_1.ScalarObject], { description: 'List registered twin templates (parameterized archetypes) with their knob schema.' }),
758
+ (0, type_graphql_1.Query)(returns => [shell_2.ScalarObject], { description: 'List registered twin templates (parameterized archetypes) with their knob schema.' }),
592
759
  tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
593
760
  tslib_1.__metadata("design:type", Function),
594
761
  tslib_1.__metadata("design:paramtypes", [Object]),
@@ -596,11 +763,11 @@ tslib_1.__decorate([
596
763
  ], TwinReferenceResolver.prototype, "twinTemplates", null);
597
764
  tslib_1.__decorate([
598
765
  (0, type_graphql_1.Directive)('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)'),
599
- (0, type_graphql_1.Mutation)(returns => shell_1.ScalarObject, {
766
+ (0, type_graphql_1.Mutation)(returns => shell_2.ScalarObject, {
600
767
  description: "Instantiate a twin template with params → synthetic master → ingest (+start+scenario). restartPolicy declares how the new twin treats its own past on reboot ('resync' | 'resume' | 'reset') — there is no default. Returns {ok, instanceId, spaceId}."
601
768
  }),
602
769
  tslib_1.__param(0, (0, type_graphql_1.Arg)('templateId')),
603
- tslib_1.__param(1, (0, type_graphql_1.Arg)('params', type => shell_1.ScalarObject)),
770
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('params', type => shell_2.ScalarObject)),
604
771
  tslib_1.__param(2, (0, type_graphql_1.Arg)('restartPolicy')),
605
772
  tslib_1.__param(3, (0, type_graphql_1.Ctx)()),
606
773
  tslib_1.__param(4, (0, type_graphql_1.Arg)('start', { nullable: true })),
@@ -610,7 +777,7 @@ tslib_1.__decorate([
610
777
  tslib_1.__metadata("design:returntype", Promise)
611
778
  ], TwinReferenceResolver.prototype, "createFromTemplate", null);
612
779
  tslib_1.__decorate([
613
- (0, type_graphql_1.Query)(returns => [shell_1.ScalarObject], { description: 'List tenant-scoped persisted twin references (adapter-backed).' }),
780
+ (0, type_graphql_1.Query)(returns => [shell_2.ScalarObject], { description: 'List tenant-scoped persisted twin references (adapter-backed).' }),
614
781
  tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
615
782
  tslib_1.__metadata("design:type", Function),
616
783
  tslib_1.__metadata("design:paramtypes", [Object]),
@@ -623,14 +790,14 @@ tslib_1.__decorate([
623
790
  tslib_1.__metadata("design:returntype", Array)
624
791
  ], TwinReferenceResolver.prototype, "referenceAdapterTypes", null);
625
792
  tslib_1.__decorate([
626
- (0, type_graphql_1.Query)(returns => [shell_1.ScalarObject], { description: 'Group integration items 2-tier: connector type ▸ connection ▸ produced twins. (reference-management §2.3)' }),
793
+ (0, type_graphql_1.Query)(returns => [shell_2.ScalarObject], { description: 'Group integration items 2-tier: connector type ▸ connection ▸ produced twins. (reference-management §2.3)' }),
627
794
  tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
628
795
  tslib_1.__metadata("design:type", Function),
629
796
  tslib_1.__metadata("design:paramtypes", [Object]),
630
797
  tslib_1.__metadata("design:returntype", Promise)
631
798
  ], TwinReferenceResolver.prototype, "twinConnections", null);
632
799
  tslib_1.__decorate([
633
- (0, type_graphql_1.Query)(returns => shell_1.ScalarObject, { nullable: true, description: 'One connection grouping location with its produced twin instances (live-checked). (reference-management §2.3)' }),
800
+ (0, type_graphql_1.Query)(returns => shell_2.ScalarObject, { nullable: true, description: 'One connection grouping location with its produced twin instances (live-checked). (reference-management §2.3)' }),
634
801
  tslib_1.__param(0, (0, type_graphql_1.Arg)('source')),
635
802
  tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
636
803
  tslib_1.__metadata("design:type", Function),
@@ -638,7 +805,7 @@ tslib_1.__decorate([
638
805
  tslib_1.__metadata("design:returntype", Promise)
639
806
  ], TwinReferenceResolver.prototype, "twinConnection", null);
640
807
  tslib_1.__decorate([
641
- (0, type_graphql_1.Query)(returns => shell_1.ScalarObject, { nullable: true, description: 'Get one tenant-scoped reference with its connection config (secret fields masked) for view/edit.' }),
808
+ (0, type_graphql_1.Query)(returns => shell_2.ScalarObject, { nullable: true, description: 'Get one tenant-scoped reference with its connection config (secret fields masked) for view/edit.' }),
642
809
  tslib_1.__param(0, (0, type_graphql_1.Arg)('source')),
643
810
  tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
644
811
  tslib_1.__metadata("design:type", Function),
@@ -646,16 +813,16 @@ tslib_1.__decorate([
646
813
  tslib_1.__metadata("design:returntype", Promise)
647
814
  ], TwinReferenceResolver.prototype, "twinReference", null);
648
815
  tslib_1.__decorate([
649
- (0, type_graphql_1.Query)(returns => [shell_1.ScalarObject], { description: 'List reference connector metadata (label, domain, config schema, availability) for the connect UI.' }),
816
+ (0, type_graphql_1.Query)(returns => [shell_2.ScalarObject], { description: 'List reference connector metadata (label, domain, config schema, availability, capabilities) for the connect UI.' }),
650
817
  tslib_1.__metadata("design:type", Function),
651
818
  tslib_1.__metadata("design:paramtypes", []),
652
819
  tslib_1.__metadata("design:returntype", Array)
653
820
  ], TwinReferenceResolver.prototype, "referenceAdapters", null);
654
821
  tslib_1.__decorate([
655
822
  (0, type_graphql_1.Directive)('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)'),
656
- (0, type_graphql_1.Mutation)(returns => shell_1.ScalarObject, { description: 'Create or update a tenant-scoped twin reference (persisted).' }),
823
+ (0, type_graphql_1.Mutation)(returns => shell_2.ScalarObject, { description: 'Create or update a tenant-scoped twin reference (persisted).' }),
657
824
  tslib_1.__param(0, (0, type_graphql_1.Arg)('source')),
658
- tslib_1.__param(1, (0, type_graphql_1.Arg)('patch', type => shell_1.ScalarObject)),
825
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('patch', type => shell_2.ScalarObject)),
659
826
  tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
660
827
  tslib_1.__metadata("design:type", Function),
661
828
  tslib_1.__metadata("design:paramtypes", [String, Object, Object]),
@@ -673,7 +840,7 @@ tslib_1.__decorate([
673
840
  ], TwinReferenceResolver.prototype, "deleteTwinReference", null);
674
841
  tslib_1.__decorate([
675
842
  (0, type_graphql_1.Directive)('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)'),
676
- (0, type_graphql_1.Mutation)(returns => shell_1.ScalarObject, { description: 'Test a reference connection via its adapter; updates status.' }),
843
+ (0, type_graphql_1.Mutation)(returns => shell_2.ScalarObject, { description: 'Test a reference connection via its adapter; updates status.' }),
677
844
  tslib_1.__param(0, (0, type_graphql_1.Arg)('source')),
678
845
  tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
679
846
  tslib_1.__metadata("design:type", Function),
@@ -681,7 +848,7 @@ tslib_1.__decorate([
681
848
  tslib_1.__metadata("design:returntype", Promise)
682
849
  ], TwinReferenceResolver.prototype, "testReferenceConnection", null);
683
850
  tslib_1.__decorate([
684
- (0, type_graphql_1.Query)(returns => shell_1.ScalarObject, { description: 'Discover sites/factories exposed by a reference. Returns { sites, error? } — an empty list with no error means the system genuinely has none, which is different from a failure.' }),
851
+ (0, type_graphql_1.Query)(returns => shell_2.ScalarObject, { description: 'Discover sites/factories exposed by a reference. Returns { sites, error? } — an empty list with no error means the system genuinely has none, which is different from a failure.' }),
685
852
  tslib_1.__param(0, (0, type_graphql_1.Arg)('source')),
686
853
  tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
687
854
  tslib_1.__metadata("design:type", Function),
@@ -690,7 +857,7 @@ tslib_1.__decorate([
690
857
  ], TwinReferenceResolver.prototype, "discoverReferenceSites", null);
691
858
  tslib_1.__decorate([
692
859
  (0, type_graphql_1.Directive)('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)'),
693
- (0, type_graphql_1.Mutation)(returns => shell_1.ScalarObject, {
860
+ (0, type_graphql_1.Mutation)(returns => shell_2.ScalarObject, {
694
861
  description: "Create twins from a reference: discover sites → filter by siteIds (if given) → fetch master(s) → provision. spaceId co-locates the twin into an existing space (one site only). start=false to only provision; live=true to run as a mirror (adapter openLiveFeed → live projector) instead of sim. restartPolicy declares how each new twin treats its own past on reboot ('resync' | 'resume' | 'reset'); omit it and live=true implies 'resync' — a mirror reads the source again — while a simulation must declare 'resume' or 'reset' for itself."
695
862
  }),
696
863
  tslib_1.__param(0, (0, type_graphql_1.Arg)('source')),