Skip to content

Latest commit

 

History

History
68 lines (52 loc) · 17.2 KB

File metadata and controls

68 lines (52 loc) · 17.2 KB
name RendererProcess
description Governs renderer-process routes, navigation registry, bootstrap tasks, API wrappers, and store ownership.
keywords
renderer
vue
routing
stores
bootstrap
ipc

RendererProcess

范围

  • 覆盖:src/renderer/src/ 下的 renderer 应用启动、文件系统路由、活动导航、renderer bootstrap 任务、renderer API wrapper 和 Pinia store 所有权。
  • 不覆盖:主进程 IPC handler、preload 暴露和跨进程契约细节;见 guidelines/MainProcess.md。Renderer 测试位置和 stub 由 guidelines/Testing.md 覆盖。UI/UX 视觉规范见 guidelines/UiDesign.md

规则

路由与导航

  • MUST 将 renderer 页面定义为 src/renderer/src/pages/ 下的 Vue SFC,并让 vue-router/auto-routes 生成 route records。Router 在 src/renderer/src/config/auto-routes.ts 中创建,并使用 createWebHashHistory() 适配 Electron renderer 导航。证据:electron.vite.config.tssrc/renderer/src/config/auto-routes.tssrc/renderer/src/pages/
  • MUST 通过 src/renderer/src/config/activity-bar.ts 中的 activityBarItems 添加主应用导航,不要在组件里硬编码侧边栏入口。ActivityBar.vue 渲染该注册表,并根据 route path 计算 active 状态。证据:src/renderer/src/config/activity-bar.tssrc/renderer/src/components/layout/ActivityBar.vue
  • MUST 保持且仅保持一个默认 activity item,并保持 activity item 的 id 和 path 唯一。该注册表会在 dev/test 中强制默认项数量,renderer 测试会断言注册表形状。证据:src/renderer/src/config/activity-bar.tstest/renderer/src/config/activity-bar.spec.ts
  • MUST 使用 ActivityBarItem.requiresWorkspace 表达 Workspace 门控导航。当 useWorkspaceStore().hasCurrentWorkspace 为 false 时,ActivityBar.vue 会禁用 Workspace 作用域的 item。证据:src/renderer/src/config/activity-bar.tssrc/renderer/src/components/layout/ActivityBar.vue
  • MUST 通过 evaluateWorkspaceNavigation() 统一 activity bar 与 route guard 的 Workspace capability 判断;阶段性不支持 Collection Chat 时,两处必须显示/执行同一禁用结果,不得向 Main 发送 primary-only fallback 请求。证据:src/renderer/src/config/navigation-gate.tssrc/renderer/src/components/layout/ActivityBar.vuesrc/renderer/src/pages/index.vue

API 与状态

  • MUST 将 renderer 对 preload API 的访问封装在 src/renderer/src/api/<domain>/<area>.ts wrapper 中。组件、composable 和 store 应导入这些 wrapper,而不是直接调用 window.api;除 src/renderer/src/api/** 外,renderer 代码不得直接访问 window.api。证据:src/renderer/src/api/platform/settings.tssrc/renderer/src/api/session/chat.tseslint.config.mjs
  • MUST 让 renderer API wrapper 对齐 window.api.<domain>.<area>,并在可用时基于 shared 契约或 preload API 类型进行类型约束,保留 preload API 返回的标准 IpcResponse<T> 流程。证据:src/preload/index.tssrc/preload/index.d.tssrc/renderer/src/api/automation/workflow-run.tssrc/shared/types/ipc.ts
  • SHOULD 将可复用异步状态和跨组件 UI 状态放在 src/renderer/src/stores/<domain>/ 下的 Pinia setup store 中。store 的形状应服务 renderer 状态和页面用例,不需要强制镜像 main services 的 area 或文件名。证据:src/renderer/src/stores/platform/settings.tssrc/renderer/src/stores/session/session.tssrc/renderer/src/stores/automation/workflow-run.ts
  • MUST 让每个 store domain 通过 src/renderer/src/stores/<domain>/index.ts 暴露本 domain 的 public store entry points;根级 src/renderer/src/stores/index.ts 只 re-export domain barrel,不逐文件 re-export store。renderer 非 store 代码应从 @renderer/stores root barrel 导入 store,不要从 @renderer/stores/<domain> 深路径导入。store 模块内部不得导入 root barrel,跨 store 组合时使用目标 domain barrel 或直接 store module,避免 stores/index.ts 形成循环依赖。证据:src/renderer/src/stores/platform/index.tssrc/renderer/src/stores/session/index.tssrc/renderer/src/stores/index.tssrc/renderer/src/pages/task.vueeslint.config.mjs
  • MUST 让每个 renderer store 只直接导入本 domain 的 API wrapper;如果需要组合其他 domain 的能力,应导入其他 domain 的 store 或由本 domain store 提供更高层 action。不得在 src/renderer/src/stores/<domain>/** 直接导入 src/renderer/src/api/<other-domain>/**。该规则由 eslint.config.mjs 强制。证据:src/renderer/src/stores/session/session.tssrc/renderer/src/stores/automation/task.tseslint.config.mjs
  • SHOULD 让页面和组件通过所属流程的 store/composable 取数和提交动作,避免直接导入无关 domain 的 API wrapper。需要跨 domain 组合时,优先把组合逻辑收敛到拥有该页面流程的 store。证据:src/renderer/src/pages/task.vuesrc/renderer/src/stores/automation/task.ts
  • SHOULD 让页面、组件和关键 composable 的跨 domain store 组合保持流程所有权清晰;当组合逻辑开始承载业务流程,应收敛到 owner store action,而不是在页面里长期堆叠多个领域的细节。该约束通过 review 判断,不再由文件级 lint 白名单维护。证据:src/renderer/src/pages/task.vuesrc/renderer/src/stores/automation/task.ts
  • MUST 通过 src/renderer/src/api/workspace/window.tsuseWorkspaceStore().bootstrapWindowWorkspace() 绑定当前窗口的 Workspace 上下文。当前 Workspace 只能来自 main 返回的 WindowContext;组件打开 Workspace 或文件夹时调用 Workspace store 的 openWorkspaceWindow() / openFolderWindow(),不要在组件中直接替换 currentWorkspace。证据:src/renderer/src/stores/workspace/workspace.tssrc/renderer/src/bootstrap/tasks/workspaces.tssrc/renderer/src/components/welcome/WelcomeView.vue
  • MUST 在 launcher context 中保持 currentWorkspace 为空;在 Workspace context 不可用、Workspace 不存在或 primary Folder path 缺失时展示页面级错误状态并清空 session state。Workspace bootstrap 必须在同一任务中按 context、Workspace list、当前 Workspace、session list 的顺序完成。证据:src/renderer/src/stores/workspace/workspace.tstest/renderer/src/stores/workspace/workspace.spec.tssrc/renderer/src/pages/index.vue
  • MUST 让 launcher 使用 active/deleted WorkspaceLauncherItem 投影管理 Folder 与 Collection Workspace;创建、成员编辑、重定位、soft delete、restore 和永久清理只通过 Workspace store action 进入,组件不得直接调用 preload API。证据:src/renderer/src/stores/workspace/workspace.tssrc/renderer/src/components/welcome/WorkspaceList.vuesrc/renderer/src/components/welcome/DeletedWorkspaceManager.vue
  • MUST 让 Workspace-scoped 异步结果绑定请求发起时的 workspaceId 和请求世代;切换 Workspace 后的迟到 list/detail 响应不得覆盖新 Workspace state,确认式删除和 Action 执行也必须拒绝 scope 已变化的操作。证据:src/renderer/src/stores/session/session.tssrc/renderer/src/stores/insight/knowledge.tssrc/renderer/src/features/fyllo-action/application/use-fyllo-action-dispatcher.tssrc/renderer/src/pages/knowledge.vue
  • MUST 让 proposal list key、detail selection、status update 和 EventRail item 使用完整 ProposalRef { folderId, changeId };不得用裸 changeId 在不同 Folder 的同名 proposal 之间查找、更新或取消 watcher。Proposal 列表和会话卡片必须展示 owner Folder,linked target 通过 worktreeModeworktreePath 呈现;Apply/Archive 入口只发送包含该 ProposalRef 的 Chat 文字消息,不得重新引入 Proposal stage-stream 或 run store。证据:src/renderer/src/stores/proposal/browser.tssrc/renderer/src/stores/session/session.tssrc/renderer/src/components/proposal/ProposalDetailSlideover.vuesrc/renderer/src/components/chat/event/ChatProposalPanel.vuesrc/renderer/src/pages/proposal.vue
  • MUST 让 Specs、Guidelines、Proposal 与 Overview store 保留 Main 返回的 per-Folder aggregate 和 completeness,列表 key、选择及详情调用分别使用完整 SpecRefGuidelineRefProposalRef;Folder filter 只能改变可见集合,不得重写 owner。页面必须区分 ready-empty、missing、error 与 partial,并让请求绑定发起时的 workspaceId 和 generation,拒绝前一 Workspace 的迟到响应。证据:src/renderer/src/stores/insight/specs.tssrc/renderer/src/stores/insight/guidelines.tssrc/renderer/src/stores/proposal/browser.tssrc/renderer/src/stores/insight/overview.tssrc/renderer/src/pages/specs.vuesrc/renderer/src/pages/guidelines.vuesrc/renderer/src/pages/proposal.vuesrc/renderer/src/pages/overview.vue
  • MUST 让 Task、Workflow definition/Run 与 Workspace Integration renderer 流程显式携带当前 workspaceId。Task target 与 repository-bound integration binding 必须显示 current/stale 或 unbound 状态;从 Task 建议 proposal owner 时,仅当原始去重 target 集合恰好一个且仍为当前成员才可预选,多 target 降级后不得猜测 owner。Workflow Editor 只负责 v2 definition CRUD;Workflow Run 使用独立 owner-keyed store 和 automation:workflow-run:* API,不得把 Run 状态并入 definition store。window.api.automation.workspaceIntegration 是 Integration 唯一公开入口,不保留 Project 命名 alias。证据:src/renderer/src/stores/automation/task.tssrc/renderer/src/stores/automation/workflow.tssrc/renderer/src/stores/automation/workflow-run.tssrc/renderer/src/stores/automation/workspace-integration.tssrc/renderer/src/features/workflow-editor/README.mdsrc/preload/index.ts
  • MUST 让 Lineage Browser 仅以当前 Workspace subjects 作为列表来源,并让每个 proposal node 携带完整 ProposalRef、owner Folder metadata 与 composite key;详情入口必须按该 ProposalRef 打开正确 repository owner,不得按裸 changeId 匹配。Workspace 切换后的迟到 lineage 响应必须丢弃,共享 Folder 的 proposal 只能使用当前 Workspace subject/reference 补充 task/session 信息,不得读取其他 Workspace 的 subject 内容。证据:src/renderer/src/stores/insight/lineage.tssrc/renderer/src/pages/lineage.vuesrc/renderer/src/composables/useProposalDetailSlideover.tssrc/main/services/insight/lineage/browser.ts
  • MUST 让 spawned completion wake 保持 level-triggered:事件只表示当前 Workspace outbox 可能变化,renderer 每次启动和 wake 都通过 typed API 重新 list,不把事件 payload 或内存队列当作事实来源。useChatStore() 必须按 parent sessionId 仲裁用户 turn 与 notification dispatch,用户提交意图优先,目标 Session submitted/streaming 时保留 pending;非 active Session 的消息刷新只能更新该 Session,不得切换 activeSessionId、清空当前 composer 或导航。窗口关闭可丢弃 UI 投影,重开后继续通过既有 Session list/loadMessages 恢复 Main 已持久化的 user reminder 与 assistant terminal。证据:src/renderer/src/bootstrap/tasks/spawn-notifications.tssrc/renderer/src/stores/session/chat.tssrc/renderer/src/stores/session/session.tssrc/renderer/src/api/session/chat.ts

Workflow Run inspection

  • MUST 将 Workflow definition 与 Workflow Run 视为两个 renderer 状态边界:src/renderer/src/stores/automation/workflow.ts 只负责 Workspace-owned v2 YAML definition CRUD,src/renderer/src/stores/automation/workflow-run.ts 负责以 workspaceId + parentSessionId + runId 为 key 的 list/detail/decide;Workflow Editor 不读取或写入 Run snapshot,也不触发执行。证据:src/renderer/src/features/workflow-editor/README.mdsrc/renderer/src/stores/automation/workflow.tssrc/renderer/src/stores/automation/workflow-run.ts
  • MUST 让 Workflow Run 使用 wake + interest-gated pull:Main wake 只表示 { workspaceId, runId } 可能失效,只有 ActivityEntry/detail view 持有 active interest 时才能调用 getDetail;无 observer 时不得因 wake 发起查询,Workspace/parent/interest generation 改变后的迟到响应必须丢弃。证据:src/renderer/src/features/workflow-run-inspector/integration/wake.tssrc/renderer/src/stores/automation/workflow-run.tssrc/renderer/src/features/workflow-run-inspector/application/use-workflow-run-inspector.tstest/renderer/src/stores/automation/workflow-run.spec.ts
  • MUST 让 WorkflowRunActivityEntrySpawnedSessionActivityEntryChatBackgroundActivityBar 中保持平级;Workflow entry 只能从 workflow-run store、自己的 interest/wake 和 Run projection 派生,不得读取 spawned store、spawn count 或 notification。fresh Agent transcript 入口必须使用 Engine 返回的 workflow session identity,不得把 session 加入 spawned list/count。证据:src/renderer/src/components/chat/ChatBackgroundActivityBar.vuesrc/renderer/src/features/workflow-run-inspector/**test/renderer/src/components/chat-background-activity-bar.spec.tstest/renderer/src/features/workflow-run-inspector/**

Bootstrap

  • MUST 通过 registerBootstrapTasks()onFylloBootstrap() 注册 renderer 启动副作用,而不是在 layout 组件中临时启动;每个 task 必须显式声明 phase: "critical" | "background"src/renderer/src/main.ts 在 mount 后使用共享 { pinia, router } context 运行已注册任务。证据:src/renderer/src/main.tssrc/renderer/src/bootstrap/core.tssrc/renderer/src/bootstrap/register.ts
  • MUST 先并行结算全部 critical task,再启动 background task,并保持每个 task 的失败隔离和 name/phase/duration 结果。Workspace bootstrap 属于 critical,且其内部 context → list → current Workspace → sessions 顺序不变;ACP renderer cache 属于 background,不得阻塞应用可交互。证据:src/renderer/src/bootstrap/core.tssrc/renderer/src/bootstrap/tasks/workspaces.tssrc/renderer/src/bootstrap/tasks/acp-agents.tstest/renderer/src/bootstrap/fyllo-bootstrap.spec.ts
  • MUST 在 critical 未结算时只渲染 StartupLoading.vue,不得提前挂载 Welcome、Workspace RouterView 或页面级业务错误;critical 成功或失败结算并在下一 nextTick() 后,通过 window.api.platform.lifecycle.markInteractive() 通知 main。Background task 状态只能影响其局部 UI。证据:src/renderer/src/App.vuesrc/renderer/src/main.tstest/renderer/src/app-startup.spec.ts
  • MUST 让 bootstrap task 注册保持幂等;新增任务注册应通过 registerBootstrapTasks() 接入,该函数会防止重复注册。证据:src/renderer/src/bootstrap/register.ts
  • MUST 将 Workflow Run wake listener 作为 background bootstrap task 注册,并在 Workspace 变化时清理旧 scope;task 只能订阅 Main 的 typed wake API,不能在页面 mount 时创建全局 listener。证据:src/renderer/src/bootstrap/tasks/workflow-runs.tssrc/renderer/src/bootstrap/register.tstest/renderer/src/bootstrap/fyllo-bootstrap.spec.ts
  • MUST 在 Workspace critical bootstrap 结算后以 background task 注册 spawned notification listener并执行首次 pull;listener 重建时必须销毁旧订阅,旧 Workspace wake 不得触发当前 scope 的 drain。证据:src/renderer/src/bootstrap/tasks/spawn-notifications.tstest/renderer/src/bootstrap/spawn-notifications.spec.ts
  • MUST 让 spawned Session inspection 使用 useSpawnedSessionStore() 保存 owner-scoped 查询状态:list key 为完整 workspaceId + parentSessionId,detail key 还必须包含 sessionId,同 key 请求合并并以 generation 拒绝 Workspace/父 Session 切换后的迟到结果。list interest 与 detail interest 必须分别引用计数;详情只在 Slideover 打开期间持有 detail interest,关闭后不得因缓存存在而继续读取。interest 范围内的 view wake 只触发 typed API 重新查询,不直接写 status/content,也不得触发 completion notification drain;in-flight 收到 wake 时必须合并为至多一个 queued post-refresh,terminal wake 不得丢失。历史 spawn.session 使用消息所属父 Session host context;当前 Chat footer 的活动宿主位于 Prompt 外部,展示同一父 Session 的 active 与 terminal 全部 list summary,并通过 spawned-session-inspector 根公共入口打开详情。证据:src/renderer/src/stores/session/spawned-session.tssrc/renderer/src/bootstrap/tasks/spawned-sessions.tssrc/renderer/src/features/fyllo-signal/**src/renderer/src/features/spawned-session-inspector/**src/renderer/src/components/chat/ChatContainer.vuesrc/renderer/src/components/chat/ChatBackgroundActivityBar.vue

验证

pnpm exec vitest run --project renderer
pnpm typecheck:web

失效信号

  • electron.vite.config.tssrc/renderer/src/main.tssrc/renderer/src/config/auto-routes.tssrc/renderer/src/config/activity-bar.tssrc/renderer/src/bootstrap/**src/renderer/src/api/**src/renderer/src/stores/** 发生变化时,重新检查本文档。