现状口径:/keil-build 与 /serial-boot-test 是 DSH 会话的技能调用名, 不是 shell 命令;「名 + 命令行参数」的命令形态改为可执行体 run_boot_test.py 的真实调用。 - AGENTS.md:别名说明重写为「技能调用名不是 shell 命令」,两条目改 「编译(/keil-build)」「串口回归(/serial-boot-test)」 - keil-build / serial-boot-test 两 skill 调用名与示例同步;task-worktree 方案文档去掉 @build/@test 惯例从句;@task 保留(不在点名范围) - 123 文件现状陈述全改;台架捕获、变更记录/时间线、rename 映射输入 等 41 行历史证据保留旧名 - gen_memory_svg.py 6 处措辞随改,3 张 SVG 产物同批重跑 - 顺手补并行方案同行漏网计数 51→52 钩子 验证:提交期快钩子对 123 文件实跑全 Passed;check_doc_links --all rc=0(3038 处、无法判定 0);check_mermaid --all rc=0;YAML 解析 52 钩子不变;skill-size rc=0;py_compile 全过;字节卫生零漂移。
23 KiB
name, description, whenToUse
| name | description | whenToUse |
|---|---|---|
| task-worktree | 并行开发的空间管理(git worktree 生命周期;`@task` 只是文档别名不是可执行命令,DSH 会话显式调用 `/task-worktree`):开任务树与分支、联接共享 `.venv` 与模拟器 SDK、build/bench 独占锁、并发上限与腾位(A)、放不掉的锁(B)、`done` 是合并点不是查看器与补绿链(C)、主树被别的会话弄脏时的手工合并(C2)、门禁指标异常的净室归因(D)、回收非登记树与悬挂台账(E)、与 DSH worktree 插件共存及联接分级(F)、树内跑门禁与消息禁词(G)、树内自查与合并后补绿命令化(`precheck` / `postmerge`)。规格 = docs/环境与部署/并行任务工作树方案.md;深规程全文在 reference/。 | 要为新任务开隔离空间、同时推进多个任务、把任务合回主干或回收工作树;或撞到 `new` 报「已有 8 个活跃任务(上限 8)」、`lock release` 报「任务 X 不存在(缺台账)」、`done` 报「门禁未全绿」或「主工作树不干净」而那份脏改动是别的会话/插件留的、两个任务抢 COM6 或 UV4、并集合并残留;或在任务树里跑门禁写输出报 ENOENT、`--all` 的问题全在 `simulator/SDL2` 联接下;或门禁指标异常增长查不出哪个文件造成;或要回收 `list` 台账看不到的树、清 branch/path 都已不存在的悬挂台账;或要确认插件树里 `.venv` 与 `simulator/SDL2` 哪个必须补;或要改并行上限这个数字并同步全仓口径。 |
task-worktree — 多任务并行开发空间(@task 仅为文档别名;DSH 显式调用 /task-worktree;可执行形态见「固定命令」)
- 执行体 =
.dsh/skills/task-worktree/scripts/task_worktree.py(纯标准库;拷整个 skill 目录即可换项目用)。 - 深规程全文在 reference/ 下:做 C2 / D / E / F / G 任何一条之前先把对应那篇读完——本文只留判得出来「要不要读」的判据。
- 为什么这么拆:本文件受 dsh-mnemon 写侧上限 65536 字节(
skill-size钩子 49152 告警;实测 72,601 字节的技能照常可读可提交,只是自进化每次「生成草案」被拒、永久卡在「生成中」)→ 新增深规程进 reference/,本文只加判据。瘦身前 frontmatter 原文 → frontmatter-history.md。
症状 → 去哪一节
| 现场 / 目标 | 去 |
|---|---|
| 开新树 / 看全景 / 追平主干 / 放弃任务 | 固定命令、典型流程 |
new 报「已有 8 个活跃任务(上限 8)」 |
A |
lock release 报「任务 X 不存在(缺台账)」,锁照旧挂着 |
B |
done 做了什么、红了怎么补绿、能不能反悔、怎么算「真完成」 |
C |
done 报「主工作树不干净」,而那份脏是别的会话/插件留的 |
C2 → merge-dirty-main-tree.md |
| 门禁指标异常增长要逐文件归因(主树全程只读) | D → clean-room-measure.md |
list 台账里没有的树要回收 / detached / 有在飞内容 / 悬挂台账 |
E → reclaim-unregistered.md |
插件树缺联接、锁归属 pw:<name>、「有插件为何还自持生命周期」 |
F → dsh-plugin-bridge.md |
树内写门禁输出 ENOENT、--all 假红、写提交/合并信息、字节体检 |
G → gates-in-tree.md |
| 收工前树内自查、或合并后体积凭证/台账红了想一条命令补绿 | precheck / postmerge(固定命令表 + C 节第 5~6 步) |
退出码读到 0 其实是 2、tail 之后误判「有冲突」 |
读输出 |
谁不许跑 done/abort、清场前必做什么、任务期哪些文件不许改 |
铁律 1–9 |
| 收工前最后一遍自查(各节该留什么证据)/全部实测坑 | verification.md / pitfalls.md |
固定命令
PY=".venv/Scripts/python.exe"; TW=".dsh/skills/task-worktree/scripts/task_worktree.py"
"$PY" "$TW" <子命令> [参数] # PowerShell:& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" <子命令>(cwd = 仓库根或任意子目录)
| 命令 | 作用 |
|---|---|
new <ID> <slug> |
建分支 task/<ID>-<slug> + 工作树 + 共享联接 + 台账,打印新空间路径 |
list |
分支 / 落后领先 / 脏文件数 / 台架状态 + 两把锁归属。只遍历台账 → 非登记树看不见(E 节) |
status <ID> |
未提交清单、相对基线漂移、台架记录、已建联接 |
sync <ID> |
在任务树里 rebase 追平主干,把冲突留在任务分支上 |
| `lock acquire | release |
| `plugin list | adopt |
done <ID> |
预演 → 合入主干 → 并集残留审计 → 重生成产物 → 门禁 → 清场 |
abort <ID> |
工作树 + 分支一起删,永久丢弃 |
precheck |
树内自查(只读):BOM 漂移 / CR / U+FFFD + docs 逐档门禁 + --msgfile --ban 消息检查;顺手补 ci-artifacts/(G 节机械项) |
postmerge |
主树合并后补绿:体积凭证自动诊断(新鲜跳过;红→先短链 --update,只读复检真漂了才升全链)+ check_doc_links --all + 列待提交文件,不代提交 |
- 退出码
0成功 /1操作失败 /2前置或环境错误 /3用法——只有不接管道才读得到(→ 读输出)。 new:--base(默认v2-on-ui)·--needs-bench(无台架 PASS 则done拒合)·--lean(跳 LFS smudge,只省磁盘不省时间)·--no-bootstrap(不建联接)·--force(只跳过并发上限检查,不解决谁让位 → A)done:--dry-run·--skip-build·--skip-gates·--force。⚠--dry-run只保护预演段、不保护清场段(实测:对已合入的分支它照样摘联接、删分支、清台账;且它的前置也要抢build锁)→ 用完复验git worktree list,细节 → 陷阱清单。lock:--task(acquire 必填)·--wait <秒>·--ttl <小时>(默认build=1h /bench=6h,超期视为陈旧、下次 acquire 自动接管)·--note·--bench-pass·--force。⚠--note只在 acquire 写进 payload,release 时被忽略 → 验收证据写主干提交说明或文档。- 环境变量:
DTU_WT_ROOT(默认仓库同级_wt/)·DTU_TASK_BASE(默认基线)·DTU_INTEGRATION_WORKTREE(集成点,用于隔离演练)。
典型流程
"$PY" "$TW" new N21 dhara-isfree --needs-bench # 把新会话工作目录切到打印出来的 _wt/N21-dhara-isfree/
"$PY" "$TW" lock acquire bench --task N21 # 烧录 / /serial-boot-test / 板级套件
"$PY" "$TW" lock release bench --task N21 --bench-pass
"$PY" "$TW" done N21 # 回主工作树(铁律 1);先 --dry-run 读全量
读输出:管道与截断(两起误判的共同根因)
| tail -N之后的退出码是tail的,恒 0 → 脚本的 1/2 全被吞(实测三条「rc=0」真码都是 2)。判据先看文本([ok]/!!/前置/环境错误:/失败:),要真码就不接管道或取${PIPESTATUS[0]}。done的输出别tail -2:管道下 python stdout 块缓冲、git 子进程 stderr 不缓冲,两股流错位 → 只剩尾部那句「--dry-run:到此为止…」加带conflict字样的 stderr 片段,据此误判「有冲突」。dry-run 全文 5~8 行,读全量。
分步操作(节号 A–G/C2、各节步骤号与铁律编号被 reference/ 与 docs 引用,不许重排)
A. new 撞并发上限 → 腾位
- 认原文
前置/环境错误:已有 8 个活跃任务(上限 8);先 done/abort,或加 --force(exit 2)。「活跃」=_wt/.state/<ID>.json在且台账path目录还在 → 手工删目录不占名额、但留悬挂台账(E 第 6 步)。 $TW list定谁让位:已完成的先done(合并回收一步不省)。- 无人认领、零提交、未 claim 的空闲树 →
abort <ID>;有未提交或有提交时脚本会拒,确认可弃才--force。 - 真要第 9 棵才
--force:并行度上去,冲突面与台架排队一起上去(每棵磁盘代价见铁律 6)。 - 别用手工
git worktree remove给登记任务腾位:台账不删(同 ID 报「任务已存在」)、又绕过「先摘联接」(铁律 5),有让 git 递归进联接删掉主树.venv的风险。要回收的是非登记树(list里没有它)→ E 节。 - 复验:
list条目数 ==ls _wt目录数,再new。
B. 非登记持有者的锁放不掉
症状:lock release bench --task acceptwin --bench-pass(带 --force 同)报 前置/环境错误:任务 acceptwin 不存在(缺台账 _wt/.state/acceptwin.json),锁照旧挂着。根因:release 带 --bench-pass 就先 load_state(task),而 acquire 侧不查台账 → 「释放」被「记账」挡死;锁没放,done 的 release_stale_locks() 对未到 TTL 的活锁判失败 → 一个没登记的名字挡住所有任务收口。
2026-10-29 已修:无台账降级为 [warn] …跳过 PASS 记录,仅释放锁,不再需要手写占位台账。
$TW lock status认现场(持有者 / pid / 已持有 / TTL)——别对同一句报错反复重试。$TW lock release <build|bench> --task <NAME> [--bench-pass]直接放;PASS 记不上会 warn,锁照放。- PASS 后面还要用(
done只认台账bench_pass)→ 从一开始就用已new的 ID 占锁,或把结论写进主干提交/文档。插件树pw:<name>天然无台账 → F 节。 - 最后手段:锁 =
_wt/.state/locks/<name>.lock的单行 JSON(name/task/pid/host/epoch/at/note/ttl),release就是 unlink 它——手工删 = 不记 PASS 的强制释放,前面各步都不可用才做。早期为绕坑手写的占位台账现在有害(既占上限名额又被list当任务)→ E 第 6 步备份后删。
C. done <ID>:它是合并点,不是查看器
- 合并前确认属主提交完:
$TW status <ID>看未提交与领先数,跨会话再问属主一句。实测教训:属主还剩「2/2」没提交就done→ 只含 1/2 的分支进了主干,只能另开一棵树补后半(自己的时序错,不是分支问题)。 - 先
--dry-run且读全量。判据两行:[ok] 前置检查通过(领先 <base> 共 N 个提交)+[ok] 无冲突,预演结果树 <sha>;N 必须等于属主应交的提交数,对不上回第 1 步。 - 正式
$TW done <ID>——必须在主工作树(铁律 1)。 - 见
!! 门禁未全绿:工作树与分支**保留**,未清场时,紧跟的主干已含合并提交 <sha>说明合并已落主干;先定位哪条钩子红([SUMMARY] static: pre-commit hooks -- passed=N failed=M),最常态是.map滞后 → 第 5 步。- ✅ 走提示 a):主工作树修正后追加提交。
- ❌ 绝不用 b) 的
reset --hard <sha>——那是丢弃已入库的合并提交,且reset --hard是项目级 Never(连未提交工作一起丢)。提示 b 是列出的可能性,不是建议。
- 补绿固定链(顺序即因果,全在主工作树,逐条不接管道看退出码):
"$PY" scripts/check_mem_numbers.py --write # 台账受管区 ↔ 新 .map 对齐
"$PY" scripts/ram_baseline.py --write # RAM 快照 / 92% 预算台账
"$PY" scripts/ram_baseline.py --update # 趋势基线追加一行
"$PY" scripts/check_test_coverage.py --update-baseline # 覆盖率基线(新单测 / 新套件时必刷)
"$PY" scripts/check_doc_links.py --all # 文档链接与章节引用
git add <上面刷出来的基线/快照文件> # 显式列文件,别 -A 把别人在飞的改动一起提交
git commit -F <UTF-8 无 BOM 的正文文件> # 提交属 Ask first(AGENTS.md 边界)
为什么必然要补 → 陷阱「done 的门禁跑在 /keil-build 之前」。合并解过冲突时,合并提交期 pre-commit 只查冲突文件(→ G 节第 3 条)→ 落主干后必须另跑全量。
注释/文档-only 但碰了 .c/.h 的短链:先比 Program Size 与合并前是否逐项相同——相同就只 ram_baseline.py --update 重写体积凭证(scripts/size_attestation.json 跟着补提交笔走),别 --write 动受管区(等于给在飞分支造冲突面);数字变了才走上面全链。已命令化:"$PY" "$TW" postmerge——新鲜直接跳过;红则先短链,再用只读 check_mem_numbers 复检裁定是否升全链;末尾跑 --all 并列出待提交文件(不代提交)。上面的手工全链仍是权威兜底。
6. 重跑 done 前主树必须干净,否则 失败:主工作树不干净(…)——合并会把未提交改动卷进来 → 第 5 步产物先提交。分支已并时重跑打印 [i] task/<ID>-<slug> 已合入 <base>,跳过合并,直接清场——该路径在门禁之前就返回,不会替你复绿:上一次门禁没跑到的尾巴(主树 ci_local.py + check_doc_links.py --all;体积/文档那半可 postmerge 一把跑)必须自己补。主树脏且那份脏不是你的 → C2。
7. 清场后再敲一次 done <ID> 得 前置/环境错误:任务 <ID> 不存在(缺台账 …json)(exit 2)——不是失败:清场最后一步 unlink 了台账。判完成以 git log 为准。
C2. done 被「主工作树不干净」挡住,而那份脏是别的并发会话留的
- 不许对它做 stash / checkout / reset / 提交 / 删除(项目级 Never,你不知它写没写完)。
done要求主树干净 → 合并与回收一起被挡,那是保护不是工具坏了。 - 正解六步,编号与 reference/merge-dirty-main-tree.md 第 1~6 步一一对应(每步的确切命令与判据在那篇):取证(mtime + diff 定性有无交集,顺带
worktree list看有无别人中途新建的树)→ 预演git merge-tree --write-tree <base> <task>(打出结果树 sha 且 RC=0)→ 主树手工合并git merge --no-ff --no-edit -F ci-artifacts/merge-msg-<slug>.txt <task>→ 对账(合并后 blob id 与 stat 同合并前存值逐项一致)→ 备份台账cp <ID>.json <ID>.json.state-bak后让done自己摘联接拆树 → 复验worktree list+lock status。 - blob id 变了 = stash pop 半冲突:立刻停止收工,把两份 blob id 与 diff 交给该文件属主,绝不
checkout抹平。 - 报告要点名:绕开
done的理由 + 补回它的证据(预演 sha 与 RC、blob 对账、主树全库check_doc_links.py --all、两把锁空闲)+ 那份外部脏文件与中途冒出来的第二棵树(谁的、你没动它的证据)。合并信息也过 G 的禁词自检;「这份脏通常是谁」见陷阱清单。
D. 门禁指标异常增长 → 净室逐文件归因(主树全程只读)
三条判据:仓外 detached 树当净室、一次只注入一份在飞文件、与 HEAD 基线逐文件差分;主树前后 git status --porcelain 逐行一致才算「全程只读」。全文 → reference/clean-room-measure.md。
E. 回收非登记工作树(list 为空 / detached / 树里有在飞或未跟踪内容)
三条不可跳过:① 逐文件内容三桶归类(与主线工作区/HEAD 比对;「很久没动」不是判据)② 动手前跑只读 reparse 预扫,拿到 REPARSE: none 才许 git worktree remove(铁律 5)③ 唯一未合并残留先备份再 --force,拆完复验 + 清悬挂台账。绝不跑 done/abort(铁律 9)。全文(第 1~6 步 + 两份探针脚本)→ reference/reclaim-unregistered.md。
F. 与 DSH worktree 插件(/worktree、worktree_create)共存
| 联接 | 级别 | 依据 | 缺了会怎样 | 逃生门 |
|---|---|---|---|---|
simulator/SDL2 |
硬需求 | simulator/CMakeLists.txt:12 的 set(SDL2_DIR …) 非 CACHE,include / lib/x64 / SDL2.dll 全从它派生 |
build_sim.bat 当场失败 |
无 |
.venv |
软需求 | scripts/ci_local.py 有 DTU_PRECOMMIT_PYTHON + 五级解释器候选 |
静默落机器级解释器(缺 fontTools → generated-fresh 假红) |
环境变量 |
plugin adopt两条都补只是因为便宜,不代表两条都硬。插件树占锁记pw:<name>、回收仍归 /worktree(本 skill 不删插件树,插件也不删_wt/)。「有插件为何还自持生命周期」=先读插件包(npm root -g)再 grep 三个概念名报命中数,插件侧零命中就是理由——取证姿势与答案边界 → reference/dsh-plugin-bridge.md F-2。
G. 任务树内跑门禁:落点、--all 假红、消息禁词
- 新树没有
ci-artifacts/(被 ignore → 不检出):重定向/写文件直接ENOENT且整段程序零执行 → 落盘前先单独一条mkdir -p ci-artifacts;树内输出一律树内相对路径(cd进树后../_wt/x.txt落到_wt/_wt/)。一次性产物不进仓库根。 - 文档门禁树内按单文件跑;全库
--all留到合并后在主树跑——树内simulator/SDL2是同一份仓库的联接,--all把它一起扫成假红(实测 RC=1 而问题全在联接下、要改的文档一条没有)。非要树内跑:① 主树同命令拿基线 ② 过滤联接下的问题行 ③ 剩下的逐行比。只做一步就是误判。 - 合并提交期 pre-commit 只查冲突文件(日志首行
[INFO] Checking merge-conflict files only.,其余全(no files to check) Skipped)→ 合并落主干后必须另跑全量scripts/ci_local.py。 - 跨层改动拆不成「按层分笔」:钩子看到的世界 = HEAD + 本笔 staged(未暂存被 stash;未跟踪文件不被 stash 但会拖红同一棵树上别人的提交)→ 登记文件 / 生成物 / 互锁引用的文档分到他笔,本笔必被整仓门禁拦(
doc-links/registration-check/generated-fresh/mem-numbers)。判据:一笔提交必须自洽。 - 跑门禁前字节体检:
lines / bytes / BOM / CR / U+FFFD / 三连续空行 / 行尾空格 / 末行单个 LF+ Markdown 表格每行列数一致性(单元格内竖线写成反斜杠竖线,否则被当分隔符断列)。坏行未必是本次改动,先定归属。doc-links/no-mojibake只覆盖编码那半。机械项已命令化:"$TW" precheck(BOM 漂移 / CR / U+FFFD + docs 逐档门禁,只读);列数一致性与归属判定仍人做。 - 提交/合并信息一律
-F读文件(UTF-8 无 BOM、标题 ≤100、正文每行 ≤80,放ci-artifacts/);清退类任务加禁词自检:git log -1 --pretty=%B | grep -c '<关键词>'必须是 0。未推送前发现消息写错 →--amend -F就地修,别追加「更正」提交(等于把禁词写回历史)。BOM/CRLF/行长/禁词一次查:"$TW" precheck --msgfile <文件> --ban <词1> --ban <词2>…。 - 全文(第 1~7 条,含 stash 机制与四道整仓门禁现场)→ reference/gates-in-tree.md。
铁律
done只能在主工作树执行——主干分支检出在主树,git 不允许同一分支两处检出。done不 push:它结束于本地合入 + 门禁全绿;推送需用户明确许可。build与bench都必须串行(UV4 不能两个实例同时-b;COM6、ST-LINK、设备 IP192.168.1.100各一份)。build锁由 /keil-build 自持自释(keil_build.py)→ 调用方不要手工 acquire/release;bench锁必须手工 acquire → 烧录 //serial-boot-test→ release--bench-pass。非登记持有者怎么放 → B 节;插件树 → F 节。- 单写者文档:
docs/任务规划/(整个文件夹)、AGENTS.md任务表块、MDK-ARM/*.uvprojx、.pre-commit-config.yaml、内存快照 —— 任务期不在任务树里改,留到done统一处理;用户显式豁免时(在任务树写、由 done 合回)要在交付报告里点名这条例外。 - 清场必须先摘联接:脚本先
os.rmdir掉.venv/simulator/SDL2联接再git worktree remove。手工拆树前必跑 E 第 3 步预扫拿到REPARSE: none——否则 git 可能递归进联接把主树.venv一起删掉。 - 活跃任务硬上限 8 = 脚本常量
MAX_CONCURRENT(task_worktree.py:30)——唯一事实源:报错文案与new的插件树提示都插值它,AGENTS.md / 方案文档 / frontmatter / A 节的数字全是引用同步。改这个数字 → 全形态扫描走stale-doc-count-sweepskill + 隔离小仓验证上限真生效;每棵磁盘代价按实测口径重述、别照抄旧数(细节 → 验证清单末条)。满了 exit 2 → A;悬挂台账 → E 第 6 步。 - 工作树放仓库同级
_wt/,绝不在仓库内部(否则污染git status、--all-files与全库搜索)。唯一例外 = 插件写死的仓内.dsh-worktrees/(改不了,已用预置 .gitignore + 三个全库扫描排除兜住 → F 节)。 done是合并点:属主没提交完就别跑;合并提交进了主干就只用「主树补提交」收尾,不用reset --hard反悔 → C 节。- 非登记 / detached 的树绝不跑
done/abort:两者都以台账为前置(无台账 exit 2),带残留台账时abort --force会绕过内容保全把别人在飞的改动连树一起删 → E 节。
陷阱与验证(收工前各扫一遍)
- reference/pitfalls.md:全部实测坑逐条(含
--dry-run不保护清场段、门禁跑在/keil-build之前、未跟踪文件在git log与合并里都不留痕、并集合并残留、新树从未构建 → 体积类门禁必然红、注释-only 也刷红体积凭证、「已合入」快路径在门禁前返回不替你复绿、--force只覆盖四类检查、联接不是 symlink(core.symlinks=false)、锁不绑进程存活、文件工具先读后改按绝对路径记账、中文门禁回显乱码但 rc 可信)。收工前扫一眼。 - reference/verification.md:每节要留的证据(预演 sha +
MT_RC=0、blob id 对账、两把锁空闲、禁词计数 0、REPARSE: none、改上限数字后的全形态扫描)。
与其它 skill 的衔接
- 构建 =
keil-build· 烧录/回读 =stlink-flash· 串口闭环 =serial-boot-test(真实 hex 路径以.serialtest/config.json的 hex 键为准——实测MDK-ARM/Objects/STM32F407-Demo.hex不存在,拷错 = 烧旧镜像) - 新
.c双端注册 =new-source-file· RAM/.map 台账 =mem-budget· UI 回归 =ui-verify· 计数同步 =stale-doc-count-sweep· 卡死的技能台账行 =mnemon-skill-ledger-stuck-repair - DSH worktree 插件(
/worktree/worktree_create,仓内.dsh-worktrees/)与本 skill 的分工、桥接命令、锁归属 → F 节 · 完整机制与目录分配 →docs/环境与部署/并行任务工作树方案.md