Files
STM32F4-Base/.dsh/skills/task-worktree/reference/dsh-plugin-bridge.md
T
wandering1 2be49dc9dd
CI / 静态门禁 (pre-commit) (push) Failing after 3m45s
CI / 主机侧单测 (MSVC) (push) Failing after 43s
docs(skills): 明确 @build/@test/@task 为文档别名并新增 precheck/postmerge 命令
AGENTS.md「构建与验证」新增总口径:@build / @test / @task 都是本仓库的
文档别名、不是 shell 命令(2026-10-30 实测:bash 敲 @task / @build 即
command not found,dsh task 被解析成 profile 名报错);`=` 后的命令行
才是可执行形态,DSH 会话显式调用为 /keil-build、/serial-boot-test、
/task-worktree。

- AGENTS.md / docs/README.md / 并行任务工作树方案.md:@task 三处定义位
  由「短命令」统一改为「文档别名」,并写入两种实测失败形态
- keil-build / serial-boot-test SKILL.md:定义位点破别名性质;@test 在
  skill 内原先没有定义句,补口径 blockquote
- task-worktree SKILL.md 与 reference/:whenToUse、标题、症状表、插件桥
  去 @task 前缀改指 list / 本 skill;frontmatter 能力清单同步
- task_worktree.py 新增 precheck(树内只读自查:BOM 漂移 / CR / U+FFFD、
  docs 逐档门禁、--msgfile 消息 BOM/CRLF/行长/禁词检查,自动补
  ci-artifacts/)与 postmerge(主树体积凭证自动诊断:新鲜跳过、红先短链
  再只读复检裁定是否升全链;check_doc_links --all;列文件不代提交)
- done 快路径打印与门禁拦截提示均指向 postmerge

实测:precheck 对坏消息文件抓到 CRLF、行长 95>80、禁词×3(rc=1);
postmerge 端到端「凭证同码新鲜→跳链→doc --all 绿(2967 章节引用 /
214 文档)→列 dirty 文件」rc=0;check_skill_size.py 与 py_compile 通过;
frontmatter 改动后 DSH 已用新值真加载过一轮。无 .c/.h 改动,不涉及体积。
2026-10-30 10:25:05 +08:00

8.1 KiB
Raw Blame History

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

本仓现在有两套隔离空间。别混用,也别互相回收:

本 skill(文档别名 @task,DSH 显式调用 /task-worktree) DSH worktree 插件(dsh-task-worktree)
树在哪 仓库外 ../_wt/<ID>-<slug>/ 仓库内 .dsh-worktrees/worktree/<name>/
谁建 new <ID> <slug>(人来敲命令) 会话选「Worktree模式」/ 模型调 worktree_create(零命令)
台账 _wt/.state/<ID>.json(本 skill) .dsh-worktrees/manifest.json(插件私有格式,别去改)
分支名 task/<ID>-<slug> worktree/<name>
共享联接 new 自动建 建树时没有 → 要 plugin adopt 补
回收 done(预演 / 合并 / 门禁 / 清场)或 abort 用户敲 /worktree finish|bring-back|remove|prune(模型碰不到收尾命令)

为什么允许它把树建在仓内(铁律 7 的唯一例外):路径写死在插件里(WORKTREE_NAMESPACE = worktree

  • <repo>/.dsh-worktrees/),而暴露给模型的工具只有 worktree_create / worktree_list / worktree_status 三个,配置面改不动。兜法三件(均已落地):
  1. .gitignore 预置 .dsh-worktrees/ —— 插件首次 create 会往 .gitignore 追加一模一样的字面量, ensureIgnored() 见到同名规则即跳过;不预置就等于让插件在会话中途改这份受控文件(diff 里冒出 一行"不是我加的")。
  2. 全库扫描的排除面登记了它:scripts/check_doc_links.py 的 SKIP_PARTS、 tools/refgraph/gen_refgraph.py 的 EXCLUDE_DIRNAMES、scripts/check_layer_deps.py 的 os.walk 兜底。 一份仓库副本被全库扫描吞进去 = 文档链接假红 + 配图生成物逐字节漂移砸在某位同事头上。
  3. 联接与锁归属由本节命令接上(下面三步)。

这棵树是不是插件的:git worktree list 里路径含 .dsh-worktrees/ 就是。$TW list 现在会在台账段 下面另起一段列出它们,联接列点名缺哪一样(已联 / 缺 .venv / 缺 SDL2 / 齐(实体SDL2))。插件树不占 MAX_CONCURRENT = 8 名额——回收不归本 skill, 硬计入只会让别人那棵关不掉的树把 new 挡死(new 会打印一行「另有 N 个插件工作树不计入上限」提醒并发代价)。

三步接上(<name> 可带 worktree/ 前缀,也可多段如 refactor/logging):

"$PY" "$TW" plugin list                      # 一览:分支 / 脏 / 缺哪个联接(缺项另起一行说后果)
"$PY" "$TW" plugin adopt N21-dhara-isfree    # 只补缺失的 .venv 与 simulator/SDL2 联接
"$PY" "$TW" plugin unadopt N21-dhara-isfree  # 只摘联接(os.rmdir),绝不动目标内容与树里文件
  • 两条联接坏了的样子完全不同:缺 .venv → ci_local.py 的四级解释器回退静默落到机器级 python(无 fontTools = generated-fresh 假红的老根因,AGENTS.md 记着);缺 simulator/SDL2 → build_sim.bat 当场报错(SDL2_DIR 是裸 set,没有 env 逃生口)。所以列表只说「已联」不够—— 它按缺的那一样分别给后果,adopt 没补齐也只报「还不能算就绪」,不许说「现在可以跑」。
  • adopt 只碰联接:已联的跳过;simulator/SDL2 若已是实体目录只告警不动;.dsh-worktrees/ 之外的路径 一律 exit 2(实测 plugin adopt ".." 被拒)——这道闸防的是把主树或 _wt 目录当插件树遮蔽掉真实内容。
  • 锁归属现在认得出来:@build 在插件树里自持自释时,持有者名从 cwd 反推成 pw:<name>(多段名压平成 -),与 plugin adopt 打印的那个名字逐字相同(keil_build.py: plugin_tree_owner() ↔ task_worktree.py: plugin_lock_owner())→ lock status 不再一律落成 build-cli。
  • 上台架仍须手工占锁、用同一个名:lock acquire bench --task pw:<name> --note <说明> → lock release bench --task pw:<name> --bench-pass。插件树没有台账 → 按 B 节只 warn、锁照常放。
  • 命名建议:插件 <name> 沿用本 skill 的 <ID>-<slug> 形态(如 N21-dhara-isfree),两套视图同名好对账; 但分支前缀必然不同(worktree/ vs task/),别把两者说成同一个分支。
  • 收尾不在这:done / abort 拿插件树名下手只会 exit 2(这是保护,别绕过去手工 git worktree remove; 要拆先 plugin unadopt 摘联接,再由用户 /worktree remove)。/worktree bring-back 要求主工作树干净, 本仓常有在飞改动 → 提交或 stash 由用户定。反过来插件也不会碰 _wt/(路径根不同,谁也删不掉谁)。

F-1. 两条共享联接不是同级需求(按仓库实配定级;别答成「两个都必须」)

  • simulator/SDL2 = 硬需求:simulator/CMakeLists.txt:12 是 set(SDL2_DIR "\${CMAKE_CURRENT_SOURCE_DIR}/SDL2/SDL2-2.32.10")——普通 set()、非 CACHE,所以既没有环境变量入口、 命令行 -DSDL2_DIR= 也会被这个普通变量盖掉;SDL2_INCLUDE_DIR(:34)、SDL2_INCLUDE_PARENT(:70)、SDL2_LIB_X64(:71)、 链接目录(:180)、SDL2.dll 拷贝(:207)全部从它派生。缺它 build_sim.bat 当场配不出来(脚本里的 SDL2_DIR 同样是 裸 set,也没有 env 逃生口)。
  • .venv = 软需求(有显式逃生门,但绕开有代价):scripts/ci_local.py 的解释器解析顺序(:108-:138)—— ① DTU_PRECOMMIT_PYTHON(显式配置,指错立即报错、不静默降级)→ ② sys.executable → ③ 仓库 .venv → ④ shutil.which("python") → ⑤ _global_python_candidates() 的常见机器级位置。树内没有 .venv 时会静默落到机器级 解释器 → 无 fontTools → generated-fresh 假红(AGENTS.md 记着的老根因);可以用 DTU_PRECOMMIT_PYTHON 顶掉, 但要每台机器自己配——这就是它「软」的意思:有路可走,走得不爽。

结论口径:要补就两条都补(plugin adopt 一次干完,成本近乎零),但「都必须」是错的——SDL2 没有任何替代路径, .venv 有 DTU_PRECOMMIT_PYTHON 这条门。现役命令形式仍是 plugin list / plugin adopt <name>;不改代码、 不改 CMakeLists.txt(真要给 SDL2 加 CACHE 逃生门属于改模拟器构建口径,另案 → lvgl-simulator skill)。

F-2. 「DSH 都有 worktree 插件了,为什么还自持 Python 生命周期」——取证姿势与答案边界

这题不许凭印象答,口径 = 实读插件源码:在 DSH 安装目录定位插件包(npm root -g → <global>/node_modules/@deepseek-ai/dsh/, 或读 profile 的 dsh.profile.bundles 找包名),记下版本号与文件路径,再对三个概念名逐个 grep 并报出命中数:

  1. 独占锁 / 资源串行(lock、acquire、release、TTL)——插件侧零命中:它的模型是「一棵树一个会话」,没有跨会话排队概念。
  2. 共享目录联接(.venv、simulator/SDL2、mklink /J、reparse point)——零命中:插件建树就是纯 git worktree add, 所以插件树天生缺这两条联接(定级见 F-1)。
  3. 联接状态显示(三态:已联 / 缺 .venv / 缺 SDL2 / 齐(实体SDL2))——零命中。

重叠面要说清:两侧底层是同一条 git worktree add,本 skill 没有重造「建树」;重叠只在开与收两个动作, 而插件的收(/worktree finish|bring-back|remove|prune)是人类命令、模型碰不到,也没有「合并前预演 → 并集残留审计 → 补基线 → 门禁 → 先摘联接」这条编排。本仓为什么必须自持:硬件各只有一份(一块 ST-LINK、一套 Keil 授权、一个 COM6、 一个 192.168.1.100)→ 要 build / bench 锁串行;SDL2 是模拟器硬依赖且无逃生门 → 要共享联接;台架 PASS 要留给 done 记账 → 要台账。给不出版本号与 grep 命中数,就不要下结论。