e5d3dc1205
A:LOCK_TTL_HOURS 由全局 6h 改为按锁分设(build 1h / bench 6h)—— 兜底窗口应与操作真实时长同量级:编译是秒~分钟级,6h 太钝。 并支持 lock acquire --ttl 自报窗口(keil_build 按其 --timeout 自报, timeout/3600+15min,下限 0.5h),使 build 锁窗口自动跟随构建上限。 B:done 收口前置把陈旧锁视为空闲并自动释放(原为硬失败), 仅当锁仍被有效占用时才拒绝。 新增 lock_ttl_hours / lock_is_stale / release_stale_locks 三个辅助; lock status 显示各锁 TTL;cmd_done 改调 release_stale_locks。 验证:20 项 TTL 探针 + 5 项 lock_cli 接线探针全绿; 假 UV4 端到端(exit 0 自持自释、payload ttl=0.5)与 busy 路径(exit 4)。
9.1 KiB
9.1 KiB
name, description, whenToUse
| name | description | whenToUse |
|---|---|---|
| task-worktree | 多任务并行开发的空间管理(git worktree 生命周期):new 开一棵工作树 + 任务分支、目录联接共享 .venv 与模拟器 SDK、build/bench 两类独占锁、done 预演合并回主干并审计并集残留。规格见 docs/环境与部署/并行任务工作树方案.md。 | 要同时推进多个任务、担心同分支同文档互相冲突;要为新任务开一个隔离空间;要把任务合并回主干;或遇到两个任务抢 COM6 / UV4、"改了文档被覆盖"、并集合并残留时。 |
task-worktree — 多任务并行开发空间(短命令 @task)
执行体 = 本 skill 自带脚本 .dsh/skills/task-worktree/scripts/task_worktree.py
(纯标准库,任意 python3;拷整个 skill 目录到别的项目即可用)。
# cwd = 仓库根(或任意子目录)
& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" <子命令> [参数]
退出码:0=成功 / 1=操作失败 / 2=前置或环境错误 / 3=用法。
固定命令
| 命令 | 作用 |
|---|---|
new <ID> <slug> |
建分支 task/<ID>-<slug> + 工作树 + 共享联接 + 台账;打印新空间路径 |
list |
所有任务一览:分支 / 落后领先 / 脏文件数 / 台架状态 + 两个锁的归属 |
status <ID> |
单任务详情(未提交清单、相对基线漂移、台架记录、已建联接) |
sync <ID> |
在任务工作树里 rebase 追平主干,把冲突控制在任务分支上 |
lock acquire|release|status <build|bench> |
独占资源锁 |
done <ID> |
预演 → 合入主干 → 并集残留审计 → 重生成产物 → 门禁 → 清场 |
abort <ID> |
放弃任务(工作树 + 分支一起删,永久丢弃) |
new 常用开关:--base <分支>(默认 v2-on-ui)· --needs-bench(无台架 PASS 则 done 拒合)·
--lean(跳过 Git LFS smudge)· --no-bootstrap(不建联接)。
done 常用开关:--dry-run(只预演,零改动)· --skip-build · --skip-gates · --force。
环境变量(高级):DTU_WT_ROOT(任务容器位置,默认仓库同级 _wt/)·
DTU_TASK_BASE(默认基线分支,默认 v2-on-ui)·
DTU_INTEGRATION_WORKTREE(集成点,默认主工作树——用于隔离演练/测试)。
典型流程
& $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
铁律
done只能在主工作树执行——主干分支检出在主工作树,git 不允许同一分支在两处检出。done不 push。它结束于本地合入 + 门禁全绿;推送需用户明确许可(项目既有纪律)。build与bench都必须串行:UV4 不能两个实例同时-b;串口 COM6、ST-LINK、 设备 IP192.168.1.100各只有一份。构建与烧录都不可并发。build锁由@build自持自释,调用方无需手工 acquire/release;bench锁必须手工:lock acquire bench --task <ID>→ 烧录/@test/探针 →lock release bench --task <ID> --bench-pass。
- 单写者文档:
docs/任务规划/(整个文件夹)、AGENTS.md的任务表块、MDK-ARM/*.uvprojx、.pre-commit-config.yaml、内存快照 —— 任务期不在任务工作树里改,留到done统一处理。 - 清场必须先摘联接:脚本会先
os.rmdir掉.venv/simulator/SDL2联接再git worktree remove。手工清场时千万别直接git worktree remove——git 可能递归进联接, 把主工作树的.venv一起删掉。 - 并发上限 3 棵工作树(每棵约 83 MB 检出)。
- 工作树放仓库同级
_wt/,绝不在仓库内部——否则污染git status、pre-commit --all-files与全库搜索。
陷阱(实测)
- 联接不是符号链接:本仓库
core.symlinks=false,git 里的 symlink 会被检出成 「内容是目标路径的文本文件」。共享.venv只能用 Windows 目录联接(mklink /J, 不需要管理员权限),且联接必须建在版本控制之外。 实测(2026-10-12):用 ctypes 直调DeviceIoControl(FSCTL_SET_REPARSE_POINT)会被系统拒绝(Win32 4392),mklink /J正常——所以脚本走后者,参数以列表交给 subprocess,含空格的路径(本项目DTU Code)由 subprocess 自动加引号。 - 联接建失败不中止
new:只告警并打印手工补建命令,避免留下半成品任务空间; 但该任务里跑不了.venv\Scripts\python.exe,@build/ci_local.py会找不到解释器。 done前主工作树必须干净:否则未提交改动会被卷进合并。- ⚠
done的门禁跑在@build之前 → 依赖.map的钩子会误报(2026-10-12 实测):done的顺序是「合并 → 重生成 → 门禁(ci_local) →@build」,而mem-budget(N-27)/mem-numbers是拿文档快照 vs.map比对 —— 门禁跑时.map还是主工作树合并前的旧产物,与刚合并进来的新台账必然不一致 → 误报[FAIL] 台账受管区与 .map 不一致。@build跑完后.map才是新的。 处置:门禁失败且只差这两项时,按提示在主工作树跑python scripts/ram_baseline.py --write+python scripts/check_mem_numbers.py --write后追加提交,再重跑done(它会检测到「已合入」直接清场)。 - ⚠ 增量构建可能不重链 →
.map数字瞬态不一致(2026-10-12 实测):@build若判定无需重链,.map保持旧值,此时--write生成的快照是瞬态值 (实测同一份源码两次读到 223,560 与 223,724,差 164 B)。以主工作树(集成点) 的.map为准;改完 RAM 相关代码后确认重链再刷新快照。 - 合并冲突常落在「生成物」上:
docs/内存管理/RAM使用分析与优化方案.md(mem-numbers 快照)在双方都改过时必冲突,且两边都不能直接选(各自基于不同基线)—— 正确做法是解冲突后用合并后的实际构建重新生成,不要挑一侧。 - 并集合并残留:双方都改过的文件(尤其
.gitignore)会被 git 自动并成并集, 可能留下指向已删目录的死规则或重复条目。脚本会自动报「双方都改过」的文件清单 + 连续重复行;权威判据是 base / 两个 tip 三方对比(git show <ref>:<file>逐项grep -F), 别用tail -N目测。 .workbuddy-ai/刻意不共享:记忆不随任务分叉,也不给联接递归删除的机会。 任务工作树自己的记忆由done/abort在清场前归并回主工作树。--lean只省磁盘不省时间:实测全量 smudge 7.25 s / 83 MB,跳过 smudge 7.05 s / 45 MB, 两者工作区都干净。默认走全量(参考图可用),磁盘紧张时才用--lean。- 锁不绑定进程存活:
lock acquire本身是短命进程,而真正用资源的烧录/构建在别的进程里跑。 所以判「陈旧」只看时效(该锁的 TTL)加显式--force;pid 仅作排障记录。 同任务重复 acquire 是幂等的(不会把自己锁死)。 - TTL 按锁分设(2026-10-12):
build= 1h、bench= 6h(LOCK_TTL_HOURS)。 口径是「兜底窗口应与操作真实时长同量级」——编译是秒~分钟级,6h 的兜底太钝; bench 是会话级(flash→@test→探针),才配得上小时级。持有者还可用lock acquire ... --ttl <小时>自报更精确的窗口(keil_build.py即按其--timeout自报)。 - 陈旧锁自动接管:超过该锁 TTL 未释放即视为陈旧,下次
acquire会接管并提示; 未到 TTL 的活锁需--force。done的收口前置也会自动释放陈旧锁(不再硬失败)。 build锁不必手工持(2026-10-12 起):@build(keil_build.py)自持自释—— 进入 acquire、finally无条件 release,所以"Agent 忘了 release、锁一直挂到 TTL"这条在构建路径上已消除。done的门禁自己持锁并给子进程设DTU_LOCK_HELD=build告知跳过(新旧脚本都兼容)。bench锁仍须 Agent 手工持(会话作用域:跨烧录→@test→探针,见keil-buildSKILL.md)。
与其它 skill 的衔接
- 构建 =
keil-buildskill(@build);烧录/回读 =stlink-flashskill;串口闭环 =serial-boot-testskill - 新
.c文件 =new-source-fileskill(uvprojx与CMakeLists.txt双端注册) - 完整机制与目录分配 →
docs/环境与部署/并行任务工作树方案.md