feat: integrate codex queue and pipeline oa flow

- add Codex Queue microservice/frontend integration and related deployment docs
- document 100% Pipeline OA event-flow requirements and E2E gates
- harden Pipeline frontend Gantt/timeline E2E assertions and rendering
This commit is contained in:
Codex
2026-05-07 18:11:43 +00:00
parent 2aaf0447a6
commit 79166574e8
27 changed files with 3209 additions and 123 deletions
+1
View File
@@ -59,6 +59,7 @@
- Task commands are delivered over WebSocket and never contain large file content
- All state changes are reported to the main server in real time by Provider Gateway
- The main server writes state updates to PostgreSQL, completing the unified closed loop
- Pipeline workflow control follows the OA event-flow model: OA is the only control bus, factual node events remain policy-neutral, and runner/monitor/frontend/CLI actions are represented as OA events; detailed constraints live in `docs/reference/pipeline-oa-event-flow.md`
- Critical Task Deployment Principles
- Single-point components such as the database, core scheduler logic, and API gateway are deployed on the main server
- The high-availability environment of the main server ensures the critical scheduling path never breaks
+2 -2
View File
@@ -12,7 +12,7 @@ UniDesk 的统一 CLI 入口是根目录 `scripts/cli.ts`,运行方式固定
- `server stop` 创建异步 job,在后台停止固定 Compose project 中的全部 UniDesk 服务。
- `server status` 查询公开端口、内部端口、Compose 容器、core/frontend/provider/database 健康检查和访问 URL。
- `server logs` 返回 `logs/` 文件日志和 Docker 容器日志的尾部,默认限制输出大小,避免日志爆炸。
- `server rebuild <backend-core|frontend|provider-gateway|todo-note>` 创建异步 job,先构建目标服务镜像,构建成功后只按 Compose project/service label 移除该服务旧容器,再用 `--no-deps` 启动目标服务;该命令用于替代手工删除容器的兜底流程,其中 `todo-note` 只重建主 server 承载的 Todo Note 后端,不会重建或删除 database 命名卷。
- `server rebuild <backend-core|frontend|provider-gateway|todo-note|codex-queue>` 创建异步 job,先构建目标服务镜像,构建成功后只按 Compose project/service label 移除该服务旧容器,再用 `--no-deps` 启动目标服务;该命令用于替代手工删除容器的兜底流程,其中 `todo-note``codex-queue` 只重建主 server 承载的对应后端,不会重建或删除 database 命名卷。
- `ssh <providerId> [ssh-like args...]` 通过 backend-core 内网 WebSocket broker 和 provider-gateway 的 Host SSH / WSL SSH 维护桥连接目标节点;无后续参数时进入远端登录 shell,有后续参数时按 ssh 远端命令体验执行并返回远端 exit code。
- `ssh <providerId> apply-patch [tool args...] < patch.diff` 直接调用远端注入的 `apply_patch` 工具,并把本地 stdin 中的标准 `*** Begin Patch` / `*** End Patch` patch 流透传给目标节点。
- `ssh <providerId> py [script-args...] < script.py` 把本地 stdin 落到远端临时 `.py` 文件后再以 `python3 -u` 执行并自动清理,避免再手写 `'python3 -'`、heredoc 或多层引号;`script-args` 会按 argv 安全透传给远端脚本。
@@ -25,7 +25,7 @@ UniDesk 的统一 CLI 入口是根目录 `scripts/cli.ts`,运行方式固定
长时操作采用 Fire-and-Forget 模式:CLI 创建 `.state/jobs/{jobId}.json`,后台进程执行真实命令,并将 stdout、stderr 分别写入 `.state/jobs/{jobId}.stdout.log``.state/jobs/{jobId}.stderr.log`。调用者通过 `bun scripts/cli.ts job status <jobId>` 查询进度和尾部输出。
`server rebuild``server start``server stop` 一样必须通过 job 状态确认结果。重建 frontend 的标准流程是运行 `bun scripts/cli.ts server rebuild frontend`,随后轮询 `bun scripts/cli.ts job status latest``succeeded`,再用 `server status``e2e run` 验证公网 frontend;重建 Todo Note 后端使用 `bun scripts/cli.ts server rebuild todo-note`,随后用 `microservice health todo-note``microservice proxy todo-note /api/instances` 验证。不得把 `docker rm` 手工兜底当成正式交付步骤。
`server rebuild``server start``server stop` 一样必须通过 job 状态确认结果。重建 frontend 的标准流程是运行 `bun scripts/cli.ts server rebuild frontend`,随后轮询 `bun scripts/cli.ts job status latest``succeeded`,再用 `server status``e2e run` 验证公网 frontend;重建 Todo Note 后端使用 `bun scripts/cli.ts server rebuild todo-note`,随后用 `microservice health todo-note``microservice proxy todo-note /api/instances` 验证;重建 Codex Queue 后端使用 `bun scripts/cli.ts server rebuild codex-queue`,随后用 `microservice health codex-queue``microservice proxy codex-queue /api/tasks` 验证。不得把 `docker rm` 手工兜底当成正式交付步骤。
## Output Contract
+1 -1
View File
@@ -26,7 +26,7 @@ TypeScript 运行时固定为 Bun。根目录 CLI、backend-core、frontend 和
`microservices` 定义挂载在计算节点或主 server Docker 中的非核心业务后端。该数组只保存业务仓库 URL、commit id、业务仓库自身 Dockerfile/docker-compose 引用、provider 映射、节点后端端口和 UniDesk frontend 集成入口;不得把业务全量代码复制进 UniDesk。`backend.public` 必须为 `false``backend.frontendOnly` 必须为 `true``backend.allowedPathPrefixes` 必须限制到业务 API 前缀,`backend.allowedMethods` 必须显式列出允许代理的 HTTP 方法;浏览器只能通过 frontend 同源代理访问这些后端。详细规则见 `docs/reference/microservices.md`
主 server 承载的 Todo Note microservice 使用 `providerId=main-server``nodeBaseUrl=http://todo-note:4211``allowedMethods=["GET","HEAD","POST","DELETE"]`,数据库使用主 PostgreSQLD601 的 FindJob 只允许 `GET/HEAD` 展示型读取路径;D601 的 Pipeline 允许 `GET/HEAD/POST`,其中 `POST` 只用于 Pipeline 后端 `/api/node-control/...` 的 append prompt、guide 和 redo/restart 等受控 node 操作。
主 server 承载的 Todo Note microservice 使用 `providerId=main-server``nodeBaseUrl=http://todo-note:4211``allowedMethods=["GET","HEAD","POST","DELETE"]`,数据库使用主 PostgreSQLCodex Queue 使用 `providerId=main-server``nodeBaseUrl=http://codex-queue:4222``allowedPathPrefixes=["/health","/logs","/api/"]`,只通过 frontend/backend/provider-gateway 私有代理访问 Compose 内网服务名;D601 的 FindJob 只允许 `GET/HEAD` 展示型读取路径;D601 的 Pipeline 允许 `GET/HEAD/POST`,其中 `POST` 只用于 Pipeline 后端 `/api/node-control/...` 的 append prompt、guide 和 redo/restart 等受控 node 操作。
## Compose Env Generation
+3 -2
View File
@@ -9,6 +9,7 @@
- `frontend` 是唯一公开 Web 控制台,提供登录、从 TSX 转译出的 React 应用资产和到 backend-core 的同源代理。
- `provider-gateway` 是当前主 server 的本机计算节点代理,通过 WebSocket 主动连到 provider ingress,挂载 `/var/run/docker.sock` 作为自动任务执行主路径,使用 `pid: "host"` 读取节点级进程资源,并周期性上报系统资源指标、进程占用与 Docker daemon 状态;维护用 Host SSH / WSL SSH 私钥目录只读挂载到 `/run/host-ssh`,不得作为自动任务调度主路径。
- `todo-note` 是主 server 承载的 Todo Note 纯后端 microservice,容器名 `todo-note-backend`,只在 Compose 内网暴露 `4211/tcp`,使用主 PostgreSQL 存储迁移后的 Todo Note 数据。
- `codex-queue` 是主 server 承载的 Codex app-server 队列 microservice,容器名 `codex-queue-backend`,仅在 Compose 内网暴露 `4222/tcp` 给本机 provider-gateway 私有代理访问,浏览器只能通过 UniDesk frontend 同源代理查看运行输出、追加 prompt、打断和重试。
## Public Exposure Boundary
@@ -28,7 +29,7 @@ Compose v2 安装后仍然必须遵守 UniDesk 的服务控制入口:全栈生
## Single Service Rebuild
前端、backend-core、本机 provider-gateway 或主 server 承载的 Todo Note microservice 需要重建时,统一使用 `bun scripts/cli.ts server rebuild <service>`,其中 `<service>` 只能是 `backend-core``frontend``provider-gateway``todo-note`。该命令先执行目标服务镜像构建,只有构建成功后才移除旧容器,避免构建失败导致运行中的服务被提前停掉。
前端、backend-core、本机 provider-gateway 或主 server 承载的 Todo Note/Codex Queue microservice 需要重建时,统一使用 `bun scripts/cli.ts server rebuild <service>`,其中 `<service>` 只能是 `backend-core``frontend``provider-gateway``todo-note``codex-queue`。该命令先执行目标服务镜像构建,只有构建成功后才移除旧容器,避免构建失败导致运行中的服务被提前停掉。
单服务重建必须按 Docker Compose label 精确选择旧容器:`com.docker.compose.project` 等于 `config.json` 中的固定 project name`com.docker.compose.service` 等于目标服务名。删除范围不得扩大到其他 Compose project、database 容器、named volume 或未匹配 label 的容器;随后必须通过 CLI 解析出的 Compose 命令执行 `up -d --no-deps <service>` 启动目标服务,避免因为重建 frontend 而连带重启 database 或 backend-core。
@@ -36,7 +37,7 @@ Compose v2 安装后仍然必须遵守 UniDesk 的服务控制入口:全栈生
## Health Criteria
服务跑通的最低标准是:backend-core 内网 `/health` 返回 okfrontend 公网 `/health` 返回 okprovider ingress 公网 `/health` 返回 okdatabase 在容器内 `pg_isready` 可用,Todo Note 后端 `/api/health` 返回 `storage=postgres``/api/nodes` 中出现 `main-server` provider 且状态为 `online``/api/nodes/system-status` 中出现 `main-server` 的 CPU/内存/硬盘采样,`/api/nodes/docker-status` 中出现 `main-server` 的 Docker 快照。交付前还必须运行 `bun scripts/cli.ts e2e run`,并以 `docs/reference/e2e.md` 的门禁作为最终判定。
服务跑通的最低标准是:backend-core 内网 `/health` 返回 okfrontend 公网 `/health` 返回 okprovider ingress 公网 `/health` 返回 okdatabase 在容器内 `pg_isready` 可用,Todo Note 后端 `/api/health` 返回 `storage=postgres`Codex Queue `/health` 返回队列摘要和默认模型,`/api/nodes` 中出现 `main-server` provider 且状态为 `online``/api/nodes/system-status` 中出现 `main-server` 的 CPU/内存/硬盘采样,`/api/nodes/docker-status` 中出现 `main-server` 的 Docker 快照。交付前还必须运行 `bun scripts/cli.ts e2e run`,并以 `docs/reference/e2e.md` 的门禁作为最终判定。
## Database Volume
+8 -4
View File
@@ -31,14 +31,18 @@ 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 microservice ports such as FindJob `3254`, MET Nonlinear `3288` and Todo Note `4211` probes must fail.
- Public exposure: Docker port summary must show only frontend and provider ingress host mappings; public core、public database and known private microservice ports such as FindJob `3254`, MET Nonlinear `3288`, Todo Note `4211` and Codex Queue host port `14222` 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.
- Provider self-connection: internal `GET /api/nodes` must contain `main-server` with `status: online`, `labels.providerGatewayVersion` equal to `src/components/provider-gateway/package.json` and `labels.providerGatewayUpgradePolicy: "always-enabled"`; 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`; 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.
- Microservices: internal `/api/microservices` must include `todo-note` on `main-server` plus `findjob`, `pipeline` and `met-nonlinear` 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/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` and `/api/microservices/pipeline/proxy/api/snapshot?__unideskArrayLimit=registry.components:8,runs:3` must return Pipeline health, registry and run previews; `/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.
- Microservices: internal `/api/microservices` must include `todo-note` and `codex-queue` on `main-server` plus `findjob`, `pipeline` and `met-nonlinear` 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/codex-queue/health` must return a Codex Queue summary with default model `gpt-5.4-mini`, and `/api/microservices/codex-queue/proxy/api/tasks` must return queue state through the same 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.
- 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.
- 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, 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``微服务 / FindJob``微服务 / Pipeline` and `微服务 / MET Nonlinear` to verify 主 server Todo Note、D601、仓库引用、私有后端映射、Todo Note 迁移清单和树形任务、FindJob 指标和岗位预览、Pipeline 组件矩阵、React Flow 控制图、epoch 甘特图和 OpenCode Step Timeline、MET Nonlinear 项目库/Fork/待启动队列/当前队列/已完成/失败诊断/GPU/镜像都通过 React 控件展示。Playwright 还必须验证至少一个深链接直达路由,例如公网 `http://<publicHost>:<frontendPort>/app/pipeline/` 能直接落到 Pipeline 页面,随后切到 `资源节点 / Docker 状态` 时地址栏更新为 `/nodes/docker/`,并且浏览器 history 返回链路仍能回到 `/app/pipeline/`;同时 `态势总览` 这类非微服务页面应落在自己的模块前缀下,例如 `/ops/status/`。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`.
- 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, 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``微服务 / Codex Queue``微服务 / FindJob``微服务 / Pipeline` and `微服务 / MET Nonlinear` to verify 主 server Todo Note/Codex Queue、D601、仓库引用、私有后端映射、Todo Note 迁移清单和树形任务、Codex Queue 队列/模型/输出/追加 prompt/打断控件、FindJob 指标和岗位预览、Pipeline 组件矩阵、结构化 OA 事件流诊断面板、React Flow 控制图、epoch 甘特图、甘特图渲染图导出、monitor 首列排序、长任务观察连线、无观察来源伪点、running node 实时闪动执行条和 OpenCode Step Timeline、MET Nonlinear 项目库/Fork/待启动队列/当前队列/已完成/失败诊断/GPU/镜像都通过 React 控件展示。Playwright 还必须验证至少一个深链接直达路由,例如公网 `http://<publicHost>:<frontendPort>/app/pipeline/` 能直接落到 Pipeline 页面,随后切到 `资源节点 / Docker 状态` 时地址栏更新为 `/nodes/docker/`,并且浏览器 history 返回链路仍能回到 `/app/pipeline/`;同时 `态势总览` 这类非微服务页面应落在自己的模块前缀下,例如 `/ops/status/`。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 右侧边栏、step timeline、详情抽屉或其他高信息密度面板, Playwright acceptance must inspect both `总高度` and `横向滚动条`. For Pipeline specifically, the OpenCode Step Timeline session head must carry shared agent/model/session facts, each step summary must keep time in the header rather than a left narrow column, and Playwright must fail if the timeline or the collapsed step summary introduces an internal horizontal scrollbar or if the collapsed summary height grows beyond a bounded compact threshold.
- OpenCode Step Timeline must also prove that idle gaps between adjacent steps remain visually blank. Playwright should fail if `.pipeline-opencode-flow::before` or any equivalent continuous connector draws across the whole timeline, because an interval such as previous `completedAt` to next `createdAt` is not execution activity.
- Microservice 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 Step Timeline` with role/model/tokens/text/reasoning/tool sections; 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.
## Frontend JSON Rule
@@ -49,7 +53,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.
Microservice 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; `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 step 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; the full microservice config, summary, snapshot, jobs preview, drafts and run JSON can only appear after an explicit `查看原始JSON` click.
Microservice 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; `Codex Queue` must show queue cards, live transcript, model/cwd/max attempt inputs, judge decision, attempt table, append prompt, interrupt and retry controls; `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 step 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; the full microservice config, summary, snapshot, jobs preview, drafts and run JSON can only appear after an explicit `查看原始JSON` click.
## Public Boundary Rule
+21 -7
View File
@@ -6,11 +6,11 @@ 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、登录、全局数据加载、主模块/子标签路由和通用控制台页面。业务 microservice 前端必须模块化到独立 TSX 文件,禁止继续把所有业务页面堆进 `app.tsx`。当前长期固定入口为:`todo-note.tsx` 承载 Todo Note 工作台,`findjob.tsx` 承载 FindJob 工作台,`pipeline.tsx` 承载 Pipeline 工作台,`met-nonlinear.tsx` 承载 MET Nonlinear 训练编排工作台;新增业务 microservice 也必须按同样规则新增独立页面模块,并由 `app.tsx` 只做导入和路由分发。
`src/components/frontend/src/app.tsx` 只承担应用 shell、登录、全局数据加载、主模块/子标签路由和通用控制台页面。业务 microservice 前端必须模块化到独立 TSX 文件,禁止继续把所有业务页面堆进 `app.tsx`。当前长期固定入口为:`todo-note.tsx` 承载 Todo Note 工作台,`findjob.tsx` 承载 FindJob 工作台,`pipeline.tsx` 承载 Pipeline 工作台,`met-nonlinear.tsx` 承载 MET Nonlinear 训练编排工作台`codex-queue.tsx` 承载 Codex Queue 控制台;新增业务 microservice 也必须按同样规则新增独立页面模块,并由 `app.tsx` 只做导入和路由分发。
## Layout
左侧边栏只切换主模块:运行总览、资源节点、任务调度、微服务、系统配置。顶部标签只切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,微服务下的服务目录、Todo Note、FindJob、Pipeline、MET Nonlinear 只属于微服务,和运行总览、任务调度、系统配置没有重复或共享语义。桌面端左侧边栏必须支持收起,只保留模块 code 和展开按钮,以便最大化主面板空间;移动端左侧边栏会转为顶部横向主模块条,但高度必须在不同主模块之间保持一致,并保持窄条、单行、不换行;主内容区无论内容多少都必须从顶部向下排列,空状态也不得上下居中制造大块留白。
左侧边栏只切换主模块:运行总览、资源节点、任务调度、微服务、系统配置。顶部标签只切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,微服务下的服务目录、Todo Note、FindJob、Pipeline、MET Nonlinear、Codex Queue 只属于微服务,和运行总览、任务调度、系统配置没有重复或共享语义。桌面端左侧边栏必须支持收起,只保留模块 code 和展开按钮,以便最大化主面板空间;移动端左侧边栏会转为顶部横向主模块条,但高度必须在不同主模块之间保持一致,并保持窄条、单行、不换行;主内容区无论内容多少都必须从顶部向下排列,空状态也不得上下居中制造大块留白。
## Route Model
@@ -58,13 +58,18 @@ frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL
- `服务目录` 必须显示 service id、Provider、仓库 URL、commit id、业务 Dockerfile/docker-compose 引用、节点后端私有映射、SSH 透传开发入口和运行态容器摘要。
- `Todo Note` 子标签必须把主 server `todo-note-backend` 后端渲染为 UniDesk React 控件,包括迁移清单、树形任务、筛选、提醒、拖放/移动、撤销/重做、字号控制和显式原始 JSON 按钮。
- `FindJob` 子标签必须把 D601 findjob 后端渲染为 UniDesk React 控件,包括岗位指标、岗位预览、草稿报告和显式原始 JSON 按钮。
- `Codex Queue` 子标签必须把主 server `codex-queue-backend` 后端渲染为 UniDesk React 控件,包括串行队列、任务提交/批量提交、默认模型 `gpt-5.4-mini`、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、运行中追加 prompt、打断、手动重试和显式原始 JSON 按钮。
- 业务 microservice 页面不得 iframe 业务旧前端、Todo Note 原 Vite 前端或 Pipeline 自身 WebUI,不得把 microservice 后端端口暴露为浏览器直连 URL,也不得把业务 API 的 JSON 裸铺在页面上。
- `Pipeline` 子标签是 D601 `/home/ubuntu/pipeline` 的 UniDesk host UI。
- Pipeline 仓库自带 WebUI 前端已经废弃;UniDesk frontend 是唯一用户可见的 Pipeline UI。
- Pipeline microservice 只提供 backend/control APIUniDesk 通过 `/api/microservices/pipeline/proxy/...` 拉取 snapshot、Gantt DTO、node detail 和控制接口。
- Pipeline 控制与观测的最终权威是 100% OA 事件流;分阶段迁移不得在交付态保留点对点控制、旧审核事件或旧 batch 推进逻辑,完整规则见 `docs/reference/pipeline-oa-event-flow.md`
- 基础视图必须包含组件矩阵、React Flow 控制图框图、epoch 列表、运行材料索引、epoch 甘特图和 node 精细控制面板。
- Pipeline scorer 结果必须结构化展示为 `x/N`、通过率、scorer 状态、item pass/fail badge 和 raw inspector 入口;主界面不得裸展示 scorer JSON。
- 用户点击控制图中的 node 后,必须通过同源 microservice 代理抓取该 node 执行过程,并支持向运行中 node 追加 prompt、给下次尝试下发 guide、对已完成 node 排队 modify、提交 monitor 审核 approve,以及排队 restart/redo。
- append-prompt、guide、modify、redo/restart 统称为 node 的管理行为;approve 是 monitor 审核决策;fetch/status 只属于观察行为。
- UniDesk 只负责同源代理、结构化展示和人工控制入口,不直接写 Pipeline `.state` 文件。
- node 精细控制面板的 append、guide、modify、approve、redo/restart 必须调用 Pipeline HTTP node-control 后端,并把 WebUI 发起者记录为结构化 `sourceKind=webui` 事件
- node 精细控制面板的 append、guide、modify、approve、redo/restart 必须调用 Pipeline 后端 OA control API`node-control` 可作为 HTTP 路由名保留,但内部必须写入 OA 控制事件,并把 UniDesk frontend 发起者记录为结构化事件;历史兼容字段可继续使用 `sourceKind=webui` 表示前端来源
- `Pipeline` epoch 和甘特图规则。
- 一个 epoch 定义为同一个 pipeline 从入口到终态完整执行一遍。
- 同一 `pipelineId` 下的多个 run 必须作为多个 epoch 展示,并允许操作员切换当前 epoch;控制图、node 控制面板、运行材料索引和甘特图都必须跟随当前 epoch。
@@ -73,6 +78,7 @@ frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL
- epoch 甘特图的纵轴是时间,左侧第一列展示时间刻度,后续每列对应一个 node;node 在 `startedAt``finishedAt` 或当前时间之间处于工作态时绘制竖向工作条,空闲时间留空。
- 甘特图 node 列顺序必须遵循 pipeline 拓扑,从上游到下游依次向右展开;不得把下游 node 排在上游左侧。
- 甘特图必须提供时间尺度滑块,用同一份时间数据调整每分钟像素密度:全局尺度压缩纵向高度以查看完整 epoch,细节尺度拉长纵向高度以查看短时间内的 prompt 点、控制点和执行线。
- 甘特图必须提供当前 epoch 的渲染图导出按钮;导出图应使用当前可见布局,执行区间先绘制,控制/观察箭头覆盖在执行区间之上,真实事件点覆盖在最上层,避免箭头被执行条遮住。
- 精确数学图形的坐标权威规则。
- 凡是精确时序图、甘特图或其他包含严格数学映射关系的渲染,坐标权威必须在后端完成。
- Pipeline `GET /api/node-control/runs/{runId}?view=gantt&scale=0..100` 应返回可直接展示的 `layout.chartHeight`、时间刻度 `ticks[].y`、执行区间 `startY/endY/y1/y2/height`、事件点 `y/timeAxisY`、控制箭头 `sourceY/targetY/y1/y2` 和对齐诊断。
@@ -83,17 +89,25 @@ frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL
- 甘特图上的执行线、prompt 点、控制点和 monitor 虚线箭头必须通过同源 `node-control` HTTP 读取接口驱动。
- run 级图形数据使用 `GET /api/node-control/runs/{runId}?view=gantt&scale=0..100`
- 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`
- 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`
- 控制事件必须依赖后端记录的 `sourceKind``sourceNodeId``targetNodeId``commandId``eventId``resetNodeIds``interruptedProcedureRunIds` 等结构化字段识别 monitor、WebUI、CLI 等发起者,不能通过解析 monitor 自己的 step 文本来反推。
- 控制事件必须依赖后端记录的 `sourceKind``sourceNodeId``targetNodeId``commandId``eventId``resetNodeIds``interruptedProcedureRunIds` 等结构化字段识别 monitor、UniDesk frontend、CLI 等发起者,不能通过解析 monitor 自己的 step 文本来反推。
- `Pipeline` monitor 审核与详情展示。
- Pipeline 后端可通过 `control.monitoring.audit.enabled=true` 开启 monitor 审核门禁
- 当前 pipeline 存在 monitor node 时,node 成功后必须等待后端记录的 `approve` 审核通过,或由 monitor 发起 `modify` 增量修改管理行为、`redo/restart` 重做管理行为
- 前端应把审核/控制事件渲染为结构化点和虚线箭头
- Pipeline 页面必须提供结构化 `OA 事件流` 诊断面板,通过 `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` 展示 100% 事件流模式、禁止残留数、no-audit 证据、monitor 审核证据、控制 queued/applied 计数和近期 run 证据;原始 diagnostics 只能通过 `查看原始JSON` 打开
- Pipeline 后端可通过 `control.monitoring.audit.enabled=true` 开启 monitor 审核门禁,但是否审核必须由 OA backend 加载 config snapshot 后判断,不能写入 `node-finished` 事实事件
- 当前 pipeline 存在 monitor node 时,node 成功后必须先进入 OA 事件流;OA 判定需要审核后才向 monitor 推送 prompt,随后必须等待 monitor 通过 OA 控制事件发起 `approve``modify``redo/restart` 或等价管理行为
- 长时间处于 `running` 的 node 必须触发 `node-long-running-observation` 观察事件,默认节奏为 2、5、10、20 分钟,之后每 20 分钟继续发送一次;前端需要把这类观察事件和普通 `node-finished` 清晰区分。
- 前端应把审核/控制事件渲染为结构化点和虚线箭头;有 monitor 的 pipeline 中,monitor node 必须固定在甘特图第 1 个 node 列,其余 node 再按上游到下游顺序从左到右排列。
- `node-long-running-observation` 必须显示从被观察 node 指向 monitor node 的观察连线,不能只在 monitor 列留下孤立事件点;观察来源本身不是一个真实行为,不能在被观察 node 上额外绘制“观察来源”点,点只代表真实收到 prompt、发出管理行为、发出审核结果等事件。
- 甘特图箭头的末端必须在目标点外回缩约一个箭头长度,箭头尖不能插入 prompt 点、控制点或 monitor 观察点内部,避免把“连线”和“真实行为点”混在一起。
- 仍处于 `running` 的 node 必须显示从实际开始时间延伸到当前时间的实时执行条,并用明确的闪动/扫描效果标识“仍在执行”;不得把 running node 渲染成只有起始点或 1s 极短条线。
- 点击甘特图中的执行线、prompt 点或控制点后,右侧边栏必须展示结构化事件字段、匹配的 procedure/attempt、以及对应 OpenCode step 的摘要与展开详情,而不是在主界面直接铺 raw JSON、JSONL、worker log 或 control event 文本。
- OpenCode step 展示必须参考 `~/.agents/skills/agent-sessions/scripts/webui/src/components/MessageList.tsx` 的信息组织方式:按消息流时间线展示角色、模型、tokens、创建/完成时间,正文摘要直接可读,思考和工具调用分区折叠,工具调用要显示工具名、状态、输入字段、输出摘要和元数据。
- 右侧边栏中的 OpenCode Step Timeline 必须把公共 session 信息(agent、model、session id)聚合到 session 头部,不得在每个 step 重复;单个 step 的默认摘要只保留时间、消息、工具调用三类信息,统计信息和 tag 必须折叠到展开层。
- 右侧边栏排版必须优先保护横向可读宽度:时间放在 step 顶部 header,而不是单独占用左侧窄列;默认摘要不得引入右侧边栏内部横向滚动条,也不得因为窄列挤压把 step 高度拉得过高。
- OpenCode Step Timeline 不能使用跨越所有 step 的连续装饰线;相邻 step 之间若存在真实时间空闲区间,例如上一个 step `completedAt` 到下一个 step `createdAt`,该区间必须视觉留白,不能被误渲染为持续执行条线。
- 调整任何高信息密度右侧边栏布局时,都必须把 `总高度``横向滚动条` 作为显式验收指标,用 Playwright 打开真实页面验证,而不是只看静态代码或本地想象。
- 运行材料只能作为结构化索引行展示计数、状态、时间和来源摘要,完整 JSON、JSONL 或 log tail 只能通过显式 `查看原始JSON` 按钮打开。
- `Pipeline` 渲染与算法验证。
+28 -7
View File
@@ -7,7 +7,7 @@ UniDesk microservice 是挂载到主 server 控制面的非核心业务后端。
- microservice 后端端口默认只绑定计算节点本机地址,例如 `127.0.0.1:<port>`,不得直接暴露公网。
- 浏览器只访问 UniDesk frontendfrontend 通过同源 `/api/microservices/*` 代理到 backend-corebackend-core 再通过目标 provider-gateway 的 `microservice.http` 能力访问计算节点本机后端。
- backend-core REST API、database 和计算节点 microservice 后端都不得新增公网端口;公网入口仍只有 frontend 和 provider ingress。
- `microservice.http` 只允许 provider-gateway 访问 `http://127.0.0.1``http://localhost``http://host.docker.internal` 这类节点本地地址;主 server 内置 microservice 可使用同一 Compose 网络内的显式服务名,例如 `todo-note:4211`。backend-core 还必须用 `allowedPathPrefixes``allowedMethods` 同时限制可代理路径和 HTTP 方法。
- `microservice.http` 只允许 provider-gateway 访问 `http://127.0.0.1``http://localhost``http://host.docker.internal` 这类节点本地地址;主 server 内置 microservice 可使用同一 Compose 网络内的显式服务名,例如 `todo-note:4211``codex-queue:4222`。backend-core 还必须用 `allowedPathPrefixes``allowedMethods` 同时限制可代理路径和 HTTP 方法。
## Config Contract
@@ -28,7 +28,7 @@ UniDesk microservice 是挂载到主 server 控制面的非核心业务后端。
## Main Server Microservices
主 server 只承载对统一入口状态迁移有明确必要的 microservice。该类服务仍遵守不暴露公网端口、前端统一 React 控件化展示、状态写入主 PostgreSQL 的规则
主 server 只承载对统一入口状态迁移或控制面自动化有明确必要的 microservice。该类服务仍遵守不暴露公网端口、前端统一 React 控件化展示的规则;业务持久状态优先写入主 PostgreSQL,控制队列这类运行态可使用 `.state/` 文件并必须提供 `/logs` 与结构化状态端点
### Todo Note On Main Server
@@ -48,6 +48,24 @@ Todo Note 首次迁移或源 JSON 修复时,在主 server 通过 Docker 内网
Todo Note 数据迁移后必须验证:`microservice proxy todo-note /api/instances` 至少能看到 `CONSTAR``大论文``找工作``小论文``事务` 五个迁移清单,总任务数不低于源数据的 100 条;再通过代理创建临时清单、添加任务、切换完成、撤销并删除临时清单,证明写入路径走 PostgreSQL 且不会污染长期数据。
### Codex Queue On Main Server
当前 Codex Queue 作为 `id=codex-queue` 的 microservice 登记在 `config.json`
- Provider`main-server`,由本机 provider-gateway 通过 `microservice.http` 访问同一 Compose 网络内的 `http://codex-queue:4222`
- 代码引用:`https://github.com/pikasTech/unidesk` 与配置中的 `repository.commitId`;服务源码位于 `src/components/microservices/codex-queue`,属于 UniDesk 自有控制面组件。
- 部署引用:UniDesk 根仓库 `docker-compose.yml` 中的 `codex-queue` serviceDockerfile 为 `src/components/microservices/codex-queue/Dockerfile`,容器名为 `codex-queue-backend`
- Codex 认证:容器只从主 server 的 `/root/.codex/config.toml` 同步 Codex provider 配置到 `.state/codex-queue/codex-home`,并通过运行时环境透传 `OPENAI_API_KEY``CRS_OAI_KEY` 等 provider 所需变量;新增 provider 的 `env_key` 时必须增加同类运行时透传,禁止把 Codex 或 MiniMax 密钥写入仓库文件。
- Codex 控制:服务内部启动 `codex app-server --listen stdio://`,用 JSON-RPC 调用 `thread/start``turn/start``turn/steer``turn/interrupt`,并监听 `turn/completed`、assistant delta、reasoning delta、command output delta、file diff delta 等通知生成前端可轮询的 transcript。
- 队列语义:`POST /api/tasks``/api/tasks/batch` 入队,服务始终只运行一个 Codex turn;当前任务真正终止后才推进下一个任务。`GET /api/tasks``GET /api/tasks/{id}` 返回队列、attempt、judge 和输出;`POST /api/tasks/{id}/steer` 向运行中 turn 推入 prompt`POST /api/tasks/{id}/interrupt``DELETE /api/tasks/{id}` 打断/取消;`POST /api/tasks/{id}/retry` 手动重试。
- 完成判定:app-server `turn/completed``turn.status=completed|interrupted|failed` 只代表 Codex turn 已结束;即使 `completed` 也必须把原始任务、assistant 最终回复、command/file-change 事件、stderr tail 和 recent events 组成 execution record 交给 judge 判断是否真的完成。配置了 `UNIDESK_CODEX_QUEUE_MINIMAX_API_KEY` 时使用 MiniMax `MiniMax-M2.7` 判定 `complete|retry|fail`,否则使用 fallback 规则。
- Retry/推进语义:`retry` 不是新开一个独立任务或完全新 session;只要已有 `codexThreadId`,服务必须 `thread/resume` 原 thread 并 append 一个继续执行 prompt。只有 judge 判定 `complete` 后,队列 worker 才把当前任务标为成功并推进下一个 queued/retry_wait 任务。
- Judge 探针:`GET|POST /api/judge/probe` 使用同一套 judge 逻辑跑内置 synthetic execution records,覆盖正常完成、正常结束但只给计划、传输中断和用户打断四类样本,返回 `hits``total``hitRate`、每例 `expected``decision`;该接口不得回显 MiniMax API key。
- 模型选择:默认 Codex 模型是 `gpt-5.4-mini`,每个入队任务可覆盖 `model``cwd``reasoningEffort``maxAttempts`
- 状态与日志:队列状态保存在 `.state/codex-queue/state.json` 对应的容器挂载路径,日志写入 UniDesk `logs/{YYYYMMDD}/..._codex-queue.jsonl``/logs` 端点返回最近结构化日志。
- 代理路径:只允许 `/health``/logs``/api/` 前缀;允许方法为 `GET``HEAD``POST``DELETE`。Codex Queue 只在 Compose 内网暴露 `4222/tcp`,不得映射或开放到公网。
- UniDesk 前端:`微服务 / Codex Queue` React 页面负责展示队列卡片、默认模型、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、追加 prompt、打断和手动重试控件;原始任务 JSON 只能通过显式 `查看原始JSON` 打开。
## D601 Microservices
当前 `D601` 同时承载以下 UniDesk microservice
@@ -77,12 +95,12 @@ FindJob 在 UniDesk 语境中按纯后端服务管理:默认页面不得 ifram
- Provider`D601`
- 开发工作树:`/home/ubuntu/pipeline`,开发和调试必须通过 UniDesk SSH 透传进入 D601。
- 代码引用:`https://github.com/pikasTech/pipeline` 与配置中的 `repository.commitId`
- 部署引用:业务仓库自身 `Dockerfile``docker-compose.yml``composeService=pipeline-webui``containerName=pipeline-v2-webui`
- 部署引用:业务仓库自身 `Dockerfile``docker-compose.yml``composeService=pipeline-control``containerName=pipeline-v2-control`
- 节点后端:D601 上 `127.0.0.1:18082`provider-gateway 容器内通过 `http://host.docker.internal:18082` 访问。
- 代理路径:只允许 `/health``/api/` 前缀;允许方法为 `GET``HEAD``POST`,其中 `POST` 仅用于 `/api/node-control/...` 这类 node 控制动作;Pipeline 自身 WebUI 静态页面即使仍由 `pipeline-webui` 提供,也不作为 UniDesk microservice 入口使用
- 代理路径:只允许 `/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 后端;超大 snapshot 必须使用 `__unideskArrayLimit=registry.components:<limit>,runs:<limit>` 做展示级裁剪。node 控制入口必须走 Pipeline 后端 HTTP API,前端不得直接写 `.state`、runner prompt 文件或命令队列。
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 的一个 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` 后显示。
@@ -116,13 +134,14 @@ MET Nonlinear 验收必须通过公网 UniDesk frontend 的交互式 UI 完成
- `bun scripts/cli.ts microservice proxy met-nonlinear /api/queue``bun scripts/cli.ts microservice proxy met-nonlinear /api/images`:读取 MET Nonlinear 队列、GPU 策略和训练镜像状态,适合人工验证,不用于公开业务端口。
- `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 todo-note``bun scripts/cli.ts microservice proxy todo-note /api/instances`:验证主 server Todo Note 后端、PostgreSQL 存储和本机 provider-gateway 私有代理链路。
- `bun scripts/cli.ts microservice health codex-queue``bun scripts/cli.ts microservice proxy codex-queue /api/tasks`:验证主 server Codex Queue 后端、队列状态文件和本机 provider-gateway 私有代理链路;写入、追加 prompt 和打断由 frontend 同源代理或直接 HTTP API 发起。
- `bun scripts/cli.ts --main-server-ip 74.48.78.17 microservice health findjob`:在计算节点或其他非主 server 主机上通过公网 frontend remote CLI 进行同一验证,不需要主 server SSH key。
`debug dispatch D601 microservice.http --payload-json ...` 仅用于开发调试 provider-gateway 代理能力;正式验收和用户入口应优先使用 `microservice` 命令与 frontend 页面。
## Frontend Rules
microservice 前端必须整合到 `src/components/frontend/src/` 下的 TypeScript + React 模块中。`app.tsx` 只做 shell/router 和导入分发,业务页面必须拆成独立 TSX,例如 `todo-note.tsx``findjob.tsx``pipeline.tsx`。默认展示必须是业务控件:指标卡、状态徽标、表格、草稿卡片、运行卡片、树形任务、表单控件、结构化材料索引、链接和字段摘要;只有操作员点击 `查看原始JSON` 时才允许打开原始 JSON 弹窗。日志、JSONL 和大块 JSON 不得在主界面按行展示,避免把裸数据伪装成 UI。
microservice 前端必须整合到 `src/components/frontend/src/` 下的 TypeScript + React 模块中。`app.tsx` 只做 shell/router 和导入分发,业务页面必须拆成独立 TSX,例如 `todo-note.tsx``findjob.tsx``pipeline.tsx``met-nonlinear.tsx``codex-queue.tsx`。默认展示必须是业务控件:指标卡、状态徽标、表格、草稿卡片、运行卡片、树形任务、表单控件、结构化材料索引、链接和字段摘要;只有操作员点击 `查看原始JSON` 时才允许打开原始 JSON 弹窗。日志、JSONL 和大块 JSON 不得在主界面按行展示,避免把裸数据伪装成 UI。
对于超大业务 JSONbackend-core 可把 `__unideskArrayLimit=<path>:<limit>` 作为 frontend-only 代理参数传给 provider-gateway,由 provider-gateway 在返回前裁剪指定 JSON 数组并写入 `_unidesk.arrayLimits` 元数据。该参数只用于控制 UniDesk 展示预览,不能替代业务后端自身分页 API 的长期设计。CLI 的 `microservice proxy` 还会对超过默认阈值的 body 做二次有界预览,防止人工验证时输出爆炸;只有显式 `--raw` 才允许倾倒完整 body。
@@ -131,12 +150,14 @@ microservice 前端必须整合到 `src/components/frontend/src/` 下的 TypeScr
microservice 交付必须同时通过后端、CLI 和公网 frontend 验证:
- 在主 server 运行 `bun scripts/cli.ts microservice list`,确认 `findjob``providerId=D601``public=false``frontendOnly=true`、仓库 URL、commit id、`127.0.0.1:3254` 映射和 `findjob-server` 容器摘要可见。
- 在主 server 运行 `bun scripts/cli.ts microservice list`,确认 `pipeline``providerId=D601``public=false``frontendOnly=true`、仓库 URL、commit id、`127.0.0.1:18082` 映射和 `pipeline-v2-webui` 容器摘要可见。
- 在主 server 运行 `bun scripts/cli.ts microservice list`,确认 `pipeline``providerId=D601``public=false``frontendOnly=true`、仓库 URL、commit id、`127.0.0.1:18082` 映射和 `pipeline-v2-control` 容器摘要可见。
- 在主 server 运行 `bun scripts/cli.ts microservice list`,确认 `met-nonlinear``providerId=D601``public=false``frontendOnly=true`、仓库 URL、commit id、`127.0.0.1:3288` 映射和 `met-nonlinear-ts` 容器摘要可见。
- 在主 server 运行 `bun scripts/cli.ts microservice list`,确认 `codex-queue``providerId=main-server``public=false``frontendOnly=true`、UniDesk 仓库 URL、`codex-queue:4222` 映射和 `codex-queue-backend` 容器摘要可见。
- 运行 `bun scripts/cli.ts microservice health findjob``bun scripts/cli.ts microservice proxy findjob /api/summary`,确认真实链路经过 backend-core、WebSocket、D601 provider-gateway 和 D601 本机 FindJob 后端。
- 运行 `bun scripts/cli.ts microservice health pipeline``bun scripts/cli.ts microservice proxy pipeline '/api/snapshot?__unideskArrayLimit=registry.components:8,runs:3'`,确认真实链路经过 backend-core、WebSocket、D601 provider-gateway 和 D601 本机 Pipeline 后端,且 run/procedure 摘要包含甘特图所需时间字段。
- 运行 `bun scripts/cli.ts microservice health met-nonlinear``bun scripts/cli.ts microservice proxy met-nonlinear /api/queue``bun scripts/cli.ts microservice proxy met-nonlinear '/api/projects?root=projects&limit=20'``bun scripts/cli.ts microservice proxy met-nonlinear /api/images`,确认真实链路经过 backend-core、WebSocket、D601 provider-gateway 和 D601 本机 MET Nonlinear TS 后端。
- 运行 `bun scripts/cli.ts microservice health todo-note``bun scripts/cli.ts microservice proxy todo-note /api/instances`,确认真实链路经过 backend-core、WebSocket、main-server provider-gateway 和主 server `todo-note-backend` 后端;输出中必须包含五个迁移清单和 PostgreSQL 存储健康状态。
- 运行 `bun scripts/cli.ts microservice health codex-queue``bun scripts/cli.ts microservice proxy codex-queue /api/tasks`,确认真实链路经过 backend-core、WebSocket、main-server provider-gateway 和主 server `codex-queue-backend` 后端;再通过 frontend 提交一个 `gpt-5.4-mini` 小任务,确认队列串行推进、输出实时更新、结束后有 judge 判定,且运行中可追加 prompt 或打断。
- 在 D601 上用 `bun scripts/cli.ts ssh D601 ...` 调试业务仓库和容器,确认 `curl http://127.0.0.1:3254/api/health` 可用;不要把调试服务部署到主 server。
- 在 D601 上用 `bun scripts/cli.ts ssh D601 ...` 调试业务仓库和容器,确认 `curl http://127.0.0.1:18082/health``curl http://127.0.0.1:18082/api/snapshot` 可用;不要把 Pipeline 调试服务部署到主 server。
- 在 D601 上用 `bun scripts/cli.ts ssh D601 ...` 调试 `~/met_nonlinear`,确认 `curl http://127.0.0.1:3288/health` 可用;最终验收必须回到公网 UniDesk frontend,通过项目库选择、Fork、加入待启动队列和启动队列完成,不要把 MET Nonlinear 后端、Docker build 或训练任务部署到主 server。
+69
View File
@@ -0,0 +1,69 @@
# Pipeline OA Event Flow
Pipeline 的最终控制模型必须是 100% OA 事件流驱动。开发过程可以分阶段迁移,但交付态不能保留中间态、双写语义、点对点控制通道或依赖旧事件的永久兜底;任何阶段性兼容只能作为局部迁移手段,必须在同一重构闭环内被删除并由 E2E 证明已退出。
## Core Principles
- OA backend 是 Pipeline runtime 的唯一控制总线;node、monitor、runner、UniDesk frontend 和 CLI 都只能通过 OA 事件表达事实、订阅事实或发出控制意图。
- Node 之间不得直接启动、暂停、重试或通知彼此;monitor 不得绕过 OA 直接控制 runnerfrontend 不得直接写 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。
- UI、CLI、E2E 和调试工具都应消费 OA 归一化读接口或通过 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 事件取证。
## 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}`
- Monitor 在 config 中只声明要订阅的业务 tag 或事件类型,OA backend 在订阅时自动追加当前 `pipeline:{pipelineId}``epoch:{runId}` 限制,避免不同 pipeline 或 epoch 串流。
- 一次 epoch 应绑定 config snapshot 或 config version;OA 的审核、拓扑推进、并发和单步策略都以该快照为准,避免运行中配置漂移导致控制解释不一致。
- 控制事件必须有幂等键,重复投递同一个 start、approve、redo/restart 或 append prompt 时,OA 应识别为同一意图而不是重复启动或重复写入。
## Fact Events
- `node-started``node-finished``node-failed``node-cancelled``node-long-running-observation` 等事实事件用于记录运行状态,不承担流程推进决策。
- `node-finished` 是中性完成事实,包含 node id、procedure run id、status、timestamps、outputs summary 和诊断摘要;它不描述是否需要审核,也不直接声明下游节点。
- 长任务观察事件只在 node 未进入终态时触发,默认节奏为 2、5、10、20 分钟,之后每 20 分钟循环;固定 30 秒 monitor 周期不得作为正式机制保留。
- `legacy batch completion gate` 只能作为派生统计或兼容只读事件,不能作为流程推进门禁;下游推进由每个 `node-finished` 加 OA config/topology 判断完成。
- 如果 node 完成后需要 monitor 审核,UI 中仍只应保留这一条 `node-finished` 作为完成/等待审核的事实点,不再创建独立的 `legacy node audit request event``legacy monitor audit request event` 事实点。
## No-Audit Flow
1. Runner 观察到 node 进入终态,并把中性 `node-finished` 事实推入 OA backend。
2. OA backend 记录事件,加载该 epoch 的 config snapshot 和 topology。
3. OA 判定该 node 不需要 monitor 审核,且下游依赖已满足。
4. OA 产生下游 `start-node` 控制事件。
5. Runner 只消费 OA 控制事件来启动下游 node,并把启动结果再写回 OA 事件流。
## Audit Flow
1. Runner 把中性 `node-finished` 事实推入 OA backend。
2. OA backend 记录事件,并按 config snapshot 判定该 node completion 需要 monitor 审核。
3. Monitor 通过 OA 订阅收到同一 scope 下的 `node-finished` 事实和 prompt;是否需要审核来自 OA policy,不来自 `node-finished.detail`
4. Monitor 通过 OA skill 或等价 OA 控制 API 发出 `approve``modify``redo/restart``reject` 等管理事件。
5. OA backend 记录 monitor 决策,按 config/topology 产生 start downstream、reset downstream、restart target 或 stop/pause 等后续控制事件。
6. Runner 只消费 OA 控制事件执行后续动作,执行结果继续回写 OA 事件流。
## Single-Step And Debug Flow
- 单步运行、人工放行、暂停、恢复、重试、跳过和取消都必须建模为 OA 控制事件;不得通过调试 HTTP 端点直接修改 runner 内存状态。
- CLI 和 UniDesk frontend 的 node 精细控制面板只能调用 Pipeline 后端的 OA 控制 API;后端可保留 `node-control` 路由名,但路由内部必须写入 OA 事件并由 OA dispatcher 执行。
- Debug 工具可以读取事件流、config snapshot 和归一化状态机结果,但不得维护另一套流程推进逻辑。
## Rendering Contract
- UniDesk Pipeline UI 只消费 Pipeline 后端根据 OA 事件流生成的 snapshot、Gantt DTO、node detail 和 control history,不直接消费旧 monitor append 文件语义。
- 甘特图中的执行条、prompt 点、monitor 决策点、长任务观察箭头和人工控制箭头都必须来自 OA 事件或 OA 派生 DTO。
- 有 monitor 的 pipeline 中,monitor node 固定在甘特图第 1 列,其余 node 再按拓扑上游到下游向右排列。
- `node-long-running-observation` 必须画从被观察 node 到 monitor node 的观察连线;被观察 node 上不得额外绘制“观察来源”点,因为点只代表真实行为。
- Running node 必须显示从实际开始时间延伸到当前时间的实时闪动执行条;相邻 OpenCode step 之间的真实空闲区间必须留白,不能被连续装饰线误渲染为执行。
- 审核开启时不得同时渲染 `node-finished` 和独立 audit-request 点;node completion 是唯一完成/等待审核点,monitor 的 approve/modify/redo/restart 是后续独立控制点。
## Migration And Completion Gates
- 分阶段重构只允许按“schema 文档与测试门禁 -> OA 事件写入 -> OA dispatcher 接管控制 -> UI/CLI 改为 OA 读写 -> 删除旧事件和旧控制路径”的方向前进,不得把双写/双读作为最终设计。
- 完成态必须删除或禁用以下残留:`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。
- Pipeline 甘特图 E2E 必须证明没有重复审核点、长任务观察有连线且无来源伪点、running node 有实时条、OpenCode step 空闲区间留白、控制箭头来自 OA 控制事件。
- 代码检查应加入防回归搜索或等价单元测试,防止重新引入 `legacy audit policy flag` 权威字段、旧 audit-request 事件、`legacy batch completion gate` 推进逻辑或非 OA 控制写路径。
+1 -1
View File
@@ -80,7 +80,7 @@ provider ingress 是唯一允许公网暴露的 provider 连接接口,当前
## Microservice HTTP Proxy
`microservice.http` 是 provider-gateway 给 UniDesk microservice 使用的私有后端访问能力。backend-core 通过真实 WebSocket dispatch 下发目标 service id、节点本机 `targetBaseUrl`、path、query、method、request body、timeout 和可选 JSON 数组裁剪参数;provider-gateway 支持 `GET``HEAD``POST``PUT``PATCH``DELETE`,但最终允许方法必须由每个 microservice 的 `backend.allowedMethods` 显式配置。provider-gateway 只允许访问 `http://127.0.0.1``http://localhost``http://host.docker.internal` 这些节点本地地址;主 server 内置 Todo Note 后端可使用 Compose 服务名 `http://todo-note:4211`。该能力不打开 provider-gateway 入站端口,也不替代业务仓库自身 Dockerfile/docker-compose。
`microservice.http` 是 provider-gateway 给 UniDesk microservice 使用的私有后端访问能力。backend-core 通过真实 WebSocket dispatch 下发目标 service id、节点本机 `targetBaseUrl`、path、query、method、request body、timeout 和可选 JSON 数组裁剪参数;provider-gateway 支持 `GET``HEAD``POST``PUT``PATCH``DELETE`,但最终允许方法必须由每个 microservice 的 `backend.allowedMethods` 显式配置。provider-gateway 只允许访问 `http://127.0.0.1``http://localhost``http://host.docker.internal` 这些节点本地地址;主 server 内置 Todo Note 与 Codex Queue 后端可分别使用 Compose 服务名 `http://todo-note:4211``http://codex-queue:4222`。该能力不打开 provider-gateway 入站端口,也不替代业务仓库自身 Dockerfile/docker-compose。
超大 JSON 响应可以使用 `jsonArrayLimits` 在 provider-gateway 返回前裁剪指定数组,并在响应体中写入 `_unidesk.arrayLimits` 元数据,便于 UniDesk frontend 预览列表而不展示裸 JSON。长期应优先推动业务后端提供分页 API;裁剪只是 UniDesk 集成层的展示保护。