docs(reference): 固化 Todo Note 写端点 + 秘书 CLI 摩擦调查方法论
三处文档改动: - microservices.md「Todo Note On Main Server」补 actions 端点形态段、backend-only 真实语义、404 catch-all 误判陷阱 - cli.md「microservice proxy」段补 Todo Note 写操作 CLI 范式(toggleTodoCompleted / addTodo / undo / redo) - secretary-reference.md 加「CLI 报错四步调查方法论」(读源码+容器实测+git history+复盘 issue) 沉淀 pikasTech/unidesk#188 复盘教训,关闭 #190 文档缺口。剩下 diary --summary 吞并的 P0 由 #9 跟踪。
This commit is contained in:
+22
-1
@@ -57,7 +57,7 @@ CI/CD、GitOps、rollout、artifact 发布、PR 合并后的 DEV/PROD 滚动、P
|
||||
- `hwlab cd status|audit|preflight|apply --env dev [--dry-run]` 是旧 D601 HWLAB DEV CD 指挥侧 wrapper,仅用于显式 legacy 诊断和迁移对照。默认通过 UniDesk provider `host.ssh` 进入 D601,再调用 HWLAB repo-owned `scripts/dev-cd-apply.mjs`,不内嵌发布 kubectl 逻辑:`status` 汇总固定 CD mirror、Git clean/main/origin-main、`deploy/deploy.json`/artifact catalog/report、D601 native k3s guard 和 CD Lease lock,并用 `scripts/dev-cd-apply.mjs --status --skip-live-verify` 取得 target/promotion 摘要;`audit` 在 k3s/CD 恢复后做只读健康审计,返回有界 JSON 的 blocker 分类、D601 guard/node、SecretRef 存在性、registry 可达性、Lease phase/holder/staleness、deploy.json 与 artifact/workload image 收敛、current Deployment image/revision/rollout、16666/16667 public health commit/readiness 和 DB/runtime durability 摘要;`preflight` 进一步检查必需 SecretRef 对象/键存在性并运行 HWLAB `scripts/dev-cd-apply.mjs --dry-run --skip-live-verify` 受控事务摘要。完整远端 stdout/stderr 写入 D601 `~/.state/unidesk-hwlab-cd/<run-id>/` 和本地 `.state/hwlab-cd/<run-id>/` task dump,stdout 只返回有界摘要。默认 HWLAB CD repo 是 `/home/ubuntu/hwlab_cd`,`/home/ubuntu/hwlab` runner 历史目录不得作为发布真相。wrapper 强制 `KUBECONFIG=/etc/rancher/k3s/k3s.yaml` 并只以这个显式目标作为 gate;显式目标出现 `docker-desktop`、`desktop-control-plane` 或 `127.0.0.1:11700` 信号会结构化拒绝,audit/preflight/apply --dry-run 都必须观察到 node `d601`。真实 apply 只暴露 `scripts/dev-cd-apply.mjs --apply --confirm-dev --confirmed-non-production --write-report` 命令形状并标注 host-commander-only,本 runner 不执行 live apply、rollout、Lease mutation 或 DEV deploy apply。长期规则见 `docs/reference/hwlab.md`。
|
||||
- `gh auth status [--repo owner/name]` 探测 GitHub 操作前置条件并输出脱敏 JSON:是否存在 `gh` binary、是否存在 `GH_TOKEN`/`GITHUB_TOKEN` 或可用 `gh auth token` fallback、REST API 是否可达、目标 repo 是否可见、issue 是否可读。degraded reason 必须归类为 `missing-binary`、`missing-token`、`auth-failed`、`github-transient`、`network-proxy-failed`、`permission-denied`、`repo-not-found`、`repo-forbidden`、`issue-not-found`、`pr-not-found`、`scope-insufficient`、`validation-failed`、`invalid-response` 或 `unsupported-command`,不得打印 token;失败对象必须包含 `runnerDisposition=infra-blocked|business-failed`,runner 应优先用该字段分流。`github-transient` 表示 GitHub DNS/API 连接在收到 HTTP 状态前失败,输出应带 `retryable=true` 或等价 commander action;这不是缺 token、认证失败、权限不足或 PR 语义失败。
|
||||
- `codex prompt-lint [prompt|--prompt-file path|--prompt-stdin]` 是派发/steer 前的本地 dry-run prompt lint。它只读取 prompt 文本,返回 `dryRun=true`、`mutation=false`、`declaredClass`、`effectiveClass`、`requiredClass`、`dispatchDisposition`、缺失或矛盾项和有界 evidence,不访问 live service、不提交任务、不打印完整 prompt。分级固定为 `read-only`、`live-read`、`live-mutating`;未声明时按 `read-only` 处理。`codex submit --dry-run` 与 `codex steer --dry-run` 会嵌入同一 `promptLint` 结果,帮助指挥官在 dispatch/steer 前发现缺失或矛盾的 live mutation 授权。长期规则见 `docs/reference/code-queue-supervision.md` 的 DEV 测试授权分级。
|
||||
- `gh issue list [owner/repo] [--state open|closed|all] [--limit N] [--repo owner/name] [--json number,title,state,url,updatedAt,createdAt,author,labels]` 通过 GitHub REST 列出 issue,默认 `state=open`、`limit=30`,输出稳定 JSON 且不依赖系统 `gh` binary。`owner/repo` 位置参数是 `--repo owner/repo` 的兼容别名;若位置 repo 与 `--repo` 冲突,或位置参数不是 `owner/repo`,必须结构化失败,禁止静默 fallback 到默认 repo。`--limit` 会映射到 GitHub `per_page` 并限制返回数量,避免一次拉爆上下文;未知 state 或未知 `--json` 字段必须结构化失败并带 `runnerDisposition=business-failed`。GitHub issues API 可能混入 PR,CLI 会从 `.data.issues` 中过滤 pull request。
|
||||
- `gh issue list [owner/repo] [--state open|closed|all] [--limit N] [--label label[,label...]]... [--repo owner/name] [--json number,title,state,url,updatedAt,createdAt,author,labels] [--raw|--full]` 通过 GitHub REST 列出 issue,默认 `state=open`、`limit=30`,输出稳定 JSON 且不依赖系统 `gh` binary。`owner/repo` 位置参数是 `--repo owner/repo` 的兼容别名;若位置 repo 与 `--repo` 冲突,或位置参数不是 `owner/repo`,必须结构化失败,禁止静默 fallback 到默认 repo。`--limit` 会映射到 GitHub `per_page` 并限制返回数量,避免一次拉爆上下文;未知 state 或未知 `--json` 字段必须结构化失败并带 `runnerDisposition=business-failed`。`--label` 是 GitHub REST `labels=label1,label2` 服务端过滤,支持重复 `--label` 和逗号分隔;filter 不在本命令上下文中使用(如 `issue read`、`pr list`)必须结构化失败并指明 `gh issue create and gh issue list` 才是合法作用域。GitHub issues API 可能混入 PR,CLI 会从 `.data.issues` 中过滤 pull request。`--raw|--full` 在 `gh issue list` 上是绕过 20 KiB stdout 截断的显式开关:响应结果会带 `noDump=true`,`output.ts` 据此跳过 head/tail 替换并把完整数据 inline 输出;当响应未超阈值时 `--raw|--full` 行为等价默认。
|
||||
- `gh issue read <number|owner/repo#number> [--repo owner/name] [--json body,title,state,comments] [--raw|--full]` 通过 GitHub REST 读取 issue title/body/state/url 和 comments,默认输出 JSON;`view` 只保留为兼容别名。`owner/repo#number` shorthand 会自动派生 `--repo owner/repo` 和 issue number;若同时提供冲突的显式 `--repo`,CLI 必须结构化失败并给出 `gh issue read <number> --repo owner/repo --json body,title,state,comments` 与 shorthand raw 的可执行命令。兼容旧脚本的 `--json body` 和 `--json body,title,state,comments` 字段选择,且正文仍稳定暴露在 `.data.issue.body`,避免调用方因为 JSON 路径变化把空值当成正文。字段白名单是 `body,title,state,comments,number,url,author,createdAt,updatedAt`,未知字段必须结构化失败并带 `runnerDisposition=business-failed`。`--raw` 与 `--full` 是显式完整披露别名:read/view 会选择完整支持字段集;issue update/edit 只有显式传入时才在成功响应里包含完整 `.data.issue.body`。当最终 `gh` JSON 超过 20 KiB 时,CLI 必须把完整 JSON 写入 `/tmp/unidesk-cli-output/*.json`,stdout 只返回 `outputTruncated=true`、dump path、总 bytes/lines 和 head/tail 预览。默认 list/read 输出仍不得扩散到无界非 JSON 文本。`gh issue create --title <title> --body-file <file|-> [--label label[,label...]]... [--dry-run]`、`gh issue update <number> --mode replace|append --body-file <file|-> [--title ...] [--dry-run] [--full|--raw]`、`gh issue comment create <number> (--body-file <file|->|--body <short-text>) [--dry-run]`、`gh issue comment delete <commentId> [--dry-run]`、`gh issue close|reopen <number> [--dry-run]` 都走 REST,不依赖 `gh` binary。`--body` 仅用于 issue comment 的短单行文本;空白、多行、疑似 shell 污染、secret-like 或过长 inline body 必须结构化失败,Markdown/生成内容/长评论继续用 `--body-file <file|->`。`--label` 仅用于 `issue create`,支持重复传入和逗号分隔;`--dry-run` 会展示解析后的 labels 与 request plan,正式创建时把 labels 放入 GitHub REST create-issue payload,GitHub 返回不存在 label 等 422 校验失败时 CLI 结构化返回 `validation-failed`,不静默成功。`gh issue delete <number>` 是结构化 `unsupported-command`,因为 GitHub REST 不支持 issue 硬删除;生命周期删除语义请使用 `close`。
|
||||
- `gh issue update <number> --mode replace|append --body-file <file|->` 是正文更新主入口,`edit` 保留为兼容别名。`replace` 用文件或 stdin 正文替换现有 body;`append` 先读取当前 body,再按 UTF-8 文件或 stdin 字节追加,保留真实换行、反引号和 Markdown 表格。更新默认拒绝字面量 `null`、空白正文和过短正文;只有真实需要写短正文时才允许显式加 `--allow-short-body`,返回 JSON 会报告该风险。#20 总看板和指挥简报类 issue 是长期 body-only issue,`--body-profile auto` 会按 issue number 自动启用 #20/#24 legacy guard:#20 必须包含 `## 看板(OPEN)`,#24 legacy 指挥简报必须包含 `## 常驻观察与长期建议`。显式 `--body-profile commander-brief` 不再固定 #24;#24 仍兼容,标题为 `YYYY-MM-DD 指挥简报(北京时间)` 或既有正文首行/关键 heading 表明为每日滚动指挥简报的 issue 也合法,并仍必须包含 `## 常驻观察与长期建议`。对非简报 issue 显式使用 `commander-brief` 会结构化失败为 `profile-issue-mismatch`。`--dry-run` 不 PATCH GitHub,输出有界 `bodyPreview`/`bodyPreviewLines`、新正文长度、SHA、关键标题检查结果、字面量 `\n`、反引号、Markdown 表格、shell 污染信号、`guard`、`concurrency`、`bodyOnlySafety` 和 `wouldPatch`;若环境里有 `GH_TOKEN` 或 `GITHUB_TOKEN`,dry-run 还会只读抓取旧正文长度、SHA 和 `updatedAt` 作为更新前对照。正式写入默认先读取当前 issue,执行 guard 和显式 `--expect-*` 并发校验,再 PATCH;成功输出 compact issue 摘要、old/new body SHA、updatedAt、bodySource 和 drill-down `readCommands`,不包含完整 `issue.body`。完整正文必须显式 `--full|--raw` 或后续执行 `readCommands.body/full/raw` 获取。
|
||||
- #20 只允许承担长期 UniDesk 指挥官 / Code Queue / CLI / infra 治理总看板职责;每日进展必须写入当天滚动指挥简报 issue,并由 #20 顶部“指挥简报索引”引用。HWLAB 用户反馈、Cloud Workbench、DEV-LIVE、M3 虚拟硬件可信闭环等产品 issue 必须写到 `pikasTech/HWLAB`;#20 只可记录 UniDesk 侧 commander/Code Queue/CLI/infra 支撑工作。`gh issue read/view 20` 会返回 `codeQueueBoardHint`;`gh issue update/edit 20` 的 body guard 会拒绝 `## 更新 YYYY-MM-DD HH:mm 北京时间`、`## YYYY-MM-DD HH:mm 北京时间指挥更新` 和 `### YYYY-MM-DD HH:mm CST:...` 这类简报段落;把 `pikasTech/HWLAB#N`、`HWLAB#N` 或 HWLAB 产品/live 验证行写入 #20 时只返回 warning 和 `codeQueueBoardHint`,不再拒绝正文 replace,以避免历史正文或治理交叉引用造成次生阻塞;`gh issue board-row list|get|update|add|move|delete|upsert --board-issue 20` 也会返回同一 hint,提醒不要把每日简报或 HWLAB 产品看板混入 #20。
|
||||
@@ -121,6 +121,27 @@ UniDesk 仓库自带 `scripts/playwright-cli.ts` 作为 host commander 浏览器
|
||||
|
||||
`microservice proxy` 是面向人工验证和受控调试的私有后端入口。默认 method 为 GET;使用 `--body-json JSON`、`--body-file path` 或 `--body-stdin` 时默认 method 切换为 POST,也可显式加 `--method POST|PUT|PATCH|DELETE`,但 GET/HEAD 不允许携带请求体。所有请求仍受 config 中的 `allowedMethods` 和 `allowedPathPrefixes` 限制。为了避免 Pipeline snapshot 这类超大业务 JSON 造成 CLI 输出爆炸,响应 body 超过默认阈值时会返回 `bodyOmitted=true`、`bodyPreview`、`bodyBytes` 和 `rawHint`;`--raw` 仍受默认硬限额保护,需要完整 body 时显式添加 `--raw --full`,或用 `--max-body-bytes <N>` 调整预览阈值。正式 frontend 展示仍应优先使用业务控件和 `__unideskArrayLimit` 这类展示级裁剪参数,而不是默认倾倒完整 JSON。
|
||||
|
||||
**Todo Note 写操作 CLI 范式(2026-06-01 [#190](https://github.com/pikasTech/unidesk/issues/190) 固化)**:Todo Note microservice 写不走 REST 集合(如 `/api/instances/:id/todos`),所有 task 写都走 action 队列 `POST /api/instances/:id/actions` + body `{action: {type, ...}}`。常见范式:
|
||||
|
||||
```bash
|
||||
# 标记完成 / 取消完成
|
||||
bun scripts/cli.ts microservice proxy todo-note \
|
||||
/api/instances/<id>/actions --method POST \
|
||||
--body-json '{"action":{"type":"toggleTodoCompleted","todoId":"todo_xxx"}}'
|
||||
|
||||
# 新增 todo
|
||||
bun scripts/cli.ts microservice proxy todo-note \
|
||||
/api/instances/<id>/actions --method POST \
|
||||
--body-json '{"action":{"type":"addTodo","title":"..."}}'
|
||||
|
||||
# 改名 / 改 reminder / 删除 / 移动:换 type 即可,见 docs/reference/microservices.md
|
||||
# 撤销 / 重做
|
||||
bun scripts/cli.ts microservice proxy todo-note /api/instances/<id>/undo --method POST
|
||||
bun scripts/cli.ts microservice proxy todo-note /api/instances/<id>/redo --method POST
|
||||
```
|
||||
|
||||
看到 `404 {"error":"Todo Note is running in backend-only mode"}` 时**第一反应**是路径错(用了不存在的 REST 端点被 catch-all 兜底),不是 mode 锁了写;详见 `docs/reference/microservices.md` 的 Todo Note 段。
|
||||
|
||||
GitHub issue/PR 写操作必须优先使用 `bun scripts/cli.ts gh issue|pr ... --body-file <file|->`。不要把 Markdown 正文拼进 shell 参数或 `gh api -f body=...`;这些路径容易把真实换行污染成字面量 `\n`。从 shell 生成正文文件时使用 quoted heredoc,例如 `cat <<'EOF' > /tmp/body.md`,保证反引号、反斜杠和 Markdown 表格不被 shell 展开;一次性写入可直接走 stdin,例如 `cat /tmp/body.md | bun scripts/cli.ts gh issue update <number> --repo owner/name --body-file -` 或 `cat /tmp/body.md | bun scripts/cli.ts gh issue comment create <number> --repo owner/name --body-file -`。`gh issue update/edit` 正式写入默认先读取当前 issue 元数据,执行 body guard 并在结果里返回旧 `bodySha`/`updatedAt` 与新正文摘要;一般看板和评论写入不需要人工先 `read` 再手填 `--expect-body-sha`。对高风险整篇替换仍可显式加 `--expect-body-sha` 或 `--expect-updated-at`,CLI 会在 PATCH 前校验,不匹配则结构化失败。`gh issue comment create --body <short-text>` 只适合人工短单行评论,默认输出只给 bounded preview、bodyChars、bodySha、source 和 readCommands,不回显长正文;同时传 `--body` 与 `--body-file` 必须结构化失败。PR 安全写入口同样支持 `--body-file <file|->`;`--body` 只适合短单行内容。JSON 请求体场景使用各命名空间自己的 `--body-file` 或 `--body-stdin`,避免长 JSON 直接塞进 shell 参数。`update --mode append` 用 REST 读取旧正文后追加文件或 stdin 字节,不引入 shell 拼接正文路径。`gh pr merge` 是 guarded write:先读 closeout metadata 并拒绝非 ready PR,`--dry-run` 只输出计划不写远端;没有 `--confirm` 之类绕过 preflight 的路径。CLI 会按 UTF-8 原样读取文件或 stdin 内容并用 JSON body 调用 REST API;issue/PR 写入输出不会默认回显完整正文。
|
||||
|
||||
`network perf` 用于生成组网性能前后对比数据。标准 Code Queue overview 读路径基准命令是 `bun scripts/cli.ts network perf --service code-queue --path /api/tasks/overview?limit=30 --count 30 --concurrency 1 --label before`,远程主 server 可用 `bun scripts/cli.ts --main-server-ip 74.48.78.17 network perf ...`。输出包含成功/失败数、状态码分布、`x-unidesk-cache`、`x-unidesk-proxy-mode`、`x-unidesk-upstream-proxy-mode` 分布和 min/p50/p90/p95/max;provider-gateway 长连接数据面验收应看到 `proxyModeCounts.provider-ws-http-tunnel`,adapter native Service 数据面验收应看到 upstream proxy mode 为 `kubernetes-native-service`,若出现 `kubernetes-api-service-proxy` 必须结合 `/api/control-plane.nativeServiceProxy.failedServices` 解释 fallback 原因。
|
||||
|
||||
@@ -58,6 +58,8 @@ Code Queue runner 也是分布式开发执行面。runner 镜像必须内置 `tr
|
||||
- 部署引用:`/root/todo_note/Dockerfile` 构建纯后端镜像,Compose service 为 `todo-note`,容器名为 `todo-note-backend`。
|
||||
- 数据库:Todo Note 不再使用 JSON 文件作为运行时权威存储;必须把 D518 `data/registry.json` 和 `data/instances/*.todo.json`、`*.history.jsonl` 迁移到主 server PostgreSQL 的 `todo_note_instances` 和 `todo_note_history` 表。
|
||||
- 代理路径:只允许 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`、`DELETE`,用于保持 Todo Note 原有清单创建/删除、任务增删改、提醒、展开/收起、移动、撤销/重做等功能。
|
||||
- **写操作端点形态(2026-06-01 复盘 [#188](https://github.com/pikasTech/unidesk/issues/188) 固化)**:Todo Note 不走 REST 集合(如 `/api/instances/:id/todos`),所有 task 写都走 **action 队列**模式 `POST /api/instances/:id/actions` + body `{action: {type, ...}}`。已注册 action type:`addTodo` / `updateTodoTitle` / `toggleTodoCompleted` / `toggleTodoExpanded` / `setAllTodosExpanded` / `moveTodo` / `deleteTodo` / `renameInstance` / `setTodoReminder`。其他写端点:`POST /api/instances`(body `{name}` 新建清单)、`DELETE /api/instances/:id`、`POST /api/instances/:id/undo`、`POST /api/instances/:id/redo`。
|
||||
- **`TODO_NOTE_BACKEND_ONLY=1` 真实语义**:仅关闭 Todo Note 自带 Vite 前端 SPA,不阻挡任何已注册 API 路由(包括所有 POST/DELETE 写)。`/api/health` 暴露的 `backendOnly` 字段是观察用,不是读路径开关。看到 `404 {"error":"Todo Note is running in backend-only mode"}` 时的第一反应是路径写错(用了不存在的 REST 端点被 catch-all 兜底),不是写被 mode 锁。CLI 写操作范式见 `docs/reference/cli.md` 的 `microservice proxy` 段。
|
||||
- UniDesk 前端:`用户服务 / Todo Note` React 页面负责展示清单列表、树形任务、筛选、提醒、拖放/上移下移、撤销/重做、字号控制和显式原始 JSON 按钮。
|
||||
|
||||
Todo Note 在 UniDesk 语境中按纯后端服务管理:不得继续公开 Todo Note 自身 Vite/Web 前端,也不得把 `4211` 映射为公网端口。浏览器只能通过 UniDesk frontend 的 `/api/microservices/todo-note/...` 同源代理访问 Todo Note 后端。标准 artifact consumer 路径为 `bun scripts/cli.ts deploy apply --env dev|prod --service todo-note`;由于 Todo Note 源码仍在外部 Gitee 仓库,D601 registry 中必须先已有 `127.0.0.1:5000/unidesk/todo-note:<commit>`。Compose 在 recreate 时注入 `UNIDESK_TODO_NOTE_DEPLOY_*`,artifact consumer 的健康探针读取 `/api/health` 并合成 `deploy.commit` 和 `deploy.requestedCommit` 供强校验。
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
|
||||
秘书应保护用户的整片清醒时间。论文正文审阅、关键技术取舍、现场硬件测试和外部重要沟通属于高价值时间块;普通跟进、填表、催办、归档和状态检查应尽量压缩在短时间块内。
|
||||
|
||||
**遇到 CLI 报错或"看起来锁了"的现象,不要凭错误文案下结论;按"读源码 + 容器内实测 + git history + 复盘 issue"四步调查。** 第一步读 microservice 源码(UniDesk 主仓 vendoring 的镜像源或外部仓的 `server.ts` / `microservice_proxy.rs`),定位注册路由表、mode 标志位、错误文案生成点;第二步在容器内直接实测(`docker exec ... node ...` 或 `wget http://service:port/...`),拿到真实 200 / 4xx 响应,验证假设;第三步查 git history 看是谁、什么时候、为什么加的;第四步把"原诊断 + 真因 + 实测证据 + 修复点"蒸馏到 `docs/reference/` 并开/更新对应 issue(参考 [pikasTech/unidesk#188](https://github.com/pikasTech/unidesk/issues/188) 复盘结构)。特别警惕"错误文案暗示的因果"——例如 `404 {"error":"X is running in backend-only mode"}` 不代表 mode 锁了写,只代表请求路径没匹配上 catch-all;模式措辞误导是常见陷阱。
|
||||
|
||||
**秘书的本份是维护 Todo Note;Decision Center diary 是用户自己的工作日志,秘书只读不写。** 收到用户口述的进展(完成/未完成/卡点/决策/排程登记),秘书维护 Todo Note(标 completed / 新增子 todo / 拆分 / 改 reminder),不写到 diary。Decision Center diary 是用户承载自己反思和工作日志的私域,秘书写进去等于抢用户的笔、污染第一人称叙述。例外只有读:秘书可以调用 `diary show` / `diary list` / `diary months` 对齐上下文,**不调用 `diary upsert` / `diary edit` / `diary import` / `diary today`**。当 Todo Note 写路径被外部阻塞(如 issue #188 的 backend-only mode)时,秘书必须先把修复升级到日程或明确告诉用户「无法维护 Todo Note,你的进展请你自己在 Todo Note 里勾完成」,不能绕道用 diary 替代。
|
||||
|
||||
## Todo Note 使用规则
|
||||
|
||||
Reference in New Issue
Block a user