Files
STM32F4-Base/.dsh/skills/task-worktree/SKILL.md
T
wandering1 f08b12a46c docs(技能渐进展开): 五个技能正文逐字外置到 reference/,主文只留索引与指针
按「主文留头量、深规程外置」重写本仓技能,全部是逐字搬移:
搬移前断言原文每条非空行必须出现在新主文或 reference 文件里,
0 丢失才落盘;步骤编号一律不重排(正文有「第 12 步」交叉引用);
每个切点保留原小标题 + 索引(加粗引导句与步骤标题)+ 指针行。

主文变化(上限口径 = 插件写侧 65536 字节,见上一笔 skill-size 钩子):
- id-family-semantic-migration 33392 -> 10968(17%),reference 3 篇
- stale-doc-count-sweep 19811 -> 6945(11%),reference 2 篇
- mem-budget 19963 -> 13042(19%),reference 3 篇
- keil-build 12917 -> 8879(14%),reference 2 篇
- task-worktree 72601 -> 28139(42%),reference 8 篇

task-worktree 本笔同时收录另一会话已写完的 C2/D/G 三节正文——那
三处手改与我的拆分落在同一份文件改动里,不可分离;其内容同样已
随 reference/ 外置,主文只剩标题、索引与指针。

外置语料合计 122211 字节 / 30 篇:围栏数逐篇为偶,无 BOM/CR/U+FFFD。
draw-diagram 与 serial-boot-test 脚本是另一会话的在飞改动,本笔未含。

skill-size 钩子在本笔首次真跑(前一笔无 .dsh/skills 文件可触发)。

gate: pre-commit 全绿(doc-links、id-check-new、check-yaml、
      skill-size、check-added-large-files);30 篇 reference 手工字节
      体检 clean;skill() 逐个复载确认加载的是拆分版。
2026-10-29 15:03:12 +08:00

28 KiB
Raw Blame History

name, description, whenToUse
name description whenToUse
task-worktree 多任务并行开发的空间管理(git worktree 生命周期):`@task new` 开工作树+任务分支、目录联接共享 .venv 与模拟器 SDK、活跃上限 8 与腾位(A 节)、build/bench 两类独占锁与非登记持有者「缺台账」释放不掉(B 节)、`done` 是合并点不是查看器(C 节)、主工作树被别的并发会话弄脏时的手工合并路径(C2 节,详情已外置)、门禁指标异常增长时用净室工作树逐文件归因(D 节,外置)、回收 `@task list` 看不到的工作树并清悬挂台账(E 节,外置)、与 DSH worktree 插件 `/worktree`、`worktree_create` 共存(F 节,外置)、任务树内跑门禁与 `simulator/SDL2` 联接假红、树内提交/合并信息的禁词自检(G 节,外置)、插件树两条共享联接的分级(`simulator/SDL2` 是硬需求、`.venv` 有解释器逃生门,F 节,外置)、并行上限数字的唯一事实源(`MAX_CONCURRENT`)。规格见 docs/环境与部署/并行任务工作树方案.md;标「外置」的深规程全文在本技能目录 reference/ 下,动手前先读对应那篇。 要同时推进多个任务、担心同分支同文档互相冲突;要为新任务开隔离空间;要把任务合并回主干;或遇到 `new` 报「已有 8 个活跃任务(上限 8)」、`lock release` 报「任务 X 不存在(缺台账)」、`done` 报「门禁未全绿」或「主工作树不干净」而那份未提交改动是别的会话/插件留下的、两个任务抢 COM6 / UV4、"改了文档被覆盖"、并集合并残留;或要在任务树里跑文档/审计门禁(往 `ci-artifacts/` 写输出报 ENOENT、`--all` 的问题全在 `simulator/SDL2` 联接下);或门禁指标异常增长(如不可判定数 +16)却查不出是哪个文件造成的;或要回收 `@task list` 里根本看不到的工作树、清台账里 branch/path 都已不存在的悬挂条目;或任务是在任务树里改文档再合主干、要写提交/合并信息(被清退的主题不得在消息里复现);或想确认插件工作树里 `.venv` 与 `simulator/SDL2` 到底哪个必须补;或要改并行上限这个数字并同步全仓口径。

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

执行体 = 本 skill 自带脚本 .dsh/skills/task-worktree/scripts/task_worktree.py (纯标准库,任意 python3;拷整个 skill 目录到别的项目即可用)。

本文档受 dsh-mnemon 写侧上限 65536 字节约束(实测 2026-10-29:正文涨到 72,601 字节后,技能自进化每次「生成草案」都被写侧拒绝、 那一行永久卡在「生成中」)。所以新增的深规程写进 reference/<篇名>.md,本文只留可执行判据的摘要 + 指针;瘦身前的 frontmatter 原文逐字留在 reference/frontmatter-history.md。动手做某条深规程前,先把对应那篇 reference 读完(摘要只用来判定要不要读)。

@T3@powershell

cwd = 仓库根(或任意子目录)

& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" <子命令> [参数] @T3@

bash(ops_bash)里固定两个变量,下文用 $TW 代指脚本:

@T3@bash PY=".venv/Scripts/python.exe"; TW=".dsh/skills/task-worktree/scripts/task_worktree.py" "$PY" "$TW" list @T3@

退出码:0=成功 / 1=操作失败 / 2=前置或环境错误 / 3=用法。 ⚠ 这几个码只有「不接管道」时才读得到——原因与对策见「读输出」一节。

固定命令

命令 作用
new <ID> <slug> 建分支 task/<ID>-<slug> + 工作树 + 共享联接 + 台账;打印新空间路径
list 所有任务一览:分支 / 落后领先 / 脏文件数 / 台架状态 + 两个锁的归属
status <ID> 单任务详情(未提交清单、相对基线漂移、台架记录、已建联接)
sync <ID> 在任务工作树里 rebase 追平主干,把冲突控制在任务分支上
lock acquire|release|status <build|bench> 独占资源锁
plugin list|adopt|unadopt [插件树名] 与 DSH worktree 插件的桥:看清单 / 补共享联接 / 摘联接(不删树)→ F 节
done <ID> 预演 → 合入主干 → 并集残留审计 → 重生成产物 → 门禁 → 清场(主树被别的会话弄脏时改走 C2 手工合并)
abort <ID> 放弃任务(工作树 + 分支一起删,永久丢弃)

new 常用开关:--base <分支>(默认 v2-on-ui)· --needs-bench(无台架 PASS 则 done 拒合)· --lean(跳过 Git LFS smudge)· --no-bootstrap(不建联接)· --force(仅跳过并发上限 8 的检查,不解决「谁该让位」,见 A 节)。

done 常用开关:--dry-run(只预演,零改动)· --skip-build · --skip-gates · --force(清场时强制移除工作树)。

lock 开关:--task <ID>(acquire 必填)· --wait <秒> · --ttl <小时>(自报窗口)· --note <文本> · --bench-pass(release 时给台账记 PASS)· --force。 ⚠ --note 只在 acquire 时写进锁 payload,release 时被忽略——别指望它留下验收证据(证据写进主干提交说明或文档)。

环境变量(高级):DTU_WT_ROOT(任务容器位置,默认仓库同级 _wt/)· DTU_TASK_BASE(默认基线分支,默认 v2-on-ui)· DTU_INTEGRATION_WORKTREE(集成点,默认主工作树——用于隔离演练/测试)。

典型流程

@T3@powershell & $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" new N21 dhara-isfree --needs-bench

→ 把新会话的工作目录切到打印出来的 _wt/N21-dhara-isfree/,在那里干活

& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" lock acquire bench --task N21

烧录 / @test / 板级套件……

& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" lock release bench --task N21 --bench-pass

回到主工作树执行回收

& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" done N21 @T3@

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

  1. ... 2>&1 | tail -N 的退出码是 tail 的,恒为 0 → 脚本的 1 / 2 全被吞。实测三条印着 「rc=0」的输出其实都是 exit 2:new 撞活跃上限、lock release 缺台账两次。 所以判据先看文本([ok] / !! / 前置/环境错误: / 失败:),要真码就直接执行不接管道或补一句 echo RC=$?;坚持接管道就在 bash 里取 ${PIPESTATUS[0]}。
  2. done / done --dry-run 的输出不要用 tail -2 截:接管道时 python 的 stdout 是块缓冲、git 子进程的 stderr 不缓冲,两股流错位混排,tail -2 只剩「--dry-run:到此为止…」加上 stderr 里带 conflict 字样的片段 → 据此误判「有冲突」。实测:tail -2 误读成 conflict,放宽到 tail -10 才看到 [ok] 无冲突,预演结果树 <sha>。dry-run 全文只有 5~8 行,直接读全量。

分步操作(八条运行时机制)

A. new 被拒「已有 8 个活跃任务(上限 8)」——怎么腾位

  1. 读原文:前置/环境错误:已有 8 个活跃任务(上限 8);先 done/abort,或加 --force(exit 2)。 上限是脚本常量 MAX_CONCURRENT = 8;「活跃」的判据 = _wt/.state/<ID>.json 台账存在,且台账里 path 指向的目录还在(手工删了工作树目录就不占名额,但会留下悬挂台账,见第 5 步与 E 第 6 步)。
  2. $TW list 看全景(分支 / 落后领先 / 脏文件数)决定谁让位:已完成的先 done(合并回收,一步不省)。
  3. 没人认领、零提交、尚未 claim 的空闲树 → abort <ID> 腾位(实测就是 abort 掉排在后面的空闲树换出名额)。 abort = 工作树 + 分支一起删、永久丢弃;工作树有未提交改动或已有提交时脚本会拒绝,确认可弃再加 --force。
  4. 确实要同时开第 9 棵才 new <ID> <slug> --force:磁盘代价约 83 MB/棵(--lean 约 45 MB), 并行度上去了、冲突面与台架排队也一起上去——不是免费的。
  5. 别用手工 git worktree remove 给「登记任务」腾位:台账不会被删(下次 new 同 ID 会报「任务 已存在」), 还绕过了「先摘联接」(铁律 5),有让 git 递归进联接把主工作树 .venv 删掉的风险。要回收的若是非登记树 ($TW list 里根本没有它:new 出来又被手工删掉只剩悬挂台账的残骸 / D 节 --detach 净室 / 别的会话手工建的) → 腾位与回收一起走 E 节,那里有「怎么证明这棵树零联接所以可以 remove」的可执行判据。
  6. 腾位后复验:$TW list 的条目数与 ls _wt 的目录数一致,再 new。

B. 非登记持有者占的锁:lock release --bench-pass 曾报「任务 X 不存在(缺台账)」

症状(lead 在主工作树跑台架验收窗,用临时名占锁 lock acquire bench --task acceptwin):

@T3@bash "$PY" "$TW" lock release bench --task acceptwin --bench-pass 前置/环境错误:任务 acceptwin 不存在(缺台账 _wt/.state/acceptwin.json) # 锁照旧挂着 "$PY" "$TW" lock release bench --task acceptwin --force --bench-pass # 同一句错误,锁仍在 @T3@

根因:release 分支里只要带 --bench-pass 就先 load_state(task) 去台账写 PASS,而 acquire 侧 不查台账——这个不对称让「释放」被「记账」挡死(--force 只覆盖持有者不符与陈旧接管,管不到台账)。 后果比报错本身大:锁没放,done 的前置 release_stale_locks() 对未到 TTL 的活锁直接判失败 → 一个名字没登记,就挡住所有任务的收口。

2026-10-29 起已修(task_worktree.py:无台账降级为 [warn] …跳过 PASS 记录,仅释放锁 并照常释放)。 现在的正解只有三步,不再需要手写占位台账:

  1. $TW lock status 认现场(持有者 / pid / 已持有时长 / TTL)——别对同一句报错反复重试 release。
  2. $TW lock release <build|bench> --task <NAME> [--bench-pass] 直接放;PASS 记不上会打印 warn,锁照放。
  3. PASS 后面还要用(done 只认台账里的 bench_pass)→ 从一开始就用已 new 出来的任务 ID 占锁, 或把验收结论写进主干提交说明 / 文档。插件树 pw:<name> 天然没有台账,别指望它留 PASS → F 节。

最后手段(脚本本身都走不通时):锁就是 _wt/.state/locks/<name>.lock 里的单行 JSON (name/task/pid/host/epoch/at/note/ttl),release 的实现就是 unlink 它——手工删这个文件等价于 「不记 PASS 的强制释放」,只在前面几步都不可用时才用。历史遗留的占位台账(早期为绕这个坑手写的 _wt/.state/<NAME>.json)现在已无必要:它活着时既占 MAX_CONCURRENT = 8 名额又被 list 当任务列出, 按 E 第 6 步备份后删掉即可。

C. done <ID> 的正确用法(它是合并点,不是查看器)

  1. 合并前确认任务属主已把全部改动提交完。$TW status <ID> 看未提交清单与领先提交数;跨会话任务 再问一句属主(send_message)。实测教训:属主还剩「2/2」没提交就 done,把只含 1/2 的分支合进了主干, 只能另开一棵树补后半并向对方说明(是自己的时序错,不是分支问题)。
  2. 先 --dry-run,并读全量输出(别 tail -2,见「读输出」)。判据两行: [ok] 前置检查通过(领先 <base> 共 N 个提交) + [ok] 无冲突,预演结果树 <sha>; N 必须等于属主应交的提交数,对不上回到第 1 步。dry-run 零改动。
  3. 正式 $TW done <ID>——必须在主工作树(铁律 1)。
  4. 输出 !! 门禁未全绿:工作树与分支**保留**,未清场 时,紧跟的 主干已含合并提交 <sha> 已经说明 合并落进主干了。
    • ✅ 走提示里的 a) 在主工作树修正后追加提交;
    • ❌ 绝不用 b) 的 git -C "<主树>" reset --hard <sha>——那是丢弃已入库的合并提交,而 reset --hard 本身是项目级 Never(会连未提交工作一起丢)。提示 b 只是列出的可能性,不是建议。
    • 先定位是哪条钩子红:门禁输出里的 [SUMMARY] static: pre-commit hooks -- passed=N failed=M, 不同根因处置不同(见陷阱「done 的门禁 = pre-commit run --all-files」)。最常态是 .map 滞后 → 第 5 步。
  5. 补绿固定链(2026-10-25 实测;顺序即因果,全在主工作树跑,逐条不接管道看退出码):

@T3@bash "$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 边界),按用户指示再做 @T3@

为什么必然要补,见陷阱「done 的门禁跑在 @build 之前」。 6. 重跑 done 之前主工作树必须干净——否则前置检查直接 失败:主工作树不干净(…)——合并会把未提交改动卷进来,所以第 5 步的产物必须先提交。重跑时若分支 已并入,会打印 [i] task/<ID>-<slug> 已合入 <base>,跳过合并,直接清场 → 摘联接 → 完成(分支此前已合入)。 7. 清场完成后再敲一次 done <ID> 会得到 前置/环境错误:任务 <ID> 不存在(缺台账 …json)(exit 2)—— 这不是失败:清场的最后一步 unlink 了台账。判完成以 git log 为准。

C2. done 被「主工作树不干净」挡住,而那份脏改动是别的并发会话留下的

  • 不许对那份外部脏改动做 stash / checkout / reset / 提交 / 删除(项目级 Never,你也不知道它写没写完); done 的前置要求主工作树干净,于是合并与回收一起被挡——这是挡给你看的保护,不是工具坏了。
  • 正解链(六步):取证(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 分支> → 合并后把 git hash-object <外部文件> 与 git diff --stat 和合并前存下的值逐项对账(实测同为 369380ca…、2+/2− → 证明 pre-commit 自己的 stash 往返没动别人的工作)→ 台账先 cp 成 <ID>.json.state-bak 再让 done(或 E 节 手工路径)自己摘 .venv / simulator/SDL2 联接拆树 → worktree list + lock status 复验收工。
  • 报告里要写明绕开 done 的理由并逐项补回它的证据:预演 sha + RC=0、blob id 对账、合并后在 主树跑全库 check_doc_links.py --all(见 G 节)、两把锁空闲;并且点名那份外部脏文件与中途冒出来的第二棵树 (谁的、怎么定性、你没动它的证据)。blob id 变了 = stash pop 半冲突:立刻停止收工,两份 blob id 与两份 diff 交给该文件属主,绝不 checkout 抹平。
  • 认得出它:本仓这份外部脏改动两次都是 .dsh/skills/serial-boot-test/scripts/run_boot_test.py(2+/2−,别的会话/插件正在写它)——别把它当成自己漏提交的文件去提交或清掉。同一次窗口里 git -C "$MAIN" worktree list 还会冒出别人中途新建的第二棵树(实测 data-trust-restructure),那是并发会话的硬证据:点名写进报告,并且不是你的树就别顺手回收(→ E 节)。
  • 合并信息也过 G 节的禁词自检:清退/删主题类任务,merge: 那行与正文里被清退主题的关键词计数必须是 0。

详情已外置(本文件须留头量给自进化:dsh-mnemon 写侧上限 65536 字节,超限的技能再也生不成草案)。全文见 reference/merge-dirty-main-tree.md —— 主树被别的并发会话弄脏时的手工合并路径(取证 → merge-tree 预演 → 主树 merge --no-ff -F → 外部改动 blob id / diff --stat 前后对账)。

D. 门禁指标异常增长 → 逐文件归因(把净室工作树当测量仪,主树全程只读)

详情已外置(本文件须留头量给自进化:dsh-mnemon 写侧上限 65536 字节,超限的技能再也生不成草案)。全文见 reference/clean-room-measure.md —— 把仓外 detached 工作树当净室测量仪:一次只注入一份在飞文件、与 HEAD 基线差分做逐文件归因、主树全程只读。

E. 回收非登记工作树($TW list 为空 / detached HEAD / 树里有在飞或未跟踪内容)

详情已外置(本文件须留头量给自进化:dsh-mnemon 写侧上限 65536 字节,超限的技能再也生不成草案)。全文见 reference/reclaim-unregistered.md —— 回收 @task list 看不到的工作树:逐文件内容三桶归类、reparse/联接只读预扫、唯一残留先备份再 --force、悬挂台账清理复验。

F. 与 DSH worktree 插件(/worktree、模型工具 worktree_create)共存

  • 两条共享联接不同级(别答成「两个都必须」):simulator/SDL2 = 硬需求——simulator/CMakeLists.txt:12 是普通 set(SDL2_DIR "${CMAKE_CURRENT_SOURCE_DIR}/SDL2/SDL2-2.32.10") 而非 CACHE,既没有环境变量也没有 -DSDL2_DIR= 逃生门,include / lib/x64 / SDL2.dll 三条路径全从它派生(:34 :70 :71 :180 :207)→ 缺它 build_sim.bat 当场失败。.venv = 软需求——scripts/ci_local.py 有 DTU_PRECOMMIT_PYTHON 显式逃生门 + 五级解释器候选(显式 var → sys.executable → 仓库 .venv → which python → 机器级常见位置),绕开联接只是静默落到机器级解释器(缺 fontTools → generated-fresh 假红)。plugin adopt 两条都补是因为便宜,不是两条都硬。
  • 「有插件了为什么还自持 Python 生命周期」别凭印象答:先实读插件包(npm root -g → node_modules/@deepseek-ai/dsh/ 下定位插件、读 package.json 记版本),再 grep 三个概念名并报命中数——独占锁 lock、共享目录联接(.venv / simulator-SDL2)、联接状态三态显示;插件侧零命中就是自持的理由。重叠面只有开/收两个动作(两侧底层都是 git worktree add,没重造「建树」);锁、联接、台账与 done 的「预演 → 合并 → 审计 → 门禁 → 清场」编排才是插件没有的东西(本仓资源各只有一份:一块 ST-LINK、一套 Keil 授权、一个 COM6,SDL2 又无逃生门 → 必须串行 + 必须共享)。

详情已外置(本文件须留头量给自进化:dsh-mnemon 写侧上限 65536 字节,超限的技能再也生不成草案)。全文见 reference/dsh-plugin-bridge.md —— 与 DSH worktree 插件(/worktree、worktree_create)共存:plugin adopt 补共享联接(两条联接的分级:SDL2 硬 / .venv 软)、锁归属 pw:<name>、回收仍归 /worktree、以及「为什么不用插件而自持生命周期」的取证姿势(F-2)。

G. 任务树内跑门禁与审计:输出落点(ci-artifacts)与 SDL2 联接假红

  • 新建的任务树里没有 ci-artifacts/(被 .gitignore 忽略 → 不会被检出):重定向输出或 fs.writeFileSync 直接 ENOENT,整段程序零执行。落盘前先单独一条 mkdir -p ci-artifacts;树内输出 一律用树内相对路径(cd 进树后 ../_wt/x.txt 会落到 _wt/_wt/,实测就是这句报错)。
  • 文档门禁在树内按单文件跑,全库 --all 基线留到合并后在主树跑:树内的 simulator/SDL2 是指向 主树同一份的目录联接,--all 会把这份「第二份仓库副本」一起扫:实测树内打 222 个文档 / 35 处问题、RC=1 而 35 处全在联接下、要改的文档一条没有 = 假红;合并后主树同一条命令 = 2,597 处 / 194 个文档 / rc=0, 这才是有效基线(单文件实测 161 处/98%/rc=0、16 处/88%/rc=0)。
  • 确要在树内跑 --all:判红固定三步——① 主树同命令拿基线 ② 过滤 simulator/SDL2 联接下的问题行 ③ 剩下的逐行比。只做一步(见 RC=1 就报「门禁挂了」/ 过滤后为空就报「全绿」)都是误判。
  • 跑门禁前先做一次字节体检(文档改动在树内提交前的固定项):lines / bytes / BOM / CR / U+FFFD / 三连续空行 / 行尾空格 / 末行单个 LF + 自写 Markdown 表格每行列数一致性检查。报出来的坏行未必是本次改动(实测 2,665 行的需求文档里两处坏表格与本次编辑无关)——先定位归属再决定动不动手,别顺手改别人的区域。这些面 doc-links / no-mojibake 只覆盖编码那半,表格断列与多出的空行要自己数。
  • 提交/合并信息一律 -F 读文件(UTF-8 无 BOM、标题 ≤100 字符、正文每行 ≤80,文件放 ci-artifacts/),清退类任务另加一条禁词自检:git log -1 --pretty=%B | grep -c '<被清退主题的关键词>' 必须是 0(标题与正文都不许再出现那个关键词,改用「其它串口通道对照内容」这类语义短语;合并信息同样查一遍)。提交前自查消息里的行数/统计有没有算错;未推送前发现错了 → git commit --amend -F <改好的文件> 就地修,别追加一笔「更正」提交(那等于把禁词又写回历史)。

详情已外置(本文件须留头量给自进化:dsh-mnemon 写侧上限 65536 字节,超限的技能再也生不成草案)。全文见 reference/gates-in-tree.md —— 任务树内跑门禁与审计:<tree>/ci-artifacts/ 先 mkdir -p、文档链接按单文件在树内跑、全库 --all 合并后在主树跑、simulator/SDL2 联接假红、提交/合并信息禁词自检、改完文档的字节与表格体检。

铁律

  1. done 只能在主工作树执行——主干分支检出在主工作树,git 不允许同一分支在两处检出。
  2. done 不 push。它结束于本地合入 + 门禁全绿;推送需用户明确许可(项目既有纪律)。
  3. build 与 bench 都必须串行:UV4 不能两个实例同时 -b;串口 COM6、ST-LINK、 设备 IP 192.168.1.100 各只有一份。构建与烧录都不可并发。
    • build 锁由 @build 自持自释,调用方无需手工 acquire/release;
    • bench 锁必须手工:lock acquire bench --task <ID> → 烧录/@test/探针 → lock release bench --task <ID> --bench-pass。非登记持有者(lead 的验收窗、插件树 pw:<name>)占了锁 怎么放 → B 节(2026-10-29 起无台账也只 warn、照常释放);插件树整体口径 → F 节。
  4. 单写者文档:docs/任务规划/(整个文件夹)、AGENTS.md 的任务表块、MDK-ARM/*.uvprojx、 .pre-commit-config.yaml、内存快照 —— 任务期不在任务工作树里改,留到 done 统一处理。
  5. 清场必须先摘联接:脚本会先 os.rmdir 掉 .venv / simulator/SDL2 联接再 git worktree remove。手工清场时千万别直接 git worktree remove——git 可能递归进联接, 把主工作树的 .venv 一起删掉。手工拆任何一棵树之前先跑 E 第 3 步的预扫,拿到 REPARSE: none 才动手。
  6. 活跃任务硬上限 8(脚本常量 MAX_CONCURRENT = 8,每棵约 83 MB 检出——du 口径,内容字节和 84.0 MiB → docs/环境与部署/并行任务工作树方案.md §4)。满了 new 直接 exit 2, 腾位顺序与 --force 的代价 → A 节;悬挂台账的清理与复验 → E 第 6 步。 改上限 = 只改 task_worktree.py 里那一行常量(MAX_CONCURRENT,:30):报错文案「已有 N 个活跃任务(上限 8)」 与 new 打的「另有 N 个 DSH 插件工作树(.dsh-worktrees/,不计入上限 8)」提示都是 .format(…, MAX_CONCURRENT) 插值该常量(实测 task_worktree.py:764-766 / :768-771)——AGENTS.md、方案文档、本技能 frontmatter 与 A 节里的 数字全是引用同步,不是第二处事实源。全形态扫描(最多 N / N 棵 / 三棵 / 第 N+1 棵 / 白占 N 个名额 / MAX_CONCURRENT = N / 硬上限)与「上限真生效」的隔离小仓验证(REPO_ROOT 由 __file__ 反推 → 脚本必须按同一 目录深度放,才能测到同级 _wt/)→ stale-doc-count-sweep skill;每棵树的磁盘代价按实测口径重述,别照抄旧数。
  7. @task 的工作树放仓库同级 _wt/,绝不在仓库内部——否则污染 git status、pre-commit --all-files 与全库搜索。唯一例外 = DSH worktree 插件写死的仓内 .dsh-worktrees/(改不了;已用「预置 .gitignore + 三个全库扫描门禁排除」兜住,见 F 节)。自己新建的树一律走 _wt/。
  8. done 是合并点:属主没提交完就别 done;合并提交一旦进了主干,只用「主工作树补提交」收尾, 不用 reset --hard 反悔 → C 节。
  9. 非登记 / detached 的树绝不跑 done / abort:两者都以台账为前置,无台账直接 exit 2;带着残留台账时 abort --force 会绕过内容保全把别人的在飞改动连树一起删。回收走 E 节(全景 → 分桶 → 联接预扫 → 备份再拆 → 复验 + 清悬挂台账)。

陷阱(实测)

详情已外置(本文件须留头量给自进化:dsh-mnemon 写侧上限 65536 字节,超限的技能再也生不成草案)。全文见 reference/pitfalls.md —— 「陷阱(实测)」逐条清单原文。

验证

详情已外置(本文件须留头量给自进化:dsh-mnemon 写侧上限 65536 字节,超限的技能再也生不成草案)。全文见 reference/verification.md —— 「验证」清单原文。

与其它 skill 的衔接

  • 构建 = keil-build skill(@build);烧录/回读 = stlink-flash skill;串口闭环 = serial-boot-test skill
  • 烧录产物的真实 hex 路径以 .serialtest/config.json 的 hex 键为准(实测 MDK-ARM/Objects/STM32F407-Demo.hex 这个路径并不存在),拷错等于烧了旧镜像 → serial-boot-test skill
  • 新 .c 文件 = new-source-file skill(uvprojx 与 CMakeLists.txt 双端注册)
  • DSH worktree 插件(人类命令 /worktree、模型工具 worktree_create,树在仓内 .dsh-worktrees/) 与本 skill 的分工、桥接命令与锁归属 → F 节(本 skill 不删插件树,插件也不删 _wt/)
  • .map 口径 / RAM 台账与趋势 → mem-budget skill;UI 回归 → ui-verify skill
  • 完整机制与目录分配 → docs/环境与部署/并行任务工作树方案.md