docs: 固化恢复优先与单次交付规则

This commit is contained in:
Codex
2026-07-14 09:31:04 +02:00
parent ccf499bcb6
commit f77d1dc221
8 changed files with 66 additions and 11 deletions
+4 -1
View File
@@ -27,7 +27,10 @@ GitHub issue/PR 正式读写必须走 `bun scripts/cli.ts gh ...` 或 `trans gh:
- 只有需要复用或正式保留的文件才使用 `--body-file`
- 不要把 Markdown 塞进 shell 参数。
- `gh pr create` 默认 Next 只给有界 `pr view``pr review-plan``pr preflight` 和 closeout status 下钻;不得默认提示 `preflight --full|--raw`、手工 CI/CD、mirror sync、PipelineRun 或 Argo refresh。
- PR merge 只走 guarded `gh pr merge`;主代理按 `$unidesk-subagent` 完成 review/preflight/merge 授权判断后再执行,不把“禁止默认 Next”理解成“PR 永远不能由主代理受控合并”;只有确认 ancestry 可丢弃时才显式 `--squash`
- PR review 先用 `review-plan` 建立文件索引,只对需要审查的文件执行 `pr diff --file`,不得机械下钻每个文件
- PR merge 只走 guarded `gh pr merge`;它内建 readiness/preflight,普通已审 PR 不先机械执行独立 `pr preflight`。主代理按 `$unidesk-subagent` 完成 review 和 merge 授权判断后执行;只有确认 ancestry 可丢弃时才显式 `--squash`
- 功能、skill、MDTODO 报告和完成状态在 merge 前已齐备时,必须进入同一个 PR。禁止仅为补写 merge SHA、`mergedAt`、branch deletion 或重复 closeout 再开文档 PR;这些事实由 GitHub PR 和 `gh pr merge` 默认摘要保存。
- 成功 merge 后默认摘要已披露 `mergeCommit``mergedAt`,不再机械执行 `pr view`。只有输出缺字段、merge 后出现新运行证据,或需要定点排障时才继续查询;只有用户明确要求把 merge 后新证据写回 Git 时,才创建第二个 closeout PR。
## 常用入口
@@ -2,7 +2,7 @@
PR 工作必须使用受控 UniDesk GitHub 命令:
- review 前先用 `pr review-plan``pr diff --file` 和有界文件下钻
- review 前先用 `pr review-plan` 建立 changed-file 索引,只对需要审查的文件使用 `pr diff --file` 和有界 hunk 下钻;不得逐文件机械展开
- 人工完整 PR 正文使用 `trans gh:/owner/repo/pr/<number> cat`;定点查找使用同一路由的 `rg <pattern>`
- `pr view <number> --json body,...``--full``--raw` 只用于显式机器结构化披露;正文只在 `.data.pullRequest.body` 输出一次。
- `pr create` 默认 Next 只给有界 observe/review/preflight/status
@@ -13,11 +13,14 @@ PR 工作必须使用受控 UniDesk GitHub 命令:
默认不得提示 `preflight --full|--raw`、手工 CI/CD、mirror sync、创建 PipelineRun 或 Argo refresh。
- `pr preflight` 是可选只读诊断;`pr merge` 内部会自行执行 preflight。
- 主代理受控合并仍然允许:
- 主代理按 `$unidesk-subagent` 完成 review/preflight/merge 判断后使用 guarded `pr merge`
- 主代理按 `$unidesk-subagent` 完成 reviewmerge 授权判断后使用 guarded `pr merge`
- 不得把 `pr create` Next 限制误解成“PR 永远不能人工受控合并”。
- `pr merge --merge` 默认删除已合并的同 repo head branch,清理匹配且干净的本地 `.worktree`,并快进位于 PR base branch 的本地主工作区。
- `pr merge --merge --sync-node JD01` 在支持时额外执行映射节点 source-workspace sync;当前用于 HWLAB `v0.3`
- 只有明确需要保留合并后状态时才使用 `--keep-branch``--skip-local-closeout`
- 只有 ancestry 和语义吸收都明确安全时才使用 squash。
- 功能实现、skill、MDTODO 报告和完成状态在 merge 前已齐备时,必须用一个 PR 收口。不得仅为补 merge SHA、`mergedAt`、branch deletion、已合并状态或重复 closeout 再开 PR。
- merge commit 与合并时间由 GitHub PR 事实和 `gh pr merge` 默认摘要保存,不要求回写 MDTODO 报告。成功合并后不机械执行 `pr view`;只有默认输出缺少必要字段或需要定点排障时才查询。
- 只有 merge 后才产生新的运行面证据,并且用户明确要求将该证据写回 Git 时,第二个 closeout PR 才合理。
Closeout 应写明 source branch、验证证据和残余风险。
同一 PR 的 closeout 应在 merge 前写明 source branch、验证证据和残余风险。
+7 -1
View File
@@ -51,6 +51,12 @@ description: UniDesk 主代理调度子代理的必读技能。用户提到子
- 每个任务终态和依赖变化后重新计算可安全派发集合;
- 主代理自己的审核、规划和等待不计入子代理并发量;不得用一个长期任务加主代理活动宣称已经并行;
- 不得为了凑并发拆出无独立价值的日志采集、重复调查、重复实现或审核代理;并发量服从原始任务边界和成功率,不服从固定数字。
- 运行面故障、用户原入口不可用,或凭据/配置变更导致服务退化时,必须先恢复运行面,再完成工程化:
- 主代理先冻结恢复判定标准,并保留恢复关键路径的控制权;
- 优先用既有受控入口实施最小、可逆、可验证的恢复,只有受控入口本身缺失并直接阻塞恢复时,才在关键路径修改工具;
- 通用抽象、输出优化、skill、报告和长期治理不得成为恢复门禁;
- 恢复达到用户原入口可用的标准后,主代理不得把临时恢复当作任务完成,必须继续完成用户明确要求的根因修复、工程化、PR、验证和治理;
- 与恢复根因解耦且具备独立 issue、MDTODO、worktree 和验收入口的工程化任务,必须在恢复期间立即并行派发,不得全部排到恢复之后串行执行。
- 子代理并行只用于成功率高、耦合度低、能放进独立 worktree/branch/issue/PR 的任务;共享架构方向、公共契约和同文件高冲突修改先串行定锚。
- 子代理产出的审核默认由主代理直接完成:
- 用户允许子代理实施、要求并行或启用多轮审查,不等于允许调度审核子代理;
@@ -62,7 +68,7 @@ description: UniDesk 主代理调度子代理的必读技能。用户提到子
- 子代理报告只承载其任务事实与结论,不能自行改写上层目标、架构取舍、优先级、主线状态或跨任务治理规则;
- 主代理审阅子代理报告后,亲自把被采纳的结论写入管理性、决策性文档。
- 纯文档交付可以按 `$git-spec` 的稳定分支快路径由主代理直接 commit/push,不要求临时 branch、`.worktree` 或 PR;发现本地分叉、并行改动、分支保护或运行面影响时必须保留状态并回到隔离交付。
- 正式 GitHub issue/PR/comment/merge 仍走 `$unidesk-gh`;子代理可以提交 PR 和写调查结论,主代理负责 review/preflight/merge,除非用户明确授权某个子代理自上线自验证。
- 正式 GitHub issue/PR/comment/merge 仍走 `$unidesk-gh`;子代理可以提交 PR 和写调查结论,主代理负责 bounded review 和 guarded merge,独立 preflight 只用于定点排障,除非用户明确授权某个子代理自上线自验证。
- 主代理和子代理必须通过 issue/PR/comment 链接传递可复用上下文、调查结论、证据链接和下一步边界;派发前先读既有评论,prompt 中只引用链接和增量任务,不复述长结论,避免不同子代理重复调查同一事实。
- 主代理派发任何执行型子代理前必须完成以下登记;对 Artificer,全部步骤必须早于 AgentRun `create task``apply``dispatch`
- 创建子 issue,把主要任务、接续上下文、目标分支/worktree、范围、禁止项和验收入口写入正文;
@@ -19,6 +19,16 @@
- 并发不能靠重复调查、同根因的多套实现、无独立验收价值的日志采集或默认审核代理虚增;每条并发线都必须有独立 issue、worktree、交付物和验收入口。
- 派发 CI/CD 或运行面调优子任务时,主代理不要在 prompt 或评论里反复解释历史;用 issue/comment 链接引用既有结论,把下一步限定为可由子代理自主真实触发的单步 gate,并要求子代理在该 gate 内小闭环调优到通过后再提交 PR。
## 恢复优先与工程化并发
- 依赖图必须先标出运行面恢复关键路径和用户原入口恢复标准。主代理控制恢复关键路径,避免多个代理同时修改同一生产对象、恢复状态机或共享配置。
- 恢复优先指先让用户原入口通过既有受控路径恢复到可验证状态,不表示工程化任务整体串行:
- 恢复关键路径上的最小诊断、配置修正和复测按真实依赖顺序执行;
- 与根因解耦的 CLI、reference、报告、独立仓库修复和长期治理,在具备独立 issue、MDTODO、worktree、PR 与验收入口时立即并行;
- 只有缺少受控恢复入口直接阻塞恢复时,才允许把工具修改放到恢复关键路径。
- 运行面恢复后,主代理继续完成根因修复、持久化配置、自动交付、原入口复测和治理收口;不得以临时恢复或单次手工成功代替用户要求的终态。
- 恢复期间降低并发必须有具名原因,例如共享生产对象、同一状态机、权限边界或运行面容量;原因解除后立即重新计算可安全并发窗口。
## 模型与思考等级
- Artificer 默认不显式覆盖模型:
@@ -71,7 +81,7 @@
- 先看架构约束和倒退风险,再看实现细节;
- 重点检查是否重新引入旧 authority、并行请求源、裸 API 绕过、隐藏默认、阈值硬编码或降低探针能力;
- 默认不再派审核子代理。
- 合并前使用 `$unidesk-gh`review/preflight/merge 入口。独立 PR 可以并行 review;有依赖的 PR 按契约基线、实现、验证工具、closeout 的顺序合并。
- 合并前使用 `$unidesk-gh`bounded review 和 guarded merge 入口;`pr merge` 已内建 preflight,普通已审 PR 不机械重复执行独立 `pr preflight`。独立 PR 可以并行 review;有依赖的 PR 按契约基线、实现、验证工具、closeout 的顺序合并。
- 子代理 PR 合并后,主代理负责同步目标 worktree、触发受控 CI/CD、执行原入口复测,并把 PipelineRun、GitOps revision、observer、trace、report SHA 等证据写回 issue。
- 若用户明确让子代理“自己上线自己验证”,子代理可以执行受控 CI/CD 和原入口验证,但必须在 issue/PR 留下完整证据;主代理仍要抽查并最终汇总。
@@ -96,7 +106,7 @@ Prompt 至少包含以下字段,按任务裁剪:
4. 计算当前可安全并发窗口并同时派发全部已就绪的低耦合子任务;共享契约先派一个基线任务。
- Artificer 的 `create task``apply``dispatch` 均只能发生在 MDTODO 登记之后。
5. 轮询子代理结果:子 issue comment、PR、验证摘要、阻塞;任一任务终态或依赖变化后立即重新计算窗口并补派,不能退化为长期单任务等待。
6. 对每个 PR 做架构 review、bounded diff、preflight 和必要本地验证。
6. 对每个 PR 做架构 review、bounded diff 和必要本地验证;只有定点排障时单独执行 preflight,正常收口直接使用内建 readiness 的 guarded merge
7. 按依赖顺序合并;合并后同步目标 worktree。
8. 触发受控 CI/CD 或让明确授权的子代理上线;主代理核对 closeout。
9. 用原入口复测;把剩余问题拆到新 issue 或追加既有 issue。
@@ -117,3 +117,7 @@
### R5.7
解决 [UniDesk #1957](https://github.com/pikasTech/unidesk/issues/1957):修复 Artificer resource bundle 缺失 `$post-task` skill 与 `$unidesk-subagent` 权威收口引用不一致;只沿 owning skill source、AipodSpec、resource bundle 和 runner 投影定位并物化,禁止 prompt 复制规则、手工 Pod/宿主补文件、第二 bundle authority 或人工 CI/CD,完成任务后以真实 Artificer follow-up 加载 `$post-task` 验收,完成任务后将详细报告写入[任务报告](./details/agentrun-runtime-reliability/R5.7_Task_Report.md)。
## R6 [completed]
将“先恢复运行面再工程化”固化为 unidesk-subagent 的泛化主线规则:运行面恢复关键路径由主代理控制,低耦合工程化任务及时并行,恢复后继续完成工程化交付;将单 PR 收口、复用 guarded merge 内建 preflight、避免仅补 merge SHA 的重复 PR 和减少碎片化 GitHub 查询固化到 unidesk-gh 及相关 reference,并让 merge 默认摘要直接披露 merge commit 与 mergedAt,完成任务后将详细报告写入[任务报告](./details/agentrun-runtime-reliability/R6_Task_Report.md)。
@@ -0,0 +1,22 @@
# R6 任务报告
## 结论
已将“先恢复运行面,再工程化”固化为子代理调度的泛化原则:主代理冻结恢复标准并控制恢复关键路径,优先通过既有受控入口做最小、可逆、可验证的恢复;恢复不再阻塞低耦合工程化任务并行,运行面恢复后仍继续完成根因修复、PR、验证与治理收口。
已将减少重复交付固化到 GitHub skill 与 PR referencereview-plan 只建立索引并按需下钻,guarded merge 内建 preflight,功能、skill、MDTODO 报告和完成状态应在同一 PR 收口,禁止仅为补 merge SHA、mergedAt 或重复 closeout 创建第二个 PR。只有 merge 后产生新运行证据且用户明确要求写回 Git 时,才允许第二个 closeout PR。
已更新 GitHub merge 默认文本摘要,成功合并直接披露 mergeCommit 与 mergedAt,并取消机械 pr view 的 Next 提示。
## 验证
- unidesk-subagent skill quick_validate:通过。
- unidesk-gh skill quick_validate:通过。
- bun --check scripts/src/gh/pr-merge.ts:通过。
- bun scripts/cli.ts check --syntax-only11 项通过,0 项失败。
- renderer 定向验证:成功输出包含 mergeCommit=abc123 与 mergedAt=2026-07-14T10:00:00Z,且不包含 gh pr view。
- git diff --check:通过。
## 交付边界
本任务不修改运行面、不新增状态库或门禁。规则、CLI、MDTODO 报告与完成状态在同一功能 PR 中交付;merge 事实由 GitHub 和受控 merge 摘要保存,不回写本报告。
+2 -2
View File
@@ -100,14 +100,14 @@ export function ghHelp(): unknown {
"Commander brief ClaudeQQ defaults to private target 645275593 through backend-core /api/microservices/claudeqq/proxy; UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_* env vars can override target, base URL, timeout, and enabled state.",
"comment update/edit PATCHes /repos/{owner}/{repo}/issues/comments/{comment_id} and preserves the comment id/timeline; comment delete is supported because GitHub supports deleting issue comments, but routine wording fixes should use update/edit. issue/pr hard delete is unsupported and close is the lifecycle alternative.",
"PR files is the canonical compact changed-file/stat summary. It uses GitHub REST, returns bounded file rows, additions/deletions/changes when available, truncation metadata, and a next command for full details. Raw diff patches are not emitted by default; gh pr diff <number> --stat is a compatibility alias for the same JSON summary.",
"PR review-plan is the review-first bounded patch index. It uses GitHub REST PR files, prints changed files with additions/deletions/hunk counts/patch-line counts/default truncation flags, and emits one per-file gh pr diff --file drill-down command without creating a local worktree.",
"PR review-plan is the review-first bounded patch index. It uses GitHub REST PR files, prints changed files with additions/deletions/hunk counts/patch-line counts/default truncation flags, and emits per-file gh pr diff --file drill-down commands without creating a local worktree. Use only the drill-downs needed for review; do not mechanically expand every file.",
"PR diff --file reads one changed file patch from GitHub REST and prints only a bounded excerpt by default. Add --hunk N to inspect one hunk, --limit N to change displayed patch lines, or --full/--raw for explicit structured full-patch disclosure. --stat remains the no-patch file/stat compatibility path.",
"PR edit/update PATCHes /repos/{owner}/{repo}/pulls/{number} through REST only, never GitHub Projects Classic GraphQL/projectCards, and returns low-noise JSON with repo, PR number, changedFields, url, and body size/SHA metadata instead of echoing the full body.",
"PR view is the canonical bounded metadata summary; read remains a UniDesk compatibility alias. Human full-body reading uses `trans gh:/owner/repo/pr/<number> cat`, and targeted lookup uses the same route with `rg <pattern>`. `--json body`, `--full`, and `--raw` are explicit structured machine disclosure only. View/read retain positional numbers, GitHub URLs, owner/repo#number shorthand, compatible --number, REST closeout fields, and on-demand GraphQL closeout metadata.",
"PR preflight/closeout accept the same owner/repo#number shorthand as PR view/read so merge readiness checks do not require repeating --repo after a PR URL has already been normalized.",
"PR list does not fetch mergeability or statusCheckRollup; request those closeout fields with gh pr view <number> --json headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup.",
"PR preflight is a low-noise read-only closeout helper for explicit diagnosis only. It combines redacted auth capability, PR branch/state metadata, mergeability, mergeStateStatus, compact status check counts, and an explicit read-only policy. It is not a required step before gh pr merge. Use --full or --raw to include all fetched status contexts.",
"PR merge is the one-command guarded write path: it reads closeout metadata itself, retries GitHub UNKNOWN/null mergeability with YAML-configured exponential backoff, refuses non-open/draft/conflicting/non-clean/failed/pending PRs, then uses GitHub REST merge. It defaults to deleting the merged same-repo head branch, cleaning the matching local .worktree when clean, and fast-forwarding the local main worktree on the PR base branch; --sync-node NODE additionally runs mapped node source-workspace sync. Use --dry-run to see the exact merge and closeout plan without writing.",
"PR merge is the one-command guarded write path: it reads closeout metadata itself, retries GitHub UNKNOWN/null mergeability with YAML-configured exponential backoff, refuses non-open/draft/conflicting/non-clean/failed/pending PRs, then uses GitHub REST merge. Its success summary includes mergeCommit and mergedAt, so a routine follow-up pr view is unnecessary. It defaults to deleting the merged same-repo head branch, cleaning the matching local .worktree when clean, and fast-forwarding the local main worktree on the PR base branch; --sync-node NODE additionally runs mapped node source-workspace sync. Use --dry-run to see the exact merge and closeout plan without writing.",
],
};
}
+9 -2
View File
@@ -169,6 +169,12 @@ export function renderPrMergeTable(result: GitHubCommandResult, fallbackDetails?
: {};
const mergeMethod = result.method ?? result.mergeMethod ?? details.method;
const deleteBranch = result.deleteBranch ?? details.deleteBranch;
const mergeResult = isRecord(result.mergeResult) ? result.mergeResult : {};
const mergeCommitValue = pullRequest.mergeCommit;
const mergeCommit = isRecord(mergeCommitValue)
? mergeCommitValue.oid
: mergeCommitValue ?? mergeResult.sha;
const mergedAt = pullRequest.mergedAt;
const status = result.ok === true
? result.alreadyMerged === true
? "already-merged"
@@ -198,13 +204,14 @@ export function renderPrMergeTable(result: GitHubCommandResult, fallbackDetails?
` repo=${ghText(result.repo)} title=${ghShort(ghText(pullRequest.title), 96)}`,
` blockers=${blockers.length === 0 ? "-" : blockers.join(",")} pending=${pending.length === 0 ? "-" : pending.join(",")}`,
` retry=${retry.attempts !== undefined && retry.maxAttempts !== undefined ? `${ghText(retry.attempts)}/${ghText(retry.maxAttempts)} exhausted=${ghText(retry.exhausted)}` : "-"}`,
` mergeCommit=${ghText(mergeCommit)} mergedAt=${ghText(mergedAt)}`,
` branchDeletion=${ghText(branchDeletion.ok ?? branchDeletion.skippedReason ?? branchDeletion.attempted)}`,
` closeout localWorktree=${closeoutCell(localWorktree)} mainWorktree=${closeoutCell(mainWorktree)} nodeSyncs=${nodeSyncs.length === 0 ? "-" : nodeSyncs.map(closeoutCell).join(",")}`,
"",
"Next:",
];
if (result.ok === true && result.alreadyMerged !== true && result.dryRun !== true) {
lines.push(` bun scripts/cli.ts gh pr view ${ghText(result.number ?? details.number)} --repo ${ghText(result.repo)}`);
if (result.ok === true && result.dryRun !== true) {
lines.push(" no follow-up required; merge facts are included in Summary.");
} else {
lines.push(` ${prMergeRetryCommand(ghText(result.repo), result.number ?? details.number, mergeMethod, deleteBranch)}`);
lines.push(` bun scripts/cli.ts gh pr preflight ${ghText(result.number ?? details.number)} --repo ${ghText(result.repo)} --full`);