Files
STM32F4-Base/.dsh/skills/task-worktree/SKILL.md
T
wandering1 ecf9d3ad3d docs(task-worktree skill): 修 @T3@ 围栏腐坏,正文瘦身并把细节外置 reference/
围栏腐坏是真 bug:@T3@ 是被误提交进仓的代码围栏占位符,Markdown 渲染出
来就是一行 @T3@ 加裸命令。git log -S 定位:842d711e(2026-10-28)
SKILL.md 还有 18 个真三反引号围栏行、0 个占位符;21c68521(2026-10-29
插件桥接那笔)起变 0 围栏 / 34 个占位符。全仓只有本技能 6 个文件有此
病,draw-diagram / keil-build / ui-verify 等都用真围栏。本轮七个文件
占位符清零、围栏全部成对。

信息密度:SKILL.md 28,139 → 23,087 B(−18%,占插件写侧上限 65536 的
35%,给自进化留头量)。同一句外置理由在 C2/D/E/F/G/陷阱/验证 重复 7 次
→ 0;frontmatter 与 F 节写了两遍的插件桥接说明收进 F 节;「这不是免费
的」「挡给你看的保护」这类修辞删掉。删掉的细节全部落进对应 reference
篇——skill-size 钩子会校验指针不悬空:现 26 处链接 / 8 篇目标 / 悬空 0,
每篇都有入口。

锚点未重排:节号 A–G/C2、各节步骤号、铁律编号被 reference/ 反向引用
(铁律 3/5/7/9 与 A 第 1、5 步,C 第 5~7 步,E 第 1~6 步均结构性存在)。
只在 SKILL.md 出现的 PIPESTATUS 教训保留(reference/ 里没有,删了就丢)。

门禁:check_skill_size.py rc=0(17 个 SKILL.md 全在写侧上限内、无悬空
指针);七文件均无 BOM / CR / U+FFFD、单个尾换行。
2026-10-30 00:31:36 +08:00

22 KiB
Raw Blame History

name, description, whenToUse
name description whenToUse
task-worktree 并行开发的空间管理(git worktree 生命周期,短命令 `@task`):开任务树与分支、联接共享 `.venv` 与模拟器 SDK、build/bench 独占锁、并发上限与腾位(A)、放不掉的锁(B)、`done` 是合并点不是查看器与补绿链(C)、主树被别的会话弄脏时的手工合并(C2)、门禁指标异常的净室归因(D)、回收非登记树与悬挂台账(E)、与 DSH worktree 插件共存及联接分级(F)、树内跑门禁与消息禁词(G)。规格 = docs/环境与部署/并行任务工作树方案.md;深规程全文在 reference/。 要为新任务开隔离空间、同时推进多个任务、把任务合回主干或回收工作树;或撞到 `new` 报「已有 8 个活跃任务(上限 8)」、`lock release` 报「任务 X 不存在(缺台账)」、`done` 报「门禁未全绿」或「主工作树不干净」而那份脏改动是别的会话/插件留的、两个任务抢 COM6 或 UV4、并集合并残留;或在任务树里跑门禁写输出报 ENOENT、`--all` 的问题全在 `simulator/SDL2` 联接下;或门禁指标异常增长查不出哪个文件造成;或要回收 `@task list` 看不到的树、清 branch/path 都已不存在的悬挂台账;或要确认插件树里 `.venv` 与 `simulator/SDL2` 哪个必须补;或要改并行上限这个数字并同步全仓口径。

task-worktree — 多任务并行开发空间(短命令 @task)

  • 执行体 = .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
@task list 里没有的树要回收 / detached / 有在飞内容 / 悬挂台账 E → reclaim-unregistered.md
插件树缺联接、锁归属 pw:<name>、「有插件为何还自持生命周期」 F → dsh-plugin-bridge.md
树内写门禁输出 ENOENT、--all 假红、写提交/合并信息、字节体检 G → gates-in-tree.md
退出码读到 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> 工作树 + 分支一起删,永久丢弃
  • 退出码 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        # 烧录 / @test / 板级套件
"$PY" "$TW" lock release bench --task N21 --bench-pass
"$PY" "$TW" done N21                             # 回主工作树(铁律 1);先 --dry-run 读全量

读输出:管道与截断(两起误判的共同根因)

  1. | tail -N 之后的退出码是 tail 的,恒 0 → 脚本的 1/2 全被吞(实测三条「rc=0」真码都是 2)。判据先看文本([ok] / !! / 前置/环境错误: / 失败:),要真码就不接管道或取 ${PIPESTATUS[0]}。
  2. done 的输出别 tail -2:管道下 python stdout 块缓冲、git 子进程 stderr 不缓冲,两股流错位 → 只剩尾部那句「--dry-run:到此为止…」加带 conflict 字样的 stderr 片段,据此误判「有冲突」。dry-run 全文 5~8 行,读全量。

分步操作(节号 A–G/C2、各节步骤号与铁律编号被 reference/ 与 docs 引用,不许重排)

A. new 撞并发上限 → 腾位

  1. 认原文 前置/环境错误:已有 8 个活跃任务(上限 8);先 done/abort,或加 --force(exit 2)。「活跃」= _wt/.state/<ID>.json 在且台账 path 目录还在 → 手工删目录不占名额、但留悬挂台账(E 第 6 步)。
  2. $TW list 定谁让位:已完成的先 done(合并回收一步不省)。
  3. 无人认领、零提交、未 claim 的空闲树 → abort <ID>;有未提交或有提交时脚本会拒,确认可弃才 --force。
  4. 真要第 9 棵才 --force:并行度上去,冲突面与台架排队一起上去(每棵磁盘代价见铁律 6)。
  5. 别用手工 git worktree remove 给登记任务腾位:台账不删(同 ID 报「任务已存在」)、又绕过「先摘联接」(铁律 5),有让 git 递归进联接删掉主树 .venv 的风险。要回收的是非登记树(list 里没有它)→ E 节。
  6. 复验: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 记录,仅释放锁,不再需要手写占位台账。

  1. $TW lock status 认现场(持有者 / pid / 已持有 / TTL)——别对同一句报错反复重试。
  2. $TW lock release <build|bench> --task <NAME> [--bench-pass] 直接放;PASS 记不上会 warn,锁照放。
  3. PASS 后面还要用(done 只认台账 bench_pass)→ 从一开始就用已 new 的 ID 占锁,或把结论写进主干提交/文档。插件树 pw:<name> 天然无台账 → F 节。
  4. 最后手段:锁 = _wt/.state/locks/<name>.lock 的单行 JSON(name/task/pid/host/epoch/at/note/ttl),release 就是 unlink 它——手工删 = 不记 PASS 的强制释放,前面各步都不可用才做。早期为绕坑手写的占位台账现在有害(既占上限名额又被 list 当任务)→ E 第 6 步备份后删。

C. done <ID>:它是合并点,不是查看器

  1. 合并前确认属主提交完:$TW status <ID> 看未提交与领先数,跨会话再问属主一句。实测教训:属主还剩「2/2」没提交就 done → 只含 1/2 的分支进了主干,只能另开一棵树补后半(自己的时序错,不是分支问题)。
  2. 先 --dry-run 且读全量。判据两行:[ok] 前置检查通过(领先 <base> 共 N 个提交) + [ok] 无冲突,预演结果树 <sha>;N 必须等于属主应交的提交数,对不上回第 1 步。
  3. 正式 $TW done <ID>——必须在主工作树(铁律 1)。
  4. 见 !! 门禁未全绿:工作树与分支**保留**,未清场 时,紧跟的 主干已含合并提交 <sha> 说明合并已落主干;先定位哪条钩子红([SUMMARY] static: pre-commit hooks -- passed=N failed=M),最常态是 .map 滞后 → 第 5 步。
    • ✅ 走提示 a):主工作树修正后追加提交。
    • ❌ 绝不用 b) 的 reset --hard <sha>——那是丢弃已入库的合并提交,且 reset --hard 是项目级 Never(连未提交工作一起丢)。提示 b 是列出的可能性,不是建议。
  5. 补绿固定链(顺序即因果,全在主工作树,逐条不接管道看退出码):
"$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 的门禁跑在 @build 之前」。合并解过冲突时,合并提交期 pre-commit 只查冲突文件(→ G 节第 3 条)→ 落主干后必须另跑全量。 6. 重跑 done 前主树必须干净,否则 失败:主工作树不干净(…)——合并会把未提交改动卷进来 → 第 5 步产物先提交。分支已并时重跑打印 [i] task/<ID>-<slug> 已合入 <base>,跳过合并,直接清场。主树脏且那份脏不是你的 → 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 只覆盖编码那半。
  • 提交/合并信息一律 -F 读文件(UTF-8 无 BOM、标题 ≤100、正文每行 ≤80,放 ci-artifacts/);清退类任务加禁词自检:git log -1 --pretty=%B | grep -c '<关键词>' 必须是 0。未推送前发现消息写错 → --amend -F 就地修,别追加「更正」提交(等于把禁词写回历史)。
  • 全文(第 1~7 条,含 stash 机制与四道整仓门禁现场)→ reference/gates-in-tree.md。

铁律

  1. ** done 只能在主工作树执行**——主干分支检出在主树,git 不允许同一分支两处检出。
  2. ** done 不 push**:它结束于本地合入 + 门禁全绿;推送需用户明确许可。
  3. ** build 与 bench 都必须串行**(UV4 不能两个实例同时 -b ;COM6、ST-LINK、设备 IP 192.168.1.100 各一份)。 build 锁由 @build 自持自释( keil_build.py )→ 调用方不要手工 acquire/release; bench 锁必须手工 acquire → 烧录/ @test → release --bench-pass 。非登记持有者怎么放 → B 节;插件树 → F 节。
  4. 单写者文档: docs/任务规划/ (整个文件夹)、 AGENTS.md 任务表块、 MDK-ARM/*.uvprojx 、 .pre-commit-config.yaml 、内存快照 —— 任务期不在任务树里改,留到 done 统一处理;用户显式豁免时(在任务树写、由 done 合回)要在交付报告里点名这条例外。
  5. 清场必须先摘联接:脚本先 os.rmdir 掉 .venv / simulator/SDL2 联接再 git worktree remove 。手工拆树前必跑 E 第 3 步预扫拿到 REPARSE: none ——否则 git 可能递归进联接把主树 .venv 一起删掉。
  6. 活跃任务硬上限 8 = 脚本常量 MAX_CONCURRENT(task_worktree.py:30)——唯一事实源:报错文案与 new 的插件树提示都插值它,AGENTS.md / 方案文档 / frontmatter / A 节的数字全是引用同步。改这个数字 → 全形态扫描走 stale-doc-count-sweep skill + 隔离小仓验证上限真生效;每棵磁盘代价按实测口径重述、别照抄旧数(细节 → 验证清单末条)。满了 exit 2 → A;悬挂台账 → E 第 6 步。 MAX_CONCURRENT ( task_worktree.py:30 )——唯一事实源**:报错文案与 new 的插件树提示都插值它,AGENTS.md / 方案文档 / frontmatter / A 节的数字全是引用同步。改这个数字 → 全形态扫描走 stale-doc-count-sweep skill,并另建隔离小仓验证上限真生效;每棵磁盘代价按实测口径重述(du 约 83 MB / --lean 约 45 MB,方案文档 §4)别照抄旧数。满了 new 直接 exit 2 → A 节;悬挂台账 → E 第 6 步。
  7. 工作树放仓库同级 _wt/ ,绝不在仓库内部(否则污染 git status 、 --all-files 与全库搜索)。唯一例外 = 插件写死的仓内 .dsh-worktrees/ (改不了,已用预置 .gitignore + 三个全库扫描排除兜住 → F 节)。
  8. ** done 是合并点**:属主没提交完就别跑;合并提交进了主干就只用「主树补提交」收尾,**不用 reset --hard ** 反悔 → C 节。
  9. **非登记 / detached 的树绝不跑 done / abort **:两者都以台账为前置(无台账 exit 2),带残留台账时 abort --force 会绕过内容保全把别人在飞的改动连树一起删 → E 节。

陷阱与验证(收工前各扫一遍)

  • reference/pitfalls.md: done 的门禁跑在 @build 之前 · 门禁 = pre-commit run --all-files
  • reference/pitfalls.md:全部实测坑逐条(含 --dry-run 不保护清场段、门禁跑在 @build 之前、未跟踪文件不留痕、并集合并残留、新树从未构建 → 体积类门禁必然红)。收工前扫一眼。 git log 与合并里都不留痕 · 并集合并残留 · 新树从未构建 → 体积类门禁必然红 · --force 只覆盖四类检查 · 联接不是 symlink( core.symlinks=false )· 锁不绑进程存活。
  • reference/verification.md:每节要留的证据(预演 sha +
  • reference/verification.md:每节要留的证据(预演 sha + MT_RC=0、blob 对账、两把锁空闲、禁词计数 0、REPARSE: none、改上限数字后的全形态扫描)。 、blob id 对账、两把锁空闲、禁词计数 0、 REPARSE: none …)。

与其它 skill 的衔接

  • 构建 = keil-build(@build)· 烧录/回读 = stlink-flash · 串口闭环 = serial-boot-test(真实 hex 路径以 .serialtest/config.json 的 hex 键为准,拷错 = 烧旧镜像) keil-build ( @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