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)。
124 lines
9.1 KiB
Markdown
124 lines
9.1 KiB
Markdown
---
|
||
name: task-worktree
|
||
description: 多任务并行开发的空间管理(git worktree 生命周期):new 开一棵工作树 + 任务分支、目录联接共享 .venv 与模拟器 SDK、build/bench 两类独占锁、done 预演合并回主干并审计并集残留。规格见 docs/环境与部署/并行任务工作树方案.md。
|
||
whenToUse: 要同时推进多个任务、担心同分支同文档互相冲突;要为新任务开一个隔离空间;要把任务合并回主干;或遇到两个任务抢 COM6 / UV4、"改了文档被覆盖"、并集合并残留时。
|
||
---
|
||
|
||
# task-worktree — 多任务并行开发空间(短命令 `@task`)
|
||
|
||
**执行体 = 本 skill 自带脚本** `.dsh/skills/task-worktree/scripts/task_worktree.py`
|
||
(纯标准库,任意 python3;拷整个 skill 目录到别的项目即可用)。
|
||
|
||
```powershell
|
||
# 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`(集成点,默认主工作树——用于隔离演练/测试)。
|
||
|
||
## 典型流程
|
||
|
||
```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
|
||
```
|
||
|
||
## 铁律
|
||
|
||
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`。
|
||
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` 一起删掉。
|
||
6. **并发上限 3 棵工作树**(每棵约 83 MB 检出)。
|
||
7. **工作树放仓库同级 `_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-build` SKILL.md)。
|
||
|
||
## 与其它 skill 的衔接
|
||
|
||
- 构建 = `keil-build` skill(`@build`);烧录/回读 = `stlink-flash` skill;串口闭环 = `serial-boot-test` skill
|
||
- 新 `.c` 文件 = `new-source-file` skill(`uvprojx` 与 `CMakeLists.txt` 双端注册)
|
||
- 完整机制与目录分配 → `docs/环境与部署/并行任务工作树方案.md`
|