Merge pull request #1487 from pikasTech/fix/1476-branch-follower-k8s-native
fix: make branch follower k8s native
This commit is contained in:
@@ -31,7 +31,7 @@ bun scripts/cli.ts cicd branch-follower status
|
||||
|
||||
- CI/CD、GitOps、rollout、PipelineRun、Argo、git-mirror 和 AgentRun 部署必须走受控 CLI;不要用裸 `kubectl`、`argo`、`tkn`、`curl` 当正式控制入口。
|
||||
- CI/CD source authority 只能来自 Kubernetes 托管的 git-mirror snapshot:受控命令先同步 GitHub refs 到 k8s git-mirror,再创建/读取不可变 `refs/unidesk/snapshots/.../<commit>` stage ref;build/status/publish 只消费该 snapshot,host worktree、本地 `git fetch/pull`、可变 branch ref 或 Pipeline 内直连 GitHub 都不能作为 authoritative source。
|
||||
- `cicd branch-follower` 的自动跟随全过程不得读取或挂载 host worktree、target dev dir、`.worktree/*` 或 local git checkout;controller pod 只能用 k8s git-mirror 和 EmptyDir 执行,状态以 K8s ConfigMap/Lease 和 adapter status 为准。
|
||||
- `cicd branch-follower` 的自动跟随全过程不得读取或挂载 host worktree、target dev dir、`.worktree/*` 或 local git checkout;controller pod/一次性 reconcile Job 只能用 k8s git-mirror cache、Tekton PipelineRun、Argo Application、runtime workload 和 EmptyDir 执行,状态以 K8s ConfigMap/Lease 承载的 native observation 为准,不得解析下游 CLI 输出。
|
||||
- CI/CD、rollout、publish、image build 和部署链路禁止新引入 Docker 依赖;不得依赖 Docker socket、Docker daemon、host Docker、`docker build`、`docker push` 或等价 Docker-only 路径。
|
||||
- 正式 CI/CD、publish、image build 和 rollout 必须走 Tekton Task/Pipeline/PipelineRun 承担 CI,并通过 GitOps/Argo 承担部署收敛;普通 Kubernetes Job 只允许用于 bounded helper、source sync、diagnostic、cleanup 或 bootstrap,不得作为正式发布、镜像构建或 rollout 入口。
|
||||
- 正式 CI/CD 必须提供一键完成入口:同一受控命令应完成 source sync、构建、发布、GitOps/Argo 收敛、runtime provenance 校验和 `/health` 端点验证;不要要求操作者手动串联多个 publish/apply/status 命令才能完成一次交付。
|
||||
|
||||
@@ -15,18 +15,19 @@ bun scripts/cli.ts cicd branch-follower events --follower <id>
|
||||
bun scripts/cli.ts cicd branch-follower logs --follower <id>
|
||||
```
|
||||
|
||||
`apply --confirm --wait` is the one-command deploy/update entry for the K8s controller. `status` is the default intermediate-state query; add `--live` only when a fresh adapter status read is needed.
|
||||
`apply --confirm --wait` is the one-command deploy/update entry for the K8s controller. `status` is the default intermediate-state query. `status --live` and local `run-once` must submit a bounded K8s reconcile Job; the Job performs all source, Tekton, Argo and runtime reads inside the cluster and writes the state ConfigMap.
|
||||
|
||||
## Source Authority
|
||||
|
||||
- Follower decisions must not read host source worktrees, target dev directories, `.worktree/*`, local git state, or direct GitHub branch refs.
|
||||
- Controller pods use EmptyDir and clone UniDesk controller source from the YAML-declared k8s git-mirror read URL, then run the CLI with the mounted registry.
|
||||
- Controller pods use EmptyDir plus the YAML-declared k8s git-mirror cache PVC, sync GitHub refs from inside Kubernetes, clone UniDesk controller source from `/cache`, then run the CLI with the mounted registry.
|
||||
- Runtime source commits, build contexts, publish inputs and closeout status remain owned by each adapter's k8s git-mirror snapshot and runtime objects.
|
||||
- Dirty, stale, or missing-dependency host worktrees are non-authoritative and must not change observed sha, trigger sha, PipelineRun, GitOps, or status output.
|
||||
- `trans` or SSH may be used only by the operator CLI as a transport to create/read Kubernetes objects on the target cluster. It must not be part of branch-follower source sync, GitHub communication, status collection, decision making or closeout.
|
||||
|
||||
## YAML Ownership
|
||||
|
||||
`config/cicd-branch-followers.yaml` owns only controller settings and the follower registry: id, adapter, source/target configRefs, command argv, closeout check labels and budgets.
|
||||
`config/cicd-branch-followers.yaml` owns controller settings and the follower registry: id, adapter, source/target configRefs, command argv, native status object refs, closeout check labels and budgets.
|
||||
|
||||
It must not copy runtime/GitOps/Secret details from owning configs:
|
||||
|
||||
@@ -48,4 +49,13 @@ Default `status` output must show follower id, phase, adapter, source branch + o
|
||||
|
||||
State machine phases are `Observed`, `Noop`, `PendingTrigger`, `Triggering`, `ClosingOut`, `Succeeded`, `Failed`, `Superseded`, `Blocked`, and `Skipped`.
|
||||
|
||||
`run-once --dry-run` is read-only: it may query K8s state and adapter status, but it must not write the state ConfigMap or trigger adapters.
|
||||
Status and decision inputs are Kubernetes-native:
|
||||
|
||||
- source: k8s git-mirror cache ref and immutable snapshot ref;
|
||||
- CI: Tekton `PipelineRun.status.conditions`;
|
||||
- deployment: Argo `Application.status.sync` and `Application.status.health`;
|
||||
- runtime: selected Deployment/StatefulSet readiness plus source commit labels, annotations or env.
|
||||
|
||||
The branch follower must not parse downstream CLI stdout/stderr, `kubectl` human tables, `argo` text, `tkn` text, or curl output to infer observed sha, target sha, readiness or closeout. `kubectl -o json` may be used inside the controller/Job as a structured Kubernetes API transport only.
|
||||
|
||||
`run-once --dry-run` is read-only for deployment: it may refresh the state ConfigMap with current native observations, but it must not trigger adapters.
|
||||
|
||||
@@ -18,7 +18,7 @@ bun scripts/cli.ts agentrun result <task>
|
||||
bun scripts/cli.ts agentrun send <session> --prompt-stdin
|
||||
```
|
||||
|
||||
AipodSpec/Artificer、task manifest、session 控制、queue 渐进披露、HWLAB Code Agent 整合、trace/output 分页、judge、中断/取消和旧队列归档见 [references/full.md](references/full.md)。
|
||||
AipodSpec/Artificer、task manifest 和 queue 渐进披露见 [references/resources.md](references/resources.md);session 控制、HWLAB Code Agent 整合、trace/output 分页、judge、中断/取消见 [references/sessions.md](references/sessions.md);旧队列归档见 [references/legacy.md](references/legacy.md)。
|
||||
|
||||
## P0 边界
|
||||
|
||||
@@ -29,6 +29,6 @@ AipodSpec/Artificer、task manifest、session 控制、queue 渐进披露、HWLA
|
||||
|
||||
## 何时读取 reference
|
||||
|
||||
- 创建/应用 AgentRun task 或 AipodSpec:读 [references/full.md](references/full.md) 的新任务入口段。
|
||||
- 查历史 `codex tasks/unread/output/trace`:读历史任务、未读积压和 Trace/Output 段。
|
||||
- 需要 judge、取消、中断、Dev Ready 或 PR preflight:读对应段。
|
||||
- 创建/应用 AgentRun task 或 AipodSpec:读 [references/resources.md](references/resources.md)。
|
||||
- 查历史 `codex tasks/unread/output/trace`:读 [references/legacy.md](references/legacy.md)。
|
||||
- 需要 judge、取消、中断、Dev Ready 或 PR preflight:读 [references/sessions.md](references/sessions.md)。
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
---
|
||||
name: unidesk-code-queue
|
||||
description: UniDesk AgentRun-backed Code Queue CLI — Skill(cli-spec)。legacy `codex` 子命令只保留历史只读和残留停止;新任务提交、Aipod/Artificer 派单、session follow-up、events/logs/result、ack/cancel、dispatch、run/command/runner 状态 drill-down 和 HWLAB Code Agent/CaseRun follow-up 必须使用 `agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|send` 资源原语。用户提到 codex、Code Queue、submit、send、resume、tasks、unread、code-queue、aipod、Artificer、HWLAB Code Agent 时使用。
|
||||
---
|
||||
|
||||
# UniDesk Code Queue / AgentRun CLI
|
||||
|
||||
旧 Code Queue 已冻结新任务和写入口。`bun scripts/cli.ts codex ...` 现在只作为历史归档、只读排障和残留任务停止入口;新的指挥官派单、Aipod/Artificer 执行、events/logs/result、ack/cancel、dispatch、session follow-up 必须走 AgentRun 资源原语,并按 cli-spec 渐进披露。UniDesk 是 render-only client:默认输出是低噪声 human 表格/摘要,脚本读取显式使用 `-o json|yaml` 的稳定客户端 schema,`--raw` 只用于查看直连 AgentRun REST envelope。
|
||||
|
||||
**固定入口前缀**: `cd /root/unidesk && bun scripts/cli.ts agentrun ...`
|
||||
|
||||
---
|
||||
|
||||
## 新任务入口
|
||||
|
||||
```bash
|
||||
# 查看 AgentRun 指挥官队列
|
||||
bun scripts/cli.ts agentrun get tasks --queue commander --limit 20
|
||||
bun scripts/cli.ts agentrun get tasks --queue commander -o wide
|
||||
|
||||
# 查看一个 task/run/command/runnerjob/session 生命周期
|
||||
bun scripts/cli.ts agentrun describe task/<taskId>
|
||||
bun scripts/cli.ts agentrun events run/<runId> --after-seq <lastSeq> --limit 100
|
||||
bun scripts/cli.ts agentrun logs session/<sessionId> --tail 100
|
||||
bun scripts/cli.ts agentrun result run/<runId> --command <commandId>
|
||||
bun scripts/cli.ts agentrun describe command/<commandId> --run <runId>
|
||||
bun scripts/cli.ts agentrun describe runnerjob/<runnerJobId> --run <runId>
|
||||
|
||||
# 查看 / 渲染 AipodSpec(Artificer 是默认分布式开发 agent)
|
||||
bun scripts/cli.ts agentrun get aipodspecs
|
||||
bun scripts/cli.ts agentrun describe aipodspec/Artificer
|
||||
bun scripts/cli.ts agentrun aipod-specs render Artificer --prompt-stdin
|
||||
|
||||
# AipodSpec apply/delete 仍由客户端资源原语发起,服务端只返回 REST 业务事实
|
||||
bun scripts/cli.ts agentrun aipod-specs apply --yaml-stdin --dry-run
|
||||
|
||||
# 提交 AgentRun task manifest
|
||||
bun scripts/cli.ts agentrun apply -f - --dry-run <<'YAML'
|
||||
kind: Task
|
||||
spec:
|
||||
tenantId: unidesk
|
||||
projectId: example
|
||||
queue: commander
|
||||
title: 任务标题
|
||||
payload:
|
||||
prompt: 任务说明
|
||||
YAML
|
||||
|
||||
# 用 AipodSpec 提交,优先用于新任务和 Artificer
|
||||
bun scripts/cli.ts agentrun create task --aipod Artificer \
|
||||
--prompt-stdin --idempotency-key <key>
|
||||
|
||||
# 查看/控制 AgentRun session
|
||||
bun scripts/cli.ts agentrun logs session/<sessionId> --tail 100
|
||||
bun scripts/cli.ts agentrun ack session/<sessionId>
|
||||
bun scripts/cli.ts agentrun dispatch task/<taskId>
|
||||
bun scripts/cli.ts agentrun send session/<sessionId> --aipod Artificer --prompt-stdin
|
||||
bun scripts/cli.ts agentrun cancel session/<sessionId> --reason <text> --dry-run
|
||||
bun scripts/cli.ts agentrun cancel command/<commandId> --run <runId> --reason <text> --dry-run
|
||||
```
|
||||
|
||||
日常 task manifest 优先使用 YAML heredoc:`agentrun apply -f -`;单 prompt 派单优先 `agentrun create task --aipod Artificer --prompt-stdin`;同 session 续跑只使用 `agentrun send session/<sessionId>`。UniDesk 客户端按 `config/agentrun.yaml` 直连 AgentRun REST API,不经过 HWLAB runtime、SSH official CLI 或旧 bridge wrapper;`send` 是唯一用户级 session follow-up 写入口,服务端按 durable session/run/command 状态自动决定内部 `steer` 或新 `turn`,旧 CLI `turn/steer` 路径不保留兼容。`--json-file`、`--prompt-file` 和 `--runner-json-file` 只是客户端输入来源,用于已审阅且可复用的受控文件。它不是旧 Code Queue adapter,不双写,也不迁移旧历史。
|
||||
|
||||
`AipodSpec` 是 AgentRun v0.1 的声明式 agent 装配:模型 profile、gitbundle、skills/tools、SecretRef 和 tool credential 都从 YAML 规格渲染。`Artificer` 默认用于 UniDesk 分布式开发任务,使用 `sub2api` provider、`gpt-5.5`、`reasoningEffort=xhigh`,并通过 SecretRef 注入 GitHub PR token、GitHub SSH 和 UniDesk SSH 透传能力。更新规格时使用 `agentrun aipod-specs apply --yaml-stdin --dry-run` 先看计划,确认后再去掉 `--dry-run`;不得把 API key、SSH key 或 token 写入 prompt、payload、YAML 或 issue。
|
||||
|
||||
AgentRun task payload 需要 runner 内使用 UniDesk SSH 透传时,只通过 `executionPolicy.secretScope.toolCredentials[].tool=unidesk-ssh` 请求 `agentrun-v01-tool-unidesk-ssh/UNIDESK_SSH_CLIENT_TOKEN` SecretRef;不要把 token 写入 prompt、payload 或 `transientEnv`。非敏感 endpoint 使用 `UNIDESK_MAIN_SERVER_IP`、`UNIDESK_MAIN_SERVER_HOST` 或 `UNIDESK_FRONTEND_URL`,可由 runner-job `transientEnv` 显式提供;G14 `agentrun-v01` manager 也会在缺省时按受控默认值自动补齐,并在 trace 中只显示 env name/count/hash 与 `valuesPrinted=false`。
|
||||
|
||||
## Queue 渐进披露
|
||||
|
||||
AgentRun queue 生命周期不是一个单独的 `queue lifecycle` 命令,而是一组资源原语的渐进披露组合:
|
||||
|
||||
1. 默认总览用 `get tasks --queue commander --limit 20`,只看 task state、queue/lane、run/cmd/rjob/session ref、age 和 attention。
|
||||
2. 单任务用 `describe task/<taskId>`,读取 `latestAttempt.runId`、`commandId`、`runnerJobId`、`sessionId/sessionPath` 和少量 `Next:`。
|
||||
3. Run 级状态用 `events run/<runId>` 和 `result run/<runId> --command <commandId>`,判断 terminalClassification、failureKind、provider interruption、timeoutBudget 和 recoveryActions。
|
||||
4. Command 级状态用 `describe command/<commandId> --run <runId>` 和 `result command/<commandId> --run <runId>`,确认 command state、ack、terminal status 和结果摘要;确认为单个 active command 卡住时,先用 `cancel command/<commandId> --run <runId> --reason <text> --dry-run` 核对 `CancelLifecycle` 的 authority、cascade、runner abort 和 fencing,再去掉 `--dry-run` 清理该 command,保留同一个 session 后再用 `send session/<sessionId>` 续跑。
|
||||
5. Runner job 只读状态用 `describe runnerjob/<runnerJobId> --run <runId>`,确认 env image reuse、jobName、namespace、phase、exitCode、retention 和 `valuesPrinted=false`。不要为了这些字段手动调用 `trans G14:k3s kubectl ...`。
|
||||
6. Runtime runner Job/Pod retention 或 operator 明确要求强杀 runner 时,不属于单个 task/session 资源原语;使用 `bun scripts/cli.ts agentrun control-plane cleanup-runners --node <node> --lane <lane> [--force-active] --dry-run|--confirm`。普通 cleanup 只删 inactive selected runner;`--force-active` 会中断 active run/command/session,必须先 dry-run 确认 selection,并且仍应优先于裸 `kubectl delete pod/job`。
|
||||
7. Session trace/output 只在 `describe task` 或 result 里有实际 `sessionId` 时使用 `logs|ack|send|cancel session/<sessionId>`;`sessionRef=null` 时不要猜 session 命令。用户级 follow-up 一律使用 `send session/<sessionId>`,不要回到旧 `turn/steer` 或 `sessions ...` 兼容路径。
|
||||
8. 已创建但尚未运行的 task 使用 `dispatch task/<taskId>` 派发,不再退回旧 bridge `queue dispatch`。
|
||||
|
||||
默认视图必须低噪声且不是 JSON envelope,`-o json|yaml` 才输出稳定机器结构,`--raw` 才保留直连 AgentRun REST envelope;命令返回里的下一步应优先是 `bun scripts/cli.ts agentrun ...` 资源原语,不得把人工 k8s 查询作为日常下一步。
|
||||
|
||||
AgentRun cancel 策略由 `config/agentrun.yaml` 的 lane 级 `deployment.runner.cancelLifecycle` 管理;操作 D601、G14 或其他非默认 lane 时必须带 `--node/--lane --dry-run` 先确认 YAML policy,不要依赖全局默认或手动 k8s 强杀来替代资源原语。
|
||||
|
||||
## HWLAB Code Agent 入口整合
|
||||
|
||||
HWLAB Code Agent / CaseRun follow-up 的日常派单也归入 AgentRun 资源原语:新任务用 `create task --aipod Artificer` 或包含 HWLAB gitbundle 的 `apply -f -`;运行中纠偏用 `send session/<sessionId> --aipod Artificer`。需要验证 HWLAB Web/Cloud API 原入口时,仍按 `$hwlab-code-agent` 使用 G14 `/root/hwlab-v02` 的 `hwlab-cli client agent ...` 拉取同一 trace/result/inspect;不要回到旧 `codex submit/resume/steer`。
|
||||
|
||||
排查 HWLAB v0.3 / D601 Code Agent trace 时,HWLAB `client agent result|trace|inspect` 仍是跨层事实锚点:它会暴露 AgentRun namespace、runId、commandId、jobName 和 providerProfile。若 `bun scripts/cli.ts agentrun events|result run/<runId>` 在默认 direct-http 面返回 `run not found`,不要据此判定 run 已丢失;先核对该 run 是否属于 D601 `agentrun-v02` 或其他非默认 lane,并继续用 HWLAB trace/result 或对应 `agentrun control-plane ... --node <node> --lane <lane>` 查询运行面。Codex provider config 的 context/auto-compact 修复属于 AgentRun lane Secret `sourceRef` 问题,应回到 UniDesk `config/agentrun.yaml` 的 `secrets[].sourceRef`、`.state/secrets/agentrun/*config.toml` 和 `agentrun control-plane secret-sync|restart` 收敛,不要在 prompt、HWLAB adapter 或 Kubernetes Secret 里手补。
|
||||
|
||||
---
|
||||
|
||||
## 本地 Codex Trace 采集
|
||||
|
||||
`bun scripts/cli.ts codex trace ...` 是本机 Codex 运行痕迹的只读诊断入口,默认扫描 `~/.codex`,也可以用 `--root <dir>` 指向其他 Codex home 或已拷贝的 trace 目录。它不提交新任务、不续跑 session,也不恢复旧 Code Queue 写入口。
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex trace list [--root ~/.codex] [--limit 30]
|
||||
bun scripts/cli.ts codex trace active [--root ~/.codex]
|
||||
bun scripts/cli.ts codex trace grep --session <id> --pattern 'playwright|auth-login-failed' [--since ISO]
|
||||
bun scripts/cli.ts codex trace grep --session <id> --messages --pattern 'Playwright|web-probe'
|
||||
bun scripts/cli.ts codex trace grep --session <id> --failed-only [--tool exec_command]
|
||||
bun scripts/cli.ts codex trace grep --pattern 'playwright|auth-login-failed' [--file sessions/...jsonl] [--since ISO]
|
||||
bun scripts/cli.ts codex trace collect [--root ~/.codex] [--output .state/codex-trace/<timestamp>] [--limit 30]
|
||||
bun scripts/cli.ts codex trace show --session <id> [--root ~/.codex] [--tail-bytes 12000]
|
||||
```
|
||||
|
||||
默认只收集 `sessions/*.jsonl`、`history.jsonl`、`shell_snapshots`、`*.log` 和 trace 命名文本;`auth.json`、`config.toml`、sqlite、cache、`.tmp`、generated images、plugins 和 skills 默认跳过。`active` 从 `/proc` 找正在被 Codex 进程打开的 session JSONL,不依赖外部 `lsof`,并输出可复制的 `SESSION-ID`。`grep/show` 优先用 `--session <id>` 定位,避免复制长 JSONL 路径;session id 支持完整 UUID、短前缀和文件名片段,歧义时会提示候选。`grep` 默认只扫 active/recent session,并用 `rg`/raw-line prefilter 先定位候选行再解析 JSONL;需要全量文件域时显式加 `--all-files`,需要跳过 raw 预筛、逐行深度解析 decoded 字段时显式加 `--deep`。排查对话脉络优先 `--messages --pattern ...`,排查工具链优先 `--tools --tool <name>`,只看失败调用用 `--failed-only [--tool <name>]` 且可以不带 pattern。默认输出优先 message 和 tool-call input;tool output 默认折叠,只给失败状态、错误摘要和对应输入,不打印整段 transcript;需要查看命中输出片段时显式加 `--include-output` 或 `-o wide`。它优先用于排查 `playwright`、`auth-login-failed`、`UNIDESK_*HINT` 等高噪声 trace。默认输出是类似 k8s 的简洁 text/table;脚本消费时显式加 `-o json|yaml|name|wide`。需要本地审阅敏感/数据库文件时必须显式加 `--include-sensitive` 或 `--include-sqlite`;`collect` 只复制有界文件并写 manifest,不压缩、不上传、不删除源文件。
|
||||
|
||||
---
|
||||
|
||||
## 冻结的旧写入口
|
||||
|
||||
以下命令必须返回 `ok=false`、`frozen=true`、`degradedReason=legacy-code-queue-frozen`,并提示 AgentRun 替代命令:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex submit ...
|
||||
bun scripts/cli.ts codex enqueue ...
|
||||
bun scripts/cli.ts codex steer <taskId> ...
|
||||
bun scripts/cli.ts codex resume <taskId> ...
|
||||
bun scripts/cli.ts codex queue create <queueId>
|
||||
bun scripts/cli.ts codex queue merge <sourceQueueId> --into <targetQueueId>
|
||||
bun scripts/cli.ts codex move <taskId> --queue <queueId>
|
||||
```
|
||||
|
||||
不要用兼容开关、legacy mode、adapter、双写或 fallback 绕开冻结边界。
|
||||
|
||||
---
|
||||
|
||||
## 历史任务视图
|
||||
|
||||
### Commander(低噪声轮询)
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex tasks --view commander [--limit N]
|
||||
```
|
||||
|
||||
返回旧 Code Queue 历史/残留任务的有界 action map:active runners 计数、少量 active item、queued/retry_wait 计数、terminal-unread 总数、关键风险计数、分类计数和 drill-down 命令。新任务队列状态用 `agentrun get tasks --queue commander`。
|
||||
|
||||
### Supervisor(分区视图)
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex tasks --view supervisor \
|
||||
[--status succeeded|running|queued|failed|canceled|judging|retry_wait] \
|
||||
[--unread] [--limit N] [--before-id id]
|
||||
```
|
||||
|
||||
返回 `activeRunning`、`running`、`completedUnread`、`recentCompleted`、`queued` 分区。状态 alias:`completed|successful → succeeded`、`cancelled → canceled`、`pending → queued`。
|
||||
|
||||
### 单任务
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex task <taskId> [--detail|--full] [--trace]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 未读积压
|
||||
|
||||
```bash
|
||||
# 摘要
|
||||
bun scripts/cli.ts codex unread [--queue id] [--repo owner/name] [--issue N]
|
||||
|
||||
# 详细列表
|
||||
bun scripts/cli.ts codex unread list [--view full] [--limit N]
|
||||
|
||||
# 标记已读
|
||||
bun scripts/cli.ts codex unread mark-read --confirm [--queue id]
|
||||
```
|
||||
|
||||
`mark-read` 必须 `--confirm`,单任务审阅优先用 `codex read <taskId>`。
|
||||
|
||||
---
|
||||
|
||||
## Trace / Output 分页
|
||||
|
||||
```bash
|
||||
# 逻辑 trace(分页)
|
||||
bun scripts/cli.ts codex task <taskId> --trace \
|
||||
[--tail|--from-start|--after-seq N|--before-seq N] [--limit N] [--full]
|
||||
|
||||
# 原始 output(分页补取)
|
||||
bun scripts/cli.ts codex output <taskId> \
|
||||
[--tail|--from-start|--after-seq N|--before-seq N] [--limit N] [--full-text]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 标记已读
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex read <taskId>
|
||||
```
|
||||
|
||||
标记单个终态任务已读,同时返回任务身份、终态 attempt 摘要和最终 response。
|
||||
|
||||
---
|
||||
|
||||
## Judge(复现评判)
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex judge <taskId> --attempt N [--dry-run] [--include-prompt]
|
||||
```
|
||||
|
||||
按指定 attempt 单步复现 judge,诊断入口。
|
||||
|
||||
---
|
||||
|
||||
## 中断/取消
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex interrupt <taskId>
|
||||
bun scripts/cli.ts codex cancel <taskId>
|
||||
```
|
||||
|
||||
仅用于停止旧 Code Queue 残留任务;新 AgentRun session 使用 `bun scripts/cli.ts agentrun cancel session/<sessionId>`,单个卡住 command 使用 `bun scripts/cli.ts agentrun cancel command/<commandId> --run <runId>`。
|
||||
|
||||
---
|
||||
|
||||
## 旧队列归档
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex queues [--full|--all] [--limit N] [--page N]
|
||||
```
|
||||
|
||||
只读查看旧队列摘要。旧 queue create/merge 和 move 已冻结。
|
||||
|
||||
---
|
||||
|
||||
## Dev Ready / PR Preflight
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex dev-ready
|
||||
bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run ...] [--pr-create-dry-run ...] [--issue N] [--full|--raw]
|
||||
```
|
||||
|
||||
---
|
||||
@@ -0,0 +1,8 @@
|
||||
# Legacy Codex Queue
|
||||
|
||||
Legacy `codex` commands are read-only or cleanup-only compatibility paths:
|
||||
|
||||
- historical task list/unread/output/trace reads;
|
||||
- residual task stop when the new AgentRun resource does not own the item.
|
||||
|
||||
Do not submit, steer or resume new work through legacy `codex`.
|
||||
@@ -0,0 +1,10 @@
|
||||
# AgentRun Resources
|
||||
|
||||
New Code Queue work uses AgentRun resource primitives:
|
||||
|
||||
- `agentrun create|apply` for task/AipodSpec manifests.
|
||||
- `agentrun get|describe` for bounded list/detail reads.
|
||||
- `agentrun events|logs|result` for progressive disclosure.
|
||||
- `agentrun dispatch|ack|cancel` for lifecycle control.
|
||||
|
||||
Do not add new mutation paths under legacy `codex`.
|
||||
@@ -0,0 +1,5 @@
|
||||
# AgentRun Sessions
|
||||
|
||||
Session follow-up uses `agentrun send`; the service decides whether the request maps to an internal steer or a new turn.
|
||||
|
||||
For HWLAB Code Agent/CaseRun follow-up, prefer AgentRun task/session/run/command/runner drill-down before direct runtime poking. Judge, cancel, interrupt, Dev Ready and PR preflight evidence should stay attached to the AgentRun resource graph.
|
||||
@@ -13,7 +13,7 @@ GitHub issue/PR 正式读写必须走 `bun scripts/cli.ts gh ...` 或 `trans gh:
|
||||
- 需要理解、拆解、执行或关闭 GitHub issue 时,必须阅读 issue 正文全文;即使 `gh issue view` 触发 stdout dump wrapper,也要跟随 `dump.path` 或改用 `trans gh:/... cat` 读完整正文,不能只凭 compact summary、body preview、body SHA 或截断摘要下结论。
|
||||
- 新 issue 正文必须包含 `目标合并分支: <repo branch/lane>`;不需要合并时写 `目标合并分支: 不适用`。
|
||||
- 大计划、后续阶段和独立改进方向创建新 issue;已有 issue 评论只写短进展、证据、阻塞和链接。
|
||||
- 规划型、多阶段、架构/API/平台运维类 issue 第一阶段必须 `P0 SPEC 先行`;细则见 [references/full.md](references/full.md) 的 `多阶段 Issue 与 SPEC-First`。
|
||||
- 规划型、多阶段、架构/API/平台运维类 issue 第一阶段必须 `P0 SPEC 先行`;细则见 [references/issues.md](references/issues.md)。
|
||||
- `P0 SPEC 先行` 段不得写入硬编码阈值、采样周期、重试次数、并发数等可调参数;必须写明这些参数由指定 YAML/source-of-truth 控制,issue 只列配置路径、字段族和验收读取方式。
|
||||
- `gh` 默认输出是 k8s 风格 text/table/summary/Next/Disclosure;脚本消费或全量排障必须显式用 `--json`、`--full` 或 `--raw`。
|
||||
- 多行正文使用 quoted heredoc:`--body-stdin <<'EOF'`;不要把长 Markdown 塞进 shell 参数。
|
||||
@@ -35,12 +35,10 @@ bun scripts/cli.ts gh pr preflight <number> --repo pikasTech/unidesk
|
||||
bun scripts/cli.ts gh pr merge <number> --repo pikasTech/unidesk --merge --delete-branch
|
||||
```
|
||||
|
||||
完整 issue/PR CRUD、comment、patch、stale-close、scan-escape、PR files/review-plan/diff/preflight/merge、看板命令和 `trans gh:` 虚拟文件系统见 [references/full.md](references/full.md)。
|
||||
Issue CRUD/comment/patch/stale-close/scan-escape 和看板命令见 [references/issues.md](references/issues.md);PR files/review-plan/diff/preflight/merge 见 [references/pull-requests.md](references/pull-requests.md);`trans gh:` 虚拟文件系统见 [references/trans-gh.md](references/trans-gh.md)。
|
||||
|
||||
## 何时读取 reference
|
||||
|
||||
- 需要具体 issue/PR/comment 命令参数、`--json` 字段或 body guard:读 [references/full.md](references/full.md) 的 Issue/PR 命令段。
|
||||
- 需要 PR bounded patch/index/drill-down:读 [references/full.md](references/full.md) 的 PR 文件变更与 review-plan 段。
|
||||
- 需要局部修补正文或评论:读 `trans gh:` 和 apply-patch 段。
|
||||
- 需要维护总看板 [#20](https://github.com/pikasTech/unidesk/issues/20):读看板命令段。
|
||||
- 需要 closeout、preflight、merge 或 ancestry/squash 判断:读 PR 命令和关键约定段。
|
||||
- 需要具体 issue/comment 命令参数、`--json` 字段、body guard 或看板命令:读 [references/issues.md](references/issues.md)。
|
||||
- 需要 PR bounded patch/index/drill-down、closeout、preflight、merge 或 ancestry/squash 判断:读 [references/pull-requests.md](references/pull-requests.md)。
|
||||
- 需要局部修补正文或评论:读 [references/trans-gh.md](references/trans-gh.md)。
|
||||
|
||||
@@ -1,453 +0,0 @@
|
||||
---
|
||||
name: unidesk-gh
|
||||
description: UniDesk GitHub CLI - 通过 `bun scripts/cli.ts gh ...` 管理 GitHub issue/PR,不依赖原生 `gh` binary。用户提到 gh、GitHub issue、GitHub PR、创建 issue、评论 issue、合并 PR、preflight、看板操作时使用。
|
||||
---
|
||||
|
||||
# UniDesk GitHub CLI
|
||||
|
||||
UniDesk 受控 GitHub 操作入口,底层 issue/PR REST 读写走 `bun scripts/cli.ts gh <subcommand>`,不依赖原生 `gh` binary,不手写 `curl`/GraphQL。Issue/PR 正文局部修补的人工首选入口是 `trans gh:/owner/repo/issue/<number> apply-patch` 或 `trans gh:/owner/repo/pr/<number> apply-patch`,底层 `gh issue patch` 只作为受控底座或兼容入口。
|
||||
|
||||
**固定入口前缀**: `cd /root/unidesk && bun scripts/cli.ts gh ...`
|
||||
|
||||
## 实现边界
|
||||
|
||||
GitHub 受控入口的长期架构权威是 `project-management/PJ2026-01/specs/PJ2026-010606-github-controlled-entry.md`。`scripts/src/gh.ts` 只保留兼容 re-export;新增或修改 GitHub 子命令必须落到 `scripts/src/gh/` 下对应职责模块(client/options/refs/body/issue/pr/board/attachments/escape-scan/help/index 等),不得把业务实现重新堆回根入口。
|
||||
|
||||
拆分后的 gh 模块文件头应保留 `SPEC: PJ2026-010606 GitHub入口 <实现引用版本>` 追溯,单个模块超过 3000 行时继续拆分。PR closeout 除常规 smoke 外,应覆盖至少一条 `trans gh:/... apply-patch --dry-run` 或等价写回路径,证明虚拟正文仍走同一 guard。
|
||||
|
||||
CLI 自检使用 `bun scripts/cli.ts check --syntax-only`、针对被改模块的 `bun --check <file>` 和必要 `gh ... --dry-run`/read smoke。不要把 `bun --check scripts/cli.ts` 当作低噪声自检;它可能执行根 help 并触发 dump。需要 scripts 类型检查时走 `bun scripts/cli.ts check --scripts-typecheck ...`,让 CLI 输出 heartbeat 和 bounded timeout 结果。
|
||||
|
||||
## 写入语言
|
||||
|
||||
- Issue/PR 的正式写入一律使用中文,包括标题、正文、评论、关闭/重开说明、PR 描述、PR 评论和 merge closeout 说明。
|
||||
- 命令、路径、trace/session/job id、API path、代码标识符、英文专有名词、原始日志摘录和外部错误原文可以保留原样;解释性文字、结论、风险、计划和验收说明必须中文。
|
||||
- 除非用户明确要求外文,不要用英文撰写 issue/PR closeout、进展评论或阻塞说明。
|
||||
- Markdown 正文、issue/PR 正文和评论里的 issue/PR 引用必须写成 `[#<number>](https://github.com/<owner>/<repo>/issues/<number>)` 或 `[#<number>](https://github.com/<owner>/<repo>/pull/<number>)`,显示短号、链接目标保留完整 URL;不要显示裸长链接、裸井号编号或 `owner/repo` 加井号编号。`owner/repo#number` 只允许作为 CLI 命令参数 shorthand。
|
||||
|
||||
---
|
||||
|
||||
## Issue 目标分支 / Lane
|
||||
|
||||
- 创建 issue、补充正式 issue 评论、关闭/重开说明或把 trace 发现的问题登记成 issue 时,正文必须显式写明目标合并分支或运行 lane。
|
||||
- 推荐固定字段:`目标合并分支: <repo> <branch/lane>`;例如 `目标合并分支: HWLAB v0.2`、`目标合并分支: AgentRun v0.1`、`目标合并分支: UniDesk master`。
|
||||
- 如果该 issue 只记录外部依赖、运维观察或不需要代码合并,仍必须写 `目标合并分支: 不适用`,并用一句话说明原因。
|
||||
- 看到用户或上下文已明确目标 lane 时必须照写;不明确时按目标仓库长期 source truth 选择:HWLAB 按当前 runtime lane(如 `v0.2`/`v0.3`)、AgentRun 默认 `v0.1`、UniDesk 默认 `master`。不能确定时写 `目标合并分支: 待确认` 并说明待确认点,不能省略字段。
|
||||
|
||||
---
|
||||
|
||||
## 计划与评论分流
|
||||
|
||||
- 大计划、改进计划、后续工作拆解、独立验收标准或会形成多阶段执行的内容,必须创建新的 issue 承载;不要在已有 issue 评论区开长计划。
|
||||
- 既有 issue 评论只写短进展、关键证据、当前结论、阻塞和新 issue 链接;评论用于串联时间线,不承载新的大范围计划正文。
|
||||
- 如果调查中发现了独立改进方向,应先用 `gh issue create --body-stdin` 创建新 issue,标题和正文写清目标合并分支/lane、背景、计划、验收标准;然后在原 issue 评论中用 1-3 句说明已拆出,并链接到新 issue。
|
||||
- 只有用户明确要求把计划写回当前 issue 正文,或当前 issue 本身就是唯一的专题计划 issue,才允许更新当前 issue 正文;即便如此,评论仍保持短小,不复制整篇计划。
|
||||
|
||||
## 多阶段 Issue 与 SPEC-First
|
||||
|
||||
- 形成多阶段实施、跨模块架构、新能力、长期 API/数据模型、平台运维能力或用户可见工作流的规划型 issue 时,第一阶段必须是 `P0 SPEC 先行`,并按 `$unidesk-oa` 的 SPEC 管理模式处理。
|
||||
- `P0 SPEC 先行` 必须在 issue 正文列出 SPEC 编号、SPEC 文档路径、上级规格、关联规格、实现引用版本、目标架构图/数据流图/关键时序图完成项,以及源码文件头部 `SPEC: <编号> <短名> <实现引用版本>` 标注规则。
|
||||
- `P0 SPEC 先行` 只能定义语义、状态机、接口字段、数据流、配置字段族和验收读取方式;不得把阈值、采样周期、重试次数、并发数、超时窗口等可调参数写成硬编码常量。此类参数必须引用指定 YAML/source-of-truth 路径控制,例如 `config/hwlab-node-lanes.yaml` 中目标 node/lane 的配置段。
|
||||
- issue 正文只能承载执行计划、阶段状态和证据索引,不能替代 `project-management/PJ2026-01/specs/` 中的长期 SPEC 正文。若稳定需求、数据流、接口或验收口径变化,先更新 SPEC,再更新 issue 阶段计划。
|
||||
- P0 未完成前,不得把代码实现、部署、CI/CD、测试补充或验收收口列为已可执行阶段;这些只能作为后续 P1+ 阶段。
|
||||
|
||||
---
|
||||
|
||||
## 认证探测
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh auth status [--repo owner/name]
|
||||
```
|
||||
|
||||
探测 token 来源(`GH_TOKEN`/`GITHUB_TOKEN`/`gh auth token`)、GitHub REST egress、repo 可见性、issue 可读性。不打印 token。
|
||||
|
||||
## 聚焦帮助
|
||||
|
||||
具体子命令用法优先直接查 scoped help,不要先打开顶层长 help 再人工搜索:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue close --help
|
||||
bun scripts/cli.ts gh issue comment --help
|
||||
bun scripts/cli.ts gh pr merge --help
|
||||
```
|
||||
|
||||
`gh <subcommand> --help`、`gh <subcommand> -h` 和 `gh <subcommand> help` 只输出匹配命令或命令组的 bounded JSON,包括相关 usage、短 notes 和完整 help 入口;`gh --help` / `gh help` 才输出完整顶层命令索引。
|
||||
|
||||
---
|
||||
|
||||
## Issue 命令
|
||||
|
||||
### 列表
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue list [owner/repo] \
|
||||
[--state open|closed|all] [--limit N] [--search text] [--title-prefix text] \
|
||||
[--label label[,label...]] [--repo owner/name] \
|
||||
[--json number,title,state,url,updatedAt,createdAt,author,labels] [--raw|--full]
|
||||
```
|
||||
|
||||
默认 `state=open`、`limit=30`。`owner/repo` 位置参数等价 `--repo`。`--search` 走 GitHub Search API 做查重。`--title-prefix` 在当前有界结果内按 issue 标题前缀做本地过滤,输出 `titleFilter` 的输入/输出/过滤数量,适合 `[FEEDBACK]` 去重:
|
||||
默认 stdout 是有界表格和短摘要;`--json`、`--full` 或 `--raw` 是显式结构化/全量披露入口。
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue list --repo pikasTech/unidesk --state all \
|
||||
--search "[FEEDBACK]" --title-prefix "[FEEDBACK]" \
|
||||
--json number,title,state,url
|
||||
```
|
||||
|
||||
### 查看
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue view <number|url|owner/repo#number> \
|
||||
[--repo owner/name] [--json body,title,state,closed,closedAt,comments,commentCount] [--raw|--full]
|
||||
```
|
||||
|
||||
`read` 是兼容别名。支持 `owner/repo#number` shorthand(如 `pikasTech/HWLAB#1024`)。
|
||||
默认 stdout 是 issue 元数据表格、短摘要、body chars/SHA/preview 和 Next 命令,不输出完整 body。人工读取 issue/PR 正文优先走 `trans gh:/owner/repo/issue/<number> cat|rg` 或 `trans gh:/owner/repo/pr/<number> cat|rg`;脚本消费或字段读取使用 `--json <fields>`,全量排障才用 `--full`/`--raw`。
|
||||
|
||||
Issue 理解、拆解、执行、调度子代理、PR review 归因或 closeout 前必须读完整正文;compact summary、body preview、body SHA、comment count 和 dump metadata 只能用于定位,不能作为需求理解依据。若 `gh issue view ... --json body|comments|--full|--raw` 触发 stdout dump wrapper,必须跟随 `data.dump.path` 读取完整 payload,或改用 `trans gh:/owner/repo/issue/<number> cat` 读取完整一楼正文。需要评论上下文时,继续读取完整 comments;默认 comments preview 不足以支持结论。
|
||||
`--json comments` 默认只返回 comment id、URL、作者、时间、正文字符数、body SHA 和短 preview;`--full` 仍保持评论列表有界,只有 `--raw` 会显式展开所有评论正文。读取单条完整 comment body 使用 `gh issue comment view <commentId> --full`,显式 `--json` 路径的 comments 位于 `.data.json.comments`,不要依赖顶层重复 comments。
|
||||
|
||||
### 创建
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue create \
|
||||
--repo owner/name \
|
||||
--title "标题" \
|
||||
--label "bug,v0.2" \
|
||||
--body-stdin <<'EOF'
|
||||
目标合并分支: HWLAB v0.2
|
||||
|
||||
正文(Markdown,支持换行、反引号、表格)
|
||||
EOF
|
||||
```
|
||||
|
||||
`--body-stdin` 是 heredoc/stdin 第一等入口。`--dry-run` 只输出计划不写 GitHub。
|
||||
|
||||
### 更新正文
|
||||
|
||||
```bash
|
||||
# 替换正文
|
||||
bun scripts/cli.ts gh issue update <number> --repo owner/name \
|
||||
--mode replace --body-stdin <<'EOF'
|
||||
新正文
|
||||
EOF
|
||||
|
||||
# 追加正文
|
||||
bun scripts/cli.ts gh issue update <number> --repo owner/name \
|
||||
--mode append --body-stdin <<'EOF'
|
||||
追加内容
|
||||
EOF
|
||||
```
|
||||
|
||||
`edit` 是 `update --mode replace` 的兼容别名。正式写入默认先读当前 issue 做 body guard。
|
||||
|
||||
### 局部修补正文(优先走 `trans gh:`)
|
||||
|
||||
```bash
|
||||
# Issue 正文:route 未写 /1 时默认一楼正文
|
||||
trans gh:/owner/name/issue/<number> apply-patch <<'PATCH'
|
||||
*** Begin Patch
|
||||
*** Update File: body.md
|
||||
@@
|
||||
-old text
|
||||
+new text
|
||||
*** End Patch
|
||||
PATCH
|
||||
|
||||
# PR 正文同样使用 body.md
|
||||
trans gh:/owner/name/pr/<number> apply-patch <<'PATCH'
|
||||
*** Begin Patch
|
||||
*** Update File: body.md
|
||||
@@
|
||||
-old text
|
||||
+new text
|
||||
*** End Patch
|
||||
PATCH
|
||||
```
|
||||
|
||||
GitHub issue/PR 正文局部修补必须优先走 `trans gh:/owner/repo/issue/<number> apply-patch` 或 `trans gh:/owner/repo/pr/<number> apply-patch`;虚拟文件固定是 `body.md`,`apply_patch` 和 `patch-apply` 仅作为兼容别名。底层 `bun scripts/cli.ts gh issue patch ... --body-patch-stdin` 是受控写回底座或兼容入口,不作为人工首选。`trans gh:` 会先读取当前正文,把 Codex `apply_patch` envelope 应用到 `body.md`,再通过现有 `gh issue/pr update` guard 写回;普通小补丁不需要固定先跑 `--dry-run`,高风险正文预览时才加 `--dry-run`。
|
||||
|
||||
### 评论
|
||||
|
||||
```bash
|
||||
# 创建评论
|
||||
bun scripts/cli.ts gh issue comment create <number|owner/repo#number> \
|
||||
--repo owner/name --body-stdin <<'EOF'
|
||||
评论正文
|
||||
EOF
|
||||
|
||||
# 读取单条评论
|
||||
bun scripts/cli.ts gh issue comment view <commentId> \
|
||||
--repo owner/name [--full|--raw]
|
||||
|
||||
# 原地修正评论
|
||||
bun scripts/cli.ts gh issue comment update <commentId> \
|
||||
--repo owner/name --body-stdin <<'EOF'
|
||||
新的评论正文
|
||||
EOF
|
||||
|
||||
# 局部修补评论
|
||||
bun scripts/cli.ts gh issue comment patch <commentId> \
|
||||
--repo owner/name --body-patch-stdin <<'PATCH'
|
||||
*** Begin Patch
|
||||
*** Update File: comment.md
|
||||
@@
|
||||
-old text
|
||||
+new text
|
||||
*** End Patch
|
||||
PATCH
|
||||
|
||||
# 删除评论
|
||||
bun scripts/cli.ts gh issue comment delete <commentId>
|
||||
```
|
||||
|
||||
`view` 默认返回 comment id、URL、作者、时间、正文字符数、body SHA 和短 preview;`--full` 只展开这一条 comment body。`edit` 是 `comment update` 的兼容别名。`--body <short-text>` 仅适合短单行。日常修正文案优先用 `patch` 或 `update/edit` 保留评论 ID 和时间线;`delete` 只用于确实需要删除的评论。`comment patch` 会先读取 comment id 对应的当前正文,把 envelope 应用到虚拟文件 `comment.md`,再 PATCH 单条评论;上下文不匹配时失败且不写入。
|
||||
|
||||
评论区不要写新的大计划。若评论草稿已经包含多阶段计划、改进清单或验收标准,改为创建新 issue,并在评论中只留下新 issue 链接和短结论。
|
||||
|
||||
### 关闭/重开
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue close <number|owner/repo#number> \
|
||||
--repo owner/name [--comment "关闭原因"]
|
||||
|
||||
bun scripts/cli.ts gh issue reopen <number|owner/repo#number> \
|
||||
--repo owner/name [--comment "重开原因"]
|
||||
```
|
||||
|
||||
附长证据时先用 `comment create` 写证据评论,再用 `close --comment <短引用>` 关闭。不支持 `delete`(走 `close`)。
|
||||
|
||||
### 批量过期关闭
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue stale-close \
|
||||
--repo owner/name [--inactive-hours 48] [--limit N] \
|
||||
[--label label] [--dry-run]
|
||||
```
|
||||
|
||||
关闭 `updatedAt < observedAt - inactiveHours` 的 open issue。先 `--dry-run` 观察,再正式执行。
|
||||
|
||||
### 转义扫描
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue scan-escape \
|
||||
--repo owner/name [--limit N] [--dry-run]
|
||||
```
|
||||
|
||||
只读扫描 issue 正文/评论中的字面量 `\n`、`\t`、ANSI escape 等污染。
|
||||
|
||||
---
|
||||
|
||||
## PR 命令
|
||||
|
||||
### 列表
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh pr list [owner/repo] \
|
||||
[--state open|closed|all] [--json ...] [--raw|--full]
|
||||
```
|
||||
|
||||
默认 stdout 是 PR 表格和短摘要,不输出 PR body 或 closeout metadata。脚本消费字段用 `--json`,全量排障用 `--full`/`--raw`。
|
||||
|
||||
### 查看
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh pr view <number|url|owner/repo#number> \
|
||||
[--repo owner/name] \
|
||||
[--json body,title,state,stateDetail,headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup] \
|
||||
[--raw|--full]
|
||||
```
|
||||
|
||||
默认 stdout 是 PR 元数据表格、短摘要、body chars/SHA/preview 和 Next 命令,不输出完整 body。`stateDetail` 归一化为 `open|closed|merged`。`mergeable`/`mergeStateStatus`/`statusCheckRollup` 走 GraphQL,且仅在显式请求相关 `--json` 字段或 `--full`/`--raw` 时读取。
|
||||
|
||||
### 文件变更
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh pr files <number> [--repo owner/name] [--limit N]
|
||||
bun scripts/cli.ts gh pr review-plan <number|url|owner/repo#number> [--repo owner/name] [--limit N]
|
||||
bun scripts/cli.ts gh pr diff <number|url|owner/repo#number> --file <path> [--hunk N] [--repo owner/name] [--limit N] [--full|--raw]
|
||||
```
|
||||
|
||||
默认 stdout 是 changed files 统计表格,不输出 raw diff。`gh pr diff <number> --stat` 是兼容别名。
|
||||
|
||||
`review-plan` 是主代理 review PR 的 bounded patch index:默认输出 changed files、add/del、hunk 数、patch line 数、默认 patch drill-down 是否会截断,以及每个返回文件的 `gh pr diff --file` 命令。它不创建本地 review worktree,也不输出 patch body。
|
||||
|
||||
`gh pr diff --file <path>` 读取单个 changed file 的 GitHub REST patch,默认只显示 bounded patch excerpt;`--hunk N` drill-down 到单个 hunk,`--limit N` 调整默认显示行数。只有显式 `--full`/`--raw` 才在结构化输出里包含完整 file patch。
|
||||
|
||||
### 收口预检
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh pr preflight <number|owner/repo#number> \
|
||||
[--repo owner/name] [--full|--raw]
|
||||
```
|
||||
|
||||
`preflight`/`closeout` 是别名。只读检查 state/draft/conflict/mergeable/statusCheck,不写 GitHub。默认只给 status check 计数与失败预览。
|
||||
|
||||
### 创建
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh pr create \
|
||||
--repo owner/name \
|
||||
--title "标题" \
|
||||
--body-stdin --base master --head <branch> \
|
||||
[--draft] [--dry-run] <<'EOF'
|
||||
PR 正文
|
||||
EOF
|
||||
```
|
||||
|
||||
`pr create` 成功后的 Next 默认提示使用 `gh pr merge ... --merge --delete-branch`。只有确认不需要保留 ancestry 或 merge parent history 时,才显式改用 `--squash`。
|
||||
|
||||
### 合并
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh pr merge <number> \
|
||||
[--repo owner/name] [--merge|--squash|--rebase] \
|
||||
[--delete-branch] [--dry-run]
|
||||
```
|
||||
|
||||
一键 guarded merge:`gh pr merge` 自己读取 closeout metadata、mergeability、mergeStateStatus 和 status checks,不要求用户先跑 `gh pr preflight`。`preflight`/`closeout` 只作为只读诊断入口保留。
|
||||
|
||||
当 GitHub GraphQL 返回 `mergeable=UNKNOWN/null`、`mergeStateStatus=UNKNOWN/null` 或 status rollup unknown 且没有明确 blockers 时,`gh pr merge` 会按 `config/unidesk-cli.yaml` 的 `github.prMerge.unknownRetry` 做指数退避重试,并在默认表格 `RETRY` 列显示 `1/5`、`2/5` 等尝试次数。重试耗尽仍 unknown 时,命令自动停止且不合并;冲突、draft、失败检查、pending check 等不可合并状态不会无意义重试。已 merged 返回 `alreadyMerged=true`。
|
||||
|
||||
### 开发任务收口
|
||||
|
||||
完成开发任务后必须完整收口:创建 PR、通过受控 `gh pr merge` 合并 PR、删除远程任务分支、删除本地任务 worktree,并把主 worktree 快进到最新 `master`。远程分支优先通过 `gh pr merge ... --delete-branch` 删除;若合并后远端分支仍存在,再显式 `git push origin --delete <branch>`。
|
||||
|
||||
删除本地 worktree 前先确认工作区 clean,且任务变更已经进入 `origin/master`;squash merge 场景下不能只看本地分支是否是 `origin/master` 祖先,还要用 PR merged 状态、`git cherry` 等价补丁或关键文件 diff 确认语义已吸收。确认后执行 `git worktree remove .worktree/<task>`;如本地任务分支仅用于该 worktree 且已语义合入,可同步删除本地分支。
|
||||
|
||||
最后在主 worktree 执行 `git fetch origin master` 和 `git pull --ff-only origin master`,让主 worktree 回到最新 `master`。如果主 worktree 存在并行脏改、非 `master` 分支或其他原因导致不能安全 fast-forward,禁止 `reset`、`stash`、`checkout --` 或清理他人改动;改用干净 `origin/master` worktree 继续后续工具命令,并在最终回复中明确说明主 worktree 未更新的原因。
|
||||
|
||||
### 编辑 / 评论 / 关闭 / 重开
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh pr update <number> --mode replace|append --body-stdin [--title ...]
|
||||
bun scripts/cli.ts gh pr comment create <number> --body-stdin
|
||||
bun scripts/cli.ts gh pr comment update <commentId> --body-stdin
|
||||
bun scripts/cli.ts gh pr comment edit <commentId> --body-stdin
|
||||
bun scripts/cli.ts gh pr comment delete <commentId>
|
||||
bun scripts/cli.ts gh pr close <number> [--comment ...]
|
||||
bun scripts/cli.ts gh pr reopen <number> [--comment ...]
|
||||
```
|
||||
|
||||
`pr comment edit` 是 `pr comment update` 的兼容别名。与 issue 对应命令行为一致;PR 评论也是 GitHub issue comment,更新目标使用 commentId。
|
||||
|
||||
---
|
||||
|
||||
## 虚拟文件系统 (`trans gh:`)
|
||||
|
||||
```bash
|
||||
# 列出 repo
|
||||
trans gh:/pikasTech/HWLAB ls
|
||||
|
||||
# 列出 PR / Issue
|
||||
trans gh:/pikasTech/HWLAB/pr ls [--state open|closed|all] [--limit N] [--full]
|
||||
trans gh:/pikasTech/HWLAB/issue ls [--state open|closed|all] [--limit N] [--search TEXT] [--label L] [--full]
|
||||
|
||||
# 读取 / 检索正文
|
||||
trans gh:/pikasTech/HWLAB/issue/1236 cat
|
||||
trans gh:/pikasTech/HWLAB/issue/1236 rg 'API_KEY|YAML-first'
|
||||
|
||||
# 查看单个条目
|
||||
trans gh:/pikasTech/HWLAB/pr/507 ls
|
||||
trans gh:/pikasTech/HWLAB/505/1 cat
|
||||
|
||||
# apply-patch 写回正文(走 gh issue/pr update)
|
||||
trans gh:/pikasTech/HWLAB/pr/507 apply-patch <<'PATCH'
|
||||
*** Begin Patch
|
||||
*** Update File: body.md
|
||||
@@
|
||||
old line
|
||||
-old line
|
||||
+new line
|
||||
more context
|
||||
*** End Patch
|
||||
PATCH
|
||||
```
|
||||
|
||||
正文一楼映射为 `body.md`;route 未写 `/1` 时默认一楼正文。`issue ls --search/--state` 用于替代常见 `bun scripts/cli.ts gh issue list ... --search/--state` 读取;单条正文优先用 `cat` 或 `rg`,不要为了看 body 再走 `gh issue view --json body --full`。写回走 `gh issue/pr update` 的 guard 规则。`apply-patch` 是首选 operation,`apply_patch` 与 `patch-apply` 只作为兼容别名;普通小补丁在已读取上下文后可以直接 apply,`--dry-run` 只作为高风险正文的可选预览。`rm` 对正文结构化拒绝。
|
||||
|
||||
---
|
||||
|
||||
## 看板命令 ([#20](https://github.com/pikasTech/unidesk/issues/20) 总看板)
|
||||
|
||||
```bash
|
||||
# 结构审计
|
||||
bun scripts/cli.ts gh issue board-audit \
|
||||
--repo pikasTech/unidesk --board-issue 20 [--dry-run]
|
||||
|
||||
# 行列表
|
||||
bun scripts/cli.ts gh issue board-row list \
|
||||
--board-issue 20 [--state open|closed|all]
|
||||
|
||||
# 行查看
|
||||
bun scripts/cli.ts gh issue board-row get <issueNumber> --board-issue 20
|
||||
|
||||
# 行更新(单字段)
|
||||
bun scripts/cli.ts gh issue board-row update <issueNumber> \
|
||||
--board-issue 20 \
|
||||
--field progress|status|validation|branch|tasks|focus \
|
||||
--value <text> \
|
||||
[--expect-body-sha <sha>|--expect-updated-at <ts>]
|
||||
|
||||
# 行新增/插入(upsert)
|
||||
bun scripts/cli.ts gh issue board-row upsert <issueNumber> \
|
||||
--board-issue 20 --section open|closed \
|
||||
--branch <branch> --tasks <task> --summary <text> \
|
||||
--focus <text> --validation <text> --progress <text> \
|
||||
[--expect-body-sha <sha>|--expect-updated-at <ts>]
|
||||
|
||||
# 行移动 / 删除
|
||||
bun scripts/cli.ts gh issue board-row move <issueNumber> \
|
||||
--board-issue 20 --to open|closed [--status OPEN|CLOSED]
|
||||
bun scripts/cli.ts gh issue board-row delete <issueNumber> \
|
||||
--board-issue 20
|
||||
```
|
||||
|
||||
写操作默认 dry-run,正式 PATCH 必须带 `--expect-body-sha` 或 `--expect-updated-at`。
|
||||
|
||||
---
|
||||
|
||||
## 关键约定
|
||||
|
||||
### heredoc 优先
|
||||
|
||||
多行正文/评论一律用 `--body-stdin <<'EOF'`(quoted heredoc),不用 `--body` 内联长文本、不用临时文件、不用 `gh api -f body=...`:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue create --repo pikasTech/HWLAB \
|
||||
--title "标题" --body-stdin <<'EOF'
|
||||
正文,支持 `code`、**bold**、表格等 Markdown
|
||||
EOF
|
||||
```
|
||||
|
||||
### owner/repo#number shorthand
|
||||
|
||||
所有接受 issue/PR number 的命令都支持:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue view pikasTech/HWLAB#1024
|
||||
bun scripts/cli.ts gh pr preflight pikasTech/HWLAB#1020
|
||||
bun scripts/cli.ts gh issue comment create pikasTech/HWLAB#1024 --body-stdin <<'EOF'
|
||||
...
|
||||
EOF
|
||||
```
|
||||
|
||||
shorthand 与显式 `--repo` 冲突时结构化失败。
|
||||
|
||||
### 高风险写入预览
|
||||
|
||||
整篇 replace、批量 lifecycle、PR merge 或并发敏感正文写入可先 `--dry-run`;`trans gh:/... apply-patch` 的普通小补丁在已读取上下文后不需要固定 dry-run:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gh issue update 20 --mode append --body-stdin --dry-run <<'EOF'
|
||||
...
|
||||
EOF
|
||||
```
|
||||
|
||||
### debug 任务入口
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run ...] [--pr-create-dry-run ...] [--issue N] [--full|--raw]
|
||||
```
|
||||
|
||||
用于 PR 型派单 admission,检查 scheduler auth、runner GH token、git worktree、GitHub egress 等。
|
||||
@@ -0,0 +1,9 @@
|
||||
# GitHub Issues
|
||||
|
||||
Issue writes use `bun scripts/cli.ts gh ...` or the `trans gh:` virtual filesystem.
|
||||
|
||||
- Body and comments default to Chinese.
|
||||
- New issues include `目标合并分支`.
|
||||
- Multi-stage architecture/API/platform issues begin with `P0 SPEC 先行`.
|
||||
- Long body text uses `--body-stdin`.
|
||||
- Use bounded views first; request `--json`, `--full` or `--raw` only when needed.
|
||||
@@ -0,0 +1,10 @@
|
||||
# GitHub Pull Requests
|
||||
|
||||
PR work uses guarded UniDesk GitHub commands:
|
||||
|
||||
- `pr review-plan`, `pr diff --file`, and bounded file drill-down before review.
|
||||
- `pr preflight` before merge when required by the issue or branch policy.
|
||||
- `pr merge --merge --delete-branch` by default.
|
||||
- Use squash only when ancestry and semantic absorption are explicitly safe.
|
||||
|
||||
Closeout should mention source branch, validation evidence and any residual risk.
|
||||
@@ -0,0 +1,5 @@
|
||||
# GitHub Virtual Filesystem
|
||||
|
||||
Use `trans gh:/... cat` or `apply-patch` when editing an existing issue, PR body or comment.
|
||||
|
||||
Keep patches localized and preserve unrelated text. Do not hand-roll GitHub API calls when the UniDesk GitHub CLI or `trans gh:` route supports the operation.
|
||||
@@ -81,4 +81,4 @@ Prefer Kubernetes-native discovery and isolation before inventing a custom contr
|
||||
- ConfigMap/Secret carry non-secret config and sourceRef-derived runtime material; output remains redacted.
|
||||
- Prometheus/ServiceMonitor may scrape `/metrics` when the namespace already has that stack, but report drill-down should stay on runner HTTP APIs or a declared shared store.
|
||||
|
||||
Read `references/full.md` for the current D601/v03 Web 哨兵 command matrix, dashboard triage checklist, and multi-sentinel target architecture.
|
||||
Read [references/web-sentinel.md](references/web-sentinel.md) for the Web 哨兵 command matrix and target boundaries, and [references/dashboard.md](references/dashboard.md) for dashboard triage and screenshot evidence.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Sentinel Dashboard
|
||||
|
||||
Dashboard evidence must distinguish service/API readiness from browser rendering.
|
||||
|
||||
- Use `dashboard verify|screenshot` for repeatable evidence.
|
||||
- Record screenshot path, SHA, HTTP status, DOM summary and overflow result.
|
||||
- A returned PNG only proves capture transport; inspect DOM or screenshot summary before accepting it.
|
||||
- Small error pages, login pages and blank shells are invalid closeout evidence.
|
||||
@@ -1,175 +0,0 @@
|
||||
# UniDesk Monitor Reference
|
||||
|
||||
## Current Web Sentinel Surface
|
||||
|
||||
Primary registry:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts web-probe sentinel status --node <node> --lane <lane>
|
||||
```
|
||||
|
||||
Known sentinel ids vary by node/lane and must come from YAML. Common `v03` examples include:
|
||||
|
||||
- `workbench-dsflash-go-tool-call-10x`
|
||||
- `workbench-auth-session-switch-2users`
|
||||
- `workbench-fake-echo-session-invariance-10x`
|
||||
- `mdtodo-visual-regression`
|
||||
- `jd01-web-probe-sentinel`
|
||||
|
||||
Per-sentinel drill-down:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts web-probe sentinel status --node <node> --lane <lane> --sentinel <id>
|
||||
bun scripts/cli.ts web-probe sentinel control-plane status --node <node> --lane <lane> --sentinel <id>
|
||||
```
|
||||
|
||||
Freshness-only check:
|
||||
|
||||
```bash
|
||||
bun scripts/web-probe-sentinel-scheduler.ts run --node <node> --lane <lane> --sentinel <id> --stale-multiplier 1 --dry-run
|
||||
```
|
||||
|
||||
Cadence/runtime validation is k3s-first:
|
||||
|
||||
```bash
|
||||
trans <node>:k3s kubectl -n <namespace> get cronjob -l app.kubernetes.io/component=cadence-scheduler
|
||||
trans <node>:k3s kubectl -n <namespace> create job --from=cronjob/<quick-verify-cronjob> <manual-job-name>
|
||||
```
|
||||
|
||||
Host `systemd` timer commands are legacy diagnostics only. Enabled HWLAB Web sentinels must run from target node/lane k3s CronJob/GitOps. If a sentinel's latest run is stale, first compare YAML cadence, latest run age and rendered CronJob state for that sentinel; a missing or stale CronJob is a runtime defect even if the dry-run scheduler can enumerate the sentinel and mark it due.
|
||||
|
||||
Dashboard render and screenshot verification:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts web-probe sentinel dashboard verify --node <node> --lane <lane> --sentinel <id>
|
||||
bun scripts/cli.ts web-probe sentinel dashboard screenshot --node <node> --lane <lane> --sentinel <id>
|
||||
```
|
||||
|
||||
The screenshot command runs through the selected node/lane remote browser and downloads the PNG artifact to the caller's `/tmp` by default. Closeout evidence should cite `localPath`, `sha256`, page HTTP status, selected DOM summary fields and `layout.horizontalOverflow` / `overflowCount`; do not replace this with a local browser screenshot or ad-hoc `web-probe script` when the sentinel command can cover the page.
|
||||
|
||||
`dashboard verify` is the canonical monitor-web DOM contract. It should assert that trend latest-point counts match the latest `/api/runs?sort=updated` run, and that the monitor-web check panel is scoped to the selected run by default. For a selected run, trend error/warning points, run-row tags and check table rows must all be derived from `/api/runs/{id}.findings` detail rows. Do not fall back to top-level sample counters such as `severityCounts` for user-visible run aggregates; historical `/api/findings` aggregates may be available only behind an explicit time-window scope and must be labeled as historical.
|
||||
|
||||
Use the freshness-only `--dry-run` scheduler command when the question is only "how old is the latest run?". It reads cadence, latest age, due status and latest run id without starting a new browser observation. If an enabled sentinel is `due` or stale while other sentinels are fresh, treat it as a sentinel-specific cadence or runtime issue and record the sentinel id, cadence, latest age and run id before starting a repair loop.
|
||||
|
||||
Report drill-down:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts web-probe sentinel report --node <node> --lane <lane> --sentinel <id> --latest --view summary
|
||||
bun scripts/cli.ts web-probe sentinel report --node <node> --lane <lane> --sentinel <id> --latest --view findings
|
||||
bun scripts/cli.ts web-probe sentinel report --node <node> --lane <lane> --sentinel <id> --run <runId> --view trace-frame
|
||||
```
|
||||
|
||||
When a findings view shows only `quick-verify-no-business-turn` / WBC-003, do not conclude that the target produced no analyzer findings until the existing artifact has been checked. Compare the public runner API with the CLI raw view for the same run:
|
||||
|
||||
```bash
|
||||
curl -fsS 'https://monitor.pikapython.com/api/report?view=findings&run=<runId>'
|
||||
bun scripts/cli.ts web-probe sentinel report --node <node> --lane <lane> --sentinel <id> --run <runId> --view findings --raw
|
||||
```
|
||||
|
||||
If the public API returns `findingCount=1` but the CLI raw view shows non-empty `artifactSummary.findings`, the service index is stale or cannot read the old `stateDir/analysis/report.json`. Reindex or backfill the existing run through the runner service's controlled record path, preserving existing report views; do not start a new observe run just to make the old finding list visible. If both views lack analyzer findings, then investigate the analyzer artifact and original observe run. If `artifactSummary.reason=analysis-report-json-missing-or-invalid`, treat it as an index/artifact visibility gap, not as proof that WBC-003 is the only finding.
|
||||
|
||||
Public dashboard paths:
|
||||
|
||||
- `https://monitor.pikapython.com/`
|
||||
- `https://monitor.pikapython.com/sentinels/d518-workbench-dsflash-go-tool-call-10x/`
|
||||
- `https://monitor.pikapython.com/sentinels/workbench-auth-session-switch-2users/`
|
||||
- `https://monitor.pikapython.com/sentinels/jd01-web-probe-sentinel/`
|
||||
|
||||
Direct API probes for shell/API/render separation:
|
||||
|
||||
```bash
|
||||
curl -sS -D - https://monitor.pikapython.com/ -o /tmp/monitor-root.html
|
||||
curl -sS -D - https://monitor.pikapython.com/api/overview -o /tmp/monitor-overview.json
|
||||
curl -sS -D - https://monitor.pikapython.com/api/runs -o /tmp/monitor-runs.json
|
||||
curl -sS -D - https://monitor.pikapython.com/sentinels/workbench-auth-session-switch-2users/api/overview -o /tmp/monitor-auth-overview.json
|
||||
```
|
||||
|
||||
Use `web-probe script` with explicit `page.goto("https://monitor.pikapython.com/...")` only for one-off exploration that is not yet covered by `web-probe sentinel dashboard verify|screenshot`; repeated checks must be promoted into those sentinel dashboard commands.
|
||||
|
||||
## YAML Ownership
|
||||
|
||||
Root registry:
|
||||
|
||||
- `config/hwlab-node-lanes.yaml#lanes.v03.targets.D601.observability.webProbe.sentinels`
|
||||
- `config/hwlab-node-lanes.yaml#lanes.v03.targets.D518.observability.webProbe.sentinels`
|
||||
- `config/hwlab-node-lanes.yaml#lanes.v03.targets.JD01.observability.webProbe.sentinels`
|
||||
|
||||
Per-sentinel management YAML:
|
||||
|
||||
- `config/hwlab-web-probe-sentinels/d601-v03/workbench-dsflash-go-tool-call-10x.yaml#sentinel`
|
||||
- `config/hwlab-web-probe-sentinels/d601-v03/workbench-auth-session-switch-2users.yaml#sentinel`
|
||||
- `config/hwlab-web-probe-sentinels/d601-v03/mdtodo-visual-regression.yaml#sentinel`
|
||||
- `config/hwlab-web-probe-sentinels/d518-v03/workbench-dsflash-go-tool-call-10x.yaml#sentinel`
|
||||
- `config/hwlab-web-probe-sentinels/d518-v03/workbench-fake-echo-session-invariance-10x.yaml#sentinel`
|
||||
- `config/hwlab-web-probe-sentinel/profiles.yaml#nodes.JD01.sentinels.jd01-web-probe-sentinel.sentinel`
|
||||
|
||||
Typical config refs:
|
||||
|
||||
- runtime: Deployment, Service, PVC, state root, SQLite, scheduler interval.
|
||||
- workflow/scenarios: observed route, cadence, command sequence, prompt/report refs.
|
||||
- promptSet: synthetic prompt ids and redaction policy.
|
||||
- reportViews: default and drill-down views.
|
||||
- publicExposure: `monitor.pikapython.com`, FRP, Caddy route prefix.
|
||||
- cicd: source, image, GitOps, Argo and target validation.
|
||||
- secrets: sourceRef and targetKey only; no values in stdout or issue bodies.
|
||||
|
||||
## Dashboard Triage Checklist
|
||||
|
||||
Classify a monitor page issue in this order:
|
||||
|
||||
1. HTML shell loads: root status, asset links, `data-sentinel-id`, `data-base-path`, contract version.
|
||||
2. API returns data: `/api/overview`, `/api/runs`, `/api/findings`, selected `/api/runs/{id}`.
|
||||
3. Count scopes are coherent: trend latest point is the selected/latest run's check row count by severity, not a raw sample count; selected run detail exposes row count and sample count separately; monitor-web checks default to selected-run scope and only show historical aggregates behind an explicit history/time-window selector.
|
||||
4. Browser render executes: page errors, console errors, DOM rows, status summary, check cards, scope labels and overflow state.
|
||||
5. Runtime service health: `/api/health`, `/metrics`, scheduler heartbeat, SQLite/PVC write probe.
|
||||
6. Control-plane health: source, registry image, git-mirror, GitOps, Argo, Deployment/Service/PVC.
|
||||
|
||||
Do not treat public root/CSS/JS 200 as dashboard success. Browser console and DOM render evidence are required.
|
||||
|
||||
When a Pod and its ClusterIP Service return the expected monitor-web asset but `https://monitor.pikapython.com/monitor-web/assets/...` still returns an old file, check the public FRP hop before changing UI code again. On D518 this has happened when a stale legacy `hwlab-web-probe-sentinel-frpc` Argo app still owned PK01 remote port `22093`, while the active `hwlab-web-probe-sentinel-fake-echo-frpc` logged `start error: port already used`; Caddy then served the old runner even though the new Pod and runtime image were healthy. Retire the stale Argo/frpc owner through a controlled YAML/CLI path and then rerun `dashboard verify`.
|
||||
|
||||
## Rollout Closeout
|
||||
|
||||
For a Web sentinel fix, closeout needs four independent evidence surfaces:
|
||||
|
||||
1. `web-probe sentinel control-plane status --node <node> --lane <lane> --sentinel <id>` must be `ok`, with source, registry, GitOps, Argo and runtime image aligned. If runtime already has the expected image but Argo target/revision is stale, run the controlled `control-plane apply --confirm --wait` path once with a bounded timeout, then recheck status; do not declare the rollout complete on runtime readiness alone.
|
||||
2. `web-probe sentinel validate --node <node> --lane <lane> --sentinel <id>` must pass `/api/health`, `/metrics`, indexed recent report, public exposure and public dashboard probes.
|
||||
3. `web-probe sentinel dashboard screenshot --node <node> --lane <lane> --sentinel <id>` must pass through the remote browser and save a verified PNG to the caller `/tmp`; record the local path, hash and layout overflow result.
|
||||
4. `web-probe sentinel report --node <node> --lane <lane> --sentinel <id> --latest --view summary` must show the latest business run, report hash, samples and finding severity. Auth/login failures, submit failures, missing samples, absent recent reports and Code Agent multi-round continuity breaks are blockers. YAML elapsed-budget or total-turn timing alerts are non-blocking warnings unless they coincide with a failed turn, broken continuity, missing report or unavailable user path.
|
||||
5. For monitor-web count fixes, `dashboard verify` must include the latest run id, trend point row counts, selected-run check table counts, selected run-row red/yellow tags and any targeted run filter evidence. A pass requires all user-visible run aggregates to match `/api/runs/{id}.findings` detail rows, not just chart arithmetic.
|
||||
|
||||
Long `quick-verify` or CI/CD waits should be bounded by the YAML-declared budget and the operator's outer timeout. If a wait would exceed about two minutes during rollout, first inspect the visible stage and either optimize the slow path, defer the expensive quick verify to manual validation, or record it as a non-blocking timing warning; do not dead-wait without new evidence.
|
||||
|
||||
After adding a new quick-verify or analyzer finding id, run the sentinel plan before rollout and verify the selected check catalog contains that id. A missing catalog row is a runtime health defect: the service can start, report `config.ok=false`, return `/api/health` 503 and leave the new pod in CrashLoopBackOff while an older ready pod continues serving. Fix the catalog through YAML/source control, redeploy, and only then validate the report/dashboard path.
|
||||
|
||||
If `origin/master` advances while rolling out a sentinel, first classify the new commits. For unrelated parallel changes, finish the current bounded check, wait briefly for the branch head to stabilize, then perform one final rollout/status pass against the stable head. Do not loop forever chasing every concurrent merge, but do not call a rollout complete while source truth, internal mirror and runtime image point at different commits.
|
||||
|
||||
Source mirror readiness must be proven by the internal mirror object/read probe for the expected commit. A GitHub/source head check alone is not sufficient evidence to skip source sync, because it does not prove the k3s publish job can fetch the object from the node-local mirror.
|
||||
|
||||
If the internal mirror branch is ahead of the expected commit, status may still be ready only when the expected object exists and `expected` is an ancestor of the mirror branch tip. Treat that as `mirror-ahead`, not as a source blocker. An exact SHA match is sufficient but not required during parallel master advancement.
|
||||
|
||||
Registry readiness must follow the target node YAML. On nodes where `registry.mode` is `k8s-workload` but the effective registry endpoint is a loopback address, use the controlled `web-probe sentinel image status` / `control-plane status` registry probe rather than ad-hoc Service DNS curls; a host-network registry that binds `127.0.0.1` can be healthy for node-local publish and pulls while its ClusterIP Service is not a valid manifest probe path. OCI manifest probes must send an OCI/Docker manifest `Accept` header before treating a tag as missing.
|
||||
|
||||
When `trigger-current --confirm --wait` defers git-mirror flush into an async job, judge rollout readiness with a follow-up `web-probe sentinel control-plane status` and `hwlab nodes git-mirror status`. If the async flush job exits nonzero but the follow-up status shows source/registry/GitOps/Argo/runtime aligned and git mirror `pending=false` / `inSync=true`, record the async job failure as a closeout visibility issue rather than blocking the already-aligned sentinel rollout. If `pending=true` or GitOps is not in sync, continue through the controlled git-mirror flush path.
|
||||
|
||||
Dashboard aggregate counters may include historical runs only when the UI labels that scope explicitly. They must not sit beside a latest-run chart or selected-run check list without a scope label. If trend, run detail and check list disagree, first identify whether each number is a type count, sample count or historical aggregate before changing code.
|
||||
|
||||
For Code Agent multi-round quick-verify, accept the latest run's `turn-summary` / `trace-frame` plus `blockingFindingCount=0` and `controlFindingCount=0`. Analyzer red findings about hydration, API-to-DOM lag or timing drift are investigation evidence unless they coincide with missing durable turns/final responses, failed submit/login/auth, broken continuity, absent report or unavailable user path.
|
||||
|
||||
## Multi-Sentinel Target Architecture
|
||||
|
||||
Current implementation has independent sentinel runner services, but each runner still serves its own dashboard. The target architecture should split:
|
||||
|
||||
- `sentinel-runner`: one Deployment/PVC/Service per sentinel id; owns scheduler, observe wrapper, SQLite/report index, `/api/report`, `/api/overview`, `/metrics`.
|
||||
- `monitor-web`: one Deployment/Service/public route per workspace or node/lane; owns dashboard shell, multi-sentinel registry view, aggregation/proxy APIs and public HTTPS exposure.
|
||||
|
||||
Recommended Kubernetes-native mechanisms:
|
||||
|
||||
- Label all runner Services and Pods with `unidesk.ai/web-probe-sentinel-id`, node, lane, workspace and component.
|
||||
- Let monitor-web discover runners using Kubernetes Services or EndpointSlices with label selectors.
|
||||
- Keep each runner Service `ClusterIP`; expose only monitor-web through declared `publicExposure`.
|
||||
- Use runner Service DNS or Kubernetes discovery for aggregation, not hard-coded route prefixes.
|
||||
- Keep per-runner PVCs for local report history unless a shared store is explicitly specified.
|
||||
- Use CronJob only for short, stateless periodic probes. Keep Deployment for long-lived browser observers, maintenance API and progressive reports.
|
||||
|
||||
This keeps runner isolation while giving users one monitor page for all sentinels in a workspace.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Web Sentinel
|
||||
|
||||
Web sentinel operations stay YAML-first. Node, lane, sentinel id, cadence, Deployment, Service, PVC, route prefix, dashboard URL and Secret sourceRefs come from config.
|
||||
|
||||
Use controlled `web-probe sentinel ...` commands for status, report, control-plane, validate and dashboard operations. Direct `kubectl create job --from=cronjob` is diagnostic only, not acceptance evidence.
|
||||
@@ -16,7 +16,6 @@ HWLAB Cloud M1 / UniDesk / AgentRun 跨仓项目治理锚定在 UniDesk 仓库
|
||||
|
||||
## Reference 路由
|
||||
|
||||
- 项目规划、层级职责、issue 锚点和工作流的完整旧规则在 [references/full.md](references/full.md)。
|
||||
- 项目规划细则读 [references/project-planning.md](references/project-planning.md)。
|
||||
- 模板读 [references/templates.md](references/templates.md)。
|
||||
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
---
|
||||
name: unidesk-oa
|
||||
description: UniDesk 项目管理运行技能。用户提到 UniDesk 项目管理目录、HWLAB OA、总项目规格、总规划、重大项目/方向/课题/子课题层级、任务书、实施方案、测试规格、阶段报告、项目偏离,或通过 project-management 与 GitHub issue 管理 HWLAB Cloud M1 / UniDesk / AgentRun 跨仓项目治理时使用。
|
||||
---
|
||||
|
||||
# UniDesk OA
|
||||
|
||||
使用本技能时,把 HWLAB Cloud M1 的项目管理稳定地锚定在 UniDesk 仓库内的项目编号目录和相互链接的 GitHub issue 执行记录上。
|
||||
|
||||
## 仓库
|
||||
|
||||
- UniDesk 项目管理真相源:`/root/unidesk/project-management/PJ2026-01`,其中 `specs/PJ2026-01-HWLAB.md` 是 L0 总规格和项目索引,随 `pikasTech/unidesk` 的 `master` 分支版本化。AgentRun 作为 HWLAB Agent编排的执行基础设施归入 `PJ2026-0102` 及其平台运维支撑规格,不单独创建新的 L0 项目。
|
||||
- 迁移前 HWLabOA 仓库:`/root/HWLabOA` 只作为历史材料和对照来源,不再作为 HWLAB Cloud M1 规格真相源。
|
||||
- 使用 UniDesk GitHub CLI、修改本技能或修改项目管理文档前,必须在 `/root/unidesk` 执行 `git status --short --branch` 和 `git pull --ff-only origin master`。
|
||||
|
||||
## 运行模型
|
||||
|
||||
- 将 `project-management/PJ2026-01` 下的 Markdown 视为长期规格真相源。
|
||||
- 将 GitHub issue 视为执行控制面、历史讨论入口和长证据承载处:状态、讨论、跨仓引用、PR 链接和收口证据可以在 issue 中流转,但规格正文不得只写在 issue 中。
|
||||
- 长证据、trace、CaseRun registry、运行日志、历史 issue 摘要和证据索引保留在 GitHub issue 或具体执行 issue 中,禁止以 `evidence/` 目录或长证据文件形式污染 `project-management/PJ2026-01`。
|
||||
- `project-management/PJ2026-01/specs/*.md` 规格文件不保留单独的迁移来源块;历史来源只写在修改历史 `v0.1` 的变更说明中,格式为 `迁移来源 <owner>/<repo>#<number>`。规格文件引用其他规格必须使用同目录相对路径 Markdown 链接,禁止引用其他规格的 GitHub issue、证据 issue、PR 或裸 `#<number>`;唯一例外是第 7 章过程控制可保留跨 L1 阶段验证 issue 索引,用于内测、灰度和用户反馈分流,不承载问卷正文、参与者资料、原始反馈、长证据或执行流水。
|
||||
- AgentRun 仓库内 `docs/reference/spec-v01-*.md` 和 `docs/reference/architecture.md` 不再承载规格正文;迁移后只保留指向 UniDesk OA 规格的交叉引用 stub,避免 AgentRun repo 与 `project-management/PJ2026-01` 双份规格漂移。AgentRun 专项内容按职责落到 `project-management/PJ2026-01/specs/PJ2026-0102-agent-orchestration.md`、`project-management/PJ2026-01/specs/PJ2026-010601-controlled-release.md`、`project-management/PJ2026-01/specs/PJ2026-010602-source-sync.md`、`project-management/PJ2026-01/specs/PJ2026-010603-yaml-first-ops.md`、`project-management/PJ2026-01/specs/PJ2026-010604-public-entry.md` 或 `project-management/PJ2026-01/specs/PJ2026-010605-observability-monitoring.md` 及其 L3,不创建 `PJ2026-02`。
|
||||
- HWLAB v0.2/v0.3 仓库内 `docs/reference/spec-*` 以及 `cloud-workbench.md`、`code-agent-chat-readiness.md`、`g14-gitops-cicd.md` / `node-gitops-cicd.md`、`dev-runtime-boundary.md`、`gateway-outbound-demo.md`、`MVP-e2e-acceptance.md`、`architecture.md` 已收编为 UniDesk OA 交叉引用或历史 stub;这些路径只保留链接兼容和运行参考,不能再作为需求规格正文来源。需要变更 Web/CLI/API、Code Agent、runtime boundary、公开入口、测试大纲或 Gateway 主动出站需求时,先更新 `project-management/PJ2026-01/specs/` 和相应测试/过程 issue。
|
||||
- 写入 GitHub issue/PR 正文或评论时,issue/PR 引用必须写成 `[#<number>](https://github.com/<owner>/<repo>/issues/<number>)` 或 `[#<number>](https://github.com/<owner>/<repo>/pull/<number>)`,显示短号、链接目标保留完整 URL;不要显示裸长链接、裸井号编号或 `owner/repo` 加井号编号。CLI 参数中的 `owner/repo#number` shorthand 只作为命令输入例外。
|
||||
- 不要让日报或阶段报告成为总规划。阶段报告只总结相对总规格的移动,不能替代中心规划。
|
||||
- 当任务缺少上级方向、验收标准或原始验证入口时,先归类为规划/调查,不要直接变成实现任务。
|
||||
- L1 方向必须是直接服务 L0 使命的能力域。文档整理、阶段报告、项目管理机制、看板维护、技能维护、仓库名、工具名和临时执行路径都不能作为 L1 方向。
|
||||
- L1 是验收主责,不是宽泛标签。跨方向 issue 可以列关联 L1,但必须且只能有一个主责 L1,选择依据是哪个方向定义完成标准。
|
||||
- 重大规划型 issue 必须 P0 SPEC-first:P0 阶段先在 `project-management/PJ2026-01/specs/` 维护对应 SPEC,明确 SPEC 编号、上级/关联规格、架构图、数据流图、关键时序图和代码引用规则,再进入后续实现。issue 正文只能承载执行计划和证据,不能替代 SPEC 正文。
|
||||
|
||||
## 需求规格写作规则
|
||||
|
||||
- 需求规格是对外系统能力、边界和职责说明,不写内部治理、issue 关闭规则、长期面板、阶段报告、执行控制面、单次验证流水、PR 过程或证据堆叠。
|
||||
- 文档控制表只保留规格自身必要字段;状态只能写 `已生效`、`已废弃` 或 `未生效`。不要在文档控制表里写长期面板、阶段规格、L1 划分、规格来源或迁移来源。
|
||||
- 修改历史只记录已经定稿的版本。用户未明确说“可以定稿”前,不新增 `待提交` 版本号,也不把每次小编辑拆成一个版本。
|
||||
- L0、L1、L2 需求规格正文固定使用裁剪后的 1-7 章结构:文档控制、目的和范围、术语表、系统边界和接口、内部分工与规格索引、原子需求、过程控制。第 7 章只索引阶段验证、内测、灰度或用户反馈分流 issue;不要在 SPEC 正文追加假设和风险、变更控制、开放缺口、验收标准或执行过程流水。
|
||||
- 涉及新能力、跨模块架构、用户可见工作流、长期 API/数据模型、平台运维能力或多阶段实施的 SPEC,必须在正文中包含目标架构图、数据流图和关键时序图;图形优先使用 Markdown 内嵌 `mermaid`,并和原子需求编号互相对应。
|
||||
- L0 系统边界必须把 HWLAB 作为完整系统看待,描述外部使用者、外部输入、受控资源、外部输出、用户接口和系统责任边界,不写内部治理材料。
|
||||
- 稳定概念用术语表表达;不要写没有判定价值的“运行概念”流水。
|
||||
- L0 的 L1 方向树和项目规格索引合并为内部模块分工与规格索引,用相对路径索引到每个内部模块规格文档。
|
||||
- 原子需求每条独立成节,信息表使用横向表格且只放短元数据,推荐列为 `编号 | 短名 | 主责模块 | 关联模块`。主责模块必须写下一层主责单元并带项目编号:L0 原子需求写 L1,L1 原子需求写 L2,L2 原子需求写 L3;禁止在 L1/L2 原子需求里把主责模块写成本规格本级。禁止把需求句、职责划分、验收口径或大段说明塞进表格,正文必须承载需求定义、职责边界、意图和范围。
|
||||
- 不要为了凑全局原子需求条数自动添加次要派生能力。证据收集、硬编码诊断、硬编码建议、任务状态收集、结果包/结果收集等需求,除非用户明确授意,不得写入 OA 规格或 L0 原子需求。
|
||||
- 平台运维在 L0 中只能作为支撑后勤职责出现;对外需求应写成系统可用性、可恢复、资源可控等用户可感知能力,不写成“HWLAB 对外提供平台运维能力”。
|
||||
- 单一主责、关闭验收、规格沉淀、回写和偏离判定是治理规则,不得伪装成 L0 原子需求。
|
||||
- L2 是稳定能力域,不是仓库文件名或原始 spec 文档一比一搬运目录。单个 L1 下的 L2 通常不超过 6 个;从仓库参考文档迁移过来的大量细项应合并到少量 L2,并把更细的服务、lane、source truth、验证切片放到 L3 或正文分工中。
|
||||
- 运维交付内容必须按职责归位:通用 CI/CD、发布判定、镜像 promotion 放到平台运维的发布流水;通用 Git mirror、source truth、GitOps branch、artifact catalog 放到源码同步;Secret/YAML/sourceRef/targetKey/fingerprint 放到 YAML运维。只有能抽象到多服务共用的规则才放通用 L2;AgentRun 固定 branch、namespace、Pipeline、GitOps path、真实 provider turn 等专项细节放到这些通用规格下的 AgentRun 专项 L3,`PJ2026-0102 Agent编排` 只引用这些支撑规格,不把运维后勤 L2 塞进 Agent编排。
|
||||
- 大规划型 issue 范围内新增或修改的源码文件,文件头部必须标注遵循的 SPEC 编号、短名和实现引用版本,例如 `SPEC: PJ2026-01060505 Workbench性能 draft-2026-06-17-p0`;不得只写 issue 编号、`latest` 或 `current`。自动生成文件、第三方 vendored 文件、纯配置、锁文件和无法承载注释头的二进制产物可例外,但对应生成器、渲染器或配置入口必须可追溯到 SPEC。
|
||||
|
||||
## HWLAB 标准 issue 锚点
|
||||
|
||||
- L0 总规格:`project-management/PJ2026-01/specs/PJ2026-01-HWLAB.md`,历史 issue `[#1194](https://github.com/pikasTech/HWLAB/issues/1194)`。
|
||||
- AgentRun 规格入口:`project-management/PJ2026-01/specs/PJ2026-0102-agent-orchestration.md`;核心 L2 为 `PJ2026-010201` AgentRun核心、`PJ2026-010202` Runtime装配、`PJ2026-010203` 队列会话、`PJ2026-010204` 后端Profile、`PJ2026-010205` HWLAB接入。平台运维支撑入口为 `PJ2026-010601` 发布流水、`PJ2026-010602` 源码同步、`PJ2026-010603` YAML运维、`PJ2026-010604` 公开入口和 `PJ2026-010605` 可观测监控;其中 AgentRun 专项 L3 只承载 AgentRun 固定 lane/source 事实,D601 v0.3 用户入口等跨模块 public entry truth 归 `PJ2026-010604`。
|
||||
- 长期导航面板:`[#645](https://github.com/pikasTech/HWLAB/issues/645)`(`HWLAB 长期总面板`)。这是面板,不是规格书。
|
||||
- 当前 Cloud M1 阶段规格:`project-management/PJ2026-01/specs/stage-cloud-spec-20260601.md`,历史 issue `[#644](https://github.com/pikasTech/HWLAB/issues/644)`。
|
||||
- 创建或更新 HWLAB 项目管理 issue 时,非平凡 L1/L2/L3/L4 工作都要链接回 `project-management/PJ2026-01` 中的对应规格文件,并保持 `[#645](https://github.com/pikasTech/HWLAB/issues/645)` 只作为导航索引。
|
||||
- 所有管理 issue 和规格都使用 HWLAB 层级编号:项目编号格式为 `PJ<YYYY>-<path>`;`PJ` 表示项目,`2026` 是立项年份,HWLAB L0 是 `PJ2026-01`;L1 编号是 `PJ2026-0101`、`PJ2026-0102` 等;L2 继续追加两位,例如 `PJ2026-010102` 表示 HWLAB L1 第 1 个方向下的第 2 个 L2 课题。稳定节点短名通常控制在 8 个中文汉字以内,解释写在正文,不写进名称。
|
||||
|
||||
## 工作流
|
||||
|
||||
1. 做规划类问题时,读取 `references/project-planning.md`。
|
||||
2. 创建或更新总规格、issue 正文时,读取 `references/templates.md`。
|
||||
3. 写入 GitHub issue/PR 评论时,同时使用 `unidesk-gh`;所有写入必须从 `/root/unidesk` 通过 `bun scripts/cli.ts gh ...` 完成。
|
||||
4. 修改长期项目管理文档时,把稳定规划内容放到 `project-management/PJ2026-01` 的总规格/规格文件,阶段报告只保留带日期的总结。
|
||||
5. 修订 L1/L2 层级时,先按完成标准判断主责,再看实现表面。工具、CLI、Web、API 形态可以形成关联 L1,但主责 L1 必须是定义完成标准的能力域。
|
||||
6. 更新重大规划型 issue 时,先把 P0 SPEC 文件和图维护到位,再用 `unidesk-gh` 回写 issue;后续代码任务必须在 issue 中引用 SPEC 编号和实现引用版本。
|
||||
|
||||
## 必需层级和职责
|
||||
|
||||
除非用户给出更严格的层级,否则使用以下任务树:
|
||||
|
||||
- L0:重大项目 / 总规格。负责使命、范围、系统边界、内部模块分工与规格索引、全局原子需求。
|
||||
- L1:方向。负责一个稳定能力域、范围边界、成功标准、L2 课题清单和子工作验收定义权。
|
||||
- L2:课题。负责某个 L1 内的具体工作计划,包括交付物、阻塞项和验证计划。
|
||||
- L3:子课题 / 验收切片。负责一个有界验收路径,例如一条 CaseRun 线、Web 检查、CLI smoke 或部署/控制面验证。
|
||||
- L4:执行任务 / PR / CaseRun / smoke / 文档收口。负责具体执行和证据收集,不能重新定义父级范围。
|
||||
|
||||
每个非平凡 issue 都应写明上级链接、目标分支/lane、验收标准、验证入口和收口回写目标。
|
||||
重大规划型 issue 还必须把“更新并确认 SPEC”列为 P0,且 P0 未完成前不得推进代码实现。
|
||||
@@ -20,7 +20,7 @@ bun scripts/cli.ts platform-infra egress-proxy traffic --target D601 --sample-se
|
||||
bun scripts/cli.ts platform-infra egress-proxy k3s-build-benchmark --targets D601,D518 --profile real-deps-500m --dry-run
|
||||
```
|
||||
|
||||
启停、健康检查、节点资源指标、swap、日志、Docker 镜像清理、磁盘 GC、服务重建/重启、PK01 PostgreSQL、YAML-first 运维、Moon Bridge、profile smoke 和 MiniMax recovery 细节见 [references/full.md](references/full.md)。
|
||||
启停、健康检查、节点资源指标、swap、日志、服务重建/重启见 [references/server.md](references/server.md);Docker 镜像清理和磁盘 GC 见 [references/gc.md](references/gc.md);PK01 PostgreSQL、YAML-first 运维、egress proxy 和 k3s benchmark 见 [references/platform-infra.md](references/platform-infra.md)。
|
||||
|
||||
## K3s Dependency Proxy Benchmark
|
||||
|
||||
@@ -84,8 +84,8 @@ D601 若需要让 `sub2api-egress-proxy` 绕开 pod overlay,可在 YAML 中显
|
||||
|
||||
## 何时读取 reference
|
||||
|
||||
- 需要服务 rebuild/restart、日志、swap 或 health 判定:读 [references/full.md](references/full.md) 的对应段。
|
||||
- 需要磁盘/镜像/trace GC:读 Docker 镜像清理和磁盘 GC 段。
|
||||
- 需要 PK01 PostgreSQL 或 YAML-first 运维:读对应段。
|
||||
- 需要服务 rebuild/restart、日志、swap 或 health 判定:读 [references/server.md](references/server.md)。
|
||||
- 需要磁盘/镜像/trace GC:读 [references/gc.md](references/gc.md)。
|
||||
- 需要 PK01 PostgreSQL 或 YAML-first 运维:读 [references/platform-infra.md](references/platform-infra.md)。
|
||||
- 需要 k3s proxy benchmark 或 D601/D518 egress proxy 流量测速时,优先使用本 SKILL 的高频入口;涉及 CI/CD rollout 再加载 `$unidesk-cicd`。
|
||||
- 需要 profile smoke、Moon Bridge 或 session recovery:读对应段。
|
||||
- 需要 profile smoke、Moon Bridge 或 session recovery:读 [references/server.md](references/server.md)。
|
||||
|
||||
@@ -1,276 +0,0 @@
|
||||
---
|
||||
name: unidesk-ops
|
||||
description: UniDesk 手动运维 CLI — `server`、`gc` 和 PK01 `platform-db postgres` 子命令,覆盖主 server 启停、健康检查、swap、日志、Docker 镜像清理、磁盘 GC、服务重建/重启和 PK01 host PostgreSQL 运维。用户提到 server start、server status、server swap、server rebuild、server restart、gc、磁盘清理、platform-db、PK01 PostgreSQL、运维时使用。
|
||||
---
|
||||
|
||||
# UniDesk 手动运维 CLI
|
||||
|
||||
主 server 运维入口,通过 `bun scripts/cli.ts server ...` 和 `bun scripts/cli.ts gc ...` 操作。
|
||||
|
||||
**固定入口前缀**: `cd /root/unidesk && bun scripts/cli.ts ...`
|
||||
|
||||
---
|
||||
|
||||
## 启停
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts server start
|
||||
bun scripts/cli.ts server stop
|
||||
bun scripts/cli.ts server restart <service>
|
||||
```
|
||||
|
||||
异步 job 模式,返回 `job.id`、日志路径。`start` 执行 Docker 构建+启动,`stop` 停止 Compose project 全部服务。
|
||||
`restart` 是无构建单服务维护重启,使用现有镜像执行 `--no-build --no-deps --force-recreate` 并等待容器 `healthy/running`,适合刷新 provider-gateway 这类运行态异常,不能替代镜像发布或源码构建。
|
||||
|
||||
---
|
||||
|
||||
## 健康检查
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts server status
|
||||
```
|
||||
|
||||
返回公开端口、受限宿主端口、内部端口、swap 摘要、Compose 容器状态、各服务健康检查和访问 URL。
|
||||
|
||||
低内存时 `swap.warning` 非空,先执行 `server swap ensure`。
|
||||
|
||||
## 节点资源指标同步
|
||||
|
||||
资源面板显示旧 CPU/内存/磁盘值时,先对照 provider 上报、backend-core 落库和内部 API 三段:
|
||||
|
||||
```bash
|
||||
docker logs --tail 120 unidesk-provider-gateway-main | rg 'system_status_sent|docker_status_sent|error'
|
||||
docker logs --tail 200 unidesk-backend-core | rg 'provider_system_status|provider_docker_status|provider_message_failed|serializing parameter'
|
||||
docker exec unidesk-backend-core sh -lc 'backend-core --fetch-json http://127.0.0.1:8080/api/nodes/system-status?limit=5'
|
||||
```
|
||||
|
||||
provider 已发送但 backend-core 没有 `provider_system_status` / `provider_docker_status`,优先查 backend-core ingest/DB 写入错误;provider 自身停发时才用 `server restart provider-gateway` 做无构建维护重启。修复 backend-core 后必须用 `server rebuild backend-core` 上线并验证 `stale=false`、`currentCollectedAt` 为当前采样。长期语义见 `docs/reference/observability.md#node-resource-status`。
|
||||
|
||||
---
|
||||
|
||||
## Swap 管理
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts server swap status
|
||||
bun scripts/cli.ts server swap ensure [--path /swapfile] [--size 2GiB] [--dry-run]
|
||||
```
|
||||
|
||||
`ensure` 在无 active swap 时创建 swapfile(`chmod 600`、`mkswap`、`swapon`、写 `/etc/fstab`)。已有 swap 时 no-op。fstab 写入失败返回 `degraded`。
|
||||
|
||||
---
|
||||
|
||||
## 日志
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts server logs
|
||||
```
|
||||
|
||||
返回文件日志和 Docker 容器日志尾部,默认限制输出大小。
|
||||
|
||||
---
|
||||
|
||||
## Docker 镜像清理
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts server cleanup plan [--min-age-hours 24] [--limit N]
|
||||
bun scripts/cli.ts server cleanup run --confirm [--min-age-hours 24] [--limit N]
|
||||
```
|
||||
|
||||
`plan` 只生成 dry-run 计划;`run --confirm` 只删除同一 classifier 选出的 stale Docker images。保守白名单:保留 running/stopped 容器镜像、UniDesk YAML/GitOps/image catalog 声明的 commit-pinned artifact、Compose stable image。禁止 `docker system prune`、`docker image prune`、`docker volume rm`、`docker compose down -v` 和数据库清理。高风险候选必须额外显式 `--include-high-risk` 才会执行。
|
||||
|
||||
---
|
||||
|
||||
## 磁盘 GC
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gc plan
|
||||
bun scripts/cli.ts gc run --confirm
|
||||
bun scripts/cli.ts gc db-trace
|
||||
bun scripts/cli.ts gc policy
|
||||
bun scripts/cli.ts gc remote <providerId> [--target-use-percent N] [--dry-run|--confirm]
|
||||
```
|
||||
|
||||
主 server 和 provider 磁盘高水位缓解。`plan` 只读输出候选、风险、估算收益和保护对象。`run` 必须 `--confirm`。`remote` 通过 SSH 透传执行远端 GC。
|
||||
|
||||
常用显式候选和目标口径:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts gc plan --target-use-percent 69 \
|
||||
--include-tool-caches \
|
||||
--include-stale-tmp \
|
||||
--include-vscode-stale-servers \
|
||||
--include-vscode-stale-extensions \
|
||||
--include-vscode-cached-vsix \
|
||||
--include-baidu-staging \
|
||||
--include-vpn-diagnostic-logs
|
||||
```
|
||||
|
||||
`--target-use-percent` 按 `df` 显示口径估算 shortfall。主 server GC 的默认 include、保留窗口、输出 limit、Codex session root、worktree main/root/baseRef、worktree 扫描预算和 `.state` allowlist roots 由 `config/unidesk-cli.yaml#gc` 拥有;CLI 参数只做一次性显式覆盖。工具缓存、`/tmp` 非 allowlist 直接子项、VS Code 历史 server/extension 版本、VS Code CachedExtensionVSIXs 下载缓存、Baidu staging 旧 PGDATA tarball、UniDesk `.state` 历史诊断/部署产物、`.state` stale scratch、Codex inactive sessions、merged worktrees、VPN 诊断 ring pcap 均默认不启用;必须显式 include 后才进入候选,且执行时仍受路径断言保护。stale `/tmp` 扫描按 `--limit` 有界枚举候选,避免为了估算全量临时目录而长时间无输出。`.state` retention 通过 `--include-state-artifacts` 和 `--include-state-stale-scratch` 读取 YAML allowlist;不得把 `.state` 根目录当成通用清理对象。Codex session 清理只删除 YAML root 下超过 keepHours 的普通 session 文件,永远不删除 auth/config。Worktree 清理只扫描 YAML root 下 inactive 且已合入 YAML baseRef 或 cherry-equivalent 的 worktree,run 删除前重新校验 full clean 状态并使用 `git worktree remove`。VS Code cached VSIX 只选择 `/root/.vscode-server/data/CachedExtensionVSIXs` 下超过 `--vscode-cached-vsix-keep-days` 的顶层普通缓存文件,执行前检查 active fd;不删除已安装 extensions、server 或 user data。VPN 诊断日志只选择 `/root/vpn-server/logs/hy2-udp-ring-*.pcap` 和 `hy2-monitor-ring-*.pcap` 中超过 `--vpn-diagnostic-log-keep-hours` 的普通文件,执行前检查 active fd;不删除 evidence JSONL。默认 GC 不触碰 `.state/recovery`、`.state/codex-queue/codex-home`、`.state/deploy/work`、`.state/baidu-netdisk`、PGDATA、Docker volumes/images、Codex auth/config state、active/unmerged/dirty worktree、runtime image/snapshot state、Baidu staging 根目录、VPN 日志根目录或 VS Code user data。
|
||||
|
||||
`gc policy install` 的每日 timer 从 `config/unidesk-cli.yaml#gc.policyTimer` 渲染 VPN 诊断 pcap retention、UniDesk `.state` artifact retention 和 VS Code CachedExtensionVSIXs retention,用于限制长期诊断/部署产物、tcpdump ring 文件与 VS Code 下载缓存增长;手动 `gc plan/run` 仍必须显式 `--include-vpn-diagnostic-logs` / `--include-state-artifacts` / `--include-vscode-cached-vsix` 才会列出或删除这些对象。
|
||||
|
||||
---
|
||||
|
||||
## 服务重建
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts server rebuild <service>
|
||||
```
|
||||
|
||||
service 可选:`backend-core` | `frontend` | `dev-frontend-proxy` | `provider-gateway` | `todo-note` | `code-queue-mgr` | `project-manager` | `baidu-netdisk` | `oa-event-flow`
|
||||
|
||||
异步 job:构建镜像 → `.state/locks/server-compose.lock` 串行保护 → `--no-deps --force-recreate` 替换容器 → 等待 `healthy/running`。
|
||||
|
||||
启动后必须轮询 job,不要把提交 job 当成已经完成:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts server rebuild backend-core
|
||||
bun scripts/cli.ts job status <jobId> --tail-bytes 12000
|
||||
```
|
||||
|
||||
backend-core 重建完成后再做运行面验证:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts server status
|
||||
docker exec unidesk-backend-core sh -lc 'backend-core --fetch-json http://127.0.0.1:8080/health --require-ok'
|
||||
```
|
||||
|
||||
**禁止事项**:
|
||||
- backend-core 常规迭代不得在 master server 编译;只有已提交修复需要上线主 server Compose runtime 时,才用 `server rebuild backend-core` 受控异步 job
|
||||
- D601 Code Queue 执行面不由 `server rebuild` 管理
|
||||
- 不重建/删除 database 命名卷
|
||||
|
||||
---
|
||||
|
||||
## PK01 Host PostgreSQL
|
||||
|
||||
PK01 host-native PostgreSQL 是平台外置状态库样板,声明文件是 `config/platform-db/postgres-pk01.yaml`,受控入口是:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-db postgres plan --config config/platform-db/postgres-pk01.yaml
|
||||
bun scripts/cli.ts platform-db postgres status --config config/platform-db/postgres-pk01.yaml
|
||||
bun scripts/cli.ts platform-db postgres export-secrets --config config/platform-db/postgres-pk01.yaml --confirm
|
||||
bun scripts/cli.ts platform-db postgres apply --config config/platform-db/postgres-pk01.yaml --confirm
|
||||
bun scripts/cli.ts platform-db postgres apply --config config/platform-db/postgres-pk01.yaml --confirm --wait
|
||||
```
|
||||
|
||||
- `plan` / `status` 只读;`apply --confirm` 默认创建本地异步 job;`apply --confirm --wait` 会启动 PK01 侧 root-owned job 并短轮询。
|
||||
- `export-secrets --confirm` 只按 YAML 重新物化本地 Secret source/export 文件,不触碰 PK01 远端 PostgreSQL;连接串格式、consumer target 或 Secret export 变更优先用它,再走对应消费者的 Secret sync。
|
||||
- 输出只显示 Secret key 名、presence、fingerprint、连接 host、SSL 状态和状态摘要;禁止打印密码或完整 `DATABASE_URL`。
|
||||
- 同一个 PK01 PostgreSQL 实例可承载多个 YAML 声明的 role/database;新增消费者按 `secrets.entries`、`objects.roles`、`objects.databases`、`postgres.auth.pgHba` 和 `exports.connectionStrings` 成套声明,不新开 PostgreSQL 实例,也不默认用 schema 隔离应用状态。
|
||||
- 跨节点消费者必须直连 YAML 的 `postgres.network.connectionHost`,当前是 PK01 公网 endpoint;不要让 D601/G14/Sub2API/HWLAB/AgentRun 通过 master server 中转 PostgreSQL。
|
||||
- 当前 TLS 口径是 PostgreSQL native TLS + `sslmode=require`。`publicDns` 只是可选 alias;只要 `connectionHost` 是可达 IP,DNS 未解析不作为切库 blocker。
|
||||
- 远端 PostgreSQL 配置或 `pg_hba` 来源 CIDR 变化后,先跑 `apply --confirm --wait`,再跑 `status`;若消费者公网出口 IP 变化,必须先更新 YAML `allowSources` 和对应 `pg_hba`。
|
||||
- `status` 验收要看 `roles[]`、`databases[]` 和 `appConnections[]`;不要只看旧的 `roleExists` / `databaseExists` 标量。
|
||||
|
||||
日常复验建议:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-db postgres status --config config/platform-db/postgres-pk01.yaml
|
||||
trans PK01 sh <<'SH'
|
||||
systemctl is-active postgresql
|
||||
systemctl is-enabled postgresql
|
||||
systemctl is-active unidesk-pk01-sub2api-pgdump.timer
|
||||
SH
|
||||
```
|
||||
|
||||
长期边界见 `docs/reference/pk01.md`;Sub2API 消费侧边界见 `docs/reference/platform-infra.md`。
|
||||
|
||||
## YAML-First 分布式运维边界
|
||||
|
||||
UniDesk 自有分布式运维以 `config/**/*.yaml` 为 desired-state truth。服务仓库里的 `deploy.json` 不能作为 UniDesk deployment truth;node/lane、runtime namespace、GitOps branch/path、image artifact、public exposure、Secret、外置数据库、probe 和 rollout 等运维选择必须进入所属 UniDesk YAML,并通过受控 CLI 渲染或同步。
|
||||
|
||||
AgentRun v0.2/D601 这类 YAML-only lane 的控制面、Secret 同步、外置 DB wiring 和状态检查使用:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts agentrun control-plane plan --node D601 --lane v02
|
||||
bun scripts/cli.ts agentrun control-plane apply --node D601 --lane v02 --confirm
|
||||
bun scripts/cli.ts agentrun control-plane secret-sync --node D601 --lane v02 --confirm
|
||||
bun scripts/cli.ts agentrun control-plane status --node D601 --lane v02 --full
|
||||
```
|
||||
|
||||
部署触发和 GitOps promotion 入口归 `$unidesk-cicd`;本 skill 只记录手动运维边界和长期排障入口。长期架构见 `docs/reference/yaml-first-ops.md`,AgentRun 细则见 `docs/reference/agentrun.md`。
|
||||
|
||||
---
|
||||
|
||||
## Moon Bridge 管理
|
||||
|
||||
Moon Bridge 是 Codex ↔ 上游 provider 的桥接服务,通过 profile 级 wrapper 管理:
|
||||
|
||||
```bash
|
||||
# DeepSeek profile
|
||||
dscx bridge-start
|
||||
dscx bridge-status
|
||||
dscx bridge-smoke dscx-bridge-ok
|
||||
dscx bridge-stop
|
||||
|
||||
# MiniMax profile
|
||||
mxcx bridge-start
|
||||
mxcx bridge-status
|
||||
mxcx bridge-smoke mxcx-bridge-ok
|
||||
mxcx bridge-stop
|
||||
```
|
||||
|
||||
- `dscx` → `127.0.0.1:38440`(Codex custom provider `deepseek`,DeepSeek V4 Pro)
|
||||
- `mxcx` → `127.0.0.1:38441`(Codex custom provider `minimax`,MiniMax-M3)
|
||||
- 启动用 `setsid` + profile-local PID file,进程不随 CLI 退出
|
||||
- 日志在 `<CODEX_HOME>/logs/moonbridge/`
|
||||
|
||||
---
|
||||
|
||||
## Codex Profile Smoke
|
||||
|
||||
```bash
|
||||
# DeepSeek
|
||||
dscx doctor
|
||||
dscx bridge-smoke dscx-bridge-ok
|
||||
dscx exec --skip-git-repo-check 'Reply exactly: dscx-codex-ok'
|
||||
|
||||
# MiniMax
|
||||
mxcx doctor
|
||||
mxcx bridge-smoke mxcx-bridge-ok
|
||||
mxcx exec --skip-git-repo-check 'Reply exactly: mxcx-codex-ok'
|
||||
|
||||
# ACX GPT direct profiles
|
||||
acx status
|
||||
acx gpt-only exec --json 'Reply exactly: acx-only-ok'
|
||||
acx gpt-sub2api exec --json 'Reply exactly: acx-sub2api-ok'
|
||||
acx exec --json 'Reply exactly: acx-default-ok'
|
||||
```
|
||||
|
||||
`bridge-smoke` 验证 Moon Bridge → provider 链路。`exec` 验证完整 Codex CLI → bridge → provider 全链路。
|
||||
|
||||
`acx` 的 GPT aliases 是 Codex custom provider 直连 Responses 上游,不经过本地 `127.0.0.1:38448` router。GPT 模式下 `acx status` 应输出 `mode=gpt-direct`、`routerRequired=false`、`portPids=[]`;小真实调用应返回期望文本,重复或 resume 流量应能看到非零 `cached_input_tokens`。OpenCode Zen Go aliases 仍通过 `acx route-start|route-status` 走 router → `gocx`/Moon Bridge 路径。长期边界见 `docs/reference/master-server-ops.md`。
|
||||
|
||||
---
|
||||
|
||||
## MiniMax Session Recovery
|
||||
|
||||
MiniMax 会话因无效 tool-call arguments 导致 `resume` 反复失败时的恢复流程:
|
||||
|
||||
```bash
|
||||
# 1. 清理无效 tool arguments
|
||||
mxcx session-clean <session-id-or-jsonl>
|
||||
|
||||
# 2. 确认幂等(应返回 changed=false)
|
||||
mxcx session-clean <session-id-or-jsonl>
|
||||
|
||||
# 3. 注入 guard 防止复发
|
||||
mxcx session-guard <session-id-or-jsonl>
|
||||
|
||||
# 4. 非交互 smoke 验证恢复
|
||||
mxcx exec resume <session-id> 'Reply exactly: recovered-ok'
|
||||
|
||||
# 5. apply-patch smoke(如涉及远端编辑)
|
||||
# 验证使用 trans <route> apply-patch,非 download/upload/sed
|
||||
```
|
||||
|
||||
`mxcx resume <session-id>` 自动执行 `session-clean` + `session-guard` 后再调用 Codex。修复最小化:只修无效 `function_call.arguments`,不压缩/截断/重排 transcript。
|
||||
|
||||
---
|
||||
|
||||
## 参考文档
|
||||
|
||||
- **主 server 架构与行为规范**: `docs/reference/master-server-ops.md`(Execution Boundary、Codex Provider Profile 架构、Moon Bridge 内部规则、MiniMax session-clean 行为约束、apply-patch 策略)
|
||||
- **磁盘 GC 长期规则**: `docs/reference/gc.md`
|
||||
- **部署边界**: `docs/reference/deployment.md`
|
||||
@@ -0,0 +1,5 @@
|
||||
# GC Operations
|
||||
|
||||
GC defaults to plan/read-only. Real deletion needs explicit `--confirm` and the configured allowlist/retention policy.
|
||||
|
||||
Do not run broad prune/reset commands. Keep evidence of reclaimed space and protected paths.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Platform Infra Operations
|
||||
|
||||
PK01 PostgreSQL, platform-infra egress proxy and k3s dependency benchmark work must use controlled UniDesk CLI commands.
|
||||
|
||||
For k3s dependency proxy benchmarks, use real remote dependencies and short status/log/traffic polling. Clean up benchmark Jobs when complete or clearly underperforming.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Server Operations
|
||||
|
||||
Server operations use `bun scripts/cli.ts server ...`.
|
||||
|
||||
Check status and logs before rebuild/restart. Do not touch backend-core after it is healthy unless the user explicitly asks. Master server is not a general build machine.
|
||||
@@ -15,7 +15,7 @@ bun scripts/cli.ts platform-infra observability trace <traceId>
|
||||
bun scripts/cli.ts platform-infra observability search --service <service> --limit 20
|
||||
```
|
||||
|
||||
基本状态、trace 查询、噪声压制、业务 trace 映射、Code Agent/AgentRun 排障、codex-stdio 追穿、读取窗口与乱序调查、何时先改进 OTel 的细节见 [references/full.md](references/full.md)。
|
||||
基本状态、trace 查询、噪声压制、读取窗口与乱序调查见 [references/traces.md](references/traces.md);业务 trace 映射、Code Agent/AgentRun 排障、codex-stdio 追穿和何时先改进 OTel 见 [references/code-agent.md](references/code-agent.md)。
|
||||
|
||||
## P0 边界
|
||||
|
||||
@@ -29,6 +29,6 @@ bun scripts/cli.ts platform-infra observability search --service <service> --lim
|
||||
|
||||
## 何时读取 reference
|
||||
|
||||
- 查具体 traceId、span、服务过滤或噪声压制时,读 [references/full.md](references/full.md) 的查询和噪声段。
|
||||
- 排障 Code Agent/AgentRun/HWLAB 链路时,读业务映射和 Code Agent/AgentRun 段。
|
||||
- 判断是否需要先改进 OTel 或交付边界时,读对应段。
|
||||
- 查具体 traceId、span、服务过滤或噪声压制时,读 [references/traces.md](references/traces.md)。
|
||||
- 排障 Code Agent/AgentRun/HWLAB 链路时,读 [references/code-agent.md](references/code-agent.md)。
|
||||
- 判断是否需要先改进 OTel 或交付边界时,读 [references/code-agent.md](references/code-agent.md)。
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# OTel Code Agent Mapping
|
||||
|
||||
Code Agent, AgentRun and HWLAB traces need cross-service mapping.
|
||||
|
||||
If a business trace has only one service span, treat missing downstream spans as an observability boundary until runId, commandId or sessionId drill-down proves otherwise. Improve instrumentation or CLI summaries before claiming business success from absent spans.
|
||||
@@ -1,221 +0,0 @@
|
||||
---
|
||||
name: unidesk-otel
|
||||
description: UniDesk OpenTelemetry/Tempo 链路追踪运维技能。用户提到 OTel、OpenTelemetry、Tempo、trace backend、platform-infra observability、链路追踪、按 traceId 查 span、provider-stream-disconnected、Code Agent/AgentRun/HWLAB 跨服务追踪、或要求“用 otel 查/改进 otel”时使用。
|
||||
---
|
||||
|
||||
# UniDesk OTel
|
||||
|
||||
Skill(cli-spec)
|
||||
|
||||
UniDesk 的 OTel 运行面在 `platform-infra` namespace:OTel Collector 负责接收 OTLP traces,Tempo 负责查询。操作入口统一走 UniDesk YAML-first CLI,不直接 `kubectl port-forward`、手写 Tempo API 或裸 `curl`。
|
||||
|
||||
**固定入口**: `cd /root/unidesk && bun scripts/cli.ts platform-infra observability ...`
|
||||
|
||||
## 基本状态
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra observability status --target D601
|
||||
bun scripts/cli.ts platform-infra observability validate --target D601
|
||||
```
|
||||
|
||||
- `status` 检查 `platform-infra` namespace、`otel-collector`、`tempo` Deployment/Service/Pod 和 readiness probe。
|
||||
- `validate` 生成一条测试 trace,经 Collector 写入 Tempo,再通过受控 service proxy 查询,证明采集和查询闭环可用。
|
||||
- `--full` 只在需要展开远端 stdout/stderr 或完整 status payload 时使用;默认输出必须保持低噪声。
|
||||
|
||||
## 查询 Trace
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra observability trace \
|
||||
--target D601 \
|
||||
--trace-id <otelTraceId>
|
||||
```
|
||||
|
||||
`--trace-id` 是 32 位 hex OpenTelemetry trace id,不是业务 traceId。默认输出只返回有界摘要:
|
||||
|
||||
- `spanCount`、`serviceCount`、`services`
|
||||
- `businessTraceIds`
|
||||
- `errorSpanCount`
|
||||
- `spanNameCounts`
|
||||
- `errorSpans`
|
||||
- 去重后的关键 spans
|
||||
- 下一步 drill-down 命令
|
||||
|
||||
默认输出不得展开完整 Tempo JSON;需要原始响应时才用 `--raw`。
|
||||
|
||||
`--target` 必须和产生 trace 的运行面一致。按 HWLAB node/lane 复测得到的 `x-hwlab-otel-trace-id` 应优先使用同一 node 查询,例如 JD01/v0.3 的 Workbench launch 或 `/v1/agent/chat` 响应头用 `--target JD01`。如果拿其他 target 查询返回 `traceFound=-`、`parseOk=false` 或平台状态超时,只能说明 target/运行面不匹配或该 target observability 不可用,不能据此判断原 trace 不存在。
|
||||
|
||||
当只有错误文案而没有 OTel trace id 时,先用 `search` 从 Tempo 最近 trace 中反查候选,再进入 `trace`:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra observability search \
|
||||
--target D601 \
|
||||
--grep 'no rollout found' \
|
||||
--lookback-minutes 360 \
|
||||
--candidate-limit 80 \
|
||||
--limit 20
|
||||
```
|
||||
|
||||
`search` 会通过受控 service proxy 调 Tempo `/api/search` 取候选 trace,并逐条拉 trace 做本地 grep 摘要;默认只输出匹配 trace、服务、业务 traceId、错误 span 和下一步命令。扩大时间窗或候选数必须显式传 `--lookback-minutes` / `--candidate-limit`,避免大 trace 输出淹没上下文。
|
||||
|
||||
## 噪声压制
|
||||
|
||||
按错误文案、span 名、failureKind 或关键属性定位时,优先用 `--grep`:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra observability trace \
|
||||
--target D601 \
|
||||
--trace-id <otelTraceId> \
|
||||
--grep provider-stream-disconnected \
|
||||
--limit 20
|
||||
```
|
||||
|
||||
- `--grep <text>` 在 span 名、status message 和关键 attributes 的摘要 JSON 中过滤。
|
||||
- `--limit <N>` 控制返回 span 数,避免大 trace 淹没上下文。
|
||||
- `--full` 展开完整 span 摘要,但仍不输出 Tempo raw body。
|
||||
- `--raw` 仅用于排查 Tempo 响应结构、CLI 解析器或后端返回本身。
|
||||
|
||||
## 业务 Trace 映射
|
||||
|
||||
HWLAB/Code Agent 的业务 traceId 通常形如 `trc_...`。当已知 OTel trace id 时,直接用 `trace --trace-id` 查询;当只知道业务 traceId 时,优先从 HWLAB trace/result、Code Agent result 或已记录的 issue 证据中取得对应 OTel trace id。不要为了找 OTel trace id 去打印 Secret、Authorization header、完整 DSN 或运行面 raw transcript。
|
||||
|
||||
OTel trace 内常见业务关联属性:
|
||||
|
||||
- `traceId`: HWLAB 业务 traceId
|
||||
- `otel.trace_id`: OTel trace id
|
||||
- `runId` / `commandId`: AgentRun run/command
|
||||
- `sessionId` / `turnId` / `threadId`: HWLAB/AgentRun 会话与 turn 关联
|
||||
- `failureKind` / `willRetry` / `terminalStatus`: 错误与终态判断
|
||||
|
||||
## HWLAB OpenCode /global/event 排障
|
||||
|
||||
OpenCode 对话长时间显示 `Thinking`、用户发消息后无 assistant 文本、或怀疑 iframe/live state 没吃到事件时,先分别证明 provider、Cloud Web 代理和浏览器事件流三层状态。provider span 可能已经 200,但 UI 仍可能因 `/global/event` 目录、ticket 或 live state 不一致而不收敛。
|
||||
|
||||
优先用增强后的 `--grep` 查询 provider/service 线索;当前 grep 会先推断合适的 TraceQL 候选查询,再在扫描到的 trace 内匹配 raw body、span name、route、status message 和 full span attributes:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra observability search \
|
||||
--target <node> \
|
||||
--grep opencode-provider-proxy \
|
||||
--lookback-minutes 30 \
|
||||
--candidate-limit 100 \
|
||||
--limit 20
|
||||
```
|
||||
|
||||
provider 侧至少看 `/v1/chat/completions` 的 HTTP status、duration、`opencode.provider.sse.content_chunks`、`opencode.provider.sse.content_chars`、`opencode.provider.sse.reasoning_only_choices_dropped`、output data lines、done lines 和 JSON error 数。只有这些正常,才能把焦点转到 Cloud Web 代理或 UI。
|
||||
|
||||
Cloud Web 侧查 `/global/event` 长连接的 start span;长连接可能在调查窗口内不关闭,所以必须依赖 stream start 可见性,而不是只等 completion span:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra observability search \
|
||||
--target <node> \
|
||||
--grep opencode.proxy.stream.start \
|
||||
--lookback-minutes 30 \
|
||||
--candidate-limit 300 \
|
||||
--limit 20
|
||||
```
|
||||
|
||||
属性 drill-down 可直接查 key/value;例如:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra observability search \
|
||||
--target <node> \
|
||||
--grep opencode.proxy.sse.directory_rewrite_enabled=true \
|
||||
--lookback-minutes 30 \
|
||||
--candidate-limit 300 \
|
||||
--limit 20
|
||||
```
|
||||
|
||||
期望看到 `opencode.proxy.stream.start`,并检查 `opencode.proxy.sse.directory_rewrite_enabled`、`opencode.proxy.sse.directory_rewrite_from`、`opencode.proxy.sse.directory_rewrite_to`、`opencode.proxy.ticket_accepted`、`span.http.route=/global/event` 和 streaming 标记。`from=/workspace`、`to=/` 代表 HWLAB iframe/public route 与 OpenCode server workspace route 已对齐;如果该 rewrite 不存在或 ticket 未接受,provider 正常也不能说明 UI 正常。
|
||||
|
||||
关闭 OpenCode UI 卡住类 issue 时,OTel 只能证明链路状态;最终还要用 `web-probe opencode-smoke` 或等价 DOM/事件证据确认浏览器看到 assistant 文本、没有残留 `Thinking`,并且 EventSource 收到 `message.part.updated`、`step-finish`、`session.idle` 等事件。若 `--grep` 没搜到新属性或 span 名,先阅读输出中的 `grepCoverage`、`grepQueryInference` 和 no-match Next;必要时再改用显式 TraceQL,例如 `{ resource.service.name = "hwlab-cloud-web" && .http.route = "/global/event" }`,并只对小范围 trace 使用 `trace --full|--raw` 做 bounded attr drill-down。
|
||||
|
||||
## Code Agent / AgentRun 排障
|
||||
|
||||
追 `Code Agent 代理暂时无法连接上游`、`provider-stream-disconnected`、Workbench 加载/转圈、turn idle 报错、AgentRun command terminal 状态时:
|
||||
|
||||
优先用一条诊断命令汇总业务 trace、OTel trace、服务追穿、AgentRun 终态、HWLAB 读模型和 HTTP 403/401/5xx 根因:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra observability diagnose-code-agent \
|
||||
--target D601 \
|
||||
--business-trace-id <trc_...>
|
||||
```
|
||||
|
||||
默认输出必须保持有界低噪声,重点看:
|
||||
|
||||
- `mapping.businessTraceId` / `mapping.otelTraceId`
|
||||
- `servicePath` 是否同时到达 `hwlab-cloud-api`、`agentrun-manager`、`agentrun-runner`
|
||||
- `identity` 里的 `runId`、`commandId`、`sessionId`、`runnerJobId`、`runnerId`、`backendProfile`、`sourceCommit`
|
||||
- `agentrun.terminalStatus`、`terminalEventType`、`runnerProviderClassification`
|
||||
- `hwlabReadModel.sourceEventCount`、`requestedSinceSeq`、`turnStatusCounts`
|
||||
- `http.problemCounts` 和 `projectionLag.status`
|
||||
- `summary.rootCause` 与按置信度排序的 `rootCauseCandidates`
|
||||
|
||||
只有需要展开 span 明细时使用 `--full`;只有排查 Tempo raw 响应或 CLI 解析器时使用 `--raw`。默认输出不得包含 Secret、Authorization header、DSN、可复制凭据或完整运行 transcript。
|
||||
|
||||
若没有业务 traceId 或诊断结果还需要 drill-down,再使用低层 trace/search:
|
||||
|
||||
1. 先确认 OTel backend ready:
|
||||
`bun scripts/cli.ts platform-infra observability status --target D601`
|
||||
2. 查业务 trace 对应 OTel trace:
|
||||
`bun scripts/cli.ts platform-infra observability trace --target D601 --trace-id <otelTraceId>`
|
||||
3. 用错误关键词过滤:
|
||||
`bun scripts/cli.ts platform-infra observability trace --target D601 --trace-id <otelTraceId> --grep <failureKind-or-message> --limit 20`
|
||||
4. 对照 `errorSpanCount`、`matchedSpanCount`、`terminalStatus`、`willRetry`、`runId`、`commandId` 判断是 terminal failure、retryable transient 还是旧 trace 缺 instrumentation。
|
||||
|
||||
旧 trace 不会因为后续 instrumentation 修复自动回填。若旧 trace 查不到错误 span,但新的 canary/真实 trace 能查到同类 `runner_error.*` span,应把旧 trace 结论写成“当时未采集到该事件”,不要倒推出运行面没有发生过错误。
|
||||
|
||||
### AgentRun codex-stdio 追穿检查
|
||||
|
||||
追 HWLAB Workbench turn idle、`waitingFor=code-agent`、工具调用后无 terminal、`provider-stream-disconnected`,或用户怀疑 AgentRun/codex-stdio 仍在运行但 OTel 没追到时,必须在同一 OTel trace 里同时看到 `hwlab-cloud-api`、`agentrun-manager` 和 `agentrun-runner`。只看到 manager dispatch 或 HWLAB business trace 不算追穿 runner。
|
||||
|
||||
优先用业务入口拿到 OTel trace id,再按 codex span 过滤:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra observability trace \
|
||||
--target D601 \
|
||||
--trace-id <otelTraceId> \
|
||||
--grep codex_stdio \
|
||||
--limit 120 \
|
||||
--full
|
||||
```
|
||||
|
||||
通过证据应能看到 `codex_app_server.starting`、`codex_app_server.started`、需要进程退出时的 `codex_app_server.exit`、`codex_stdio.thread_start.*` 或 `thread_resume.*`、`codex_stdio.turn_start.*`、`codex_stdio.tool_call.started|completed|failed`、`codex_stdio.turn_completed`,以及问题相关的 `idle_warning`、`idle_timeout`、`provider_stream_disconnected` 或 `missing_terminal_after_tool`。这些 span 应带 `runId`、`commandId`、`runnerJobId`、`runnerId`、`sessionId`、`backendProfile`、`sourceCommit`、`traceId`、`otel.trace_id` 和 `valuesPrinted=false`。若默认摘要或 grep 看不到 `runnerJobId`,先用当前 UniDesk CLI 执行 `platform-infra observability trace --grep runnerJobId --full` 复查摘要器输出,必要时用 `--raw` 只排查 Tempo/CLI 解析结构;只有 raw 或更新后的摘要仍缺 `runnerJobId`,或同一 trace 没有 `agentrun-runner` service,才回 AgentRun runner-side instrumentation 排查。
|
||||
|
||||
如果需要确认工具调用 started/completed 的归一化,canary 应要求一次只读 shell 工具调用。Codex notification 的 `item/started` + `status=inProgress` 应落为 `codex_stdio.tool_call.started`,不得和 completed 混在一起。长期规则见 `docs/reference/agentrun.md#agentrun--hwlab-otel-追踪口径`。
|
||||
|
||||
## Trace 读取窗口与乱序调查
|
||||
|
||||
排查 HWLAB/Workbench trace 乱序、分页缺口、`--after-seq`/`--tail` 不生效、旧 trace 只返回局部事件或 read model 是否完整时,优先查 `trace_events_read` span:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra observability trace \
|
||||
--target D601 \
|
||||
--trace-id <otelTraceId> \
|
||||
--grep trace_events_read \
|
||||
--limit 20 \
|
||||
--full
|
||||
```
|
||||
|
||||
摘要里重点看 `returnedEvents`、`sinceSeq`、`limit`、`fromSeq`、`toSeq`、`totalEvents`、`hasMore`、`fullTraceLoaded`、`rawEventCount`、`maxSeq`、`traceLastSeq`、`endSeq` 和 `commandFiltered`。这些字段用于判断“查询窗口是否正确传到后端”“后端是否只返回了局部事件”“read model 是否已经加载完整 trace”。若 `errorSpanCount=0` 但用户可见 timeline 仍乱序,先把结论写成展示/投影/renderer 调查 issue,不要把它定性为后端错误 span。
|
||||
|
||||
旧业务 trace 在 runtime 重启或保留策略后可能只剩局部事件;OTel 只能证明读取窗口、span 和当时观测到的字段,不能自动恢复业务事件流。需要验证新 instrumentation 时,使用新 canary 或仍可完整读取的真实 trace。
|
||||
|
||||
## 何时先改进 OTel
|
||||
|
||||
遇到以下情况,先修 OTel CLI 或 instrumentation,再继续业务排障:
|
||||
|
||||
- trace 命令只能返回 raw/tail,不能给出可读 span 摘要。
|
||||
- 大 trace 输出淹没上下文,缺少 `--grep`、`--limit`、错误 span 汇总或下一步 drill-down。
|
||||
- 关键 runner/backend/projection 事件只存在业务事件流,不进入 OTel。
|
||||
- error span 缺 `failureKind`、`willRetry`、`terminalStatus`、`runId`、`commandId` 等定位字段。
|
||||
- CLI 默认输出 Secret、Authorization header、DSN 或其他敏感值。
|
||||
|
||||
改进后必须用一条 canary 或真实 trace 证明新 span/摘要可查询,再继续定位原业务问题。
|
||||
|
||||
## 交付边界
|
||||
|
||||
- OTel 平台配置真相是 `config/platform-infra/observability.yaml`。
|
||||
- OTel CLI 实现在 `scripts/src/platform-infra-observability.ts`,帮助入口由 `scripts/src/platform-infra.ts` 暴露。
|
||||
- 修改 OTel CLI 属于 UniDesk 轻量 CLI 变更:默认只做语法检查、命令形态验证和真实 trace 查询,不新增合同测试。
|
||||
- 修改 AgentRun/HWLAB instrumentation 属于对应仓库/运行面的代码变更,必须按目标 repo 的 source truth、PR/CD 和原入口验收规则执行。
|
||||
@@ -0,0 +1,5 @@
|
||||
# OTel Traces
|
||||
|
||||
Use `platform-infra observability trace|search|status` for Tempo/OTel work.
|
||||
|
||||
Default output should stay bounded. Use `--full` or `--raw` only when span context, attributes or complete payload are required. Match `--target` to the node/lane that produced the trace.
|
||||
@@ -21,7 +21,7 @@ trans D601:win/c/test git commit -m 'fix: update docs'
|
||||
trans gh:/owner/repo/issue/<number> cat
|
||||
```
|
||||
|
||||
Host workspace、k3s、Windows、GitHub issue/PR route,sh/bash/argv/apply-patch/py/upload/download/kubectl/logs/skills/tcp-pool 操作,以及 apply-patch envelope 语法和 quoting 陷阱见 [references/full.md](references/full.md)。
|
||||
Host workspace、k3s、Windows、GitHub issue/PR route 见 [references/routes.md](references/routes.md);sh/bash/argv/py/upload/download/kubectl/logs/skills/tcp-pool 操作见 [references/operations.md](references/operations.md);apply-patch envelope 和 quoting 陷阱见 [references/apply-patch.md](references/apply-patch.md)。
|
||||
|
||||
## P0 边界
|
||||
|
||||
@@ -35,6 +35,6 @@ Host workspace、k3s、Windows、GitHub issue/PR route,sh/bash/argv/apply-patc
|
||||
|
||||
## 何时读取 reference
|
||||
|
||||
- 不确定 route 语法、k3s/workspace/Windows 定位时,读 [references/full.md](references/full.md) 的 Route 语法段。
|
||||
- 编写远端 patch 前,读 apply-patch 语法、上下文定位和常见失败段。
|
||||
- 需要 shell heredoc、Python、upload/download 或超时处理时,读 Operation 和 60s 段。
|
||||
- 不确定 route 语法、k3s/workspace/Windows 定位时,读 [references/routes.md](references/routes.md)。
|
||||
- 编写远端 patch 前,读 [references/apply-patch.md](references/apply-patch.md)。
|
||||
- 需要 shell heredoc、Python、upload/download 或超时处理时,读 [references/operations.md](references/operations.md)。
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Remote Apply Patch
|
||||
|
||||
Remote text edits should use `trans <route> apply-patch` with a normal apply-patch envelope.
|
||||
|
||||
- Keep hunks small and scoped.
|
||||
- Read enough context first with `rg`, `sed -n` or file-specific commands.
|
||||
- Avoid download/edit/upload or ad hoc `sed -i` as the final patch path.
|
||||
- For Windows routes, prefer the filesystem adapter path rather than shell-encoded write blocks.
|
||||
@@ -1,364 +0,0 @@
|
||||
---
|
||||
name: unidesk-trans
|
||||
description: UniDesk SSH 透传与 apply-patch 语法 — `trans <route> <operation>` 分布式执行入口,包含 route 语法、workspace/k3s/Windows 路由、apply-patch envelope 格式、sh/bash/py/upload/download operation 和 60s 短连接约束。用户提到 trans、tran、ssh 透传、远端执行、apply-patch、apply_patch、远端 patch、k3s route、workspace route 时使用。
|
||||
---
|
||||
|
||||
# UniDesk Trans(SSH 透传 + apply-patch)
|
||||
|
||||
UniDesk 分布式 SSH 透传入口,统一通过 `trans <route> <operation>` 在远端 host/k3s/Windows 上执行命令或文本 patch。
|
||||
|
||||
**固定入口**: `trans <route> ...`(wrapper 位于 `/root/.local/bin/trans`,委托 repo 内 ssh-only 启动入口 `bun scripts/ssh-cli.ts ssh "$@"`,避免被无关 CLI 子命令模块解析失败拖垮)
|
||||
|
||||
---
|
||||
|
||||
## Route 语法
|
||||
|
||||
```
|
||||
trans <providerId>[/absolute/workspace] <operation> [args...]
|
||||
trans <providerId>:<plane>[:<namespace>:<resource>[:<container>]] <operation> [args...]
|
||||
```
|
||||
|
||||
### Host workspace route
|
||||
|
||||
```bash
|
||||
trans G14:/root/hwlab git status --short --branch
|
||||
trans G14:/root/hwlab-v02 sh -- 'git fetch origin v0.2 && git pull --ff-only origin v0.2'
|
||||
trans D601:/home/ubuntu/workspace/unidesk-dev sh <<'SH'
|
||||
...
|
||||
SH
|
||||
```
|
||||
|
||||
**规则**: workspace 路径写在 route 第一个 token,不写进 `cd` 串。显式 host workspace 进入失败必须返回 `UNIDESK_SSH_CWD_FAILED` 和非零退出,不得静默落回默认目录;provider-gateway 侧完整语义见 `docs/reference/provider-gateway.md`。反面示例:~~`trans G14 sh -- 'cd /root/hwlab && git status'`~~
|
||||
|
||||
### k3s route
|
||||
|
||||
```bash
|
||||
# 控制面
|
||||
trans G14:k3s kubectl get pods -n hwlab-dev
|
||||
trans D601:k3s kubectl get pods -A
|
||||
|
||||
# 指定 namespace + workload
|
||||
trans G14:k3s:hwlab-dev:hwlab-cloud-web:app exec --cwd /app -- cat /app/version.txt
|
||||
trans G14:k3s:hwlab-dev:pod:hwlab-cloud-web-abc:app apply-patch --cwd /workspace <<'PATCH'
|
||||
...
|
||||
PATCH
|
||||
```
|
||||
|
||||
CLI 自动注入 `KUBECONFIG=/etc/rancher/k3s/k3s.yaml`。k3s route 中 `:` 是分布式路由分隔符,`/` 只表示容器内文件系统 cwd;容器选择必须写 `:<container>` 或 operation 参数 `--container <container>`,不要写成 `pod/<pod>/<container>`。
|
||||
|
||||
### Windows route
|
||||
|
||||
```bash
|
||||
trans D601:win ps <<'PS'
|
||||
Get-ChildItem C:\test
|
||||
PS
|
||||
|
||||
trans D601:win/c/test cmd cd
|
||||
trans D601:win/c/test pwd
|
||||
trans D601:win/c/test ls --limit 50
|
||||
trans D601:win/c/test cat README.md
|
||||
trans D601:win/c/test head -n 40 README.md
|
||||
trans D601:win/c/test tail -n 40 README.md
|
||||
trans D601:win/c/test stat README.md
|
||||
trans D601:win/c/test wc README.md
|
||||
trans D601:win/c/test rg -i needle .
|
||||
trans D601:win/c/test git status --short --branch
|
||||
trans D601:win/c/test git diff --check
|
||||
trans D601:win/c/test git commit -m 'fix: update docs'
|
||||
```
|
||||
|
||||
Windows route 里的 `win` 只表示 route plane,后面 operation 直接写 `ps`、`cmd`、`git` 或 fs helper;不要写成 `trans D601:win/... win ps`。Windows operation 必须显式区分:`ps` 走 PowerShell,`cmd` 走 cmd.exe。`git` 是 Windows cmd convenience wrapper,会通过 Windows cmd 在 route cwd 下执行,支持 `git status`、`git diff` 和非交互 `git commit -m ...` 等常规 argv 形态;会打开编辑器或需要复杂 shell 审阅的命令请用 `ps` 或 `cmd` 包装。`pwd|ls|cat|head|tail|stat|wc|rg` 是 Windows 文件系统只读 helper,带 UTF-8/binary 检查和输出上限,不表示 Windows route 有 POSIX `sh`/`bash`。其中 `rg` 是受限 UTF-8 正则搜索子集,支持 `-i/--ignore-case`、`-F/--fixed-strings`、`-n`、`-m/--max-count`、`--max-files`、`--max-bytes`。
|
||||
|
||||
扩展 Windows helper 时保持 operation-scoped PowerShell payload。不要把多 operation 的大 switch 一次性塞进 single `EncodedCommand`;超过 Windows/WSL argv 限制时,常见表现是 `/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe: Invalid argument`,stderr 不会指出真实原因。需要更长逻辑时优先拆短 helper 或切到临时脚本/受控 stdin 路径。
|
||||
|
||||
### GitHub issue/PR route
|
||||
|
||||
```bash
|
||||
trans gh:/pikasTech/HWLAB/issue ls --state all --limit 20 --search 'YAML-first 测试账号 API_KEY 准备 PJ2026-0105' --full
|
||||
trans gh:/pikasTech/HWLAB/issue/1236 cat
|
||||
trans gh:/pikasTech/HWLAB/issue/1236 rg 'API_KEY|YAML-first'
|
||||
trans gh:/pikasTech/HWLAB/issue/1236 apply-patch <<'PATCH'
|
||||
*** Begin Patch
|
||||
*** Update File: body.md
|
||||
@@
|
||||
-old text
|
||||
+new text
|
||||
*** End Patch
|
||||
PATCH
|
||||
```
|
||||
|
||||
GitHub issue/PR 正文读写也走 `trans gh:/owner/repo/...`。常见读取优先用 `issue ls --search/--state`、`cat`、`rg`,不要为了看正文先回到 `bun scripts/cli.ts gh issue view --json body --full`;普通小补丁可直接 `apply-patch`,`--dry-run` 只作为高风险正文预览。
|
||||
|
||||
---
|
||||
|
||||
## Operation
|
||||
|
||||
### sh / bash(显式 shell heredoc)
|
||||
|
||||
```bash
|
||||
# POSIX /bin/sh heredoc
|
||||
trans G14:/root/hwlab sh <<'SH'
|
||||
echo "step 1"
|
||||
git status --short --branch
|
||||
echo "step 2"
|
||||
SH
|
||||
|
||||
# Bash heredoc:只有用到 pipefail、数组、[[ ... ]]、${var:0:8} 等 Bash 语法时使用
|
||||
trans G14:/root/hwlab bash <<'BASH'
|
||||
set -euo pipefail
|
||||
short="${GITHUB_SHA:0:8}"
|
||||
printf '%s\n' "$short"
|
||||
BASH
|
||||
|
||||
# one-liner
|
||||
trans G14:/root/hwlab sh -- 'git fetch origin G14 && git pull --ff-only origin G14'
|
||||
|
||||
# 单进程 direct argv 不放在 sh/bash 下,直接用已知子命令或 argv
|
||||
trans D601:/path sed -n '1,20p' AGENTS.md
|
||||
trans D601:/path argv sed -n '1,20p' AGENTS.md
|
||||
```
|
||||
|
||||
`script` 和 `shell` operation 已移除并会失败;必须在 operation 位置明确写 `sh` 或 `bash`,让调用者和 Agent 都知道脚本语法边界。Windows PowerShell 必须用 `ps`。
|
||||
|
||||
### argv(单命令)
|
||||
|
||||
```bash
|
||||
trans G14:/root/hwlab git status --short --branch
|
||||
trans D601 hostname
|
||||
```
|
||||
|
||||
### apply-patch(远端文本 patch)
|
||||
|
||||
|
||||
- **P0: 所有远端文本/源码修改必须优先使用 `apply-patch`,禁止用 `py`(python3 写文件)或 `sed` heredoc 拼接大段文本替代。** 只有当 apply-patch 本身不可用或需处理非文本/批量机械生成文件时,才使用其他受控方式;使用前必须在注释中说明原因,修改后立即用 `git diff` 或文件尾部检查确认没有截断或污染。
|
||||
|
||||
```bash
|
||||
trans G14:/root/hwlab apply-patch <<'PATCH'
|
||||
*** Begin Patch
|
||||
*** Update File: AGENTS.md
|
||||
@@
|
||||
## Heading
|
||||
context line
|
||||
-old line
|
||||
+new line
|
||||
more context
|
||||
*** End Patch
|
||||
PATCH
|
||||
|
||||
# 从文件读
|
||||
trans G14:/root/hwlab apply-patch < patch.diff
|
||||
```
|
||||
|
||||
v2 引擎(默认):本地 TypeScript 解析 hunk,远端只读写文件。v1 legacy 入口:`apply-patch-v1`。
|
||||
|
||||
Host/WSL 与 Windows route 的 `apply-patch` 优先使用 route fs adapter 的 bulk update path。单文件文本 update 应看到 `remoteOperationCounts` 类似 `{"fs.readFiles":1,"fs.applyReplacementsBulk":1}`,而不是旧慢路径 `stat/read-b64-block/write-b64-argv`。如果 D601 这类 provider 出现瞬态通道抖动,先保留 `apply-patch` 路径并重试/查 `debug ssh-pool <provider>`,不要退回 PowerShell/Python/sed 临时写文件。
|
||||
|
||||
### py(远端 Python 脚本)
|
||||
|
||||
```bash
|
||||
trans D601 py -- arg1 arg2 < script.py
|
||||
```
|
||||
|
||||
自动写到远端临时 `.py` 文件,`python3 -u` 执行后清理。
|
||||
|
||||
### upload / download(整文件传输)
|
||||
|
||||
```bash
|
||||
trans G14:/root/hwlab upload ./local-file.txt /root/remote-file.txt
|
||||
trans G14:/root/hwlab download /root/remote-file.txt ./local-file.txt
|
||||
```
|
||||
|
||||
自动校验 SHA-256,结果中 `verified=true` 即完整性证明。
|
||||
|
||||
### kubectl / logs(k3s 诊断)
|
||||
|
||||
```bash
|
||||
trans G14:k3s kubectl get pods -n hwlab-dev
|
||||
trans G14:k3s logs deploy/hwlab-cloud-api -n hwlab-dev --tail 50
|
||||
trans G14:k3s:hwlab-dev:hwlab-cloud-web-abc logs --tail 100
|
||||
```
|
||||
|
||||
### skills(远端 skill 发现)
|
||||
|
||||
```bash
|
||||
trans G14 skills [--scope all|wsl|windows] [--limit N]
|
||||
```
|
||||
|
||||
### tcp-pool 状态与并发 smoke
|
||||
|
||||
查看 Provider 是否使用 SSH TCP data pool:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts debug health
|
||||
```
|
||||
|
||||
在输出或 frontend 原始 JSON 里看这些 labels:`providerGatewaySshDataTransport=tcp-pool`、`providerGatewaySshDataPoolReady`、`providerGatewaySshDataPoolClaimed`、`providerGatewaySshDataPoolDesired`、`providerGatewaySshDataPoolLastError`。
|
||||
|
||||
单个 provider 的低噪声池状态:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts debug ssh-pool D601
|
||||
```
|
||||
|
||||
如果 `trans`/`tran` stderr 出现 `UNIDESK_SSH_TCP_POOL_HINT`,优先按其中的 `failureKind` 处理:`provider-data-channel-closed` 表示会话中途 data channel 断开,`provider-data-channel-missing` 表示 core/provider 对 channel 状态不一致,`provider-data-pool-exhausted` 表示没有空闲 channel。对幂等受控操作先查 `debug ssh-pool <provider>`,再重试原受控 CLI;不要把这类 hint 单独定性为远端 runtime 配置失败。
|
||||
|
||||
快速验证 D601 维护桥:
|
||||
|
||||
```bash
|
||||
trans D601 argv true
|
||||
trans D601 argv hostname
|
||||
```
|
||||
|
||||
并发 smoke 用短命令并给每路加本地 timeout:
|
||||
|
||||
```bash
|
||||
tmp=$(mktemp -d /tmp/trans-d601-pool-10.XXXXXX)
|
||||
for i in $(seq 1 10); do
|
||||
timeout 30s trans D601 sh -- 'printf start:'"$i"':%s\\n "$(date +%s%3N)"; sleep 2; printf end:'"$i"':%s\\n "$(date +%s%3N)"' >"$tmp/$i.out" 2>"$tmp/$i.err" &
|
||||
done
|
||||
wait
|
||||
for i in $(seq 1 10); do echo "[$i]"; cat "$tmp/$i.out"; cat "$tmp/$i.err" >&2; done
|
||||
```
|
||||
|
||||
期望是每一路 rc=0、stderr 为空、stdout 同时包含 start/end;结束后 pool labels 回到 `claimed=0`。
|
||||
|
||||
---
|
||||
|
||||
## apply-patch 语法(Envelope 格式)
|
||||
|
||||
**不是 unified diff**,是 Codex 专用 envelope。
|
||||
|
||||
```
|
||||
*** Begin Patch
|
||||
*** Update File: <relative-path>
|
||||
@@
|
||||
context line
|
||||
-old line
|
||||
+new line
|
||||
more context
|
||||
*** End Patch
|
||||
```
|
||||
|
||||
### 关键规则
|
||||
|
||||
| 规则 | 正确 | 错误 |
|
||||
|------|------|------|
|
||||
| 路径必须相对 | `AGENTS.md` | `/root/unidesk/AGENTS.md` |
|
||||
| hunk 分隔符 | `@@` | `@@ -N,M +K,L @@` |
|
||||
| 空行 | ` \n`(空格+换行) | 裸 `\n` |
|
||||
| 首行/尾行 | `*** Begin Patch` / `*** End Patch` | 外层不能有空行 |
|
||||
|
||||
### 操作类型
|
||||
|
||||
```bash
|
||||
*** Update File: path/to/file.ext # 修改现有文件
|
||||
*** Add File: path/to/new.ext # 新增文件(可覆盖已有)
|
||||
*** Delete File: path/to/old.ext # 删除文件
|
||||
*** Move File: old/path -> new/path # 移动/重命名
|
||||
```
|
||||
|
||||
### 上下文定位
|
||||
|
||||
默认 3 行上文 + 3 行下文。同一 hunk 在文件中重复时用 `@@` 跳到 class/function:
|
||||
|
||||
```
|
||||
*** Update File: src/app.ts
|
||||
@@ class MyComponent
|
||||
old context
|
||||
-old code
|
||||
+new code
|
||||
more context
|
||||
```
|
||||
|
||||
### 常见失败
|
||||
|
||||
| 症状 | 原因 | 处理 |
|
||||
|------|------|------|
|
||||
| `failed to find expected lines` | 上下文不匹配(文件已变) | 重读目标块,缩小 hunk |
|
||||
| 空 stdout + stderr 报错 | 首行/路径格式错误 | 检查相对路径、envelope |
|
||||
| partialChanges | 前序 hunk 成功,当前失败 | 基于当前文件状态补小 patch |
|
||||
|
||||
成功 stdout:`Success. Updated the following files:` + 文件列表。失败 stdout 为空,stderr 写原因。
|
||||
|
||||
### 远端 patch 正确姿势
|
||||
|
||||
第一个 route token 直接定位到目标 pod/container,容器内 cwd 用 operation 参数 `--cwd /path`,不要从 host 生成 diff 再改路径上传:
|
||||
|
||||
```bash
|
||||
# ✅ 正确
|
||||
trans G14:k3s:hwlab-dev:pod:hwlab-cloud-web-abc:app apply-patch --cwd /web <<'PATCH'
|
||||
*** Begin Patch
|
||||
*** Update File: app.mjs
|
||||
@@
|
||||
-old
|
||||
+new
|
||||
*** End Patch
|
||||
PATCH
|
||||
|
||||
# ❌ 错误:host 生成 diff → 本地 sed 改路径 → 管道到 pod
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 60s 硬超时与短连接
|
||||
|
||||
`trans` 有 60 秒硬超时(不可调大)。长任务必须拆成 submit-and-poll:
|
||||
|
||||
```bash
|
||||
# ✅ 异步启动 + 短轮询
|
||||
trans G14:/root/hwlab sh -- 'hwlab-cli case run start d601-f103-v2-compile'
|
||||
trans G14:/root/hwlab sh -- 'hwlab-cli case run status <runId>'
|
||||
|
||||
# ❌ 长时间挂着等
|
||||
trans G14:/root/hwlab sh -- 'long_running_command && wait'
|
||||
```
|
||||
|
||||
### HINT 信号
|
||||
|
||||
| stderr 输出 | 含义 |
|
||||
|-------------|------|
|
||||
| `UNIDESK_SSH_HINT` | SSH 握手/超时摩擦,提示改用 `sh`/`bash` stdin heredoc |
|
||||
| `UNIDESK_TRAN_TIMEOUT_HINT` | 60s 硬超时触发,提示改用短查询+轮询 |
|
||||
| `UNIDESK_SSH_TIMING` | 慢命令 warning(>10s),用于性能回归监控 |
|
||||
| `UNIDESK_APPLY_PATCH_TIMING` | apply-patch 耗时摘要 |
|
||||
|
||||
---
|
||||
|
||||
## 关键约定
|
||||
|
||||
### heredoc 优先
|
||||
|
||||
多行远端脚本优先用 heredoc,不拼接 shell 字符串:
|
||||
|
||||
```bash
|
||||
# ✅ 推荐
|
||||
trans G14:/root/hwlab sh <<'SH'
|
||||
cmd1
|
||||
cmd2
|
||||
SH
|
||||
|
||||
# ❌ 避免
|
||||
trans G14 sh -- 'cmd1 && cmd2 && cmd3'
|
||||
```
|
||||
|
||||
### 本地 shell 运算符陷阱
|
||||
|
||||
`&&` / `;` / `|` 在 `trans` 后面会被本地 shell 截获。需要远端执行多条命令时用 `sh` 或 `bash` 包裹:
|
||||
|
||||
```bash
|
||||
# ❌ 第二个 sed 在 master server 本地执行
|
||||
trans G14:/root/hwlab sed -n '1,20p' a && sed -n '1,20p' b
|
||||
|
||||
# ✅ 两个 sed 都在远端
|
||||
trans G14:/root/hwlab sh -- 'sed -n "1,20p" a && sed -n "1,20p" b'
|
||||
```
|
||||
|
||||
### Windows quoting
|
||||
|
||||
Windows PowerShell heredoc 用单引号 `<<'PS'`:
|
||||
|
||||
```bash
|
||||
trans D601:win ps <<'PS'
|
||||
Write-Output "hello"
|
||||
PS
|
||||
```
|
||||
@@ -0,0 +1,10 @@
|
||||
# Trans Operations
|
||||
|
||||
Prefer direct, bounded operations:
|
||||
|
||||
- `sh` / `bash`: explicit shell scripts only, with short output.
|
||||
- `kubectl`: diagnostics and emergency reads; formal CI/CD control remains in the UniDesk CLI.
|
||||
- `logs`: tail bounded logs, not full dumps.
|
||||
- `py`, `upload`, `download`: one-off support operations when patch is not appropriate.
|
||||
|
||||
Ordinary trans/SSH work must fit the short connection budget. Long build, CI/CD, trace, probe or rollout work must use submit-and-poll through a controlled job/status surface.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Trans Routes
|
||||
|
||||
Use route only to locate the target. Operation tokens after the route belong to the operation parser.
|
||||
|
||||
- Host workspace route: `NODE:/abs/path`.
|
||||
- K3s route: `NODE:k3s`, optionally with namespace/workload/container suffix for logs.
|
||||
- Windows route: `NODE:win` or `NODE:win/<drive>/<path>`.
|
||||
- GitHub virtual route: `gh:/owner/repo/issue/<number>` or PR/comment paths when supported.
|
||||
|
||||
Do not use a host workspace route as source authority for CI/CD, branch follower, GitOps or rollout decisions.
|
||||
@@ -14,7 +14,7 @@ description: UniDesk Web 开发与浏览器验证技能。用户处理 UniDesk/H
|
||||
- 真实用户入口验证优先;源码检查、构建通过或截图局部正常不能替代原入口验收。
|
||||
- 禁止在本地或 master server 直接跑 `vue-tsc` / 前端全量 typecheck 作为默认验证;本地只做语法级检查和真实入口复测,完整类型检查交给 CI、PipelineRun 或明确指定的受控构建运行面。
|
||||
- 桌面版 Web 截图、视觉复测和 web-probe 采样默认使用 `1920x1080` 视口;只有用户、issue 或 SPEC 明确指定其他尺寸时才覆盖,并在证据中写明覆盖原因。
|
||||
- Web probe、Playwright、fake-server 的详细命令和历史判定口径见 [references/full.md](references/full.md)。
|
||||
- Web probe 命令和历史判定口径见 [references/web-probe.md](references/web-probe.md);Playwright/fake-server 复现见 [references/playwright.md](references/playwright.md)。
|
||||
- 前端改动遵循仓库既有设计系统和 `$frontend-design` 全局 UI 规则;不要做营销式落地页替代真实工具页面。
|
||||
|
||||
## 通用 Web 设计默认
|
||||
@@ -60,10 +60,10 @@ bun scripts/playwright-cli.ts --help
|
||||
bun scripts/cli.ts check --syntax-only
|
||||
```
|
||||
|
||||
HWLAB Web probe、fake-server Playwright、fixture 采集脱敏、Workbench/Performance 判定、Web E2E 和线上复测的完整命令矩阵在 [references/full.md](references/full.md)。
|
||||
HWLAB Web probe、fixture 采集脱敏、Workbench/Performance 判定、Web E2E 和线上复测见 [references/web-probe.md](references/web-probe.md);fake-server Playwright 见 [references/playwright.md](references/playwright.md)。
|
||||
|
||||
## 何时读取 reference
|
||||
|
||||
- 需要 HWLAB Web probe 采集、observe/analyze、sentinel 或线上截图时,读 [references/full.md](references/full.md) 的 `HWLAB Web Probe`、`受控 web-probe` 段。
|
||||
- 需要 fake-server 或 Playwright 复现时,读 `Fake-Server Playwright`、`UniDesk Frontend E2E` 段。
|
||||
- 需要 Workbench/Performance 页判定口径时,读对应段落,不要凭经验补规则。
|
||||
- 需要 HWLAB Web probe 采集、observe/analyze、sentinel 或线上截图时,读 [references/web-probe.md](references/web-probe.md)。
|
||||
- 需要 fake-server 或 Playwright 复现时,读 [references/playwright.md](references/playwright.md)。
|
||||
- 需要 Workbench/Performance 页判定口径时,读 [references/web-probe.md](references/web-probe.md),不要凭经验补规则。
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
---
|
||||
name: unidesk-webdev
|
||||
description: UniDesk Web 开发与浏览器验证技能。用户处理 UniDesk/HWLAB Cloud Web、Workbench、Performance 页、前端状态投影、Playwright、fake-server、web-probe、截图、响应式布局、Web E2E 回归或线上 Web bug 复测/提 issue 时使用。
|
||||
---
|
||||
|
||||
# UniDesk WebDev
|
||||
|
||||
本技能是 UniDesk/HWLAB Web 开发、浏览器复测和 Playwright 回归的唯一操作面。需求真相源仍是 UniDesk OA 规格,例如 `project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md` 和 `PJ2026-01060505-workbench-performance.md`;本技能只规定如何开发、采集、复现和验收。
|
||||
|
||||
## P0 防分叉原则
|
||||
|
||||
- 单一权威入口:先按 issue/CLI 明确的 node + lane 解析 workspace、web-probe origin、public origin、namespace、sourceRef 和目标分支。没有明确目标时才读取受控 YAML;不得把 D601、G14、v0.2、v0.3、旧端口、ClusterIP 或本地 dev server 写成隐藏默认。
|
||||
- 防分叉原则:Web/Workbench 修复必须先确定唯一 authority、唯一 API 契约和唯一状态投影入口;不得写成“当字段缺失/请求失败/状态不符/超时/刷新后,再改走另一个 endpoint、旧 workspace/conversation 模型、localStorage、trace polling、result polling、GET read-through repair、内部 manager、legacy route 或测试专用后门”。字段缺失、状态滞后或投影不完整时,修 schema、projector、read model 或正式 mutation 源头;代码里只能保留单一路径内的输入规范化、校验和错误暴露,不能用 `A ?? B`、`if missing then fallback`、兼容分支或双写双读把缺口遮住。
|
||||
- 禁止状态竞争行为:不得在代码、API、reducer、read model、fake-server 或测试中保留两个事实源,再用“覆盖、压过、override、优先级、优先采用、以 A 为准否则 B、A 赢过 B”等仲裁规则决定展示状态;这就是条件分叉和竞争投影。必须先定义唯一投影对象、唯一归属字段和唯一生成时机,让 UI/API 只消费该投影对象的单一状态;其他原始字段只能作为审计输入、诊断字段或待收敛数据,不能参与展示状态仲裁。
|
||||
- 严禁读侧推理:REST GET、SSE consumer、compat wrapper、CLI renderer、Web reducer/selectors、Trace renderer、fake-server 和测试断言都不能从 trace tail、message text、tool event、result cache、session summary、list row、workspace snapshot 或 localStorage 推断 turn/session/message 的 lifecycle、terminal、final response 或 running 状态。读侧只能读取唯一投影对象已经写好的字段;字段缺失、投影滞后或多字段矛盾时必须暴露 diagnostic/blocker 并修 projection writer/finalizer/schema,不得用 `result?.status ?? trace?.status`、最后一条 event `completed`、message fallback、session fallback、elapsed timeout 或 UI heuristic 补造事实。
|
||||
- Sealed final response 不可被读侧诊断覆盖:assistant final response 一旦由唯一 durable projection 写入 terminal/sealed 字段,主消息正文、finalResponse、message status 和 turn terminal 就是 sealed 用户结果;turn polling、trace hydration、SSE gap、realtime timeout、transport close、compat wrapper error 或 read model lag 只能进入 trace detail、transport diagnostics、projection diagnostics、session health 或消息详情入口,不能替换主 timeline 正文。
|
||||
- 无多路径、无 fallback:这是本技能的核心原则。Web、CLI、fake-server 和线上 web-probe 必须复用同一套 API 契约、同一 route/session/conversation/trace authority 和同一状态投影;不得为了让测试通过增加第二套 dispatcher、localStorage 真相、workspace snapshot 真相、内部 manager 调用、旧 lane 端口、raw request、临时兼容路径或“测试专用后门”。
|
||||
- 严禁事后 repair / 0repair:Web、Workbench、fake-server 和 web-probe 不得通过 reload、切换 session、realign、`sessionRepair`、workspace selection repair、active tab repair、GET read-through、SSE gap repair、localStorage truth 或测试 helper 自动点击,把已经分裂的 route/session/message/trace 状态补成看起来正确。合法页面必须在首次进入、刷新、deep link、session 切换和 SSE 重连时按同一权威自然收敛;不一致必须暴露为 blocker/diagnostic,并修 schema、projector、read model、reducer 或 route authority 源头。允许 session-bound hydrate/gap-fill 只写目标 session 自己的 cache/read model,不得改变 active selection、URL 或当前消息区。
|
||||
- Workbench GET 纯读投影:`GET /v1/workbench/*` 只能读取已持久化的 Workbench 投影事实并组装摘要;不得在 GET 中调用 AgentRun、Code Agent 或其他上游执行面做同步、补事件、修 trace、finalize terminal、改 running/completed 或写 message/session。投影滞后必须显式暴露 `projectionStatus`、`lastProjectedSeq`、`sourceRunId`、`blocker` 或等价诊断字段,并由后台 projector/finalizer 或显式受控 mutation 处理;刷新页面、切换 session 或打开详情不能成为事实推进入口。
|
||||
- Workbench session lifecycle:空消息/无活动 session 的回收只能由后端 lifecycle GC、后台 finalizer 或显式受控 mutation 完成,TTL 和开关从选中 node/lane 的受控配置进入运行面;Web、fake-server 和 web-probe 不得靠隐藏 tab、前端 DELETE timer、GET read-through 或 deep link repair 清理。归档/删除后的 session deep link 必须观察同一 authority 的 404/archived diagnostic,不能补建或复活 session。
|
||||
- Workbench 前端无破坏性投影权:删除 session、清空 active session、清空当前消息页、清空 tabs、把 composer 降成 `session_required`,或把 route session 标记为 not-found/archived/deleted,都是 lifecycle mutation;Web reducer/selectors、route hydrate、GET/list/detail/messages/SSE consumer、fake-server 和测试 helper 都不能由读侧失败、404、空列表、网络瞬断或 late response 触发这些 mutation。只有用户显式 mutation 成功,或后端 canonical lifecycle projection,能改变 session lifecycle。需求真相见 UniDesk OA `PJ2026-010401 Web工作台` 的“破坏性投影权”。
|
||||
- fake-server 不是第二后端:它只按正式 API 契约重放脱敏 fixture 和必要边界变形。未 mock 的 `/auth/*`、`/v1/*`、`/health*` 请求应失败并暴露 path;不得访问 live Cloud API、AgentRun、HWPOD、数据库或 Kubernetes 作为通过条件。
|
||||
- 真实数据优先:fixture seed 优先从目标 node/lane 的受控真实样本采集。合成 fixture 只补真实样本难以稳定覆盖的边界,并标明 `derivedFrom` 与 `syntheticReason`。
|
||||
- 原入口闭环:fake-server Playwright 用例负责可重复红灯和源码回归;线上 `web-probe` 负责同一 node/lane public origin 的 P4 原入口验收。二者不能互相替代。
|
||||
- Master server 禁重型验证:不要在 master server 跑仓库级 check、Web build、Playwright/browser smoke 或镜像构建。HWLAB Web 验证走目标 node/lane workspace、k3s/Tekton、D601 runner 或受控 web-probe。
|
||||
- 禁止裸写 Playwright:UniDesk/HWLAB Web 复现、截图、DOM/API 采样、长程 Workbench 观测和线上 closeout 默认必须走 `web-probe run|script|observe`;不得直接写 `playwright-cli`、临时 Node Playwright 脚本或本地 browser daemon 作为正式证据入口。确有 web-probe 不覆盖的短生命周期需求时,必须说明例外原因,且可复用动作要回收进 web-probe。
|
||||
|
||||
## 工作流
|
||||
|
||||
1. 定位目标:确认 repo、node、lane、workspace、public origin 和目标 SPEC。HWLAB D601 v0.3 例子是 `/home/ubuntu/workspace/hwlab-v03` + `https://hwlab.pikapython.com`,但只能在 issue/CLI 指向该目标时使用。
|
||||
2. 读规则:进入目标 workspace 前读取目标 `AGENTS.md`;涉及规格或测试设计时读取 UniDesk OA 对应 SPEC。
|
||||
3. 线上复现:短动作用 `web-probe run|script`,长程 Workbench/session 观测用 `web-probe observe start|command|status|stop|analyze`;桌面版默认使用 `1920x1080` 视口,除非用户、issue 或 SPEC 明确指定其他尺寸;保存 `scriptSha256`、`runDir`、observer id、stateDir、截图名、截图 SHA、URL、DOM/API 摘要、prompt hash、trace/session/run id。
|
||||
4. 建红灯:修 Workbench/Performance 用户可见 bug 前,在目标 HWLAB workspace 的 `web/hwlab-cloud-web` fake-server Playwright 套件中补确定性用例;fixture 从真实采集样本脱敏产生。
|
||||
5. 修源码:保持状态读写单一路径。状态投影类修复优先收敛 server-state/reducer/projection,不在 UI 组件、trace polling、result polling 或 localStorage 中新增竞争事实。
|
||||
6. 验证:先跑 fake-server Playwright 目标用例,再回到同一 node/lane public origin 用 web-probe 复测;多轮任务必须用同一个 observer/session 采样到终态。截图、report hash 和 analyze finding 作为证据回传,不进入源码仓库。
|
||||
7. 写 issue/closeout:问题登记和收口必须写明目标合并分支、node/lane、fixture 来源、Playwright fake-server 用例、线上 web-probe 命令/脚本 SHA、截图 SHA 和剩余边界。
|
||||
|
||||
## HWLAB Web Probe
|
||||
|
||||
线上 Cloud Web DOM 验收优先使用受控入口:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts web-probe run --node D601 --lane v03
|
||||
```
|
||||
|
||||
web-probe 入口分三类:
|
||||
|
||||
- `run`:repo-owned 标准 DOM probe,适合固定 P4 验收和已有脚本。
|
||||
- `script`:受控 Playwright 托管脚本,适合一轮 55 秒内完成的 DOM/API 断言、截图、route/intercept 和边界采样。
|
||||
- `observe`:纯客户端长程观测,适合同一 Workbench session 多轮任务、realtime/projection 问题、长时间 trace/DOM/network 采样和无副作用报告生成。长程 Workbench 观测默认同时打开两个浏览器页面:control 页面只执行显式 `observe command` 用户动作,observer 页面只打开同一个 session 做被动观察,并默认每 3 分钟整页刷新一次同一 session,模拟用户离开后返回,用来抓多用户/多页面下同一 session 的投影差异、历史 trace 丢失、耗时跳变和 loading 差异。
|
||||
|
||||
工测优先级:Workbench trace 乱序、完成行位置、耗时不一致、时间跳变、final response flicker、session 不刷新、性能慢路径和多轮可靠性问题,默认先用 `observe` 采样并跑 `observe analyze`;不要把临时 Playwright spec、裸 DOM 调试脚本或单次 `script` 结果当作主要工测证据。`script/run` 只作为短路径断言、截图/API 摘要或 observe/analyze 后的补充复核。
|
||||
|
||||
需要 Playwright route/intercept、延迟 API、读取 in-flight DOM 或截图时仍使用受控 `web-probe script`,不要裸写 Playwright:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts web-probe script --node D601 --lane v03 <<'JS'
|
||||
export default async ({ gotoStable, screenshot, fetchJson, fetchApiMatrix, recordStep }) => {
|
||||
await gotoStable('/workbench');
|
||||
const sessionsResponse = await fetchJson('/v1/workbench/sessions?limit=5');
|
||||
const firstSession = sessionsResponse.body?.sessions?.[0] ?? sessionsResponse.body?.items?.[0] ?? null;
|
||||
const sessionId = firstSession?.sessionId ?? firstSession?.id ?? null;
|
||||
const apiMatrix = await fetchApiMatrix([
|
||||
'/v1/workbench/sessions?limit=5',
|
||||
...(sessionId ? [
|
||||
`/v1/workbench/sessions/${encodeURIComponent(sessionId)}`,
|
||||
`/v1/workbench/sessions/${encodeURIComponent(sessionId)}/messages`
|
||||
] : []),
|
||||
'/auth/session'
|
||||
]);
|
||||
recordStep('workbench-ready', { sessionsOk: sessionsResponse.ok, sessionId, apiMatrixOk: apiMatrix.ok });
|
||||
return {
|
||||
sessionsOk: sessionsResponse.ok,
|
||||
sessionCount: sessionsResponse.body?.sessions?.length ?? sessionsResponse.body?.items?.length ?? null,
|
||||
sessionId,
|
||||
screenshot: await screenshot('workbench.png')
|
||||
};
|
||||
};
|
||||
JS
|
||||
```
|
||||
|
||||
长程 Workbench 观测使用 `observe` 命令组:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts web-probe observe start --node D601 --lane v03 --target-path /workbench --sample-interval-ms 5000 --screenshot-interval-ms 60000 --command-timeout-seconds 55
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type newSession
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type selectProvider --provider codex-api
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type sendPrompt --text 'ping'
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type steer --text '继续观察当前 trace'
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type cancel
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type sendPrompt --text-stdin <<'EOF'
|
||||
long prompt
|
||||
EOF
|
||||
bun scripts/cli.ts web-probe observe status webobs-xxxx --tail-lines 6
|
||||
bun scripts/cli.ts web-probe observe collect webobs-xxxx --view turn-summary
|
||||
bun scripts/cli.ts web-probe observe collect webobs-xxxx --view timeline --command-id cmd-xxxx
|
||||
bun scripts/cli.ts web-probe observe collect webobs-xxxx --view trace-frame --trace-id trc_xxx --sample-seq 42
|
||||
bun scripts/cli.ts web-probe observe stop webobs-xxxx
|
||||
bun scripts/cli.ts web-probe observe analyze webobs-xxxx
|
||||
bun scripts/cli.ts web-probe sentinel plan --node D601 --lane v03 --dry-run
|
||||
bun scripts/cli.ts web-probe sentinel status --node D601 --lane v03
|
||||
bun scripts/cli.ts web-probe sentinel image status --node D601 --lane v03
|
||||
bun scripts/cli.ts web-probe sentinel image build --node D601 --lane v03 --dry-run
|
||||
bun scripts/cli.ts web-probe sentinel control-plane plan --node D601 --lane v03 --dry-run
|
||||
bun scripts/cli.ts web-probe sentinel control-plane status --node D601 --lane v03
|
||||
bun scripts/cli.ts web-probe sentinel control-plane trigger-current --node D601 --lane v03 --dry-run
|
||||
bun scripts/web-probe-sentinel-service.ts --node D601 --lane v03 --state-root .state/web-probe-sentinel-smoke --scheduler-disabled --once
|
||||
```
|
||||
|
||||
`observe analyze` 的 duplicate final response 判定必须以 trace-frame 可见行事实为准。`observe collect --view trace-frame` 固定渲染的 `Final Response` 区块是 summary,不是第二条业务 assistant message;只有同一 trace-frame 中出现两个可见 assistant final rows 且内容重复时,才应报告 duplicate finding,并在证据中写明 `finalResponseSummaryBlockCounted=false`。
|
||||
|
||||
`observe collect --view timeline` 用于 issue closeout 前的 artifact drill-down:它只读取现有 `control.jsonl`、`samples.jsonl` 和 `commands/{pending,processing,done,failed,abandoned}/*.json`,默认输出 bounded timeline、关键 metadata、脱敏 disclosure 和下一步命令。按 `--command-id`、`--turn`、`--trace-id`、`--sample-seq`、`--timestamp` 或 `--window-ms` 缩小窗口;需要完整原始内容时必须显式使用 `--raw` 或 `--view files --file ...`。
|
||||
|
||||
项目管理 / MDTODO 页面同样优先使用 `observe`,不要退回一次性 Playwright 脚本。MDTODO 主动编辑验收必须把常见动作沉淀成 `observe command`,同一 observer 串联 source 配置、HWPOD probe/reindex、文件/任务选择、Rxx 树操作、编辑写回和 Workbench launch:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts web-probe observe start --node D601 --lane v03 --target-path /projects/mdtodo --sample-interval-ms 5000 --screenshot-interval-ms 60000 --command-timeout-seconds 55
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type gotoProjectMdtodo
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type configureMdtodoHwpodSource --hwpod-id d601-f103-v2 --node-id node-d601-f103-v2 --root docs/MDTODO/
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type probeMdtodoSource
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type reindexMdtodoSource
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type closeMdtodoSourceConfig
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type selectMdtodoSource --source-id <opaque-source-id>
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type selectMdtodoFile --file-ref <opaque-file-ref>
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type selectMdtodoFile --filename <direct-mdtodo-file-name.md>
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type selectMdtodoTask --task-ref <opaque-task-ref-or-rxx>
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type openMdtodoReportPreview --task <rxx-or-task-ref> --link <report-label-or-path-fragment>
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type toggleMdtodoReportFullscreen --text toggle
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type editMdtodoTaskInline --task <rxx-or-task-ref> --field title --text 'web-probe interactive edit acceptance'
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type editMdtodoTaskInline --task <rxx-or-task-ref> --field body --text 'body updated through web-probe command'
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type editMdtodoTaskTitle --task <rxx-or-task-ref> --text 'web-probe interactive edit acceptance'
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type editMdtodoTaskBody --task <rxx-or-task-ref> --text 'body updated through web-probe command'
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type toggleMdtodoTaskStatus --task <rxx-or-task-ref> --status completed
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type addMdtodoSubTask --parent <rxx-or-task-ref> --title 'web-probe command subtask'
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type continueMdtodoTask --task <rxx-or-task-ref> --title 'web-probe sibling task'
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type deleteMdtodoTask --task <rxx-or-task-ref>
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type launchWorkbenchFromMdtodo --task <rxx-or-task-ref>
|
||||
bun scripts/cli.ts web-probe observe collect webobs-xxxx --view project-mdtodo-summary
|
||||
bun scripts/cli.ts web-probe observe analyze webobs-xxxx
|
||||
```
|
||||
|
||||
`launchWorkbenchFromMdtodo` 是 Project Management 到 Workbench 的正式用户动作复现入口。传入 `--filename` / `--file-ref` 和 `--task` / `--task-ref` 时,runner 必须通过公开 source/file/task 控件完成选择;若 control 页停在 `/projects/mdtodo` 根路由,应先选择默认 source,再选择文件和任务。`source` 是 observe command provenance 字段,不能当成 MDTODO source id;需要指定 source 时只能使用显式 `--source-id`。显式文件或任务目标选不中时,命令必须失败并输出结构化 before/after project snapshot、目标摘要和选项样本,禁止静默选第一项或退回任意任务。
|
||||
|
||||
MDTODO → Workbench launch closeout 必须引用 command result 中的 `launchStatus`、`sessionId`、`workbenchUrl`、`chatStatus`、业务 trace、`otelTraceId` / `chatOtelTraceId`、文件/任务选择摘要和 control URL。`project-mdtodo-summary` 的 `LAUNCH ok/fail` 与 `OTEL` 计数是第一层汇总;若 analyzer 的 archive red 与当前 command result 冲突,以当前 command result、control 页面截图和最新 project summary 为准,并说明 archive red 来自旧样本或旧规则。
|
||||
|
||||
MDTODO 或 Project Management 的 Web 重写/布局 closeout 不能只引用组件 diff、PipelineRun 成功或一张默认截图。最小证据应按 SPEC 覆盖:默认深链任务正文可见、Source/File 下拉显示 direct MDTODO 文件名、Rxx 任务树为 outline 辅助视图、右侧报告栏可打开且不挤爆主工作区、报告全屏可读并可关闭。涉及报告渲染时,先用有 `reportLinkCount > 0` 的任务执行 `openMdtodoReportPreview`,再执行 `toggleMdtodoReportFullscreen`,并在 closeout 中记录 command id、`reportPreviewVisible`、`reportFullscreenVisible`、报告 deep link、截图 SHA 和 analyze report SHA。
|
||||
|
||||
`observe` 的 Project Management 采样可能同时包含 control 页和 observer 页。control 页是显式用户动作所在页面;observer 页用于被动对照和周期刷新。若 `project-mdtodo-summary` 或 `observe analyze` 的最新汇总被 observer 周期刷新、stop 命令或根路由样本带成 `SRC=0/FILES=0/TASKS=0`,不得直接推翻已完成 command result 和 control 页面截图;必须按 `pageRole`、command JSON、control URL、screenshot path/SHA 和相邻 samples 解释。反过来,若 control command 失败、command backlog 未清空或截图 URL 不在目标 source/file/task/report 深链上,也不能用 observer 的健康样本替代用户入口证据。
|
||||
|
||||
`observe command --type newSession` 是显式用户/control action:它通过当前 Workbench UI 点击 `#session-create` 创建新 session,等待 active session id 改变和 composer ready,并把前后 snapshot 写入 control log。它只能用于采样开始时建立目标 session,或作为用户明确的新建会话动作;不得在 route/session mismatch 后当作 repair 手段。
|
||||
|
||||
约束:
|
||||
|
||||
- `web-probe script` 不运行默认探针,必须通过 stdin heredoc 或 `--script-file <path>` 提供脚本;只需要 repo-owned 标准 DOM probe 时使用 `web-probe run`。
|
||||
- `web-probe run|script|observe start` 的默认 URL、browser proxy mode、observe/analyze 报警阈值和 project-management 采样/命令 allowlist 必须来自 `config/hwlab-node-lanes.yaml` 的 `webProbe`;需要排除公网/FRP/跨国 proxy 抖动时,在 YAML 里把目标 node/lane 的 `webProbe.defaultOrigin` 配成内部 Service ClusterIP origin,不要在命令行长期手写 `--url` 或裸 Playwright。
|
||||
- 任何桌面版截图、视觉复测或布局采样没有显式尺寸要求时,默认视口必须是 `1920x1080`;若受控 CLI 暂无直接参数,应在脚本内调用正式 viewport helper 或等价页面上下文设置,并在 closeout 记录最终视口。
|
||||
- `web-probe observe start` 默认是被动观测:记录 DOM 摘要、自然页面 request/response/requestfailed、截图和 performance 样本,不主动 fetch Workbench API、不切换 control session、不拦截路由、不调用 repair helper。长程 Workbench 观测必须保留 control/observer 双页面模型:control 页面执行显式 command,observer 页面只同步到同一 session URL 后被动采样,并按默认 180000ms 周期整页刷新同一 session 来模拟用户往返;周期刷新只作用于 observer,不得改变 control active session 或作为通过条件。两页的 `pageRole`、`pageId`、`sampleGroupSeq` 必须进入样本和 analyzer 报表。任何 `newSession`、`selectProvider`、`sendPrompt`、`steer`、`cancel`、`goto`、`screenshot`、`mark`、`stop` 都必须通过 `observe command` 显式下发,并进入 `control.jsonl`;长 prompt 必须优先用 `sendPrompt --text-stdin` 或 `steer --text-stdin`,不要为了绕开 shell quoting 退回裸 Playwright 或临时脚本。MDTODO 高频操作也必须优先沉淀为 `observe command`;同类动作第二次出现时不要继续写临时 `web-probe script`。
|
||||
- `observe command --type steer` 和 `--type cancel` 是显式用户/control action:steer 复用当前 Workbench composer 的运行中 turn 引导路径,cancel 复用同一 composer 主按钮的取消路径。二者必须进入 `control.jsonl`,不能用后端私有 API、AgentRun direct cancel 或测试后门替代。`configureMdtodoHwpodSource`、`probeMdtodoSource`、`reindexMdtodoSource`、`closeMdtodoSourceConfig`、`selectMdtodoSource`、`selectMdtodoFile`、`selectMdtodoTask`、`editMdtodoTaskTitle`、`editMdtodoTaskBody`、`toggleMdtodoTaskStatus`、`addMdtodoSubTask`、`continueMdtodoTask`、`deleteMdtodoTask` 和 `launchWorkbenchFromMdtodo` 也是显式用户/control action,只能使用页面公开 `data-*` id、正式按钮和 YAML 允许的自然 API;它们通过 public source/file/task id 与 Workbench 关联,不能读取内部 store、私有后端或把 MDTODO 页面包含进 Workbench。
|
||||
- `observe collect --view turn-summary` 是第一层 CLI 阅读视图:只从 `samples.jsonl`、`control.jsonl` 和已有 `analysis/report.json` 按需渲染同一 session 的多 turn 摘要,包含用户消息 preview/hash、traceId、状态、耗时/最近更新时间、steer/cancel 标记和 Final Response 摘要。`observe collect --view trace-frame --trace-id <id> --sample-seq <n>` 是第二层 CLI 阅读视图:从同一采样帧渲染单帧 trace 文字截图,并固定输出 `Final Response` 区块。`observe collect --view project-summary|project-mdtodo-summary` 从同一 artifact 渲染项目管理 / MDTODO DOM 采样、Source/File/Task 计数、command/mutation 结果、Workbench launch、捕获到的 `x-hwlab-otel-trace-id` 和 OTel/Tempo drill-down 线索;project collect 的远端 payload 必须保持 bounded compact rows,由本地 renderer 生成表格,避免 `trans` stdout 截断后 JSON parse 失败。`observe collect --view performance-summary` 用于性能卡顿首屏归因,只输出 LongTask/LoAF/event-loop gap 汇总、CPU profile hotspots、CPU profile stack 摘要、LoAF script hotspots、sourceFiles 和下钻命令;不得默认返回完整 CPU profile frame 树、完整 findings 或大数组,完整细节必须显式读取 `analysis/report.json`、`performance-events.jsonl` 或 `artifacts.jsonl`。collect 视图不是采样器新增保存物,不构成第二事实源。
|
||||
- `performanceCapture` 未被 runner 消费、`commands/pending` 残留、observer 已 `not-running/failed` 或 `performance-summary` 显示 `captures=0` 时,只能把当前性能结论标注为 LoAF/event-loop-gap/LongTask 级别;不得宣称已取得 CPU profile 调用栈。后续优化应先补 source-map/function attribution、payload bytes、`Response.json.then` 关联路径和 pending command 工具状态,再把热点归因到源码函数。
|
||||
- `observe start/status/command/collect/analyze` 默认输出包含 `Wrapper contract` 区块;该区块证明 Web 哨兵只能 wrap 现有 observe CLI verb、现有 runner/analyzer 和既有 artifact contract,不新增第二套 Playwright runner、analyzer、状态机或私有 web-probe API。
|
||||
- `web-probe sentinel plan|status` 只读取 `observability.webProbe.sentinel.enabled/configRefs` 和 owning YAML,渲染 redacted 配置引用图、文件 hash、缺失字段和跨 ref 冲突;`web-probe sentinel image|control-plane` 继续从 owning YAML 渲染 image、GitOps、Argo 和 manifest 计划,并在远端 publish job 接通前拒绝报告部署 mutation。它不启动浏览器、不读取 Secret 值、不保存采样结果,也不是第二套 runner/analyzer。真正的采样和判定仍以 `observe start|command|collect|analyze` artifacts 为准。
|
||||
- Web 哨兵 public dashboard/origin 必须以 issue/SPEC/YAML 既定计划为准;当前 P6 计划沿用 `monitor.pikapython.com`,不要未经明确变更改成 `hwlab-monitor.pikapython.com` 或其他新域名。验证 report 时记录 `publicOrigin`,但不要把域名硬编码到 runner/analyzer 逻辑里。
|
||||
- 验证 sentinel public dashboard **页面 DOM/截图**(区别于 CI/CD `sentinel status|plan|image|control-plane`)时,优先使用 `web-probe sentinel dashboard verify|screenshot --node <node> --lane <lane> --sentinel <id>`;该受控入口会从 YAML public exposure 进入 dashboard 页面,使用目标 node/lane 远程浏览器,并输出 bounded DOM/截图证据。`web-probe script` 的默认 origin 仍是 lane 的 HWLAB Cloud Web,不是 sentinel dashboard origin;只有受控 dashboard 命令暂时覆盖不到的一次性探索才用显式 `page.goto('<sentinel publicBaseUrl>/')`,同类动作第二次出现时必须沉淀回 sentinel dashboard 命令。
|
||||
- `scripts/web-probe-sentinel-service.ts` 是 Web 哨兵 Pod entrypoint;`--once` 只做 config/PVC/SQLite/scheduler/analyzer-command health 快照,`--scheduler-disabled` 仅用于本地服务健康冒烟,不能作为生产运行参数。HTTP 服务只提供 `/api/health`、`/api/status`、`/api/runs`、`/api/maintenance`、`/metrics` 和 redacted dashboard 外壳,底层采样仍只能经 observe CLI adapter。
|
||||
- `trace-frame` 出现 `(无 trace rows;这是 blocker...)` 时,必须先看同一输出中的 `TRACE DIAGNOSTIC`:记录 pageRole/pageId、traceRows/turns/messages 数量、sampleTraceIds、尾部 traceRow/turn/message 归属。若目标 trace 的 turn/message/final 存在但 traceRows 全部属于旧 trace,应按 Workbench read model authority 分裂登记到架构/业务 issue(例:HWLAB #2124),不得把旧 traceRows 当作新 turn 通过证据,也不得让 analyzer 的聚合计数压过 CLI trace 视图。
|
||||
- analyzer finding 不得压过 CLI `trace-frame` 人工视图。尤其 `trace-assistant-message-duplicates-final-response` 只有在 `trace-frame` 中同一 completed turn 可见多条相同 assistant final rows 时才按业务 bug 处理;如果 `trace-frame` 只有一条 assistant final row、后面固定 `Final Response` 区块正确且 API messages/turns 对齐,该 amber 归类为 analyzer 精度问题,应登记/修工具,不得阻止业务 closeout。
|
||||
- 若 `observe status` 显示 PID still alive 但 heartbeat/sample 不推进、`commands/pending/*.json` 不被消费,或 `observe stop --force` 只是继续排队 stop command,应先按 web-probe runner 工具缺陷处理(例:UniDesk #874),用 route 只读确认 PID/heartbeat 后清理进程;不要把 pending command、未触发的 cancel 或 runner stale 混入 Workbench 业务结论。
|
||||
- `web-probe observe` 的 issue evidence 优先记录 observer id、stateDir、report JSON/Markdown SHA、samples/control/network/artifact 计数、routeSessionId、activeSessionId、prompt hash/textBytes、traceId、AgentRun runId/commandId、最终 status 和必要摘要;不要把 prompt 原文、assistant 大段正文、完整 stdout/stderr 或 provider payload 粘贴到 issue。
|
||||
- 多轮 Workbench 采样必须证明同一个 `sessionId` 连续承载所有轮次;每轮至少记录 prompt hash、traceId、终态、最终回答摘要和性能/产物表。若 Web UI 投影卡住但 Code Agent/AgentRun result 已 terminal,应同时登记“执行终态”和“Workbench 投影未收敛”,不得用 `goto`、reload、切 session 或 result polling 把 UI 失败伪装成通过。
|
||||
- 只验收 Workbench 三态一致性时,判定只看当前根目录 artifacts 中最新 `turn-summary` / `trace-frame` / 必要同源 durable API 摘要:运行态应是 session/turn running 且 Final Response 为空;terminal 态(completed/failed/canceled/blocked)应是 session/turn terminal 且 Final Response 存在。`observe analyze` 的 `archive red`、旧样本或非目标 trace 的历史 finding 只能作为排障线索,不能压过当前多轮样本的三态结论;trace rows hydration、CDP responsiveness、慢路径等非三态红灯应拆到对应问题,不阻塞三态 closeout。
|
||||
- Workbench 使用 Pinia Colada 治理 read path 后,terminal turn 的 Final Response 必须由 turn/status authority 直接 seal 到 active agent message;不得依赖强刷 session messages 全量页来兜底正文。若 `workbench-turn-state-triad-inconsistent` 只来自新 turn 提交到 terminal 之间的 transient 样本,而同一目标 trace 的最新 `workbench-triad` / `trace-frame` 已是 completed 且 Final Response 非空,应登记 analyzer 精度或性能问题,不得把 transient 聚合红灯当作三态业务失败。
|
||||
- `observe command --type sendPrompt` 是普通新 turn 路径,composer 主按钮应处于 `data-action=turn`;如果仍是 `steer` 或 `cancel`,必须作为工具/页面状态不一致失败暴露,不能点击后再等待错误接口。空输入状态下 submit disabled 可以为 true,不能把 disabled 当作新 turn 不可用的唯一依据;应先看 input present/enabled、warning absent 和 action=turn,再由正式填入 prompt 后提交。
|
||||
- `observe analyze` 是离线分析,只读取 artifact JSONL 并写 `analysis/report.md` 与 `analysis/report.json`,不访问 Workbench API、不驱动浏览器。`observe start` 每次启动必须先把同一 stateDir 中已有的根目录 JSONL 轮转到带时间戳的 `archive/` 文件;`observe analyze` 默认只分析当前根目录 JSONL,不扫描历史 archive,只有显式指定 archive prefix 时才分析历史轮转窗口。报告必须输出采样点 vs 每个 turn 的总耗时/最近更新时间表、trace row 视觉顺序异常、terminal/轮次完成 row 是否最后、Code Agent 卡片耗时与 trace/轮次完成总耗时一致性、可见“加载中”的数量/归属/并发 owner/连续出现区间、DOM diagnostic/HTTP/console/requestfailed/runtime execution error 分组、page asset provenance segment、同源 API Resource Timing 分位表和超过 YAML `webProbe.alertThresholds` budget 的慢路径 finding;项目管理页还必须输出 DOM readiness、source/file/task 计数、缺失 public task ref、Workbench launch success/failure、captured OTel trace header、自然 project-management API 分组和超过 YAML `webProbe.projectManagement.slowApiBudgetMs` 的慢路径 finding。页面/API 加载、可见“加载中”、长连接打开耗时、turn timing 跳变、trace row 顺序、卡片耗时/轮次完成耗时一致性、session fallback 标题比例和项目管理 API 慢路径阈值只能改 YAML,不能在 analyzer/renderer 中写死。修复必须降低真实请求、投影、渲染或后端路径耗时,禁止为了减少“加载中”出现时间而提前展示未加载完的内容,也不能靠下游 retry/reload/fallback 掩盖。报告里的 `trace-row-order-nonmonotonic`、`trace-completion-row-not-last`、`round-completion-elapsed-mismatch`、`code-agent-card-duration-underreported`、`final-response-flicker`、`uncommanded-visible-state-change`、`mdtodo-workbench-launch-otel-trace-missing`、`project-management-api-slow`、session changed、network 503 等 finding 是排障线索;用于 closeout 时必须结合原始 session/trace/DOM 证据解释,避免把采样噪声直接当作业务结论。
|
||||
- `sentinel validate --quick-verify` 超过 120s 是严重超时,必须保持 warning/red 并优先从 envreuse、git mirror、warm runner 复用、first tool execution、Workbench 投影和 observe/analyze 开销排查;不要通过提高 budget、减少轮数、放宽 analyzer 或绕过 CLI trace 视图来让场景变绿。quick-verify 等待每轮终态时应读取既有 sampler artifacts 和 bounded collect 视图,不能反复启动完整 collect 或新增第二份“trace 截图”保存来源。
|
||||
- 自定义 `web-probe script` 仍运行在 UniDesk `trans` 60s 最外层短连接约束内;能在一轮内完成的 P4 验收优先把 `--command-timeout-seconds` 控制在 55 秒以内,并减少无界 selector/network 等待。确需等待更久时,改用 `web-probe run` 的异步 job/status 语义,或把动作拆成“提交/采样/截图/状态读取”多次短 probe。若输出出现 `UNIDESK_SSH_RUNTIME_TIMEOUT` 但同时恢复了 `reportPath`、`reportSha256`、screenshots 或 DOM steps,先按远端报告判断脚本/页面实际状态;最终关闭证据仍优先用一次未触发短连接超时的 bounded rerun。
|
||||
- issue closeout 优先引用 `web-probe script` 输出的顶层 `issueEvidence` 或 `summary.issueEvidence`;只有需要展开调查时才粘贴 `probe.script.result`、`probe.steps` 或完整 `reportPath`,避免 stdout、summary 和 report 多层重复同一证据。
|
||||
- stdin heredoc 与 `--script-file` 都按 ES module 加载,脚本必须导出 `export default async ({ page, gotoStable, recordStep, ... }) => { ... }`;不要在模块顶层直接写 `return`。失败为 `Illegal return statement`、`does not provide an export named default` 或 finalUrl 仍是 `about:blank` 且 stepCount=0 时,先按 probe 脚本入口误用处理,不要归因成 Cloud Web 行为失败。
|
||||
- 自定义脚本需要主动失败时,优先返回 `{ ok: false, failedCondition: "..." }` 或抛出错误;兼容返回 `{ pass: false }`、`{ success: false }`,且 `{ ok: true, pass: false }` 仍按失败处理。`failedCondition`、`errorMessage`、`message`、`error`、`reason` 或 `summary` 会进入失败摘要;不要只把失败埋在普通业务字段里。
|
||||
- OpenCode iframe 或 direct-host smoke 优先使用 typed `web-probe opencode-smoke --node <node> --lane <lane>`,不要重复手写一次性 Playwright 片段。该命令负责登录 HWLAB public origin、打开 `/opencode`、定位 iframe/direct origin、打开 project/composer、点击可见 `[data-action='prompt-submit']`、采集 EventSource,并断言 DOM assistant 文本与 update/finish/idle 终态。只有 typed 命令覆盖不到的额外 API 或 UI 细节才退回 `web-probe script`。
|
||||
- 自定义 OpenCode smoke 必须等到 OpenCode frame/direct origin 可用后再操作 composer;填入 prompt 后优先点击可见的 `[data-action='prompt-submit']`,不要把 Enter-only 当成稳定提交路径。关闭 OpenCode 对话类 issue 时,`web-probe script` 顶层 `ok` 不能只是“脚本没抛错”;必须断言 DOM body 出现最终 assistant 文本、没有残留 `Thinking`,并记录 EventSource 或等价事件探针看到 `message.part.updated`、`step-finish`、`session.idle`。手工 EventSource 探针可以证明 `/global/event` 消息到达,但不能替代 UI DOM 终态证据。
|
||||
- web-probe 由 UniDesk CLI 从 YAML 声明的 bootstrap admin sourceRef 读取凭据并建立同源 `hwlab_session`;脚本不得自行读取、打印或复制 Web 登录凭据、cookie、token 或完整 API key。
|
||||
- 需要禁用 `EventSource`、mock `Date`/clock、注入 preload hook 或修改浏览器启动前全局对象时,`page.addInitScript()` 必须在目标页面整页加载前注册,并用 `page.goto(new URL(path, origin).toString(), { waitUntil: "domcontentloaded" })` 进入目标 deep link;不要依赖 `gotoStable()` 复用当前 SPA 页面后再期待 init script 生效。若只在已加载页面上 route/block 请求,既有 SSE 或 store 状态可能继续更新,不能作为“无后端刷新依赖”的验收。
|
||||
- 脚本构造 URL 时使用 `new URL(path, baseUrl).toString()`;不要拼出 `//v1/...`。
|
||||
- 先通过 `gotoStable`、`waitWorkbenchReady` 或等价导航进入目标 origin,再用 `fetchJson` 读同源 API;不要在 `about:blank` 上请求 `/v1/...`。`fetchJson` 返回包装对象 `{ ok, status, body, failureKind, ... }`,业务字段必须从 `.body` 读取,并同时记录 `ok/status/failureKind`;如果返回 `status=0` 且页面还不是目标 origin,先按 probe 脚本误用处理,不要归因成 Cloud API 或 Workbench 投影失败。
|
||||
- Workbench 有 SSE/长轮询时不要用 `networkidle` 判断通过;用明确 DOM/API 条件,如 final URL、route sessionId、active tab、message card、trace row 或 durable session detail/messages。
|
||||
- 当前 HWLAB v0.3 Workbench durable read model 的验收端点是 `/v1/workbench/sessions`、`/v1/workbench/sessions/{sessionId}`、`/v1/workbench/sessions/{sessionId}/messages`、`/v1/workbench/turns/{traceId}` 和 `/v1/workbench/traces/{traceId}/events`;提交 mutation 可用正式 `/v1/agent/chat` 产生真实 turn。`/v1/workbench/workspace` 只在当前 API 契约声明为选择/config 快照时采集,不作为消息或 trace authority;`/v1/agent/conversations` 属于旧模型,不得作为 v0.3 Workbench durable projection 的通过条件。
|
||||
- 需要稳定 running message、timing、SSE 或 projection 样本时,优先通过正式同源 mutation 创建样本:`POST /v1/agent/sessions` 建 session,再按前端同一路径提交 `/v1/agent/chat`,轮询 `/v1/workbench/sessions/{sessionId}/messages` 等待 durable projection。不要把历史 session 是否仍 running 当作验收前提,也不要用旧 conversation/workspace 端点造样本。验收结束后能取消的 turn 必须调用 `/v1/agent/chat/cancel` 清理,并在 issue evidence 只记录 trace/session、status、script/report SHA 和必要摘要,避免粘贴完整 provider payload。
|
||||
- HWLAB v0.3 `fa2ad4845a111e0f9e86d473eb4204ed1b2b0a3c` 后,repo-owned `scripts/web-live-dom-probe.mjs` 的默认 `web-probe run --fresh-session` 已使用 durable Workbench session 端点,不应再依赖 `/v1/workbench/workspace` 或 `/v1/agent/conversations` 作为消息/trace authority。若 `session-not-selected` 复现,先确认目标 workspace 已包含该提交,再对 helper 做旧端点扫描;只有扫描仍命中旧端点时才按工具链回归处理。
|
||||
- `web-probe run --fresh-session` 空消息路径的基线证据是 D601 v0.3 job `web-live-dom-probe-1781842424721-44066`:`freshSessionAligned=true`、`messageCount=0`、report SHA `d34c6765943e14bbd639b91bfc17139d582f670ed5cf409a957acd40fae7871f`、screenshot SHA `8d341b7c30293baa9be8f1e63e147ea0fdafbdbcfc43f0b05e4fb9f316ef996c`。
|
||||
- 若带 `--message` 的 fresh-session 路径已经达到 `freshSessionAligned=true`、`promptSubmitted=true`、`messageCount>0` 和 session API 200,但随后报 `trace-id-missing`、`Workbench turn is not visible to the current actor` 或 trace 面板同类不可见文案,应登记为 Workbench turn/trace actor visibility 问题,不再归因到 fresh-session 选择 helper 或旧端点。
|
||||
- Playwright `page.evaluate` 只能传一个可序列化参数;多个值包成对象,或用 `safeEvaluate(fn, { a, b })`。
|
||||
- `safeEvaluate` 返回结构化包装对象;脚本断言前必须解包,例如 `const evaluated = await safeEvaluate(...); const dom = evaluated?.value ?? evaluated;`。`recordStep` 可以记录包装对象用于诊断,但行为断言不要直接读包装对象的业务字段,避免把已通过的 DOM 误判成 `undefined`。
|
||||
- 脚本用 `page.evaluate` 模拟滚动、resize、click 或派发 DOM event 后,若断言 Vue/React 绑定出的 `data-*`、ARIA、按钮状态或 scroll-follow 状态,必须先等待一次浏览器/框架更新,例如 `await wait(300)`、`page.waitForFunction(...)` 或下一次明确 DOM 条件;不要在同一个 evaluate 内写完事件就立刻读取状态,否则会把尚未 flush 的响应式状态误判为业务失败。验证用户滚动暂停/恢复时优先记录操作前后 scroll metrics 和 `data-following` 的延迟稳定值。
|
||||
- `web-probe script` 顶层/default export 运行在 Node 上下文,浏览器全局 `window`、`document`、`location`、`CSS` 只在 `safeEvaluate`/`page.evaluate` 回调内可用。Node 侧 deep link 用 `const origin = new URL(page.url()).origin; await page.goto(new URL('/workbench/sessions/' + encodeURIComponent(id), origin).toString())`,或直接用 `gotoStable('/workbench/sessions/<id>')`;不要在 Node 侧写 `location.origin`、`CSS.escape(...)`,也不要用没有 baseURL 的 `page.goto('/relative/path')`。若失败为 `location is not defined`、`CSS is not defined` 或相对 URL invalid,先按脚本误用处理;工具提示改进跟踪见 [pikasTech/unidesk#479](https://github.com/pikasTech/unidesk/issues/479)。
|
||||
- 脚本中用 `recordStep(name, data)` 保存关键 DOM/API partial evidence;API 批量探测优先用 `fetchApiMatrix(paths)`,单个 API 失败不应让后续证据丢失。
|
||||
- deleted/archived/not-found session deep link 验收用 `web-probe script` 直接 `gotoStable('/workbench/sessions/<id>')`,再用同源 `fetchJson('/v1/workbench/sessions/<id>')`、messages 和 list API 断言 404/不在列表;不要调用要求 active session selection、composer readiness 或 workspace repair 的 helper 作为通过条件。
|
||||
- 验证 Cloud Web runtime config 或 HTML 注入时,优先加 cache-busting query 和 `cache-control: no-cache`,并按层拆开判断:Deployment env、Pod 内 `127.0.0.1` HTML、公网原始 HTML、浏览器 DOM。不要只凭一次 web-probe DOM 缺字段就判定 rollout 失败;先确认是运行面未渲染、Pod 未滚动、边缘缓存,还是脚本读取层级错误。
|
||||
- web-probe 只能观察、截图和断言,不得用 `sessionRepair`、`realignFreshSession`、自动点击 session、reload 循环或 workspace selection repair 作为通过条件。发现 `routeSessionId`、`activeSessionId`、`activeConversationId`、message session 或 trace session 不一致时,必须记录 mismatch 并失败;截图和 API 摘要是证据,不是修复动作。
|
||||
- 失败证据至少保留 `failureKind`、`errorMessage`、`scriptSha256`、`runDir`、`lastUrl`、`lastScreenshot`、`probe.summary` 和 `reportPath`/`reportSha256`;默认失败截图是 `failure.png`。
|
||||
|
||||
## Fake-Server Playwright
|
||||
|
||||
Workbench 回归入口在目标 HWLAB repo 的 `web/hwlab-cloud-web`:
|
||||
|
||||
```bash
|
||||
bun run e2e:workbench -- --project=chromium
|
||||
```
|
||||
|
||||
运行面要求:
|
||||
|
||||
- 在 issue/CLI 选中的 node/lane workspace 或其独立 worktree 上运行,不在 master server 本地运行。
|
||||
- 同源 fake-server 只提供正式 API 契约,覆盖 `/auth/*`、session list/detail/messages、turn snapshot、trace event page、Workbench event stream、Performance summary 等当前用例所需端点。
|
||||
- 测试断言优先使用用户可见文本、ARIA role 和稳定 DOM 标识;不要通过内部 store、localStorage、数据库或 live API 直接判定通过。
|
||||
- 非终态瞬时不变量必须用立即 DOM 快照断言:例如 running/pending 阶段要求 final response 为空时,先定位当前 agent card,再用 `locator.evaluate` / `page.evaluate` 直接读取 `.message-markdown.message-text` 数组并立刻判断;不要用会自动等待的 `expect(locator).toHaveCount(0)`、`not.toContainText` 或类似负向 locator 断言,否则测试可能等到 terminal DOM 替换后误通过,掩盖 running 阶段污染。
|
||||
- 失败必须保留截图、Playwright trace 或等价 artifact;关键路径可生成命名截图。
|
||||
|
||||
Workbench 至少覆盖:
|
||||
|
||||
- session 切换后主工作区 loading 和目标 conversation 恢复;
|
||||
- 刷新页面后以同一 message/turn/trace 标识还原 timeline;
|
||||
- SSE 断线、丢事件或重连后通过 REST snapshot 与 trace page 补洞;
|
||||
- session tab、workspace card、message card、composer 主按钮、Trace detail 的 `running`/`completed`/`failed`/`canceled` 一致性;
|
||||
- completed assistant 已 sealed 后,turn snapshot、trace event page、SSE 或 realtime diagnostic 失败不覆盖主消息正文,诊断只进入详情/感叹号/transport health;
|
||||
- Trace 阅读按事件顺序渲染可读 row,正确处理分页、终态、失败、自动展开和终态折叠;
|
||||
- deep link 与普通点击 session 走同一 authority path;
|
||||
- Performance 页的摘要、错误态、刷新、移动端表格可达性和低基数脱敏展示。
|
||||
|
||||
## Fixture 采集与脱敏
|
||||
|
||||
- 采集范围至少包括 session list/detail/messages、session/turn snapshot、trace event page、Workbench event stream 和 Web performance summary;workspace selection/config 只有在当前 API 契约仍声明该读模型时才采集。
|
||||
- 产物必须记录 `capturedFrom`、`capturedAt`、schema/redaction 版本和 `valuesPrinted=false`。
|
||||
- 使用稳定伪 ID 映射保留 conversation、session、thread、turn、trace、message、sourceSeq 的关系。
|
||||
- 删除或替换 `HWLAB_API_KEY`、cookie、Authorization header、DB DSN、provider token、真实用户身份、非公开 prompt、stdout/stderr 大段原文和完整 provider payload。
|
||||
- 真实样本难以覆盖的延迟响应、SSE 断线、分页缺口、列表缺少当前选中项、可选字段异常、空集合和特定 HTTP 错误可以合成,但必须说明来源和原因。
|
||||
|
||||
## Workbench 判定口径
|
||||
|
||||
- Workbench API 的 GET 路径不是修复入口。若 session/messages/turn/trace 出现状态不一致,应登记为投影唯一性或投影延迟问题,修 backend projector/read model;不得用前端轮询、session 切换、reload、read-through sync 或 GET 内部修复把问题掩盖。
|
||||
- Workbench 读侧不得推理生命周期。`turn.status`、`message.status`、`session.running`、`trace terminal`、`finalResponse`、`projectionStatus` 只能来自唯一 durable projection;trace event row 的 `completed` 只表示该 event/tool row 完成,不能终结 turn;message 文本为空只表示无可展示正文,不能生成占位 final response;list summary、session detail、turn snapshot、trace page 之间的差异只能作为 diagnostic,不得用优先级函数在读侧“选一个看起来合理的状态”。
|
||||
- Workbench sealed final response 必须与读侧诊断分仓。`messageProjection` 承载 role/status/text/finalResponse/sealedAt,`traceDetail` 承载 events/page hydration/trace diagnostics,`sessionStatus` 承载 rail/card 运行摘要,`transportDiagnostics` 承载 SSE/poll/hydration timeout 等可见性诊断;等价结构可以使用不同命名,但主消息投影和 diagnostic 不能竞争同一字段。`showMessageText()`、message diagnostic selector 和组件模板不得让 diagnostic 压过 sealed final response。
|
||||
- Workbench trace 视觉顺序的唯一权威是 durable projection 分配的 `projectedSeq`。`projectedSeq` 必须由持久化 runtime store 在 trace 内全局递增分配,并按 `traceId + sourceEventId` 幂等复用;本地 `event.seq`、`sourceSeq`、事件到达顺序、时间戳、DOM 顺序和 renderer 输入顺序只能作为审计输入或源游标,不能直接派生视觉位置或参与仲裁。重复源事件不能分配新 `projectedSeq`;不同源事件即使局部 `event.seq` 重置为 1 也必须分配新的全局 `projectedSeq`,这个幂等性需要有最小单元测试覆盖。
|
||||
- Workbench trace 时间字段的 source truth 是后端和 trace event page 暴露的 ISO/UTC 时间戳;后端 projector、durable read model 和共享 renderer 默认行为不得改成本地时区字符串。Web 展示北京时间或其他用户时区时,只能在 Vue/Web 渲染层从 runtime `displayTime` 注入 formatter;CLI、CaseRun 和原始 trace API 仍显示/返回 UTC,不通过多字段仲裁、默认 fallback 或后端补写时区字符串修显示。
|
||||
- Trace event page 检测到同一 trace 内历史 `projectedSeq` collision 时必须暴露 `projectionStatus=blocked`、`projectionHealth=degraded` 和稳定 blocker code,不能把损坏序列交给 CLI/Web renderer 继续展示。CLI/Web trace renderer 只能按 projection 已给定的位置一次渲染;不得通过 assistant 文本相等/包含、final response 匹配、尾部排序、terminal row 合并或 completion row 追加来事后修正视觉顺序。
|
||||
- Workbench durable projection 验收必须覆盖同一 session/trace 的 list、detail、messages、turn、trace events 和 DOM session tab/message card;尤其要用 fresh browser 或 runtime 重启后的历史 terminal 样本证明 read model 能从 durable trace events 恢复终态。若内存 trace 仍显示 running 而 durable trace 已 terminal,应修唯一 read model 的投影对象,不得在 GET handler、前端 reducer 或 probe 脚本里做临时状态仲裁。
|
||||
- HWLAB #1585/#1596/#1690 是 Workbench 投影禁令的固定判例:AgentRun 已 terminal 但 Workbench 停在旧 seq 时,不得用 GET/read-side 补 result;turn/card completed 但 TraceEventPage seq/range 不合法时,不得用 completed 状态压过 trace 不可读事实;Trace timeline 乱序或 terminal row 重排时,不得让 `sourceSeq`、局部 `event.seq`、输入顺序和 renderer 文本匹配多来源仲裁。合法修复是后台 projector/resumer 补 durable projection,在同一 trace event 快照内修正分页/顺序契约,或暴露 historical projection blocker;不得推断 lifecycle、补写 GET、切 session/reload 修页面、添加多来源优先级或在 renderer 做事后 repair。
|
||||
- Session rail 会话集合只消费 `/v1/workbench/sessions`;当前 session detail 只消费 `/v1/workbench/sessions/{sessionId}`;消息本体只消费 `/v1/workbench/sessions/{sessionId}/messages`;turn 状态只消费 `/v1/workbench/turns/{traceId}`;trace 阅读只消费 `/v1/workbench/traces/{traceId}/events` 或正式 trace event page。不要让 workspace snapshot、列表摘要、localStorage、trace polling 或 result polling 互相覆盖。
|
||||
- 空消息 session 回收验收必须同时证明:未超过 TTL 的空 session 保留,有消息、running/admitting、terminal result 或 active trace 的 session 不被回收;超过 TTL 的空 idle session 由后端 GC 归档/删除后,rail/list/detail/messages/deep link 都按同一 canonical lifecycle 状态表现。
|
||||
- UI transient state 只保存 route、显式选择、composer draft、scroll、展开状态和临时交互状态;服务端事实进入 server-state/reducer/projection。
|
||||
- running turn 必须同时反映在主任务区、composer 主按钮、session tab 和可取消入口;清空、切换、刷新不得把 running trace 隐藏成空态。
|
||||
- 显式新建或选择 session 是 active selection 的 mutation authority;`POST /v1/agent/sessions` 返回 201 或用户点击目标 session 后,URL、active tab、当前消息区和 composer 必须立即归属目标 session。目标 session detail/messages 慢只能表现为该目标 session 的 loading/empty 态;旧 session 的 list refresh、detail、turn、trace、SSE 迟到事件只能更新旧 session 自己的 cache/status,不得继续占据或夺回 active route、message area、composer/cancel 状态。
|
||||
- 读侧没有破坏性投影权。`GET /v1/workbench/sessions`、session detail、messages、SSE、route hydrate 或 refreshSessions 的失败、404、空列表、网络错误和迟到响应,只能显示 loading/degraded/unknown/blocker 或保留上一份成功投影;不得 `forgetSession()`、`replaceActiveSessionSelection(null)`、清空 messages/tabs,或把 composer 改成 `session_required`。deleted/archived/not-found 必须来自后端 canonical lifecycle projection,或来自用户显式 DELETE mutation 成功后的展示更新。
|
||||
- terminal failure、blocked、canceled 都是最终结果;若 API 返回可读 error/blocker,final response 展示区必须直接显示,不能只放在 trace rows 或详情面板中。
|
||||
- completed、failed、canceled、blocked 的 sealed final result 一旦显示,后续 polling/hydration/SSE/realtime 的 timeout、500、gap、close 和 lag 只能改变诊断区,不得把已显示 final response 闪回 `Code Agent 代理暂时无法连接上游`、`turn 超时无新活动`、`projection-resume:sync-failed` 或同类读侧错误态。
|
||||
- session 切换验收必须覆盖点击态和持久化恢复态:选择目标 session、确认 route/session list/detail/messages 归属同一 `sessionId`、刷新后同一 session 仍 active。
|
||||
- completed turn / Trace 重放 / deep link 验收必须用 fresh browser context 或等价 fresh login 直接打开 `/workbench/sessions/<sessionId>`,不能只用同页 reload。
|
||||
- 0repair 验收必须用 fresh context 直达 session/deep link,或从当前 session 显式切换到目标 session;不得依赖测试 helper 选择、reload、切走再切回、`sessionRepair` 或 `realignFreshSession` 修正页面后再判通过。旧 session 的延迟 detail、turn、trace、SSE 或 refreshSessions 晚到,只能更新该 session cache/list,不能改变 active URL、active tab、current messages 或 composer。
|
||||
- Workbench reducer/selectors/0repair 收敛类 closeout 必须做负向源码扫描并在 issue/PR 证据里写明结果;至少覆盖直接写服务端事实和旧 repair 名称,例如 `messages.value =`、`messages.value.push`、`sessions.value =`、`previousMessages`、`previousSessionId`、`restorePrevious`、`restoreMessagesTraceAuthority`、`repairWorkbenchMessagePage`、`findMessageTextFallback`、`projectionCatchup`、`PROJECTION_CATCHUP` 和组件侧 `workbench.messages`。这些扫描是辅助证据,不能替代 fake-server Playwright 与 public web-probe。
|
||||
- Workbench 浏览器卡死、CDP metrics timeout 或内存持续增长时,优先排查并修应用侧真实压力源,例如请求风暴、未清理的 `PerformanceObserver` / `ResourceTiming` buffer、长列表同步扫描、低层 performance beacon 重试和高基数字段聚合;不得通过降低 web-probe 采样、改浏览器 baseline、自动刷新、fallback、隐藏红灯或放宽 analyzer 让 smoke 通过。使用 `browser-process.jsonl` 取证时按 `pageRole`/`pageId` 和单页面口径分析,预算阈值、baseline 扣除和 kill/red 策略只从 YAML/SPEC 读取,不能把 control+observer 双页面总 RSS 直接当成单页超标。
|
||||
- Workbench RUM/API timing 采集必须保持低基数且有界:同源 API route label 要模板化或脱敏,不能把 session/trace/query 写入聚合标签;从浏览器 ResourceTiming enrich 请求耗时时,应单 pass 选择候选、读完后清理或轮转 resource timing buffer,并用最小单元测试覆盖 buffer 清理和 route label 归一化,避免多轮 Workbench 把诊断采样本身变成内存增长或主线程卡顿来源。
|
||||
- Trace 实时性要用间隔采样,区分“加载中”和“思考中”;终态卡片不得继续保留“思考中”空态。
|
||||
- 用户反馈“当前 session 不刷新、切走再切回才刷新”时,证据必须成对采集:直达目标 URL、原地等待、切到其他 session、切回目标 session 的 DOM 截图;同时记录 session list、session detail、messages、turn snapshot 和 trace event page 的状态。issue 正文要明确这是事件投影/重放问题,不能只登记成普通 deep link 或单接口 404。
|
||||
|
||||
## Performance 页判定口径
|
||||
|
||||
- `/performance` 只展示低基数、脱敏的 Workbench/RUM/API/long task 汇总;不得泄露 `conversationId`、`sessionId`、`traceId`、`runId`、prompt、assistant 正文、tool 参数、stdout/stderr、Secret 或用户身份。
|
||||
- 单位以 API `unit` 为准;后端若以 seconds 存储,前端显示 ms 时必须只做一次换算。
|
||||
- 移动端和窄屏断言要检查目标表格/按钮是否可见、可点击、layout rect 非零且可触达;不要只用 desktop selector readiness 代表所有 viewport。
|
||||
- 刷新按钮应有明确 loading/error/empty 状态,重复点击不能制造 stuck loading、旧数据冒充新数据或未捕获 console/page error。
|
||||
|
||||
## UniDesk Frontend E2E
|
||||
|
||||
UniDesk 主前端交付门禁仍由 `bun scripts/cli.ts e2e run` 承担;本技能规定浏览器侧如何选取和解释验证。
|
||||
|
||||
- 先跑最小相关选择集,例如 `bun scripts/cli.ts e2e run --only frontend:pipeline`;目标检查通过后再跑完整 `bun scripts/cli.ts e2e run`。选择集是真执行,不是输出过滤。
|
||||
- 浏览器必须打开 `config.json` / publicHost 派生的公开 frontend 或 dev frontend proxy;不要用 localhost、Docker internal URL 或后端 API 直调替代用户入口。
|
||||
- 前端断言优先使用用户可见文本、ARIA role、稳定 DOM 标识和 React 控件状态;禁止用 raw JSON、内部 store、localStorage 或后端数据库绕过 UI。
|
||||
- 高信息密度区域如 Pipeline 右侧栏、Trace timeline、详情抽屉、甘特图坐标、资源监控表和 Performance 面板,必须显式检查总高度、横向滚动条、关键控件可见可点、文本不重叠。
|
||||
- 用户服务页必须等待真实后端数据,而不是只看页面骨架;Todo Note、OA Event Flow、Code Queue、FindJob、Pipeline、MET Nonlinear、ClaudeQQ 等页面的通过条件以当前 E2E check 名称和 issue scope 为准,不在文档里复制所有字段清单。
|
||||
- raw JSON 只能在用户显式点击 `查看原始JSON` 后出现;默认页面必须把 JSON 渲染为表格、卡片、徽章、树、图或其他可读控件。
|
||||
- 任何 frontend E2E 失败都应保留截图路径、resultPath、failedChecks 和关键 console/page error 摘要;不要把 missing browser/deps 当作 skip,按锁文件和目标执行面补依赖后继续。
|
||||
|
||||
## 受控 web-probe 代替裸 Playwright
|
||||
|
||||
UniDesk/HWLAB Web 工作不再把裸 Playwright 当作默认操作面。需要截图、DOM 断言、API matrix、route/intercept、SSE 降级、长程 session 采样、性能页面复测或 Workbench 多轮任务时,统一选择 `web-probe run|script|observe`。
|
||||
|
||||
`web-probe observe analyze` 必须把 Workbench session 列表标题纳入默认采样与报告:可见列表中 `Session ses_...` fallback 标题超过一半时输出红灯 finding。修复方向必须让上游 session list projection 直接携带名称;点击详情后的下游补名、reload repair 或多来源仲裁不能作为修复。
|
||||
|
||||
`playwright-cli` 和临时 Node Playwright 脚本只允许作为 web-probe 尚未覆盖且一次性不可复用的诊断例外。例外使用前必须写明为什么 `web-probe run|script|observe|screenshot` 不适用;同类动作出现第二次就应补进 web-probe CLI 或 repo-owned probe。
|
||||
|
||||
需要把截图回传到本机时,优先用 `web-probe screenshot`,或长程观测中使用 `observe command --type screenshot --label <label>`,并在 issue 中引用截图 SHA、report SHA、observer id 和 stateDir。不要把本地 Playwright artifact 当作 HWLAB node/lane 原入口验收替代品。
|
||||
@@ -0,0 +1,7 @@
|
||||
# Playwright And Fake Server
|
||||
|
||||
Use Playwright/fake-server for local reproduction when the real entry or web-probe path needs an isolated fixture.
|
||||
|
||||
- Keep viewport and target URL explicit.
|
||||
- Prefer screenshots plus DOM assertions over raw logs.
|
||||
- Do not replace real online validation with fake-server evidence when closing user-facing issues.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Web Probe
|
||||
|
||||
Use web-probe for real user-entry evidence:
|
||||
|
||||
- `observe start`, `observe command`, `observe collect`, `observe analyze` for interactive flows.
|
||||
- `run` or `script` only for scoped probes and smoke checks.
|
||||
- Closeout references observer id, command id, artifact/report SHA, screenshot SHA and key bounded fields.
|
||||
|
||||
Workbench and Performance investigations should use dedicated collect/analyze views before grepping raw JSONL artifacts.
|
||||
@@ -25,6 +25,12 @@ controller:
|
||||
repository: pikasTech/unidesk
|
||||
branch: master
|
||||
gitMirrorReadUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/unidesk.git
|
||||
gitMirrorCachePvcName: hwlab-git-mirror-cache
|
||||
githubSsh:
|
||||
secretName: git-mirror-github-ssh
|
||||
privateKeySecretKey: ssh-privatekey
|
||||
proxyHost: jd01-host-proxy.platform-infra.svc.cluster.local
|
||||
proxyPort: 10808
|
||||
sourceAuthority:
|
||||
mode: gitMirrorSnapshot
|
||||
resolver: k8s-git-mirror
|
||||
@@ -86,6 +92,29 @@ followers:
|
||||
logs:
|
||||
argv: ["bun", "scripts/cli.ts", "hwlab", "nodes", "control-plane", "status", "--node", "JD01", "--lane", "v03", "--full"]
|
||||
timeoutSeconds: 35
|
||||
nativeStatus:
|
||||
source:
|
||||
gitMirrorReadUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
|
||||
gitMirrorNamespace: devops-infra
|
||||
gitMirrorDeployment: git-mirror-http
|
||||
repoPath: /cache/pikasTech/HWLAB.git
|
||||
tekton:
|
||||
namespace: hwlab-ci
|
||||
pipelineRunPrefix: hwlab-jd01-v03-ci-poll
|
||||
argo:
|
||||
namespace: argocd
|
||||
application: hwlab-node-v03
|
||||
runtime:
|
||||
namespace: hwlab-v03
|
||||
workloads:
|
||||
- kind: Deployment
|
||||
name: hwlab-cloud-api
|
||||
sourceCommit:
|
||||
labels: ["hwlab.pikastech.local/source-commit", "hwlab.pikastech.local/gitops-render-source-commit"]
|
||||
annotations: ["hwlab.pikastech.local/source-commit", "hwlab.pikastech.local/gitops-render-source-commit"]
|
||||
podLabels: ["hwlab.pikastech.local/source-commit", "hwlab.pikastech.local/gitops-render-source-commit"]
|
||||
podAnnotations: ["hwlab.pikastech.local/source-commit", "hwlab.pikastech.local/gitops-render-source-commit", "hwlab.pikastech.local/boot-commit"]
|
||||
env: ["HWLAB_COMMIT_ID"]
|
||||
closeout:
|
||||
checks: ["sourceSnapshot", "pipelineRun", "gitMirrorPostFlush", "gitops", "argo", "runtime", "publicHealth"]
|
||||
|
||||
@@ -132,6 +161,27 @@ followers:
|
||||
logs:
|
||||
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "status", "--node", "JD01", "--lane", "jd01-v02", "--full"]
|
||||
timeoutSeconds: 35
|
||||
nativeStatus:
|
||||
source:
|
||||
gitMirrorReadUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
|
||||
gitMirrorNamespace: devops-infra
|
||||
gitMirrorDeployment: git-mirror-http
|
||||
repoPath: /cache/pikasTech/agentrun.git
|
||||
tekton:
|
||||
namespace: agentrun-ci
|
||||
pipelineRunPrefix: agentrun-jd01-v02-ci
|
||||
argo:
|
||||
namespace: argocd
|
||||
application: agentrun-jd01-v02
|
||||
runtime:
|
||||
namespace: agentrun-v02
|
||||
workloads:
|
||||
- kind: Deployment
|
||||
name: agentrun-mgr
|
||||
sourceCommit:
|
||||
annotations: ["agentrun.pikastech.local/source-commit"]
|
||||
podAnnotations: ["agentrun.pikastech.local/source-commit"]
|
||||
env: ["AGENTRUN_SOURCE_COMMIT", "AGENTRUN_BOOT_COMMIT"]
|
||||
closeout:
|
||||
checks: ["sourceSnapshot", "pipelineRun", "gitops", "argo", "manager", "runtimeHealth"]
|
||||
|
||||
@@ -176,5 +226,23 @@ followers:
|
||||
logs:
|
||||
argv: ["bun", "scripts/cli.ts", "web-probe", "sentinel", "report", "--node", "JD01", "--lane", "v03", "--sentinel", "jd01-web-probe-sentinel", "--latest", "--view", "summary", "--raw"]
|
||||
timeoutSeconds: 35
|
||||
nativeStatus:
|
||||
source:
|
||||
gitMirrorReadUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/unidesk.git
|
||||
gitMirrorNamespace: devops-infra
|
||||
gitMirrorDeployment: git-mirror-http
|
||||
repoPath: /cache/pikasTech/unidesk.git
|
||||
argo:
|
||||
namespace: argocd
|
||||
application: hwlab-web-probe-sentinel-jd01
|
||||
runtime:
|
||||
namespace: hwlab-v03
|
||||
workloads:
|
||||
- kind: Deployment
|
||||
name: hwlab-web-probe-sentinel-jd01
|
||||
sourceCommit:
|
||||
annotations: ["unidesk.ai/source-commit", "hwlab.pikastech.local/source-commit"]
|
||||
podAnnotations: ["unidesk.ai/source-commit", "hwlab.pikastech.local/source-commit"]
|
||||
env: ["UNIDESK_SOURCE_COMMIT", "WEB_PROBE_SENTINEL_SOURCE_COMMIT"]
|
||||
closeout:
|
||||
checks: ["sourceMirror", "imageRegistry", "gitops", "argo", "runtimeHealthEndpoint", "dashboard", "report"]
|
||||
|
||||
+834
-232
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user