@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.
- package/LICENSE +611 -0
- package/README.md +220 -0
- package/bin/hermit.mjs +364 -0
- package/bin/kill-dev.js +20 -0
- package/dist-renderer/assets/01-BApSFlV4.png +0 -0
- package/dist-renderer/assets/02-CRQGs29u.png +0 -0
- package/dist-renderer/assets/03-BFCM2jnD.png +0 -0
- package/dist-renderer/assets/04-B2FThbKO.png +0 -0
- package/dist-renderer/assets/05-D9p0Znkd.png +0 -0
- package/dist-renderer/assets/06-DZAfbDlP.png +0 -0
- package/dist-renderer/assets/07-B_PXWGCc.png +0 -0
- package/dist-renderer/assets/08-DGRMZ6sl.png +0 -0
- package/dist-renderer/assets/09-SGCQvc7U.png +0 -0
- package/dist-renderer/assets/10-Cve81Q3W.png +0 -0
- package/dist-renderer/assets/11-DGglolDW.png +0 -0
- package/dist-renderer/assets/12-C3lnu79c.png +0 -0
- package/dist-renderer/assets/13-M59meqdw.png +0 -0
- package/dist-renderer/assets/ProjectEditorOverlay-BNoDw9T1.js +57 -0
- package/dist-renderer/assets/TeamGraphOverlay-CfGRKQIu.js +1 -0
- package/dist-renderer/assets/_basePickBy-Ct8Hm5_h.js +1 -0
- package/dist-renderer/assets/_baseUniq-BofrAFBx.js +1 -0
- package/dist-renderer/assets/apl-B4CMkyY2.js +1 -0
- package/dist-renderer/assets/arc-AbJgatzR.js +1 -0
- package/dist-renderer/assets/architectureDiagram-VXUJARFQ-gpniCJVk.js +36 -0
- package/dist-renderer/assets/asciiarmor-Df11BRmG.js +1 -0
- package/dist-renderer/assets/asn1-EdZsLKOL.js +1 -0
- package/dist-renderer/assets/asterisk-B-8jnY81.js +1 -0
- package/dist-renderer/assets/blockDiagram-VD42YOAC-aBbbmONC.js +122 -0
- package/dist-renderer/assets/brainfuck-C4LP7Hcl.js +1 -0
- package/dist-renderer/assets/c4Diagram-YG6GDRKO-DJio1IsU.js +10 -0
- package/dist-renderer/assets/channel-CZ8sd5Xf.js +1 -0
- package/dist-renderer/assets/chunk-4BX2VUAB-D1_HKao2.js +1 -0
- package/dist-renderer/assets/chunk-55IACEB6-NAmVxF4k.js +1 -0
- package/dist-renderer/assets/chunk-B4BG7PRW-Ce829laz.js +165 -0
- package/dist-renderer/assets/chunk-DI55MBZ5-Ct2Le12y.js +220 -0
- package/dist-renderer/assets/chunk-FMBD7UC4-Cie3DzKk.js +15 -0
- package/dist-renderer/assets/chunk-QN33PNHL-4f5Yb50e.js +1 -0
- package/dist-renderer/assets/chunk-QZHKN3VN-D9ranl9c.js +1 -0
- package/dist-renderer/assets/chunk-TZMSLE5B-bdGZWlEy.js +1 -0
- package/dist-renderer/assets/classDiagram-2ON5EDUG-CMcfSKj5.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CMcfSKj5.js +1 -0
- package/dist-renderer/assets/clike-B9uivgTg.js +1 -0
- package/dist-renderer/assets/clojure-BMjYHr_A.js +1 -0
- package/dist-renderer/assets/clone-CMuwA8RV.js +1 -0
- package/dist-renderer/assets/cmake-BQqOBYOt.js +1 -0
- package/dist-renderer/assets/cobol-CWcv1MsR.js +1 -0
- package/dist-renderer/assets/coffeescript-S37ZYGWr.js +1 -0
- package/dist-renderer/assets/commonlisp-DBKNyK5s.js +1 -0
- package/dist-renderer/assets/cose-bilkent-S5V4N54A-C6tvfcVi.js +1 -0
- package/dist-renderer/assets/crystal-SjHAIU92.js +1 -0
- package/dist-renderer/assets/css-BnMrqG3P.js +1 -0
- package/dist-renderer/assets/cypher-C_CwsFkJ.js +1 -0
- package/dist-renderer/assets/cytoscape.esm-DsxaTqgk.js +331 -0
- package/dist-renderer/assets/d-pRatUO7H.js +1 -0
- package/dist-renderer/assets/dagre-6UL2VRFP-B-4qcZam.js +4 -0
- package/dist-renderer/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist-renderer/assets/diagram-PSM6KHXK-CwT3TLjx.js +24 -0
- package/dist-renderer/assets/diagram-QEK2KX5R-BWH6-ZFd.js +43 -0
- package/dist-renderer/assets/diagram-S2PKOQOG-DfpPnfi1.js +24 -0
- package/dist-renderer/assets/diff-DbItnlRl.js +1 -0
- package/dist-renderer/assets/dockerfile-BKs6k2Af.js +1 -0
- package/dist-renderer/assets/dtd-DF_7sFjM.js +1 -0
- package/dist-renderer/assets/dylan-DwRh75JA.js +1 -0
- package/dist-renderer/assets/ebnf-CDyGwa7X.js +1 -0
- package/dist-renderer/assets/ecl-Cabwm37j.js +1 -0
- package/dist-renderer/assets/eiffel-CnydiIhH.js +1 -0
- package/dist-renderer/assets/elm-vLlmbW-K.js +1 -0
- package/dist-renderer/assets/erDiagram-Q2GNP2WA-BFbEFR4x.js +60 -0
- package/dist-renderer/assets/erlang-BNw1qcRV.js +1 -0
- package/dist-renderer/assets/factor-kuTfRLto.js +1 -0
- package/dist-renderer/assets/favicon-B8xY-GVk.png +0 -0
- package/dist-renderer/assets/fcl-Kvtd6kyn.js +1 -0
- package/dist-renderer/assets/flowDiagram-NV44I4VS-Dg3cf5hW.js +162 -0
- package/dist-renderer/assets/forth-Ffai-XNe.js +1 -0
- package/dist-renderer/assets/fortran-DYz_wnZ1.js +1 -0
- package/dist-renderer/assets/ganttDiagram-JELNMOA3-B21y55W5.js +267 -0
- package/dist-renderer/assets/gas-Bneqetm1.js +1 -0
- package/dist-renderer/assets/gherkin-heZmZLOM.js +1 -0
- package/dist-renderer/assets/gitGraphDiagram-V2S2FVAM-BDV3BJzn.js +65 -0
- package/dist-renderer/assets/graph-BfaZ4hZt.js +1 -0
- package/dist-renderer/assets/groovy-D9Dt4D0W.js +1 -0
- package/dist-renderer/assets/haskell-BWDZoCOh.js +1 -0
- package/dist-renderer/assets/haxe-H-WmDvRZ.js +1 -0
- package/dist-renderer/assets/http-DBlCnlav.js +1 -0
- package/dist-renderer/assets/idl-BEugSyMb.js +1 -0
- package/dist-renderer/assets/index-BMXHMpkG.js +1 -0
- package/dist-renderer/assets/index-CCqtDawH.js +1 -0
- package/dist-renderer/assets/index-CVMSpK8C.js +1 -0
- package/dist-renderer/assets/index-CZltVMDP.js +1844 -0
- package/dist-renderer/assets/index-CaG9mf8s.css +1 -0
- package/dist-renderer/assets/index-Ct0-y9TF.js +1 -0
- package/dist-renderer/assets/index-pMg_LlsS.js +1 -0
- package/dist-renderer/assets/infoDiagram-HS3SLOUP-DvMlS0CL.js +2 -0
- package/dist-renderer/assets/init-Gi6I4Gst.js +1 -0
- package/dist-renderer/assets/javascript-qCveANmP.js +1 -0
- package/dist-renderer/assets/journeyDiagram-XKPGCS4Q-DIyMluRv.js +139 -0
- package/dist-renderer/assets/julia-DuME0IfC.js +1 -0
- package/dist-renderer/assets/kanban-definition-3W4ZIXB7-CVOx8f-7.js +89 -0
- package/dist-renderer/assets/katex-DGN8GczM.js +261 -0
- package/dist-renderer/assets/layout-BPKIXUf4.js +1 -0
- package/dist-renderer/assets/linear-CScZGLr2.js +1 -0
- package/dist-renderer/assets/livescript-BwQOo05w.js +1 -0
- package/dist-renderer/assets/lua-BgMRiT3U.js +1 -0
- package/dist-renderer/assets/mathematica-DTrFuWx2.js +1 -0
- package/dist-renderer/assets/mbox-CNhZ1qSd.js +1 -0
- package/dist-renderer/assets/mindmap-definition-VGOIOE7T-CmDQ7Wo6.js +68 -0
- package/dist-renderer/assets/mirc-CjQqDB4T.js +1 -0
- package/dist-renderer/assets/mllike-CXdrOF99.js +1 -0
- package/dist-renderer/assets/modelica-Dc1JOy9r.js +1 -0
- package/dist-renderer/assets/mscgen-BA5vi2Kp.js +1 -0
- package/dist-renderer/assets/mumps-BT43cFF4.js +1 -0
- package/dist-renderer/assets/nginx-DdIZxoE0.js +1 -0
- package/dist-renderer/assets/nsis-LdVXkNf5.js +1 -0
- package/dist-renderer/assets/ntriples-BfvgReVJ.js +1 -0
- package/dist-renderer/assets/octave-Ck1zUtKM.js +1 -0
- package/dist-renderer/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist-renderer/assets/oz-BzwKVEFT.js +1 -0
- package/dist-renderer/assets/pascal--L3eBynH.js +1 -0
- package/dist-renderer/assets/perl-CdXCOZ3F.js +1 -0
- package/dist-renderer/assets/pieDiagram-ADFJNKIX-DbVClin-.js +30 -0
- package/dist-renderer/assets/pig-CevX1Tat.js +1 -0
- package/dist-renderer/assets/powershell-CFHJl5sT.js +1 -0
- package/dist-renderer/assets/properties-C78fOPTZ.js +1 -0
- package/dist-renderer/assets/protobuf-ChK-085T.js +1 -0
- package/dist-renderer/assets/pug-DukmZTjD.js +1 -0
- package/dist-renderer/assets/puppet-DMA9R1ak.js +1 -0
- package/dist-renderer/assets/python-BuPzkPfP.js +1 -0
- package/dist-renderer/assets/q-pXgVlZs6.js +1 -0
- package/dist-renderer/assets/quadrantDiagram-AYHSOK5B-CAB0MYcW.js +7 -0
- package/dist-renderer/assets/r-DUYO_cvP.js +1 -0
- package/dist-renderer/assets/requirementDiagram-UZGBJVZJ-w2Lfpg3T.js +64 -0
- package/dist-renderer/assets/rpm-CTu-6PCP.js +1 -0
- package/dist-renderer/assets/ruby-B2Rjki9n.js +1 -0
- package/dist-renderer/assets/sankeyDiagram-TZEHDZUN-kvG1QoKY.js +10 -0
- package/dist-renderer/assets/sas-B4kiWyti.js +1 -0
- package/dist-renderer/assets/scheme-C41bIUwD.js +1 -0
- package/dist-renderer/assets/sequenceDiagram-WL72ISMW-DCVBQ23J.js +145 -0
- package/dist-renderer/assets/shell-CjFT_Tl9.js +1 -0
- package/dist-renderer/assets/sieve-C3Gn_uJK.js +1 -0
- package/dist-renderer/assets/simple-mode-GW_nhZxv.js +1 -0
- package/dist-renderer/assets/smalltalk-CnHTOXQT.js +1 -0
- package/dist-renderer/assets/solr-DehyRSwq.js +1 -0
- package/dist-renderer/assets/sparql-DkYu6x3z.js +1 -0
- package/dist-renderer/assets/splashScene-C8lWNnm4.js +1 -0
- package/dist-renderer/assets/spreadsheet-BCZA_wO0.js +1 -0
- package/dist-renderer/assets/sql-D0XecflT.js +1 -0
- package/dist-renderer/assets/stateDiagram-FKZM4ZOC-ItZ0JBvq.js +1 -0
- package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-Hpmw4dMm.js +1 -0
- package/dist-renderer/assets/stex-C3f8Ysf7.js +1 -0
- package/dist-renderer/assets/stylus-B533Al4x.js +1 -0
- package/dist-renderer/assets/swift-BzpIVaGY.js +1 -0
- package/dist-renderer/assets/tcl-DVfN8rqt.js +1 -0
- package/dist-renderer/assets/textile-CnDTJFAw.js +1 -0
- package/dist-renderer/assets/tiddlywiki-DO-Gjzrf.js +1 -0
- package/dist-renderer/assets/tiki-DGYXhP31.js +1 -0
- package/dist-renderer/assets/timeline-definition-IT6M3QCI-BzSFaAjV.js +61 -0
- package/dist-renderer/assets/toml-Bm5Em-hy.js +1 -0
- package/dist-renderer/assets/treemap-GDKQZRPO-fSz4hQn0.js +162 -0
- package/dist-renderer/assets/troff-wAsdV37c.js +1 -0
- package/dist-renderer/assets/ttcn-CfJYG6tj.js +1 -0
- package/dist-renderer/assets/ttcn-cfg-B9xdYoR4.js +1 -0
- package/dist-renderer/assets/turtle-B1tBg_DP.js +1 -0
- package/dist-renderer/assets/vb-CmGdzxic.js +1 -0
- package/dist-renderer/assets/vbscript-BuJXcnF6.js +1 -0
- package/dist-renderer/assets/velocity-D8B20fx6.js +1 -0
- package/dist-renderer/assets/verilog-C6RDOZhf.js +1 -0
- package/dist-renderer/assets/vhdl-lSbBsy5d.js +1 -0
- package/dist-renderer/assets/webidl-ZXfAyPTL.js +1 -0
- package/dist-renderer/assets/xquery-CQfU5ijd.js +1 -0
- package/dist-renderer/assets/xychartDiagram-PRI3JC2R-CT1kaGlv.js +7 -0
- package/dist-renderer/assets/yacas-BJ4BC0dw.js +1 -0
- package/dist-renderer/assets/z80-Hz9HOZM7.js +1 -0
- package/dist-renderer/index.html +1274 -0
- package/package.json +181 -0
- package/src/features/README.md +24 -0
- package/src/features/agent-graph/README.md +21 -0
- package/src/features/agent-graph/STABLE_SLOT_LAYOUT_PLAN.md +2846 -0
- package/src/features/agent-graph/core/domain/buildInlineActivityEntries.ts +416 -0
- package/src/features/agent-graph/core/domain/collapseOverflowStacks.ts +126 -0
- package/src/features/agent-graph/core/domain/graphOwnerIdentity.ts +55 -0
- package/src/features/agent-graph/core/domain/taskGraphSemantics.ts +48 -0
- package/src/features/agent-graph/renderer/adapters/TeamGraphAdapter.ts +1400 -0
- package/src/features/agent-graph/renderer/hooks/useGraphActivityContext.ts +34 -0
- package/src/features/agent-graph/renderer/hooks/useGraphCreateTaskDialog.tsx +126 -0
- package/src/features/agent-graph/renderer/hooks/useGraphMemberPopoverContext.ts +34 -0
- package/src/features/agent-graph/renderer/hooks/useGraphSidebarVisibility.ts +52 -0
- package/src/features/agent-graph/renderer/hooks/useTeamGraphAdapter.ts +173 -0
- package/src/features/agent-graph/renderer/hooks/useTeamGraphSurfaceActions.ts +98 -0
- package/src/features/agent-graph/renderer/index.ts +14 -0
- package/src/features/agent-graph/renderer/ui/GraphActivityCard.tsx +96 -0
- package/src/features/agent-graph/renderer/ui/GraphActivityHud.tsx +498 -0
- package/src/features/agent-graph/renderer/ui/GraphBlockingEdgePopover.tsx +207 -0
- package/src/features/agent-graph/renderer/ui/GraphNodePopover.tsx +573 -0
- package/src/features/agent-graph/renderer/ui/GraphProvisioningHud.tsx +113 -0
- package/src/features/agent-graph/renderer/ui/GraphTaskCard.tsx +149 -0
- package/src/features/agent-graph/renderer/ui/GraphTransientHandoffHud.tsx +176 -0
- package/src/features/agent-graph/renderer/ui/TeamGraphOverlay.tsx +224 -0
- package/src/features/agent-graph/renderer/ui/TeamGraphTab.tsx +257 -0
- package/src/features/agent-graph/renderer/ui/buildTransientHandoffMessage.ts +70 -0
- package/src/features/recent-projects/contracts/api.ts +5 -0
- package/src/features/recent-projects/contracts/channels.ts +2 -0
- package/src/features/recent-projects/contracts/dto.ts +24 -0
- package/src/features/recent-projects/contracts/index.ts +4 -0
- package/src/features/recent-projects/contracts/normalize.ts +32 -0
- package/src/features/recent-projects/core/application/models/ListDashboardRecentProjectsResponse.ts +6 -0
- package/src/features/recent-projects/core/application/ports/ClockPort.ts +3 -0
- package/src/features/recent-projects/core/application/ports/ListDashboardRecentProjectsOutputPort.ts +5 -0
- package/src/features/recent-projects/core/application/ports/LoggerPort.ts +5 -0
- package/src/features/recent-projects/core/application/ports/RecentProjectsCachePort.ts +5 -0
- package/src/features/recent-projects/core/application/ports/RecentProjectsSourcePort.ts +14 -0
- package/src/features/recent-projects/core/application/use-cases/ListDashboardRecentProjectsUseCase.ts +191 -0
- package/src/features/recent-projects/core/domain/models/ProviderId.ts +1 -0
- package/src/features/recent-projects/core/domain/models/RecentProjectAggregate.ts +14 -0
- package/src/features/recent-projects/core/domain/models/RecentProjectCandidate.ts +14 -0
- package/src/features/recent-projects/core/domain/models/RecentProjectOpenTarget.ts +3 -0
- package/src/features/recent-projects/core/domain/policies/mergeRecentProjectCandidates.ts +88 -0
- package/src/features/recent-projects/main/adapters/input/http/registerRecentProjectsHttp.ts +30 -0
- package/src/features/recent-projects/main/adapters/output/presenters/DashboardRecentProjectsPresenter.ts +27 -0
- package/src/features/recent-projects/main/adapters/output/sources/ClaudeRecentProjectsSourceAdapter.ts +91 -0
- package/src/features/recent-projects/main/adapters/output/sources/CodexRecentProjectsSourceAdapter.ts +326 -0
- package/src/features/recent-projects/main/composition/createRecentProjectsFeature.ts +43 -0
- package/src/features/recent-projects/main/index.ts +3 -0
- package/src/features/recent-projects/main/infrastructure/cache/InMemoryRecentProjectsCache.ts +34 -0
- package/src/features/recent-projects/main/infrastructure/codex/CodexAppServerClient.ts +116 -0
- package/src/features/recent-projects/main/infrastructure/identity/RecentProjectIdentityResolver.ts +20 -0
- package/src/features/recent-projects/main/infrastructure/identity/normalizeIdentityPath.ts +10 -0
- package/src/features/recent-projects/renderer/adapters/RecentProjectsSectionAdapter.ts +132 -0
- package/src/features/recent-projects/renderer/hooks/useOpenRecentProject.ts +143 -0
- package/src/features/recent-projects/renderer/hooks/useRecentProjectsSection.ts +289 -0
- package/src/features/recent-projects/renderer/index.ts +2 -0
- package/src/features/recent-projects/renderer/ui/RecentProjectCard.tsx +221 -0
- package/src/features/recent-projects/renderer/ui/RecentProjectsSection.tsx +167 -0
- package/src/features/recent-projects/renderer/utils/activeProjectTeams.ts +48 -0
- package/src/features/recent-projects/renderer/utils/navigation.ts +65 -0
- package/src/features/recent-projects/renderer/utils/projectDecorations.ts +11 -0
- package/src/features/recent-projects/renderer/utils/recentProjectOpenHistory.ts +268 -0
- package/src/features/recent-projects/renderer/utils/recentProjectsClientCache.ts +78 -0
- package/src/main/constants/messageTags.ts +46 -0
- package/src/main/constants/worktreePatterns.ts +47 -0
- package/src/main/server.ts +3705 -0
- package/src/main/services/UpdateService.ts +166 -0
- package/src/main/services/ccConnect/CcConnectBridge.ts +313 -0
- package/src/main/services/ccConnect/CcConnectClient.ts +397 -0
- package/src/main/services/ccConnect/MessageBridge.ts +162 -0
- package/src/main/services/ccConnect/ProjectMappingStore.ts +148 -0
- package/src/main/services/ccConnect/index.ts +8 -0
- package/src/main/services/teams-mvp/TeamProvisioningService.ts +275 -0
- package/src/main/services/teams-mvp/TeamWorkspaceService.ts +404 -0
- package/src/main/services/teams-mvp/index.ts +26 -0
- package/src/main/types/chunks.ts +506 -0
- package/src/main/types/domain.ts +342 -0
- package/src/main/types/index.ts +24 -0
- package/src/main/types/jsonl.ts +355 -0
- package/src/main/types/messages.ts +395 -0
- package/src/renderer/App.tsx +287 -0
- package/src/renderer/api/httpClient.ts +2207 -0
- package/src/renderer/api/index.ts +19 -0
- package/src/renderer/api/providers.ts +77 -0
- package/src/renderer/assets/participant-avatars/01.png +0 -0
- package/src/renderer/assets/participant-avatars/02.png +0 -0
- package/src/renderer/assets/participant-avatars/03.png +0 -0
- package/src/renderer/assets/participant-avatars/04.png +0 -0
- package/src/renderer/assets/participant-avatars/05.png +0 -0
- package/src/renderer/assets/participant-avatars/06.png +0 -0
- package/src/renderer/assets/participant-avatars/07.png +0 -0
- package/src/renderer/assets/participant-avatars/08.png +0 -0
- package/src/renderer/assets/participant-avatars/09.png +0 -0
- package/src/renderer/assets/participant-avatars/10.png +0 -0
- package/src/renderer/assets/participant-avatars/11.png +0 -0
- package/src/renderer/assets/participant-avatars/12.png +0 -0
- package/src/renderer/assets/participant-avatars/13.png +0 -0
- package/src/renderer/components/chat/AIChatGroup.tsx +519 -0
- package/src/renderer/components/chat/ChatHistory.tsx +1115 -0
- package/src/renderer/components/chat/ChatHistoryEmptyState.tsx +15 -0
- package/src/renderer/components/chat/ChatHistoryItem.tsx +144 -0
- package/src/renderer/components/chat/ChatHistoryLoadingState.tsx +45 -0
- package/src/renderer/components/chat/CompactBoundary.tsx +169 -0
- package/src/renderer/components/chat/ContextBadge.tsx +582 -0
- package/src/renderer/components/chat/DisplayItemList.tsx +431 -0
- package/src/renderer/components/chat/LastOutputDisplay.tsx +259 -0
- package/src/renderer/components/chat/SessionContextPanel/DirectoryTree/DirectoryTreeNode.tsx +125 -0
- package/src/renderer/components/chat/SessionContextPanel/DirectoryTree/buildDirectoryTree.ts +47 -0
- package/src/renderer/components/chat/SessionContextPanel/DirectoryTree/types.ts +12 -0
- package/src/renderer/components/chat/SessionContextPanel/components/ClaudeMdFilesSection.tsx +90 -0
- package/src/renderer/components/chat/SessionContextPanel/components/ClaudeMdSection.tsx +86 -0
- package/src/renderer/components/chat/SessionContextPanel/components/CollapsibleSection.tsx +77 -0
- package/src/renderer/components/chat/SessionContextPanel/components/FlatInjectionList.tsx +248 -0
- package/src/renderer/components/chat/SessionContextPanel/components/MentionedFilesSection.tsx +50 -0
- package/src/renderer/components/chat/SessionContextPanel/components/RankedInjectionList.tsx +284 -0
- package/src/renderer/components/chat/SessionContextPanel/components/SessionContextHeader.tsx +290 -0
- package/src/renderer/components/chat/SessionContextPanel/components/SessionContextHelpTooltip.tsx +165 -0
- package/src/renderer/components/chat/SessionContextPanel/components/TaskCoordinationSection.tsx +47 -0
- package/src/renderer/components/chat/SessionContextPanel/components/ThinkingTextSection.tsx +47 -0
- package/src/renderer/components/chat/SessionContextPanel/components/ToolOutputsSection.tsx +47 -0
- package/src/renderer/components/chat/SessionContextPanel/components/UserMessagesSection.tsx +47 -0
- package/src/renderer/components/chat/SessionContextPanel/index.tsx +314 -0
- package/src/renderer/components/chat/SessionContextPanel/items/ClaudeMdItem.tsx +76 -0
- package/src/renderer/components/chat/SessionContextPanel/items/MentionedFileItem.tsx +91 -0
- package/src/renderer/components/chat/SessionContextPanel/items/TaskCoordinationItem.tsx +116 -0
- package/src/renderer/components/chat/SessionContextPanel/items/ThinkingTextItem.tsx +96 -0
- package/src/renderer/components/chat/SessionContextPanel/items/ToolBreakdownItem.tsx +38 -0
- package/src/renderer/components/chat/SessionContextPanel/items/ToolOutputItem.tsx +113 -0
- package/src/renderer/components/chat/SessionContextPanel/items/UserMessageItem.tsx +69 -0
- package/src/renderer/components/chat/SessionContextPanel/types.ts +96 -0
- package/src/renderer/components/chat/SessionContextPanel/utils/formatting.ts +6 -0
- package/src/renderer/components/chat/SessionContextPanel/utils/pathParsing.ts +23 -0
- package/src/renderer/components/chat/SystemChatGroup.tsx +60 -0
- package/src/renderer/components/chat/UserChatGroup.tsx +668 -0
- package/src/renderer/components/chat/items/BaseItem.tsx +213 -0
- package/src/renderer/components/chat/items/ExecutionTrace.tsx +279 -0
- package/src/renderer/components/chat/items/LinkedToolItem.tsx +235 -0
- package/src/renderer/components/chat/items/MetricsPill.tsx +215 -0
- package/src/renderer/components/chat/items/SlashItem.tsx +81 -0
- package/src/renderer/components/chat/items/SubagentItem.tsx +592 -0
- package/src/renderer/components/chat/items/TeammateMessageItem.tsx +261 -0
- package/src/renderer/components/chat/items/TextItem.tsx +82 -0
- package/src/renderer/components/chat/items/ThinkingItem.tsx +82 -0
- package/src/renderer/components/chat/items/baseItemHelpers.ts +42 -0
- package/src/renderer/components/chat/items/linkedTool/CollapsibleOutputSection.tsx +57 -0
- package/src/renderer/components/chat/items/linkedTool/DefaultToolViewer.tsx +56 -0
- package/src/renderer/components/chat/items/linkedTool/EditToolViewer.tsx +74 -0
- package/src/renderer/components/chat/items/linkedTool/ReadToolViewer.tsx +102 -0
- package/src/renderer/components/chat/items/linkedTool/SkillToolViewer.tsx +67 -0
- package/src/renderer/components/chat/items/linkedTool/ToolErrorDisplay.tsx +43 -0
- package/src/renderer/components/chat/items/linkedTool/WriteToolViewer.tsx +66 -0
- package/src/renderer/components/chat/items/linkedTool/index.ts +13 -0
- package/src/renderer/components/chat/items/linkedTool/renderHelpers.tsx +259 -0
- package/src/renderer/components/chat/markdownComponents.tsx +257 -0
- package/src/renderer/components/chat/markdownCopyUtils.ts +18 -0
- package/src/renderer/components/chat/searchHighlightUtils.ts +166 -0
- package/src/renderer/components/chat/viewers/CodeBlockViewer.tsx +244 -0
- package/src/renderer/components/chat/viewers/DiffViewer.tsx +459 -0
- package/src/renderer/components/chat/viewers/FileLink.tsx +182 -0
- package/src/renderer/components/chat/viewers/MarkdownViewer.tsx +1093 -0
- package/src/renderer/components/chat/viewers/MermaidDiagram.tsx +116 -0
- package/src/renderer/components/chat/viewers/index.ts +3 -0
- package/src/renderer/components/chat/viewers/syntaxHighlighter.ts +583 -0
- package/src/renderer/components/common/AppLogo.tsx +61 -0
- package/src/renderer/components/common/CliInstallWarningBanner.tsx +57 -0
- package/src/renderer/components/common/ConfirmDialog.tsx +176 -0
- package/src/renderer/components/common/ConnectionStatusBadge.tsx +56 -0
- package/src/renderer/components/common/ContextSwitchOverlay.tsx +38 -0
- package/src/renderer/components/common/CopyButton.tsx +85 -0
- package/src/renderer/components/common/CopyablePath.tsx +68 -0
- package/src/renderer/components/common/ErrorBoundary.tsx +211 -0
- package/src/renderer/components/common/ExportDropdown.tsx +142 -0
- package/src/renderer/components/common/FileTree.tsx +182 -0
- package/src/renderer/components/common/GlobalProviderStatusHeader.tsx +316 -0
- package/src/renderer/components/common/OngoingIndicator.tsx +67 -0
- package/src/renderer/components/common/ProviderBrandLogo.tsx +206 -0
- package/src/renderer/components/common/RepositoryDropdown.tsx +230 -0
- package/src/renderer/components/common/TokenUsageDisplay.tsx +581 -0
- package/src/renderer/components/common/WarningBanner.tsx +25 -0
- package/src/renderer/components/common/WorkspaceIndicator.tsx +183 -0
- package/src/renderer/components/common/WorktreeBadge.tsx +123 -0
- package/src/renderer/components/dashboard/CliStatusBanner.tsx +1845 -0
- package/src/renderer/components/dashboard/DashboardView.tsx +274 -0
- package/src/renderer/components/extensions/ExtensionStoreView.tsx +591 -0
- package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +52 -0
- package/src/renderer/components/extensions/apikeys/ApiKeyCard.tsx +143 -0
- package/src/renderer/components/extensions/apikeys/ApiKeyFormDialog.tsx +282 -0
- package/src/renderer/components/extensions/apikeys/ApiKeysPanel.tsx +280 -0
- package/src/renderer/components/extensions/common/InstallButton.tsx +186 -0
- package/src/renderer/components/extensions/common/InstallCountBadge.tsx +21 -0
- package/src/renderer/components/extensions/common/SearchInput.tsx +70 -0
- package/src/renderer/components/extensions/common/SourceBadge.tsx +31 -0
- package/src/renderer/components/extensions/mcp/CustomMcpServerDialog.tsx +560 -0
- package/src/renderer/components/extensions/mcp/McpServerCard.tsx +314 -0
- package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +669 -0
- package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +543 -0
- package/src/renderer/components/extensions/plugins/CapabilityChips.tsx +70 -0
- package/src/renderer/components/extensions/plugins/CategoryChips.tsx +67 -0
- package/src/renderer/components/extensions/plugins/PluginCard.tsx +146 -0
- package/src/renderer/components/extensions/plugins/PluginDetailDialog.tsx +270 -0
- package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +436 -0
- package/src/renderer/components/extensions/skills/SkillCodeEditor.tsx +117 -0
- package/src/renderer/components/extensions/skills/SkillDetailDialog.tsx +372 -0
- package/src/renderer/components/extensions/skills/SkillEditorDialog.tsx +856 -0
- package/src/renderer/components/extensions/skills/SkillImportDialog.tsx +343 -0
- package/src/renderer/components/extensions/skills/SkillReviewDialog.tsx +166 -0
- package/src/renderer/components/extensions/skills/SkillsPanel.tsx +784 -0
- package/src/renderer/components/extensions/skills/skillDraftUtils.ts +234 -0
- package/src/renderer/components/extensions/skills/skillFolderNameUtils.ts +19 -0
- package/src/renderer/components/extensions/skills/skillProjectUtils.ts +13 -0
- package/src/renderer/components/extensions/skills/skillValidationUtils.ts +31 -0
- package/src/renderer/components/layout/MiddlePanel.tsx +18 -0
- package/src/renderer/components/layout/MoreMenu.tsx +243 -0
- package/src/renderer/components/layout/PaneContainer.tsx +27 -0
- package/src/renderer/components/layout/PaneContent.tsx +84 -0
- package/src/renderer/components/layout/PaneResizeHandle.tsx +85 -0
- package/src/renderer/components/layout/PaneSplitDropZone.tsx +54 -0
- package/src/renderer/components/layout/PaneView.tsx +75 -0
- package/src/renderer/components/layout/SessionTabContent.tsx +102 -0
- package/src/renderer/components/layout/Sidebar.tsx +205 -0
- package/src/renderer/components/layout/SortableTab.tsx +261 -0
- package/src/renderer/components/layout/TabBar.tsx +354 -0
- package/src/renderer/components/layout/TabBarActions.tsx +176 -0
- package/src/renderer/components/layout/TabBarRow.tsx +99 -0
- package/src/renderer/components/layout/TabContextMenu.tsx +171 -0
- package/src/renderer/components/layout/TabbedLayout.tsx +186 -0
- package/src/renderer/components/layout/TeamTabSectionNav.tsx +146 -0
- package/src/renderer/components/notifications/NotificationRow.tsx +228 -0
- package/src/renderer/components/notifications/NotificationsView.tsx +371 -0
- package/src/renderer/components/report/AssessmentBadge.tsx +78 -0
- package/src/renderer/components/report/ReportSection.tsx +58 -0
- package/src/renderer/components/report/SessionReportTab.tsx +102 -0
- package/src/renderer/components/report/sections/CostSection.tsx +259 -0
- package/src/renderer/components/report/sections/ErrorSection.tsx +100 -0
- package/src/renderer/components/report/sections/FrictionSection.tsx +91 -0
- package/src/renderer/components/report/sections/GitSection.tsx +72 -0
- package/src/renderer/components/report/sections/InsightsSection.tsx +207 -0
- package/src/renderer/components/report/sections/KeyTakeawaysSection.tsx +55 -0
- package/src/renderer/components/report/sections/OverviewSection.tsx +64 -0
- package/src/renderer/components/report/sections/QualitySection.tsx +151 -0
- package/src/renderer/components/report/sections/SubagentSection.tsx +88 -0
- package/src/renderer/components/report/sections/TimelineSection.tsx +111 -0
- package/src/renderer/components/report/sections/TokenSection.tsx +116 -0
- package/src/renderer/components/report/sections/ToolSection.tsx +77 -0
- package/src/renderer/components/runtime/ProviderModelBadges.tsx +142 -0
- package/src/renderer/components/runtime/ProviderRuntimeBackendSelector.tsx +327 -0
- package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +288 -0
- package/src/renderer/components/runtime/providerConnectionUi.ts +408 -0
- package/src/renderer/components/schedules/SchedulesView.tsx +529 -0
- package/src/renderer/components/search/CommandPalette.tsx +610 -0
- package/src/renderer/components/search/SearchBar.tsx +171 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/AddTriggerForm.tsx +233 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/ColorPaletteSelector.tsx +144 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/DynamicConfigSection.tsx +189 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/GeneralInfoSection.tsx +68 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/IgnorePatternsSection.tsx +73 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/ModeSelector.tsx +45 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/RepositoryScopeSection.tsx +63 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/SectionHeader.tsx +15 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerCard.tsx +150 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerCardHeader.tsx +125 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerConfiguration.tsx +342 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerPreview.tsx +108 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useAddTriggerFormHandlers.ts +218 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useAddTriggerFormState.ts +135 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useRepositoryLookup.ts +47 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useTriggerCardState.ts +281 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useTriggerForm.ts +185 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/index.tsx +87 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/types.ts +39 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/utils/constants.ts +50 -0
- package/src/renderer/components/settings/NotificationTriggerSettings/utils/trigger.ts +113 -0
- package/src/renderer/components/settings/SettingsTabs.tsx +110 -0
- package/src/renderer/components/settings/SettingsView.tsx +153 -0
- package/src/renderer/components/settings/components/SettingRow.tsx +44 -0
- package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +24 -0
- package/src/renderer/components/settings/components/SettingsSelect.tsx +100 -0
- package/src/renderer/components/settings/components/SettingsToggle.tsx +45 -0
- package/src/renderer/components/settings/components/index.ts +8 -0
- package/src/renderer/components/settings/hooks/index.ts +6 -0
- package/src/renderer/components/settings/hooks/useSettingsConfig.ts +270 -0
- package/src/renderer/components/settings/hooks/useSettingsHandlers.ts +468 -0
- package/src/renderer/components/settings/sections/AdvancedSection.tsx +234 -0
- package/src/renderer/components/settings/sections/CliStatusSection.tsx +930 -0
- package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +391 -0
- package/src/renderer/components/settings/sections/GeneralSection.tsx +665 -0
- package/src/renderer/components/settings/sections/HarnessSection.tsx +133 -0
- package/src/renderer/components/settings/sections/PlatformsSection.tsx +517 -0
- package/src/renderer/components/settings/sections/index.ts +8 -0
- package/src/renderer/components/sidebar/DateGroupedSessions.tsx +1115 -0
- package/src/renderer/components/sidebar/GlobalTaskList.tsx +853 -0
- package/src/renderer/components/sidebar/SessionContextMenu.tsx +182 -0
- package/src/renderer/components/sidebar/SessionFiltersPopover.tsx +115 -0
- package/src/renderer/components/sidebar/SessionItem.tsx +393 -0
- package/src/renderer/components/sidebar/SidebarSessions.tsx +542 -0
- package/src/renderer/components/sidebar/SidebarTaskItem.tsx +286 -0
- package/src/renderer/components/sidebar/TaskContextMenu.tsx +86 -0
- package/src/renderer/components/sidebar/TaskFiltersPopover.tsx +203 -0
- package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +370 -0
- package/src/renderer/components/sidebar/dateGroupedSessionsSelection.ts +33 -0
- package/src/renderer/components/sidebar/projectGroupPagination.ts +89 -0
- package/src/renderer/components/sidebar/taskFiltersState.ts +82 -0
- package/src/renderer/components/splash/splashScene.ts +979 -0
- package/src/renderer/components/team/CcSessionsSection.tsx +202 -0
- package/src/renderer/components/team/ClaudeLogsDialog.tsx +71 -0
- package/src/renderer/components/team/ClaudeLogsFilterPopover.tsx +213 -0
- package/src/renderer/components/team/ClaudeLogsPanel.tsx +170 -0
- package/src/renderer/components/team/ClaudeLogsSection.tsx +154 -0
- package/src/renderer/components/team/CliLogsRichView.tsx +640 -0
- package/src/renderer/components/team/CollapsibleTeamSection.tsx +177 -0
- package/src/renderer/components/team/HarnessCards.ts +38 -0
- package/src/renderer/components/team/MemberBadge.tsx +117 -0
- package/src/renderer/components/team/ProcessesSection.tsx +193 -0
- package/src/renderer/components/team/ProvisioningProgressBlock.tsx +389 -0
- package/src/renderer/components/team/RoleSelect.tsx +171 -0
- package/src/renderer/components/team/StepProgressBar.tsx +165 -0
- package/src/renderer/components/team/TaskTooltip.tsx +197 -0
- package/src/renderer/components/team/TeamDetailView.tsx +3002 -0
- package/src/renderer/components/team/TeamEmptyState.tsx +102 -0
- package/src/renderer/components/team/TeamListFilterPopover.tsx +183 -0
- package/src/renderer/components/team/TeamListView.tsx +1336 -0
- package/src/renderer/components/team/TeamProvisioningBanner.tsx +16 -0
- package/src/renderer/components/team/TeamProvisioningPanel.tsx +115 -0
- package/src/renderer/components/team/TeamSessionsSection.tsx +267 -0
- package/src/renderer/components/team/ToolApprovalDiffPreview.tsx +206 -0
- package/src/renderer/components/team/ToolApprovalSheet.tsx +675 -0
- package/src/renderer/components/team/UnreadCommentsBadge.tsx +37 -0
- package/src/renderer/components/team/activity/ActiveTasksBlock.tsx +191 -0
- package/src/renderer/components/team/activity/ActivityItem.tsx +1649 -0
- package/src/renderer/components/team/activity/ActivityTimeline.tsx +959 -0
- package/src/renderer/components/team/activity/AnimatedHeightReveal.tsx +117 -0
- package/src/renderer/components/team/activity/LeadThoughtsGroup.tsx +1152 -0
- package/src/renderer/components/team/activity/MessageExpandDialog.tsx +213 -0
- package/src/renderer/components/team/activity/PendingRepliesBlock.tsx +275 -0
- package/src/renderer/components/team/activity/ReplyQuoteBlock.tsx +79 -0
- package/src/renderer/components/team/activity/ThoughtBodyContent.tsx +150 -0
- package/src/renderer/components/team/activity/activityMarkdown.ts +36 -0
- package/src/renderer/components/team/activity/activityMessageContext.ts +68 -0
- package/src/renderer/components/team/activity/collapseState.ts +66 -0
- package/src/renderer/components/team/activity/useNewItemKeys.ts +70 -0
- package/src/renderer/components/team/attachments/AttachmentDisplay.tsx +132 -0
- package/src/renderer/components/team/attachments/AttachmentPreviewItem.tsx +62 -0
- package/src/renderer/components/team/attachments/AttachmentPreviewList.tsx +193 -0
- package/src/renderer/components/team/attachments/AttachmentThumbnail.tsx +42 -0
- package/src/renderer/components/team/attachments/DropZoneOverlay.tsx +54 -0
- package/src/renderer/components/team/attachments/ImageLightbox.tsx +132 -0
- package/src/renderer/components/team/attachments/SourceMessageAttachments.tsx +70 -0
- package/src/renderer/components/team/dialogs/AddMemberDialog.tsx +222 -0
- package/src/renderer/components/team/dialogs/AdvancedCliSection.tsx +347 -0
- package/src/renderer/components/team/dialogs/AnthropicFastModeSelector.tsx +120 -0
- package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +489 -0
- package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +484 -0
- package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +448 -0
- package/src/renderer/components/team/dialogs/EffortLevelSelector.tsx +69 -0
- package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +165 -0
- package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +2859 -0
- package/src/renderer/components/team/dialogs/LimitContextCheckbox.tsx +57 -0
- package/src/renderer/components/team/dialogs/MembersJsonEditor.tsx +123 -0
- package/src/renderer/components/team/dialogs/OptionalSettingsSection.tsx +124 -0
- package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +145 -0
- package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +289 -0
- package/src/renderer/components/team/dialogs/ProjectPathSelector.tsx +330 -0
- package/src/renderer/components/team/dialogs/ProvisioningProviderStatusList.tsx +744 -0
- package/src/renderer/components/team/dialogs/ReviewDialog.tsx +130 -0
- package/src/renderer/components/team/dialogs/SendMessageDialog.tsx +530 -0
- package/src/renderer/components/team/dialogs/SkipPermissionsCheckbox.tsx +62 -0
- package/src/renderer/components/team/dialogs/StatusHistoryTimeline.tsx +229 -0
- package/src/renderer/components/team/dialogs/TaskAttachments.tsx +394 -0
- package/src/renderer/components/team/dialogs/TaskCommentAwaitingReply.tsx +57 -0
- package/src/renderer/components/team/dialogs/TaskCommentInput.tsx +420 -0
- package/src/renderer/components/team/dialogs/TaskCommentsSection.tsx +620 -0
- package/src/renderer/components/team/dialogs/TaskDetailDialog.tsx +1480 -0
- package/src/renderer/components/team/dialogs/TeamModelSelector.tsx +560 -0
- package/src/renderer/components/team/dialogs/TeammateRuntimeCompatibilityNotice.tsx +60 -0
- package/src/renderer/components/team/dialogs/ToolApprovalSettingsPanel.tsx +173 -0
- package/src/renderer/components/team/dialogs/editTeamRuntimeChanges.ts +179 -0
- package/src/renderer/components/team/dialogs/globalTaskDetailDialogLoading.ts +34 -0
- package/src/renderer/components/team/dialogs/launchDialogPrefill.ts +125 -0
- package/src/renderer/components/team/dialogs/memberModelScope.ts +77 -0
- package/src/renderer/components/team/dialogs/platformMeta.ts +118 -0
- package/src/renderer/components/team/dialogs/projectPathOptions.ts +50 -0
- package/src/renderer/components/team/dialogs/providerPrepareDiagnostics.ts +222 -0
- package/src/renderer/components/team/dialogs/providerPrepareRequestSignature.ts +122 -0
- package/src/renderer/components/team/dialogs/provisioningMemberScope.ts +10 -0
- package/src/renderer/components/team/dialogs/provisioningModelIssues.ts +124 -0
- package/src/renderer/components/team/dialogs/teamNameSets.ts +67 -0
- package/src/renderer/components/team/dialogs/teamRelaunchFlow.ts +30 -0
- package/src/renderer/components/team/dialogs/teammateLaunchMode.ts +49 -0
- package/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx +101 -0
- package/src/renderer/components/team/editor/CodeMirrorEditor.tsx +506 -0
- package/src/renderer/components/team/editor/EditorBinaryPlaceholder.tsx +43 -0
- package/src/renderer/components/team/editor/EditorBinaryState.tsx +29 -0
- package/src/renderer/components/team/editor/EditorBreadcrumb.tsx +84 -0
- package/src/renderer/components/team/editor/EditorContextMenu.tsx +213 -0
- package/src/renderer/components/team/editor/EditorEmptyState.tsx +35 -0
- package/src/renderer/components/team/editor/EditorErrorBoundary.tsx +63 -0
- package/src/renderer/components/team/editor/EditorErrorState.tsx +41 -0
- package/src/renderer/components/team/editor/EditorFileTree.tsx +903 -0
- package/src/renderer/components/team/editor/EditorImagePreview.tsx +138 -0
- package/src/renderer/components/team/editor/EditorSearchPanel.tsx +508 -0
- package/src/renderer/components/team/editor/EditorSelectionMenu.tsx +112 -0
- package/src/renderer/components/team/editor/EditorShortcutsHelp.tsx +125 -0
- package/src/renderer/components/team/editor/EditorStatusBar.tsx +72 -0
- package/src/renderer/components/team/editor/EditorTabBar.tsx +265 -0
- package/src/renderer/components/team/editor/EditorTabContextMenu.tsx +88 -0
- package/src/renderer/components/team/editor/EditorToolbar.tsx +163 -0
- package/src/renderer/components/team/editor/FileIcon.tsx +66 -0
- package/src/renderer/components/team/editor/GitStatusBadge.tsx +47 -0
- package/src/renderer/components/team/editor/GoToLineDialog.tsx +186 -0
- package/src/renderer/components/team/editor/MarkdownPreviewPane.tsx +57 -0
- package/src/renderer/components/team/editor/MarkdownSplitView.tsx +127 -0
- package/src/renderer/components/team/editor/NewFileDialog.tsx +131 -0
- package/src/renderer/components/team/editor/ProjectEditorOverlay.tsx +924 -0
- package/src/renderer/components/team/editor/QuickOpenDialog.tsx +163 -0
- package/src/renderer/components/team/editor/SearchInFilesPanel.tsx +358 -0
- package/src/renderer/components/team/editor/fileIcons.ts +222 -0
- package/src/renderer/components/team/kanban/KanbanBoard.tsx +664 -0
- package/src/renderer/components/team/kanban/KanbanColumn.tsx +61 -0
- package/src/renderer/components/team/kanban/KanbanFilterPopover.tsx +210 -0
- package/src/renderer/components/team/kanban/KanbanGridLayout.tsx +460 -0
- package/src/renderer/components/team/kanban/KanbanSearchInput.tsx +284 -0
- package/src/renderer/components/team/kanban/KanbanSortPopover.tsx +140 -0
- package/src/renderer/components/team/kanban/KanbanTaskCard.test.tsx +199 -0
- package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +446 -0
- package/src/renderer/components/team/kanban/TrashDialog.tsx +112 -0
- package/src/renderer/components/team/members/CurrentTaskIndicator.tsx +56 -0
- package/src/renderer/components/team/members/LeadModelRow.test.tsx +133 -0
- package/src/renderer/components/team/members/LeadModelRow.tsx +183 -0
- package/src/renderer/components/team/members/MemberCard.tsx +665 -0
- package/src/renderer/components/team/members/MemberDetailDialog.tsx +309 -0
- package/src/renderer/components/team/members/MemberDetailHeader.tsx +183 -0
- package/src/renderer/components/team/members/MemberDetailStats.tsx +80 -0
- package/src/renderer/components/team/members/MemberDraftRow.test.tsx +184 -0
- package/src/renderer/components/team/members/MemberDraftRow.tsx +515 -0
- package/src/renderer/components/team/members/MemberExecutionLog.tsx +224 -0
- package/src/renderer/components/team/members/MemberHoverCard.tsx +292 -0
- package/src/renderer/components/team/members/MemberLaunchDiagnosticsButton.tsx +60 -0
- package/src/renderer/components/team/members/MemberList.tsx +405 -0
- package/src/renderer/components/team/members/MemberLogsTab.tsx +958 -0
- package/src/renderer/components/team/members/MemberMessagesTab.tsx +251 -0
- package/src/renderer/components/team/members/MemberPresenceDot.tsx +28 -0
- package/src/renderer/components/team/members/MemberRoleEditor.tsx +84 -0
- package/src/renderer/components/team/members/MemberStatsTab.tsx +299 -0
- package/src/renderer/components/team/members/MemberTasksTab.tsx +87 -0
- package/src/renderer/components/team/members/MemberWorkspaceTab.tsx +141 -0
- package/src/renderer/components/team/members/MembersEditorSection.tsx +495 -0
- package/src/renderer/components/team/members/SubagentRecentMessagesPreview.tsx +125 -0
- package/src/renderer/components/team/members/TeamRosterEditorSection.tsx +153 -0
- package/src/renderer/components/team/members/memberActivityEntries.ts +42 -0
- package/src/renderer/components/team/members/memberDetailTypes.ts +3 -0
- package/src/renderer/components/team/members/memberNameSets.ts +65 -0
- package/src/renderer/components/team/members/membersEditorTypes.ts +21 -0
- package/src/renderer/components/team/members/membersEditorUtils.ts +267 -0
- package/src/renderer/components/team/messages/MessageComposer.tsx +939 -0
- package/src/renderer/components/team/messages/MessagesFilterPopover.tsx +228 -0
- package/src/renderer/components/team/messages/MessagesPanel.tsx +1508 -0
- package/src/renderer/components/team/messages/OpenCodeDeliveryWarning.tsx +151 -0
- package/src/renderer/components/team/messages/StatusBlock.tsx +126 -0
- package/src/renderer/components/team/provisioningSteps.ts +363 -0
- package/src/renderer/components/team/review/ChangeReviewDialog.tsx +133 -0
- package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +218 -0
- package/src/renderer/components/team/schedule/CronScheduleInput.tsx +254 -0
- package/src/renderer/components/team/schedule/ScheduleEmptyState.tsx +15 -0
- package/src/renderer/components/team/schedule/ScheduleRunLogDialog.tsx +277 -0
- package/src/renderer/components/team/schedule/ScheduleRunRow.tsx +106 -0
- package/src/renderer/components/team/schedule/ScheduleSection.tsx +281 -0
- package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +55 -0
- package/src/renderer/components/team/sidebar/TeamSidebarHost.tsx +76 -0
- package/src/renderer/components/team/sidebar/TeamSidebarPortalManager.ts +173 -0
- package/src/renderer/components/team/sidebar/TeamSidebarPortalSource.tsx +66 -0
- package/src/renderer/components/team/sidebar/TeamSidebarRail.tsx +68 -0
- package/src/renderer/components/team/sidebar/teamSidebarUiState.ts +136 -0
- package/src/renderer/components/team/taskLogs/ExactTaskLogCard.tsx +132 -0
- package/src/renderer/components/team/taskLogs/ExactTaskLogsSection.tsx +258 -0
- package/src/renderer/components/team/taskLogs/ExecutionSessionsSection.tsx +48 -0
- package/src/renderer/components/team/taskLogs/TaskActivityLinkedToolCard.tsx +31 -0
- package/src/renderer/components/team/taskLogs/TaskActivitySection.tsx +462 -0
- package/src/renderer/components/team/taskLogs/TaskLogStreamSection.tsx +375 -0
- package/src/renderer/components/team/taskLogs/TaskLogsPanel.tsx +294 -0
- package/src/renderer/components/team/taskLogs/featureGates.ts +22 -0
- package/src/renderer/components/team/tasks/TaskList.tsx +111 -0
- package/src/renderer/components/team/tasks/TaskRow.tsx +65 -0
- package/src/renderer/components/team/teamProjectSelection.ts +156 -0
- package/src/renderer/components/team/teamSessionFetchGuards.ts +26 -0
- package/src/renderer/components/team/useClaudeLogsController.ts +668 -0
- package/src/renderer/components/team/useTeamProvisioningPresentation.ts +54 -0
- package/src/renderer/components/terminal/TerminalLogPanel.tsx +37 -0
- package/src/renderer/components/ui/ChipInteractionLayer.tsx +255 -0
- package/src/renderer/components/ui/CodeChipBadge.tsx +37 -0
- package/src/renderer/components/ui/ExpandableContent.tsx +110 -0
- package/src/renderer/components/ui/MemberSelect.tsx +209 -0
- package/src/renderer/components/ui/MentionInteractionLayer.tsx +121 -0
- package/src/renderer/components/ui/MentionSuggestionList.tsx +274 -0
- package/src/renderer/components/ui/MentionableTextarea.tsx +1426 -0
- package/src/renderer/components/ui/SlashCommandInteractionLayer.tsx +88 -0
- package/src/renderer/components/ui/TaskReferenceInteractionLayer.tsx +101 -0
- package/src/renderer/components/ui/UrlInteractionLayer.tsx +102 -0
- package/src/renderer/components/ui/alert-dialog.tsx +127 -0
- package/src/renderer/components/ui/auto-resize-textarea.tsx +93 -0
- package/src/renderer/components/ui/badge.tsx +37 -0
- package/src/renderer/components/ui/button.tsx +54 -0
- package/src/renderer/components/ui/checkbox.tsx +29 -0
- package/src/renderer/components/ui/combobox.tsx +168 -0
- package/src/renderer/components/ui/context-menu.tsx +124 -0
- package/src/renderer/components/ui/dialog.tsx +114 -0
- package/src/renderer/components/ui/hover-card.tsx +30 -0
- package/src/renderer/components/ui/input.tsx +22 -0
- package/src/renderer/components/ui/label.tsx +21 -0
- package/src/renderer/components/ui/popover.tsx +31 -0
- package/src/renderer/components/ui/select.tsx +150 -0
- package/src/renderer/components/ui/tabs.tsx +52 -0
- package/src/renderer/components/ui/textarea.tsx +21 -0
- package/src/renderer/components/ui/tiptap/TiptapBubbleMenu.tsx +75 -0
- package/src/renderer/components/ui/tiptap/TiptapEditor.tsx +73 -0
- package/src/renderer/components/ui/tiptap/TiptapToolbar.tsx +269 -0
- package/src/renderer/components/ui/tiptap/index.ts +3 -0
- package/src/renderer/components/ui/tiptap/presets.ts +46 -0
- package/src/renderer/components/ui/tiptap/tiptapStyles.css +235 -0
- package/src/renderer/components/ui/tiptap/types.ts +32 -0
- package/src/renderer/components/ui/tiptap/useTiptapEditor.ts +94 -0
- package/src/renderer/components/ui/tooltip.tsx +32 -0
- package/src/renderer/constants/cssVariables.ts +226 -0
- package/src/renderer/constants/layout.ts +6 -0
- package/src/renderer/constants/teamColors.ts +397 -0
- package/src/renderer/constants/teamRoles.ts +41 -0
- package/src/renderer/contexts/TabUIContext.tsx +51 -0
- package/src/renderer/contexts/useTabUIContext.ts +18 -0
- package/src/renderer/favicon.png +0 -0
- package/src/renderer/features/CLAUDE.md +19 -0
- package/src/renderer/hooks/navigation/utils.ts +263 -0
- package/src/renderer/hooks/useAttachments.ts +312 -0
- package/src/renderer/hooks/useAutoScrollBottom.ts +285 -0
- package/src/renderer/hooks/useBranchSync.ts +105 -0
- package/src/renderer/hooks/useChipDraftPersistence.ts +172 -0
- package/src/renderer/hooks/useCliInstaller.ts +106 -0
- package/src/renderer/hooks/useCollapsedGroups.ts +71 -0
- package/src/renderer/hooks/useComposerDraft.ts +504 -0
- package/src/renderer/hooks/useContinuousScrollNav.ts +50 -0
- package/src/renderer/hooks/useCreateTeamDraft.ts +280 -0
- package/src/renderer/hooks/useDraftPersistence.ts +140 -0
- package/src/renderer/hooks/useEditorKeyboardShortcuts.ts +257 -0
- package/src/renderer/hooks/useEffectiveCliProviderStatus.ts +66 -0
- package/src/renderer/hooks/useExtensionsTabState.ts +206 -0
- package/src/renderer/hooks/useFileListCacheWarmer.ts +38 -0
- package/src/renderer/hooks/useFileSuggestions.ts +255 -0
- package/src/renderer/hooks/useKeyboardShortcuts.ts +363 -0
- package/src/renderer/hooks/useLazyFileContent.ts +150 -0
- package/src/renderer/hooks/useMarkCommentsRead.ts +27 -0
- package/src/renderer/hooks/useMarkdownScrollSync.ts +158 -0
- package/src/renderer/hooks/useMemberStats.ts +45 -0
- package/src/renderer/hooks/useMentionDetection.ts +375 -0
- package/src/renderer/hooks/usePersistedGridLayout.ts +109 -0
- package/src/renderer/hooks/useResizableColumns.ts +140 -0
- package/src/renderer/hooks/useResizablePanel.ts +144 -0
- package/src/renderer/hooks/useStableTeamMentionMeta.ts +68 -0
- package/src/renderer/hooks/useSyncedAnimationStyle.ts +29 -0
- package/src/renderer/hooks/useTabNavigationController.ts +524 -0
- package/src/renderer/hooks/useTabUI.ts +252 -0
- package/src/renderer/hooks/useTaskLocalState.ts +163 -0
- package/src/renderer/hooks/useTaskSuggestions.ts +131 -0
- package/src/renderer/hooks/useTeamMessagesExpanded.ts +34 -0
- package/src/renderer/hooks/useTeamMessagesRead.ts +52 -0
- package/src/renderer/hooks/useTeamSuggestions.ts +78 -0
- package/src/renderer/hooks/useTheme.ts +138 -0
- package/src/renderer/hooks/useToolApprovalDiff.ts +212 -0
- package/src/renderer/hooks/useUnreadCommentCount.ts +14 -0
- package/src/renderer/hooks/useViewedFiles.ts +74 -0
- package/src/renderer/hooks/useViewportCommentRead.ts +147 -0
- package/src/renderer/hooks/useViewportObserver.ts +138 -0
- package/src/renderer/hooks/useVisibleAIGroup.ts +122 -0
- package/src/renderer/hooks/useVisibleFileSection.ts +114 -0
- package/src/renderer/hooks/useZoomFactor.ts +36 -0
- package/src/renderer/index.css +1560 -0
- package/src/renderer/index.html +1293 -0
- package/src/renderer/lib/utils.ts +6 -0
- package/src/renderer/main.tsx +30 -0
- package/src/renderer/sentry.ts +104 -0
- package/src/renderer/services/__tests__/createTeamPreferences.test.ts +67 -0
- package/src/renderer/services/commentReadStorage.ts +349 -0
- package/src/renderer/services/composerDraftStorage.ts +271 -0
- package/src/renderer/services/contextStorage.ts +201 -0
- package/src/renderer/services/createTeamDraftStorage.ts +151 -0
- package/src/renderer/services/createTeamPreferences.ts +361 -0
- package/src/renderer/services/dashboardCliStatusBannerPreference.ts +20 -0
- package/src/renderer/services/draftStorage.ts +128 -0
- package/src/renderer/services/layout-system/BrowserGridLayoutRepository.ts +111 -0
- package/src/renderer/services/layout-system/GridLayoutRepository.ts +8 -0
- package/src/renderer/services/layout-system/gridLayoutSchema.ts +137 -0
- package/src/renderer/services/layout-system/gridLayoutTypes.ts +17 -0
- package/src/renderer/store/index.ts +1556 -0
- package/src/renderer/store/slices/changeReviewSlice.ts +1694 -0
- package/src/renderer/store/slices/cliInstallerSlice.ts +689 -0
- package/src/renderer/store/slices/configSlice.ts +111 -0
- package/src/renderer/store/slices/connectionSlice.ts +221 -0
- package/src/renderer/store/slices/contextSlice.ts +394 -0
- package/src/renderer/store/slices/conversationSlice.ts +510 -0
- package/src/renderer/store/slices/editorSlice.ts +1455 -0
- package/src/renderer/store/slices/extensionsSlice.ts +1415 -0
- package/src/renderer/store/slices/notificationSlice.ts +277 -0
- package/src/renderer/store/slices/paneSlice.ts +357 -0
- package/src/renderer/store/slices/projectSlice.ts +70 -0
- package/src/renderer/store/slices/repositorySlice.ts +165 -0
- package/src/renderer/store/slices/scheduleSlice.ts +246 -0
- package/src/renderer/store/slices/sessionDetailSlice.ts +755 -0
- package/src/renderer/store/slices/sessionSlice.ts +539 -0
- package/src/renderer/store/slices/subagentSlice.ts +145 -0
- package/src/renderer/store/slices/tabSlice.ts +842 -0
- package/src/renderer/store/slices/tabUISlice.ts +319 -0
- package/src/renderer/store/slices/teamSlice.ts +5080 -0
- package/src/renderer/store/slices/uiSlice.ts +45 -0
- package/src/renderer/store/types.ts +103 -0
- package/src/renderer/store/utils/paneHelpers.ts +134 -0
- package/src/renderer/store/utils/pathResolution.ts +121 -0
- package/src/renderer/store/utils/stateResetHelpers.ts +72 -0
- package/src/renderer/types/api.ts +8 -0
- package/src/renderer/types/claudeMd.ts +74 -0
- package/src/renderer/types/contextInjection.ts +309 -0
- package/src/renderer/types/data.ts +144 -0
- package/src/renderer/types/groups.ts +406 -0
- package/src/renderer/types/inlineChip.ts +110 -0
- package/src/renderer/types/mention.ts +38 -0
- package/src/renderer/types/notifications.ts +18 -0
- package/src/renderer/types/panes.ts +35 -0
- package/src/renderer/types/sessionReport.ts +386 -0
- package/src/renderer/types/tabs.ts +249 -0
- package/src/renderer/types/teamMessagesPanelMode.ts +1 -0
- package/src/renderer/utils/__tests__/teamEffortOptions.test.ts +217 -0
- package/src/renderer/utils/__tests__/teamModelAvailability.codexCatalog.test.ts +383 -0
- package/src/renderer/utils/agentMessageFormatting.ts +139 -0
- package/src/renderer/utils/aiGroupEnhancer.ts +78 -0
- package/src/renderer/utils/aiGroupHelpers.ts +208 -0
- package/src/renderer/utils/attachmentUtils.ts +60 -0
- package/src/renderer/utils/bootstrapPromptSanitizer.ts +225 -0
- package/src/renderer/utils/bugReportUtils.ts +157 -0
- package/src/renderer/utils/buildSelectionAction.ts +116 -0
- package/src/renderer/utils/chipUtils.ts +372 -0
- package/src/renderer/utils/claudeCodeOnlyProviders.ts +126 -0
- package/src/renderer/utils/claudeMdTracker.ts +644 -0
- package/src/renderer/utils/codemirrorLanguages.ts +141 -0
- package/src/renderer/utils/codemirrorSelectionInfo.ts +41 -0
- package/src/renderer/utils/codemirrorTheme.ts +138 -0
- package/src/renderer/utils/contextMath.ts +55 -0
- package/src/renderer/utils/contextTracker.ts +1100 -0
- package/src/renderer/utils/crossTeamPendingReplies.ts +92 -0
- package/src/renderer/utils/dateGrouping.ts +91 -0
- package/src/renderer/utils/diffViewedStorage.ts +120 -0
- package/src/renderer/utils/displayItemBuilder.ts +587 -0
- package/src/renderer/utils/displaySummary.ts +74 -0
- package/src/renderer/utils/editorBridge.ts +90 -0
- package/src/renderer/utils/fileTreeBuilder.ts +110 -0
- package/src/renderer/utils/formatAgentRole.ts +24 -0
- package/src/renderer/utils/formatters.ts +61 -0
- package/src/renderer/utils/groupTransformer.ts +744 -0
- package/src/renderer/utils/idleNotificationSemantics.ts +76 -0
- package/src/renderer/utils/keyboardUtils.ts +92 -0
- package/src/renderer/utils/lastOutputDetector.ts +150 -0
- package/src/renderer/utils/markdownPlugins.ts +60 -0
- package/src/renderer/utils/memberAvatarCatalog.ts +38 -0
- package/src/renderer/utils/memberHelpers.ts +858 -0
- package/src/renderer/utils/memberLaunchDiagnostics.ts +216 -0
- package/src/renderer/utils/memberRuntimeSummary.ts +122 -0
- package/src/renderer/utils/memberSpawnStatusPolling.ts +29 -0
- package/src/renderer/utils/mentionLinkify.ts +90 -0
- package/src/renderer/utils/mentionSuggestions.ts +34 -0
- package/src/renderer/utils/mergeTeamMessages.ts +27 -0
- package/src/renderer/utils/messageRenderEquality.ts +158 -0
- package/src/renderer/utils/modelExtractor.ts +90 -0
- package/src/renderer/utils/multimodelProviderVisibility.ts +32 -0
- package/src/renderer/utils/openCodeModelRecommendations.ts +1326 -0
- package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +79 -0
- package/src/renderer/utils/pathDisplay.ts +149 -0
- package/src/renderer/utils/pathNormalize.ts +61 -0
- package/src/renderer/utils/pathUtils.ts +47 -0
- package/src/renderer/utils/platformKeys.ts +24 -0
- package/src/renderer/utils/previewRegistry.ts +45 -0
- package/src/renderer/utils/projectColor.ts +54 -0
- package/src/renderer/utils/projectLookup.ts +64 -0
- package/src/renderer/utils/providerBackendIdentity.ts +43 -0
- package/src/renderer/utils/providerSlashCommands.ts +122 -0
- package/src/renderer/utils/quickOpenCache.ts +40 -0
- package/src/renderer/utils/refreshCliStatus.ts +17 -0
- package/src/renderer/utils/reportAssessments.ts +555 -0
- package/src/renderer/utils/reviewDecisionScope.ts +81 -0
- package/src/renderer/utils/reviewKey.ts +132 -0
- package/src/renderer/utils/runtimeDisplayName.ts +26 -0
- package/src/renderer/utils/scheduleFormatters.ts +45 -0
- package/src/renderer/utils/sessionAnalyzer.ts +1346 -0
- package/src/renderer/utils/sessionExporter.ts +427 -0
- package/src/renderer/utils/sessionTitleParser.ts +69 -0
- package/src/renderer/utils/skillCommandSuggestions.ts +77 -0
- package/src/renderer/utils/slashCommandExtractor.ts +154 -0
- package/src/renderer/utils/streamJsonParser.ts +393 -0
- package/src/renderer/utils/stringUtils.ts +50 -0
- package/src/renderer/utils/syntaxHighlighter.ts +158 -0
- package/src/renderer/utils/tabLabelDisambiguation.ts +99 -0
- package/src/renderer/utils/taskChangePresence.ts +19 -0
- package/src/renderer/utils/taskChangeRequest.ts +122 -0
- package/src/renderer/utils/taskCommentPendingReply.ts +74 -0
- package/src/renderer/utils/taskGrouping.ts +151 -0
- package/src/renderer/utils/taskReferenceUtils.ts +331 -0
- package/src/renderer/utils/teamEffortOptions.ts +150 -0
- package/src/renderer/utils/teamLaunchSummaryCopy.ts +17 -0
- package/src/renderer/utils/teamMessageExpandStorage.ts +39 -0
- package/src/renderer/utils/teamMessageFiltering.ts +95 -0
- package/src/renderer/utils/teamMessageKey.ts +14 -0
- package/src/renderer/utils/teamMessageReadStorage.ts +49 -0
- package/src/renderer/utils/teamModelAvailability.ts +465 -0
- package/src/renderer/utils/teamModelCatalog.ts +502 -0
- package/src/renderer/utils/teamModelContext.ts +34 -0
- package/src/renderer/utils/teamProvisioningPresentation.ts +772 -0
- package/src/renderer/utils/teamRuntimeSummary.ts +53 -0
- package/src/renderer/utils/toolLinkingEngine.ts +117 -0
- package/src/renderer/utils/toolRendering/index.ts +14 -0
- package/src/renderer/utils/toolRendering/toolContentChecks.ts +58 -0
- package/src/renderer/utils/toolRendering/toolSummaryHelpers.ts +276 -0
- package/src/renderer/utils/toolRendering/toolTokens.ts +57 -0
- package/src/renderer/utils/unwrapIpc.ts +31 -0
- package/src/renderer/utils/urlMatchUtils.ts +45 -0
- package/src/renderer/vite-env.d.ts +21 -0
- package/src/shared/constants/agentBlocks.ts +129 -0
- package/src/shared/constants/attachments.ts +175 -0
- package/src/shared/constants/cache.ts +12 -0
- package/src/shared/constants/cli.ts +15 -0
- package/src/shared/constants/crossTeam.ts +126 -0
- package/src/shared/constants/index.ts +15 -0
- package/src/shared/constants/kanban.ts +9 -0
- package/src/shared/constants/memberColors.ts +140 -0
- package/src/shared/constants/opencodeTaskLogAttribution.ts +1 -0
- package/src/shared/constants/teamLimits.ts +2 -0
- package/src/shared/constants/trafficLights.ts +60 -0
- package/src/shared/constants/triggerColors.ts +126 -0
- package/src/shared/constants/window.ts +12 -0
- package/src/shared/types/api.ts +1062 -0
- package/src/shared/types/ccConnect.ts +399 -0
- package/src/shared/types/cliInstaller.ts +335 -0
- package/src/shared/types/editor.ts +279 -0
- package/src/shared/types/extensions/api.ts +90 -0
- package/src/shared/types/extensions/apikey.ts +40 -0
- package/src/shared/types/extensions/common.ts +16 -0
- package/src/shared/types/extensions/index.ts +67 -0
- package/src/shared/types/extensions/mcp.ts +132 -0
- package/src/shared/types/extensions/plugin.ts +85 -0
- package/src/shared/types/extensions/skill.ts +173 -0
- package/src/shared/types/index.ts +48 -0
- package/src/shared/types/ipc.ts +5 -0
- package/src/shared/types/notifications.ts +407 -0
- package/src/shared/types/providers.ts +116 -0
- package/src/shared/types/review.ts +319 -0
- package/src/shared/types/schedule.ts +124 -0
- package/src/shared/types/team.ts +1726 -0
- package/src/shared/types/terminal.ts +49 -0
- package/src/shared/types/visualization.ts +60 -0
- package/src/shared/utils/__tests__/contextMetrics.test.ts +260 -0
- package/src/shared/utils/__tests__/ephemeralProjectPath.test.ts +42 -0
- package/src/shared/utils/__tests__/teamProvider.test.ts +29 -0
- package/src/shared/utils/agentLanguage.ts +122 -0
- package/src/shared/utils/anthropicLaunchModel.ts +96 -0
- package/src/shared/utils/anthropicModelDefaults.ts +3 -0
- package/src/shared/utils/apiErrorDetector.ts +13 -0
- package/src/shared/utils/boardTaskActivityLabels.ts +128 -0
- package/src/shared/utils/boardTaskActivityPresentation.ts +75 -0
- package/src/shared/utils/cliArgsParser.ts +129 -0
- package/src/shared/utils/contentSanitizer.ts +207 -0
- package/src/shared/utils/contextMetrics.ts +236 -0
- package/src/shared/utils/costFormatting.ts +45 -0
- package/src/shared/utils/diffContextHash.ts +22 -0
- package/src/shared/utils/effortLevels.ts +73 -0
- package/src/shared/utils/ephemeralProjectPath.ts +41 -0
- package/src/shared/utils/errorHandling.ts +26 -0
- package/src/shared/utils/extensionNormalizers.ts +336 -0
- package/src/shared/utils/idleNotificationSemantics.ts +86 -0
- package/src/shared/utils/inboxNoise.ts +158 -0
- package/src/shared/utils/leadDetection.ts +63 -0
- package/src/shared/utils/logger.ts +69 -0
- package/src/shared/utils/markdownTextSearch.ts +210 -0
- package/src/shared/utils/mcpScopes.ts +36 -0
- package/src/shared/utils/modelParser.ts +158 -0
- package/src/shared/utils/opencodeModelRef.ts +78 -0
- package/src/shared/utils/platformPath.ts +103 -0
- package/src/shared/utils/pricing.ts +129 -0
- package/src/shared/utils/providerBackend.ts +90 -0
- package/src/shared/utils/providerExtensionCapabilities.ts +92 -0
- package/src/shared/utils/providerModelSelection.ts +5 -0
- package/src/shared/utils/providerModelVisibility.ts +47 -0
- package/src/shared/utils/rateLimitDetector.ts +334 -0
- package/src/shared/utils/reviewState.ts +74 -0
- package/src/shared/utils/sentryConfig.ts +26 -0
- package/src/shared/utils/skillRoots.ts +93 -0
- package/src/shared/utils/slashCommands.ts +128 -0
- package/src/shared/utils/taskChangePresence.ts +35 -0
- package/src/shared/utils/taskChangeSince.ts +51 -0
- package/src/shared/utils/taskChangeState.ts +49 -0
- package/src/shared/utils/taskHistory.ts +82 -0
- package/src/shared/utils/taskIdentity.ts +32 -0
- package/src/shared/utils/teamGraphDefaultLayout.ts +97 -0
- package/src/shared/utils/teamMemberColors.ts +107 -0
- package/src/shared/utils/teamMemberName.ts +77 -0
- package/src/shared/utils/teamProvider.ts +73 -0
- package/src/shared/utils/teamStableOwnerId.ts +12 -0
- package/src/shared/utils/teammateMessageParser.ts +52 -0
- package/src/shared/utils/tokenFormatting.ts +91 -0
- package/src/shared/utils/toolSummary.ts +279 -0
- package/src/shared/utils/version.ts +29 -0
- package/src/types/agent-teams-controller.d.ts +163 -0
- package/src/types/node-pty.d.ts +22 -0
- 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.
|