@opetope/runtime 0.1.0

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 (314) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +345 -0
  4. package/README.ru.md +344 -0
  5. package/dist/application-compiler-edges.d.ts +3 -0
  6. package/dist/application-compiler-edges.js +2 -0
  7. package/dist/application-compiler-edges.js.map +1 -0
  8. package/dist/application-compiler-graph.d.ts +8 -0
  9. package/dist/application-compiler-graph.js +2 -0
  10. package/dist/application-compiler-graph.js.map +1 -0
  11. package/dist/application-compiler.d.ts +116 -0
  12. package/dist/application-compiler.js +2 -0
  13. package/dist/application-compiler.js.map +1 -0
  14. package/dist/application-conditions.d.ts +18 -0
  15. package/dist/application-conditions.js +2 -0
  16. package/dist/application-conditions.js.map +1 -0
  17. package/dist/application-definition.d.ts +30 -0
  18. package/dist/application-definition.js +2 -0
  19. package/dist/application-definition.js.map +1 -0
  20. package/dist/application-error.d.ts +10 -0
  21. package/dist/application-error.js +2 -0
  22. package/dist/application-error.js.map +1 -0
  23. package/dist/application-execution.d.ts +43 -0
  24. package/dist/application-execution.js +2 -0
  25. package/dist/application-execution.js.map +1 -0
  26. package/dist/application-feature-bindings.d.ts +11 -0
  27. package/dist/application-feature-bindings.js +2 -0
  28. package/dist/application-feature-bindings.js.map +1 -0
  29. package/dist/application-feature-instance.d.ts +5 -0
  30. package/dist/application-feature-instance.js +2 -0
  31. package/dist/application-feature-instance.js.map +1 -0
  32. package/dist/application-group-order.d.ts +29 -0
  33. package/dist/application-group-order.js +2 -0
  34. package/dist/application-group-order.js.map +1 -0
  35. package/dist/application-instance-retirement.d.ts +36 -0
  36. package/dist/application-instance-retirement.js +2 -0
  37. package/dist/application-instance-retirement.js.map +1 -0
  38. package/dist/application-open-options.d.ts +29 -0
  39. package/dist/application-open-options.js +2 -0
  40. package/dist/application-open-options.js.map +1 -0
  41. package/dist/application-port-compiler.d.ts +28 -0
  42. package/dist/application-port-compiler.js +2 -0
  43. package/dist/application-port-compiler.js.map +1 -0
  44. package/dist/attachment-call-declaration.d.ts +43 -0
  45. package/dist/attachment-call-declaration.js +2 -0
  46. package/dist/attachment-call-declaration.js.map +1 -0
  47. package/dist/attachment-declaration.d.ts +68 -0
  48. package/dist/attachment-declaration.js +2 -0
  49. package/dist/attachment-declaration.js.map +1 -0
  50. package/dist/attachment-execution.d.ts +9 -0
  51. package/dist/attachment-execution.js +2 -0
  52. package/dist/attachment-execution.js.map +1 -0
  53. package/dist/attachment-retirement-scheduler.d.ts +14 -0
  54. package/dist/attachment-retirement-scheduler.js +2 -0
  55. package/dist/attachment-retirement-scheduler.js.map +1 -0
  56. package/dist/call-option-snapshot.d.ts +20 -0
  57. package/dist/call-option-snapshot.js +2 -0
  58. package/dist/call-option-snapshot.js.map +1 -0
  59. package/dist/compile-call-target-bindings.d.ts +14 -0
  60. package/dist/compile-call-target-bindings.js +2 -0
  61. package/dist/compile-call-target-bindings.js.map +1 -0
  62. package/dist/compile-module-template.d.ts +38 -0
  63. package/dist/compile-module-template.js +2 -0
  64. package/dist/compile-module-template.js.map +1 -0
  65. package/dist/condition-group-execution.d.ts +44 -0
  66. package/dist/condition-group-execution.js +2 -0
  67. package/dist/condition-group-execution.js.map +1 -0
  68. package/dist/condition-override.d.ts +28 -0
  69. package/dist/condition-override.js +2 -0
  70. package/dist/condition-override.js.map +1 -0
  71. package/dist/condition-source.d.ts +10 -0
  72. package/dist/condition-source.js +2 -0
  73. package/dist/condition-source.js.map +1 -0
  74. package/dist/condition-types.d.ts +14 -0
  75. package/dist/condition.d.ts +28 -0
  76. package/dist/condition.js +2 -0
  77. package/dist/condition.js.map +1 -0
  78. package/dist/control-registry.d.ts +56 -0
  79. package/dist/control-registry.js +2 -0
  80. package/dist/control-registry.js.map +1 -0
  81. package/dist/dynamic-scope-child.d.ts +23 -0
  82. package/dist/dynamic-scope-child.js +2 -0
  83. package/dist/dynamic-scope-child.js.map +1 -0
  84. package/dist/dynamic-scope-controller.d.ts +16 -0
  85. package/dist/dynamic-scope-controller.js +2 -0
  86. package/dist/dynamic-scope-controller.js.map +1 -0
  87. package/dist/feature-attachment-authoring-types.d.ts +51 -0
  88. package/dist/feature-attachment-lowering.d.ts +18 -0
  89. package/dist/feature-attachment-lowering.js +2 -0
  90. package/dist/feature-attachment-lowering.js.map +1 -0
  91. package/dist/feature-attachment.d.ts +45 -0
  92. package/dist/feature-attachment.js +2 -0
  93. package/dist/feature-attachment.js.map +1 -0
  94. package/dist/feature-authoring-types.d.ts +196 -0
  95. package/dist/feature-authoring.d.ts +15 -0
  96. package/dist/feature-authoring.js +2 -0
  97. package/dist/feature-authoring.js.map +1 -0
  98. package/dist/feature-body.d.ts +53 -0
  99. package/dist/feature-body.js +2 -0
  100. package/dist/feature-body.js.map +1 -0
  101. package/dist/feature-call-authority.d.ts +8 -0
  102. package/dist/feature-call-authority.js +2 -0
  103. package/dist/feature-call-authority.js.map +1 -0
  104. package/dist/feature-call-types.d.ts +39 -0
  105. package/dist/feature-call.d.ts +18 -0
  106. package/dist/feature-call.js +2 -0
  107. package/dist/feature-call.js.map +1 -0
  108. package/dist/feature-calls.d.ts +6 -0
  109. package/dist/feature-calls.js +2 -0
  110. package/dist/feature-calls.js.map +1 -0
  111. package/dist/feature-contract.d.ts +70 -0
  112. package/dist/feature-contract.js +2 -0
  113. package/dist/feature-contract.js.map +1 -0
  114. package/dist/feature-contribution-model.d.ts +38 -0
  115. package/dist/feature-contribution-model.js +2 -0
  116. package/dist/feature-contribution-model.js.map +1 -0
  117. package/dist/feature-contribution.d.ts +126 -0
  118. package/dist/feature-contribution.js +2 -0
  119. package/dist/feature-contribution.js.map +1 -0
  120. package/dist/feature-definition-api.d.ts +53 -0
  121. package/dist/feature-definition-support.d.ts +21 -0
  122. package/dist/feature-definition-support.js +2 -0
  123. package/dist/feature-definition-support.js.map +1 -0
  124. package/dist/feature-effect.d.ts +26 -0
  125. package/dist/feature-effect.js +2 -0
  126. package/dist/feature-effect.js.map +1 -0
  127. package/dist/feature-event.d.ts +32 -0
  128. package/dist/feature-event.js +2 -0
  129. package/dist/feature-event.js.map +1 -0
  130. package/dist/feature-generation.d.ts +34 -0
  131. package/dist/feature-generation.js +2 -0
  132. package/dist/feature-generation.js.map +1 -0
  133. package/dist/feature-lazy-generation.d.ts +6 -0
  134. package/dist/feature-lazy-generation.js +2 -0
  135. package/dist/feature-lazy-generation.js.map +1 -0
  136. package/dist/feature-lazy.d.ts +44 -0
  137. package/dist/feature-lazy.js +2 -0
  138. package/dist/feature-lazy.js.map +1 -0
  139. package/dist/feature-materialization-binding.d.ts +31 -0
  140. package/dist/feature-materialization-binding.js +2 -0
  141. package/dist/feature-materialization-binding.js.map +1 -0
  142. package/dist/feature-materialization-types.d.ts +45 -0
  143. package/dist/feature-model-dependencies.d.ts +23 -0
  144. package/dist/feature-model-dependencies.js +2 -0
  145. package/dist/feature-model-dependencies.js.map +1 -0
  146. package/dist/feature-model.d.ts +60 -0
  147. package/dist/feature-model.js +2 -0
  148. package/dist/feature-model.js.map +1 -0
  149. package/dist/feature-optional.d.ts +15 -0
  150. package/dist/feature-optional.js +2 -0
  151. package/dist/feature-optional.js.map +1 -0
  152. package/dist/feature-own-lowering.d.ts +22 -0
  153. package/dist/feature-own-lowering.js +2 -0
  154. package/dist/feature-own-lowering.js.map +1 -0
  155. package/dist/feature-port-binding.d.ts +6 -0
  156. package/dist/feature-port-binding.js +2 -0
  157. package/dist/feature-port-binding.js.map +1 -0
  158. package/dist/feature-port.d.ts +66 -0
  159. package/dist/feature-port.js +2 -0
  160. package/dist/feature-port.js.map +1 -0
  161. package/dist/feature-record.d.ts +6 -0
  162. package/dist/feature-record.js +2 -0
  163. package/dist/feature-record.js.map +1 -0
  164. package/dist/feature-resource.d.ts +29 -0
  165. package/dist/feature-resource.js +2 -0
  166. package/dist/feature-resource.js.map +1 -0
  167. package/dist/feature-scope-types.d.ts +35 -0
  168. package/dist/feature-scope.d.ts +29 -0
  169. package/dist/feature-scope.js +2 -0
  170. package/dist/feature-scope.js.map +1 -0
  171. package/dist/feature-stream.d.ts +37 -0
  172. package/dist/feature-stream.js +2 -0
  173. package/dist/feature-stream.js.map +1 -0
  174. package/dist/feature-timers.d.ts +14 -0
  175. package/dist/feature-timers.js +2 -0
  176. package/dist/feature-timers.js.map +1 -0
  177. package/dist/index.d.ts +19 -0
  178. package/dist/index.js +2 -0
  179. package/dist/index.js.map +1 -0
  180. package/dist/inspection-activity-protocol.d.ts +63 -0
  181. package/dist/inspection-activity.d.ts +54 -0
  182. package/dist/inspection-activity.js +2 -0
  183. package/dist/inspection-activity.js.map +1 -0
  184. package/dist/inspection-diff.d.ts +10 -0
  185. package/dist/inspection-diff.js +2 -0
  186. package/dist/inspection-diff.js.map +1 -0
  187. package/dist/inspection-module-activity.d.ts +4 -0
  188. package/dist/inspection-module-activity.js +2 -0
  189. package/dist/inspection-module-activity.js.map +1 -0
  190. package/dist/inspection-observer.d.ts +38 -0
  191. package/dist/inspection-observer.js +2 -0
  192. package/dist/inspection-observer.js.map +1 -0
  193. package/dist/inspection-plan.d.ts +33 -0
  194. package/dist/inspection-plan.js +2 -0
  195. package/dist/inspection-plan.js.map +1 -0
  196. package/dist/inspection-protocol.d.ts +333 -0
  197. package/dist/inspection-protocol.js +2 -0
  198. package/dist/inspection-protocol.js.map +1 -0
  199. package/dist/inspection-registry.d.ts +41 -0
  200. package/dist/inspection-registry.js +2 -0
  201. package/dist/inspection-registry.js.map +1 -0
  202. package/dist/inspection-session.d.ts +92 -0
  203. package/dist/inspection-session.js +2 -0
  204. package/dist/inspection-session.js.map +1 -0
  205. package/dist/inspection-snapshot.d.ts +4 -0
  206. package/dist/inspection-snapshot.js +2 -0
  207. package/dist/inspection-snapshot.js.map +1 -0
  208. package/dist/inspection-state.d.ts +91 -0
  209. package/dist/inspection-state.js +2 -0
  210. package/dist/inspection-state.js.map +1 -0
  211. package/dist/instance-demand.d.ts +31 -0
  212. package/dist/instance-demand.js +2 -0
  213. package/dist/instance-demand.js.map +1 -0
  214. package/dist/internal.d.ts +41 -0
  215. package/dist/internal.js +2 -0
  216. package/dist/internal.js.map +1 -0
  217. package/dist/keyed-scope-controller.d.ts +11 -0
  218. package/dist/keyed-scope-controller.js +2 -0
  219. package/dist/keyed-scope-controller.js.map +1 -0
  220. package/dist/model-kernel.d.ts +18 -0
  221. package/dist/model-kernel.js +2 -0
  222. package/dist/model-kernel.js.map +1 -0
  223. package/dist/module-call-context.d.ts +7 -0
  224. package/dist/module-call-context.js +2 -0
  225. package/dist/module-call-context.js.map +1 -0
  226. package/dist/module-call-runtime.d.ts +21 -0
  227. package/dist/module-call-runtime.js +2 -0
  228. package/dist/module-call-runtime.js.map +1 -0
  229. package/dist/module-generation.d.ts +57 -0
  230. package/dist/module-generation.js +2 -0
  231. package/dist/module-generation.js.map +1 -0
  232. package/dist/module-instance-types.d.ts +156 -0
  233. package/dist/module-instance.d.ts +19 -0
  234. package/dist/module-instance.js +2 -0
  235. package/dist/module-instance.js.map +1 -0
  236. package/dist/module-runtime-identity.d.ts +4 -0
  237. package/dist/module-runtime-identity.js +2 -0
  238. package/dist/module-runtime-identity.js.map +1 -0
  239. package/dist/module-scope-open.d.ts +4 -0
  240. package/dist/module-scope-open.js +2 -0
  241. package/dist/module-scope-open.js.map +1 -0
  242. package/dist/module-scope-retirement.d.ts +3 -0
  243. package/dist/module-scope-retirement.js +2 -0
  244. package/dist/module-scope-retirement.js.map +1 -0
  245. package/dist/module-template-ir.d.ts +59 -0
  246. package/dist/owner-generation-retirement.d.ts +6 -0
  247. package/dist/owner-generation-retirement.js +2 -0
  248. package/dist/owner-generation-retirement.js.map +1 -0
  249. package/dist/owner-generation-state.d.ts +123 -0
  250. package/dist/owner-generation-state.js +2 -0
  251. package/dist/owner-generation-state.js.map +1 -0
  252. package/dist/owner-generation.d.ts +16 -0
  253. package/dist/owner-generation.js +2 -0
  254. package/dist/owner-generation.js.map +1 -0
  255. package/dist/public-module-definition.d.ts +9 -0
  256. package/dist/public-module-definition.js +2 -0
  257. package/dist/public-module-definition.js.map +1 -0
  258. package/dist/public-module-instance.d.ts +6 -0
  259. package/dist/public-module-instance.js +2 -0
  260. package/dist/public-module-instance.js.map +1 -0
  261. package/dist/public-module-retirement-diagnostics.d.ts +5 -0
  262. package/dist/public-module-retirement-diagnostics.js +2 -0
  263. package/dist/public-module-retirement-diagnostics.js.map +1 -0
  264. package/dist/public-module-retirement.d.ts +3 -0
  265. package/dist/public-module-retirement.js +2 -0
  266. package/dist/public-module-retirement.js.map +1 -0
  267. package/dist/public-module-scope.d.ts +5 -0
  268. package/dist/public-module-scope.js +2 -0
  269. package/dist/public-module-scope.js.map +1 -0
  270. package/dist/public-module-state.d.ts +28 -0
  271. package/dist/public-module-state.js +2 -0
  272. package/dist/public-module-state.js.map +1 -0
  273. package/dist/public-module-types.d.ts +295 -0
  274. package/dist/public-module.d.ts +4 -0
  275. package/dist/resource-cache.d.ts +12 -0
  276. package/dist/resource-cache.js +2 -0
  277. package/dist/resource-cache.js.map +1 -0
  278. package/dist/resource-controller.d.ts +29 -0
  279. package/dist/resource-controller.js +2 -0
  280. package/dist/resource-controller.js.map +1 -0
  281. package/dist/resource-policy.d.ts +16 -0
  282. package/dist/resource-policy.js +2 -0
  283. package/dist/resource-policy.js.map +1 -0
  284. package/dist/resource-snapshot.d.ts +12 -0
  285. package/dist/resource-snapshot.js +2 -0
  286. package/dist/resource-snapshot.js.map +1 -0
  287. package/dist/resource-types.d.ts +3 -0
  288. package/dist/runtime-error-reporting.d.ts +4 -0
  289. package/dist/runtime-error-reporting.js +2 -0
  290. package/dist/runtime-error-reporting.js.map +1 -0
  291. package/dist/stream-backpressure.d.ts +18 -0
  292. package/dist/stream-backpressure.js +2 -0
  293. package/dist/stream-backpressure.js.map +1 -0
  294. package/dist/stream-cleanup.d.ts +17 -0
  295. package/dist/stream-cleanup.js +2 -0
  296. package/dist/stream-cleanup.js.map +1 -0
  297. package/dist/stream-controller-types.d.ts +51 -0
  298. package/dist/stream-controller.d.ts +5 -0
  299. package/dist/stream-controller.js +2 -0
  300. package/dist/stream-controller.js.map +1 -0
  301. package/docs/agent-guide.md +214 -0
  302. package/docs/agent-guide.ru.md +208 -0
  303. package/docs/cookbook.md +734 -0
  304. package/docs/cookbook.ru.md +729 -0
  305. package/docs/decisions.md +1437 -0
  306. package/docs/devtools.md +423 -0
  307. package/docs/devtools.ru.md +419 -0
  308. package/docs/how-it-works.md +521 -0
  309. package/docs/how-it-works.ru.md +495 -0
  310. package/docs/releases.md +78 -0
  311. package/docs/releases.ru.md +78 -0
  312. package/docs/spec.md +874 -0
  313. package/docs/spec.ru.md +884 -0
  314. package/package.json +72 -0
@@ -0,0 +1,419 @@
1
+ # Opetope Devtools
2
+
3
+ Одна правдивая проекция скомпилированной топологии и текущего состояния рантайма и ответ на вопрос «почему этот узел
4
+ сейчас в этом состоянии». `@opetope/devtools` — необязательный инструмент разработки; исключайте подключение панели из production-сборки.
5
+
6
+ Этот документ нормативен для devtools: протокол наблюдения, архитектура его доставки, панель, которая его рисует, и
7
+ управление условиями, которое пишет обратно. При расхождении этого документа с рантаймом побеждают
8
+ рантайм и [`spec.md`](spec.ru.md), а расхождение фиксируется строкой в §9.
9
+
10
+ ## 1. Что это и чем это не является
11
+
12
+ Отвечает на вопросы: почему фича не активна; чего она ждёт; какой экземпляр текущий, а какой ушёл; куда доходят её
13
+ вклады; что случилось перед отказом.
14
+
15
+ Не читает значения моделей и репозиториев, пропсы React и полезную нагрузку вызовов. Не воспроизводит эффекты, не
16
+ меняет граф объявлений, не заменяет продуктовые флаги и не ходит в сеть. Текст ошибки в протокол не попадает: отказ
17
+ это ограниченный код, субъект и фаза.
18
+
19
+ ## 2. Протокол наблюдения
20
+
21
+ ### 2.1 Две проекции
22
+
23
+ Снимок несёт `plan` — неизменную топологию, которую построил компилятор приложения, — и `runtime` — живой мир,
24
+ который тот открыл. Их никогда не смешивают: объявление фичи переживает много своих экземпляров, а склейка этих двух
25
+ вещей и есть ошибка, ради которой протокол существует. Топология неизменна внутри одной сессии; замена плана
26
+ открывает новый `sessionId`.
27
+
28
+ ### 2.2 Узлы
29
+
30
+ Названы публичными словами [`spec.md`](spec.ru.md) §3: `application`, `condition`, `condition-group`, `feature`,
31
+ `feature-instance`, `attachment`, `contribution`, `contribution-target`, `port`, `host-contract`, `absent-provider`.
32
+
33
+ Группа условий это выведенный владелец активации: фичи, чей `when` называет один и тот же набор условий, образуют
34
+ одну группу. Фича с пустым `when` принадлежит самому приложению. `absent-provider` это контракт экспорта фичи,
35
+ который план называет, но за которым ничего нет, чтобы у ребра `absent-import` было начало.
36
+
37
+ ### 2.3 Рёбра
38
+
39
+ Направление зафиксировано: провайдер, блокер или владелец → потребитель, ожидающий или владеемый узел. Три класса:
40
+
41
+ - **жёсткие** — `hard-import`, `requires-port`. Единственные рёбра, которых ждёт активация; несут `pending`.
42
+ - **слабые** — `optional-import`, `optional-port`, `absent-import`, `absent-port`. Одно слово `optional` и для фич,
43
+ и для портов. Потребитель может жить дольше провайдера, читает `Lookup` (`found` или `missing`) и никогда не ждёт.
44
+ - **структурные** — `owns`, `provides-port`, `contributes-to`, `condition-control`, `on-demand-import`. Собственного
45
+ состояния не несут.
46
+
47
+ Компилятор выдаёт один вид `requires-port` для обеих сил; протокол делит его по полю
48
+ `CompiledApplicationRequirementIR.optional`, симметрично паре импортов, которую компилятор делит сам.
49
+
50
+ ### 2.4 Автоматы состояний и три оси
51
+
52
+ Универсального `node.status` нет. Каждый вид несёт тот автомат, который у рантайма действительно есть: группа условий
53
+ из `condition-group-execution.ts`, экземпляр фичи из кодов `FeatureError` в `spec.md` §4, attachment из
54
+ `AttachmentLifecycleSnapshot`, вклад из хаба (`published`, и `withheld`, когда его `when` читается как false), порт из
55
+ компиляторного выбора единственного провайдера, host-контракт из `bind`.
56
+
57
+ Панель стилизует узлы тремя ортогональными осями, выведенными из этих объединений, и никогда наоборот:
58
+
59
+ - `desired` — `active` либо `inactive`, со значениями источника, override и итоговым;
60
+ - `lifecycle` — `declared`, `waiting`, `opening`, `ready`, `fencing`, `retiring`, `closed`;
61
+ - `health` — `healthy`, `blocked`, `failed`, `cancelled`, `stale`, `quarantined`.
62
+
63
+ `declared` это седьмое значение оси: фича, которую план объявил, а экземпляр не открывал, не находится ни в одном из
64
+ остальных шести, и назвать её `waiting` значило бы заявить блокера, которого нет.
65
+
66
+ Загрузка `body` заголовка восьмого значения не добавляет. Экземпляр существует и находится в `opening`, пока его код
67
+ едет, — так это докладывает рантайм и так это рисует панель (D186); собственный `loading` был бы вторым словом для
68
+ состояния, которое в протоколе уже есть, и легенде пришлось бы объяснять разницу.
69
+
70
+ ### 2.5 Отказы, фазы и причины
71
+
72
+ Отказ это собственная ошибка рантайма, дискриминированная пара `subject` и `code`: `feature`, `call`, `readable`,
73
+ `contribution`, `application`, `declaration`. Фазы — словарь D14: `open`, `close`, `rollback`, `fence`, `drain`,
74
+ `retry`. Причина это ref, который панель разрешает по снимку, а не проза.
75
+
76
+ У протокола ровно одни часы, `cause.at`, и они отмечают триггер, а не коммит. Записи состояний часов не несут: вторые
77
+ часы позволили бы панели показать длительность, которой рантайм не измерял. У кадра без причины времени нет, и
78
+ timeline оставляет эту колонку пустой.
79
+
80
+ Если `cause.at` присутствует, это неотрицательное целое число миллисекунд эпохи не больше
81
+ `8_640_000_000_000_000`, максимального допустимого времени Date. Счётчики sequence и revision сохраняют отдельный
82
+ домен неотрицательных безопасных целых (D242).
83
+
84
+ ### 2.6 Идентичность, метки и редакция
85
+
86
+ Идентификаторы это данные, и один идентификатор называет одну вещь. Диагностический id это префикс вида и id
87
+ объявлений его частей, соединённые через `:`; часть сначала экранируется через `_`, поэтому символ, который
88
+ грамматика потребителя не принимает — `/` в id объявления, `|` в ключе группы условий, — остаётся собой, а не
89
+ сливается с соседом, и части составного id остаются разделимыми (D200). Метка это presentation metadata, выведенная
90
+ компилятором из объявления: панель её рисует и никогда не переводит, и она никогда не даёт прав. В снимке нет ни колбэков, ни `FeatureInstance`, ни моделей и
91
+ ресурсов, ни сырого `Error`, `AbortSignal` или thenable, ни DOM и React-объектов. Экспорт это канонический
92
+ data-only JSON под лимитами записей, в буфер обмена или локальный файл; сетевого транспорта нет.
93
+
94
+ Узел приложения имеет id `application:<экранированный id объявления>`; узел фичи сохраняет экранированный id
95
+ объявления. Приложение и фича могут иметь одинаковый id объявления, сохраняя разные диагностические identities (D242).
96
+
97
+ ## 3. Архитектура наблюдения
98
+
99
+ ### 3.1 Кому принадлежит семантика
100
+
101
+ Рантайм владеет идентификаторами, видами, переходами и причинными ссылками. Devtools не сканирует приватные карты, не
102
+ патчит методы и не восстанавливает граф по логам, поэтому продовый планировщик и отладчик описывают одну машину.
103
+ `RuntimeInspectionSession` это _потребляемый_ интерфейс: семантика принадлежит рантайму, и он ничего отсюда не
104
+ импортирует, поэтому в рантайме об этом пакете ничего не известно. Протокол, редьюсер, проекции и панель тоже не
105
+ импортируют ни один пакет `@opetope/*`. Единственное исключение это `./integration`, который открывает порты за
106
+ приложение и потому держит `@opetope/runtime/internal` (D248).
107
+
108
+ ### 3.2 Подключение
109
+
110
+ `createInspectionSession(execution)` на `@opetope/runtime/internal`, доступный только тому, кто держит
111
+ `ApplicationExecution`, созданный хостом. Ни process-wide синглтона, ни глобального хука. Без сессии рантайм не
112
+ аллоцирует записей, а закрытие приложения уносит сессию с собой.
113
+
114
+ Приложение эту фабрику не вызывает. Оно передаёт execution в `installDevtools({ execution, … })` из
115
+ `@opetope/devtools/integration`, который открывает порт наблюдения — и, при `mode: 'control'`, порт записи — и
116
+ владеет обоими до закрытия своего handle: `/internal` это шов между пакетами самой библиотеки, а не поверхность,
117
+ до которой приложению положено доставать (D248).
118
+
119
+ Подключение возвращает отключение, и оно принадлежит владельцу времени жизни execution, а не тому, кто его открыл.
120
+ Панель приезжает динамическим импортом, поэтому приложение, закрытое до его резолва, иначе получило бы панель,
121
+ которая ему уже не нужна, а следующее приложение застало бы на странице предыдущую.
122
+
123
+ ### 3.3 Доставка, разрывы и resync
124
+
125
+ Рантайм фиксирует кадр и уведомляет; клиент читает пакет сам, поэтому медленная или закрытая панель не создаёт
126
+ backpressure. Доставка может быть вне очереди перехода — слушателя не зовут внутри транзакции. Кадр называет свою
127
+ сессию, базовую и следующую ревизию и диапазон номеров; разрыв, дубль, скачок ревизии или чужая сессия отвергаются в
128
+ пользу свежего снимка, потому что частично применённая дельта это отладчик, который врёт. История ограничена и числом
129
+ кадров, и приблизительными байтами, а переполнение оставляет метку разрыва, а не молчаливую дыру.
130
+
131
+ Продюсер `createInspectionSession(execution, { ringCapacity })` принимает неотрицательное безопасное целое,
132
+ по умолчанию 256 кадров. Ноль не удерживает кадры: снимки остаются доступны, а читатель, отставший от отброшенного
133
+ перехода, получает `resync-required`. Неверная ёмкость отвергается до занятия observation slot (D242).
134
+ Кадр с пустыми `operations` всё ещё фиксирует транзакцию, если несёт замену `activity`, включая пустую замену,
135
+ очищающую прежнюю activity. Кадр без обоих изменений отвергается contract kit.
136
+
137
+ ### 3.4 Нейтральность к спросу
138
+
139
+ Ноль, один или несколько подключённых инспекторов оставляют исходы приложения одинаковыми. Наблюдение не создаёт
140
+ спроса, не удерживает экземпляр и никогда не меняет того, что публикует фича.
141
+
142
+ ## 4. Панель
143
+
144
+ ### 4.1 Две вкладки
145
+
146
+ `Graph` и `Timeline`. Одна показывает граф в момент времени, другая историю коммитов. Всё, что раньше было вкладкой,
147
+ стало фильтром внутри `Graph`.
148
+
149
+ ### 4.2 Чипы фильтра рёбер и пресеты
150
+
151
+ Чип включает и выключает вид ребра; пресет это именованный набор чипов.
152
+
153
+ | Пресет | Что включает | На какой вопрос отвечает |
154
+ | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
155
+ | Импорты и порты | `hard-import`, `optional-import`, `absent-import`, `on-demand-import`, `requires-port`, `optional-port`, `absent-port`, `provides-port` | Кто кому нужен и что из этого сейчас недоступно |
156
+ | Условия | `condition-control`, `owns` от группы условий | Кто гейтит эту фичу и что ещё живёт в её группе |
157
+ | Владение | `owns` | Что этот узел породил и что переживёт его |
158
+ | Точки расширения | `contributes-to`, `owns` до вкладов, плюс второй шаг проекции | Куда уходят вклады этой фичи и кто ещё вкладывается в ту же цель |
159
+
160
+ Первые три — честные фильтры рёбер. Четвёртый исключение, и это названо прямо: вклад это узел _между_ фичей и целью,
161
+ поэтому окрестность в один шаг показывает вклады, но не показывает, куда они идут, — то есть не отвечает на вопрос,
162
+ ради которого её открывают. Он строит два шага, фича → вклады → цели, и показывает других контрибьюторов той же
163
+ цели, потому что приоритет и конкуренция видны только рядом. Выбор цели переворачивает проекцию.
164
+
165
+ ### 4.3 Выделение, breadcrumbs, инспектор
166
+
167
+ Одно выделение, один граф, один инспектор. Смена пресета не меняет выделение, а выделение не сбрасывает пресет.
168
+ Breadcrumbs показывают путь до выбранного узла и работают как навигация: крошка предка возвращает к нему, текущий
169
+ узел остаётся подписью. Инспектор показывает идентичность и происхождение, текущее состояние и счётчик экземпляра,
170
+ непосредственную причину, зависимости узла с `Lookup` у каждой слабой строки, чем он владеет и последнюю причинную
171
+ цепочку.
172
+
173
+ Имя, которое не влезает в свою рамку, сокращает панель, а не рамка: сосед пишется относительно выделения, не
174
+ влезающее и после этого имя режется по середине, чтобы уцелел последний сегмент, строка группы говорит `when` один
175
+ раз и называет условия в теге рядом, а полный id всегда лежит в `title`. Многоточие справа съело бы хвост, а именно
176
+ хвост и отличает двух соседей друг от друга.
177
+
178
+ ### 4.4 Узкие экраны
179
+
180
+ Ниже 1100 px три колонки перестают читаться, и окрестность рисуется списком. Список и инспектор при этом делят вторую
181
+ колонку стопкой, а не выпадает один из них: панель, которая не может сказать, чего узел ждёт, теряет вопрос, ради
182
+ которого существует. Ручного переключателя `Graph`/`List` нет ни на какой ширине: список не даёт ничего сверх графа, а
183
+ вторая форма стоит второй раскладки, вторых тестов и вторых строк словаря.
184
+
185
+ ### 4.5 Статус-бар
186
+
187
+ Постоянно видно то, что отвечает на вопрос «можно ли верить тому, что я вижу»: `frame · state rev`, число `dropped` и
188
+ `LIVE`. За «⋯»: `reducer · paint ms`, источник темы и ревизия схемы — они описывают панель, а не приложение.
189
+
190
+ ### 4.6 Кодирование состояния, легенда, локаль
191
+
192
+ Состояние никогда не передаётся одним цветом: глиф, цвет и текстовый тег вместе, у каждого свой label для скринридера.
193
+ Одна сворачиваемая легенда покрывает семь состояний lifecycle, три визуальных вида рёбер с перечислением протокольных
194
+ рёбер под каждым и три бейджа осей.
195
+
196
+ Панель говорит по-английски и по-русски, по умолчанию берёт язык браузера, переключается в шапке. Проза переводится,
197
+ словарь протокола — нет: `Feature`, `Condition`, `declared`, `quarantined`, `hard-import`, `found`, `missing`, `drain`
198
+ это идентификаторы, которые разработчик грепает и сверяет с `spec.md` §3, поэтому они вставляются _в_ переведённые
199
+ предложения, а не заменяются. Тест рендерит панель по-русски по всем поверхностям и падает на любом латинском слове
200
+ вне двух списков: словаря протокола и слов, пришедших из самого графа.
201
+
202
+ ### 4.7 Камера
203
+
204
+ Линза обрезает содержимое, поэтому добраться до узла вне кадра — работа камеры: перетаскивание пустого места двигает,
205
+ колесо приближает вокруг курсора, кнопки дают намеренный шаг 1.2× вокруг центра, а открытие узла вписывает граф.
206
+ Колесо масштабирует по величине жеста, а не фиксированным множителем, `deltaMode` приводится к пикселям, и один кадр
207
+ зажат, чтобы грубое устройство не перепрыгнуло диапазон. Протяжка двигает камеру и ничего не выделяет.
208
+
209
+ ## 5. Управление условиями
210
+
211
+ ### 5.1 Что можно переключать
212
+
213
+ Только условие и его группу. Узел графа это диагностическая запись, а не изменяемая ручка: прямая запись состояния
214
+ обошла бы планировщик, жёсткие зависимости, вклады, фенс, уборку и карантин. Фича с пустым `when` не отключается, и
215
+ это закон, а не пробел: синтетические условия на каждой фиче отвергнуты, потому что сделали бы отладчик источником
216
+ времени жизни, которого в плане нет.
217
+
218
+ ### 5.2 Действия
219
+
220
+ | Действие | Где стоит | Что делает | Чего не обещает |
221
+ | --------------- | ---------------------------- | ------------------------------------------------------------------------ | ---------------------------------------- |
222
+ | `Suspend` | узел группы условий | Ставит override «принудительно false»; группа гаснет обычной транзакцией | Не отменяет продуктовый источник условия |
223
+ | `Resume` | узел группы условий | Снимает override; группа снова следует источнику | Не обещает `ready` |
224
+ | `Retry cleanup` | узел карантинного экземпляра | Повторяет уборку карантинного фронтира через существующий retry-контракт | Не объявляет зависший ресурс закрытым |
225
+
226
+ `Retry cleanup` стоит на экземпляре, потому что карантин принадлежит экземпляру: это его уборка не завершилась. Он
227
+ предлагается только там, где карантинный фронтир есть, а приложение доходит до него, открываясь с
228
+ `cleanupFailure: 'quarantine'` (D182); при умолчании `report` карантина не бывает, а значит нет и кнопки.
229
+
230
+ Команды стоят в двух местах, и ни одно из них не сама карточка: панель над канвасом, которая принадлежит выбранному
231
+ узлу, не будучи нарисованной внутри него, и инспектор. Карточка остаётся одной целью выбора — вложенный в неё
232
+ контрол был бы кнопкой внутри кнопки, а плавающий над ней уезжал бы с каждым панорамированием.
233
+
234
+ Бейдж override стоит рядом с командами, пока override держится, и читается из снимка самой группы, а не из того, что
235
+ панель просила. Применение спрашивает подтверждение, потому что меняет живое приложение, и второе нажатие называет
236
+ цель. Каждое применение и каждый отказ доходят до timeline отдельной audit-строкой.
237
+
238
+ ### 5.3 Закон зависимостей
239
+
240
+ Обратного каскада нет. `Suspend` группы-провайдера отправляет жёстких зависимых по их обычной семантике недоступности
241
+ и retirement, а слабые продолжают работать и читают `missing`. `Suspend` группы-потребителя не гасит провайдера:
242
+ потеря потребителя не выключает никого.
243
+
244
+ ### 5.4 Контракт write-порта
245
+
246
+ Отдельная фабрика рядом с портом наблюдения, потому что держать inspection-сессию не должно значить иметь право
247
+ писать.
248
+
249
+ ```ts
250
+ interface RuntimeControlSession {
251
+ close(): void;
252
+ resume(conditionId: string): Promise<ControlOutcome>;
253
+ retryCleanup(instanceId: string): Promise<ControlOutcome>;
254
+ suspend(conditionId: string): Promise<ControlOutcome>;
255
+ }
256
+
257
+ type ControlOutcome =
258
+ | { readonly kind: 'applied'; readonly sequence: number }
259
+ | { readonly kind: 'rejected'; readonly reason: 'not-controllable' | 'stale' | 'unknown-target' };
260
+
261
+ declare function createControlSession(execution: ApplicationExecution): RuntimeControlSession;
262
+ ```
263
+
264
+ Применение сериализуется тем же планировщиком, который ведёт переходы условий, и публикует обычный атомарный кадр,
265
+ поэтому панель узнаёт результат тем же способом, что и всё остальное; `sequence` указывает на кадр, где эффект виден.
266
+ Отказ это данные, а не исключение. У одного execution одновременно живёт не больше одной сессии управления: второй
267
+ `createControlSession` при открытой первой бросает, а `close()` освобождает слот, поэтому перезагруженная панель может
268
+ взять управление снова (D191). Хост его тоже не открывает: это делает `installDevtools` при `mode: 'control'`, и
269
+ отказ даёт провалившуюся установку, а не панель, которая молча поднялась read-only (D248). `close()` снимает и все
270
+ override этой сессии, поэтому закрытие панели не оставляет приложение в отлаженном состоянии, а сам override не
271
+ переживает reload и HMR.
272
+
273
+ Команды управления используют текущую схему graph/frame `/3` без отдельной revision. Применённая команда выразима: кадр несёт существующую
274
+ причину `control-command` со своим условием, а снимок группы заполняет существующее поле
275
+ `desired.override: 'force-inactive'` рядом с `desired.source` — именно его читает бейдж override. Отказ не порождает
276
+ кадра вовсе, потому что кадр, который ничего не коммитит, лгал бы.
277
+
278
+ Поэтому audit-след ведёт сама панель: она пишет локальные строки timeline — `control · suspend cond.x · applied →
279
+ frame N`, `control · resume cond.y · rejected · unknown-target` — и связывает применённую строку с кадром продюсера
280
+ по `sequence` из исхода. Требование владельца «каждое применение и каждый отказ видны одной лентой» выполняется без
281
+ изобретения кадра, которого рантайм не коммитил.
282
+
283
+ Машинерия `preview → apply` с одноразовым permit не берётся. Управляемый субъект один, его эффект описан законом
284
+ зависимостей и уже нарисован в графе, поэтому предпросмотр дублировал бы граф; подтверждение плюс audit-след это
285
+ соразмерная форма. Если у управления появится вторая цель, вопрос откроется заново.
286
+
287
+ ## 6. Производительность и память
288
+
289
+ Снимок строится за `O(N + E)` без рекурсивного обхода на пути приёма; кадр стоит `O(изменённых записей)`. Дерево
290
+ кэширует декларативную топологию на сессию и разрешает display по нарисованной строке, поэтому коммит, меняющий
291
+ только состояние, его не перестраивает. Поиск, проекция инспектора и layout графа мемоизированы по счётчику версий
292
+ стора, который меняется ровно раз на применённый кадр. Виртуализация списков/инспектора от 200 строк и worker layout
293
+ от 300 узлов — предложенные пороги, не реализованное поведение и не измеренные гарантии. Текущие список и layout
294
+ работают на главном потоке; будущей реализации нужны измерения корпуса на этих границах. Панель держит для
295
+ себя p95 в 4 мс на применение кадра не более чем со 100 изменёнными записями и один кадр анимации на отрисовку, и
296
+ сама показывает обе цифры в status bar, поэтому регрессия видна там, где случилась, а не в отчёте.
297
+
298
+ Поверхности, о которой сессия не попросила, в бандле панели нет вовсе. Четыре грузятся по требованию, у каждой свой
299
+ бюджет: control plane — когда хост передал порт записи и в выделении есть адресуемый субъект, легенда — при первом
300
+ открытии, выгрузка — по нажатию, которое её использует, хронология — вместе со своей вкладкой. Каждая несёт и
301
+ собственный словарь, поэтому панель, которая ни одну из них не открыла, не везёт ни их кода, ни их слов. Этим бюджет
302
+ панели и остаётся честным по мере её роста: сегодня 20.18 кБ из 21 против 1.68, 1.45, 1.14 и 0.91 кБ за собственными
303
+ cap-ами и 5.02 из 5.5 кБ у headless session consumer (D240, D241).
304
+
305
+ Поверхность может и не приехать. Панель держит каждую как pending, loaded или failed: не приехавшая говорит об этом
306
+ на своём месте и предлагает попробовать снова, летящий запрос разделяется вместо второго, а неудачная попытка
307
+ забывается, чтобы повтор был настоящим повтором. Ничто не остаётся отклонённым промисом, о котором узнает хост, —
308
+ это то же правило, которому следуют исходы копирования и выгрузки (§7).
309
+
310
+ Измеряй приложение отдельно с выключенным, подключённым и отключённым inspection. Фиксируй режим сборки,
311
+ начальные байты и загрузку по требованию, задержку взаимодействия и удержанную память. Особенности загрузки
312
+ и backend конкретного приложения не являются гарантиями производительности библиотеки.
313
+
314
+ ## 7. Семантика отказов
315
+
316
+ | Событие | Требуемое поведение |
317
+ | --------------------------- | ---------------------------------------------------------------- |
318
+ | Наблюдатель бросил | Отцеплен и доложен; переход уже зафиксирован |
319
+ | Переполнение очереди | Ограниченная метка разрыва, resync по снимку, приложение не ждёт |
320
+ | Дубль или нарушение порядка | Отвергнуть кадр и пересинхронизироваться |
321
+ | Кадр от заменённого плана | Игнорировать |
322
+ | Отказ первого снимка | Закрыть сессию, доложить `snapshot-failed`, ничего не менять |
323
+ | Карантин уборки | Логический фенс сохраняется; только точный повтор |
324
+ | Панель размонтирована | Отцепить потребителя, освободить UI-состояние, приложение живёт |
325
+
326
+ Недоверенные данные — воспроизведённый экспорт, фикстура из отчёта об ошибке — проходят строгий декодер с бюджетами
327
+ по числу записей, длине строк и байтам. Незнакомые виды и лишние поля отвергаются вместе с записью, а не терпятся:
328
+ совместимость вперёд несёт ревизия схемы, а не снисходительность. Свойство-аксессор отвергается до чтения — и поле
329
+ записи, и элемент массива, — а ни один метод входного значения не вызывается, поэтому payload не может исполнить код
330
+ внутри декодера. Бюджет байтов считается в UTF-8 байтах до разбора: лимит, названный в байтах, но считающий
331
+ UTF-16 units, втрое слабее, чем читается. Приоритет вклада принимает любое конечное число, включая отрицательное
332
+ и дробное, как и producer рантайма; счётчики и индексы сохраняют собственные целочисленные ограничения (D212).
333
+
334
+ У идентификаторов собственные длины, выведенные из законов producer-а, а не выбранные: атом это declaration id не
335
+ длиннее 160 символов, экранирование добавляет по символу на разделитель (D200), node id добавляет самый длинный
336
+ префикс, а edge id экранирует оба конца ещё раз. Условие публикуется одним экранированным атомом и адресуется им, а
337
+ написанный автором declaration id остаётся в label: границей служит экранированная длина, а показывается условие
338
+ везде по label. Плоский лимит короче этой арифметики отвергает снимки, которые законное приложение производит. Группа, ключ которой соединяет больше условий, чем помещается в один экранированный
339
+ атом, отвергается как слишком длинный id — это отказ по бюджету, как `maxNodes`, а не утверждение о форме.
340
+
341
+ ## 8. Приёмка
342
+
343
+ Топология и экземпляры не склеиваются. Снимок плюс непрерывные кадры детерминированно воспроизводят состояние на
344
+ каждой границе коммита. Кадр виден только после атомарного коммита. Устаревшие, дублирующие, разорванные и чужие
345
+ кадры отвергаются. У каждого состояния ожидания, отказа и карантина есть типизированная причина. Ничто не читается
346
+ как работающее после фенса владельца. Число инспекторов не меняет исходов. В проде чанка devtools нет.
347
+
348
+ Исполняемая форма — `assertInspectionSessionContract` в `@opetope/devtools/testing`: рантайм, реализующий порт,
349
+ прогоняет её на своей сессии, не читая исходники devtools.
350
+
351
+ ## 9. Лог решений
352
+
353
+ Строки, описывающие текущее поведение. Вытесненные свёрнуты в одну строку вместо полного текста.
354
+
355
+ ### 9.1 Протокол
356
+
357
+ | № | Решение |
358
+ | --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
359
+ | T1 | У кадра нет собственных часов. `cause.at` единственные, и они отмечают триггер; записи состояний часов не несут, и длительности из времён коммитов не выводятся |
360
+ | T3 | `declared` это седьмое значение оси lifecycle, для фичи, которую план объявил, а экземпляр не открывал |
361
+ | T4 | Таксономия узлов пересобрана по IR плана и публичному словарю; `activation-domain`, `module-*`, `eligibility-attempt`, `capability-resolution`, `scope-generation`, `view-root` удалены |
362
+ | T6 | Шесть activation-семантик сведены к двум классам, которые различает компилятор: жёсткие рёбра несут `pending`, слабые несут `Lookup` и никогда не ждут |
363
+ | T7 | У `start-after` нет соответствия: порядок открытия выведенный, а не объявленный, и живёт в `plan.groups[].order` |
364
+ | T8 | Провайдер выбирается на компиляции, поэтому в рантайме нет ни допуска, ни списка кандидатов, ни неоднозначности — только вопрос, есть ли у выбранного провайдера живой экземпляр |
365
+ | T13 | `requires-port` делится на жёсткий и слабый по `CompiledApplicationRequirementIR.optional`, потому что компилятор выдаёт один вид для обоих |
366
+ | T14 | Рёбра порта идут через узел порта: `provides-port` от провайдера, `requires-port` и `absent-port` к потребителю |
367
+ | T15 | `absent-provider` это узел, чтобы у ребра `absent-import` было начало |
368
+ | T16 | Вклад это два ребра: `owns` от контрибьютора и `contributes-to` к цели |
369
+ | T17 | Отказы это собственные классы ошибок рантайма как дискриминированная пара `subject` и `code` |
370
+ | T19 | Схема `opetope.devtools-graph/3` и `opetope.devtools-frame/3` |
371
+ | T23 | Враждебный декодер строгий: незнакомые поля и виды отвергаются вместе с записью, аксессоры отвергаются до чтения, а загрязняющие ключи — всегда |
372
+ | T25 | Перф-корпус генерируется и детерминирован по построению: 1 330 узлов / 5 008 рёбер и 26 331 / 70 040 |
373
+ | T26 | Структура дерева кэшируется на сессию, display разрешается по нарисованной строке; поиск, проекция и layout мемоизированы по версии стора |
374
+ | T28 | Canary фазы 4. Одиночный flip `when` даёт один кадр `contribution-change` и переводит вклад `published` ↔ `withheld`; закрытие живого приложения снимает его экземпляры и вклады. Оба были сломаны и починены D167; тесты написаны так, чтобы маскировка не повторилась |
375
+ | T29 | Потребляемый интерфейс принимает те идентификаторы, которые объявляет рантайм. Бренды это необязательные маркеры, поэтому плоская строка принимается и ни один хост не несёт каста; гарантия живёт в валидаторах и декодере |
376
+ | T31 | Матрица close/detach/GC: утечки в пакете нет. Рост на цикл затухает, а выживающий контейнер держит один из module-level слотов root-а React DOM — `rootWithNestedUpdates` или `rootWithPassiveNestedUpdates`; наблюдался passive, а он есть только в development-сборке. Оба держат по одному контейнеру за раз, оба вне devtools, а панель dev-only, поэтому в проде вопрос не встаёт |
377
+ | T33 | Отключение, которое возвращает подключение, принадлежит launcher-у, владеющему execution: оно срабатывает до close, отсекает панель с поздним импортом и закрывает ровно тот handle, который поставило |
378
+ | T34 | Декодер читает элементы массива по индексу через их собственные дескрипторы: элемент бывает аксессором ровно так же, как поле, а `map` входного массива — метод payload-а, а не наш. Бюджет текста считает UTF-8 байты, а оценки сверху и снизу по длине в code units держат точный подсчёт вне горячего пути |
379
+ | — | Вытеснены: T2, T5, T9–T12, T18, T20–T22, T24, T27, T30, T32 описывали прежние таксономии, бюджеты и открытые вопросы и заменены строками выше и разделами §2, §4 и §6 |
380
+
381
+ ### 9.2 Панель
382
+
383
+ | № | Решение |
384
+ | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
385
+ | U11 | Пороги виртуализации заданы, а не измерены: 200 строк для списков и инспектора, 300 узлов для layout в Worker |
386
+ | U12 | Линза обрезает содержимое; pan, zoom колесом, кнопки и fit-to-view это способ добраться до того, что вне кадра |
387
+ | U13 | Крошка предка работает как навигация; текущий узел остаётся подписью |
388
+ | U14 | Карточки узлов непрозрачны и выше рёбер, поэтому линия не может пересечь бейдж |
389
+ | U15 | Экспорт доставляется в буфер обмена или локальный файл по способностям хоста и всегда сообщает исход |
390
+ | U16 | Панель не предлагает действия, которого не может выполнить; статус-бар один раз сообщает, что control plane выключен |
391
+ | U18 | Метка это presentation metadata, выведенная из объявления: не идентичность, не право и никогда не имя протокольного поля на экране |
392
+ | U19 | Одна сворачиваемая легенда для состояний, рёбер и бейджей |
393
+ | U20 | Английский и русский, по умолчанию язык браузера, переключение в шапке; английский словарь — типизированный источник истины |
394
+ | U21 | Граница перевода: проза переводится, словарь протокола нет, и протокольные слова вставляются в переведённые предложения |
395
+ | U22 | Cap размера панели 20 кБ brotli, поднят с 15 с записанным измерением; headless-консьюмер остаётся под 5 кБ |
396
+ | U24 | Полноту перевода стережёт тест, который рендерит русскую панель по всем поверхностям и падает на непризнанной прозе |
397
+ | U26 | Zoom колесом масштабирует по величине жеста; кнопки дают шаг 1.2×; `deltaMode` приводится к пикселям, и один кадр зажат |
398
+ | U27 | Протяжка двигает камеру и ничего не выделяет |
399
+ | U28 | Две вкладки, `Graph` и `Timeline`; прежние линзы становятся чипами фильтра рёбер с четырьмя пресетами |
400
+ | U29 | «Точки расширения» это пресет-проекция в два шага, а не линза: фича → вклады → цели, с переворотом при выборе цели |
401
+ | U30 | Ручного переключателя `Graph`/`List` больше нет; список это автоматическая форма ниже 1100 px |
402
+ | U31 | Статус-бар держит на виду `frame`, `dropped` и `LIVE`, а собственную диагностику панели прячет за «⋯» |
403
+ | U32 | Управление ограничено override условия, на узле группы, через отдельный dev-only write-порт со своей authority |
404
+ | U33 | Control plane, легенда, выгрузка и хронология грузятся по требованию — у каждой свой чанк, свой словарь и свой бюджет размера; панель везёт только то, чем пользуется каждая сессия |
405
+ | U34 | Поверхность управления скачивается, когда в выделении есть адресуемый портом субъект — группа условий с условием или карантинный экземпляр, — а не просто потому, что порт записи существует |
406
+ | — | Вытеснены: U1–U10, U17, U23, U25 описывали глифы v1, поведение канваса по умолчанию, колонку результата поиска и выбор названия пресета точек расширения и заменены строками выше и разделом §4 |
407
+
408
+ Inspection graph/frame имеют схему `/3` и optional snapshots `opetope.runtime-activity/1`. В пределах одной session
409
+ frame без `activity` сохраняет предыдущую activity; полный snapshot/reset без этого поля очищает наблюдение (D216).
410
+ Frame с `activity` заменяет предыдущую activity целиком.
411
+ Используйте согласованные версии runtime/devtools: readers `/2` отвергают новую revision. Activity указывает execution,
412
+ фактическое поколение feature, физические Calls, точных текущих lane blockers, зарегистрированные leases и попытки загрузок.
413
+ Спрос хоста и UI-модели неизвестны; у stream наблюдается state, но не физические identities загрузок. `freshness`
414
+ и `truncated` отличают полное live-наблюдение от усечённого или отключённого. Закрытая session имеет stale-снимок;
415
+ `closed: true` требует успешного физического drain приложения. Control authority и продуктовые payload не добавляются.
416
+ Размер activity ограничен capacity записей. Сбор снимка обходит зарегистрированных owners, executors и resources,
417
+ поэтому capacity не ограничивает стоимость обхода. Сбор останавливается после доказанного truncation;
418
+ idle executors могут требовать обхода, чтобы подтвердить полноту данных. Обычный call dispatch не создаёт диагностических записей при
419
+ выключенном наблюдении. Frames ограничены существующей ring capacity.