feat: add unified OA event flow
This commit is contained in:
@@ -22,7 +22,7 @@ UniDesk 的统一 CLI 入口是根目录 `scripts/cli.ts`,运行方式固定
|
||||
- `codex task <taskId>` 通过 Code Queue 私有代理按任务 ID 查询结构化执行摘要;默认只返回有界 prompt/response 预览、执行 Provider、工作目录、最后 assistant message、最近工具调用摘要、attempt、judge、错误、耗时和 trace 翻页提示,适合在新队列任务中引用历史 session 且避免噪声爆炸。
|
||||
- `codex task <taskId> --trace --tail|--from-start|--after-seq N|--before-seq N --limit N` 按页拉取 Code Queue 的逻辑 trace;响应会返回 `nextAfterSeq`、`previousBeforeSeq`、`hasMore`、`hasBefore` 和下一页/上一页命令,默认 `--trace` 取最新一页,需要完整 prompt/最后 response 时加 `--full`。
|
||||
- `codex output <taskId> --tail|--from-start|--after-seq N|--before-seq N --limit N [--full-text]` 按原始 output seq 分页读取底层记录;当 trace 行提示 `commandOmittedLines`、`bodyOmittedLines` 或 `rawSeqs` 时,用该命令按 seq 补取完整信息,默认仍有单条文本预览上限,显式 `--full-text` 才返回该页全文。
|
||||
- Code Queue 多队列 lane 由 `codex` 命令命名空间管理:`queues` 列表、`queue create <queueId>` 创建、`queue merge <sourceQueueId> --into <targetQueueId>` 合并、`move <taskId> --queue <queueId>` 迁移;同一个 queue 内部串行执行,不同 queue 之间并行执行。合并只移动任务归属,必须保留源 queue 记录且不提供删除语义;合并后的目标 queue 按任务原 `queueEnteredAt`/`createdAt` 时间顺序串行。迁移 queued/retry_wait 任务后会立即调度目标 queue。
|
||||
- Code Queue 多队列 lane 由 `codex` 命令命名空间管理:`queues` 列表、`queue create <queueId>` 创建、`queue merge <sourceQueueId> --into <targetQueueId>` 合并、`move <taskId> --queue <queueId>` 迁移;同一个 queue 内部串行执行,不同 queue 之间并行执行。合并会移动任务归属并自动删除源 queue 记录,只保留合并后的目标 queue;合并后的目标 queue 按任务原 `queueEnteredAt`/`createdAt` 时间顺序串行。迁移 queued/retry_wait 任务后会立即调度目标 queue。
|
||||
- `job list` 与 `job status` 查询 `.state/jobs/` 文件系统状态,是异步命令的可观测入口。
|
||||
- `debug health`、`debug dispatch` 与 `debug task` 走真实内部 core、WebSocket、数据库、provider、系统指标、Docker 状态和 Host SSH 维护桥流程,只用于开发调试,不写入 `TEST.md` 的正式验收步骤。
|
||||
- `e2e run [--only pattern[,pattern...]] [--skip pattern[,pattern...]]` 使用 publicHost 派生的公开 frontend/provider ingress URL,并通过 Docker 内网验证 core API、PostgreSQL、provider self-connection、系统指标曲线、Docker 状态快照、provider.upgrade 预检和 Playwright 前端页面,是交付前的自动化 E2E 门禁;CLI 默认输出 check 状态摘要,完整诊断写入 `resultPath`,日常迭代应优先用 `--only` / `--skip` 跑最小必要集合。
|
||||
|
||||
@@ -31,17 +31,17 @@ Typical targeted commands:
|
||||
- `bun scripts/cli.ts e2e run --only frontend --skip frontend:todo-note-integrated-visible,frontend:findjob-integrated-visible`
|
||||
- `bun scripts/cli.ts e2e run --only network,provider-ingress`
|
||||
|
||||
- Public exposure: Docker port summary must show only frontend and provider ingress host mappings; public core、public database and known private user-service ports such as FindJob `3254`, MET Nonlinear `3288`, Todo Note `4211`, Code Queue host port `14222` and File Browser provider port `4251` probes must fail.
|
||||
- Public exposure: Docker port summary must show only frontend and provider ingress host mappings; public core、public database and known private user-service ports such as FindJob `3254`, MET Nonlinear `3288`, Todo Note `4211`, Code Queue host port `14222`, OA Event Flow `4255` and File Browser provider port `4251` probes must fail.
|
||||
- Core API: `docker exec unidesk-backend-core` calls internal `GET /api/overview`, which must report `dbReady: true`, `pgdata.volumeName=unidesk_pgdata_10gb`, a positive PostgreSQL database byte count, and at least one online node; internal `GET /api/performance` must report component request statistics, internal operation statistics, PGDATA usage and Code Queue PostgreSQL storage metadata.
|
||||
- Provider self-connection: internal `GET /api/nodes` must contain `main-server` with `status: online`, `labels.providerGatewayVersion` equal to `src/components/provider-gateway/package.json`, `labels.providerGatewayUpgradePolicy: "always-enabled"`, `labels.providerGatewayRestartPolicyOk: true`, `labels.providerGatewayPidModeOk: true`, and `labels.providerGatewayRuntimeGuardOk: true`; internal `GET /api/nodes/system-status` must contain CPU/memory/disk samples plus a non-empty process resource list sorted by memory by default; internal `GET /api/nodes/docker-status` must contain a Docker snapshot for `main-server`; every running `provider-gateway` container visible in Docker snapshots must report `restartPolicy: "always"` and `pidMode: "host"`; public provider ingress `/health` must return ok.
|
||||
- Provider remote control: internal `/api/dispatch` must successfully complete a real `provider.upgrade` task in `mode: "plan"` so the upgrade path is validated without recreating the running gateway during E2E.
|
||||
- User services: internal `/api/microservices` must include `todo-note` and `code-queue` on `main-server`, canonical `filebrowser` on `D518`, plus `findjob`, `pipeline`, `met-nonlinear`, `claudeqq` and `filebrowser-d601` on `D601` with `public=false`; `/api/microservices/todo-note/health` must report `storage=postgres`, `/api/microservices/todo-note/proxy/api/instances` must expose the migrated Todo Note lists, and a temporary Todo Note list create/add/toggle/undo/delete cycle must succeed through the real provider-gateway proxy; `/api/microservices/code-queue/health` must return a Code Queue summary with default model `gpt-5.5`, and `/api/microservices/code-queue/proxy/api/tasks` must return queue state through the same proxy; `/api/microservices/filebrowser/health`, `/api/microservices/filebrowser-d601/health` and `/api/microservices/filebrowser/proxy/` must prove File Browser health and WebUI access through UniDesk proxy; `/api/microservices/findjob/health` and `/api/microservices/findjob/proxy/api/summary` must succeed through the real provider-gateway proxy; `/api/microservices/findjob/proxy/api/jobs?__unideskArrayLimit=jobs:5` must return a bounded preview with `_unidesk.arrayLimits` metadata; `/api/microservices/pipeline/health`, `/api/microservices/pipeline/proxy/api/snapshot?__unideskArrayLimit=registry.components:8,runs:3` and `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` must return Pipeline health, registry/run previews and OA event-flow evidence; `/api/microservices/met-nonlinear/health`, `/api/microservices/met-nonlinear/proxy/api/queue`, `/api/microservices/met-nonlinear/proxy/api/projects?root=projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects?root=ex_projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects/config?path=<projectPath>` and `/api/microservices/met-nonlinear/proxy/api/images` must return the D601 TS backend health, queue/GPU policy, full project tree inputs, structured project detail and ready `met-nonlinear-ml:tf26` image status.
|
||||
- User services: internal `/api/microservices` must include `todo-note`, `oa-event-flow` and `code-queue` on `main-server`, canonical `filebrowser` on `D518`, plus `findjob`, `pipeline`, `met-nonlinear`, `claudeqq` and `filebrowser-d601` on `D601` with `public=false`; `/api/microservices/todo-note/health` must report `storage=postgres`, `/api/microservices/todo-note/proxy/api/instances` must expose the migrated Todo Note lists, and a temporary Todo Note list create/add/toggle/undo/delete cycle must succeed through the real provider-gateway proxy; `/api/microservices/oa-event-flow/health`, `/api/microservices/oa-event-flow/proxy/api/diagnostics`, `/api/microservices/oa-event-flow/proxy/api/events`, `/api/microservices/oa-event-flow/proxy/api/events?tags=service:pipeline` and `/api/microservices/oa-event-flow/proxy/api/stats/trace` must prove the independent OA event table、Pipeline bridge 和 stats center are reachable through UniDesk proxy; `/api/microservices/code-queue/health` must return a Code Queue summary with default model `gpt-5.5`, and `/api/microservices/code-queue/proxy/api/tasks` must return queue state through the same proxy; `/api/microservices/filebrowser/health`, `/api/microservices/filebrowser-d601/health` and `/api/microservices/filebrowser/proxy/` must prove File Browser health and WebUI access through UniDesk proxy; `/api/microservices/findjob/health` and `/api/microservices/findjob/proxy/api/summary` must succeed through the real provider-gateway proxy; `/api/microservices/findjob/proxy/api/jobs?__unideskArrayLimit=jobs:5` must return a bounded preview with `_unidesk.arrayLimits` metadata; `/api/microservices/pipeline/health`, `/api/microservices/pipeline/proxy/api/snapshot?__unideskArrayLimit=registry.components:8,runs:3` and `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` must return Pipeline health, registry/run previews and OA event-flow evidence; `/api/microservices/met-nonlinear/health`, `/api/microservices/met-nonlinear/proxy/api/queue`, `/api/microservices/met-nonlinear/proxy/api/projects?root=projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects?root=ex_projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects/config?path=<projectPath>` and `/api/microservices/met-nonlinear/proxy/api/images` must return the D601 TS backend health, queue/GPU policy, full project tree inputs, structured project detail and ready `met-nonlinear-ml:tf26` image status.
|
||||
- ClaudeQQ availability: `/api/microservices/claudeqq/health` must only pass when `ready=true`, NapCat HTTP and WebSocket are connected, and `napcat.loginState=logged_in`; `/api/microservices/claudeqq/proxy/api/napcat/login` must show the same logged-in account state and `/api/microservices/claudeqq/proxy/api/events/recent` must prove the backend can read the persistent event cache. A QR-code-only or not-logged-in NapCat state must be treated as unhealthy.
|
||||
- Database: the command writes an `unidesk_e2e_markers` row through `docker exec unidesk-database psql`, confirms provider state is stored in PostgreSQL, and checks Todo Note rows exist in `todo_note_instances` using the same named volume.
|
||||
- Pipeline OA event flow: `microservice:pipeline-oa-event-flow` must prove both no-audit and monitor-audit runs are driven by OA events end to end. The event stream must show `node-finished` as a neutral fact with `pipeline:{pipelineId}` and `epoch:{runId}` tags, OA policy as the source of downstream/audit decisions, monitor decisions as OA control events, and runner control-result evidence. E2E must fail if delivery still depends on a legacy detail audit policy flag as policy authority, independent legacy audit-request points, a legacy batch completion gate, direct monitor-to-runner calls, or frontend/CLI writes to Pipeline `.state`.
|
||||
- The same Pipeline OA diagnostics must fail on legacy file-transport residuals. Procedure containers, monitor sessions, UI/Gantt DTO builders and CLI fetches must consume prompt/control/stop/display evidence only from the OA event ledger and normalized HTTP read APIs; `control-prompts.jsonl`, `monitor-prompts.jsonl`, `monitor-control`, `control-events.jsonl`, monitor stop files, `.state/pipeline-runs/{runId}/control/commands/`, `PIPELINE_*_APPEND_FILE`, local JSONL append/read helpers, and monitor `/pipeline-state` mounts are forbidden in runtime source.
|
||||
- Pipeline live Gantt setup: when `frontend:pipeline-gantt-observation-live-running` is selected, E2E first looks for a current Pipeline run that already contains both a `node-long-running-observation` marker and a still-running execution interval. If no such candidate exists, the E2E setup starts the D601 `monitor-management-behavior-test` pipeline through `bun scripts/cli.ts ssh D601 ...` and polls the private backend proxy until the observation candidate exists; the acceptance assertion itself still opens the public frontend with Playwright and verifies the rendered arrows, absence of observation source pseudo-points, target arrow inset, and live flashing running bar through React DOM controls.
|
||||
- Frontend: Playwright must open the public frontend URL derived from `network.publicHost`, not localhost or a Docker-internal URL; it logs in with the configured account, waits for `核心在线`, asserts that `main-server` and `Main Server Provider` are visible, verifies desktop sidebar collapse and `PGDATA` overview metric, opens `运行总览 / 性能面板` to verify `Bwebui`、组件汇总、最近失败请求、内部操作汇总和最近慢操作, clicks `查看原始JSON` to verify Provider data from the frontend, confirms no raw JSON is visible before that click, opens task history to verify duration and failure diagnostics, opens resource nodes `资源监控` to verify CPU/Memory/Disk curves, the structured process resource table, default memory-desc sorting, sortable CPU column and provider upgrade precheck dispatch, opens `Docker 状态`, switches to `main-server`, and verifies the Docker Desktop-style container view including the database named volume `unidesk_pgdata_10gb`, opens `网关版本` and verifies the provider-gateway version, SSH 透传可用性、远程更新可用性 plus structured remote update records for `provider.upgrade`, then opens `用户服务 / 服务目录`、`用户服务 / Todo Note`、`用户服务 / Code Queue`、`用户服务 / FindJob`、`用户服务 / Pipeline` and `用户服务 / MET Nonlinear` to verify 主 server Todo Note/Code Queue、D601、仓库引用、私有后端映射、Todo Note 迁移清单和树形任务、Code Queue 队列/模型/输出/初始 `Submitted prompt`/终态任务自动加载完整 Trace/追加 prompt/打断控件、FindJob 指标和岗位预览、Pipeline 组件矩阵、MiniMax 限额卡片、结构化 OA 事件流诊断面板、React Flow 控制图、epoch 甘特图、甘特图渲染图导出、monitor 首列排序、长任务观察连线、无观察来源伪点、running node 实时闪动执行条和 OpenCode Trace、MET Nonlinear 项目库/Fork/待启动队列/当前队列/已完成/失败诊断/GPU/镜像都通过 React 控件展示。Playwright 还必须验证深链接直达路由,例如公网 `http://<publicHost>:<frontendPort>/app/pipeline/` 能直接落到 Pipeline 页面,随后切到 `资源节点 / Docker 状态` 时地址栏更新为 `/nodes/docker/`,并且浏览器 history 返回链路仍能回到 `/app/pipeline/`;还必须直开 `/app/code-queue/` 验证页面存在 `app-shell`、左侧主模块边栏、顶部状态栏、顶部子标签和 `code-queue-page`,防止用户服务 deep link 退化成缺 shell 的 standalone 页面;同时 `态势总览` 这类非用户服务页面应落在自己的模块前缀下,例如 `/ops/status/`。Playwright 必须覆盖默认可见时间按北京时间显示,至少包括顶部 `北京时间` 时钟、任务历史/网关版本更新时间和用户服务刷新时间,不得随浏览器本地时区漂移。Task history and provider upgrade records must not display a real sub-second duration as `0s`; MET Nonlinear running rows must show an ETA derived from backend progress or from `startedAt` plus epoch progress, and queue/completed rows must show training speed as `epoch/h`.
|
||||
- Frontend: Playwright must open the public frontend URL derived from `network.publicHost`, not localhost or a Docker-internal URL; it logs in with the configured account, waits for `核心在线`, asserts that `main-server` and `Main Server Provider` are visible, verifies desktop sidebar collapse and `PGDATA` overview metric, opens `运行总览 / 性能面板` to verify `Bwebui`、组件汇总、最近失败请求、内部操作汇总和最近慢操作, clicks `查看原始JSON` to verify Provider data from the frontend, confirms no raw JSON is visible before that click, opens task history to verify duration and failure diagnostics, opens resource nodes `资源监控` to verify CPU/Memory/Disk curves, the structured process resource table, default memory-desc sorting, sortable CPU column and provider upgrade precheck dispatch, opens `Docker 状态`, switches to `main-server`, and verifies the Docker Desktop-style container view including the database named volume `unidesk_pgdata_10gb`, opens `网关版本` and verifies the provider-gateway version, SSH 透传可用性、远程更新可用性 plus structured remote update records for `provider.upgrade`, then opens `用户服务 / 服务目录`、`用户服务 / Todo Note`、`用户服务 / OA Event Flow`、`用户服务 / Code Queue`、`用户服务 / FindJob`、`用户服务 / Pipeline` and `用户服务 / MET Nonlinear` to verify 主 server Todo Note/OA Event Flow/Code Queue、D601、仓库引用、私有后端映射、Todo Note 迁移清单和树形任务、OA Event Flow 事件表和 Trace stats 表、Code Queue 队列/模型/输出/初始 `Submitted prompt`/终态任务自动加载完整 Trace/追加 prompt/打断控件、FindJob 指标和岗位预览、Pipeline 组件矩阵、MiniMax 限额卡片、结构化 OA 事件流诊断面板、React Flow 控制图、epoch 甘特图、甘特图渲染图导出、monitor 首列排序、长任务观察连线、无观察来源伪点、running node 实时闪动执行条和 OpenCode Trace、MET Nonlinear 项目库/Fork/待启动队列/当前队列/已完成/失败诊断/GPU/镜像都通过 React 控件展示。Playwright 还必须验证深链接直达路由,例如公网 `http://<publicHost>:<frontendPort>/app/pipeline/` 能直接落到 Pipeline 页面,随后切到 `资源节点 / Docker 状态` 时地址栏更新为 `/nodes/docker/`,并且浏览器 history 返回链路仍能回到 `/app/pipeline/`;还必须直开 `/app/code-queue/` 验证页面存在 `app-shell`、左侧主模块边栏、顶部状态栏、顶部子标签和 `code-queue-page`,防止用户服务 deep link 退化成缺 shell 的 standalone 页面;同时 `态势总览` 这类非用户服务页面应落在自己的模块前缀下,例如 `/ops/status/`。Playwright 必须覆盖默认可见时间按北京时间显示,至少包括顶部 `北京时间` 时钟、任务历史/网关版本更新时间和用户服务刷新时间,不得随浏览器本地时区漂移。Task history and provider upgrade records must not display a real sub-second duration as `0s`; MET Nonlinear running rows must show an ETA derived from backend progress or from `startedAt` plus epoch progress, and queue/completed rows must show training speed as `epoch/h`.
|
||||
- Frontend dense-layout regression gate: whenever a frontend change touches Pipeline 右侧边栏、Trace timeline、详情抽屉、甘特图坐标或其他高信息密度面板, Playwright acceptance must inspect both `总高度` and `横向滚动条`. For Pipeline specifically, the OpenCode Trace session head must carry shared agent/model/session facts and the Trace body must use the same Code Queue `TraceView` styling; Playwright must fail if old `.pipeline-opencode-step`, `.pipeline-opencode-flow`, `.pipeline-step-message-card` or `.pipeline-opencode-part` user-visible styles reappear, if the Trace container introduces an internal horizontal scrollbar, or if `frontend:pipeline-gantt-frontend-y-accuracy` fails to prove the frontend `frontend-y` layout maps ticks, markers and execution bars from timestamps to y coordinates within tolerance.
|
||||
- OpenCode Trace must use Code Queue Trace styling and must not render the deprecated Pipeline continuous step connector; Playwright should fail if `.pipeline-opencode-flow`, `.pipeline-opencode-step` or any equivalent continuous connector/card returns to the user-visible Trace.
|
||||
- User service frontend assertions must wait for real backend data, not only the page skeleton. For Todo Note this means the page must show the migrated lists `CONSTAR`、`大论文`、`找工作`、`小论文`、`事务`, support creating a temporary list and task through the frontend, and delete that temporary list afterwards. The temporary list must be selected again by its unique generated name before deletion so E2E never deletes a migrated source list by accident. For FindJob this means the page must show a numeric `岗位总量`, `HEALTH OK`, and a non-empty `PREVIEW` count such as `40/1463 PREVIEW`; for Pipeline this means the page must show `Pipeline v2 工作台`, `Health OK`, a numeric component count, a non-empty React Flow control graph, `控制图`, `Epoch 甘特图`, and after clicking a Gantt execution line it must show `OpenCode Trace` rendered by the shared Code Queue-style Trace component with messages and tool-call groups; for MET Nonlinear this means the page must show `MET Nonlinear 训练编排`, `Health OK`, `Fork Project`, `加入待启动队列`, `启动队列`, `当前队列`, 最大并发设置、task queue and GPU/image panels, and must not show the removed hard-coded `创建10个10轮任务` frontend entry. The MET Nonlinear project library must render `projects/` and `ex_projects/` as a true path tree with folder Project counts; clicking a project row must open a structured detail panel containing `config.json`, `data/ 训练状态`, `模型参数`, `指标` and a parameter count such as `Total Params`; clicking a completed/current/failed job row must open a structured job detail and both the row and detail must show `epoch/h`. Full MET Nonlinear acceptance is driven by public frontend controls: choose a visible source Project, set batch size, epochs and max concurrency in inputs, fork into `projects/unidesk_forks/`, stage the selected forks, start the queue, and verify completed rows plus automatic `metnl-train-*` container removal; loading placeholders like `--` or empty states are not sufficient for E2E success.
|
||||
@@ -55,7 +55,7 @@ Remote update records in the frontend are covered by the same rule: `provider.up
|
||||
|
||||
Provider operation availability is also covered by the structured rendering rule. `host.ssh` availability must be displayed as badges or equivalent controls derived from capabilities and `hostSsh*` labels, and remote update availability must be displayed from `provider.upgrade` capability plus the `always-enabled` policy; these fields must not require opening raw Provider JSON.
|
||||
|
||||
User service pages are covered by the same rule. `Todo Note` must show lists, task tree, filters, reminder input, movement controls, undo/redo and metrics as controls; `Code Queue` must show queue cards, live transcript, model/cwd/max attempt inputs, judge decision, attempt table, append prompt, interrupt and retry controls; `File Browser` must show D518 as the default target, D601 as an alternate target, a screenshot export action, and an embedded upstream WebUI frame served through `/api/microservices/<id>/proxy/` with compact file rows that do not let material-icon fallback text cover file metadata; `FindJob` must show metrics, jobs and drafts as cards/tables; `Pipeline` must show component classes, React Flow graph nodes/edges, run cards, Gantt execution lines and OpenCode Trace timelines as controls; `MET Nonlinear` must show queue rows, GPU/image cards, a real path tree for the project library, structured project/job detail panels, project config preview, `data/` training state, model parameter count, metrics, progress bars, ETA, `epoch/h` speed and history diagnostics as controls; `ClaudeQQ` must show NapCat HTTP/WS/login badges, QR/login panel, event cache, subscriptions and message push controls; the full user-service config, summary, snapshot, jobs preview, drafts and run JSON can only appear after an explicit `查看原始JSON` click.
|
||||
User service pages are covered by the same rule. `Todo Note` must show lists, task tree, filters, reminder input, movement controls, undo/redo and metrics as controls; `OA Event Flow` must show health, live tag stream state, event table, tag filter presets and Trace stats table as controls; `Code Queue` must show queue cards, live transcript, model/cwd/max attempt inputs, judge decision, attempt table, append prompt, interrupt and retry controls; `File Browser` must show D518 as the default target, D601 as an alternate target, a screenshot export action, and an embedded upstream WebUI frame served through `/api/microservices/<id>/proxy/` with compact file rows that do not let material-icon fallback text cover file metadata; `FindJob` must show metrics, jobs and drafts as cards/tables; `Pipeline` must show component classes, React Flow graph nodes/edges, run cards, Gantt execution lines and OpenCode Trace timelines as controls; `MET Nonlinear` must show queue rows, GPU/image cards, a real path tree for the project library, structured project/job detail panels, project config preview, `data/` training state, model parameter count, metrics, progress bars, ETA, `epoch/h` speed and history diagnostics as controls; `ClaudeQQ` must show NapCat HTTP/WS/login badges, QR/login panel, event cache, subscriptions and message push controls; the full user-service config, summary, snapshot, jobs preview, drafts, OA events and run JSON can only appear after an explicit `查看原始JSON` click.
|
||||
|
||||
## Public Boundary Rule
|
||||
|
||||
|
||||
@@ -6,7 +6,13 @@ UniDesk 前端是 React 组件化工业控制台,不追求展示型大屏效
|
||||
|
||||
frontend 应用源码必须使用 TypeScript + React,禁止在 `src/components/frontend` 中维护手写 `.js` / `.jsx` 应用源码。浏览器请求的 `/app.js` 只能由 frontend Bun server 从 `src/components/frontend/src/app.tsx` 及其 TSX imports 转译生成;`public/` 目录只保存 HTML/CSS 等静态资产,不提交手写 `app.js`。
|
||||
|
||||
`src/components/frontend/src/app.tsx` 只承担应用 shell、登录、全局数据加载、主模块/子标签路由和通用控制台页面。用户服务前端必须模块化到独立 TSX 文件,禁止继续把所有业务页面堆进 `app.tsx`。当前长期固定入口为:`todo-note.tsx` 承载 Todo Note 工作台,`findjob.tsx` 承载 FindJob 工作台,`pipeline.tsx` 承载 Pipeline 工作台,`met-nonlinear.tsx` 承载 MET Nonlinear 训练编排工作台,`claudeqq.tsx` 承载 ClaudeQQ QQ 消息网关工作台,`baidu-netdisk.tsx` 承载 Baidu Netdisk 存储工作台,`code-queue.tsx` 承载 Code Queue 控制台;新增用户服务也必须按同样规则新增独立页面模块,并由 `app.tsx` 只做导入和路由分发。
|
||||
`src/components/frontend/src/app.tsx` 只承担应用 shell、登录、全局数据加载、主模块/子标签路由和通用控制台页面。用户服务前端必须模块化到独立 TSX 文件,禁止继续把所有业务页面堆进 `app.tsx`。当前长期固定入口为:`todo-note.tsx` 承载 Todo Note 工作台,`findjob.tsx` 承载 FindJob 工作台,`pipeline.tsx` 承载 Pipeline 工作台,`met-nonlinear.tsx` 承载 MET Nonlinear 训练编排工作台,`claudeqq.tsx` 承载 ClaudeQQ QQ 消息网关工作台,`baidu-netdisk.tsx` 承载 Baidu Netdisk 存储工作台,`code-queue.tsx` 承载 Code Queue 控制台,`oa-event-flow.tsx` 承载统一 OA 事件流控制台;新增用户服务也必须按同样规则新增独立页面模块,并由 `app.tsx` 只做导入和路由分发。
|
||||
|
||||
## Shared Dialog Component
|
||||
|
||||
通用模态弹窗统一复用 `src/components/frontend/src/dialog.tsx` 的 `UniDeskDialog`。新增确认弹窗、选择弹窗、表单弹窗或小窗口不得在业务页面内重复手写 `modal-backdrop` / `raw-dialog` 结构;业务页面只传入 title、body、actions、test id、关闭行为和少量业务 class。这样可以统一 `role="dialog"`、`aria-modal`、关闭按钮、点击遮罩关闭、滚动边界、action 区和 E2E 定位语义。
|
||||
|
||||
Code Queue 的 queue 合并弹窗是该公共组件的首个业务复用示例;后续用户服务前端需要独立小窗口时应优先复用 `UniDeskDialog`,只有原始 JSON 查看器这类已有专用展示器可以继续保留自己的内容渲染。
|
||||
|
||||
## Deployment Contract
|
||||
|
||||
@@ -32,7 +38,7 @@ frontend 所有默认可见的时间、日期、时钟、更新时间、心跳
|
||||
|
||||
## Layout
|
||||
|
||||
左侧边栏只切换主模块:运行总览、资源节点、任务调度、用户服务、系统配置。顶部标签只切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,用户服务下的服务目录、Todo Note、FindJob、Pipeline、MET Nonlinear、ClaudeQQ、Baidu Netdisk、Code Queue 只属于用户服务,和运行总览、任务调度、系统配置没有重复或共享语义。桌面端左侧边栏必须支持收起,只保留模块 code 和展开按钮,以便最大化主面板空间;移动端左侧边栏会转为顶部横向主模块条,但高度必须在不同主模块之间保持一致,并保持窄条、单行、不换行;主内容区无论内容多少都必须从顶部向下排列,空状态也不得上下居中制造大块留白。
|
||||
左侧边栏只切换主模块:运行总览、资源节点、任务调度、用户服务、系统配置。顶部标签只切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,用户服务下的服务目录、Todo Note、FindJob、Pipeline、MET Nonlinear、ClaudeQQ、Baidu Netdisk、Code Queue、OA Event Flow 只属于用户服务,和运行总览、任务调度、系统配置没有重复或共享语义。桌面端左侧边栏必须支持收起,只保留模块 code 和展开按钮,以便最大化主面板空间;移动端左侧边栏会转为顶部横向主模块条,但高度必须在不同主模块之间保持一致,并保持窄条、单行、不换行;主内容区无论内容多少都必须从顶部向下排列,空状态也不得上下居中制造大块留白。
|
||||
|
||||
## Route Model
|
||||
|
||||
@@ -88,17 +94,18 @@ frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL
|
||||
- `FindJob` 子标签必须把 D601 findjob 后端渲染为 UniDesk React 控件,包括岗位指标、岗位预览、草稿报告和显式原始 JSON 按钮。
|
||||
- `ClaudeQQ` 子标签必须把 D601 ClaudeQQ 后端渲染为 UniDesk React 控件,包括 NapCat 容器登录二维码、NapCat HTTP/WS 状态、事件缓存、QQ 事件订阅表、订阅创建表单、消息推送表单、主用户私聊账号 `645275593` 标记、最近 QQ 事件、已发送记录和显式原始 JSON 按钮。
|
||||
- `Baidu Netdisk` 子标签必须把主 server `baidu-netdisk-backend` 后端渲染为 UniDesk React 控件,包括 OAuth 设备码二维码/用户码登录、账号容量、配置工作根文件浏览(当前默认百度网盘根目录 `/`)、staging 目录上传/下载任务、上传/下载自测按钮与 MD5 结果、脱敏安全说明、日志摘要和显式原始 JSON 按钮;不得把 access token、refresh token、dlink 或 staging 文件字节流裸露到浏览器。
|
||||
- `Code Queue` 子标签必须把主 server `code-queue-backend` 后端渲染为 UniDesk React 控件,包括多 queue lane、queue 内串行、queue 间并行、queue 合并(只能合并任务归属、不能删除源 queue,合并后按原 queueEnteredAt/createdAt 时间顺序串行)、任务 ID/复制任务 ID、引用按钮、任务耗时、任务提交/批量提交、引用任务 ID、创建成功提示、清空输入、模型下拉、显式入队份数、默认模型 `gpt-5.5`、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、运行中追加 prompt、打断、手动重试和显式原始 JSON 按钮;Codex CLI-like 输出流必须始终保留任务的初始 `Submitted prompt` 和运行中 `Steer prompt`;整个 agent loop 消息流统一命名为专有名词 `Trace`,`Trace` 包含 assistant message、user prompt、system event 和 tool call;Code Queue 与 Pipeline/OpenCode messages 必须共用 `src/components/frontend/src/trace.tsx` 的 Trace 公共组件、统一 Trace item 接口和 codex/opencode port 适配层;连续 read/edit/run 工具调用只是在 Trace 内折叠为可展开工具调用组,汇总格式至少包含 `xx read, xx edit, xx run`,并展示读取文件、编辑文件、运行命令和耗时摘要;最近 3 个工具调用保持展开,工具调用内容不得自动换行且必须在工具调用块内部横向滚动,工具调用组展开后不得再增加额外左侧缩进;message 与 prompt 必须自动换行,普通 message 不显示左侧项目符号缩进且永不折叠;Trace 首屏可以是摘要预览,但终态任务被选中后必须自动在后台加载完整 Trace,手动“加载完整 Trace”也必须从 Code Queue output archive 分页补齐早期 trace,不得把 preview 的 `hasMore=false` 当成完整历史;即使热状态为控制体积裁剪了早期 raw output,也要从结构化 `basePrompt/displayPrompt/promptHistory` 和 archive 合成完整用户输入与 agent trace,并且初始 prompt 默认显示注入前 prompt 而不是引用注入全文;当初始 prompt 含引用注入时,引用内容必须默认折叠,并只在 Trace 的初始消息中提供可展开的“最终传入 Codex 的真实完整 prompt”,不得再渲染独立 Prompt 全量卡片;多轮引用注入必须按上游/最早上下文在前、直接引用在后的顺序排列,每一轮必须有明确 `Reference Round N/M` 分割线和时间范围,不能用固定 6 轮截断引用链;点击队列引用按钮必须自动把该任务 ID 写入提交表单的引用输入框,引用任务 ID 创建新任务时必须自动注入 `bun scripts/cli.ts codex task <taskId>` 的提示;连续执行同一 prompt 应通过入队份数一次性生成多条任务,避免快速连点造成操作员误判。
|
||||
- `OA Event Flow` 子标签必须把主 server `oa-event-flow-backend` 后端渲染为 UniDesk React 控件,包括服务健康、事件表、tag 过滤、SSE live 状态、Trace/STEP stats 表、Code Queue/Pipeline 标签入口和显式原始 JSON 按钮;默认页面不得裸铺完整事件 JSON,事件表只展示结构化摘要,完整 envelope/payload 只能通过 `查看原始JSON` 打开。
|
||||
- `Code Queue` 子标签必须把主 server `code-queue-backend` 后端渲染为 UniDesk React 控件,包括多 queue lane、queue 内串行、queue 间并行、queue 合并(点击“合并 queue”后必须用公共 `UniDeskDialog` 打开独立小窗口,用下拉菜单选择源 queue;不得把源 queue 选择控件塞进正常提交任务的 Queue 选择区;合并后自动删除源 queue,只保留合并后的目标 queue,目标 queue 按原 queueEnteredAt/createdAt 时间顺序串行)、任务 ID/复制任务 ID、引用按钮、任务耗时、任务提交/批量提交、引用任务 ID、创建成功提示、清空输入、模型下拉、显式入队份数、默认模型 `gpt-5.5`、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、运行中追加 prompt、打断、手动重试和显式原始 JSON 按钮;Codex CLI-like 输出流必须始终保留任务的初始 `Submitted prompt` 和运行中 `Steer prompt`;整个 agent loop 消息流统一命名为专有名词 `Trace`,`Trace` 包含 assistant message、user prompt、system event 和 tool call;Code Queue 与 Pipeline/OpenCode messages 必须共用 `src/components/frontend/src/trace.tsx` 的 Trace 公共组件、统一 Trace item 接口和 codex/opencode port 适配层;连续 read/edit/run 工具调用只是在 Trace 内折叠为可展开工具调用组,汇总格式至少包含 `xx read, xx edit, xx run`,并展示读取文件、编辑文件、运行命令和耗时摘要;最近 3 个工具调用保持展开,工具调用内容不得自动换行且必须在工具调用块内部横向滚动,工具调用组展开后不得再增加额外左侧缩进;message 与 prompt 必须自动换行,普通 message 不显示左侧项目符号缩进且永不折叠;Trace 首屏可以是摘要预览,但终态任务被选中后必须自动在后台加载完整 Trace,手动“加载完整 Trace”也必须从 Code Queue output archive 分页补齐早期 trace,不得把 preview 的 `hasMore=false` 当成完整历史;即使热状态为控制体积裁剪了早期 raw output,也要从结构化 `basePrompt/displayPrompt/promptHistory` 和 archive 合成完整用户输入与 agent trace,并且初始 prompt 默认显示注入前 prompt 而不是引用注入全文;当初始 prompt 含引用注入时,引用内容必须默认折叠,并只在 Trace 的初始消息中提供可展开的“最终传入 Codex 的真实完整 prompt”,不得再渲染独立 Prompt 全量卡片;多轮引用注入必须按上游/最早上下文在前、直接引用在后的顺序排列,每一轮必须有明确 `Reference Round N/M` 分割线和时间范围,不能用固定 6 轮截断引用链;点击队列引用按钮必须自动把该任务 ID 写入提交表单的引用输入框,引用任务 ID 创建新任务时必须自动注入 `bun scripts/cli.ts codex task <taskId>` 的提示;连续执行同一 prompt 应通过入队份数一次性生成多条任务,避免快速连点造成操作员误判。
|
||||
- `Code Queue` 前端改进必须在同一任务内重建并上线公网 frontend,不能只修改源码或本地 bundle;重建 frontend 是无状态 WebUI 替换,不会导致 Code Queue 长期任务失败。已结束未读任务只能在 task card 边角显示类似未读消息的 `codex-unread-badge` 圆点和“标为已读”操作,不得把整张卡片改成红色/琥珀色失败态边框、背景或胶囊标签;状态栏的“结束未读”提示也不得使用失败态红色。
|
||||
- `Code Queue` 前端必须把 PostgreSQL-backed backend API 作为 task、queue、readAt/未读状态和 attempt 状态的唯一数据来源;不得用 `localStorage`、`sessionStorage` 或 IndexedDB 持久化这些业务状态,也不得在后端标记已读失败时伪造本地成功。前端允许保留 React 内存态、请求 in-flight guard 和本轮页面缓存,但刷新页面或切换设备后的状态必须完全由后端 PostgreSQL 数据恢复。
|
||||
- `Code Queue` 前后端通信必须采用渐进式披露:首屏只请求 queue/task 轻量摘要、必要的 selected preview 和小体积统计,不得默认拉取完整 transcript、raw output、原始 JSON 或全部历史任务;加载下一页或搜索分页时必须显式传递 `selected=0`、`includeActive=0`、`stats=0` 等价开关,避免每一页重复请求 selected/active/stats;点击/选中 task 后再按需加载 summary、prompt part、trace step、raw output 或完整 Trace。`read`/`mark all read` 应调用专用 mutation 并用后端返回的 patch 更新当前内存态,不能为了隐藏未读圆点而强制刷新完整 overview;请求仍需遵守 PostgreSQL 权威源,失败时不得本地伪造已读。Code Queue 性能问题应优先通过缩小 API 响应、分页/cursor、去重 in-flight 请求、短 TTL 且 mutation 失效的页面缓存和后端 SQL 聚合解决,避免以重写渲染层或把大 JSON 藏在 DOM/React state 中规避慢请求。
|
||||
- `Code Queue` 的 queue/session 左侧边栏必须提供 task 关键词搜索,并采用顶部对齐和内容高度优先布局:搜索栏、列表、分组和 task card 都不得用居中、space-between、stretch 或隐式等高网格去拉满侧栏高度;item 少时允许下半部分留空,不能把单个 item 拉高来铺满;每个 task card 必须显示 `最近更新: ...前` 这类相对更新时间,便于判断运行中的 Trace 是否卡住;`queued` task card 的状态徽标必须显示排队原因,例如 `QUEUED(PREV TASK)`、`QUEUED(MEM LIMIT)`。SSE/事件驱动刷新只能更新队列列表与当前选中任务的详情,不得因为其他任务产生 `task-step` 或 `task-updated` 而自动切换当前选中的 task;只有用户点击任务、提交新任务或显式切换 queue 才能改变选中任务。提交任务时必须立即锁定 prompt、引用 ID、queue、模型、工作目录、最大尝试和入队份数等输入控件,显示等待状态,并用前端 in-flight guard 阻止重复点击造成重复入队;当解析到多个待入队任务时必须显式要求用户勾选批量确认,防止 `---` 分隔或入队份数误操作导致错误传入多个任务。Trace 面板的主滚动条使用全站细窄现代滚动条;工具调用块内部的横向滚动必须可滚动但隐藏横向滚动条,避免移动端阅读被滚动条占用。公共 `TraceView` 的自动滚动必须采用 follow-tail 语义:只有当前滚动位置在底部附近时才跟随新增输出;用户手动向上滚动后立即暂停自动滚动,异步刷新不得把视图拉回底部,直到用户再次滚动到最底部才恢复自动跟随。
|
||||
- `Code Queue` 的 queue/session 左侧边栏必须提供 task 关键词搜索,并采用顶部对齐和内容高度优先布局:搜索栏、列表、分组和 task card 都不得用居中、space-between、stretch 或隐式等高网格去拉满侧栏高度;item 少时允许下半部分留空,不能把单个 item 拉高来铺满;每个 task card 必须显示 `最近更新: ...前` 这类相对更新时间,便于判断运行中的 Trace 是否卡住;`queued` task card 的状态徽标必须显示排队原因,例如 `QUEUED(PREV TASK)`、`QUEUED(MEM LIMIT)`。Code Queue 的事件驱动刷新必须订阅统一 `oa-event-flow` 的 `service:code-queue` tag stream,并消费 `trace-stats-updated` 等统计变化事件;旧 Code Queue 私有 SSE 不得作为 Trace/STEP 刷新权威。左侧 STEP、执行过程摘要页的 read/edit/run/STEP 只能显示 `oa_trace_stats` 投影值;投影缺失时显示同步中/`--`,不得回退到 task JSON、transcript 或前端 `toolCallCount` 重算。事件驱动刷新只能更新队列列表与当前选中任务的详情,不得因为其他任务产生 `task-step` 或 `task-updated` 而自动切换当前选中的 task;只有用户点击任务、提交新任务或显式切换 queue 才能改变选中任务。提交任务时必须立即锁定 prompt、引用 ID、queue、模型、工作目录、最大尝试和入队份数等输入控件,显示等待状态,并用前端 in-flight guard 阻止重复点击造成重复入队;当解析到多个待入队任务时必须显式要求用户勾选批量确认,防止 `---` 分隔或入队份数误操作导致错误传入多个任务。Trace 面板的主滚动条使用全站细窄现代滚动条;工具调用块内部的横向滚动必须可滚动但隐藏横向滚动条,避免移动端阅读被滚动条占用。公共 `TraceView` 的自动滚动必须采用 follow-tail 语义:只有当前滚动位置在底部附近时才跟随新增输出;用户手动向上滚动后立即暂停自动滚动,异步刷新不得把视图拉回底部,直到用户再次滚动到最底部才恢复自动跟随。
|
||||
- 用户服务页面不得 iframe 业务旧前端、Todo Note 原 Vite 前端或 Pipeline 自身 WebUI,不得把用户服务后端端口暴露为浏览器直连 URL,也不得把业务 API 的 JSON 裸铺在页面上。
|
||||
- `Pipeline` 子标签是 D601 `/home/ubuntu/pipeline` 的 UniDesk host UI。
|
||||
- Pipeline 仓库自带 WebUI 前端已经废弃;UniDesk frontend 是唯一用户可见的 Pipeline UI。
|
||||
- Pipeline 用户服务只提供 backend/control API,UniDesk 通过 `/api/microservices/pipeline/proxy/...` 拉取 snapshot、Gantt DTO、node detail 和控制接口。
|
||||
- Pipeline 页面必须通过同源用户服务代理展示 `model/minimax-m27` 的 MiniMax 限额,包括当前窗口总量/已用/剩余、重置时间和查询状态;主界面不得展示 API key,完整 quota JSON 只能通过显式 `查看原始JSON` 打开。
|
||||
- Pipeline 控制与观测的最终权威是 100% OA 事件流;分阶段迁移不得在交付态保留点对点控制、旧审核事件或旧 batch 推进逻辑,完整规则见 `docs/reference/pipeline-oa-event-flow.md`。
|
||||
- Pipeline 控制与观测的最终权威是统一 `oa-event-flow` 事件流与 Pipeline OA 状态机;分阶段迁移不得在交付态保留点对点控制、旧审核事件或旧 batch 推进逻辑,共享事件表与 tag 订阅规则见 `docs/reference/oa-event-flow.md`,Pipeline 控制流完整规则见 `docs/reference/pipeline-oa-event-flow.md`。
|
||||
- 基础视图必须包含组件矩阵、React Flow 控制图框图、epoch 列表、运行材料索引、epoch 甘特图和 node 精细控制面板。首屏信息顺序必须把核心操作前置:`控制图` 是 Pipeline hero 之后的第一个业务面板,`Epoch 甘特图` 紧随其后;观测指标、评分器、MiniMax 限额、OA 事件流和组件矩阵只能排在这两个核心面板之后,移动端也必须保持同一 DOM 顺序。控制图和甘特图的右侧详情栏默认收起,主图占满可用宽度;点击控制图 node 或甘特图执行线/事件点后才展开对应右侧栏,并必须提供手动收起入口。
|
||||
- Pipeline scorer 结果必须结构化展示为 `x/N`、通过率、scorer 状态、item pass/fail badge 和 raw inspector 入口;主界面不得裸展示 scorer JSON。
|
||||
- 用户点击控制图中的 node 后,必须通过同源用户服务代理抓取该 node 执行过程,并支持向运行中 node 追加 prompt、给下次尝试下发 guide、对已完成 node 排队 modify、提交 monitor 审核 approve,以及排队 restart/redo。
|
||||
@@ -125,9 +132,9 @@ frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL
|
||||
- run 级图形事实数据使用 `GET /api/node-control/runs/{runId}?view=timeline&tail=N`;该接口不得要求返回后端 y 坐标。
|
||||
- node 级 OpenCode 明细来自用户选择后的 `GET /api/node-control/runs/{runId}/nodes/{nodeId}`,避免跨 UniDesk provider HTTP 代理时一次性拉取全 run 的大体积 step 数组。
|
||||
- 这些 monitor/control 事件的权威来源是 Pipeline OA 事件流;Pipeline 后端会为每条事件自动附加当前 `epoch:{runId}` 与 `pipeline:{pipelineId}` scope tag,并在 monitor 订阅时自动追加同一组 scope 限制,避免不同 epoch 之间串流。UniDesk 只消费 Pipeline 暴露的结构化读接口,不直接依赖旧的 monitor append 文件语义;OA 事件流的完整约束见 `docs/reference/pipeline-oa-event-flow.md`。
|
||||
- prompt 点来源于 attempt 级 `controlEventRecords` 中的 prompt-delivered 事件,必要时回退到 `controlPromptRecords` / `monitorPromptRecords`。
|
||||
- prompt 点只能来源于 attempt 级 `controlEventRecords` 中的 prompt-delivered 事件;`controlPromptRecords` / `monitorPromptRecords` 只能作为旧数据取证字段,不得参与交付态 UI 渲染或补点。
|
||||
- Monitor prompt 点必须将 `node-finished`、`node-long-running-observation` 作为一等结构化事件展示;`node-finished` 是中性完成事实,不得携带是否审核的权威策略字段。是否需要审核由 OA backend 根据当前 epoch 的 Pipeline config/topology 判定;审核开启时同一个 node completion 在 UI 中只保留一条完成/等待审核点,不得再生成独立 `legacy node audit request event` 或 `legacy monitor audit request event` 点。
|
||||
- 控制点和 monitor/人工控制箭头来源于 run 级 `controlEvents`,必要时回退到 `controlCommands`。
|
||||
- 控制点和 monitor/人工控制箭头只能来源于 run 级 `controlEvents`;`controlCommands` 不得作为 Gantt/UI 补点或控制显示 fallback。
|
||||
- 控制事件必须依赖后端记录的 `sourceKind`、`sourceNodeId`、`targetNodeId`、`commandId`、`eventId`、`resetNodeIds` 和 `interruptedProcedureRunIds` 等结构化字段识别 monitor、UniDesk frontend、CLI 等发起者,不能通过解析 monitor 自己的 step 文本来反推。
|
||||
- `Pipeline` monitor 审核与详情展示。
|
||||
- Pipeline 页面必须提供结构化 `OA 事件流` 诊断面板,通过 `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` 展示 100% 事件流模式、禁止残留数、no-audit 证据、monitor 审核证据、控制 queued/applied 计数和近期 run 证据;原始 diagnostics 只能通过 `查看原始JSON` 打开。
|
||||
|
||||
@@ -62,6 +62,21 @@ Todo Note 首次迁移或源 JSON 修复时,在主 server 通过 Docker 内网
|
||||
|
||||
Todo Note 数据迁移后必须验证:`microservice proxy todo-note /api/instances` 至少能看到 `CONSTAR`、`大论文`、`找工作`、`小论文`、`事务` 五个迁移清单,总任务数不低于源数据的 100 条;再通过代理创建临时清单、添加任务、切换完成、撤销并删除临时清单,证明写入路径走 PostgreSQL 且不会污染长期数据。
|
||||
|
||||
|
||||
### OA Event Flow On Main Server
|
||||
|
||||
当前 OA Event Flow 作为 `id=oa-event-flow` 的用户服务登记在 `config.json`:
|
||||
|
||||
- Provider:`main-server`,由 backend-core 直接访问同一 Compose 网络内的 `http://oa-event-flow:4255`,公网不发布 `4255`。
|
||||
- 代码引用:`https://github.com/pikasTech/unidesk` 与配置中的 `repository.commitId`;服务源码位于 `src/components/microservices/oa-event-flow`,属于 UniDesk 自有主 server 用户服务。
|
||||
- 部署引用:UniDesk 根仓库 `docker-compose.yml` 中的 `oa-event-flow` service,Dockerfile 为 `src/components/microservices/oa-event-flow/Dockerfile`,容器名为 `oa-event-flow-backend`。
|
||||
- 数据库:事件表、projection offset、Trace/STEP stats 和 Trace step 投影写入主 PostgreSQL;服务启动时自动创建/补齐 schema,不依赖仅首次生效的 database init SQL。
|
||||
- API:`GET /health`;`POST /api/events`;`GET /api/events`;`GET /api/events/stream`;`GET /api/stats/trace`;`GET /api/diagnostics`。
|
||||
- 代理路径:只允许 `/health`、`/logs` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`。
|
||||
- UniDesk 前端:`用户服务 / OA Event Flow` React 页面负责展示服务健康、事件表、tag 过滤、live stream 状态、Trace/STEP stats 表、Code Queue/Pipeline 标签入口和显式原始 JSON 按钮。
|
||||
|
||||
OA Event Flow 在 UniDesk 语境中按共享控制面基础设施管理:不得暴露公网端口,不得把事件或统计权威状态写入 `.state/`;Code Queue 与 Pipeline 都必须通过该服务发布事实事件、订阅 tag stream 和读取统计中心。共享事件流、统计中心和完成门禁见 `docs/reference/oa-event-flow.md`。
|
||||
|
||||
### Project Manager On Main Server
|
||||
|
||||
当前 Project Manager 作为 `id=project-manager` 的用户服务登记在 `config.json`:
|
||||
@@ -109,7 +124,7 @@ Baidu Netdisk 在 UniDesk 语境中按纯后端服务管理:不得暴露百度
|
||||
|
||||
### Code Queue On Main Server
|
||||
|
||||
当前 Code Queue 作为 `id=code-queue` 的用户服务登记在 `config.json`:
|
||||
当前 Code Queue 作为 `id=code-queue` 的用户服务登记在 `config.json`,并接入统一 `oa-event-flow` 发布 Trace/STEP 事实事件与读取统计中心:
|
||||
|
||||
- Provider:`main-server`,由本机 provider-gateway 通过 `microservice.http` 访问同一 Compose 网络内的 `http://code-queue:4222`。
|
||||
- 代码引用:`https://github.com/pikasTech/unidesk` 与配置中的 `repository.commitId`;服务源码位于 `src/components/microservices/code-queue`,属于 UniDesk 自有控制面组件。
|
||||
@@ -136,7 +151,8 @@ Baidu Netdisk 在 UniDesk 语境中按纯后端服务管理:不得暴露百度
|
||||
- 模型选择:默认 Codex 模型是 `gpt-5.5`,内置模型队列包含 `gpt-5.5`、`gpt-5.4-mini`、`gpt-5.4`;`gpt-5.5` 的默认 reasoning effort 必须是 `xhigh`,可通过 `CODE_QUEUE_MODEL_REASONING_EFFORTS` 追加或覆盖模型级默认值;每个入队任务可通过前端模型下拉菜单或 API 覆盖 `model`、`cwd`、`reasoningEffort` 和 `maxAttempts`,`maxAttempts` 上限为 `99`。Judge 判定 `retry` 或非用户取消类 `fail` 时必须继续已有 `codexThreadId`,不能新建 session;重试间隔使用指数退避,从 `1s` 开始,最大 `10min`。MiniMax 不可用而进入 fallback/non-LLM 判定时,当前 attempt 的 429、Too Many Requests、exceeded retry limit、overloaded、stream disconnected 等服务/限流错误应判定为 `retry`,不能当作完成;MiniMax 可用时,这些内容只能作为当前 attempt 的 factual evidence 提供给 MiniMax,不能通过硬编码覆盖 MiniMax 结果。
|
||||
- 状态与日志:`main-server` 默认工作目录为容器内 `/root/unidesk`,该路径映射主 server 的 `~/unidesk`;同时保留 `/workspace` 映射以兼容历史任务。非主 server Provider 的任务默认工作目录为 `/home/ubuntu`,任务 JSON、列表、Trace 摘要和 CLI 查询都必须显示 `providerId` 与最终 `cwd`。Code Queue 的任务、queue、`readAt`/未读状态、attempt、judge、`promptHistory`、active session 元数据、控制状态和 ClaudeQQ 通知 outbox 一律以主 PostgreSQL 为权威,分别写入 `unidesk_code_queue_tasks`、`unidesk_code_queue_queues` 与 `unidesk_code_queue_notifications`;`DATABASE_URL` 是必需配置,服务不得在 PostgreSQL 缺失或不可用时进入文件存储模式。`.state/code-queue/state.json` 不再作为任务或 queue 状态存储,不得重新引入本地 JSON fallback;服务启动必须以 PostgreSQL 为唯一来源恢复队列,并把 running/judging 任务恢复为 retry_wait。主 server 内存很少,Code Queue 必须把“内存是稀缺资源”作为核心设计约束:历史任务列表、详情、统计和只读 Trace 查询优先从 PostgreSQL 直读,进程内只保留当前 running/judging、queued、retry_wait 等调度必需热任务,不得把全部历史 task JSON 长期缓存到 Bun 堆;需要短期热缓存时必须有严格上限、可裁剪、可从 PostgreSQL 和 append-only 输出归档重建。WebUI 不得用 browser `localStorage`、`sessionStorage` 或 IndexedDB 持久化 task/queue/readAt/unread 等业务状态;浏览器只能保留临时 UI 内存缓存,刷新后必须重新从后端读取 PostgreSQL 权威数据。Codex CLI-like output/Trace 的完整记录可以使用 append-only 文件作为日志型归档,但任务状态、未读状态和列表摘要不得依赖这些文件作为权威来源;`/api/tasks/<id>/transcript` 与 `/api/tasks/<id>/output` 必须能分页重建完整历史,不得因为热状态裁剪而丢失早期 trace。热 task JSON 只保留可配置窗口以保证 `/health`、`/api/tasks` 和 PostgreSQL flush 不被长任务拖死;主 server 为 Code Queue 放宽到 600M 容器预算后仍默认 `CODE_QUEUE_IN_MEMORY_OUTPUT_RECORDS=10`、`CODE_QUEUE_IN_MEMORY_EVENT_RECORDS=10`,启动时必须在 PostgreSQL 查询侧裁剪 hot output/events,并只 flush dirty task,禁止启动后无条件重写全量历史 task JSON;更高预算才允许调大热窗口。WebUI 必须支持多 queue 查看、显式创建 queue、提交时下拉选择 queue、提交时下拉选择执行 Provider,并支持把已创建且非 active 的任务移动到其他 queue;queue 内串行,queue 间默认并行且不互相排队;`CODE_QUEUE_MAX_ACTIVE_QUEUES` 仅作为显式配置的全局 active slot 上限,`0` 表示不按 queue 数量限流,内存不足时由 cgroup memory pressure 阻止新 run 并在任务响应中暴露 `QUEUED(MEM LIMIT)`。Code Queue 镜像必须内置 Playwright Chromium 浏览器与系统依赖,并使用 `bun --smol` 运行后端,保证队列任务能直接执行公网 frontend Playwright 回归且主进程内存可控,不得只在宿主机临时安装。日志写入 UniDesk `logs/{YYYYMMDD}/{startStamp}_{YYYYMMDD}_{HH}_code-queue.jsonl`,按小时切片并按日志族默认保留 `1GiB`;Codex app-server 上游产生的 `logs_*.sqlite` 只能作为短暂缓冲,必须由 Code Queue 周期性导出为 `logs/{YYYYMMDD}/{startStamp}_{YYYYMMDD}_{HH}_codex-app-server.jsonl`,导出后删除/压缩已导出的 SQLite 行,避免重新形成 `logs_2.sqlite` 大文件;`/logs` 端点返回最近结构化日志。`/health` 的 `queue.storage.primary` 必须恒为 `postgres`,并通过 `queue.storage.postgresReady`、`queue.devReady` 和 `/api/dev-ready` 暴露 PostgreSQL 可用性、develop-ready 自检、必需工具、Docker socket、`docker compose`、默认工作目录、Codex config 状态和 `/root/.ssh` 共享 SSH key 状态。Codex CLI-like 输出可能很大,服务必须节流状态持久化,禁止对每个 output delta 同步重写完整 state 导致 `/health` 和控制 API 卡死;容器 healthcheck 必须使用带超时的 HTTP 探针,不能留下堆积的无超时探针进程。
|
||||
- ClaudeQQ 通知:Code Queue 可通过 backend-core 的 `claudeqq` 用户服务代理调用 `POST /api/push/text`,在每个任务进入 `succeeded`、`failed` 或 `canceled` 终态后向配置目标发送最终 response,并附带 task id、queue、状态、模型、attempt、当前 running/queued/retry_wait 数和任务总耗时;当所有 queue 进入 `0 running / 0 queued` 空闲态时,必须单独发送一次空闲提醒。通知由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED` 控制,目标由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE=private|group`、`CODE_QUEUE_NOTIFY_CLAUDEQQ_USER_ID`、`CODE_QUEUE_NOTIFY_CLAUDEQQ_GROUP_ID` 配置,默认私聊 `645275593`;代理基址、最终 response 最大字符数、单次超时和发送尝试次数分别由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL`、`CODE_QUEUE_NOTIFY_CLAUDEQQ_MAX_RESPONSE_CHARS`、`CODE_QUEUE_NOTIFY_CLAUDEQQ_TIMEOUT_MS` 和 `CODE_QUEUE_NOTIFY_CLAUDEQQ_SEND_ATTEMPTS` 配置。任务终态和队列空闲通知必须先写入 PostgreSQL outbox 表 `unidesk_code_queue_notifications` 再异步发送;不得使用 `.state/code-queue/claudeqq-notifications.json`、`CODE_QUEUE_NOTIFY_CLAUDEQQ_OUTBOX_PATH` 或任何本地 JSON 作为通知权威存储。发送失败、NapCat 离线、代理 502 或容器重启时不能丢通知,必须按 `CODE_QUEUE_NOTIFY_CLAUDEQQ_RETRY_INTERVAL_MS` 指数退避重试并跨进程/容器重启保留。`/health` 的 `queue.notifications.claudeqq` 必须暴露非敏感配置、目标配置状态和 PostgreSQL outbox 统计;`GET /api/notifications/claudeqq` 返回 outbox 明细,`POST /api/notifications/claudeqq/drain` 手动触发发送,`POST /api/notifications/claudeqq/backfill` 可按 `since` 补入某次故障窗口内已终态任务,确保 QQ/NapCat 超时或离线不会让任务完成通知永久丢失。
|
||||
- 代理路径:只允许 `/health`、`/logs` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`、`DELETE`。Code Queue 只在 Compose 内网暴露 `4222/tcp`,不得映射或开放到公网。
|
||||
- OA 接入:Code Queue 后端通过 `OA_EVENT_FLOW_BASE_URL=http://oa-event-flow:4255` 发布每个 TraceView 可见执行行的 `trace-step-created`、幂等种子/乱序校正用 `trace-stats-snapshot`、`task-updated` 和 queue 事件;服务启动或手动 backfill 时必须用相同 `eventId` 幂等回放历史 TraceView 可见执行行,避免历史任务停留在旧 STEP 统计口径。前端通过 `oa-event-flow` 的 `service:code-queue` tag stream 更新 STEP 和 Trace Summary,Code Queue 私有 SSE 不再作为刷新权威。`STEP` 表示 TraceView 可见执行行数,工具调用数必须由 `readCount+editCount+runCount` 展示,不能复用 `stepCount`。
|
||||
- 代理路径:只允许 `/health`、`/logs` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`、`DELETE`、`PATCH`。Code Queue 只在 Compose 内网暴露 `4222/tcp`,不得映射或开放到公网。
|
||||
- UniDesk 前端:`用户服务 / Code Queue` React 页面负责展示队列卡片、任务 ID、复制任务 ID、引用按钮、任务耗时、默认模型、模型下拉、执行 Provider 下拉、Provider 对应默认工作目录、显式入队份数、引用任务 ID、清空输入、创建成功提示、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、追加 prompt、打断和手动重试控件;整个 agent loop 消息流统一命名为专有名词 `Trace`,`Trace` 包含 assistant message、user prompt、system event 和 tool call;Code Queue 与 Pipeline/OpenCode messages 必须共用 `src/components/frontend/src/trace.tsx` 的 Trace 公共组件、统一 Trace item 接口和 codex/opencode port 适配层;连续 read/edit/run 工具调用只是在 Trace 内折叠为可展开工具调用组,汇总格式至少包含 `xx read, xx edit, xx run`,并展示读取文件、编辑文件、运行命令和耗时摘要;最近 3 个工具调用保持展开,工具调用内容不得自动换行且必须在工具调用块内部横向滚动,工具调用组展开后不得再增加额外左侧缩进;message 与 prompt 必须自动换行,普通 message 不显示左侧项目符号缩进且永不折叠;点击队列卡片引用按钮必须自动把该任务 ID 写入提交表单的引用任务 ID 输入框;引用任务 ID 创建新任务时必须自动注入 `bun scripts/cli.ts codex task <taskId>` 的提示,让 Codex 读取初始 prompt、最后消息和工具摘要后继续;连续执行同一 prompt 应使用 `入队份数` 一次性生成多条队列任务,而不是依赖快速连点按钮;左侧 queue/session 卡片的 `QUEUED` 状态必须显示原因,例如 `QUEUED(PREV TASK)`、`QUEUED(MEM LIMIT)`、`QUEUED(ACTIVE LIMIT)`;原始任务 JSON 只能通过显式 `查看原始JSON` 打开。
|
||||
|
||||
## D601 User Services
|
||||
@@ -184,7 +200,7 @@ FindJob 在 UniDesk 语境中按纯后端服务管理:默认页面不得 ifram
|
||||
- 代理路径:只允许 `/health` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`,其中 `POST` 仅用于 `/api/node-control/...` 这类 node 控制动作;Pipeline 自身 WebUI 前端已废弃,UniDesk 只访问 Pipeline control backend。
|
||||
- UniDesk 前端:`用户服务 / Pipeline` React 页面负责展示 health、组件数量、React Flow pipeline 控制图框图、epoch 列表、epoch 甘特图、OA/procedure 结构化摘要、运行材料索引、点击 node 后的执行过程抓取、append prompt、guide 和 redo/restart 控件,以及显式原始 JSON 按钮。
|
||||
|
||||
Pipeline 在 UniDesk 语境中按控制与观测后端服务管理:默认页面不得 iframe 或跳转到 Pipeline 自身 WebUI,也不得直接暴露 D601 的 `18082` 到公网。UniDesk frontend 只能通过 `/api/microservices/pipeline/health`、`/api/microservices/pipeline/proxy/api/snapshot?...` 和 `/api/microservices/pipeline/proxy/api/node-control/...` 访问 Pipeline control backend;超大 snapshot 必须使用 `__unideskArrayLimit=registry.components:<limit>,runs:<limit>` 做展示级裁剪。node 控制入口必须走 Pipeline 后端 OA control API,前端不得直接写 `.state`、runner prompt 文件或命令队列;scorer 结果必须在 UniDesk Pipeline UI 中以结构化 score 卡片展示。Pipeline 控制与观测的最终态必须 100% 由 OA 事件流驱动,不得保留点对点控制、旧审核事件或旧 batch 推进逻辑,权威规则见 `docs/reference/pipeline-oa-event-flow.md`。
|
||||
Pipeline 在 UniDesk 语境中按控制与观测后端服务管理:默认页面不得 iframe 或跳转到 Pipeline 自身 WebUI,也不得直接暴露 D601 的 `18082` 到公网。UniDesk frontend 只能通过 `/api/microservices/pipeline/health`、`/api/microservices/pipeline/proxy/api/snapshot?...`、`/api/microservices/pipeline/proxy/api/node-control/...` 和供主 server `oa-event-flow` bridge 使用的 `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` 访问 Pipeline control backend;主 server `oa-event-flow` 的迁移 bridge 当前只允许单一路径读取 `/api/snapshot` 与 `/api/oa-event-flow/diagnostics`,发布 `pipeline-run-snapshot` 到统一事件表,不得再实现 ledger/snapshot 双路径 fallback。超大 snapshot 必须使用 `__unideskArrayLimit=registry.components:<limit>,runs:<limit>` 做展示级裁剪。node 控制入口必须走 Pipeline 后端 OA control API,前端不得直接写 `.state`、runner prompt 文件或命令队列;scorer 结果必须在 UniDesk Pipeline UI 中以结构化 score 卡片展示。Pipeline 控制与观测的最终态必须 100% 由统一 `oa-event-flow` 与 Pipeline OA 状态机驱动,不得保留点对点控制、旧审核事件或旧 batch 推进逻辑;共享事件服务规则见 `docs/reference/oa-event-flow.md`,Pipeline 专有控制流规则见 `docs/reference/pipeline-oa-event-flow.md`。
|
||||
|
||||
Pipeline 的一个 epoch 是同一个 pipeline 从入口到终态完整执行一遍,UniDesk 前端把同一 `pipelineId` 下的多个 run 作为多个 epoch 管理。甘特图必须从 Pipeline snapshot 中的 `startedAt`、`finishedAt`、`durationMs` 和 procedure run 摘要生成,不得按某个 pipeline 实例或 node 名称硬编码布局。甘特图纵轴按时间从上到下排列,左侧固定时间列,后续每列对应一个 node;当前可见时间窗口内没有工作区间的 node 列应自动隐藏。默认页面不得展示裸 JSON、JSONL、worker log 或 control event 行;运行材料和 node 过程只能作为一组一行的结构化索引展示,完整原始内容只能在操作员点击 `查看原始JSON` 后显示。
|
||||
|
||||
@@ -241,7 +257,8 @@ ClaudeQQ 在 UniDesk 语境中按消息网关后端服务管理:不得直接
|
||||
- `bun scripts/cli.ts microservice proxy met-nonlinear '/api/projects?root=projects&limit=500'` 与 `bun scripts/cli.ts microservice proxy met-nonlinear '/api/projects/config?path=projects/<name>' --raw`:验证项目库文件树输入和结构化项目详情;详情应包含 config、progress、data、model、metrics 字段,供前端渲染训练状态、模型参数量和指标。
|
||||
- `bun scripts/cli.ts microservice health claudeqq`、`bun scripts/cli.ts microservice proxy claudeqq /api/napcat/login`、`bun scripts/cli.ts microservice proxy claudeqq /api/events/recent` 和 `bun scripts/cli.ts microservice proxy claudeqq /api/events/subscriptions`:验证 ClaudeQQ 后端、NapCat 容器登录、事件订阅和私有代理链路;消息推送使用 `POST /api/push/text`,不得开放 D601 `3290/3000/3001/6099` 公网端口。
|
||||
- `bun scripts/cli.ts microservice health todo-note` 与 `bun scripts/cli.ts microservice proxy todo-note /api/instances`:验证主 server Todo Note 后端、PostgreSQL 存储和本机 provider-gateway 私有代理链路。
|
||||
- `bun scripts/cli.ts microservice health code-queue` 与 `bun scripts/cli.ts microservice proxy code-queue /api/tasks`:验证主 server Code Queue 后端、PostgreSQL 强制持久化和本机 provider-gateway 私有代理链路;写入、追加 prompt、打断和 readAt/未读状态都必须由 backend 写入 PostgreSQL,frontend 不得用本地存储伪造成功状态。
|
||||
- `bun scripts/cli.ts microservice health oa-event-flow`、`bun scripts/cli.ts microservice proxy oa-event-flow /api/diagnostics --raw` 与 `bun scripts/cli.ts microservice proxy oa-event-flow '/api/events?tags=service:code-queue&limit=20' --raw`:验证统一 OA 事件流、事件表、tag 查询和统计中心。
|
||||
- `bun scripts/cli.ts microservice health code-queue` 与 `bun scripts/cli.ts microservice proxy code-queue /api/tasks`:验证主 server Code Queue 后端、PostgreSQL 强制持久化、统一 OA 事件发布和本机 provider-gateway 私有代理链路;写入、追加 prompt、打断和 readAt/未读状态都必须由 backend 写入 PostgreSQL,frontend 不得用本地存储伪造成功状态。
|
||||
- `bun scripts/cli.ts microservice health filebrowser`、`bun scripts/cli.ts microservice health filebrowser-d601` 与 `bun scripts/cli.ts microservice proxy filebrowser / --max-body-bytes 2000`:验证 D518 主 File Browser 和 D601 备用 File Browser 私有代理链路;浏览器 WebUI 必须通过 `/api/microservices/filebrowser/proxy/` 或 `/api/microservices/filebrowser-d601/proxy/` 访问,不得直接开放 `4251` 公网端口。
|
||||
- `bun scripts/cli.ts --main-server-ip 74.48.78.17 microservice health findjob`:在计算节点或其他非主 server 主机上通过公网 frontend remote CLI 进行同一验证,不需要主 server SSH key。
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# Unified OA Event Flow
|
||||
|
||||
Unified OA Event Flow 是 UniDesk 的事件流与统计投影基础设施。它独立部署为主 server 用户服务 `id=oa-event-flow`,为 Pipeline、Code Queue 以及后续 agent runtime 提供统一的事实事件写入、按 tag 订阅、事件表查询和统计中心读接口。Pipeline 专有的流程推进规则仍以 `docs/reference/pipeline-oa-event-flow.md` 为准;本文件只定义跨业务共享的事件存储、订阅和统计中心契约。
|
||||
|
||||
## Architecture Boundary
|
||||
|
||||
- `oa-event-flow` 必须是独立微服务,运行在主 server Compose 中,只暴露 Docker 内网端口,不开放公网端口;浏览器只能通过 UniDesk frontend 同源 `/api/microservices/oa-event-flow/...` 访问。
|
||||
- 业务服务只发布事实事件或控制意图,不直接修改其他服务状态;统计中心只消费事件流更新数据库投影,并通过读接口和派生事件暴露统计变化。
|
||||
- 前端页面不得轮询业务 transcript、JSONL、runner 私有文件或旧 SSE 来推断 STEP/Trace 统计;应订阅 `trace-stats-updated` 等统计变化事件,并按需拉取 `oa_trace_stats` 投影。
|
||||
- 事件表、投影表和 projection offset 必须写入主 PostgreSQL;`.state/` 只能保存日志、缓存或可重建工件,不能作为事件或统计权威状态。
|
||||
- 兼容迁移只能在同一重构闭环内存在;交付态不得保留两套互相竞争的 STEP 统计、Trace 刷新或 Pipeline 推进权威来源。
|
||||
|
||||
## Event Envelope
|
||||
|
||||
所有写入 `oa_events` 的事件都必须使用统一 envelope:
|
||||
|
||||
- `eventId`:全局稳定幂等键;重复发布同一事实必须命中同一 `eventId`,不能造成重复统计。
|
||||
- `type`:事实事件或派生事件类型,例如 `trace-step-created`、`task-updated`、`node-finished`、`trace-stats-updated`。
|
||||
- `createdAt`:事件发生时间;服务端可补齐但不得覆盖调用方提供的真实事实时间。
|
||||
- `sourceKind` 与 `sourceId`:事件来源类型和来源实例,例如 `service/code-queue`、`service/pipeline`、`projection/trace-stats`。
|
||||
- `aggregateType` 与 `aggregateId`:事件所属聚合,例如 `task/<taskId>`、`pipeline/<pipelineId>`、`epoch/<runId>`。
|
||||
- `correlationId` 与可选 `causationId`:跨服务追踪和派生事件回溯链路。
|
||||
- `tags`:字符串数组,用于 scope、业务标签、订阅和投影筛选。
|
||||
- `payload`:事件事实数据;只放事实和必要摘要,不放未分页的大 transcript、完整日志或 secret。
|
||||
|
||||
## Tag Contract
|
||||
|
||||
Tag 是 OA 事件流的订阅和投影索引。所有 tag 必须是稳定字符串,不依赖 UI 文案。
|
||||
|
||||
- 共享 tag:`service:<id>`、`trace`、`stats`、`control`、`diagnostics`。
|
||||
- Code Queue scope:`service:code-queue`、`task:<taskId>`、`queue:<queueId>`、`attempt:<index>`。
|
||||
- Pipeline scope:`service:pipeline`、`pipeline:<pipelineId>`、`epoch:<runId>`、`node:<nodeId>`、`attempt:<attemptId>`、`procedure:<procedureRunId>`。
|
||||
- 订阅接口按 tag 取交集:`tags=service:code-queue,task:<taskId>` 只返回同时带有这两个 tag 的事件。
|
||||
- 事件生产者必须自动附加业务 scope tag;前端只传当前页面或当前选择的 tag,不在浏览器里拼接跨业务控制语义。
|
||||
|
||||
## Event API
|
||||
|
||||
`oa-event-flow` 至少提供以下后端接口:
|
||||
|
||||
- `GET /health`:返回数据库 ready、事件数、投影数、SSE client 数和服务启动时间。
|
||||
- `POST /api/events`:批量或单条写入 OA 事件;服务端按 `eventId` 幂等去重,成功写入后同步推进内置投影。
|
||||
- `GET /api/events?tags=<tag,...>&afterSeq=<n>&limit=<n>`:查询事件表,默认按 `sequence ASC` 返回有界结果。
|
||||
- `GET /api/events/stream?tags=<tag,...>&afterSeq=<n>`:SSE 订阅事件表;连接建立后先补发 `afterSeq` 之后的有界 backlog,再推送 live event。
|
||||
- `GET /api/stats/trace?scopeId=<scope>` 或 `scopeIds=<scope,...>`:读取 Trace/STEP 统计投影,`scope` 形如 `task:<taskId>`、`task:<taskId>:attempt:<index>` 或 `pipeline:<pipelineId>`。
|
||||
- `GET /api/diagnostics`:返回事件表、投影表、近期事件类型、近期统计变化和订阅客户端摘要。
|
||||
|
||||
## Statistics Center
|
||||
|
||||
统计中心是事件流内置投影,不由前端或业务页面临时重算:
|
||||
|
||||
- `oa_trace_stats` 是 Trace/STEP 统计权威表,按 `scopeId` 保存 `statsRevision`、`stepCount`、`llmStepCount`、`readCount`、`editCount`、`runCount`、`errorCount`、`traceLineCount`、`outputMaxSeq`、attempt 摘要和 `updatedAt`;其中 `stepCount` 表示 TraceView 可见执行行数,不等同于工具调用数,工具调用数由 `readCount+editCount+runCount` 表达。
|
||||
- Code Queue 任务级 scope `task:<taskId>` 只表达整个任务的累计统计;每个执行过程摘要必须使用独立 attempt scope `task:<taskId>:attempt:<index>`。同一个 `trace-step-created` 事实可由统计投影同时写入任务级和 attempt 级 `oa_trace_steps/oa_trace_stats`,但前端 attempt 卡片只能读取自己的 attempt scope,不得复用任务级累计统计。
|
||||
- 投影只消费已经写入 `oa_events` 的事实事件;不得直接读取 Code Queue transcript、Pipeline `.state` 或 runner JSONL 作为实时统计来源。
|
||||
- `trace-stats-snapshot` 可以作为迁移、服务重启或事件乱序时的权威种子事件;它仍必须进入事件表,由统计投影消费,不能绕过事件流直接写投影。投影消费 `trace-step-created` 时必须以 `oa_trace_steps` 的幂等行集合更新统计,不能把 snapshot 和 step 事件对同一个 step 重复累加。
|
||||
- 投影更新后必须发布派生事件 `trace-stats-updated`,带 `stats` payload 和 `stats` tag;前端订阅该事件来更新左侧 STEP、Trace 摘要指标和 OA 可视化页面。
|
||||
- 统计投影必须按 `eventId` 幂等;重复事实不能重复增加 STEP 或 read/edit/run 计数。
|
||||
|
||||
## Code Queue Integration
|
||||
|
||||
- Code Queue 不再维护独立 SSE 作为前端 Trace/STEP 刷新权威;任务输出、状态变更、queue 变更和统计快照必须发布到 `oa-event-flow`。
|
||||
- Code Queue 左侧 task card 的 `STEP`、选中 task 的 Trace Summary 和全局统计只能读取 `oa_trace_stats`;本地 task JSON 中的历史字段只能作为发布 snapshot 事件的输入,不作为前端权威统计来源。统计尚未投影完成时必须明确显示 `statsSource=unavailable` 或 `STEP --`,不得回退到 transcript、本地 `stepCount` 或前端重算。
|
||||
- 运行中每个新的 TraceView 可见执行行都必须发布 `trace-step-created`,并带 `task:<taskId>`、`queue:<queueId>`、`attempt:<index>`、`service:code-queue`、`trace` tag,以及 payload 中的 `scopeId=task:<taskId>`、`attemptIndex` 和 `attemptScopeId=task:<taskId>:attempt:<index>`;统计中心据此幂等更新任务级累计统计和 attempt 级独立统计,message/system/error 行只增加 STEP 或 error,不伪装为工具调用。
|
||||
- Trace Summary 顶部执行摘要读取任务级 `task:<taskId>`;执行过程摘要 `#<index>` 读取 `task:<taskId>:attempt:<index>`。如果 attempt scope 尚未投影完成,必须显示 `statsSource=unavailable` 或 `--`,不得回退到任务级累计统计、transcript 重算或旧本地字段。
|
||||
- 任务入队、开始、终态、移动 queue、标记已读等状态事实必须发布 `task-updated` 或更具体的事实事件,供事件表和后续审计使用。
|
||||
- Code Queue 服务启动后可对 PostgreSQL 中已有任务回放每个 TraceView 可见执行行的 `trace-step-created`,并发布 `trace-stats-snapshot` 事件完成统计中心种子同步;回放必须使用相同 `eventId` 保持幂等,不得阻塞队列恢复。历史回放必须按 attempt start 行推导 `attemptIndex`,确保重建投影时 attempt scope 可独立恢复。
|
||||
|
||||
## Pipeline Integration
|
||||
|
||||
- Pipeline 运行时的 OA 事实、控制事件和 Gantt 取证事件应写入同一个 `oa-event-flow`,并带 `service:pipeline` 与 Pipeline scope tag。
|
||||
- 当 Pipeline 后端部署在远端 provider 且尚未直接写入主 server `oa-event-flow` 时,主 server `oa-event-flow` 必须通过 backend-core 内网代理启用单一路径 Pipeline snapshot bridge:只读取 Pipeline 的 `/api/snapshot` 与 `/api/oa-event-flow/diagnostics` 结构化接口,并发布稳定的 `pipeline-run-snapshot` 迁移事件,确保统一事件表中可见 `service:pipeline` 与 `epoch:<runId>` 事实。bridge 只做迁移期事件可见性接入,不得再实现“先读 ledger,失败后 snapshot”的双路径 fallback;Pipeline 直接发布到统一事件流后,应删除该 bridge。
|
||||
- Pipeline 后端可继续向 UniDesk 暴露 Pipeline 专用 snapshot、Gantt DTO、node detail 和 control API,但这些 DTO 的权威来源必须是统一 OA 事件流,而不是旧文件通道或旧 batch 推进状态。
|
||||
- UniDesk Pipeline 页面可以保留结构化 Pipeline 诊断面板;跨业务事件表、tag 订阅和原始 OA 事件可见性统一进入 `用户服务 / OA Event Flow` 页面。
|
||||
- Pipeline 接入完成态必须同时满足本文件的共享事件流契约和 `docs/reference/pipeline-oa-event-flow.md` 的 Pipeline 控制流去残留门禁。
|
||||
|
||||
## Frontend Visibility
|
||||
|
||||
`用户服务 / OA Event Flow` 是事件表和统计中心的可视化入口:
|
||||
|
||||
- 页面必须显示服务健康、事件总数、最新 sequence、Trace stats scope 数和 live stream 状态。
|
||||
- 页面必须提供 tag 过滤输入,展示事件表列:sequence、type、source、aggregate、tags、createdAt、payload 摘要和显式 `查看原始JSON`。
|
||||
- 页面必须展示 Trace stats 表,至少包含 scope、service、subject、STEP、read/edit/run、error、outputMaxSeq、statsRevision 和更新时间。
|
||||
- 默认界面不得裸铺完整 JSON;完整事件和统计记录只能通过显式原始 JSON 按钮打开。
|
||||
|
||||
## Completion Gates
|
||||
|
||||
- `config.json`、`docker-compose.yml`、CLI `server rebuild`、`server status/logs` 和 E2E 必须认识 `oa-event-flow`。
|
||||
- Code Queue 前端事件订阅必须指向 `oa-event-flow` 的 tag stream;不得再依赖 Code Queue 私有 `/api/events` SSE 作为刷新权威。
|
||||
- Code Queue Trace Summary 和 task list 响应必须带 `traceStats.source=oa-event-flow` 或明确的 `statsSource=unavailable` 状态;unavailable 时不得返回本地重算 STEP 伪装为权威统计。
|
||||
- Code Queue TraceView 的 attempt 执行摘要必须带 `attemptScopeId`、attempt 级 `traceStats.source=oa-event-flow` 或明确的 `statsSource=unavailable`;前端收到 `task-attempt` 的 `trace-stats-updated` 时只能刷新选中任务 Trace Summary,不能把 attempt 统计覆盖到左侧 task 级 STEP。
|
||||
- Pipeline bridge 启用时,`/api/diagnostics` 必须暴露 `pipelineBridge.mode=snapshot` 或各 run 的 `bridgeMode=snapshot` 状态,`GET /api/events?tags=service:pipeline` 必须能看到带 `epoch:<runId>` 的 Pipeline 事件;远端 Pipeline 后端直接发布到统一服务后,应删除 bridge 作为完成态清理项。
|
||||
- 新增或修改 OA 事件流页面、Code Queue TraceView 或 Pipeline OA 可视化后,必须重建并验证 `oa-event-flow`、相关业务后端和 `frontend`。
|
||||
@@ -4,12 +4,12 @@ Pipeline 的最终控制模型必须是 100% OA 事件流驱动。开发过程
|
||||
|
||||
## Core Principles
|
||||
|
||||
- OA backend 是 Pipeline runtime 的唯一控制总线;node、monitor、runner、UniDesk frontend 和 CLI 都只能通过 OA 事件表达事实、订阅事实或发出控制意图。
|
||||
- 统一 `oa-event-flow` 微服务是跨业务 OA 事件存储、tag 订阅和统计投影基础设施;Pipeline backend 在其上实现 Pipeline runtime 的唯一控制总线。node、monitor、runner、UniDesk frontend 和 CLI 都只能通过 OA 事件表达事实、订阅事实或发出控制意图。
|
||||
- Node 之间不得直接启动、暂停、重试或通知彼此;monitor 不得绕过 OA 直接控制 runner;frontend 不得直接写 Pipeline `.state`、prompt 文件、命令队列或 worker 状态文件。
|
||||
- 事实事件只描述已经发生的事实,策略判断由 OA 加载 Pipeline config/topology 后完成;`node-finished` 不得携带 `legacy audit policy flag`、`nextNodeIds`、`shouldStartDownstream` 这类控制决策字段。
|
||||
- 控制行为必须由 OA 控制/管理事件表达,例如 start downstream、pause、resume、single-step、append prompt、guide、modify、approve、redo/restart、skip、cancel 和 retry。
|
||||
- 如果 procedure 或 monitor 结束后缺少预期 OA 提交,runner 必须把它视为可恢复的 OA contract failure:在重试预算内通过 OA 事件写入明确反馈并启动 fresh attempt,不能直接把一次缺失提交升级成最终失败;预算耗尽后才 fail closed。
|
||||
- UI、CLI、E2E 和调试工具都应消费 OA 归一化读接口或通过 OA 控制 API 写入事件,不得解析 monitor 文本、worker log、runner 私有文件或旧 JSONL 文件来反推控制语义。
|
||||
- UI、CLI、E2E 和调试工具都应消费统一 OA 归一化读接口、`oa-event-flow` 事件表/tag stream 或通过 OA 控制 API 写入事件,不得解析 monitor 文本、worker log、runner 私有文件或旧 JSONL 文件来反推控制语义。
|
||||
- 旧的本地文件传输通道必须完全退出控制路径:不得创建、轮询或依赖 `control-prompts.jsonl`、`monitor-prompts.jsonl`、`monitor-control`、`control-events.jsonl`、monitor stop 文件、`.state/pipeline-runs/{runId}/control/commands/` 或给 monitor 容器挂载可读 `/pipeline-state` 来完成 prompt、stop、guide、approve、redo/restart、append 或 UI/Gantt 事件取证。
|
||||
|
||||
## Benchmark Prompt And Skill Boundary
|
||||
@@ -41,8 +41,8 @@ Pipeline 的最终控制模型必须是 100% OA 事件流驱动。开发过程
|
||||
|
||||
## Event Identity And Tags
|
||||
|
||||
- 每条事件必须有稳定 `eventId`、`type`、`createdAt`、`sourceKind`、`sourceId`、`correlationId` 和可选 `causationId`,用于排序、幂等、回放和追踪控制来源。
|
||||
- Pipeline 后端必须自动附加 scope tag:`pipeline:{pipelineId}`、`epoch:{runId}`,node 相关事件还必须带 `node:{nodeId}`;attempt/procedure 相关事件应带 `attempt:{attemptId}` 或 `procedure:{procedureRunId}`。
|
||||
- 每条事件必须满足统一 OA envelope:稳定 `eventId`、`type`、`createdAt`、`sourceKind`、`sourceId`、`aggregateType`、`aggregateId`、`correlationId`、可选 `causationId`、`tags` 和 `payload`,用于排序、幂等、回放和追踪控制来源;共享字段契约见 `docs/reference/oa-event-flow.md`。
|
||||
- Pipeline 后端必须自动附加 scope tag:`service:pipeline`、`pipeline:{pipelineId}`、`epoch:{runId}`,node 相关事件还必须带 `node:{nodeId}`;attempt/procedure 相关事件应带 `attempt:{attemptId}` 或 `procedure:{procedureRunId}`。
|
||||
- Monitor 在 config 中只声明要订阅的业务 tag 或事件类型,OA backend 在订阅时自动追加当前 `pipeline:{pipelineId}` 与 `epoch:{runId}` 限制,避免不同 pipeline 或 epoch 串流。
|
||||
- 一次 epoch 应绑定 config snapshot 或 config version;OA 的审核、拓扑推进、并发和单步策略都以该快照为准,避免运行中配置漂移导致控制解释不一致。
|
||||
- 控制事件必须有幂等键,重复投递同一个 start、approve、redo/restart 或 append prompt 时,OA 应识别为同一意图而不是重复启动或重复写入。
|
||||
@@ -89,7 +89,8 @@ Pipeline 的最终控制模型必须是 100% OA 事件流驱动。开发过程
|
||||
|
||||
## Migration And Completion Gates
|
||||
|
||||
- 分阶段重构只允许按“schema 文档与测试门禁 -> OA 事件写入 -> OA dispatcher 接管控制 -> UI/CLI 改为 OA 读写 -> 删除旧事件和旧控制路径”的方向前进,不得把双写/双读作为最终设计。
|
||||
- 分阶段重构只允许按“统一 oa-event-flow schema 文档与测试门禁 -> Pipeline/Code Queue 事实事件写入 -> OA dispatcher 接管 Pipeline 控制 -> UI/CLI 改为 OA 读写 -> 删除旧事件、旧统计和旧控制路径”的方向前进,不得把双写/双读作为最终设计。
|
||||
- 远端 Pipeline 尚未直接发布到主 server `oa-event-flow` 时,可以通过主 server Pipeline snapshot bridge 把 `/api/snapshot` 与 `/api/oa-event-flow/diagnostics` 形成的结构化运行事实以 `service:pipeline`、`pipeline:{pipelineId}` 和 `epoch:{runId}` tag 写入统一事件表;该 bridge 是迁移期单一路径接入层,只能保持幂等可见性,不能成为新的控制权威,也不得保留 ledger/snapshot 双路径 fallback。Pipeline 直接发布统一事件后必须删除该 bridge。
|
||||
- 完成态必须删除或禁用以下残留:`legacy detail audit policy flag` 作为权威策略字段、`legacy node audit request event` / `legacy monitor audit request event` 独立审核请求事件、`legacy batch completion gate` 控制推进、monitor 直接调用 runner、node 直接启动下游、frontend/CLI 直接写 `.state` 文件。
|
||||
- 完成态还必须删除文件传输残留:`PIPELINE_MONITOR_APPEND_FILE`、`PIPELINE_MONITOR_STOP_FILE`、`PIPELINE_MONITOR_CONTROL_DIR`、`PIPELINE_NODE_PROMPT_APPEND_FILE`、`control-events.jsonl`、`runControlEventFile`、`appendJsonLine`、`readJsonLineRecords`、`readPromptAppendFile`、`parseCommandFile`、`parseMonitorCommandFile`、`readMonitorCommands`、`fallbackMonitorInterventions`、`syntheticMonitorStopPrompt` 以及 monitor 的 `/pipeline-state` 挂载都不得出现在运行代码中。
|
||||
- E2E 必须覆盖有审核与无审核两条链路:无审核时由 OA 从 `node-finished` 推进下游;有审核时由 monitor 经 OA 控制事件推进下游;事件都必须带 `pipeline:{pipelineId}` 与 `epoch:{runId}` tag。
|
||||
|
||||
Reference in New Issue
Block a user