@yancyyu/openhermit 1.5.8

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 (980) hide show
  1. package/LICENSE +611 -0
  2. package/README.md +220 -0
  3. package/bin/hermit.mjs +364 -0
  4. package/bin/kill-dev.js +20 -0
  5. package/dist-renderer/assets/01-BApSFlV4.png +0 -0
  6. package/dist-renderer/assets/02-CRQGs29u.png +0 -0
  7. package/dist-renderer/assets/03-BFCM2jnD.png +0 -0
  8. package/dist-renderer/assets/04-B2FThbKO.png +0 -0
  9. package/dist-renderer/assets/05-D9p0Znkd.png +0 -0
  10. package/dist-renderer/assets/06-DZAfbDlP.png +0 -0
  11. package/dist-renderer/assets/07-B_PXWGCc.png +0 -0
  12. package/dist-renderer/assets/08-DGRMZ6sl.png +0 -0
  13. package/dist-renderer/assets/09-SGCQvc7U.png +0 -0
  14. package/dist-renderer/assets/10-Cve81Q3W.png +0 -0
  15. package/dist-renderer/assets/11-DGglolDW.png +0 -0
  16. package/dist-renderer/assets/12-C3lnu79c.png +0 -0
  17. package/dist-renderer/assets/13-M59meqdw.png +0 -0
  18. package/dist-renderer/assets/ProjectEditorOverlay-BNoDw9T1.js +57 -0
  19. package/dist-renderer/assets/TeamGraphOverlay-CfGRKQIu.js +1 -0
  20. package/dist-renderer/assets/_basePickBy-Ct8Hm5_h.js +1 -0
  21. package/dist-renderer/assets/_baseUniq-BofrAFBx.js +1 -0
  22. package/dist-renderer/assets/apl-B4CMkyY2.js +1 -0
  23. package/dist-renderer/assets/arc-AbJgatzR.js +1 -0
  24. package/dist-renderer/assets/architectureDiagram-VXUJARFQ-gpniCJVk.js +36 -0
  25. package/dist-renderer/assets/asciiarmor-Df11BRmG.js +1 -0
  26. package/dist-renderer/assets/asn1-EdZsLKOL.js +1 -0
  27. package/dist-renderer/assets/asterisk-B-8jnY81.js +1 -0
  28. package/dist-renderer/assets/blockDiagram-VD42YOAC-aBbbmONC.js +122 -0
  29. package/dist-renderer/assets/brainfuck-C4LP7Hcl.js +1 -0
  30. package/dist-renderer/assets/c4Diagram-YG6GDRKO-DJio1IsU.js +10 -0
  31. package/dist-renderer/assets/channel-CZ8sd5Xf.js +1 -0
  32. package/dist-renderer/assets/chunk-4BX2VUAB-D1_HKao2.js +1 -0
  33. package/dist-renderer/assets/chunk-55IACEB6-NAmVxF4k.js +1 -0
  34. package/dist-renderer/assets/chunk-B4BG7PRW-Ce829laz.js +165 -0
  35. package/dist-renderer/assets/chunk-DI55MBZ5-Ct2Le12y.js +220 -0
  36. package/dist-renderer/assets/chunk-FMBD7UC4-Cie3DzKk.js +15 -0
  37. package/dist-renderer/assets/chunk-QN33PNHL-4f5Yb50e.js +1 -0
  38. package/dist-renderer/assets/chunk-QZHKN3VN-D9ranl9c.js +1 -0
  39. package/dist-renderer/assets/chunk-TZMSLE5B-bdGZWlEy.js +1 -0
  40. package/dist-renderer/assets/classDiagram-2ON5EDUG-CMcfSKj5.js +1 -0
  41. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CMcfSKj5.js +1 -0
  42. package/dist-renderer/assets/clike-B9uivgTg.js +1 -0
  43. package/dist-renderer/assets/clojure-BMjYHr_A.js +1 -0
  44. package/dist-renderer/assets/clone-CMuwA8RV.js +1 -0
  45. package/dist-renderer/assets/cmake-BQqOBYOt.js +1 -0
  46. package/dist-renderer/assets/cobol-CWcv1MsR.js +1 -0
  47. package/dist-renderer/assets/coffeescript-S37ZYGWr.js +1 -0
  48. package/dist-renderer/assets/commonlisp-DBKNyK5s.js +1 -0
  49. package/dist-renderer/assets/cose-bilkent-S5V4N54A-C6tvfcVi.js +1 -0
  50. package/dist-renderer/assets/crystal-SjHAIU92.js +1 -0
  51. package/dist-renderer/assets/css-BnMrqG3P.js +1 -0
  52. package/dist-renderer/assets/cypher-C_CwsFkJ.js +1 -0
  53. package/dist-renderer/assets/cytoscape.esm-DsxaTqgk.js +331 -0
  54. package/dist-renderer/assets/d-pRatUO7H.js +1 -0
  55. package/dist-renderer/assets/dagre-6UL2VRFP-B-4qcZam.js +4 -0
  56. package/dist-renderer/assets/defaultLocale-DX6XiGOO.js +1 -0
  57. package/dist-renderer/assets/diagram-PSM6KHXK-CwT3TLjx.js +24 -0
  58. package/dist-renderer/assets/diagram-QEK2KX5R-BWH6-ZFd.js +43 -0
  59. package/dist-renderer/assets/diagram-S2PKOQOG-DfpPnfi1.js +24 -0
  60. package/dist-renderer/assets/diff-DbItnlRl.js +1 -0
  61. package/dist-renderer/assets/dockerfile-BKs6k2Af.js +1 -0
  62. package/dist-renderer/assets/dtd-DF_7sFjM.js +1 -0
  63. package/dist-renderer/assets/dylan-DwRh75JA.js +1 -0
  64. package/dist-renderer/assets/ebnf-CDyGwa7X.js +1 -0
  65. package/dist-renderer/assets/ecl-Cabwm37j.js +1 -0
  66. package/dist-renderer/assets/eiffel-CnydiIhH.js +1 -0
  67. package/dist-renderer/assets/elm-vLlmbW-K.js +1 -0
  68. package/dist-renderer/assets/erDiagram-Q2GNP2WA-BFbEFR4x.js +60 -0
  69. package/dist-renderer/assets/erlang-BNw1qcRV.js +1 -0
  70. package/dist-renderer/assets/factor-kuTfRLto.js +1 -0
  71. package/dist-renderer/assets/favicon-B8xY-GVk.png +0 -0
  72. package/dist-renderer/assets/fcl-Kvtd6kyn.js +1 -0
  73. package/dist-renderer/assets/flowDiagram-NV44I4VS-Dg3cf5hW.js +162 -0
  74. package/dist-renderer/assets/forth-Ffai-XNe.js +1 -0
  75. package/dist-renderer/assets/fortran-DYz_wnZ1.js +1 -0
  76. package/dist-renderer/assets/ganttDiagram-JELNMOA3-B21y55W5.js +267 -0
  77. package/dist-renderer/assets/gas-Bneqetm1.js +1 -0
  78. package/dist-renderer/assets/gherkin-heZmZLOM.js +1 -0
  79. package/dist-renderer/assets/gitGraphDiagram-V2S2FVAM-BDV3BJzn.js +65 -0
  80. package/dist-renderer/assets/graph-BfaZ4hZt.js +1 -0
  81. package/dist-renderer/assets/groovy-D9Dt4D0W.js +1 -0
  82. package/dist-renderer/assets/haskell-BWDZoCOh.js +1 -0
  83. package/dist-renderer/assets/haxe-H-WmDvRZ.js +1 -0
  84. package/dist-renderer/assets/http-DBlCnlav.js +1 -0
  85. package/dist-renderer/assets/idl-BEugSyMb.js +1 -0
  86. package/dist-renderer/assets/index-BMXHMpkG.js +1 -0
  87. package/dist-renderer/assets/index-CCqtDawH.js +1 -0
  88. package/dist-renderer/assets/index-CVMSpK8C.js +1 -0
  89. package/dist-renderer/assets/index-CZltVMDP.js +1844 -0
  90. package/dist-renderer/assets/index-CaG9mf8s.css +1 -0
  91. package/dist-renderer/assets/index-Ct0-y9TF.js +1 -0
  92. package/dist-renderer/assets/index-pMg_LlsS.js +1 -0
  93. package/dist-renderer/assets/infoDiagram-HS3SLOUP-DvMlS0CL.js +2 -0
  94. package/dist-renderer/assets/init-Gi6I4Gst.js +1 -0
  95. package/dist-renderer/assets/javascript-qCveANmP.js +1 -0
  96. package/dist-renderer/assets/journeyDiagram-XKPGCS4Q-DIyMluRv.js +139 -0
  97. package/dist-renderer/assets/julia-DuME0IfC.js +1 -0
  98. package/dist-renderer/assets/kanban-definition-3W4ZIXB7-CVOx8f-7.js +89 -0
  99. package/dist-renderer/assets/katex-DGN8GczM.js +261 -0
  100. package/dist-renderer/assets/layout-BPKIXUf4.js +1 -0
  101. package/dist-renderer/assets/linear-CScZGLr2.js +1 -0
  102. package/dist-renderer/assets/livescript-BwQOo05w.js +1 -0
  103. package/dist-renderer/assets/lua-BgMRiT3U.js +1 -0
  104. package/dist-renderer/assets/mathematica-DTrFuWx2.js +1 -0
  105. package/dist-renderer/assets/mbox-CNhZ1qSd.js +1 -0
  106. package/dist-renderer/assets/mindmap-definition-VGOIOE7T-CmDQ7Wo6.js +68 -0
  107. package/dist-renderer/assets/mirc-CjQqDB4T.js +1 -0
  108. package/dist-renderer/assets/mllike-CXdrOF99.js +1 -0
  109. package/dist-renderer/assets/modelica-Dc1JOy9r.js +1 -0
  110. package/dist-renderer/assets/mscgen-BA5vi2Kp.js +1 -0
  111. package/dist-renderer/assets/mumps-BT43cFF4.js +1 -0
  112. package/dist-renderer/assets/nginx-DdIZxoE0.js +1 -0
  113. package/dist-renderer/assets/nsis-LdVXkNf5.js +1 -0
  114. package/dist-renderer/assets/ntriples-BfvgReVJ.js +1 -0
  115. package/dist-renderer/assets/octave-Ck1zUtKM.js +1 -0
  116. package/dist-renderer/assets/ordinal-Cboi1Yqb.js +1 -0
  117. package/dist-renderer/assets/oz-BzwKVEFT.js +1 -0
  118. package/dist-renderer/assets/pascal--L3eBynH.js +1 -0
  119. package/dist-renderer/assets/perl-CdXCOZ3F.js +1 -0
  120. package/dist-renderer/assets/pieDiagram-ADFJNKIX-DbVClin-.js +30 -0
  121. package/dist-renderer/assets/pig-CevX1Tat.js +1 -0
  122. package/dist-renderer/assets/powershell-CFHJl5sT.js +1 -0
  123. package/dist-renderer/assets/properties-C78fOPTZ.js +1 -0
  124. package/dist-renderer/assets/protobuf-ChK-085T.js +1 -0
  125. package/dist-renderer/assets/pug-DukmZTjD.js +1 -0
  126. package/dist-renderer/assets/puppet-DMA9R1ak.js +1 -0
  127. package/dist-renderer/assets/python-BuPzkPfP.js +1 -0
  128. package/dist-renderer/assets/q-pXgVlZs6.js +1 -0
  129. package/dist-renderer/assets/quadrantDiagram-AYHSOK5B-CAB0MYcW.js +7 -0
  130. package/dist-renderer/assets/r-DUYO_cvP.js +1 -0
  131. package/dist-renderer/assets/requirementDiagram-UZGBJVZJ-w2Lfpg3T.js +64 -0
  132. package/dist-renderer/assets/rpm-CTu-6PCP.js +1 -0
  133. package/dist-renderer/assets/ruby-B2Rjki9n.js +1 -0
  134. package/dist-renderer/assets/sankeyDiagram-TZEHDZUN-kvG1QoKY.js +10 -0
  135. package/dist-renderer/assets/sas-B4kiWyti.js +1 -0
  136. package/dist-renderer/assets/scheme-C41bIUwD.js +1 -0
  137. package/dist-renderer/assets/sequenceDiagram-WL72ISMW-DCVBQ23J.js +145 -0
  138. package/dist-renderer/assets/shell-CjFT_Tl9.js +1 -0
  139. package/dist-renderer/assets/sieve-C3Gn_uJK.js +1 -0
  140. package/dist-renderer/assets/simple-mode-GW_nhZxv.js +1 -0
  141. package/dist-renderer/assets/smalltalk-CnHTOXQT.js +1 -0
  142. package/dist-renderer/assets/solr-DehyRSwq.js +1 -0
  143. package/dist-renderer/assets/sparql-DkYu6x3z.js +1 -0
  144. package/dist-renderer/assets/splashScene-C8lWNnm4.js +1 -0
  145. package/dist-renderer/assets/spreadsheet-BCZA_wO0.js +1 -0
  146. package/dist-renderer/assets/sql-D0XecflT.js +1 -0
  147. package/dist-renderer/assets/stateDiagram-FKZM4ZOC-ItZ0JBvq.js +1 -0
  148. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-Hpmw4dMm.js +1 -0
  149. package/dist-renderer/assets/stex-C3f8Ysf7.js +1 -0
  150. package/dist-renderer/assets/stylus-B533Al4x.js +1 -0
  151. package/dist-renderer/assets/swift-BzpIVaGY.js +1 -0
  152. package/dist-renderer/assets/tcl-DVfN8rqt.js +1 -0
  153. package/dist-renderer/assets/textile-CnDTJFAw.js +1 -0
  154. package/dist-renderer/assets/tiddlywiki-DO-Gjzrf.js +1 -0
  155. package/dist-renderer/assets/tiki-DGYXhP31.js +1 -0
  156. package/dist-renderer/assets/timeline-definition-IT6M3QCI-BzSFaAjV.js +61 -0
  157. package/dist-renderer/assets/toml-Bm5Em-hy.js +1 -0
  158. package/dist-renderer/assets/treemap-GDKQZRPO-fSz4hQn0.js +162 -0
  159. package/dist-renderer/assets/troff-wAsdV37c.js +1 -0
  160. package/dist-renderer/assets/ttcn-CfJYG6tj.js +1 -0
  161. package/dist-renderer/assets/ttcn-cfg-B9xdYoR4.js +1 -0
  162. package/dist-renderer/assets/turtle-B1tBg_DP.js +1 -0
  163. package/dist-renderer/assets/vb-CmGdzxic.js +1 -0
  164. package/dist-renderer/assets/vbscript-BuJXcnF6.js +1 -0
  165. package/dist-renderer/assets/velocity-D8B20fx6.js +1 -0
  166. package/dist-renderer/assets/verilog-C6RDOZhf.js +1 -0
  167. package/dist-renderer/assets/vhdl-lSbBsy5d.js +1 -0
  168. package/dist-renderer/assets/webidl-ZXfAyPTL.js +1 -0
  169. package/dist-renderer/assets/xquery-CQfU5ijd.js +1 -0
  170. package/dist-renderer/assets/xychartDiagram-PRI3JC2R-CT1kaGlv.js +7 -0
  171. package/dist-renderer/assets/yacas-BJ4BC0dw.js +1 -0
  172. package/dist-renderer/assets/z80-Hz9HOZM7.js +1 -0
  173. package/dist-renderer/index.html +1274 -0
  174. package/package.json +181 -0
  175. package/src/features/README.md +24 -0
  176. package/src/features/agent-graph/README.md +21 -0
  177. package/src/features/agent-graph/STABLE_SLOT_LAYOUT_PLAN.md +2846 -0
  178. package/src/features/agent-graph/core/domain/buildInlineActivityEntries.ts +416 -0
  179. package/src/features/agent-graph/core/domain/collapseOverflowStacks.ts +126 -0
  180. package/src/features/agent-graph/core/domain/graphOwnerIdentity.ts +55 -0
  181. package/src/features/agent-graph/core/domain/taskGraphSemantics.ts +48 -0
  182. package/src/features/agent-graph/renderer/adapters/TeamGraphAdapter.ts +1400 -0
  183. package/src/features/agent-graph/renderer/hooks/useGraphActivityContext.ts +34 -0
  184. package/src/features/agent-graph/renderer/hooks/useGraphCreateTaskDialog.tsx +126 -0
  185. package/src/features/agent-graph/renderer/hooks/useGraphMemberPopoverContext.ts +34 -0
  186. package/src/features/agent-graph/renderer/hooks/useGraphSidebarVisibility.ts +52 -0
  187. package/src/features/agent-graph/renderer/hooks/useTeamGraphAdapter.ts +173 -0
  188. package/src/features/agent-graph/renderer/hooks/useTeamGraphSurfaceActions.ts +98 -0
  189. package/src/features/agent-graph/renderer/index.ts +14 -0
  190. package/src/features/agent-graph/renderer/ui/GraphActivityCard.tsx +96 -0
  191. package/src/features/agent-graph/renderer/ui/GraphActivityHud.tsx +498 -0
  192. package/src/features/agent-graph/renderer/ui/GraphBlockingEdgePopover.tsx +207 -0
  193. package/src/features/agent-graph/renderer/ui/GraphNodePopover.tsx +573 -0
  194. package/src/features/agent-graph/renderer/ui/GraphProvisioningHud.tsx +113 -0
  195. package/src/features/agent-graph/renderer/ui/GraphTaskCard.tsx +149 -0
  196. package/src/features/agent-graph/renderer/ui/GraphTransientHandoffHud.tsx +176 -0
  197. package/src/features/agent-graph/renderer/ui/TeamGraphOverlay.tsx +224 -0
  198. package/src/features/agent-graph/renderer/ui/TeamGraphTab.tsx +257 -0
  199. package/src/features/agent-graph/renderer/ui/buildTransientHandoffMessage.ts +70 -0
  200. package/src/features/recent-projects/contracts/api.ts +5 -0
  201. package/src/features/recent-projects/contracts/channels.ts +2 -0
  202. package/src/features/recent-projects/contracts/dto.ts +24 -0
  203. package/src/features/recent-projects/contracts/index.ts +4 -0
  204. package/src/features/recent-projects/contracts/normalize.ts +32 -0
  205. package/src/features/recent-projects/core/application/models/ListDashboardRecentProjectsResponse.ts +6 -0
  206. package/src/features/recent-projects/core/application/ports/ClockPort.ts +3 -0
  207. package/src/features/recent-projects/core/application/ports/ListDashboardRecentProjectsOutputPort.ts +5 -0
  208. package/src/features/recent-projects/core/application/ports/LoggerPort.ts +5 -0
  209. package/src/features/recent-projects/core/application/ports/RecentProjectsCachePort.ts +5 -0
  210. package/src/features/recent-projects/core/application/ports/RecentProjectsSourcePort.ts +14 -0
  211. package/src/features/recent-projects/core/application/use-cases/ListDashboardRecentProjectsUseCase.ts +191 -0
  212. package/src/features/recent-projects/core/domain/models/ProviderId.ts +1 -0
  213. package/src/features/recent-projects/core/domain/models/RecentProjectAggregate.ts +14 -0
  214. package/src/features/recent-projects/core/domain/models/RecentProjectCandidate.ts +14 -0
  215. package/src/features/recent-projects/core/domain/models/RecentProjectOpenTarget.ts +3 -0
  216. package/src/features/recent-projects/core/domain/policies/mergeRecentProjectCandidates.ts +88 -0
  217. package/src/features/recent-projects/main/adapters/input/http/registerRecentProjectsHttp.ts +30 -0
  218. package/src/features/recent-projects/main/adapters/output/presenters/DashboardRecentProjectsPresenter.ts +27 -0
  219. package/src/features/recent-projects/main/adapters/output/sources/ClaudeRecentProjectsSourceAdapter.ts +91 -0
  220. package/src/features/recent-projects/main/adapters/output/sources/CodexRecentProjectsSourceAdapter.ts +326 -0
  221. package/src/features/recent-projects/main/composition/createRecentProjectsFeature.ts +43 -0
  222. package/src/features/recent-projects/main/index.ts +3 -0
  223. package/src/features/recent-projects/main/infrastructure/cache/InMemoryRecentProjectsCache.ts +34 -0
  224. package/src/features/recent-projects/main/infrastructure/codex/CodexAppServerClient.ts +116 -0
  225. package/src/features/recent-projects/main/infrastructure/identity/RecentProjectIdentityResolver.ts +20 -0
  226. package/src/features/recent-projects/main/infrastructure/identity/normalizeIdentityPath.ts +10 -0
  227. package/src/features/recent-projects/renderer/adapters/RecentProjectsSectionAdapter.ts +132 -0
  228. package/src/features/recent-projects/renderer/hooks/useOpenRecentProject.ts +143 -0
  229. package/src/features/recent-projects/renderer/hooks/useRecentProjectsSection.ts +289 -0
  230. package/src/features/recent-projects/renderer/index.ts +2 -0
  231. package/src/features/recent-projects/renderer/ui/RecentProjectCard.tsx +221 -0
  232. package/src/features/recent-projects/renderer/ui/RecentProjectsSection.tsx +167 -0
  233. package/src/features/recent-projects/renderer/utils/activeProjectTeams.ts +48 -0
  234. package/src/features/recent-projects/renderer/utils/navigation.ts +65 -0
  235. package/src/features/recent-projects/renderer/utils/projectDecorations.ts +11 -0
  236. package/src/features/recent-projects/renderer/utils/recentProjectOpenHistory.ts +268 -0
  237. package/src/features/recent-projects/renderer/utils/recentProjectsClientCache.ts +78 -0
  238. package/src/main/constants/messageTags.ts +46 -0
  239. package/src/main/constants/worktreePatterns.ts +47 -0
  240. package/src/main/server.ts +3705 -0
  241. package/src/main/services/UpdateService.ts +166 -0
  242. package/src/main/services/ccConnect/CcConnectBridge.ts +313 -0
  243. package/src/main/services/ccConnect/CcConnectClient.ts +397 -0
  244. package/src/main/services/ccConnect/MessageBridge.ts +162 -0
  245. package/src/main/services/ccConnect/ProjectMappingStore.ts +148 -0
  246. package/src/main/services/ccConnect/index.ts +8 -0
  247. package/src/main/services/teams-mvp/TeamProvisioningService.ts +275 -0
  248. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +404 -0
  249. package/src/main/services/teams-mvp/index.ts +26 -0
  250. package/src/main/types/chunks.ts +506 -0
  251. package/src/main/types/domain.ts +342 -0
  252. package/src/main/types/index.ts +24 -0
  253. package/src/main/types/jsonl.ts +355 -0
  254. package/src/main/types/messages.ts +395 -0
  255. package/src/renderer/App.tsx +287 -0
  256. package/src/renderer/api/httpClient.ts +2207 -0
  257. package/src/renderer/api/index.ts +19 -0
  258. package/src/renderer/api/providers.ts +77 -0
  259. package/src/renderer/assets/participant-avatars/01.png +0 -0
  260. package/src/renderer/assets/participant-avatars/02.png +0 -0
  261. package/src/renderer/assets/participant-avatars/03.png +0 -0
  262. package/src/renderer/assets/participant-avatars/04.png +0 -0
  263. package/src/renderer/assets/participant-avatars/05.png +0 -0
  264. package/src/renderer/assets/participant-avatars/06.png +0 -0
  265. package/src/renderer/assets/participant-avatars/07.png +0 -0
  266. package/src/renderer/assets/participant-avatars/08.png +0 -0
  267. package/src/renderer/assets/participant-avatars/09.png +0 -0
  268. package/src/renderer/assets/participant-avatars/10.png +0 -0
  269. package/src/renderer/assets/participant-avatars/11.png +0 -0
  270. package/src/renderer/assets/participant-avatars/12.png +0 -0
  271. package/src/renderer/assets/participant-avatars/13.png +0 -0
  272. package/src/renderer/components/chat/AIChatGroup.tsx +519 -0
  273. package/src/renderer/components/chat/ChatHistory.tsx +1115 -0
  274. package/src/renderer/components/chat/ChatHistoryEmptyState.tsx +15 -0
  275. package/src/renderer/components/chat/ChatHistoryItem.tsx +144 -0
  276. package/src/renderer/components/chat/ChatHistoryLoadingState.tsx +45 -0
  277. package/src/renderer/components/chat/CompactBoundary.tsx +169 -0
  278. package/src/renderer/components/chat/ContextBadge.tsx +582 -0
  279. package/src/renderer/components/chat/DisplayItemList.tsx +431 -0
  280. package/src/renderer/components/chat/LastOutputDisplay.tsx +259 -0
  281. package/src/renderer/components/chat/SessionContextPanel/DirectoryTree/DirectoryTreeNode.tsx +125 -0
  282. package/src/renderer/components/chat/SessionContextPanel/DirectoryTree/buildDirectoryTree.ts +47 -0
  283. package/src/renderer/components/chat/SessionContextPanel/DirectoryTree/types.ts +12 -0
  284. package/src/renderer/components/chat/SessionContextPanel/components/ClaudeMdFilesSection.tsx +90 -0
  285. package/src/renderer/components/chat/SessionContextPanel/components/ClaudeMdSection.tsx +86 -0
  286. package/src/renderer/components/chat/SessionContextPanel/components/CollapsibleSection.tsx +77 -0
  287. package/src/renderer/components/chat/SessionContextPanel/components/FlatInjectionList.tsx +248 -0
  288. package/src/renderer/components/chat/SessionContextPanel/components/MentionedFilesSection.tsx +50 -0
  289. package/src/renderer/components/chat/SessionContextPanel/components/RankedInjectionList.tsx +284 -0
  290. package/src/renderer/components/chat/SessionContextPanel/components/SessionContextHeader.tsx +290 -0
  291. package/src/renderer/components/chat/SessionContextPanel/components/SessionContextHelpTooltip.tsx +165 -0
  292. package/src/renderer/components/chat/SessionContextPanel/components/TaskCoordinationSection.tsx +47 -0
  293. package/src/renderer/components/chat/SessionContextPanel/components/ThinkingTextSection.tsx +47 -0
  294. package/src/renderer/components/chat/SessionContextPanel/components/ToolOutputsSection.tsx +47 -0
  295. package/src/renderer/components/chat/SessionContextPanel/components/UserMessagesSection.tsx +47 -0
  296. package/src/renderer/components/chat/SessionContextPanel/index.tsx +314 -0
  297. package/src/renderer/components/chat/SessionContextPanel/items/ClaudeMdItem.tsx +76 -0
  298. package/src/renderer/components/chat/SessionContextPanel/items/MentionedFileItem.tsx +91 -0
  299. package/src/renderer/components/chat/SessionContextPanel/items/TaskCoordinationItem.tsx +116 -0
  300. package/src/renderer/components/chat/SessionContextPanel/items/ThinkingTextItem.tsx +96 -0
  301. package/src/renderer/components/chat/SessionContextPanel/items/ToolBreakdownItem.tsx +38 -0
  302. package/src/renderer/components/chat/SessionContextPanel/items/ToolOutputItem.tsx +113 -0
  303. package/src/renderer/components/chat/SessionContextPanel/items/UserMessageItem.tsx +69 -0
  304. package/src/renderer/components/chat/SessionContextPanel/types.ts +96 -0
  305. package/src/renderer/components/chat/SessionContextPanel/utils/formatting.ts +6 -0
  306. package/src/renderer/components/chat/SessionContextPanel/utils/pathParsing.ts +23 -0
  307. package/src/renderer/components/chat/SystemChatGroup.tsx +60 -0
  308. package/src/renderer/components/chat/UserChatGroup.tsx +668 -0
  309. package/src/renderer/components/chat/items/BaseItem.tsx +213 -0
  310. package/src/renderer/components/chat/items/ExecutionTrace.tsx +279 -0
  311. package/src/renderer/components/chat/items/LinkedToolItem.tsx +235 -0
  312. package/src/renderer/components/chat/items/MetricsPill.tsx +215 -0
  313. package/src/renderer/components/chat/items/SlashItem.tsx +81 -0
  314. package/src/renderer/components/chat/items/SubagentItem.tsx +592 -0
  315. package/src/renderer/components/chat/items/TeammateMessageItem.tsx +261 -0
  316. package/src/renderer/components/chat/items/TextItem.tsx +82 -0
  317. package/src/renderer/components/chat/items/ThinkingItem.tsx +82 -0
  318. package/src/renderer/components/chat/items/baseItemHelpers.ts +42 -0
  319. package/src/renderer/components/chat/items/linkedTool/CollapsibleOutputSection.tsx +57 -0
  320. package/src/renderer/components/chat/items/linkedTool/DefaultToolViewer.tsx +56 -0
  321. package/src/renderer/components/chat/items/linkedTool/EditToolViewer.tsx +74 -0
  322. package/src/renderer/components/chat/items/linkedTool/ReadToolViewer.tsx +102 -0
  323. package/src/renderer/components/chat/items/linkedTool/SkillToolViewer.tsx +67 -0
  324. package/src/renderer/components/chat/items/linkedTool/ToolErrorDisplay.tsx +43 -0
  325. package/src/renderer/components/chat/items/linkedTool/WriteToolViewer.tsx +66 -0
  326. package/src/renderer/components/chat/items/linkedTool/index.ts +13 -0
  327. package/src/renderer/components/chat/items/linkedTool/renderHelpers.tsx +259 -0
  328. package/src/renderer/components/chat/markdownComponents.tsx +257 -0
  329. package/src/renderer/components/chat/markdownCopyUtils.ts +18 -0
  330. package/src/renderer/components/chat/searchHighlightUtils.ts +166 -0
  331. package/src/renderer/components/chat/viewers/CodeBlockViewer.tsx +244 -0
  332. package/src/renderer/components/chat/viewers/DiffViewer.tsx +459 -0
  333. package/src/renderer/components/chat/viewers/FileLink.tsx +182 -0
  334. package/src/renderer/components/chat/viewers/MarkdownViewer.tsx +1093 -0
  335. package/src/renderer/components/chat/viewers/MermaidDiagram.tsx +116 -0
  336. package/src/renderer/components/chat/viewers/index.ts +3 -0
  337. package/src/renderer/components/chat/viewers/syntaxHighlighter.ts +583 -0
  338. package/src/renderer/components/common/AppLogo.tsx +61 -0
  339. package/src/renderer/components/common/CliInstallWarningBanner.tsx +57 -0
  340. package/src/renderer/components/common/ConfirmDialog.tsx +176 -0
  341. package/src/renderer/components/common/ConnectionStatusBadge.tsx +56 -0
  342. package/src/renderer/components/common/ContextSwitchOverlay.tsx +38 -0
  343. package/src/renderer/components/common/CopyButton.tsx +85 -0
  344. package/src/renderer/components/common/CopyablePath.tsx +68 -0
  345. package/src/renderer/components/common/ErrorBoundary.tsx +211 -0
  346. package/src/renderer/components/common/ExportDropdown.tsx +142 -0
  347. package/src/renderer/components/common/FileTree.tsx +182 -0
  348. package/src/renderer/components/common/GlobalProviderStatusHeader.tsx +316 -0
  349. package/src/renderer/components/common/OngoingIndicator.tsx +67 -0
  350. package/src/renderer/components/common/ProviderBrandLogo.tsx +206 -0
  351. package/src/renderer/components/common/RepositoryDropdown.tsx +230 -0
  352. package/src/renderer/components/common/TokenUsageDisplay.tsx +581 -0
  353. package/src/renderer/components/common/WarningBanner.tsx +25 -0
  354. package/src/renderer/components/common/WorkspaceIndicator.tsx +183 -0
  355. package/src/renderer/components/common/WorktreeBadge.tsx +123 -0
  356. package/src/renderer/components/dashboard/CliStatusBanner.tsx +1845 -0
  357. package/src/renderer/components/dashboard/DashboardView.tsx +274 -0
  358. package/src/renderer/components/extensions/ExtensionStoreView.tsx +591 -0
  359. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +52 -0
  360. package/src/renderer/components/extensions/apikeys/ApiKeyCard.tsx +143 -0
  361. package/src/renderer/components/extensions/apikeys/ApiKeyFormDialog.tsx +282 -0
  362. package/src/renderer/components/extensions/apikeys/ApiKeysPanel.tsx +280 -0
  363. package/src/renderer/components/extensions/common/InstallButton.tsx +186 -0
  364. package/src/renderer/components/extensions/common/InstallCountBadge.tsx +21 -0
  365. package/src/renderer/components/extensions/common/SearchInput.tsx +70 -0
  366. package/src/renderer/components/extensions/common/SourceBadge.tsx +31 -0
  367. package/src/renderer/components/extensions/mcp/CustomMcpServerDialog.tsx +560 -0
  368. package/src/renderer/components/extensions/mcp/McpServerCard.tsx +314 -0
  369. package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +669 -0
  370. package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +543 -0
  371. package/src/renderer/components/extensions/plugins/CapabilityChips.tsx +70 -0
  372. package/src/renderer/components/extensions/plugins/CategoryChips.tsx +67 -0
  373. package/src/renderer/components/extensions/plugins/PluginCard.tsx +146 -0
  374. package/src/renderer/components/extensions/plugins/PluginDetailDialog.tsx +270 -0
  375. package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +436 -0
  376. package/src/renderer/components/extensions/skills/SkillCodeEditor.tsx +117 -0
  377. package/src/renderer/components/extensions/skills/SkillDetailDialog.tsx +372 -0
  378. package/src/renderer/components/extensions/skills/SkillEditorDialog.tsx +856 -0
  379. package/src/renderer/components/extensions/skills/SkillImportDialog.tsx +343 -0
  380. package/src/renderer/components/extensions/skills/SkillReviewDialog.tsx +166 -0
  381. package/src/renderer/components/extensions/skills/SkillsPanel.tsx +784 -0
  382. package/src/renderer/components/extensions/skills/skillDraftUtils.ts +234 -0
  383. package/src/renderer/components/extensions/skills/skillFolderNameUtils.ts +19 -0
  384. package/src/renderer/components/extensions/skills/skillProjectUtils.ts +13 -0
  385. package/src/renderer/components/extensions/skills/skillValidationUtils.ts +31 -0
  386. package/src/renderer/components/layout/MiddlePanel.tsx +18 -0
  387. package/src/renderer/components/layout/MoreMenu.tsx +243 -0
  388. package/src/renderer/components/layout/PaneContainer.tsx +27 -0
  389. package/src/renderer/components/layout/PaneContent.tsx +84 -0
  390. package/src/renderer/components/layout/PaneResizeHandle.tsx +85 -0
  391. package/src/renderer/components/layout/PaneSplitDropZone.tsx +54 -0
  392. package/src/renderer/components/layout/PaneView.tsx +75 -0
  393. package/src/renderer/components/layout/SessionTabContent.tsx +102 -0
  394. package/src/renderer/components/layout/Sidebar.tsx +205 -0
  395. package/src/renderer/components/layout/SortableTab.tsx +261 -0
  396. package/src/renderer/components/layout/TabBar.tsx +354 -0
  397. package/src/renderer/components/layout/TabBarActions.tsx +176 -0
  398. package/src/renderer/components/layout/TabBarRow.tsx +99 -0
  399. package/src/renderer/components/layout/TabContextMenu.tsx +171 -0
  400. package/src/renderer/components/layout/TabbedLayout.tsx +186 -0
  401. package/src/renderer/components/layout/TeamTabSectionNav.tsx +146 -0
  402. package/src/renderer/components/notifications/NotificationRow.tsx +228 -0
  403. package/src/renderer/components/notifications/NotificationsView.tsx +371 -0
  404. package/src/renderer/components/report/AssessmentBadge.tsx +78 -0
  405. package/src/renderer/components/report/ReportSection.tsx +58 -0
  406. package/src/renderer/components/report/SessionReportTab.tsx +102 -0
  407. package/src/renderer/components/report/sections/CostSection.tsx +259 -0
  408. package/src/renderer/components/report/sections/ErrorSection.tsx +100 -0
  409. package/src/renderer/components/report/sections/FrictionSection.tsx +91 -0
  410. package/src/renderer/components/report/sections/GitSection.tsx +72 -0
  411. package/src/renderer/components/report/sections/InsightsSection.tsx +207 -0
  412. package/src/renderer/components/report/sections/KeyTakeawaysSection.tsx +55 -0
  413. package/src/renderer/components/report/sections/OverviewSection.tsx +64 -0
  414. package/src/renderer/components/report/sections/QualitySection.tsx +151 -0
  415. package/src/renderer/components/report/sections/SubagentSection.tsx +88 -0
  416. package/src/renderer/components/report/sections/TimelineSection.tsx +111 -0
  417. package/src/renderer/components/report/sections/TokenSection.tsx +116 -0
  418. package/src/renderer/components/report/sections/ToolSection.tsx +77 -0
  419. package/src/renderer/components/runtime/ProviderModelBadges.tsx +142 -0
  420. package/src/renderer/components/runtime/ProviderRuntimeBackendSelector.tsx +327 -0
  421. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +288 -0
  422. package/src/renderer/components/runtime/providerConnectionUi.ts +408 -0
  423. package/src/renderer/components/schedules/SchedulesView.tsx +529 -0
  424. package/src/renderer/components/search/CommandPalette.tsx +610 -0
  425. package/src/renderer/components/search/SearchBar.tsx +171 -0
  426. package/src/renderer/components/settings/NotificationTriggerSettings/components/AddTriggerForm.tsx +233 -0
  427. package/src/renderer/components/settings/NotificationTriggerSettings/components/ColorPaletteSelector.tsx +144 -0
  428. package/src/renderer/components/settings/NotificationTriggerSettings/components/DynamicConfigSection.tsx +189 -0
  429. package/src/renderer/components/settings/NotificationTriggerSettings/components/GeneralInfoSection.tsx +68 -0
  430. package/src/renderer/components/settings/NotificationTriggerSettings/components/IgnorePatternsSection.tsx +73 -0
  431. package/src/renderer/components/settings/NotificationTriggerSettings/components/ModeSelector.tsx +45 -0
  432. package/src/renderer/components/settings/NotificationTriggerSettings/components/RepositoryScopeSection.tsx +63 -0
  433. package/src/renderer/components/settings/NotificationTriggerSettings/components/SectionHeader.tsx +15 -0
  434. package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerCard.tsx +150 -0
  435. package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerCardHeader.tsx +125 -0
  436. package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerConfiguration.tsx +342 -0
  437. package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerPreview.tsx +108 -0
  438. package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useAddTriggerFormHandlers.ts +218 -0
  439. package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useAddTriggerFormState.ts +135 -0
  440. package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useRepositoryLookup.ts +47 -0
  441. package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useTriggerCardState.ts +281 -0
  442. package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useTriggerForm.ts +185 -0
  443. package/src/renderer/components/settings/NotificationTriggerSettings/index.tsx +87 -0
  444. package/src/renderer/components/settings/NotificationTriggerSettings/types.ts +39 -0
  445. package/src/renderer/components/settings/NotificationTriggerSettings/utils/constants.ts +50 -0
  446. package/src/renderer/components/settings/NotificationTriggerSettings/utils/trigger.ts +113 -0
  447. package/src/renderer/components/settings/SettingsTabs.tsx +110 -0
  448. package/src/renderer/components/settings/SettingsView.tsx +153 -0
  449. package/src/renderer/components/settings/components/SettingRow.tsx +44 -0
  450. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +24 -0
  451. package/src/renderer/components/settings/components/SettingsSelect.tsx +100 -0
  452. package/src/renderer/components/settings/components/SettingsToggle.tsx +45 -0
  453. package/src/renderer/components/settings/components/index.ts +8 -0
  454. package/src/renderer/components/settings/hooks/index.ts +6 -0
  455. package/src/renderer/components/settings/hooks/useSettingsConfig.ts +270 -0
  456. package/src/renderer/components/settings/hooks/useSettingsHandlers.ts +468 -0
  457. package/src/renderer/components/settings/sections/AdvancedSection.tsx +234 -0
  458. package/src/renderer/components/settings/sections/CliStatusSection.tsx +930 -0
  459. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +391 -0
  460. package/src/renderer/components/settings/sections/GeneralSection.tsx +665 -0
  461. package/src/renderer/components/settings/sections/HarnessSection.tsx +133 -0
  462. package/src/renderer/components/settings/sections/PlatformsSection.tsx +517 -0
  463. package/src/renderer/components/settings/sections/index.ts +8 -0
  464. package/src/renderer/components/sidebar/DateGroupedSessions.tsx +1115 -0
  465. package/src/renderer/components/sidebar/GlobalTaskList.tsx +853 -0
  466. package/src/renderer/components/sidebar/SessionContextMenu.tsx +182 -0
  467. package/src/renderer/components/sidebar/SessionFiltersPopover.tsx +115 -0
  468. package/src/renderer/components/sidebar/SessionItem.tsx +393 -0
  469. package/src/renderer/components/sidebar/SidebarSessions.tsx +542 -0
  470. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +286 -0
  471. package/src/renderer/components/sidebar/TaskContextMenu.tsx +86 -0
  472. package/src/renderer/components/sidebar/TaskFiltersPopover.tsx +203 -0
  473. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +370 -0
  474. package/src/renderer/components/sidebar/dateGroupedSessionsSelection.ts +33 -0
  475. package/src/renderer/components/sidebar/projectGroupPagination.ts +89 -0
  476. package/src/renderer/components/sidebar/taskFiltersState.ts +82 -0
  477. package/src/renderer/components/splash/splashScene.ts +979 -0
  478. package/src/renderer/components/team/CcSessionsSection.tsx +202 -0
  479. package/src/renderer/components/team/ClaudeLogsDialog.tsx +71 -0
  480. package/src/renderer/components/team/ClaudeLogsFilterPopover.tsx +213 -0
  481. package/src/renderer/components/team/ClaudeLogsPanel.tsx +170 -0
  482. package/src/renderer/components/team/ClaudeLogsSection.tsx +154 -0
  483. package/src/renderer/components/team/CliLogsRichView.tsx +640 -0
  484. package/src/renderer/components/team/CollapsibleTeamSection.tsx +177 -0
  485. package/src/renderer/components/team/HarnessCards.ts +38 -0
  486. package/src/renderer/components/team/MemberBadge.tsx +117 -0
  487. package/src/renderer/components/team/ProcessesSection.tsx +193 -0
  488. package/src/renderer/components/team/ProvisioningProgressBlock.tsx +389 -0
  489. package/src/renderer/components/team/RoleSelect.tsx +171 -0
  490. package/src/renderer/components/team/StepProgressBar.tsx +165 -0
  491. package/src/renderer/components/team/TaskTooltip.tsx +197 -0
  492. package/src/renderer/components/team/TeamDetailView.tsx +3002 -0
  493. package/src/renderer/components/team/TeamEmptyState.tsx +102 -0
  494. package/src/renderer/components/team/TeamListFilterPopover.tsx +183 -0
  495. package/src/renderer/components/team/TeamListView.tsx +1336 -0
  496. package/src/renderer/components/team/TeamProvisioningBanner.tsx +16 -0
  497. package/src/renderer/components/team/TeamProvisioningPanel.tsx +115 -0
  498. package/src/renderer/components/team/TeamSessionsSection.tsx +267 -0
  499. package/src/renderer/components/team/ToolApprovalDiffPreview.tsx +206 -0
  500. package/src/renderer/components/team/ToolApprovalSheet.tsx +675 -0
  501. package/src/renderer/components/team/UnreadCommentsBadge.tsx +37 -0
  502. package/src/renderer/components/team/activity/ActiveTasksBlock.tsx +191 -0
  503. package/src/renderer/components/team/activity/ActivityItem.tsx +1649 -0
  504. package/src/renderer/components/team/activity/ActivityTimeline.tsx +959 -0
  505. package/src/renderer/components/team/activity/AnimatedHeightReveal.tsx +117 -0
  506. package/src/renderer/components/team/activity/LeadThoughtsGroup.tsx +1152 -0
  507. package/src/renderer/components/team/activity/MessageExpandDialog.tsx +213 -0
  508. package/src/renderer/components/team/activity/PendingRepliesBlock.tsx +275 -0
  509. package/src/renderer/components/team/activity/ReplyQuoteBlock.tsx +79 -0
  510. package/src/renderer/components/team/activity/ThoughtBodyContent.tsx +150 -0
  511. package/src/renderer/components/team/activity/activityMarkdown.ts +36 -0
  512. package/src/renderer/components/team/activity/activityMessageContext.ts +68 -0
  513. package/src/renderer/components/team/activity/collapseState.ts +66 -0
  514. package/src/renderer/components/team/activity/useNewItemKeys.ts +70 -0
  515. package/src/renderer/components/team/attachments/AttachmentDisplay.tsx +132 -0
  516. package/src/renderer/components/team/attachments/AttachmentPreviewItem.tsx +62 -0
  517. package/src/renderer/components/team/attachments/AttachmentPreviewList.tsx +193 -0
  518. package/src/renderer/components/team/attachments/AttachmentThumbnail.tsx +42 -0
  519. package/src/renderer/components/team/attachments/DropZoneOverlay.tsx +54 -0
  520. package/src/renderer/components/team/attachments/ImageLightbox.tsx +132 -0
  521. package/src/renderer/components/team/attachments/SourceMessageAttachments.tsx +70 -0
  522. package/src/renderer/components/team/dialogs/AddMemberDialog.tsx +222 -0
  523. package/src/renderer/components/team/dialogs/AdvancedCliSection.tsx +347 -0
  524. package/src/renderer/components/team/dialogs/AnthropicFastModeSelector.tsx +120 -0
  525. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +489 -0
  526. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +484 -0
  527. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +448 -0
  528. package/src/renderer/components/team/dialogs/EffortLevelSelector.tsx +69 -0
  529. package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +165 -0
  530. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +2859 -0
  531. package/src/renderer/components/team/dialogs/LimitContextCheckbox.tsx +57 -0
  532. package/src/renderer/components/team/dialogs/MembersJsonEditor.tsx +123 -0
  533. package/src/renderer/components/team/dialogs/OptionalSettingsSection.tsx +124 -0
  534. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +145 -0
  535. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +289 -0
  536. package/src/renderer/components/team/dialogs/ProjectPathSelector.tsx +330 -0
  537. package/src/renderer/components/team/dialogs/ProvisioningProviderStatusList.tsx +744 -0
  538. package/src/renderer/components/team/dialogs/ReviewDialog.tsx +130 -0
  539. package/src/renderer/components/team/dialogs/SendMessageDialog.tsx +530 -0
  540. package/src/renderer/components/team/dialogs/SkipPermissionsCheckbox.tsx +62 -0
  541. package/src/renderer/components/team/dialogs/StatusHistoryTimeline.tsx +229 -0
  542. package/src/renderer/components/team/dialogs/TaskAttachments.tsx +394 -0
  543. package/src/renderer/components/team/dialogs/TaskCommentAwaitingReply.tsx +57 -0
  544. package/src/renderer/components/team/dialogs/TaskCommentInput.tsx +420 -0
  545. package/src/renderer/components/team/dialogs/TaskCommentsSection.tsx +620 -0
  546. package/src/renderer/components/team/dialogs/TaskDetailDialog.tsx +1480 -0
  547. package/src/renderer/components/team/dialogs/TeamModelSelector.tsx +560 -0
  548. package/src/renderer/components/team/dialogs/TeammateRuntimeCompatibilityNotice.tsx +60 -0
  549. package/src/renderer/components/team/dialogs/ToolApprovalSettingsPanel.tsx +173 -0
  550. package/src/renderer/components/team/dialogs/editTeamRuntimeChanges.ts +179 -0
  551. package/src/renderer/components/team/dialogs/globalTaskDetailDialogLoading.ts +34 -0
  552. package/src/renderer/components/team/dialogs/launchDialogPrefill.ts +125 -0
  553. package/src/renderer/components/team/dialogs/memberModelScope.ts +77 -0
  554. package/src/renderer/components/team/dialogs/platformMeta.ts +118 -0
  555. package/src/renderer/components/team/dialogs/projectPathOptions.ts +50 -0
  556. package/src/renderer/components/team/dialogs/providerPrepareDiagnostics.ts +222 -0
  557. package/src/renderer/components/team/dialogs/providerPrepareRequestSignature.ts +122 -0
  558. package/src/renderer/components/team/dialogs/provisioningMemberScope.ts +10 -0
  559. package/src/renderer/components/team/dialogs/provisioningModelIssues.ts +124 -0
  560. package/src/renderer/components/team/dialogs/teamNameSets.ts +67 -0
  561. package/src/renderer/components/team/dialogs/teamRelaunchFlow.ts +30 -0
  562. package/src/renderer/components/team/dialogs/teammateLaunchMode.ts +49 -0
  563. package/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx +101 -0
  564. package/src/renderer/components/team/editor/CodeMirrorEditor.tsx +506 -0
  565. package/src/renderer/components/team/editor/EditorBinaryPlaceholder.tsx +43 -0
  566. package/src/renderer/components/team/editor/EditorBinaryState.tsx +29 -0
  567. package/src/renderer/components/team/editor/EditorBreadcrumb.tsx +84 -0
  568. package/src/renderer/components/team/editor/EditorContextMenu.tsx +213 -0
  569. package/src/renderer/components/team/editor/EditorEmptyState.tsx +35 -0
  570. package/src/renderer/components/team/editor/EditorErrorBoundary.tsx +63 -0
  571. package/src/renderer/components/team/editor/EditorErrorState.tsx +41 -0
  572. package/src/renderer/components/team/editor/EditorFileTree.tsx +903 -0
  573. package/src/renderer/components/team/editor/EditorImagePreview.tsx +138 -0
  574. package/src/renderer/components/team/editor/EditorSearchPanel.tsx +508 -0
  575. package/src/renderer/components/team/editor/EditorSelectionMenu.tsx +112 -0
  576. package/src/renderer/components/team/editor/EditorShortcutsHelp.tsx +125 -0
  577. package/src/renderer/components/team/editor/EditorStatusBar.tsx +72 -0
  578. package/src/renderer/components/team/editor/EditorTabBar.tsx +265 -0
  579. package/src/renderer/components/team/editor/EditorTabContextMenu.tsx +88 -0
  580. package/src/renderer/components/team/editor/EditorToolbar.tsx +163 -0
  581. package/src/renderer/components/team/editor/FileIcon.tsx +66 -0
  582. package/src/renderer/components/team/editor/GitStatusBadge.tsx +47 -0
  583. package/src/renderer/components/team/editor/GoToLineDialog.tsx +186 -0
  584. package/src/renderer/components/team/editor/MarkdownPreviewPane.tsx +57 -0
  585. package/src/renderer/components/team/editor/MarkdownSplitView.tsx +127 -0
  586. package/src/renderer/components/team/editor/NewFileDialog.tsx +131 -0
  587. package/src/renderer/components/team/editor/ProjectEditorOverlay.tsx +924 -0
  588. package/src/renderer/components/team/editor/QuickOpenDialog.tsx +163 -0
  589. package/src/renderer/components/team/editor/SearchInFilesPanel.tsx +358 -0
  590. package/src/renderer/components/team/editor/fileIcons.ts +222 -0
  591. package/src/renderer/components/team/kanban/KanbanBoard.tsx +664 -0
  592. package/src/renderer/components/team/kanban/KanbanColumn.tsx +61 -0
  593. package/src/renderer/components/team/kanban/KanbanFilterPopover.tsx +210 -0
  594. package/src/renderer/components/team/kanban/KanbanGridLayout.tsx +460 -0
  595. package/src/renderer/components/team/kanban/KanbanSearchInput.tsx +284 -0
  596. package/src/renderer/components/team/kanban/KanbanSortPopover.tsx +140 -0
  597. package/src/renderer/components/team/kanban/KanbanTaskCard.test.tsx +199 -0
  598. package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +446 -0
  599. package/src/renderer/components/team/kanban/TrashDialog.tsx +112 -0
  600. package/src/renderer/components/team/members/CurrentTaskIndicator.tsx +56 -0
  601. package/src/renderer/components/team/members/LeadModelRow.test.tsx +133 -0
  602. package/src/renderer/components/team/members/LeadModelRow.tsx +183 -0
  603. package/src/renderer/components/team/members/MemberCard.tsx +665 -0
  604. package/src/renderer/components/team/members/MemberDetailDialog.tsx +309 -0
  605. package/src/renderer/components/team/members/MemberDetailHeader.tsx +183 -0
  606. package/src/renderer/components/team/members/MemberDetailStats.tsx +80 -0
  607. package/src/renderer/components/team/members/MemberDraftRow.test.tsx +184 -0
  608. package/src/renderer/components/team/members/MemberDraftRow.tsx +515 -0
  609. package/src/renderer/components/team/members/MemberExecutionLog.tsx +224 -0
  610. package/src/renderer/components/team/members/MemberHoverCard.tsx +292 -0
  611. package/src/renderer/components/team/members/MemberLaunchDiagnosticsButton.tsx +60 -0
  612. package/src/renderer/components/team/members/MemberList.tsx +405 -0
  613. package/src/renderer/components/team/members/MemberLogsTab.tsx +958 -0
  614. package/src/renderer/components/team/members/MemberMessagesTab.tsx +251 -0
  615. package/src/renderer/components/team/members/MemberPresenceDot.tsx +28 -0
  616. package/src/renderer/components/team/members/MemberRoleEditor.tsx +84 -0
  617. package/src/renderer/components/team/members/MemberStatsTab.tsx +299 -0
  618. package/src/renderer/components/team/members/MemberTasksTab.tsx +87 -0
  619. package/src/renderer/components/team/members/MemberWorkspaceTab.tsx +141 -0
  620. package/src/renderer/components/team/members/MembersEditorSection.tsx +495 -0
  621. package/src/renderer/components/team/members/SubagentRecentMessagesPreview.tsx +125 -0
  622. package/src/renderer/components/team/members/TeamRosterEditorSection.tsx +153 -0
  623. package/src/renderer/components/team/members/memberActivityEntries.ts +42 -0
  624. package/src/renderer/components/team/members/memberDetailTypes.ts +3 -0
  625. package/src/renderer/components/team/members/memberNameSets.ts +65 -0
  626. package/src/renderer/components/team/members/membersEditorTypes.ts +21 -0
  627. package/src/renderer/components/team/members/membersEditorUtils.ts +267 -0
  628. package/src/renderer/components/team/messages/MessageComposer.tsx +939 -0
  629. package/src/renderer/components/team/messages/MessagesFilterPopover.tsx +228 -0
  630. package/src/renderer/components/team/messages/MessagesPanel.tsx +1508 -0
  631. package/src/renderer/components/team/messages/OpenCodeDeliveryWarning.tsx +151 -0
  632. package/src/renderer/components/team/messages/StatusBlock.tsx +126 -0
  633. package/src/renderer/components/team/provisioningSteps.ts +363 -0
  634. package/src/renderer/components/team/review/ChangeReviewDialog.tsx +133 -0
  635. package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +218 -0
  636. package/src/renderer/components/team/schedule/CronScheduleInput.tsx +254 -0
  637. package/src/renderer/components/team/schedule/ScheduleEmptyState.tsx +15 -0
  638. package/src/renderer/components/team/schedule/ScheduleRunLogDialog.tsx +277 -0
  639. package/src/renderer/components/team/schedule/ScheduleRunRow.tsx +106 -0
  640. package/src/renderer/components/team/schedule/ScheduleSection.tsx +281 -0
  641. package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +55 -0
  642. package/src/renderer/components/team/sidebar/TeamSidebarHost.tsx +76 -0
  643. package/src/renderer/components/team/sidebar/TeamSidebarPortalManager.ts +173 -0
  644. package/src/renderer/components/team/sidebar/TeamSidebarPortalSource.tsx +66 -0
  645. package/src/renderer/components/team/sidebar/TeamSidebarRail.tsx +68 -0
  646. package/src/renderer/components/team/sidebar/teamSidebarUiState.ts +136 -0
  647. package/src/renderer/components/team/taskLogs/ExactTaskLogCard.tsx +132 -0
  648. package/src/renderer/components/team/taskLogs/ExactTaskLogsSection.tsx +258 -0
  649. package/src/renderer/components/team/taskLogs/ExecutionSessionsSection.tsx +48 -0
  650. package/src/renderer/components/team/taskLogs/TaskActivityLinkedToolCard.tsx +31 -0
  651. package/src/renderer/components/team/taskLogs/TaskActivitySection.tsx +462 -0
  652. package/src/renderer/components/team/taskLogs/TaskLogStreamSection.tsx +375 -0
  653. package/src/renderer/components/team/taskLogs/TaskLogsPanel.tsx +294 -0
  654. package/src/renderer/components/team/taskLogs/featureGates.ts +22 -0
  655. package/src/renderer/components/team/tasks/TaskList.tsx +111 -0
  656. package/src/renderer/components/team/tasks/TaskRow.tsx +65 -0
  657. package/src/renderer/components/team/teamProjectSelection.ts +156 -0
  658. package/src/renderer/components/team/teamSessionFetchGuards.ts +26 -0
  659. package/src/renderer/components/team/useClaudeLogsController.ts +668 -0
  660. package/src/renderer/components/team/useTeamProvisioningPresentation.ts +54 -0
  661. package/src/renderer/components/terminal/TerminalLogPanel.tsx +37 -0
  662. package/src/renderer/components/ui/ChipInteractionLayer.tsx +255 -0
  663. package/src/renderer/components/ui/CodeChipBadge.tsx +37 -0
  664. package/src/renderer/components/ui/ExpandableContent.tsx +110 -0
  665. package/src/renderer/components/ui/MemberSelect.tsx +209 -0
  666. package/src/renderer/components/ui/MentionInteractionLayer.tsx +121 -0
  667. package/src/renderer/components/ui/MentionSuggestionList.tsx +274 -0
  668. package/src/renderer/components/ui/MentionableTextarea.tsx +1426 -0
  669. package/src/renderer/components/ui/SlashCommandInteractionLayer.tsx +88 -0
  670. package/src/renderer/components/ui/TaskReferenceInteractionLayer.tsx +101 -0
  671. package/src/renderer/components/ui/UrlInteractionLayer.tsx +102 -0
  672. package/src/renderer/components/ui/alert-dialog.tsx +127 -0
  673. package/src/renderer/components/ui/auto-resize-textarea.tsx +93 -0
  674. package/src/renderer/components/ui/badge.tsx +37 -0
  675. package/src/renderer/components/ui/button.tsx +54 -0
  676. package/src/renderer/components/ui/checkbox.tsx +29 -0
  677. package/src/renderer/components/ui/combobox.tsx +168 -0
  678. package/src/renderer/components/ui/context-menu.tsx +124 -0
  679. package/src/renderer/components/ui/dialog.tsx +114 -0
  680. package/src/renderer/components/ui/hover-card.tsx +30 -0
  681. package/src/renderer/components/ui/input.tsx +22 -0
  682. package/src/renderer/components/ui/label.tsx +21 -0
  683. package/src/renderer/components/ui/popover.tsx +31 -0
  684. package/src/renderer/components/ui/select.tsx +150 -0
  685. package/src/renderer/components/ui/tabs.tsx +52 -0
  686. package/src/renderer/components/ui/textarea.tsx +21 -0
  687. package/src/renderer/components/ui/tiptap/TiptapBubbleMenu.tsx +75 -0
  688. package/src/renderer/components/ui/tiptap/TiptapEditor.tsx +73 -0
  689. package/src/renderer/components/ui/tiptap/TiptapToolbar.tsx +269 -0
  690. package/src/renderer/components/ui/tiptap/index.ts +3 -0
  691. package/src/renderer/components/ui/tiptap/presets.ts +46 -0
  692. package/src/renderer/components/ui/tiptap/tiptapStyles.css +235 -0
  693. package/src/renderer/components/ui/tiptap/types.ts +32 -0
  694. package/src/renderer/components/ui/tiptap/useTiptapEditor.ts +94 -0
  695. package/src/renderer/components/ui/tooltip.tsx +32 -0
  696. package/src/renderer/constants/cssVariables.ts +226 -0
  697. package/src/renderer/constants/layout.ts +6 -0
  698. package/src/renderer/constants/teamColors.ts +397 -0
  699. package/src/renderer/constants/teamRoles.ts +41 -0
  700. package/src/renderer/contexts/TabUIContext.tsx +51 -0
  701. package/src/renderer/contexts/useTabUIContext.ts +18 -0
  702. package/src/renderer/favicon.png +0 -0
  703. package/src/renderer/features/CLAUDE.md +19 -0
  704. package/src/renderer/hooks/navigation/utils.ts +263 -0
  705. package/src/renderer/hooks/useAttachments.ts +312 -0
  706. package/src/renderer/hooks/useAutoScrollBottom.ts +285 -0
  707. package/src/renderer/hooks/useBranchSync.ts +105 -0
  708. package/src/renderer/hooks/useChipDraftPersistence.ts +172 -0
  709. package/src/renderer/hooks/useCliInstaller.ts +106 -0
  710. package/src/renderer/hooks/useCollapsedGroups.ts +71 -0
  711. package/src/renderer/hooks/useComposerDraft.ts +504 -0
  712. package/src/renderer/hooks/useContinuousScrollNav.ts +50 -0
  713. package/src/renderer/hooks/useCreateTeamDraft.ts +280 -0
  714. package/src/renderer/hooks/useDraftPersistence.ts +140 -0
  715. package/src/renderer/hooks/useEditorKeyboardShortcuts.ts +257 -0
  716. package/src/renderer/hooks/useEffectiveCliProviderStatus.ts +66 -0
  717. package/src/renderer/hooks/useExtensionsTabState.ts +206 -0
  718. package/src/renderer/hooks/useFileListCacheWarmer.ts +38 -0
  719. package/src/renderer/hooks/useFileSuggestions.ts +255 -0
  720. package/src/renderer/hooks/useKeyboardShortcuts.ts +363 -0
  721. package/src/renderer/hooks/useLazyFileContent.ts +150 -0
  722. package/src/renderer/hooks/useMarkCommentsRead.ts +27 -0
  723. package/src/renderer/hooks/useMarkdownScrollSync.ts +158 -0
  724. package/src/renderer/hooks/useMemberStats.ts +45 -0
  725. package/src/renderer/hooks/useMentionDetection.ts +375 -0
  726. package/src/renderer/hooks/usePersistedGridLayout.ts +109 -0
  727. package/src/renderer/hooks/useResizableColumns.ts +140 -0
  728. package/src/renderer/hooks/useResizablePanel.ts +144 -0
  729. package/src/renderer/hooks/useStableTeamMentionMeta.ts +68 -0
  730. package/src/renderer/hooks/useSyncedAnimationStyle.ts +29 -0
  731. package/src/renderer/hooks/useTabNavigationController.ts +524 -0
  732. package/src/renderer/hooks/useTabUI.ts +252 -0
  733. package/src/renderer/hooks/useTaskLocalState.ts +163 -0
  734. package/src/renderer/hooks/useTaskSuggestions.ts +131 -0
  735. package/src/renderer/hooks/useTeamMessagesExpanded.ts +34 -0
  736. package/src/renderer/hooks/useTeamMessagesRead.ts +52 -0
  737. package/src/renderer/hooks/useTeamSuggestions.ts +78 -0
  738. package/src/renderer/hooks/useTheme.ts +138 -0
  739. package/src/renderer/hooks/useToolApprovalDiff.ts +212 -0
  740. package/src/renderer/hooks/useUnreadCommentCount.ts +14 -0
  741. package/src/renderer/hooks/useViewedFiles.ts +74 -0
  742. package/src/renderer/hooks/useViewportCommentRead.ts +147 -0
  743. package/src/renderer/hooks/useViewportObserver.ts +138 -0
  744. package/src/renderer/hooks/useVisibleAIGroup.ts +122 -0
  745. package/src/renderer/hooks/useVisibleFileSection.ts +114 -0
  746. package/src/renderer/hooks/useZoomFactor.ts +36 -0
  747. package/src/renderer/index.css +1560 -0
  748. package/src/renderer/index.html +1293 -0
  749. package/src/renderer/lib/utils.ts +6 -0
  750. package/src/renderer/main.tsx +30 -0
  751. package/src/renderer/sentry.ts +104 -0
  752. package/src/renderer/services/__tests__/createTeamPreferences.test.ts +67 -0
  753. package/src/renderer/services/commentReadStorage.ts +349 -0
  754. package/src/renderer/services/composerDraftStorage.ts +271 -0
  755. package/src/renderer/services/contextStorage.ts +201 -0
  756. package/src/renderer/services/createTeamDraftStorage.ts +151 -0
  757. package/src/renderer/services/createTeamPreferences.ts +361 -0
  758. package/src/renderer/services/dashboardCliStatusBannerPreference.ts +20 -0
  759. package/src/renderer/services/draftStorage.ts +128 -0
  760. package/src/renderer/services/layout-system/BrowserGridLayoutRepository.ts +111 -0
  761. package/src/renderer/services/layout-system/GridLayoutRepository.ts +8 -0
  762. package/src/renderer/services/layout-system/gridLayoutSchema.ts +137 -0
  763. package/src/renderer/services/layout-system/gridLayoutTypes.ts +17 -0
  764. package/src/renderer/store/index.ts +1556 -0
  765. package/src/renderer/store/slices/changeReviewSlice.ts +1694 -0
  766. package/src/renderer/store/slices/cliInstallerSlice.ts +689 -0
  767. package/src/renderer/store/slices/configSlice.ts +111 -0
  768. package/src/renderer/store/slices/connectionSlice.ts +221 -0
  769. package/src/renderer/store/slices/contextSlice.ts +394 -0
  770. package/src/renderer/store/slices/conversationSlice.ts +510 -0
  771. package/src/renderer/store/slices/editorSlice.ts +1455 -0
  772. package/src/renderer/store/slices/extensionsSlice.ts +1415 -0
  773. package/src/renderer/store/slices/notificationSlice.ts +277 -0
  774. package/src/renderer/store/slices/paneSlice.ts +357 -0
  775. package/src/renderer/store/slices/projectSlice.ts +70 -0
  776. package/src/renderer/store/slices/repositorySlice.ts +165 -0
  777. package/src/renderer/store/slices/scheduleSlice.ts +246 -0
  778. package/src/renderer/store/slices/sessionDetailSlice.ts +755 -0
  779. package/src/renderer/store/slices/sessionSlice.ts +539 -0
  780. package/src/renderer/store/slices/subagentSlice.ts +145 -0
  781. package/src/renderer/store/slices/tabSlice.ts +842 -0
  782. package/src/renderer/store/slices/tabUISlice.ts +319 -0
  783. package/src/renderer/store/slices/teamSlice.ts +5080 -0
  784. package/src/renderer/store/slices/uiSlice.ts +45 -0
  785. package/src/renderer/store/types.ts +103 -0
  786. package/src/renderer/store/utils/paneHelpers.ts +134 -0
  787. package/src/renderer/store/utils/pathResolution.ts +121 -0
  788. package/src/renderer/store/utils/stateResetHelpers.ts +72 -0
  789. package/src/renderer/types/api.ts +8 -0
  790. package/src/renderer/types/claudeMd.ts +74 -0
  791. package/src/renderer/types/contextInjection.ts +309 -0
  792. package/src/renderer/types/data.ts +144 -0
  793. package/src/renderer/types/groups.ts +406 -0
  794. package/src/renderer/types/inlineChip.ts +110 -0
  795. package/src/renderer/types/mention.ts +38 -0
  796. package/src/renderer/types/notifications.ts +18 -0
  797. package/src/renderer/types/panes.ts +35 -0
  798. package/src/renderer/types/sessionReport.ts +386 -0
  799. package/src/renderer/types/tabs.ts +249 -0
  800. package/src/renderer/types/teamMessagesPanelMode.ts +1 -0
  801. package/src/renderer/utils/__tests__/teamEffortOptions.test.ts +217 -0
  802. package/src/renderer/utils/__tests__/teamModelAvailability.codexCatalog.test.ts +383 -0
  803. package/src/renderer/utils/agentMessageFormatting.ts +139 -0
  804. package/src/renderer/utils/aiGroupEnhancer.ts +78 -0
  805. package/src/renderer/utils/aiGroupHelpers.ts +208 -0
  806. package/src/renderer/utils/attachmentUtils.ts +60 -0
  807. package/src/renderer/utils/bootstrapPromptSanitizer.ts +225 -0
  808. package/src/renderer/utils/bugReportUtils.ts +157 -0
  809. package/src/renderer/utils/buildSelectionAction.ts +116 -0
  810. package/src/renderer/utils/chipUtils.ts +372 -0
  811. package/src/renderer/utils/claudeCodeOnlyProviders.ts +126 -0
  812. package/src/renderer/utils/claudeMdTracker.ts +644 -0
  813. package/src/renderer/utils/codemirrorLanguages.ts +141 -0
  814. package/src/renderer/utils/codemirrorSelectionInfo.ts +41 -0
  815. package/src/renderer/utils/codemirrorTheme.ts +138 -0
  816. package/src/renderer/utils/contextMath.ts +55 -0
  817. package/src/renderer/utils/contextTracker.ts +1100 -0
  818. package/src/renderer/utils/crossTeamPendingReplies.ts +92 -0
  819. package/src/renderer/utils/dateGrouping.ts +91 -0
  820. package/src/renderer/utils/diffViewedStorage.ts +120 -0
  821. package/src/renderer/utils/displayItemBuilder.ts +587 -0
  822. package/src/renderer/utils/displaySummary.ts +74 -0
  823. package/src/renderer/utils/editorBridge.ts +90 -0
  824. package/src/renderer/utils/fileTreeBuilder.ts +110 -0
  825. package/src/renderer/utils/formatAgentRole.ts +24 -0
  826. package/src/renderer/utils/formatters.ts +61 -0
  827. package/src/renderer/utils/groupTransformer.ts +744 -0
  828. package/src/renderer/utils/idleNotificationSemantics.ts +76 -0
  829. package/src/renderer/utils/keyboardUtils.ts +92 -0
  830. package/src/renderer/utils/lastOutputDetector.ts +150 -0
  831. package/src/renderer/utils/markdownPlugins.ts +60 -0
  832. package/src/renderer/utils/memberAvatarCatalog.ts +38 -0
  833. package/src/renderer/utils/memberHelpers.ts +858 -0
  834. package/src/renderer/utils/memberLaunchDiagnostics.ts +216 -0
  835. package/src/renderer/utils/memberRuntimeSummary.ts +122 -0
  836. package/src/renderer/utils/memberSpawnStatusPolling.ts +29 -0
  837. package/src/renderer/utils/mentionLinkify.ts +90 -0
  838. package/src/renderer/utils/mentionSuggestions.ts +34 -0
  839. package/src/renderer/utils/mergeTeamMessages.ts +27 -0
  840. package/src/renderer/utils/messageRenderEquality.ts +158 -0
  841. package/src/renderer/utils/modelExtractor.ts +90 -0
  842. package/src/renderer/utils/multimodelProviderVisibility.ts +32 -0
  843. package/src/renderer/utils/openCodeModelRecommendations.ts +1326 -0
  844. package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +79 -0
  845. package/src/renderer/utils/pathDisplay.ts +149 -0
  846. package/src/renderer/utils/pathNormalize.ts +61 -0
  847. package/src/renderer/utils/pathUtils.ts +47 -0
  848. package/src/renderer/utils/platformKeys.ts +24 -0
  849. package/src/renderer/utils/previewRegistry.ts +45 -0
  850. package/src/renderer/utils/projectColor.ts +54 -0
  851. package/src/renderer/utils/projectLookup.ts +64 -0
  852. package/src/renderer/utils/providerBackendIdentity.ts +43 -0
  853. package/src/renderer/utils/providerSlashCommands.ts +122 -0
  854. package/src/renderer/utils/quickOpenCache.ts +40 -0
  855. package/src/renderer/utils/refreshCliStatus.ts +17 -0
  856. package/src/renderer/utils/reportAssessments.ts +555 -0
  857. package/src/renderer/utils/reviewDecisionScope.ts +81 -0
  858. package/src/renderer/utils/reviewKey.ts +132 -0
  859. package/src/renderer/utils/runtimeDisplayName.ts +26 -0
  860. package/src/renderer/utils/scheduleFormatters.ts +45 -0
  861. package/src/renderer/utils/sessionAnalyzer.ts +1346 -0
  862. package/src/renderer/utils/sessionExporter.ts +427 -0
  863. package/src/renderer/utils/sessionTitleParser.ts +69 -0
  864. package/src/renderer/utils/skillCommandSuggestions.ts +77 -0
  865. package/src/renderer/utils/slashCommandExtractor.ts +154 -0
  866. package/src/renderer/utils/streamJsonParser.ts +393 -0
  867. package/src/renderer/utils/stringUtils.ts +50 -0
  868. package/src/renderer/utils/syntaxHighlighter.ts +158 -0
  869. package/src/renderer/utils/tabLabelDisambiguation.ts +99 -0
  870. package/src/renderer/utils/taskChangePresence.ts +19 -0
  871. package/src/renderer/utils/taskChangeRequest.ts +122 -0
  872. package/src/renderer/utils/taskCommentPendingReply.ts +74 -0
  873. package/src/renderer/utils/taskGrouping.ts +151 -0
  874. package/src/renderer/utils/taskReferenceUtils.ts +331 -0
  875. package/src/renderer/utils/teamEffortOptions.ts +150 -0
  876. package/src/renderer/utils/teamLaunchSummaryCopy.ts +17 -0
  877. package/src/renderer/utils/teamMessageExpandStorage.ts +39 -0
  878. package/src/renderer/utils/teamMessageFiltering.ts +95 -0
  879. package/src/renderer/utils/teamMessageKey.ts +14 -0
  880. package/src/renderer/utils/teamMessageReadStorage.ts +49 -0
  881. package/src/renderer/utils/teamModelAvailability.ts +465 -0
  882. package/src/renderer/utils/teamModelCatalog.ts +502 -0
  883. package/src/renderer/utils/teamModelContext.ts +34 -0
  884. package/src/renderer/utils/teamProvisioningPresentation.ts +772 -0
  885. package/src/renderer/utils/teamRuntimeSummary.ts +53 -0
  886. package/src/renderer/utils/toolLinkingEngine.ts +117 -0
  887. package/src/renderer/utils/toolRendering/index.ts +14 -0
  888. package/src/renderer/utils/toolRendering/toolContentChecks.ts +58 -0
  889. package/src/renderer/utils/toolRendering/toolSummaryHelpers.ts +276 -0
  890. package/src/renderer/utils/toolRendering/toolTokens.ts +57 -0
  891. package/src/renderer/utils/unwrapIpc.ts +31 -0
  892. package/src/renderer/utils/urlMatchUtils.ts +45 -0
  893. package/src/renderer/vite-env.d.ts +21 -0
  894. package/src/shared/constants/agentBlocks.ts +129 -0
  895. package/src/shared/constants/attachments.ts +175 -0
  896. package/src/shared/constants/cache.ts +12 -0
  897. package/src/shared/constants/cli.ts +15 -0
  898. package/src/shared/constants/crossTeam.ts +126 -0
  899. package/src/shared/constants/index.ts +15 -0
  900. package/src/shared/constants/kanban.ts +9 -0
  901. package/src/shared/constants/memberColors.ts +140 -0
  902. package/src/shared/constants/opencodeTaskLogAttribution.ts +1 -0
  903. package/src/shared/constants/teamLimits.ts +2 -0
  904. package/src/shared/constants/trafficLights.ts +60 -0
  905. package/src/shared/constants/triggerColors.ts +126 -0
  906. package/src/shared/constants/window.ts +12 -0
  907. package/src/shared/types/api.ts +1062 -0
  908. package/src/shared/types/ccConnect.ts +399 -0
  909. package/src/shared/types/cliInstaller.ts +335 -0
  910. package/src/shared/types/editor.ts +279 -0
  911. package/src/shared/types/extensions/api.ts +90 -0
  912. package/src/shared/types/extensions/apikey.ts +40 -0
  913. package/src/shared/types/extensions/common.ts +16 -0
  914. package/src/shared/types/extensions/index.ts +67 -0
  915. package/src/shared/types/extensions/mcp.ts +132 -0
  916. package/src/shared/types/extensions/plugin.ts +85 -0
  917. package/src/shared/types/extensions/skill.ts +173 -0
  918. package/src/shared/types/index.ts +48 -0
  919. package/src/shared/types/ipc.ts +5 -0
  920. package/src/shared/types/notifications.ts +407 -0
  921. package/src/shared/types/providers.ts +116 -0
  922. package/src/shared/types/review.ts +319 -0
  923. package/src/shared/types/schedule.ts +124 -0
  924. package/src/shared/types/team.ts +1726 -0
  925. package/src/shared/types/terminal.ts +49 -0
  926. package/src/shared/types/visualization.ts +60 -0
  927. package/src/shared/utils/__tests__/contextMetrics.test.ts +260 -0
  928. package/src/shared/utils/__tests__/ephemeralProjectPath.test.ts +42 -0
  929. package/src/shared/utils/__tests__/teamProvider.test.ts +29 -0
  930. package/src/shared/utils/agentLanguage.ts +122 -0
  931. package/src/shared/utils/anthropicLaunchModel.ts +96 -0
  932. package/src/shared/utils/anthropicModelDefaults.ts +3 -0
  933. package/src/shared/utils/apiErrorDetector.ts +13 -0
  934. package/src/shared/utils/boardTaskActivityLabels.ts +128 -0
  935. package/src/shared/utils/boardTaskActivityPresentation.ts +75 -0
  936. package/src/shared/utils/cliArgsParser.ts +129 -0
  937. package/src/shared/utils/contentSanitizer.ts +207 -0
  938. package/src/shared/utils/contextMetrics.ts +236 -0
  939. package/src/shared/utils/costFormatting.ts +45 -0
  940. package/src/shared/utils/diffContextHash.ts +22 -0
  941. package/src/shared/utils/effortLevels.ts +73 -0
  942. package/src/shared/utils/ephemeralProjectPath.ts +41 -0
  943. package/src/shared/utils/errorHandling.ts +26 -0
  944. package/src/shared/utils/extensionNormalizers.ts +336 -0
  945. package/src/shared/utils/idleNotificationSemantics.ts +86 -0
  946. package/src/shared/utils/inboxNoise.ts +158 -0
  947. package/src/shared/utils/leadDetection.ts +63 -0
  948. package/src/shared/utils/logger.ts +69 -0
  949. package/src/shared/utils/markdownTextSearch.ts +210 -0
  950. package/src/shared/utils/mcpScopes.ts +36 -0
  951. package/src/shared/utils/modelParser.ts +158 -0
  952. package/src/shared/utils/opencodeModelRef.ts +78 -0
  953. package/src/shared/utils/platformPath.ts +103 -0
  954. package/src/shared/utils/pricing.ts +129 -0
  955. package/src/shared/utils/providerBackend.ts +90 -0
  956. package/src/shared/utils/providerExtensionCapabilities.ts +92 -0
  957. package/src/shared/utils/providerModelSelection.ts +5 -0
  958. package/src/shared/utils/providerModelVisibility.ts +47 -0
  959. package/src/shared/utils/rateLimitDetector.ts +334 -0
  960. package/src/shared/utils/reviewState.ts +74 -0
  961. package/src/shared/utils/sentryConfig.ts +26 -0
  962. package/src/shared/utils/skillRoots.ts +93 -0
  963. package/src/shared/utils/slashCommands.ts +128 -0
  964. package/src/shared/utils/taskChangePresence.ts +35 -0
  965. package/src/shared/utils/taskChangeSince.ts +51 -0
  966. package/src/shared/utils/taskChangeState.ts +49 -0
  967. package/src/shared/utils/taskHistory.ts +82 -0
  968. package/src/shared/utils/taskIdentity.ts +32 -0
  969. package/src/shared/utils/teamGraphDefaultLayout.ts +97 -0
  970. package/src/shared/utils/teamMemberColors.ts +107 -0
  971. package/src/shared/utils/teamMemberName.ts +77 -0
  972. package/src/shared/utils/teamProvider.ts +73 -0
  973. package/src/shared/utils/teamStableOwnerId.ts +12 -0
  974. package/src/shared/utils/teammateMessageParser.ts +52 -0
  975. package/src/shared/utils/tokenFormatting.ts +91 -0
  976. package/src/shared/utils/toolSummary.ts +279 -0
  977. package/src/shared/utils/version.ts +29 -0
  978. package/src/types/agent-teams-controller.d.ts +163 -0
  979. package/src/types/node-pty.d.ts +22 -0
  980. package/src/types/pidusage.d.ts +23 -0
@@ -0,0 +1,2846 @@
1
+ # Agent Graph - Stable Slot Layout Plan
2
+
3
+ ## Статус
4
+
5
+ - Дата фиксации: `2026-04-15`
6
+ - Выбранный подход: `Variant 3 - stable sectors around lead`
7
+ - Оценка выбранного подхода: `🎯 8 🛡️ 9 🧠 8`
8
+ - Примерный объём: `500-1100 строк + тесты + cleanup`
9
+ - Тип документа: `implementation spec`
10
+
11
+ Этот документ считается **нормативным** для следующей большой переделки layout графа.
12
+
13
+ Если в документе что-то сформулировано как "фиксируем", "обязательно", "не делаем", это уже не brainstorming, а зафиксированное решение. Переоткрывать такие решения в процессе реализации не нужно, если только не найден новый критичный риск.
14
+
15
+ ## Нормативность разделов
16
+
17
+ Чтобы не было путаницы между spec и advisory-текстом, фиксируем это явно.
18
+
19
+ ### Нормативные разделы
20
+
21
+ Нормативными считаются все разделы, которые описывают:
22
+
23
+ - model / topology
24
+ - stable identity
25
+ - storage/source of truth
26
+ - geometry contracts
27
+ - planner rules
28
+ - validation / commit behavior
29
+ - drag / fit / filter semantics
30
+ - acceptance criteria
31
+ - test plan
32
+
33
+ Именно они определяют, как feature должна работать.
34
+
35
+ ### Advisory разделы
36
+
37
+ Advisory-разделами считаются:
38
+
39
+ - `Recommended PR split`
40
+ - `Что ещё можно тюнить без изменения архитектуры`
41
+ - примеры псевдокода, если они не противоречат более строгим правилам выше
42
+
43
+ Они помогают выполнять refactor, но не имеют права переопределять core rules.
44
+
45
+ ### Правило при конфликте
46
+
47
+ Если пример, псевдокод или PR-split визуально расходятся с более строгим invariant/rule/acceptance пунктом, побеждает более строгий invariant/rule/acceptance пункт.
48
+
49
+ ## TL;DR
50
+
51
+ Мы уходим от текущей полусвободной схемы, где owner-зоны лечатся локальными packer-ами и overlay-хитростями, и переходим к более стабильной модели:
52
+
53
+ - `lead` остаётся в центре
54
+ - каждый `member` получает **свой slot**
55
+ - slots распределяются **по стабильным секторам вокруг lead**
56
+ - если места не хватает, используется **second ring**, а при необходимости и следующие outer rings
57
+ - внутри каждого slot layout всегда один и тот же:
58
+ - `Activity` сверху
59
+ - `Member` в центре
60
+ - `Process` как attached sub-rail
61
+ - `Tasks` снизу
62
+ - drag сохраняет не `x/y`, а `slot assignment`
63
+ - `tasks` bounded по высоте до `5` visible rows
64
+ - `activity` bounded до `3` items
65
+ - все active non-empty kanban columns показываются, а `slot width` увеличивается, чтобы их вместить
66
+
67
+ Главный результат, который должен почувствовать пользователь:
68
+
69
+ - у каждого участника есть своё место
70
+ - `Activity` и `Tasks` больше не налезают на чужие зоны
71
+ - zoom/pan больше не создают ощущение, что owner-local UI "плавает" отдельно от диаграммы
72
+ - dense teams читаются стабильнее и предсказуемее
73
+
74
+ ## Quick decision table
75
+
76
+ Чтобы implementer не листал весь документ ради очевидного ответа, фиксируем самые важные развилки прямо здесь.
77
+
78
+ - `lead` - не обычный member slot, а `central reserved block`
79
+ - `member` - получает `member sector slot`
80
+ - `unassigned tasks` - получают `special slot` под lead только если такие задачи реально есть в dataset
81
+ - `showTasks/showProcesses/showEdges` - скрывают presentation, но не меняют layout topology
82
+ - `graph tab/fullscreen` - шарят layout state, но могут иметь разный camera state
83
+ - `slotAssignmentsByTeam` - хранит только member sector assignments
84
+ - `teamName` в `v1` - storage scope key для layout state
85
+ - `member.name -> agentId` - one-time migration, если version не сбрасывался
86
+ - `team rename` - автоматическая миграция layout state не входит в этот refactor
87
+ - `no lead in dataset` - новый planner не должен пытаться строить stable sectors без lead
88
+
89
+ ## State transition matrix
90
+
91
+ Это краткая operational-шпаргалка: какое событие что именно имеет право менять.
92
+
93
+ | Событие | Меняется owner set | Меняется slot assignment | Меняется camera state | Нужен planner run | Примечание |
94
+ |---|---|---:|---:|---:|---|
95
+ | `zoom / pan` | нет | нет | да | нет | только camera transform |
96
+ | `showTasks / showProcesses / showEdges` | нет | нет | нет | нет | меняется только presentation |
97
+ | новый `message/comment` без роста footprint | нет | нет | нет | нет | activity content update only |
98
+ | process content update без роста reserved band | нет | нет | нет | нет | process presentation only |
99
+ | growth owner footprint | нет | возможно | нет | да | partial replanning only |
100
+ | `member add` | да | да | нет | да | новый owner ищет первый valid slot |
101
+ | `member remove/hide` | да | нет для остальных | нет | возможно | без global compaction |
102
+ | hidden member reappears | да | обычно нет | нет | возможно | сначала пробуем старый assignment |
103
+ | drag/drop owner | нет | да | нет | да | snap/swap path |
104
+ | `member.name -> agentId` | нет | возможно | нет | возможно | one-time migration before planner |
105
+ | team switch | да, scoped | нет | да | нет | просто переключаем team-scoped state |
106
+ | team rename | зависит от storage key | как новый scope в `v1` | нет | возможно | auto-migration не входит |
107
+ | no lead transient state | dataset invalid для planner | нет | нет | нет | safe fallback only |
108
+
109
+ ## Coder Start Here
110
+
111
+ Если начинать реализацию прямо сейчас, безопасный порядок такой:
112
+
113
+ 1. Сначала протянуть `agentId` и перевести owner identity на `stableOwnerId`
114
+ 2. Потом вынести pure planner helpers и покрыть их unit tests
115
+ 3. Потом добавить `slotAssignmentsByTeam` как новый source of truth
116
+ 4. Потом интегрировать planner в owner placement
117
+ 5. Потом привязать `Tasks`, `Process`, `Activity` к `slot frame`
118
+ 6. Потом сделать drag/snap/swap
119
+ 7. Потом удалить старые geometry paths
120
+
121
+ Нельзя начинать с UI и рисования activity/task zones поверх старой geometry - это почти гарантированно снова создаст двусмысленность и временные баги.
122
+
123
+ ## Responsibility split by layer
124
+
125
+ Чтобы реализация не размазала layout-логику по разным React paths, фиксируем ownership заранее.
126
+
127
+ ### Data / adapter layer отвечает только за content model
128
+
129
+ Сюда относится:
130
+
131
+ - `stableOwnerId`
132
+ - grouping задач, activity и process-данных по owner-у
133
+ - вычисление active non-empty kanban columns
134
+ - подготовка content metadata для task/activity/process bands
135
+
136
+ Сюда **не** относится:
137
+
138
+ - вычисление world positions
139
+ - slot assignment
140
+ - screen-space correction
141
+ - DOM measurement
142
+
143
+ ### Layout / planner layer отвечает только за topology и geometry
144
+
145
+ Сюда относится:
146
+
147
+ - `OwnerFootprint`
148
+ - `slotAssignmentsByTeam`
149
+ - `SlotFrame`
150
+ - ring / sector planning
151
+ - collision / exclusion validation
152
+ - fit bounds
153
+
154
+ Сюда **не** относится:
155
+
156
+ - отрисовка текста
157
+ - hover state
158
+ - tooltip placement
159
+ - post-render visual "подталкивание" lane-ов
160
+
161
+ ### Renderer / interaction layer отвечает только за presentation
162
+
163
+ Сюда относится:
164
+
165
+ - drawing canvas / DOM content внутри уже готового `SlotFrame`
166
+ - hit testing
167
+ - hover / selection / popover
168
+ - drag preview и commit нового assignment
169
+
170
+ Сюда **не** относится:
171
+
172
+ - решение cross-owner overlap
173
+ - самостоятельный пересчёт slot width/height
174
+ - отдельный layout path для fullscreen vs tab
175
+
176
+ ## Persistent vs derived state
177
+
178
+ Одна из главных вещей, которую нельзя оставить "по ощущениям" - что именно мы храним, а что каждый раз честно пересчитываем.
179
+
180
+ ### Persistent state
181
+
182
+ Храним только то, что реально должно переживать refresh и reopening:
183
+
184
+ - `slotAssignmentsByTeam[teamName][stableOwnerId]`
185
+ - `slotLayoutVersion`
186
+
187
+ Опционально можно хранить технические timestamp/debug markers, но они не должны становиться источником истины для geometry.
188
+
189
+ ### Derived state
190
+
191
+ Каждый planner run заново считает:
192
+
193
+ - `visible owner set`
194
+ - `OwnerFootprint`
195
+ - `lead central reserved block`
196
+ - `runtimeCentralExclusion`
197
+ - `SlotFrame[]`
198
+ - `fit bounds`
199
+ - band-local anchors
200
+
201
+ ### Session-local last valid snapshot cache
202
+
203
+ Для `v1` дополнительно допускается **только session-local** cache последнего валидного `StableSlotLayoutSnapshot` по `teamName`.
204
+
205
+ Это не persistent source of truth для placement, а технический safety/cache layer.
206
+
207
+ #### Что это значит
208
+
209
+ - assignments остаются source of truth для member placement
210
+ - snapshot cache не заменяет assignments
211
+ - snapshot cache не пишется в config/main/storage как обязательная часть этого refactor
212
+ - snapshot cache используется только для safe handoff между planner runs и для fail-closed поведения
213
+
214
+ #### Что нельзя делать
215
+
216
+ - использовать snapshot cache как новую альтернативу `slotAssignmentsByTeam`
217
+ - восстанавливать из него layout identity независимо от текущих assignments
218
+ - silently коммитить stale snapshot как будто это новый валидный planner result
219
+
220
+ ### Что принципиально не должно быть persistent
221
+
222
+ - raw `x/y`
223
+ - screen-space coordinates
224
+ - post-render measured widths/heights
225
+ - activity/process/task band positions
226
+ - `lead central reserved block`
227
+ - `unassigned task slot`
228
+
229
+ Иначе layout снова станет зависеть от случайного порядка рендера и старых геометрических хвостов.
230
+
231
+ ## Hard invariants
232
+
233
+ Ниже набор правил, которые нельзя нарушать в реализации даже временно, если только это не изолированный промежуточный локальный WIP.
234
+
235
+ - owner placement source of truth - это `slot assignment`, не raw `x/y`
236
+ - `lead` всегда центральный anchor
237
+ - slot contents всегда upright и не ротируются по sector angle
238
+ - `activity band`, `process band`, `task band` bounded по высоте
239
+ - все `active non-empty` kanban columns показываются
240
+ - slot width может расти, slot height по bands остаётся bounded
241
+ - вся slot geometry считается в `world coordinates`, а не в screen-space
242
+ - planner не читает размеры из DOM и не зависит от post-render measurement
243
+ - zoom/pan меняют только camera transform
244
+ - planner сохраняет existing placements whenever still valid
245
+ - старый pack/force/pinning path не должен параллельно переопределять новый planner
246
+
247
+ ## Что не входит в этот план
248
+
249
+ Этот документ описывает именно **layout refactor для owner-local zones**.
250
+
251
+ В этот pass не нужно заново переоткрывать или смешивать сюда:
252
+
253
+ - redesign review semantics
254
+ - новые edge interactions
255
+ - minimap
256
+ - timeline
257
+ - particles redesign
258
+ - отдельные эксперименты с process content semantics
259
+ - отдельные refactor-ы не связанные с owner slot geometry
260
+
261
+ Если что-то из этого потребуется, это должен быть отдельный spec, а не тихое расширение этого плана.
262
+
263
+ ## Зачем нужен новый layout
264
+
265
+ Текущая схема уже лучше, чем старый overlay-path, но всё ещё имеет системную проблему:
266
+
267
+ - `Activity` и `Tasks` конкурируют за одно и то же пространство
268
+ - owner-local зоны удерживаются постфактум через packer и визуальные коррекции
269
+ - при большом числе участников диаграмма становится слишком зависимой от текущей геометрии и порядка обновлений
270
+ - поведение при refresh, zoom, плотном графе и смене количества задач остаётся недостаточно предсказуемым
271
+
272
+ Пользовательский приоритет в этой фиче уже явно выбран:
273
+
274
+ - меньше "живой физики"
275
+ - больше стабильности
276
+ - больше читаемости
277
+ - больше правдивой резервации места под owner-local UI
278
+
279
+ ## Что зафиксировано окончательно
280
+
281
+ Ниже список решений, которые уже приняты.
282
+
283
+ ## 1. Общая модель layout
284
+
285
+ - Используем `stable sectors around lead`
286
+ - Не возвращаемся к полностью свободной раскладке owners
287
+ - Не делаем "одна жёсткая колонка строго вниз"
288
+ - `lead` остаётся в центре как отдельный central reserved block
289
+ - `members` располагаются вокруг `lead`, и каждый `member` живёт внутри собственного вертикального slot
290
+ - `unassigned tasks`, если они есть, живут в отдельном нижнем bounded slot под `lead`
291
+
292
+ ## 1.1. Кто получает layout-reserved место
293
+
294
+ Чтобы не было разной трактовки состава layout actors, фиксируем это явно.
295
+
296
+ ### Central reserved block получает
297
+
298
+ - `lead`
299
+
300
+ `Lead` получает не обычный `member slot`, а свой отдельный центральный reserved block.
301
+
302
+ ### Sector slot получают
303
+
304
+ - каждый **видимый активный member**, который участвует в текущем graph owner set
305
+
306
+ ### Conditional special slot получает
307
+
308
+ - `unassigned tasks`, если в текущем visible graph dataset есть хотя бы одна такая задача
309
+
310
+ ### Slot не получают как самостоятельные owners
311
+
312
+ - tasks
313
+ - processes
314
+ - particles
315
+ - edges
316
+ - removed members, если они не должны быть видимы по текущему graph visibility rule
317
+
318
+ То есть:
319
+
320
+ - у `member` есть sector slot
321
+ - у `lead` есть central reserved block
322
+ - у `unassigned tasks` есть отдельный special slot только когда он реально нужен
323
+ - `tasks/process/activity` - это внутренние band-ы slot-а или central block-а, а не самостоятельные owners
324
+
325
+ ## 2. Lead
326
+
327
+ - `lead` остаётся центральным anchor
328
+ - `lead` не является обычным `member slot`
329
+ - `lead` не участвует в обычном drag/snap flow
330
+ - `lead` всегда остаётся в центре layout
331
+ - `launch HUD` для `lead` остаётся отдельной reserved zone
332
+ - `lead activity` тоже остаётся отдельной reserved zone, но входит в central exclusion
333
+ - `lead` не участвует в member ring planner как ещё один slot candidate
334
+ - `lead` не получает обычный member-style `task band`
335
+ - Для `v1` фиксируем default стороны:
336
+ - `lead activity` - слева от lead
337
+ - `launch HUD` - справа от lead
338
+
339
+ ### Важное уточнение
340
+
341
+ Для `v1` `lead activity` использует те же bounded activity rules:
342
+
343
+ - `3` visible items
344
+ - `+N more`
345
+ - `newest first`
346
+
347
+ ## 3. Activity
348
+
349
+ - `Activity` является частью owner slot, а не отдельным свободным overlay-режимом
350
+ - Видимых элементов: `3`
351
+ - Порядок: **newest first**
352
+ - После них показывается `+N more`
353
+ - `+N more` открывает профиль участника сразу на вкладке `Activity`
354
+ - В activity feed попадают и `messages`, и `comments`
355
+ - У комментария target - это `task`, а не другой участник
356
+ - Для activity нужно переиспользовать уже существующий compact UI сообщений, а не придумывать новый визуальный язык
357
+
358
+ ## 4. Tasks
359
+
360
+ - Task area живёт внутри owner slot
361
+ - Сохраняем current multi-column kanban semantics
362
+ - Видимая высота task band ограничена `5` rows
363
+ - Все **активные non-empty** kanban columns показываются
364
+ - Если колонок больше, `slot width` увеличивается
365
+ - Не вводим scroll внутри slot как основную механику
366
+ - Не скрываем колонки только ради того, чтобы "поместилось"
367
+ - Внутри колонки сохраняем текущую каноническую task order semantics, а не придумываем новый sort-rule в рамках этого refactor
368
+
369
+ ## 4.1. Unassigned tasks
370
+
371
+ Так как в системе могут быть задачи без owner-а, это тоже нужно зафиксировать.
372
+
373
+ ### Правило для v1
374
+
375
+ - задачи без owner-а не теряются
376
+ - они не распределяются случайно по member slots
377
+ - для них используется отдельный `unassigned task slot`
378
+
379
+ ### Где он живёт
380
+
381
+ - `unassigned task slot` располагается в нижней части central area, под lead
382
+ - он не участвует в member ring placement
383
+ - он не draggable
384
+ - он создаётся только если есть хотя бы одна видимая unassigned task
385
+
386
+ ### Что происходит при его появлении или исчезновении
387
+
388
+ - это может расширить нижнюю central exclusion зону
389
+ - planner может локально вытолкнуть конфликтующие нижние member slots дальше
390
+ - это не должно вызывать глобальный reshuffle всех owners
391
+
392
+ ### Что внутри
393
+
394
+ - только `task band`
395
+ - без `activity band`
396
+ - без `process band`
397
+ - bounded по тем же правилам:
398
+ - `5 rows`
399
+ - все active non-empty columns
400
+ - overflow stack per column
401
+
402
+ Это отдельный pseudo-owner case, чтобы planner не терял нераспределённые задачи и не пытался притвориться, что их не существует.
403
+
404
+ ## 5. Process
405
+
406
+ - Process остаётся в диаграмме
407
+ - Process становится owner-local
408
+ - Process отображается как маленький attached sub-rail участника
409
+ - Текущий визуальный стиль process хорош и должен быть переиспользован
410
+ - Process должен учитываться в slot footprint
411
+
412
+ ## 6. Drag
413
+
414
+ - Drag owner разрешён
415
+ - После отпускания owner снапается в ближайший slot
416
+ - Если slot занят, по умолчанию делаем `swap`
417
+ - Raw `x/y` не сохраняем как источник истины
418
+ - Сохраняем именно `slot assignment`
419
+
420
+ ## 7. Stable identity
421
+
422
+ - Основной стабильный идентификатор участника: `config.members[].agentId`
423
+ - Fallback: `member.name`
424
+ - `ResolvedTeamMember` должен получить `agentId?: string`
425
+ - Все layout-решения должны жить на stable owner id, а не на display name
426
+
427
+ ### Контракт уникальности
428
+
429
+ План исходит из того, что внутри одной команды:
430
+
431
+ - `agentId` уникален, если он присутствует
432
+ - `member.name` уникален в fallback-режиме
433
+
434
+ Если это нарушено, реализация не должна тихо склеивать двух owners в один.
435
+
436
+ Обязательное поведение:
437
+
438
+ - в dev/test - явный assert или error
439
+ - в runtime - явный warning/error path без silent merge
440
+
441
+ ## 8. Width buckets
442
+
443
+ - Используем `S / M / L`
444
+ - Buckets нужны для packing/planning
445
+ - Buckets не имеют права обрезать контент
446
+ - Buckets не подменяют реальную ширину выдуманной константой
447
+
448
+ ## 9. Second ring
449
+
450
+ - `Second ring` нужен
451
+ - Ring capacity считаем по footprint / budget, а не по тупому member count
452
+ - Нельзя строить правило вида "до 10 участников один круг, потом второй"
453
+ - В `v1` у каждого ring есть `6` canonical sector anchors, то есть номинально один ring не может вместить больше `6` owners
454
+ - Реальная usable capacity ring-а может быть **меньше**, если какие-то slot frames не проходят по footprint / exclusion / gap constraints
455
+ - То есть spill на outer ring определяется не условием `memberCount > N`, а реальной валидностью candidate frames
456
+ - `Second ring` здесь - это shorthand для outer-ring model. Planner не должен останавливаться ровно на двух rings, если команда реально крупнее
457
+
458
+ ## 10. Сектора
459
+
460
+ - Для `v1` фиксируем `6` секторных направлений вокруг lead
461
+ - Порядок секторов - по часовой стрелке, начиная сверху:
462
+ - `top`
463
+ - `upper-right`
464
+ - `lower-right`
465
+ - `bottom`
466
+ - `lower-left`
467
+ - `upper-left`
468
+ - Следующий ring использует те же sector ids
469
+
470
+ ## Уточнения, которые снимают двусмысленность
471
+
472
+ Ниже решения, которые нужно понимать **однозначно**, чтобы не было разных трактовок при реализации.
473
+
474
+ ## 1. Что значит "видно 5 задач"
475
+
476
+ Это **не** "5 задач суммарно на весь slot".
477
+
478
+ Это значит:
479
+
480
+ - task band имеет высоту `5 rows`
481
+ - каждая kanban column внутри этого band может использовать максимум `5 visible rows`
482
+ - если колонка переполнена, последний visible row превращается в overflow stack
483
+
484
+ То есть для overflowing column действует правило:
485
+
486
+ - `4 реальных pills + overflow stack`
487
+
488
+ Высота task band остаётся постоянной.
489
+
490
+ ## 2. Что значит "видно 3 activity"
491
+
492
+ Это значит:
493
+
494
+ - в activity band показываются `3` реальных activity items
495
+ - ниже может быть отдельный footer `+N more`
496
+ - footer не считается "четвёртым activity item"
497
+
498
+ ## 3. Что значит "все колонки показываем"
499
+
500
+ Это значит:
501
+
502
+ - не ограничиваем число видимых **active non-empty** kanban columns
503
+ - не вводим horizontal scroll
504
+ - не делаем "покажи первые 3, остальные спрячь"
505
+ - если активных колонок стало больше, `slot width` должен вырасти
506
+
507
+ Это **не** значит, что нужно внезапно начать показывать пустые canonical columns, которых раньше на доске не было видно. Текущее правило "показываем только active non-empty columns" сохраняется.
508
+
509
+ ### Что считается active non-empty column
510
+
511
+ Для этого плана колонка считается `active non-empty`, если в ней есть хотя бы одна task, которая должна быть видима в owner task zone до применения overflow-stack подрезки.
512
+
513
+ Проще говоря:
514
+
515
+ - пустые канонические колонки не показываем
516
+ - колонка с задачами показывается, даже если часть задач ушла в overflow stack
517
+
518
+ ## 4. Что именно bounded
519
+
520
+ Bounded должны быть:
521
+
522
+ - высота activity band
523
+ - высота process band
524
+ - высота task band
525
+
526
+ Не bounded:
527
+
528
+ - число kanban columns
529
+ - общая slot width
530
+
531
+ ## 5. Когда layout может двигаться
532
+
533
+ Layout **может** менять slot placement только в понятных случаях:
534
+
535
+ - добавился новый участник
536
+ - удалился участник
537
+ - пользователь вручную перетащил участника
538
+ - owner slot реально вырос по footprint настолько, что текущий ring больше не может его честно вместить
539
+ - пользователь явно нажал reset layout
540
+
541
+ Во всех остальных случаях layout placement меняться не должен.
542
+
543
+ Во всех остальных случаях layout **не должен** перескакивать.
544
+
545
+ ## 6. Когда layout не должен двигаться
546
+
547
+ Layout не должен менять slot placement из-за:
548
+
549
+ - zoom
550
+ - pan
551
+ - нового message/comment
552
+ - смены unread count
553
+ - появления или исчезновения process rail, если footprint band остаётся в пределах резерва
554
+ - rename, если `agentId` тот же
555
+ - изменений данных внутри уже существующих visible rows, если slot footprint не меняется
556
+
557
+ ## Негативные решения - что сознательно не делаем
558
+
559
+ Эти варианты сознательно отклонены и не должны "случайно вернуться" в код под видом быстрых фиксов.
560
+
561
+ - Не продолжаем лечить owner overlap screen-space packer-ом как основным механизмом
562
+ - Не сохраняем raw `x/y` для owner layout
563
+ - Не возвращаем owner placement под свободный d3-force
564
+ - Не вводим local scroll внутри slot как первую механику
565
+ - Не скрываем kanban columns ради fit
566
+ - Не оставляем process floating отдельно от owner
567
+ - Не делаем агрессивный auto-compact всех owners при каждом member remove
568
+
569
+ ## Канонические термины
570
+
571
+ Чтобы не путаться в названиях при реализации:
572
+
573
+ - `owner` - `lead` или `member`, вокруг которого строится локальная зона
574
+ - `stableOwnerId` - стабильный id участника, построенный из `agentId` или fallback на `name`
575
+ - `slot` - общий разговорный термин для layout-reserved зоны, но в коде лучше не использовать его без уточнения
576
+ - `member sector slot` - обычный slot участника, который реально планируется ring/sector planner-ом
577
+ - `special slot` - специальная зарезервированная зона, не живущая в member assignments, например `unassigned task slot`
578
+ - `central reserved block` - специальная центральная зона lead-а
579
+ - `sector` - одно из 6 направлений вокруг lead
580
+ - `ring` - круг уровнем дальше от lead
581
+ - `slot assignment` - привязка **member sector slot** к `(ringIndex, sectorIndex)`
582
+ - `slot frame` - прямоугольник **member sector slot** в world coordinates, если отдельно не сказано иное
583
+ - `central exclusion` - запрещённая зона вокруг lead, учитывающая lead, launch HUD и lead activity
584
+ - `task band` - нижняя часть slot, где живёт kanban
585
+ - `activity band` - верхняя часть slot
586
+ - `process band` - узкая зона между owner и task band
587
+
588
+ ### Runtime central exclusion
589
+
590
+ Чтобы не было разной трактовки в коде, под итоговым `runtime central exclusion` в этом документе понимается:
591
+
592
+ ```ts
593
+ runtimeCentralExclusion =
594
+ union(
595
+ leadCentralReservedBlock,
596
+ optionalUnassignedTaskSlot
597
+ ) + centralSafetyPadding
598
+ ```
599
+
600
+ То есть нижний `unassigned task slot`, если он существует, становится частью итоговой центральной запрещённой зоны для member slot planner-а.
601
+
602
+ ## Целевая визуальная схема
603
+
604
+ ```text
605
+ [ Activity x3 ]
606
+ [ Member A ]
607
+ [ Process ]
608
+ [ Tasks x5 ]
609
+
610
+
611
+ [ Activity x3 ] [ Activity x3 ]
612
+ [ Member B ] [ Lead ] [ Member C ]
613
+ [ Process ] [ Launch HUD ] [ Process ]
614
+ [ Tasks x5 ] [ Tasks x5 ]
615
+
616
+
617
+ [ Activity x3 ]
618
+ [ Member D ]
619
+ [ Process ]
620
+ [ Tasks x5 ]
621
+ ```
622
+
623
+ Если owner slots не помещаются в ring 1 честно, следующий owner уходит в ring 2, а не пытается "додавиться" между соседями.
624
+
625
+ ## Точная структура slot
626
+
627
+ Каждый member slot строится по одинаковой схеме.
628
+
629
+ ### Политика резервации места
630
+
631
+ Slot резервирует геометрию под все свои bands **всегда**, даже если в конкретный момент band визуально пустой.
632
+
633
+ Это значит:
634
+
635
+ - пустой `Activity` band может не рисовать карточки, но его высота зарезервирована
636
+ - пустой `Process` band может не рисовать rail, но его высота зарезервирована
637
+ - пустой `Task` band может не рисовать tasks, но его высота зарезервирована
638
+
639
+ Это осознанный tradeoff ради того, чтобы slot не прыгал по высоте при каждом изменении данных.
640
+
641
+ ### Slot local anatomy
642
+
643
+ 1. `Activity band`
644
+ 2. `Owner band`
645
+ 3. `Process band`
646
+ 4. `Task band`
647
+
648
+ ### Activity band
649
+
650
+ - расположен сверху slot
651
+ - имеет фиксированную высоту под:
652
+ - `3 activity items`
653
+ - `+N more footer`
654
+ - footer height резервируется всегда, чтобы не было layout jump
655
+ - connector к owner рисуется короткий и локальный
656
+ - если activity entries сейчас нет, band остаётся пустым визуально, но не схлопывается геометрически
657
+
658
+ ### Owner band
659
+
660
+ - содержит сам `member node`
661
+ - owner node является точкой привязки локального layout
662
+ - label / role / runtime status остаются на owner node
663
+
664
+ ### Process band
665
+
666
+ - живёт между owner и tasks
667
+ - имеет фиксированную высоту
668
+ - резервируется всегда, даже если сейчас process не отображается
669
+ - это сознательный tradeoff ради стабильности
670
+
671
+ ### Task band
672
+
673
+ - расположен внизу slot
674
+ - имеет фиксированную высоту `5 rows`
675
+ - содержит все активные non-empty kanban columns
676
+ - каждая колонка рендерится внутри общей высоты band
677
+ - если task-ов сейчас нет, band остаётся пустым визуально, но не схлопывается геометрически
678
+
679
+ ## Поведение task overflow
680
+
681
+ Так как мы сохраняем multi-column kanban, overflow нужно трактовать строго.
682
+
683
+ ### Правило
684
+
685
+ Для каждой колонки отдельно:
686
+
687
+ - если задач `<= 5`, показываются реальные tasks
688
+ - если задач `> 5`, показывается:
689
+ - `4` реальных tasks
690
+ - `1` overflow stack в последнем row
691
+
692
+ ### Что это даёт
693
+
694
+ - высота slot стабильна
695
+ - все columns видимы
696
+ - overflow честно обозначен
697
+ - не нужен scroll
698
+
699
+ ### Порядок задач внутри колонки
700
+
701
+ Этот refactor **не меняет** текущую каноническую задачу порядка внутри колонки.
702
+
703
+ Значит:
704
+
705
+ - если сейчас в колонке есть уже существующий deterministic order, его сохраняем
706
+ - stable slot layout не должен попутно вводить новый sort по времени, title или id
707
+ - overflow stack строится поверх уже существующего column order
708
+
709
+ ## Поведение activity overflow
710
+
711
+ Для activity lane:
712
+
713
+ - показываем `3` реальных items
714
+ - потом показываем `+N more`
715
+ - `+N more` открывает профиль участника на вкладке `Activity`
716
+
717
+ Внутри профиля участника уже должны продолжать работать:
718
+
719
+ - `All`
720
+ - `Messages`
721
+ - `Comments`
722
+
723
+ Это не часть новой layout-логики, но поведение нельзя потерять.
724
+
725
+ ## Activity item semantics
726
+
727
+ ### Messages
728
+
729
+ - переиспользуют существующий compact message widget
730
+ - клики и hover-поведение должны остаться совместимыми с уже существующим UI
731
+ - reuse boundary здесь - существующий visual/component behavior, а не обязательная привязка к текущему screen-space positioning path
732
+
733
+ ### Comments
734
+
735
+ - в visual target показывают `task display id`
736
+ - не маскируются под message между двумя участниками
737
+ - должны явно читаться как `comment on task`
738
+
739
+ ### Ordering
740
+
741
+ - в slot: newest first
742
+ - в полном activity tab: текущий существующий порядок не должен деградировать
743
+
744
+ ### Tie-break для activity ordering
745
+
746
+ Чтобы `newest first` не реализовали по-разному в двух местах, фиксируем:
747
+
748
+ 1. сначала сортируем по `timestamp desc`
749
+ 2. если timestamps совпали, используем стабильный secondary key:
750
+ - existing source order, если он уже есть в данных
751
+ - иначе `id`
752
+
753
+ Это нужно, чтобы activity lane не дрожала при одинаковых timestamps и повторных rebuild-ах.
754
+
755
+ ## Process semantics
756
+
757
+ Для `v1` process rail показывает **один самый релевантный process**:
758
+
759
+ 1. сначала running process
760
+ 2. если running нет, последний недавно завершённый visible process
761
+ 3. если релевантного process нет, band остаётся пустым, но место под него остаётся зарезервированным
762
+
763
+ ## Lead-specific geometry
764
+
765
+ Lead обрабатывается отдельно.
766
+
767
+ ### Lead central exclusion включает
768
+
769
+ - сам `lead node`
770
+ - `launch HUD`
771
+ - `lead activity band`
772
+ - минимальный safety padding вокруг этого блока
773
+
774
+ `launch HUD` reserved zone должна сохраняться анти-jump образом, даже если compact HUD сейчас скрыт или dismissed. Для planner-а это persistent часть central exclusion.
775
+
776
+ ### Важный инвариант
777
+
778
+ Ни один member slot не должен строиться так, будто этих central zones не существует.
779
+
780
+ Иначе первый ring снова залезет в центр.
781
+
782
+ ### Recommended central block anatomy
783
+
784
+ Чтобы `lead central reserved block` не трактовали по-разному в разных местах, фиксируем рекомендованную структуру:
785
+
786
+ 1. `lead activity frame` слева
787
+ 2. `lead core frame` по центру
788
+ 3. `launch HUD frame` справа
789
+
790
+ Где:
791
+
792
+ - `lead activity frame` использует те же bounded rules, что и member activity lane
793
+ - `lead core frame` содержит сам `lead node` и минимальную label/status area
794
+ - `launch HUD frame` резервируется даже если compact HUD сейчас hidden/dismissed
795
+
796
+ Итоговый `leadCentralReservedBlock` считается как union этих трёх частей плюс обязательные внутренние gap-ы и внешний safety padding.
797
+
798
+ ### Что важно для v1
799
+
800
+ - `lead activity frame` не схлопывается только потому, что сейчас у lead мало activity
801
+ - `launch HUD frame` не схлопывается только потому, что compact HUD сейчас не показан
802
+ - member planner не знает про внутренние части central block-а по отдельности, он видит уже собранный итоговый `leadCentralReservedBlock`
803
+
804
+ Это нужно, чтобы центр оставался стабильным и не пересобирался от мелких UI-состояний.
805
+
806
+ ## Stable identity - обязательная часть
807
+
808
+ Это самая критичная инфраструктурная часть плана.
809
+
810
+ Без неё slot assignment будет хрупким и будет ломаться при rename и refresh.
811
+
812
+ ### Что меняем
813
+
814
+ `ResolvedTeamMember` должен получить:
815
+
816
+ ```ts
817
+ agentId?: string;
818
+ ```
819
+
820
+ ### Правило stable owner id
821
+
822
+ ```ts
823
+ stableOwnerId = member.agentId ?? member.name
824
+ ```
825
+
826
+ ### Правило node id
827
+
828
+ Member node id в графе должен строиться на `stableOwnerId`, а не на display name.
829
+
830
+ То есть вместо старой name-based схемы нужен stable id path:
831
+
832
+ ```ts
833
+ memberNodeId = `member:${teamName}:${stableOwnerId}`
834
+ ```
835
+
836
+ Display label при этом остаётся `member.name`.
837
+
838
+ ### Где stableOwnerId обязателен
839
+
840
+ - member node id
841
+ - task.ownerId reference
842
+ - process ownership
843
+ - activity ownership
844
+ - slot assignment storage
845
+ - drag/snap
846
+ - ordering
847
+
848
+ ### Где name можно оставить только как display field
849
+
850
+ - label на node
851
+ - human-readable popover title
852
+ - callbacks, где UI дальше открывает профиль по имени
853
+
854
+ ## Visibility / removed member policy
855
+
856
+ Чтобы новый layout не начал внезапно показывать другой состав owners, фиксируем:
857
+
858
+ - этот refactor не переопределяет сам по себе graph visibility policy для removed members
859
+ - если removed member сейчас не должен быть owner node в графе, он не получает slot
860
+ - если задача ссылается на owner, которого нет в текущем visible owner set, такая задача должна попадать в `unassigned task slot`, а не ломать planner
861
+
862
+ ### Важное уточнение
863
+
864
+ `visible owner set` для planner-а строится из owner/node visibility policy, а не из presentation filters вида:
865
+
866
+ - `showTasks`
867
+ - `showProcesses`
868
+ - `showEdges`
869
+
870
+ То есть filters не должны внезапно менять состав member owners, которых planner пытается раскладывать.
871
+
872
+ ## Stable ordering - обязательное правило
873
+
874
+ Initial placement должен быть детерминированным.
875
+
876
+ Фиксируем такой порядок:
877
+
878
+ 1. Сначала уже сохранённые `slot assignments`
879
+ 2. Затем `teamData.config.members[]`, сматченные по `stableOwnerId`
880
+ 3. Затем owners, которых нет в config order
881
+ 4. Final tie-break - `stableOwnerId`
882
+
883
+ Это важно, чтобы:
884
+
885
+ - layout не дёргался при refresh
886
+ - порядок был связан с конфигом команды
887
+ - ordering не ломался при rename
888
+
889
+ ## Owner set resolution - before planner
890
+
891
+ Чтобы planner не собирал layout actors "по пути" из разных источников, фиксируем один этап до планирования.
892
+
893
+ ### До запуска planner-а должен быть построен
894
+
895
+ - `lead central reserved block`
896
+ - ordered visible member owners
897
+ - условный `unassigned task slot`, если он нужен
898
+
899
+ ### Чего там быть не должно
900
+
901
+ - tasks как самостоятельных owners
902
+ - process nodes как самостоятельных owners
903
+ - activity items как самостоятельных owners
904
+ - зависимостей от hover, selection, unread highlight или camera state
905
+
906
+ То есть сначала строим **layout actors set**, и только потом планируем геометрию.
907
+
908
+ ## Source of truth by concern
909
+
910
+ Чтобы в коде не появилось несколько "почти главных" источников истины, фиксируем это явно.
911
+
912
+ ### Identity
913
+
914
+ - source of truth: `stableOwnerId = agentId ?? member.name`
915
+
916
+ ### Owner placement
917
+
918
+ - source of truth для **member sector slots**: `slotAssignmentsByTeam[teamName][stableOwnerId]`
919
+ - `lead central reserved block` не хранится в `slotAssignmentsByTeam`
920
+ - `unassigned task slot` не хранится в `slotAssignmentsByTeam` и строится derivation-логикой от текущего dataset
921
+
922
+ ### Geometry
923
+
924
+ - source of truth: planner helpers, которые из assignments и footprints строят `SlotFrame`
925
+
926
+ ### Active render geometry
927
+
928
+ - source of truth: последний **валидный и текущий** `StableSlotLayoutSnapshot`, собранный из текущих inputs
929
+ - invalid candidate snapshot не становится active render geometry
930
+ - stale snapshot cache не должен подменять current render geometry, если для текущего pass активирован fallback path
931
+
932
+ ### Activity data
933
+
934
+ - source of truth: существующий messages/comments data path
935
+ - activity lane не строится из particles и не строится из transient overlay state
936
+
937
+ ### Task ordering
938
+
939
+ - source of truth: текущая каноническая kanban/column order semantics
940
+ - этот refactor её не переизобретает
941
+
942
+ ### Process selection
943
+
944
+ - source of truth: текущий process data path
945
+ - новый layout меняет positioning, а не выдумывает новый параллельный источник process state
946
+
947
+ ## Runtime precedence matrix
948
+
949
+ Чтобы во время интеграции не появилось несколько конкурирующих "почти верных" состояний, фиксируем runtime precedence явно.
950
+
951
+ ### 1. Placement identity precedence
952
+
953
+ Для `member sector slots` порядок такой:
954
+
955
+ 1. `slotAssignmentsByTeam[teamName][stableOwnerId]`
956
+ 2. planner default placement для owner без assignment
957
+
958
+ Больше никаких placement identity sources у `v1` нет.
959
+
960
+ ### 2. Geometry build precedence
961
+
962
+ Для текущего render-pass порядок такой:
963
+
964
+ 1. текущие inputs
965
+ 2. current assignments
966
+ 3. planner builds candidate snapshot
967
+ 4. validator либо подтверждает snapshot, либо отклоняет его
968
+
969
+ То есть geometry нельзя брать:
970
+
971
+ - из старого DOM layout
972
+ - из raw pinning
973
+ - из screen-space overlay коррекций
974
+
975
+ ### 3. Active render precedence
976
+
977
+ Для renderer порядок такой:
978
+
979
+ 1. если есть валидный current snapshot для этого pass - рендерим его
980
+ 2. если current snapshot invalid - не делаем его active render geometry
981
+ 3. если текущий path в `no-lead fallback`, рендерим fallback presentation path
982
+ 4. session-local last valid snapshot cache не становится active render geometry автоматически
983
+
984
+ ### 4. Persistence precedence
985
+
986
+ Для store/state:
987
+
988
+ 1. валидный committed assignment update пишет `slotAssignmentsByTeam`
989
+ 2. invalid candidate geometry ничего не пишет в assignments
990
+ 3. snapshot cache не пишет assignments сам по себе
991
+
992
+ ### Ключевая мысль
993
+
994
+ `slotAssignmentsByTeam` отвечает за identity placement, `StableSlotLayoutSnapshot` отвечает за валидную текущую geometry-картину, fallback path отвечает за безопасный transient rendering, и эти роли нельзя смешивать.
995
+
996
+ ## Где хранить slot assignment
997
+
998
+ Источник истины для slot placement должен жить **в renderer-side team UI state**, а не в graph package.
999
+
1000
+ Graph package должен получать уже готовые assignments / placement inputs.
1001
+
1002
+ Нормативная структура `v1`:
1003
+
1004
+ ```ts
1005
+ type OwnerSlotAssignment = {
1006
+ ownerStableId: string;
1007
+ ringIndex: number;
1008
+ sectorIndex: number;
1009
+ };
1010
+
1011
+ type TeamSlotAssignments = Record<string, OwnerSlotAssignment>;
1012
+ type TeamSlotAssignmentsByTeam = Record<string, TeamSlotAssignments>;
1013
+ ```
1014
+
1015
+ Где key верхнего уровня - `teamName`.
1016
+
1017
+ ### Почему верхний key именно `teamName` в `v1`
1018
+
1019
+ Текущий team data / IPC / graph integration path в проекте в основном уже team-scoped через `teamName`.
1020
+
1021
+ Поэтому для `v1` фиксируем:
1022
+
1023
+ - layout state scoped по `teamName`
1024
+ - team switch просто переключает текущий scoped layout state
1025
+ - assignments одной команды не должны протекать в другую
1026
+
1027
+ Если в будущем появится first-class stable `teamId`, это может стать отдельным улучшением, но не является обязательной частью этого refactor.
1028
+
1029
+ ### Что это значит для team rename
1030
+
1031
+ Так как в `v1` верхний key именно `teamName`, фиксируем явный tradeoff:
1032
+
1033
+ - если меняется именно storage identity команды, то layout state для неё считается новым scoped state
1034
+ - автоматическая миграция layout state между старым и новым `teamName` в этот refactor не входит
1035
+
1036
+ Важно не путать это с:
1037
+
1038
+ - rename участника при том же `agentId`
1039
+ - display-only label change, которая не меняет team storage key
1040
+
1041
+ ### Что именно хранится здесь
1042
+
1043
+ Только assignments для **draggable member sector slots**.
1044
+
1045
+ Здесь **не** храним:
1046
+
1047
+ - `lead central reserved block`
1048
+ - `launch HUD`
1049
+ - `lead activity`
1050
+ - `unassigned task slot`
1051
+
1052
+ Они должны строиться derivation-логикой, а не жить как псевдо-persisted assignments.
1053
+
1054
+ ### Что важно
1055
+
1056
+ - Не хранить здесь raw `x/y`
1057
+ - Не смешивать это с existing pinning model
1058
+ - Если в коде уже есть pinned positions, для owner layout их нужно:
1059
+ - либо мигрировать в ближайший slot один раз
1060
+ - либо игнорировать для lead/member и постепенно удалить owner-specific path
1061
+
1062
+ ## Snapshot lifecycle and precedence
1063
+
1064
+ Это отдельный обязательный contract, чтобы renderer не оказался между "старой" и "новой" геометрией.
1065
+
1066
+ ### Normal path
1067
+
1068
+ Если inputs валидны и planner собрал валидный snapshot:
1069
+
1070
+ 1. собираем новый `StableSlotLayoutSnapshot`
1071
+ 2. валидируем его
1072
+ 3. делаем его active render geometry
1073
+ 4. обновляем session-local last valid snapshot cache
1074
+
1075
+ ### Validation failure path
1076
+
1077
+ Если inputs есть, но candidate snapshot невалиден:
1078
+
1079
+ 1. candidate snapshot не коммитится
1080
+ 2. active render geometry не обновляется этим candidate snapshot
1081
+ 3. `slotAssignmentsByTeam` не перезаписывается частично
1082
+ 4. session-local last valid snapshot cache остаётся прежним
1083
+ 5. пишется diagnostic warning
1084
+
1085
+ ### No-lead path
1086
+
1087
+ Если текущий dataset не содержит `lead`:
1088
+
1089
+ 1. stable-slot planner path не строит новый snapshot
1090
+ 2. active render geometry для stable-slot path не обновляется
1091
+ 3. session-local last valid snapshot cache не очищается автоматически
1092
+ 4. renderer для этого pass использует fallback presentation path, а не stale stable-slot snapshot как активную картинку
1093
+
1094
+ ### Главное различие
1095
+
1096
+ - last valid snapshot cache нужен для safety и continuity логики
1097
+ - active render geometry должна соответствовать текущему валидному render path
1098
+
1099
+ Нельзя подменять второе первым.
1100
+
1101
+ ### Persistence scope для v1
1102
+
1103
+ Для первой реализации достаточно renderer-side persistence в team UI state.
1104
+
1105
+ В этом pass **не нужно**:
1106
+
1107
+ - писать slot assignments в team config
1108
+ - тащить их через main process
1109
+ - делать cross-session durable migration как обязательную часть layout refactor
1110
+
1111
+ Сначала нужен стабильный рабочий layout внутри текущего renderer state path.
1112
+
1113
+ ### Legacy pin migration policy
1114
+
1115
+ Чтобы не оставлять это на усмотрение implementer-а, фиксируем стартовую политику:
1116
+
1117
+ - для `lead/member` legacy raw pinned positions в новом режиме **не являются** источником истины
1118
+ - если такие данные существуют, на первом входе в `stable-slots-v1` их нужно один раз:
1119
+ - сматчить в ближайший валидный slot assignment
1120
+ - после этого дальше жить уже только через slot assignment
1121
+
1122
+ Не нужно бесконечно поддерживать два параллельных источника истины:
1123
+
1124
+ - raw owner pinning
1125
+ - slot assignment
1126
+
1127
+ ### Migration from fallback name to agentId
1128
+
1129
+ Это отдельный переходный случай, который нельзя оставлять "как-нибудь само".
1130
+
1131
+ Сценарий:
1132
+
1133
+ - раньше member жил на fallback key `member.name`
1134
+ - позже для него стал доступен `agentId`
1135
+
1136
+ Для `v1` фиксируем такое правило:
1137
+
1138
+ - если у текущего visible member появился `agentId`
1139
+ - и assignment под новым `stableOwnerId` ещё не существует
1140
+ - но есть старый assignment под его прежним fallback `member.name`
1141
+
1142
+ то этот assignment нужно **один раз** перенести на новый `stableOwnerId` до запуска planner-а.
1143
+
1144
+ Это нужно, чтобы член команды не "терял место" только потому, что identity стала более качественной.
1145
+
1146
+ ### Migration precedence - чтобы не было двойной трактовки
1147
+
1148
+ Порядок для `v1` фиксируем такой:
1149
+
1150
+ 1. если `slotLayoutVersion` не совпадает - старые member assignments сбрасываем целиком
1151
+ 2. если version совпадает и assignment уже есть под новым `stableOwnerId` - используем его как source of truth
1152
+ 3. если assignment под новым `stableOwnerId` ещё нет, но есть старый assignment под fallback `member.name` - переносим его один раз
1153
+ 4. если существуют и старый fallback assignment, и новый stable assignment одновременно - побеждает новый stable assignment, fallback alias удаляется
1154
+
1155
+ Это правило нужно, чтобы миграция не создавала две конкурирующие записи для одного и того же member-а.
1156
+
1157
+ ## Slot frame model
1158
+
1159
+ Для planner-а нужен явный rectangular model.
1160
+
1161
+ Нормативная внутренняя структура `v1`:
1162
+
1163
+ ```ts
1164
+ type SlotFrame = {
1165
+ ownerStableId: string;
1166
+ ringIndex: number;
1167
+ sectorIndex: number;
1168
+ x: number;
1169
+ y: number;
1170
+ width: number;
1171
+ height: number;
1172
+ };
1173
+ ```
1174
+
1175
+ `x/y` здесь - top-left slot frame в world coordinates.
1176
+
1177
+ ### Важное уточнение
1178
+
1179
+ `SlotFrame` в этом документе означает именно frame для **member sector slot**.
1180
+
1181
+ Для special geometry используем отдельные понятия:
1182
+
1183
+ - `lead central reserved block`
1184
+ - `UnassignedTaskSlotFrame`
1185
+
1186
+ Они могут быть AABB-похожими по форме, но не должны участвовать как обычные member slot assignments.
1187
+
1188
+ ### Почему прямоугольник, а не только radius
1189
+
1190
+ Потому что пользовательская проблема именно в прямоугольных зонах:
1191
+
1192
+ - activity cards
1193
+ - process rail
1194
+ - task columns
1195
+
1196
+ Значит planner должен работать не только с радиусом, а с **реальными AABB bounds**.
1197
+
1198
+ ## World-space geometry contract
1199
+
1200
+ Это один из самых критичных пунктов плана, потому что прошлые баги были именно из-за смешивания world-space и screen-space.
1201
+
1202
+ ### Обязательное правило
1203
+
1204
+ - planner, slot frames и local anchors живут в `world coordinates`
1205
+ - camera zoom/pan применяется только как визуальный transform поверх уже готовой world geometry
1206
+ - `GraphActivityHud` и похожие UI-слои не должны повторно "спасать" layout через screen-space repositioning
1207
+
1208
+ ### Что запрещено
1209
+
1210
+ - post-render screen-space packing соседних owners
1211
+ - DOM-based reflow logic, которая двигает slot zones независимо от planner-а
1212
+ - вычисление layout из текущего zoom level
1213
+
1214
+ Если какой-то элемент выглядит налезающим, исправлять нужно planner / footprint / slot bounds, а не screen-space коррекцией.
1215
+
1216
+ ## Owner anchor inside slot
1217
+
1218
+ Чтобы implementer не трактовал slot как угодно, фиксируем owner anchor rule.
1219
+
1220
+ ### Правило
1221
+
1222
+ - `(ringIndex, sectorIndex)` сначала определяют `ownerAnchor` на соответствующем sector ray
1223
+ - `slot frame` задаёт outer bounds всей owner-local зоны
1224
+ - `slot frame` строится вокруг `ownerAnchor`, а не выбирается произвольно постфактум
1225
+ - сам `member node` располагается по горизонтальному центру slot
1226
+ - по вертикали `member node` располагается в `owner band`, между `activity band` и `process band`
1227
+ - `activity band` всегда выше owner node
1228
+ - `task band` всегда ниже owner node
1229
+
1230
+ ### Каноническая формула для `SlotFrame`
1231
+
1232
+ Чтобы не было двух разных трактовок top-left координаты, фиксируем canonical build rule:
1233
+
1234
+ ```ts
1235
+ slotFrame.x = ownerAnchor.x - slotWidth / 2
1236
+ slotFrame.y = ownerAnchor.y - (activityBandHeight + ownerBandHeight / 2)
1237
+ slotFrame.width = slotWidth
1238
+ slotFrame.height = slotHeight
1239
+ ```
1240
+
1241
+ То есть:
1242
+
1243
+ - `ownerAnchor.x` всегда совпадает с horizontal centerline slot-а
1244
+ - `ownerAnchor.y` всегда совпадает с вертикальным центром `owner band`
1245
+ - верхняя граница slot-а определяется от activity band, а не "как получится"
1246
+
1247
+ Это делает geometry детерминированной и одинаковой для planner-а, hit testing и renderer-а.
1248
+
1249
+ ### Канонические локальные origins
1250
+
1251
+ Локальные точки считаем только от `slotFrame`, а не от DOM/layout side effects:
1252
+
1253
+ ```ts
1254
+ activityOrigin = {
1255
+ x: slotFrame.x,
1256
+ y: slotFrame.y,
1257
+ }
1258
+
1259
+ ownerBandOrigin = {
1260
+ x: slotFrame.x,
1261
+ y: slotFrame.y + activityBandHeight,
1262
+ }
1263
+
1264
+ processOrigin = {
1265
+ x: slotFrame.x,
1266
+ y: slotFrame.y + activityBandHeight + ownerBandHeight,
1267
+ }
1268
+
1269
+ taskOrigin = {
1270
+ x: slotFrame.x,
1271
+ y: slotFrame.y + activityBandHeight + ownerBandHeight + processBandHeight,
1272
+ }
1273
+ ```
1274
+
1275
+ Если какой-то renderer-pathу нужен другой anchor, он должен вычисляться как производный от этих канонических origins, а не как отдельная независимая правда.
1276
+
1277
+ ### Практически
1278
+
1279
+ Нужен один helper уровня domain/layout, который из `SlotFrame` возвращает локальные anchor points:
1280
+
1281
+ - `ownerAnchor`
1282
+ - `activityOrigin`
1283
+ - `processOrigin`
1284
+ - `taskOrigin`
1285
+
1286
+ UI и canvas не должны заново высчитывать эти точки каждый по-своему.
1287
+
1288
+ ## Owner footprint contract
1289
+
1290
+ `OwnerFootprint` должен считаться детерминированно из layout rules и данных, а не из уже отрендеренного DOM.
1291
+
1292
+ ### В `OwnerFootprint` входят
1293
+
1294
+ - итоговый `slotWidth`
1295
+ - итоговый `slotHeight`
1296
+ - bucket `S / M / L`
1297
+ - optional flags, влияющие на layout validity:
1298
+ - есть ли `activity items`
1299
+ - есть ли релевантный `process`
1300
+
1301
+ ### Важно
1302
+
1303
+ - `OwnerFootprint` считается до рендера
1304
+ - он должен быть pure и testable
1305
+ - разные React paths не должны считать footprint по-разному
1306
+
1307
+ ## Slot orientation rule
1308
+
1309
+ Чтобы не возникло двух разных реализаций "stable sectors", фиксируем это явно:
1310
+
1311
+ - slot contents **не ротируются** по углу сектора
1312
+ - text и UI внутри slot всегда остаются upright
1313
+ - `Activity / Member / Process / Tasks` всегда идут в одном и том же вертикальном порядке сверху вниз
1314
+
1315
+ То есть sector влияет на положение slot вокруг lead, но не на внутреннюю ориентацию карточек и текста.
1316
+
1317
+ ## Horizontal alignment inside slot
1318
+
1319
+ Чтобы band-ы не выравнивались хаотично по-разному, фиксируем правило:
1320
+
1321
+ - у slot есть общий горизонтальный centerline
1322
+ - `member node` центрируется по этой линии
1323
+ - `activity band` центрируется по этой линии
1324
+ - `process rail` центрируется по этой линии
1325
+ - `task band` центрируется по этой линии
1326
+
1327
+ Если у band своя фактическая ширина меньше `slotWidth`, он не липнет к левому краю slot, а центрируется внутри slot frame.
1328
+
1329
+ ## Slot width rules
1330
+
1331
+ `slotWidth` считается честно, от контента.
1332
+
1333
+ ### Формула
1334
+
1335
+ ```ts
1336
+ slotWidth = max(
1337
+ activityWidth,
1338
+ ownerMinWidth,
1339
+ processRailWidth,
1340
+ kanbanWidth
1341
+ )
1342
+ ```
1343
+
1344
+ ### Где
1345
+
1346
+ - `activityWidth` - ширина compact activity lane
1347
+ - `ownerMinWidth` - минимальная ширина под owner node + label area
1348
+ - `processRailWidth` - ширина attached process rail
1349
+ - `kanbanWidth` - суммарная ширина всех активных columns с gutter-ами
1350
+
1351
+ ### Важно
1352
+
1353
+ - bucket не определяет итоговую ширину
1354
+ - bucket только классифицирует уже посчитанную ширину
1355
+
1356
+ ### Что влияет на slot width
1357
+
1358
+ - число `active non-empty` kanban columns
1359
+ - фиксированная ширина compact activity lane
1360
+ - фиксированная ширина process rail
1361
+ - минимальная owner label area
1362
+
1363
+ ### Что не должно влиять на slot width
1364
+
1365
+ - случайная длина message preview текста
1366
+ - случайная длина task subject, если pill уже имеет фиксированную ширину и truncation
1367
+ - unread badge count
1368
+ - hover / selection state
1369
+
1370
+ Иными словами: slot width должен расти из-за реальной структурной ширины owner-local зон, а не из-за случайного текстового контента.
1371
+
1372
+ ## Slot height rules
1373
+
1374
+ `slotHeight` bounded и предсказуем.
1375
+
1376
+ ### Формула
1377
+
1378
+ ```ts
1379
+ slotHeight =
1380
+ activityBandHeight +
1381
+ ownerBandHeight +
1382
+ processBandHeight +
1383
+ taskBandHeight +
1384
+ verticalGaps
1385
+ ```
1386
+
1387
+ ### Важно
1388
+
1389
+ - activity band height фиксирован
1390
+ - process band height фиксирован
1391
+ - task band height фиксирован (`5 rows`)
1392
+ - значит рост задач меняет в первую очередь `slotWidth`, а не `slotHeight`
1393
+
1394
+ Это ключевой выбор ради стабильности.
1395
+
1396
+ ## Width buckets - как трактовать правильно
1397
+
1398
+ `S / M / L` нужны planner-у как packing heuristic, но не как UI limit.
1399
+
1400
+ ### Правильное правило
1401
+
1402
+ 1. Сначала считаем **реальный** `slotWidth`
1403
+ 2. Потом присваиваем bucket
1404
+ 3. Потом planner использует bucket и точный width
1405
+
1406
+ ### Что не делаем
1407
+
1408
+ - не считаем bucket по display name
1409
+ - не считаем bucket только по числу задач
1410
+ - не используем bucket как замену реальной ширины
1411
+
1412
+ ### Стартовая bucket policy для v1
1413
+
1414
+ Чтобы первая реализация не разошлась в трактовках, фиксируем стартовое правило:
1415
+
1416
+ - `S` - `1` active non-empty kanban column
1417
+ - `M` - `2-3` active non-empty kanban columns
1418
+ - `L` - `4+` active non-empty kanban columns
1419
+
1420
+ Если `activityWidth` или `processRailWidth` делает slot шире ожидаемого bucket-а, planner всё равно должен опираться на **реальный `slotWidth`**, а bucket использовать только как coarse hint.
1421
+
1422
+ ## Геометрические defaults для v1
1423
+
1424
+ Чтобы implementer не подбирал layout "на глаз" каждый по-своему, фиксируем стартовые значения:
1425
+
1426
+ - `slotVerticalGap = 24`
1427
+ - `slotHorizontalGap = 32`
1428
+ - `ringGap = 140`
1429
+ - `centralSafetyPadding = 48`
1430
+ - `memberSlotInnerPadding = 16`
1431
+
1432
+ Это именно стартовые defaults. Их можно тюнить, но они должны жить в одном месте и изменяться осознанно, а не расползаться по коду.
1433
+
1434
+ ### Source of geometry constants
1435
+
1436
+ Все такие значения должны жить в одном domain-level constants module для stable-slot layout.
1437
+
1438
+ Нельзя:
1439
+
1440
+ - дублировать их отдельно в planner
1441
+ - отдельно в renderer
1442
+ - отдельно в hit testing
1443
+ - отдельно в fit helpers
1444
+
1445
+ Иначе визуально одинаковый slot начнёт иметь разные размеры в разных частях системы.
1446
+
1447
+ ## Ring radius rule
1448
+
1449
+ Радиус следующего ring нельзя считать "по ощущению".
1450
+
1451
+ Для `v1` фиксируем стартовое правило:
1452
+
1453
+ ```ts
1454
+ nextRingRadius =
1455
+ previousRingRadius +
1456
+ maxSlotDepthOnPreviousRing +
1457
+ ringGap
1458
+ ```
1459
+
1460
+ Где `maxSlotDepthOnPreviousRing` - максимальный размер slot по радиальному направлению среди owners этого ring.
1461
+
1462
+ ## Ring planner - строгая логика
1463
+
1464
+ Planner должен быть детерминированным и минимально разрушительным.
1465
+
1466
+ ### Что именно planner планирует
1467
+
1468
+ Этот planner планирует только **member sector slots**.
1469
+
1470
+ Он не должен пытаться раскладывать:
1471
+
1472
+ - `lead central reserved block`
1473
+ - `launch HUD`
1474
+ - `lead activity`
1475
+ - `unassigned task slot`
1476
+
1477
+ Эти части должны быть построены заранее как fixed/special geometry, влияющая на exclusion и bounds.
1478
+
1479
+ ### Inputs
1480
+
1481
+ - central exclusion bounds
1482
+ - ordered visible member owners
1483
+ - saved slot assignments
1484
+ - slot footprints
1485
+ - ring / sector constants
1486
+
1487
+ ### Output
1488
+
1489
+ - `SlotFrame[]` для member sector slots
1490
+
1491
+ ### Базовый алгоритм
1492
+
1493
+ ```ts
1494
+ for owner in orderedOwners:
1495
+ if savedAssignment(owner) still valid:
1496
+ keep it
1497
+ reserve frame
1498
+ continue
1499
+
1500
+ place owner into first valid candidate:
1501
+ by preferred ring
1502
+ then by preferred sector
1503
+ then by next available ring/sector
1504
+ where candidate frame:
1505
+ does not intersect central exclusion
1506
+ does not intersect occupied slot frames
1507
+ respects min gap
1508
+ ```
1509
+
1510
+ ### Правило минимального разрушения
1511
+
1512
+ Если owner уже был привязан к сектору, planner должен сначала попытаться:
1513
+
1514
+ 1. оставить тот же `sectorIndex`
1515
+ 2. если не помещается - оставить тот же сектор, но увести owner на внешний ring
1516
+ 3. только потом искать новый сектор
1517
+
1518
+ Это даёт более стабильное поведение, чем немедленный полный reshuffle по всем секторам.
1519
+
1520
+ ### Порядок выбора candidate slots
1521
+
1522
+ Чтобы planner не получился "похожим, но разным" в двух местах кода, фиксируем порядок выбора явно.
1523
+
1524
+ #### Для owner с уже существующим assignment
1525
+
1526
+ 1. тот же `(ringIndex, sectorIndex)`, если он всё ещё valid
1527
+ 2. тот же `sectorIndex` на следующем внешнем ring
1528
+ 3. ближайшие соседние sectors на том же ring
1529
+ 4. ближайшие соседние sectors на внешних rings
1530
+
1531
+ #### Для нового owner без assignment
1532
+
1533
+ 1. ring 1, sectors в фиксированном canonical order
1534
+ 2. если ничего не влезло - ring 2, тот же canonical order
1535
+ 3. и так далее
1536
+
1537
+ #### Canonical sector order для planner-а
1538
+
1539
+ Используем тот же порядок, что уже зафиксирован выше:
1540
+
1541
+ 1. `top`
1542
+ 2. `upper-right`
1543
+ 3. `lower-right`
1544
+ 4. `bottom`
1545
+ 5. `lower-left`
1546
+ 6. `upper-left`
1547
+
1548
+ Это правило важно, чтобы initial placement, replanning и drag-target selection не расходились между собой.
1549
+
1550
+ ### Что значит nearest valid slot при drag
1551
+
1552
+ Чтобы drag/snap не реализовали двумя разными способами, фиксируем:
1553
+
1554
+ - nearest candidate считается по расстоянию от текущего dragged `ownerAnchor` до candidate `ownerAnchor`
1555
+ - metric - обычное Euclidean distance в world coordinates
1556
+ - если расстояние одинаковое, tie-break идёт по canonical sector order
1557
+
1558
+ Это даёт детерминированное поведение и не зависит от текущего zoom/pan.
1559
+
1560
+ ### Что значит "still valid"
1561
+
1562
+ Saved assignment считается valid, если:
1563
+
1564
+ - slot frame для него можно построить
1565
+ - frame не пересекает central exclusion
1566
+ - frame не пересекает уже занятые slot frames
1567
+ - current owner footprint всё ещё помещается
1568
+
1569
+ Если assignment invalid, owner перепланируется, но **не весь layout с нуля**, а только конфликтующие части.
1570
+
1571
+ ## Reflow policy - когда planner имеет право перестраивать схему
1572
+
1573
+ Чтобы не было хаотических reshuffle, вводим чёткое правило.
1574
+
1575
+ ### Layout нельзя перестраивать полностью из-за
1576
+
1577
+ - новых activity items
1578
+ - комментариев
1579
+ - изменения unread badges
1580
+ - process content update без роста footprint
1581
+ - zoom / pan
1582
+ - rename при том же `agentId`
1583
+
1584
+ ### Layout может перестраивать часть owners из-за
1585
+
1586
+ - появления нового owner
1587
+ - удаления owner
1588
+ - drag/swap
1589
+ - роста slot footprint, который делает current assignment invalid
1590
+ - появления `unassigned task slot`, если он расширил lower central exclusion и создал реальный конфликт
1591
+ - исчезновения `unassigned task slot`, только если пользователь явно сделал `reset layout`
1592
+
1593
+ ### Предпочтительный принцип
1594
+
1595
+ `keep existing placements whenever still valid`
1596
+
1597
+ Это обязательный инвариант для стабильности.
1598
+
1599
+ ## Post-plan validation and fail-closed behavior
1600
+
1601
+ Даже хороший planner иногда можно сломать интеграцией. Поэтому после каждого planner run нужна отдельная deterministic validation pass.
1602
+
1603
+ ### Нужен отдельный pure helper
1604
+
1605
+ Рекомендуемая форма:
1606
+
1607
+ ```ts
1608
+ validateStableSlotLayout({
1609
+ slotFrames,
1610
+ runtimeCentralExclusion,
1611
+ ownerFootprints,
1612
+ assignments,
1613
+ })
1614
+ ```
1615
+
1616
+ ### Validator обязан проверять
1617
+
1618
+ - все `SlotFrame` конечные и не содержат `NaN/Infinity`
1619
+ - нет двух owners с одним и тем же assignment
1620
+ - ни один `member sector slot` не пересекает `runtimeCentralExclusion`
1621
+ - `member sector slots` не пересекаются между собой
1622
+ - полный frame slot-а включает `Activity`, `Owner`, `Process` и `Task` bands
1623
+ - локальные anchors лежат внутри своего `SlotFrame`
1624
+ - итоговые fit bounds конечные и ненулевые
1625
+
1626
+ ### Fail-closed правило
1627
+
1628
+ Если validation не прошла:
1629
+
1630
+ - не рендерим "полусломанный" новый layout как будто он валиден
1631
+ - оставляем предыдущий последний валидный layout snapshot для этой команды, если он есть
1632
+ - если валидного snapshot нет, используем безопасный fallback без persistent overwrite
1633
+ - пишем diagnostic warning в лог, чтобы баг можно было воспроизвести
1634
+
1635
+ ### Что нельзя делать при validation failure
1636
+
1637
+ - silently чинить layout screen-space коррекцией
1638
+ - частично коммитить невалидные assignments в store
1639
+ - двигать только Activity/Process/Tasks отдельно от slot frame, пытаясь "спасти картинку"
1640
+
1641
+ Это критично: broken layout должен ломаться явно и безопасно, а не превращаться в новый источник хаоса.
1642
+
1643
+ ## Conflict resolution order - when one slot stops fitting
1644
+
1645
+ Это один из самых важных практических пунктов. Именно здесь чаще всего implementer случайно делает hidden global reshuffle.
1646
+
1647
+ ### Если один owner стал невалиден из-за роста footprint
1648
+
1649
+ Planner должен идти по такому порядку:
1650
+
1651
+ 1. сохранить все unaffected owners на месте
1652
+ 2. попробовать оставить problem owner в том же `sectorIndex`, но увести на следующий outer ring
1653
+ 3. если этого недостаточно, попробовать минимальный локальный spill конфликтующего подмножества owners
1654
+ 4. не делать полный global reshuffle, если пользователь явно не вызвал `reset layout`
1655
+
1656
+ ### Что считается preferred behavior
1657
+
1658
+ - cheapest valid local fix wins
1659
+ - количество затронутых owners должно быть минимальным
1660
+ - owner, который инициировал конфликт ростом footprint, должен двигаться первым, если это решает проблему
1661
+
1662
+ Это правило важно, чтобы граф оставался предсказуемым и не "переезжал весь" из-за одной widened kanban zone.
1663
+
1664
+ ## Canonical layout build pipeline
1665
+
1666
+ Чтобы новый path не был реализован в разных местах по-разному, фиксируем канонический порядок сборки layout.
1667
+
1668
+ ### Шаги
1669
+
1670
+ 1. Построить visible graph dataset
1671
+ 2. Разрешить `stableOwnerId` для members
1672
+ 3. Построить `lead central reserved block`
1673
+ 4. Если есть unassigned tasks - построить `unassigned task slot`
1674
+ 5. Из пунктов `3-4` собрать итоговый `central exclusion`
1675
+ 6. Построить ordered visible member owners
1676
+ 7. Для каждого member owner посчитать `OwnerFootprint`
1677
+ 8. Запустить member slot planner
1678
+ 9. Получить `SlotFrame[]` для member slots
1679
+ 10. Из `SlotFrame` построить local anchors:
1680
+ - `ownerAnchor`
1681
+ - `activityOrigin`
1682
+ - `processOrigin`
1683
+ - `taskOrigin`
1684
+ 11. Собрать цельный `StableSlotLayoutSnapshot`
1685
+ 12. Прогнать validation на полном snapshot
1686
+ 13. Только после этого передать world-space geometry в renderer / graph package
1687
+ 14. Только после этого применять camera zoom/pan
1688
+
1689
+ ### Что нельзя делать
1690
+
1691
+ - сначала отрендерить Activity/Tasks, а потом ими "уточнить" layout
1692
+ - сначала запустить screen-space pack, а потом пытаться сохранить это как source of truth
1693
+ - считать planner output неполным и дозаполнять его DOM-side reposition логикой
1694
+
1695
+ ## Atomic layout transaction rule
1696
+
1697
+ Это важный anti-bug contract. Layout update должен коммититься как одна транзакция, а не серией мелких частичных записей.
1698
+
1699
+ ### Правило
1700
+
1701
+ Один graph update делает только такой путь:
1702
+
1703
+ 1. derive inputs
1704
+ 2. build full snapshot
1705
+ 3. validate snapshot
1706
+ 4. commit whole snapshot
1707
+ 5. render from committed snapshot
1708
+
1709
+ ### Что запрещено
1710
+
1711
+ - сначала записать новые assignments, а `SlotFrame` пересчитать позже
1712
+ - сначала обновить `memberSlotFrames`, а `fitBounds` и `runtimeCentralExclusion` дотянуть в следующем tick
1713
+ - отдельно коммитить `Activity` geometry и отдельно `Task` geometry
1714
+ - держать в renderer одновременно старые `fitBounds` и новые `SlotFrame`
1715
+
1716
+ ### Почему это обязательно
1717
+
1718
+ Большая часть "странных" overlap и jump-багов рождается не из формулы planner-а, а из того, что разные части UI в течение одного render-cycle смотрят на разные поколения layout state.
1719
+
1720
+ Новый path должен быть transaction-like: либо весь snapshot валиден и коммитнут, либо остаётся предыдущий валидный snapshot.
1721
+
1722
+ ## Debug / observability requirements
1723
+
1724
+ Этот refactor слишком геометрический, чтобы оставлять отладку на `console.log` по месту.
1725
+
1726
+ ### Минимум, который нужен в `v1`
1727
+
1728
+ - dev-only возможность вывести для owner:
1729
+ - `stableOwnerId`
1730
+ - `ringIndex`
1731
+ - `sectorIndex`
1732
+ - `slotWidth`
1733
+ - `slotHeight`
1734
+ - bucket `S/M/L`
1735
+ - dev-only warning при validation failure с причиной
1736
+ - dev-only возможность понять, какой owner был локально перепланирован и почему
1737
+
1738
+ ### Чего не нужно делать
1739
+
1740
+ - тащить это в публичный product UI
1741
+ - делать отдельный user-facing debug mode
1742
+
1743
+ Это purely implementation aid, но он сильно снижает риск, что спорные overlap-cases будут разбираться "на глаз".
1744
+
1745
+ ## View modes - tab and fullscreen
1746
+
1747
+ Новый layout не должен иметь две разные правды для разных способов открытия графа.
1748
+
1749
+ ### Правило
1750
+
1751
+ - graph tab и fullscreen overlay используют один и тот же `slotAssignmentsByTeam`
1752
+ - graph tab и fullscreen overlay используют один и тот же `slotLayoutVersion`
1753
+ - открытие fullscreen не должно заново seed-ить owner placement
1754
+ - drag в одном режиме должен сразу отражаться в другом режиме
1755
+ - camera state при этом может быть разным и не обязан шариться между режимами
1756
+
1757
+ Иными словами: разные view modes - это разные камеры и контейнеры, но не разные layout models.
1758
+
1759
+ ## Team switch behavior
1760
+
1761
+ Layout state должен быть жёстко team-scoped.
1762
+
1763
+ ### Правило
1764
+
1765
+ - переключение на другую команду читает только её `slotAssignmentsByTeam[teamName]`
1766
+ - возврат назад использует ранее сохранённый scoped layout этой команды
1767
+ - assignments и camera state одной команды не должны случайно применяться к другой
1768
+ - если одна и та же команда открыта в нескольких pane-ах, layout state у неё общий
1769
+ - при конкурентном обновлении layout state для одной команды действует обычное shared-state правило `last write wins`
1770
+
1771
+ Это особенно важно для случаев, когда несколько graph tabs / panes открыты параллельно.
1772
+
1773
+ ## Hidden member -> reappear behavior
1774
+
1775
+ Это полезно зафиксировать отдельно, чтобы реализация не делала лишний churn layout-а.
1776
+
1777
+ ### Правило
1778
+
1779
+ - если member временно исчез из `visible owner set`, его slot не участвует в текущем planner run
1780
+ - при этом его сохранённый member assignment может оставаться в `slotAssignmentsByTeam`
1781
+ - если тот же member потом возвращается с тем же `stableOwnerId`, planner сначала пытается переиспользовать прежний assignment
1782
+ - если прежний assignment больше не валиден, только тогда делается локальный replanning
1783
+
1784
+ Это даёт более стабильное поведение, чем каждый раз забывать slot при любом временном исчезновении owner-а.
1785
+
1786
+ ## Graph filters - влияние на layout
1787
+
1788
+ Это место обязательно нужно зафиксировать, иначе после рефактора легко вернуть layout jumps через UI toggles.
1789
+
1790
+ ### Для `v1` правило такое
1791
+
1792
+ - `showTasks`
1793
+ - `showProcesses`
1794
+ - `showEdges`
1795
+
1796
+ не являются входом для planner-а и не должны менять slot assignments.
1797
+
1798
+ ### Что они делают
1799
+
1800
+ - `showTasks = false` скрывает task rendering, но не перестраивает member slots и не уничтожает reserved task band geometry
1801
+ - `showProcesses = false` скрывает process rail rendering, но не перестраивает member slots и не убирает reserved process band geometry
1802
+ - `showEdges = false` влияет только на edges
1803
+
1804
+ ### Отдельно про `unassigned task slot`
1805
+
1806
+ Так как это special slot, состоящий только из task band-а, фиксируем отдельно:
1807
+
1808
+ - если в dataset есть unassigned tasks, сам `unassigned task slot` продолжает учитываться в topology
1809
+ - `showTasks = false` может скрыть его presentation, но не должен убирать его reserved topology footprint
1810
+ - исчезновение `unassigned task slot` как layout actor происходит только когда unassigned tasks реально исчезли из dataset, а не из-за UI filter toggle
1811
+
1812
+ Это осознанный tradeoff ради стабильности. Filters в `v1` скрывают presentation, а не перестраивают топологию layout.
1813
+
1814
+ ## No-lead fallback
1815
+
1816
+ Stable sector planner в этом плане опирается на наличие `lead`.
1817
+
1818
+ ### Для `v1` фиксируем точное поведение
1819
+
1820
+ - если в текущем visible dataset временно нет `lead`
1821
+ - новый stable-slot planner не должен пытаться строить сектора "вокруг пустоты"
1822
+ - новый `StableSlotLayoutSnapshot` в таком состоянии не строится и не коммитится
1823
+ - persistent assignments не перезаписываются
1824
+ - если для этой команды уже есть последний валидный stable-slot snapshot текущей сессии, он остаётся последним валидным snapshot и не заменяется случайной геометрией
1825
+ - если валидного snapshot ещё не было, stable-slot presentation path для этого render-pass не активируется до возвращения lead
1826
+
1827
+ ### Чего нельзя делать
1828
+
1829
+ - сохранять случайные placements как будто это валидный stable layout
1830
+ - создавать fake lead только ради того, чтобы planner "отработал"
1831
+
1832
+ Это transient safety rule, чтобы неполные данные не портили persistent layout state.
1833
+
1834
+ ## Drag and snap semantics
1835
+
1836
+ Drag нужен, но он не должен возвращать нас к свободной физике.
1837
+
1838
+ ### Правила
1839
+
1840
+ - Drag доступен только для `member slots`
1841
+ - `lead` не draggable
1842
+ - Во время drag можно подсвечивать ближайший candidate slot
1843
+ - При drop owner снапается в nearest valid slot
1844
+ - Если target занят:
1845
+ - делаем `swap`
1846
+ - не делаем overlap
1847
+ - не делаем silent fallback в произвольную соседнюю точку
1848
+
1849
+ ### Что сохраняем после drop
1850
+
1851
+ Только:
1852
+
1853
+ - `ringIndex`
1854
+ - `sectorIndex`
1855
+
1856
+ Не сохраняем:
1857
+
1858
+ - абсолютные координаты
1859
+
1860
+ ### Важное уточнение про swap
1861
+
1862
+ Swap допустим только если обе итоговые позиции валидны после обмена.
1863
+
1864
+ Если ближайший занятый slot приводит к невалидной паре placement-ов, нужно брать следующий nearest valid candidate, а не насильно выполнять swap.
1865
+
1866
+ ### Поведение при drop вне валидного target
1867
+
1868
+ Если пользователь отпускает owner там, где нет валидного slot candidate:
1869
+
1870
+ - owner возвращается в свой предыдущий assignment
1871
+ - промежуточная невалидная world position не сохраняется
1872
+
1873
+ Это обязательно, чтобы drag не оставлял граф в полусломанном состоянии.
1874
+
1875
+ ## Почему swap выбран по умолчанию
1876
+
1877
+ Потому что это самое понятное поведение для пользователя:
1878
+
1879
+ - место уже занято
1880
+ - я кладу owner туда
1881
+ - значит владельцы меняются местами
1882
+
1883
+ Любая "умная" скрытая перестановка менее предсказуема.
1884
+
1885
+ ## Activity connector - как трактовать
1886
+
1887
+ Connector нужен, но он должен быть локальным и cheap.
1888
+
1889
+ ### Правило
1890
+
1891
+ - connector рисуется между activity band и owner band внутри одного slot
1892
+ - connector не участвует в глобальном packing
1893
+ - connector не должен тянуться через пол-графа
1894
+
1895
+ Это просто визуальная связь, а не самостоятельный layout actor.
1896
+
1897
+ ## Process rendering - важное уточнение
1898
+
1899
+ Для `v1` безопаснее **не выкидывать process nodes из графовой модели полностью**.
1900
+
1901
+ Лучший путь:
1902
+
1903
+ - оставить process data в graph domain
1904
+ - но перестать раскладывать process nodes как независимые свободные entities
1905
+ - вместо этого позиционировать process presentation внутри owner slot
1906
+
1907
+ То есть меняем **геометрию и ownership**, а не обязательно весь data contract в один проход.
1908
+
1909
+ ## Activity rendering - важное уточнение
1910
+
1911
+ `GraphActivityHud` в новой модели не должен сам быть planner-ом.
1912
+
1913
+ Его роль после переделки:
1914
+
1915
+ - взять уже посчитанные slot-local coordinates
1916
+ - отрендерить compact activity UI
1917
+ - отрендерить локальный connector
1918
+
1919
+ Чего он делать больше не должен:
1920
+
1921
+ - сам pack-ить owner lanes друг относительно друга
1922
+ - сам спасать cross-owner overlap
1923
+ - сам решать world geometry
1924
+
1925
+ ## Zoom / pan / fit - обязательные инварианты
1926
+
1927
+ ### Zoom и pan
1928
+
1929
+ Zoom и pan должны менять только camera transform.
1930
+
1931
+ Они не имеют права:
1932
+
1933
+ - перераскладывать slots
1934
+ - менять relative positions внутри slot
1935
+ - менять размер slot в screen-space независимо от world model
1936
+
1937
+ ### Fit
1938
+
1939
+ `zoomToFit` и initial fit обязаны учитывать:
1940
+
1941
+ - member slot frames
1942
+ - lead central exclusion
1943
+ - launch HUD
1944
+ - unassigned task slot
1945
+ - activity bands
1946
+ - task bands
1947
+ - process rails
1948
+
1949
+ Не только центры owner nodes.
1950
+
1951
+ ### Важное уточнение про filters
1952
+
1953
+ Даже если `showTasks = false` или `showProcesses = false`, fit в `v1` должен учитывать **reserved topology bounds**, а не только текущие видимые DOM/canvas элементы.
1954
+
1955
+ Иначе переключение filters снова будет вызывать визуальный jump layout-а и нарушит главный инвариант стабильности.
1956
+
1957
+ ## Member add/remove behavior
1958
+
1959
+ ### Add
1960
+
1961
+ - existing valid assignments сохраняются
1962
+ - новый owner получает первый валидный slot по planner rules
1963
+ - если ring 1 full по footprint, новый owner идёт в ring 2
1964
+
1965
+ ### Remove
1966
+
1967
+ - slot освобождается
1968
+ - остальные owners не auto-compact-ятся только из-за самого факта remove/hide
1969
+
1970
+ Фиксируем правило:
1971
+
1972
+ - без explicit reset-layout не делаем агрессивный global compaction
1973
+
1974
+ Это уменьшает визуальные скачки.
1975
+
1976
+ ### Reset layout behavior
1977
+
1978
+ Нужен явный reset path:
1979
+
1980
+ - очистить `slotAssignmentsByTeam[teamName]`
1981
+ - заново построить placements по planner rules
1982
+
1983
+ Это нужно и для пользователя, и для отладки, и для быстрого выхода из редких неудачных layout-состояний.
1984
+
1985
+ ## Rename behavior
1986
+
1987
+ Если:
1988
+
1989
+ - `agentId` тот же
1990
+ - изменился только `member.name`
1991
+
1992
+ то layout обязан сохранить:
1993
+
1994
+ - slot assignment
1995
+ - drag placement
1996
+ - activity ownership
1997
+ - process ownership
1998
+ - task ownership references
1999
+
2000
+ Если `agentId` нет и используется fallback на имя, это known-weaker mode, и это нужно явно покрыть тестом.
2001
+
2002
+ ## Поведение при росте задач
2003
+
2004
+ Это отдельный важный edge case, который уже обсуждался.
2005
+
2006
+ ### Сценарий
2007
+
2008
+ У owner сначала мало задач, потом их становится много.
2009
+
2010
+ ### Что не делаем
2011
+
2012
+ - не включаем scroll внутри slot
2013
+ - не скрываем часть columns
2014
+ - не даём tasks раздавить activity соседнего owner
2015
+
2016
+ ### Что делаем
2017
+
2018
+ - task band остаётся высотой `5 rows`
2019
+ - все active non-empty columns продолжают показываться
2020
+ - slot width растёт
2021
+ - если текущий ring больше не вмещает выросший slot, owner может уйти на более дальний ring
2022
+
2023
+ ### Ключевой инвариант
2024
+
2025
+ Даже если owner уходит во внешний ring, это должно быть:
2026
+
2027
+ - детерминированно
2028
+ - минимально разрушительно
2029
+ - без общего хаотичного reshuffle
2030
+
2031
+ ## Public / shared contract changes
2032
+
2033
+ Ниже то, что нужно явно зафиксировать, чтобы не появлялись "невидимые" зависимости.
2034
+
2035
+ ## Shared types
2036
+
2037
+ Обязательно:
2038
+
2039
+ - `ResolvedTeamMember.agentId?: string`
2040
+
2041
+ ## Graph node identity
2042
+
2043
+ Обязательно:
2044
+
2045
+ - member node ids переходят на `stableOwnerId`
2046
+
2047
+ Нормативный шаблон `v1`:
2048
+
2049
+ ```ts
2050
+ member:${teamName}:${stableOwnerId}
2051
+ ```
2052
+
2053
+ ### Что сознательно не меняем в этом refactor
2054
+
2055
+ Существующие UI/event ports могут по-прежнему использовать:
2056
+
2057
+ - `teamName`
2058
+ - `memberName`
2059
+ - `taskId`
2060
+
2061
+ если это нужно для открытия профиля, task detail или других UI-paths.
2062
+
2063
+ То есть stable ids обязательны для layout identity и planner storage, но не требуют в этом pass перепридумывать весь пользовательский navigation/event contract.
2064
+
2065
+ ## Renderer-side layout state
2066
+
2067
+ Нужно добавить owner slot assignment storage по team.
2068
+
2069
+ Дополнительно нужен технический marker текущего layout path, например:
2070
+
2071
+ ```ts
2072
+ slotLayoutVersion = 'stable-slots-v1'
2073
+ ```
2074
+
2075
+ Он поможет безопасно отличать новый planner path от старого во время миграции и cleanup.
2076
+
2077
+ ### Versioning contract
2078
+
2079
+ - `slotLayoutVersion` хранится рядом с member slot assignments
2080
+ - если сохранённая версия не совпадает с текущей, старые member assignments нужно сбросить и пересчитать по planner defaults
2081
+ - не нужно пытаться поддерживать неявную backwards-совместимость между разными planner semantics
2082
+
2083
+ Лучше один явный reset assignments path, чем тихое использование устаревшей geometry-модели.
2084
+
2085
+ ## Internal planner types
2086
+
2087
+ Нужны internal-only helper types:
2088
+
2089
+ - `SlotFrame`
2090
+ - `UnassignedTaskSlotFrame`
2091
+ - `OwnerSlotAssignment`
2092
+ - `OwnerFootprint`
2093
+ - `RingPlanCandidate`
2094
+ - `StableSlotLayoutSnapshot`
2095
+
2096
+ Эти типы лучше держать в feature domain / graph package internals, а не тянуть в публичный API без необходимости.
2097
+
2098
+ ### Нормативный `StableSlotLayoutSnapshot` для `v1`
2099
+
2100
+ Чтобы renderer и simulation не собирали layout из полусырых кусков, фиксируем aggregated result type:
2101
+
2102
+ ```ts
2103
+ type StableSlotLayoutSnapshot = {
2104
+ teamName: string;
2105
+ slotLayoutVersion: string;
2106
+ memberSlotFrames: SlotFrame[];
2107
+ leadCentralReservedBlock: Rect;
2108
+ unassignedTaskSlot?: UnassignedTaskSlotFrame;
2109
+ runtimeCentralExclusion: Rect;
2110
+ fitBounds: Rect;
2111
+ };
2112
+ ```
2113
+
2114
+ ### Зачем нужен snapshot
2115
+
2116
+ - один planner run должен выдавать один цельный результат
2117
+ - renderer не должен сам дособирать geometry из отдельных store-полей
2118
+ - validation должна проверять именно полный snapshot, а не куски по отдельности
2119
+
2120
+ Это уменьшает риск, что `Activity`, `Tasks`, `Process`, fit bounds и exclusion будут жить в слегка разных версиях одного и того же layout pass.
2121
+
2122
+ ## Слои и ответственности
2123
+
2124
+ ## Shared / main
2125
+
2126
+ Точки внимания:
2127
+
2128
+ - `src/shared/types/team.ts`
2129
+ - `src/main/services/team/TeamMemberResolver.ts`
2130
+
2131
+ Ответственность:
2132
+
2133
+ - протащить `agentId` в `ResolvedTeamMember`
2134
+ - сделать stable identity доступной renderer-слою
2135
+
2136
+ ## Feature domain
2137
+
2138
+ Точки внимания:
2139
+
2140
+ - `src/features/agent-graph/core/domain/`
2141
+
2142
+ Ответственность:
2143
+
2144
+ - `stableOwnerId`
2145
+ - `slotWidth/slotHeight`
2146
+ - width buckets
2147
+ - ring planner
2148
+ - slot validity checks
2149
+ - drag/snap helpers
2150
+
2151
+ ## Adapter layer
2152
+
2153
+ Точки внимания:
2154
+
2155
+ - `src/features/agent-graph/renderer/adapters/TeamGraphAdapter.ts`
2156
+
2157
+ Ответственность:
2158
+
2159
+ - строить member node ids на stable owner id
2160
+ - привязать tasks / processes / activity к stable owner identity
2161
+ - не заниматься спасением geometry post-facto
2162
+
2163
+ ## Graph package layout
2164
+
2165
+ Точки внимания:
2166
+
2167
+ - `packages/agent-graph/src/hooks/useGraphSimulation.ts`
2168
+ - `packages/agent-graph/src/layout/kanbanLayout.ts`
2169
+ - `packages/agent-graph/src/layout/activityLane.ts`
2170
+
2171
+ Ответственность:
2172
+
2173
+ - owner placement идёт от slot planner, а не от free-force
2174
+ - task layout получает `slot frame`
2175
+ - activity получает `slot-local origin`
2176
+ - process rail позиционируется owner-local
2177
+
2178
+ ## Renderer UI
2179
+
2180
+ Точки внимания:
2181
+
2182
+ - `src/features/agent-graph/renderer/ui/GraphActivityHud.tsx`
2183
+ - `src/features/agent-graph/renderer/ui/GraphProvisioningHud.tsx`
2184
+ - `src/features/agent-graph/renderer/ui/GraphNodePopover.tsx`
2185
+
2186
+ Ответственность:
2187
+
2188
+ - быть consumer-слоем готовой geometry
2189
+ - не быть главным solver-ом layout
2190
+ - сохранить существующие полезные interactions
2191
+
2192
+ ## Подробный implementation plan
2193
+
2194
+ Ниже порядок, который уменьшает риск наломать багов.
2195
+
2196
+ ## Phase gates - когда можно идти дальше
2197
+
2198
+ Это не просто список задач по порядку. Между фазами есть обязательные quality gates.
2199
+
2200
+ ### Нельзя переходить к slot UI integration, пока не выполнено
2201
+
2202
+ - stable identity уже протянута до graph layer
2203
+ - planner helpers уже pure и покрыты базовыми unit tests
2204
+ - `slotAssignmentsByTeam` уже стал source of truth
2205
+ - planner уже выдаёт валидный `StableSlotLayoutSnapshot`
2206
+ - validation pass уже работает
2207
+ - snapshot lifecycle и fail-closed commit behavior уже определены и подключены
2208
+
2209
+ ### Нельзя считать activity/process/tasks phase завершённой, если
2210
+
2211
+ - для позиционирования всё ещё используется screen-space self-packing
2212
+ - renderer сам высчитывает geometry вместо consumption `slot frame` и local anchors
2213
+ - overlap "чинится" постфактум DOM-side коррекцией
2214
+
2215
+ ### Нельзя удалять старые geometry paths, пока не доказано
2216
+
2217
+ - `Activity`
2218
+ - `Process`
2219
+ - `Tasks`
2220
+ - fit / camera
2221
+ - drag / snap / swap
2222
+
2223
+ уже работают на новом planner path без регрессии ключевых UX-сценариев.
2224
+
2225
+ ## Phase 0 - audit and kill-switch cleanup
2226
+
2227
+ ### Цель
2228
+
2229
+ Перед тем как писать новый planner, зафиксировать, какие текущие механизмы нужно убрать или перестать считать source of truth.
2230
+
2231
+ ### Сделать
2232
+
2233
+ 1. Найти текущие owner-related packers и manual reposition paths
2234
+ 2. Отдельно отметить:
2235
+ - screen-space activity pack
2236
+ - owner free-force assumptions
2237
+ - raw pinning paths
2238
+ 3. Зафиксировать, что после migration source of truth будет slot planner
2239
+
2240
+ ### Результат
2241
+
2242
+ Список устаревающих механизмов и мест, которые должны стать no-op или быть удалены на финальной фазе.
2243
+
2244
+ ## Phase 0.5 - temporary rollout guard
2245
+
2246
+ ### Цель
2247
+
2248
+ Внедрять новый planner безопасно и иметь быстрый способ локально сравнить новое поведение со старым.
2249
+
2250
+ ### Сделать
2251
+
2252
+ 1. Добавить временный internal switch / feature flag для нового planner path
2253
+ 2. Оставить возможность локально переключать:
2254
+ - current layout
2255
+ - stable slot layout
2256
+ 3. После достижения parity удалить переключатель или сделать его технически неактивным
2257
+
2258
+ ### Rollout contract
2259
+
2260
+ Feature flag должен переключать **целый layout mode**, а не отдельные куски.
2261
+
2262
+ Если включён новый path:
2263
+
2264
+ - owner placement идёт только от stable-slot planner
2265
+ - activity/process/tasks берут geometry только из нового slot path
2266
+ - старые owner pack/reposition механизмы не должны параллельно влиять на те же owner-local зоны
2267
+
2268
+ Если включён старый path:
2269
+
2270
+ - новый planner может жить только как dev/test path
2271
+ - его geometry не должна подмешиваться в production rendering случайно
2272
+
2273
+ ### Что запрещено
2274
+
2275
+ - mixed mode, где tasks уже от нового slot frame, а activity всё ещё пакуется старым screen-space способом
2276
+ - mixed mode, где новый planner строит assignments, но старый renderer потом "допихивает" geometry
2277
+ - включение feature flag только для одной owner-local подсистемы без согласованного переключения всей owner-local topology модели
2278
+
2279
+ ### Done when
2280
+
2281
+ - новый planner можно изолированно проверять во время разработки, не ломая возможность сравнения
2282
+
2283
+ ## Phase 1 - stable identity plumbing
2284
+
2285
+ ### Цель
2286
+
2287
+ Убрать хрупкую зависимость layout от display name.
2288
+
2289
+ ### Сделать
2290
+
2291
+ 1. Добавить `agentId?: string` в `ResolvedTeamMember`
2292
+ 2. Протащить `agentId` через `TeamMemberResolver`
2293
+ 3. Добавить helper `getStableOwnerId(member)`
2294
+ 4. Перевести member node ids на stable owner id
2295
+ 5. Проверить все ссылки `task.ownerId`, `process owner`, `activity owner`
2296
+
2297
+ ### Done when
2298
+
2299
+ - rename при том же `agentId` больше не меняет graph identity owner node
2300
+
2301
+ ## Phase 2 - slot state and planner helpers
2302
+
2303
+ ### Цель
2304
+
2305
+ Вынести геометрию и planner из UI-слоя в чистые helper-и.
2306
+
2307
+ ### Сделать
2308
+
2309
+ 1. Добавить типы:
2310
+ - `OwnerSlotAssignment`
2311
+ - `SlotFrame`
2312
+ - `OwnerFootprint`
2313
+ - `StableSlotLayoutSnapshot`
2314
+ 2. Реализовать pure helpers:
2315
+ - `computeOwnerFootprint`
2316
+ - `classifyWidthBucket`
2317
+ - `buildCentralExclusion`
2318
+ - `buildOwnerAnchor`
2319
+ - `buildSlotFrameFromOwnerAnchor`
2320
+ - `buildSlotLocalOrigins`
2321
+ - `buildUnassignedTaskSlotFrame`
2322
+ - `planOwnerSlots`
2323
+ - `resolveNearestSlot`
2324
+ - `isSlotAssignmentValid`
2325
+ - `computeRingRadius`
2326
+ 3. Добавить min-gap / ring-gap constants в одном месте
2327
+ 4. Собрать planner result в единый snapshot и валидировать его до render/commit
2328
+
2329
+ ### Done when
2330
+
2331
+ - planner можно покрыть unit tests без React и без canvas
2332
+
2333
+ ## Phase 3 - renderer-side slot assignment storage
2334
+
2335
+ ### Цель
2336
+
2337
+ Сделать persistent source of truth для owner placement.
2338
+
2339
+ ### Сделать
2340
+
2341
+ 1. Добавить store-state `slotAssignmentsByTeam`
2342
+ 2. Хранить assignments по `stableOwnerId`
2343
+ 3. Добавить actions:
2344
+ - `setOwnerSlotAssignment`
2345
+ - `swapOwnerSlots`
2346
+ - `clearTeamSlotAssignments`
2347
+ - `resetTeamSlotAssignmentsToPlannedDefaults`
2348
+ 4. Продумать first-load migration для старых raw pinning paths
2349
+ 5. Явно зафиксировать, что storage относится только к member sector slots, а не к lead/unassigned geometry
2350
+ 6. Добавить compare-and-reset semantics для `slotLayoutVersion`
2351
+ 7. Добавить one-time migration path `fallback member.name -> agentId`, если assignment уже существовал
2352
+ 8. Зафиксировать precedence между version reset, existing stable assignment и fallback migration
2353
+
2354
+ ### Done when
2355
+
2356
+ - owner placement переживает refresh и не зависит от случайной d3-стабилизации
2357
+ - legacy owner pinning больше не конкурирует со slot assignment как второй source of truth
2358
+ - `slotLayoutVersion` mismatch сбрасывает старые member assignments предсказуемо
2359
+ - existing fallback assignment может сохраниться при переходе `member.name -> agentId`
2360
+ - version reset и fallback migration не создают две конкурирующие записи для одного member-а
2361
+
2362
+ ## Phase 4 - integrate planner into graph simulation
2363
+
2364
+ ### Цель
2365
+
2366
+ Сделать owner positions planner-derived.
2367
+
2368
+ ### Сделать
2369
+
2370
+ 1. `useGraphSimulation` получает slot frames
2371
+ 2. Lead фиксируется в центре
2372
+ 3. `unassigned task slot`, если нужен, строится как special fixed frame под lead
2373
+ 4. Member node positions берутся из slot frames
2374
+ 5. Free-force больше не определяет owner layout
2375
+ 6. Если d3-force остаётся, он не должен двигать owners вне slot planner-а
2376
+
2377
+ ### Done when
2378
+
2379
+ - owner topology не меняется из-за zoom, pan или случайного re-tick
2380
+
2381
+ ## Phase 5 - task band integration
2382
+
2383
+ ### Цель
2384
+
2385
+ Встроить kanban в slot frame без cross-owner overlap.
2386
+
2387
+ ### Сделать
2388
+
2389
+ 1. `KanbanLayoutEngine` начинает работать от `slot frame`
2390
+ 2. Сохраняем current column order / semantics
2391
+ 3. Считаем реальный `kanbanWidth`
2392
+ 4. Ограничиваем task band по `5 rows`
2393
+ 5. Реализуем overflow stack per column
2394
+ 6. Явно сохранить существующий column order source, не вводя новый sort-rule
2395
+ 7. Отдельно встроить `unassigned task slot` как special pseudo-owner case:
2396
+ - без `activity band`
2397
+ - без `process band`
2398
+ - с теми же bounded task rules
2399
+
2400
+ ### Done when
2401
+
2402
+ - tasks не залезают в activity zone соседнего owner
2403
+ - unassigned tasks больше не теряются и не "прилипают" к случайным member slots
2404
+
2405
+ ## Phase 6 - process rail integration
2406
+
2407
+ ### Цель
2408
+
2409
+ Прикрепить process к owner slot.
2410
+
2411
+ ### Сделать
2412
+
2413
+ 1. Убрать свободное process placement
2414
+ 2. Привязать process presentation к process band внутри slot
2415
+ 3. Сохранить current visual style
2416
+ 4. Ограничить до одного релевантного process rail для `v1`
2417
+
2418
+ ### Done when
2419
+
2420
+ - process перестаёт быть отдельным плавающим источником хаоса
2421
+
2422
+ ## Phase 7 - activity band integration
2423
+
2424
+ ### Цель
2425
+
2426
+ Сделать activity частью slot, а не внешним solver-ом.
2427
+
2428
+ ### Сделать
2429
+
2430
+ 1. `GraphActivityHud` перестаёт pack-ить owners
2431
+ 2. Получает slot-local coordinates
2432
+ 3. Рисует local connector
2433
+ 4. Сохраняет existing compact message UI
2434
+ 5. Сохраняет `+N more -> Activity tab`
2435
+ 6. Сохраняет deterministic `newest first` + tie-break path
2436
+ 7. Удаляет screen-space self-packing и DOM-measurement-driven repositioning из activity path
2437
+
2438
+ ### Done when
2439
+
2440
+ - activity больше не может сместиться независимо от owner slot
2441
+
2442
+ ## Phase 8 - drag / snap / swap
2443
+
2444
+ ### Цель
2445
+
2446
+ Сохранить manual control, не ломая стабильность.
2447
+
2448
+ ### Сделать
2449
+
2450
+ 1. Drag разрешён только для member slots
2451
+ 2. На drop находим nearest valid slot
2452
+ 3. Если slot занят, делаем `swap`
2453
+ 4. Сохраняем assignment
2454
+ 5. Обновляем planner state без full random reshuffle
2455
+
2456
+ ### Done when
2457
+
2458
+ - drag меняет slot assignment, а не свободную world position
2459
+
2460
+ ## Phase 9 - fit / bounds / camera
2461
+
2462
+ ### Цель
2463
+
2464
+ Чтобы camera видела реальный layout.
2465
+
2466
+ ### Сделать
2467
+
2468
+ 1. `zoomToFit` учитывает полные slot bounds
2469
+ 2. Учитывать:
2470
+ - activity bands
2471
+ - task bands
2472
+ - process rails
2473
+ - central exclusion
2474
+ - unassigned task slot
2475
+ 3. Проверить initial fit и manual fit
2476
+
2477
+ ### Done when
2478
+
2479
+ - fit больше не обрезает реальные owner-local зоны
2480
+
2481
+ ## Phase 10 - cleanup old geometry paths
2482
+
2483
+ ### Цель
2484
+
2485
+ Убрать старые механизмы, которые будут конфликтовать с новым planner-ом.
2486
+
2487
+ ### Сделать
2488
+
2489
+ 1. Удалить или задизейблить устаревший owner overlap pack path
2490
+ 2. Удалить owner-specific reliance on raw pinning
2491
+ 3. Удалить activity self-packing logic, если она больше не нужна
2492
+ 4. Проверить, что остались только необходимые world transforms
2493
+
2494
+ ### Done when
2495
+
2496
+ - в коде остаётся один понятный source of truth для owner layout
2497
+
2498
+ ## Phase 11 - parity review
2499
+
2500
+ ### Цель
2501
+
2502
+ Перед финальным merge проверить, что новый planner не потерял уже работающие UX-paths.
2503
+
2504
+ ### Проверить
2505
+
2506
+ 1. `+N more -> Activity tab`
2507
+ 2. existing activity item click behavior
2508
+ 3. process visual styling
2509
+ 4. lead launch HUD
2510
+ 5. fit / zoom controls
2511
+ 6. graph tab / fullscreen shared layout behavior
2512
+ 7. filters не вызывают layout jumps
2513
+
2514
+ ### Done when
2515
+
2516
+ - новый layout стабилен и не деградирует уже полезные interaction-ы
2517
+
2518
+ ## Recommended PR split
2519
+
2520
+ Чтобы этот refactor было реально безопасно довезти, лучше не делать его одним гигантским diff.
2521
+
2522
+ Рекомендуемая нарезка:
2523
+
2524
+ ### PR 1 - stable identity and slot state
2525
+
2526
+ - `agentId -> ResolvedTeamMember`
2527
+ - `stableOwnerId`
2528
+ - новые member node ids
2529
+ - `slotAssignmentsByTeam`
2530
+ - migration policy для legacy pinning
2531
+
2532
+ ### PR 2 - pure planner and simulation integration
2533
+
2534
+ - `OwnerFootprint`
2535
+ - `SlotFrame`
2536
+ - planner helpers
2537
+ - `validateStableSlotLayout`
2538
+ - `StableSlotLayoutSnapshot`
2539
+ - ring/sector logic
2540
+ - интеграция planner-а в `useGraphSimulation`
2541
+
2542
+ ### PR 3 - task/process/activity slot integration
2543
+
2544
+ - `KanbanLayoutEngine` от `slot frame`
2545
+ - process rail inside slot
2546
+ - activity inside slot
2547
+ - local anchors / connector path
2548
+
2549
+ ### PR 4 - drag, fit, cleanup, parity
2550
+
2551
+ - drag/snap/swap
2552
+ - reset-layout path
2553
+ - fit bounds
2554
+ - удаление старых geometry paths
2555
+ - parity review и финальный cleanup
2556
+
2557
+ Если в реальности придётся объединить PR 2 и PR 3 - это ещё допустимо. Но делать всё одним большим PR хуже по риску и по способности нормально проверить регрессии.
2558
+
2559
+ ## Тест-план
2560
+
2561
+ Ниже обязательные тесты, без которых этот refactor нельзя считать завершённым.
2562
+
2563
+ ## Identity / ordering
2564
+
2565
+ - `ResolvedTeamMember.agentId` проходит до graph layer
2566
+ - member node id строится на `stableOwnerId`
2567
+ - rename при том же `agentId` не меняет slot assignment
2568
+ - fallback на `member.name` работает, если `agentId` отсутствует
2569
+ - duplicate `agentId` или duplicate fallback `member.name` не приводят к silent merge owners
2570
+ - existing fallback assignment корректно мигрирует на `agentId`, если `agentId` появился позже
2571
+ - hidden member при возвращении с тем же `stableOwnerId` пытается переиспользовать прежний assignment
2572
+ - initial ordering стабилен и совпадает с `config.members[]`
2573
+
2574
+ ## Planner
2575
+
2576
+ - owner slots не пересекаются друг с другом
2577
+ - owner slots не пересекают central exclusion
2578
+ - `leadCentralReservedBlock` не схлопывается от hidden launch HUD или пустого lead activity state
2579
+ - `SlotFrame` строится по канонической формуле от `ownerAnchor`
2580
+ - `activityOrigin/processOrigin/taskOrigin` детерминированно выводятся из `SlotFrame`
2581
+ - validator ловит `NaN/Infinity` в frame-ах и bounds
2582
+ - validator ловит duplicate assignment-ы
2583
+ - validator проверяет, что band-local anchors не вылезают из своего `SlotFrame`
2584
+ - при validation failure новый broken layout не перетирает последний валидный snapshot
2585
+ - один planner run собирает один цельный `StableSlotLayoutSnapshot`
2586
+ - snapshot коммитится атомарно, без частичного обновления `SlotFrame`/`fitBounds`/`central exclusion`
2587
+ - ring 1 overflow создаёт ring 2
2588
+ - один ring не принимает больше одного owner на один sector anchor
2589
+ - existing valid assignments сохраняются
2590
+ - invalid assignment перепланируется локально, без полного reshuffle
2591
+ - planner сначала пытается сохранить сектор, потом увести owner на внешний ring
2592
+ - planner работает в world coordinates и не зависит от camera zoom/pan
2593
+ - planner не пытается сам разместить `lead` или `unassigned task slot`
2594
+ - planner не запускается как полноценный stable-sector layout без `lead`
2595
+ - при конфликте из-за роста одного owner footprint planner сначала пытается двигать именно этот owner, а не весь ring
2596
+
2597
+ ## Tasks
2598
+
2599
+ - task band ограничен `5 rows`
2600
+ - overflow stack работает per column
2601
+ - все active non-empty columns показываются
2602
+ - slot width растёт при росте числа columns
2603
+ - current канонический order внутри column сохраняется
2604
+ - задачи без owner-а попадают в отдельный `unassigned task slot`
2605
+ - задачи с owner-ом, который сейчас не видим или не существует, тоже уходят в `unassigned task slot`
2606
+
2607
+ ## Activity
2608
+
2609
+ - activity band показывает `3` items
2610
+ - `+N more` считается корректно
2611
+ - comments показывают target task, а не fake-recipient member
2612
+ - activity order внутри slot - newest first
2613
+ - tie-break при одинаковом timestamp детерминирован и стабилен
2614
+ - `+N more` открывает профиль на вкладке `Activity`
2615
+
2616
+ ## Process
2617
+
2618
+ - process rail остаётся owner-local
2619
+ - running process имеет приоритет над finished
2620
+ - пустой process band не вызывает layout jump
2621
+
2622
+ ## Drag / persistence
2623
+
2624
+ - drop снапает в nearest valid slot
2625
+ - занятый target slot делает `swap`
2626
+ - невалидный drop возвращает owner в прежний assignment
2627
+ - refresh сохраняет slot assignments
2628
+ - zoom/pan не меняют owner placement
2629
+ - graph tab и fullscreen overlay используют один и тот же member slot state
2630
+ - graph tab и fullscreen overlay могут иметь разный camera state
2631
+ - `slotLayoutVersion` mismatch сбрасывает устаревшие member assignments
2632
+ - fallback migration `member.name -> agentId` не создаёт дубли assignment-ов
2633
+ - team switch не смешивает assignments разных команд
2634
+ - если одна команда открыта в нескольких pane-ах, layout state у них общий и синхронизируется через shared store
2635
+ - no-lead transient state не портит persistent assignments
2636
+ - validation failure не делает invalid snapshot active render geometry
2637
+ - no-lead path не делает stale stable-slot snapshot активным render-path вместо fallback UI
2638
+ - snapshot cache не подменяет `slotAssignmentsByTeam` как placement source of truth
2639
+
2640
+ ## Fit / camera
2641
+
2642
+ - initial fit учитывает slot bounds
2643
+ - manual fit учитывает slot bounds
2644
+ - initial fit учитывает `unassigned task slot`
2645
+ - manual fit учитывает `unassigned task slot`
2646
+ - initial fit учитывает `lead central reserved block`
2647
+ - manual fit учитывает `lead central reserved block`
2648
+ - zoom меняет только camera transform
2649
+ - pan меняет только camera transform
2650
+ - filters не меняют topology bounds, которые использует fit
2651
+
2652
+ ## Filters
2653
+
2654
+ - `showTasks` не меняет slot topology
2655
+ - `showProcesses` не меняет slot topology
2656
+ - `showEdges` не меняет slot topology
2657
+ - скрытие tasks/processes не убирает reserved band geometry и не вызывает layout jump
2658
+ - `showTasks = false` не удаляет topology footprint у `unassigned task slot`, если в dataset всё ещё есть unassigned tasks
2659
+
2660
+ ## Team scoping
2661
+
2662
+ - `slotAssignmentsByTeam` изолирует layout state по `teamName`
2663
+ - team switch не переиспользует assignments от другой команды
2664
+ - параллельно открытые graph panes не должны ломать team-scoped layout друг друга
2665
+
2666
+ ## Rollout / flag behavior
2667
+
2668
+ - feature flag не включает mixed mode между старым и новым owner-local layout path
2669
+ - при включённом новом path activity/process/tasks используют один и тот же stable-slot geometry source
2670
+ - старый owner pack/reposition path не влияет на новый stable-slot render path
2671
+
2672
+ ## Dense teams
2673
+
2674
+ - граф с большим числом участников уходит во второй ring, а не превращается в overlap-chaos
2675
+ - широкие slots не придавливают соседние owner zones
2676
+ - lead central zone остаётся чистой
2677
+ - появление `unassigned task slot` не ломает весь ring-layout глобальным reshuffle
2678
+
2679
+ ## Самые слабые места и что проверять особенно внимательно
2680
+
2681
+ ## 1. Stable identity
2682
+
2683
+ Если тут останется хотя бы один тихий fallback на `member.name` в layout storage, всё снова станет хрупким.
2684
+
2685
+ И отдельно:
2686
+
2687
+ - если silent merge случится из-за неуникального `agentId` или `member.name`, это сломает сразу и slot assignments, и drag, и ownership links
2688
+
2689
+ ## 2. Slot width growth
2690
+
2691
+ Это самый рискованный geometry block после identity, потому что пользователь сознательно выбрал:
2692
+
2693
+ - показывать все columns
2694
+ - не вводить scroll
2695
+ - не cap-ить visible columns
2696
+
2697
+ ## 3. Partial replanning
2698
+
2699
+ Самая большая UX-ошибка тут - случайно делать global reshuffle там, где достаточно локального spill на outer ring.
2700
+
2701
+ ## 4. Lead central exclusion
2702
+
2703
+ Если planner забудет учесть хотя бы один из центральных блоков:
2704
+
2705
+ - lead
2706
+ - launch HUD
2707
+ - lead activity
2708
+
2709
+ то первый ring начнёт врезаться в центр.
2710
+
2711
+ ## 5. Old geometry paths
2712
+
2713
+ Если оставить старые packers и force-assumptions живыми параллельно, новый planner будет "исправляться" чужой логикой, и баги станут трудноотлавливаемыми.
2714
+
2715
+ ## 6. World-space vs screen-space mixing
2716
+
2717
+ Это самый коварный класс багов после identity.
2718
+
2719
+ Если хотя бы один owner-local слой снова начнёт:
2720
+
2721
+ - сам себя pack-ить в screen-space
2722
+ - двигаться из DOM measurements
2723
+ - учитывать текущий zoom как вход planner-а
2724
+
2725
+ то визуально всё опять будет "плавать" отдельно от графа.
2726
+
2727
+ ## Запрещённые переинтерпретации плана
2728
+
2729
+ Ниже короткий список вещей, которые implementer не должен "упростить по ходу", потому что именно так обычно и возвращаются старые баги.
2730
+
2731
+ - нельзя трактовать `Activity` как свободный overlay поверх итогового layout
2732
+ - нельзя трактовать `lead` как обычный member slot только с особыми стилями
2733
+ - нельзя сохранять raw `x/y` "временно, пока не доделаем slot assignment"
2734
+ - нельзя позволять filters менять topology, даже если визуально это кажется проще
2735
+ - нельзя схлопывать `lead activity frame` или `launch HUD frame` только потому, что они сейчас не видимы
2736
+ - нельзя строить `SlotFrame` отдельно в planner и отдельно в renderer с немного разными формулами
2737
+ - нельзя заменять local replanning глобальным reshuffle только потому, что так проще написать первую версию
2738
+ - нельзя допускать, чтобы fullscreen и tab считали layout независимо друг от друга
2739
+ - нельзя лечить overlap постфактум screen-space сдвигами вместо исправления planner-а или footprint contract
2740
+
2741
+ Если для какой-то задачи кажется, что одно из этих правил "мешает быстро доделать", значит меняется не реализация плана, а сам план. Это нужно сначала явно переоткрыть как product/architecture decision, а не менять молча по коду.
2742
+
2743
+ ## Acceptance criteria
2744
+
2745
+ - [ ] lead остаётся центральным anchor
2746
+ - [ ] lead использует central reserved block, а не обычный member slot
2747
+ - [ ] `leadCentralReservedBlock` детерминированно собирается из lead activity frame, lead core frame и launch HUD frame
2748
+ - [ ] member slots распределяются по стабильным секторам
2749
+ - [ ] second ring работает по footprint-budget, а не по member count
2750
+ - [ ] stable owner id построен на `agentId`, с fallback на `member.name`
2751
+ - [ ] member node ids больше не name-based
2752
+ - [ ] slot assignment хранится отдельно от raw `x/y`
2753
+ - [ ] `slotAssignmentsByTeam` хранит только member sector assignments, а не lead/unassigned geometry
2754
+ - [ ] persistent layout state ограничен assignment-ами и version marker-ом, а geometry остаётся derived
2755
+ - [ ] `slotLayoutVersion` mismatch безопасно сбрасывает старые member assignments
2756
+ - [ ] existing fallback assignment может мигрировать на `agentId`, если `agentId` появился позже
2757
+ - [ ] precedence между version reset, stable assignment и fallback migration определена и не создаёт дублей
2758
+ - [ ] slot geometry считается в world coordinates и не чинится screen-space packer-ом
2759
+ - [ ] `SlotFrame` строится по одному каноническому правилу от `ownerAnchor`, без альтернативных renderer-side трактовок
2760
+ - [ ] geometry constants живут в одном stable-slot layout module и не дублируются по коду
2761
+ - [ ] renderer/simulation получают один цельный `StableSlotLayoutSnapshot`, а не набор несвязанных geometry-кусочков
2762
+ - [ ] planner output проходит отдельную validation pass перед commit/render
2763
+ - [ ] validation failure не приводит к partially-broken render и не записывает невалидные assignments в store
2764
+ - [ ] один layout update коммитится атомарно, без смешивания старых и новых поколений geometry state
2765
+ - [ ] session-local last valid snapshot cache не становится альтернативным source of truth для placement
2766
+ - [ ] no-lead path не коммитит новый stable-slot snapshot и не подменяет fallback render stale snapshot-ом
2767
+ - [ ] runtime precedence между assignments, active snapshot, snapshot cache и fallback path соблюдается без mixed-state
2768
+ - [ ] tasks bounded по высоте до `5 rows`
2769
+ - [ ] activity bounded до `3 items`
2770
+ - [ ] все active non-empty kanban columns показываются
2771
+ - [ ] slot width может расти
2772
+ - [ ] unassigned tasks живут в отдельном bounded slot под lead
2773
+ - [ ] process attached к owner slot
2774
+ - [ ] drag работает через snap-to-slot
2775
+ - [ ] occupied slot приводит к swap
2776
+ - [ ] invalid drop возвращает owner в прежний slot
2777
+ - [ ] zoom/pan не меняют owner topology
2778
+ - [ ] graph tab и fullscreen overlay разделяют один и тот же layout state
2779
+ - [ ] graph tab и fullscreen overlay могут иметь независимый camera state без расхождения layout
2780
+ - [ ] graph filters не перестраивают slot topology
2781
+ - [ ] `showTasks = false` не убирает topology footprint у `unassigned task slot`, если dataset не изменился
2782
+ - [ ] team switch не смешивает layout state разных команд
2783
+ - [ ] team rename не обязан мигрировать layout state в `v1` и это явно осознано как tradeoff
2784
+ - [ ] одна и та же команда в нескольких pane-ах использует общий shared layout state
2785
+ - [ ] hidden member при возвращении с тем же `stableOwnerId` может переиспользовать прежний assignment
2786
+ - [ ] no-lead transient state не порождает случайный persistent layout
2787
+ - [ ] fit использует topology bounds, а не только текущую видимую presentation
2788
+ - [ ] fit учитывает полные slot bounds
2789
+ - [ ] dense teams больше не превращают graph в overlay-chaos
2790
+
2791
+ ## Итоговое решение в одной секции
2792
+
2793
+ Финально мы пришли вот к чему:
2794
+
2795
+ - берём `Variant 3`
2796
+ - делаем `stable sectors around lead`
2797
+ - фиксируем `6` sector anchors в `v1`
2798
+ - добавляем `second ring`
2799
+ - считаем ring capacity по footprint-budget
2800
+ - используем `S / M / L` только как packing heuristic
2801
+ - показываем все active non-empty kanban columns
2802
+ - ограничиваем task band по высоте до `5 rows`
2803
+ - ограничиваем activity band до `3 items`
2804
+ - unassigned tasks уводим в отдельный bounded slot под lead
2805
+ - process делаем attached sub-rail owner-а
2806
+ - drag делаем через snap и swap
2807
+ - slot assignment храним отдельно
2808
+ - stable owner identity строим на `agentId`, fallback на `member.name`
2809
+
2810
+ Это и есть зафиксированный target-state для следующей большой переделки graph layout.
2811
+
2812
+ ## Что ещё можно тюнить без изменения архитектуры
2813
+
2814
+ Блокирующих product-level вопросов не осталось.
2815
+
2816
+ Разрешён только визуальный и micro-spacing tuning, который не меняет planner semantics:
2817
+
2818
+ - padding внутри activity item shell
2819
+ - точные visual offsets process rail
2820
+ - glow / shadow / label spacing
2821
+
2822
+ Нельзя под видом тюнинга менять:
2823
+
2824
+ - source of truth
2825
+ - slot assignment model
2826
+ - ring planner semantics
2827
+ - bounded band rules
2828
+ - drag/snap/swap rules
2829
+
2830
+ ## IOF execution checklist
2831
+
2832
+ Это короткий practical checklist, по которому удобно идти во время реализации, чтобы не потерять зафиксированные решения.
2833
+
2834
+ 1. Сначала убедиться, что `ResolvedTeamMember` реально получил `agentId` и что graph member node ids перестали зависеть от `member.name`.
2835
+ 2. До включения нового planner path зафиксировать все места, где старый код всё ещё двигает owners через force/pack/pinning.
2836
+ 3. Вынести slot geometry в pure helpers до переписывания UI.
2837
+ 4. Сначала сделать planner и его unit tests, и только потом подключать UI.
2838
+ 5. Прежде чем трогать `GraphActivityHud`, зафиксировать один канонический `ownerAnchor/activityOrigin/processOrigin/taskOrigin`.
2839
+ 6. Не трогать одновременно и data model, и visual tuning, пока planner не стал детерминированным.
2840
+ 7. После интеграции planner-а отдельно проверить, что zoom/pan больше не влияют на topology.
2841
+ 8. После интеграции task band отдельно проверить dense-team cases, где slot width растёт.
2842
+ 9. После интеграции drag отдельно проверить invalid drop, swap и reset-layout.
2843
+ 10. Отдельно проверить cases без owner-а и с "битым" owner reference, чтобы такие задачи гарантированно уходили в `unassigned task slot`.
2844
+ 11. Перед cleanup старых paths убедиться, что новый planner покрывает `Activity`, `Process`, `Tasks` и fit bounds.
2845
+ 12. Перед финальным merge вручную проверить `+N more -> Activity`, launch HUD и process visual parity.
2846
+ 13. Не мержить состояние, где в коде остаются два равноправных source of truth для owner placement.