Files
dsh_ai1net_server/.workbuddy/memory/2026-10-14.md
T
admin c1707b7c97 记忆:补记「技能根绝对路径清零」
用户令:「SKILL.md:189 判据源写 E:/ProgramData/.workbuddy/skills/
严格禁止技能中写这里绝对路径(除非跨根目录因引用),跨环境全部报错」

判据:技能根(本包自己 + 同根技能)一律不写绝对路径;
跨根引用(工作区根 / 文档库根 / 本机安装位 / 宿主数据目录)才落到具体位置。

技能仓提交 2f65609(10 文件 · 19 增 18 删),已推送。
另 15 处在 dsh-knowledge / dsh-workflow,两包被 .gitignore 全挡且无自己的 .git ⇒ 改动不入仓。

本轮最大教训:Grep 工具(ripgrep)跳过被 ignore 的文件,第一遍扫描结论是错的;
改用 grep -r 才照出漏网的 15 处。核实"清干净没有"一律用 grep -r。
2026-10-14 21:11:33 +08:00

50 KiB
Raw Blame History

2026-10-14 工作日志

主会话 · 排查「执行/检查会话批量操作不能归档、不能删除」(07:2x~07:4x)

用户提问:为什么会话机制创建的执行会话和检查会话,批量操作时不能归档也无法删除。

取证路径(全部只读,未改任何代码与数据):

  1. 只读打开宿主库 E:\ProgramData\.workbuddy\workbuddy.db,查 sessions 表结构与取值分布。
  2. 读出 asar 头部索引(13910 文件),自建 tmp/asar-extract.js / tmp/asar-grep.js 做定点抽取与全量检索。
  3. 抽取并核实 main/conversations.js、main/handlers.js、main/server.js、 renderer/assets/adapters-*.js、renderer/assets/DataManagementPanel-*.js、 renderer/assets/ui-docs-viewer-*.js。

结论(产品层设计,非本项目机制缺陷):

  1. 产品没有会话的批量归档/批量删除接口 —— archiveSessions/deleteSessions/batchArchive 全量检索 0 命中。
  2. 归档可用性判据(ui-docs-viewer): _n = e => !((e.status??'').toLowerCase()==='archived' || e.projectId || I4n(e, 空间)) ⇒ archived / projectId 非空 / I4n 命中,三者任一即不可归档。 I4n(e,t) = e.transport==='cloud' ? false : e.spaceType==='assistant' || M4n(e.cwd,t)。
  3. 归档只改 status,deleted_at 保持 NULL;而保留策略自动清理对已归档项是 archivedSkipped += 1; continue ⇒ 归档不会让会话消失,只移出默认列表。
  4. 「删除」在本地是软删(SET deleted_at = ?,transport='local'),行仍留在 sessions 表 ⇒ 从库里看「像没删掉」。deleted_at = -1 是另一套语义(云端索引 WHERE deleted_at = -1 表未删)。
  5. DataManagementPanel 的批量交互操作的是「归档列表项」,按 kind 分流; readonly 项(origin 为 project/cloud)结构上不渲染操作按钮。

现场读数:sessions 412 行,deleted_at IS NOT NULL 319 行; [执行]/[检查] 会话全部 is_background_automation=1 + transport='local'。

产出:tmp/会话批量归档删除-排查-20261014.md(完整证据链与可复现取证方法)。

未做(等拍板):机制层加「批量归档/清理已完结执行·检查会话」的动作 —— 属直接改宿主库, 须守活库改动铁律(停 app 或 backup() API,⛔ 禁文件级 cp)。

本机坑(再次命中):⛔ 别把 MSYS 路径(/e/…)交给 Windows 原生 node,会落到 E:\e\…;一律传 E:/…。


主会话 · 排查「mcn-aigc-ontology 为什么自己创建了一个主会话」(07:34~07:40)

用户提问:mcn-aigc-ontology 为什么这个工作区运行会话项目机制,自己创建了一个主会话。

现场读数(全部只读):

  1. 该区已装机制:.workbuddy/collab/collabd.config.json 在位,supervise.switch = on。
  2. sessions 表里该 cwd 只有 1 条会话:9e069caf「加载会话技能」,status=working, is_background_automation=None(不是机制建的),created_at 07:12:14 —— 这条就是当前用户在用的主会话本身。
  3. 该区 automations 只有 2 条:
    • 473c13a3「[主]-super-i卡片采集-主会话」recurring FREQ=DAILY;BYHOUR=09;BYMINUTE=0 cwds=["E:/ProgramData/AIProject/mcn-aigc-ontology"],created_at 07:31:11,last_run_at=None (下次触发 09:00,从没跑过)。prompt 逐字:「你是本工作区的主会话……要推进就直接建 [执行] 排期拉起任务会话」。
    • 5e16f0de「[执行]-super-i卡片采集-全量抓取与落盘」once,scheduled_at=2026-10-14T07:35,created_at 07:31:19。
  4. 常驻:logs/supervise-heartbeat.json → pid=38624、started_h=07:30:37、round=25、 argv0 指向本区 .workbuddy/collab/collabd.py(argv0 合规)。
  5. 🔴 logs/_collabd.log 每 10 s 一条 all_sessions_idle 读库失败 no such table: sessions —— 典型「启动器丢了 CODEBUDDY_CONFIG_DIR」故障(读 0 字节空库 ⇒ 检查程序静默失效)。

结论:机制没有"自己创建主会话"。 真实发生了四件事,被误读成"机制自创主会话":

🔴🔴 2026-10-14 07:53 订正(用户点破):本行结论只对了一半,且说偏了。 用户原话:「那就更不对了,技能中没有任何创建主会话的机制,除非是创建接续会话」。 「机制运行中没有偷偷建主会话」这半句对;但技能文档里确实存在"排期创建主会话"这条路(写成了合法场景), 那才是真问题。⇒ 别照本行往下读,改看本文件末节《订正「技能里存在"创建主会话"这条路"》》。 (本行保留仅为留痕 —— 「过程 ≠ 成品」同理:错结论⛔ 不删,加状态块指向订正处。)

  1. 07:12 用户自己在该区开了一条会话(标题「加载会话技能」)= 本区真主会话。
  2. 07:30 该区常驻被拉起(pid 38624),随即把这条 07:12 的主会话登记进 goal.json (日志逐字:登记主会话 9e069caf(创建目标时顺手登记))。
  3. 07:31 该会话(或同轮动作)新建了一条 recurring 排期 [主]-super-i卡片采集-主会话, cwds 指本区、每天 09:00 触发 —— 这条排期到点会真的开出一条新的主会话类型会话。
  4. 同一分钟还建了一条 [执行] once 排期(07:35 触发)。

⇒ 「自己创建了一个主会话」的可见现象=排期台账里那条 [主]-…-主会话; 它尚未运行(last_run_at=None),不是已经多出来一个窗口。

风险(实测,未处置):

  • 该 recurring 排期每天 09:00 会开一条新会话;若长此以往 ⇒ 每天多一条"主会话"⇒ 与用户实际在用的 9e069caf 并存, 变成"一个工作区两个主会话"。技能口径:主会话由用户手动开,⛔ 不该由排期生产。
  • _collabd.log 的 no such table: sessions ⇒ 该区检查程序(建检查会话那条腿)静默失效。 与该排期是否清理是两件独立的事。

本机机制层观察:排期 scheduled_at 给了毫秒级时间戳;rrule 为空串(once)。


主会话 · 修复 mcn-aigc-ontology 两项(07:35~07:45)

用户令:「两个问题都按照建议进行修复」⇒ 按倾向 A 执行。

修复一:撤掉每日主会话排期

  • [主]-super-i卡片采集-主会话(id 473c13a3)已置 PAUSED,next_run_at 清空。
  • 用 automation_update 改状态(⛔ 未用 rm/sqlite,符合机制铁律);零删除、可逆(改回 ACTIVE 即恢复)。
  • 那条 [执行]-super-i卡片采集-全量抓取与落盘(5e16f0de)保持 ACTIVE,07:35:09 已正常触发建出执行会话 c6226e12。

修复二:常驻读库故障(no such table: sessions)

  • 真因(证据链):supervise-launch.py 的默认值在 10-11 被改成 ~/.workbuddy, 而本机 C:\Users\Administrator\.workbuddy\workbuddy.db 是 0 字节、零张表(实测); 真库在 E:\ProgramData\.workbuddy\workbuddy.db(124 MB)。ai1net 区没炸是因为它 config 里 host_db 兜住了。
  • 修法(双保险,与技能 P0-73 口径一致): ① supervise-launch.py:默认值改回真库绝对路径 E:\ProgramData\.workbuddy(setdefault 语义保留,宿主 env 优先); ② collabd.config.json:补 "host_db": "E:/ProgramData/.workbuddy/workbuddy.db"(本该有的单区兜底)。
  • 改前备份:supervise-launch.py.bak-configdir-20261014-073619、collabd.config.json.bak-hostdb-20261014-073619。
  • 重启:计划任务 collabd-keepalive-mcn-aigc-ontology(pythonw.exe,合规)⇒ 旧 pid 38624 → 新 pid 28628。

验收(三条对齐,非恒绿):

  1. 启动器解析出的库 = E:/ProgramData/.workbuddy/workbuddy.db,sessions 413 行读通。
  2. 重启后日志:all_sessions_idle:还有 1 条 working(c6226e12)⇒ 不算全结束 —— 正常读到库, 本轮 no such table 0 条(重启前是每 20s 一条、连续 25+ 轮)。
  3. 变异对照:旧形态(~/.workbuddy + 无 host_db)⇒ 复现 no such table: sessions; 新形态 ⇒ 读通 413 行。⇒ 判据有效,不是恒绿。

遗留(技能/其他区层面,本轮未动):

  • 🔴 技能源 E:\ProgramData\.workbuddy\skills\session-mechanism\scripts\supervise-launch.py 第 30 行仍是 os.environ.setdefault("CODEBUDDY_CONFIG_DIR", os.path.expanduser("~/.workbuddy")) ⇒ 凡是照技能源部署的新区,都会命中同一个空库故障。本区已单区修好,技能源未动 = 待拍板。
  • 各区部署副本形态不一(实测): ai1net-dsh-server/contentm_agent/mcn-aigc-ontology(已修)= 写死 E:\ProgramData\.workbuddy + config 有 host_db; aigc-idea-impression = 写死绝对路径但 config 无 host_db(靠启动器那条腿活着,实测日志正常); vibe-product = 未部署启动器。
  • 本机 ~/.workbuddy/workbuddy.db 那个 0 字节空文件仍在(无害,但会持续误导"以为 ~ 是活动环境")。

终态复核:写法定为与 contentm_agent 逐行一致(setdefault + 绝对路径),语法 OK、重启后 pid 38428、读库失败 0 条。


主会话 · 技能源相对化 + 四区统一部署(07:45~07:52)

用户令:「按照建议处理,技能中 文件路径 必须用相对路径」⇒ 该条推翻了上一轮"给各区写死绝对路径"的做法。

动因:上一轮为救 mcn 区把技能源里的 ~/.workbuddy 换成写死绝对路径 —— 这本身是把「一台机器的环境」焊进技能, 与既有的 collabctl.py::_resolve_airoot() 口径(⛔ 不在代码里写死盘符/目录)冲突。用户第四条指令正是纠这个。

改法:HERE 相对基准 + 三级外置取值 + ⛔ 禁回落 ~

HERE = os.path.dirname(os.path.abspath(__file__))     # 全部路径以 HERE 为基准(相对)
CB   = <HERE 推导出的 collabd.py>;CFG = <HERE 推导出的 collabd.config.json>

_resolve_config_dir() 三级:
  ① os.environ["CODEBUDDY_CONFIG_DIR"]   —— 宿主 env 已给 ⇒ 直接用,⛔ 不动
  ② 本区 collabd.config.json 的 host_db  —— 取其父目录
  ③ 包内 env/roots.env 的 CODEBUDDY_CONFIG_DIR —— 向上最多 7 层找 env/roots.env
  三级全空 ⇒ 置空 + stderr 告警,⛔ 绝不回落 ~/.workbuddy(那是 0 字节空库)
  • 三级取值均外置(宿主 env/本区配置/包内 env),代码内零绝对路径。
  • 保留 setdefault 语义的反面:宿主 env 优先级最高(命中①时原样不动)。
  • docstring 里示例路径改写为正斜杠(E:/ProgramData/.workbuddy)—— 顺带解掉 \U unicodeescape 报错。
  • 新增 import json。

验证(探针,非纸面):造临时区跑探针 ⇒ ① 宿主 env 命中(D:/fake/host/env,原样采用); ② 无宿主 env 但有 host_db ⇒ 解析出 E:\ProgramData\.workbuddy; ③ 三者皆无 ⇒ CODEBUDDY_CONFIG_DIR 为 None 并打出告警(fail-safe 方向正确:宁可报错也不静默读空库)。

分发与终态:

  • deploy_code.py --ws <区> 分发四区,自动备份旧副本 ⇒ 四区 supervise-launch.py md5 统一 bfa03b2f。
  • aigc-idea-impression 补 "host_db": "E:/ProgramData/.workbuddy/workbuddy.db"(JSON 校验通过)—— 这是四级兜底里它原本缺的一环。
  • 技能包 env/roots.env 第 9 行已含 CODEBUDDY_CONFIG_DIR=E:/ProgramData/.workbuddy ⇒ 第③级在位,无需补。

四区常驻重启终验:

ai1net-dsh-server     旧 20680 → 37280   07:50:41  ✅ 读库正常
mcn-aigc-ontology     旧 38428 →  4728   07:50:46  ✅ 读库正常
aigc-idea-impression  旧 29364 → 20304   07:50:52  ✅ 读库正常
contentm_agent        旧 27092 → 29400   07:50:58  ✅ 读库正常
日志实证:all_sessions_idle:还有 1 条 working(f920d73e)⇒ 不算全结束   / no such table 共 0 条

🔴 必须记住的一条口径:技能源改动只影响将来部署的新区;已在跑的四区是靠部署副本 + 重启生效的。 ⇒ 以后改技能源,动作永远是「改源 → deploy_code.py 分发 → 重启常驻 → 验心跳 started_h 晚于部署时间」,三步缺一即未生效。

遗留:~/.workbuddy/workbuddy.db(0 字节)仍在,无害但会持续误导;vibe-product 未部署启动器。


沉淀(跨区通用,供后续同类问题直接引用)

  • 🔴 读写宿主库的脚本,路径一律「外置 + 相对基准」:代码里只认 CODEBUDDY_CONFIG_DIR/本区 host_db/包内 env/roots.env, ⛔ 不写死盘符、⛔ 不猜 ~。判据:把区整个搬到另一台机器仍能跑通 ⇒ 合格。
  • 🔴 绝对路径修复是「治好了但焊死了」:同一症状下次换机器/换盘符必复发 ⇒ 只能作应急,须在同轮内补上相对/外置改造。
  • 🔴 ~/.workbuddy 在本机是空壳(0 字节库),凡代码里出现 expanduser("~/.workbuddy") 一律视为高危默认值。

主会话 · 订正「技能里存在"创建主会话"这条路」(07:53~08:00)

用户订正原话:「那就更不对了,技能中没有任何创建主会话的机制,除非是创建接续会话」。 ⇒ 我上轮把那条排期说成"多出来的主会话(只是还没跑)",说岔了 —— 用户的点不在"它跑没跑", 而在技能里根本就不该有"排期创建主会话"这条路。核实结论:用户判断正确,技能里确实有这么一条(错的)路。

病灶(四处,同源):supervise-persistence.md 早期把「定时任务创建主会话」与「用户手动创建主会话」 并列成两种合法场景,SKILL.md §⑥ 顺着抄了一遍 ⇒ 读到的人(或 AI)会理解成"这条路是允许的",于是照做。

事故链条(mcn-aigc-ontology,全部实测):

  1. 07:12 用户手动开 9e069caf「加载会话技能」= 该区真主会话(is_background_automation=None)。
  2. 07:30 init_workspace.py 装机制(goal.json 的 lifecycle_why 逐字「init_workspace.py 新建」), 常驻拉起并顺手登记主会话(日志逐字「登记主会话 9e069caf(创建目标时顺手登记)」)。
  3. 07:31:11 建出 recurring 排期 [主]-super-i卡片采集-主会话(FREQ=DAILY;BYHOUR=09;BYMINUTE=0), prompt 逐字:「你是本工作区(…)的主会话,只负责目标与方向,不亲自干活。……职责:判断与派活 (要推进就直接建 [执行] 排期拉起任务会话)」 ⇒ 这是"主会话"的口径,被套到了一条排期身上 —— 排期开出来的会话恒是任务会话那一档。
  4. ⇒ 每天 09:00 会开出一条顶着主会话名头的任务会话;长期跑 ⇒ 台账里"主会话"越积越多,用户那边没多窗口。

权威口径(技能里本来就有,只是被上述那处盖住了):SKILL.md《三类会话》类别表逐字 ——「主会话 | 用户手动开 | 管目标与方向、派活 | 用户关窗」; architecture.md §1 同款;排期只服务派活线([执行]-…)与检查线([检查]-…)。 「接续会话」是主会话撞阈值时由它自己建出来的形态(角色继承),⛔ 不是第三条建主会话的路。

修复(三件,已全部落地并验收):

① 改文档(4 处):

  • references/supervise-persistence.md §〇:表格从"场景 A/场景 B 两列"改成只有一列(「用户手动打开主会话」), 并新增《为什么⛔ 不能"用排期拉起主会话"》小节 —— 含事故回放 + 明确写「本档早期表格是病灶,2026-10-14 已改掉」。
  • 同档 §八 对照表:删掉「B · 定时任务创建主会话」那一行,只留有价值的读数(once 过期即哑)。
  • SKILL.md §⑥:那句「用户手动创建主会话 / 定时任务创建主会话」改成 「形态只有一种:主会话由用户手动开 —— ⛔ 排期拉不出主会话」。
  • references/architecture.md §1:新增 🔴🔴 段《主会话怎么来:只能由用户手动开》,含判据指针。

② 加判据(第 ⑬ 条,session-rules-check.py):

  • 扫本区排期,命中 name 以 [主] 开头或含"主会话" ⇒ fail(还在 ACTIVE)/warn(已非 ACTIVE 的留痕)/否则 ok。
  • ⚠️ 扫描面 recurring 与 once 都含(当年 ⑧ 只扫 recurring 才看不见机制建的 once 排期,同类坑)。
  • 变异对照(证明非恒绿):喂 mcn 区真数据三种输入 —— 原样 PAUSED ⇒ warn;伪造成 ACTIVE ⇒ fail; 无此类排期的区 ⇒ ok。三档全判得出。夹具 tmp/probe-main-sched-judge.py。
  • SKILL.md §体检 清单从「查三类、十二项」改成「十三项」;变异对照记录补上 ⑬ 那行。

③ 分发与验收:

  • deploy_code.py 分发四区,session-rules-check.py md5 统一 4b53c88d(技能源同值)。
  • mcn 区用部署副本真跑 ⇒ 第 ⑬ 条按预期报 warn(那排期现为 PAUSED)。
  • 顺带抓出另一条真隐患:那条排期 model_id=deepseek-v4.1-flash + model_is_thinking=0 ⇒ 每次触发必被服务端拒(判据 ⑧ 报 fail)。⚠️ 该排期现已 PAUSED ⇒ 不触发,但若哪天改回 ACTIVE 必须同时改模型。

沉淀(本机口径):

  • 🔴 主会话只能由用户手动开。排期开出来的会话恒是任务会话那一档(is_background_automation=1); 名字里带 [主]/"主会话"不改变它的档位。判据=排期名,见第 ⑬ 条。
  • 🔴 技能文档里"并列两种合法场景"的写法极危险 —— 它会把一条本该唯一的路读成"二选一"。 ⇒ 写"形态"类内容一律单列;要留旧形态只作事故回放/留痕,并明确写"⛔ 别照做"。

主会话 · 三项拍板执行(09:46~09:55)

用户令(逐字):「1、A / 2、确认调整无误 就更新 / 3、不需要就删除,避免创建无效文件」。

① 排期保持 PAUSED(候选 A)

  • mcn 区 [主]-super-i卡片采集-主会话 保持 PAUSED 不动(next_run_at=None ⇒ 不触发;零风险、可逆)。
  • 终态复核:该区 4 条排期 —— [主]-… PAUSED|[执行]-…第2棒 ACTIVE(next_run_at 已排)|[检查]-第1棒 ACTIVE| [执行]-…全量抓取与落盘(首棒)已跑完。链路正常。

② 核实无误后更新(推送技能总仓)

  • 入库前必查三件(全部通过):白名单核对 git ls-tree -d = 7 个技能目录、与 .gitignore 放行条逐条对上; 凭据扫描(password/token/secret/api_key/PRIVATE/x-access-token/ghp_/sk-)零命中;两个 py 语法编译通过。
  • 错口径清零复核:SKILL.md/architecture.md 里「定时任务创建主会话」「场景 B」零命中; supervise-persistence.md 剩 4 处全是事故回放/留痕(明写「⛔ 别照做」)。
  • 编号冲突复核:体检清单 ⑬ 唯一;pitfalls.md 里那套 ⑦~⑬ 是 t_srsm_busy 夹具的独立编号,不冲突。
  • 提交:053a6a6「session-mechanism:主会话只能由用户开(撤掉"排期建主会话"那条路)」,5 files / +180 / -26。
  • 推送:[email protected]:admin/workbuddy_skills.git ⇒ 20eacef..053a6a6 master -> master(成功)。
  • 终验:本地 HEAD = 远端 origin/master = 053a6a67231fe39bc6fcbcfe983d8ab5b6438196;git status 干净。

③ 删除 0 字节空壳库(避免误导)

  • 清单:C:\Users\Administrator\.workbuddy\workbuddy.db —— 0 字节、2026-10-03 00:06 建、零张表、无 WAL/SHM 伴生 (该目录其余 MEMORY.md/device-id/logs/observe/pending-telemetry 一律不动)。
  • 做法:送回收站(SHFileOperationW + FOF_ALLOWUNDO,⛔ 不直删)。
  • ⚠️ 删前加了一道自检:脚本先查大小,非 0 字节就拒绝执行(防误删真库)。
  • 结果:rc=2(SHFileOperation 的"部分成功"语义),原位已不存在。
  • 影响面复核:真库 E:\ProgramData\.workbuddy\workbuddy.db 在位(sessions 416 行/automations 341 行); 四区常驻读库正常 —— no such table 最后一次出现分别是 10-05 12:14(ai1net)与 今天 07:37(mcn), 均为重启前历史行,aigc/contentm 为 0 条。本轮删除未造成任何新故障。

临时文件清理(用户令「避免创建无效文件」)

  • 送回收站:who-created-main-sched.py/main-sched-prompt.py/trash-empty-db.py/cleanup-20261014.py(清理脚本自身也一并送走)。
  • 保留 1 个:tmp/probe-main-sched-judge.py —— 技能文档已把它登记为判据 ⑬ 的变异对照夹具, 删了以后 ⑬ 的红绿对照就没法复现(技能纪律:夹具"跑完即弃"指不占常驻位置,⛔ 不等于删掉证据)。

主会话 · 核实「行尾符与同源冲突」接手单(11:09~11:20)

来件:C:\Users\Administrator\Desktop\workbuddy_skills-行尾符与同源冲突-交仓库会话.md (另一个环境留下的接手单;用户令「确认是否有相关问题」⇒ 只要核实结论,⛔ 不动手改)。

⚠️ 来件与实际不符的一处(先说清):单子写本机仓是 D:\.workbuddy\skills, 实际该路径不存在;D 盘那个仓在 D:/github/_alotbuy-gitea/workbuddy_skills(remote 指向 work.alotbuy.com,与本机用的不是同一个 origin)。 更要紧的是:单子称该仓「本地领先远端 2 个提交」1774693 / 886d99a —— 这两笔提交在 D 盘仓里不存在 (git cat-file 报 Not a valid object name),且该仓 origin/master..HEAD 与 HEAD..origin/master 双向为空(既不领先也不落后)。 ⇒ 单子里第三条("有 2 笔提交停在本地待同步")在本机无法证实,⛔ 不作为待办。

核实结论:三条里第一、二条属实,且第一条比单子描述更严重。

① 行尾符 —— 属实,实测读数(仓 E:/ProgramData/.workbuddy/skills):

  • .gitattributes 不存在;core.autocrlf = true;.gitattributes 属性列 ���仓为空(零覆盖)。
  • 全仓入库形态统计:i/lf 4576 / i/crlf 244 / i/-text 28 / i/none 5 / i/mixed 1。
  • CRLF 244 个的分布:product-planning/references/ 占 237 个(冲突重灾区),session-mechanism/scripts 5 个、 session-mechanism/references 1 个、product-planning/SKILL.md 1 个。
  • 🔴 单子没提到的一个更糟样本:product-planning/references/stage-requirements/references/create-prd.md 是 i/lf + w/crlf(入库 LF、工作副本 CRLF)⇒ 这正是"本地一改就整文件 diff / 合并整块冲突"的最典型形态。
  • 另两处同批读数:stage-delivery/SKILL.md = i/crlf w/crlf;stage-discovery/.../grill-me.md = i/lf w/lf。
  • 本机当前无受害症状:git status 干净(0 行)、无冲突标记残留 ⇒ 隐患在位、尚未发作。
  • ⚠️ 我自己上一笔提交 053a6a6 的 5 个文件全部 i/lf w/lf,不受影响。

② session-mechanism/SKILL.md frontmatter —— 属实:

  • 实况:第 6 行 last_change: 2026-10-05 10:5x · 🔴🔴 … 是一行超长标量, 其后12 行以 · 开头且无缩进 ⇒ 顶层出现无键的行,不是合法 YAML(单子描述准确)。
  • 同文件其它字段(name/description/version/updated_at)写法正常,确属孤例。

③ 同源冲突(两份各写一版)—— 本机无异常:

  • 单子列的 4 个冲突文件路径写少了一层(实际都在二级 references/ 下): stage-delivery/SKILL.md、stage-delivery/references/execution-runbook.md、 stage-discovery/references/grill-me.md、stage-requirements/references/create-prd.md。
  • 本机仓里这 4 个文件无未提交改动、无冲突标记(grep '^<<<<<<<' 零命中)⇒ 当前是合好的状态。
  • 单子那句"动手前先 git fetch、同一件事只在一处定稿"是作业纪律,本机已在守(本轮推送前就跑了 git fetch)。

待办(⛔ 本轮未动,属另一个仓/需出方案):见本轮回复末尾的待拍板项。


主会话 · 执行「行尾符 + frontmatter」三项修复并推送(11:22~11:40)

用户令:「修改完成后 检查无误 推到技能仓库」⇒ 按上一轮三项的倾向 A 全做。

① .gitattributes(新文件,仓根)

  • 内容:默认 * text=auto eol=lf,再对文本类逐类显式声明(md/py/sh/json/css/html/js/mjs/yml/yaml 等); .bat/.cmd 保留 CRLF(Windows 批处理对 LF 敏感);图片/字体/压缩包/音视频/数据库一律 binary。
  • 🔴 .gitignore 同步放行 !/.gitattributes —— 本仓是「先 /* 全挡再逐个放行」的白名单模式, 不放行 ⇒ 这个文件根本进不了库;而它跟 .gitignore 同属仓级规则,必须入库, 否则别的机器取仓后仍按各自 core.autocrlf 各行其是。
  • 提交 cf0ec41(2 files, +68)。

② 本机 core.autocrlf 设 false(立刻止血)

  • true → false(仓级配置,只影响本仓)。
  • 验:git ls-files --eol 的 attr 列从空变成 attr/text eol=lf ⇒ 属性真的生效了(⛔ 不是只写了文件)。

③ --renormalize 重新规范化(单独一笔)

  • 实跑改动 245 个文件:product-planning 239 / session-mechanism 6。
  • 🔴 关键验收:纯行尾符,零内容改动 —— git diff --cached --numstat --ignore-cr-at-eol 输出为空; 且 insertions 44412 / deletions 44412 完全对称(行尾符转换的数学特征)。
  • 复核没碰二进制与资源(grep -E '\.(png|jpg|ttf|woff2?|zip|gz)$' 零命中)。
  • 提交 9e779c4。单独一笔的理由:把「改规则」与「按规则刷数据」分开, 将来撞车时可直接回退这一笔而不牵连 .gitattributes。
  • 行尾符终态:i/crlf 244 → 0;i/lf 4822;剩 i/-text 28(二进制)+ i/none 5。

④ session-mechanism/SKILL.md frontmatter 合法化(内容一字不动)

  • 原形态:last_change: 一行超长标量 + 其后 12 行以 · 开头无缩进 ⇒ 顶层无键行 12 处。
  • 🔴 顺带发现接手单没提的第二处:agent_created: 前面还夹着一行游离的 · **本包自身**:install.py … —— 同样是顶层无键行(改前共 12 处,接手单只指出了前 11 行)。
  • 改法:块标量 last_change: |- + 续行统一两格缩进(含那行游离内容)。
  • 验收四条(改前 vs 改后逐字比对): ① 顶层无键行 12 → 0;② last_change 正文 12 行逐字一致; ③ 正文(第二个 --- 之后)逐字一致;④ PyYAML 解析通过, 字段=name/description/version/updated_at/last_change/agent_created,last_change 3358 字符 / 36 行。
  • 提交 5d16c6d(1 file, +14/-13)。
  • ⚠️ 验证脚本第一版有 bug(split("|-")[1] 把首段读成空串)⇒ 一度误报"内容不守恒"; 修掉解析后复验才通过。教训:验证脚本自身也要先自证,否则会把"改动正确"误判成"改坏了"。

⑤ 推送与终验

  • git push origin master ⇒ d9445b5..5d16c6d(成功)。
  • 终验:本地 HEAD = origin/master = 5d16c6df706f63543073ff63c50889dd99c55c81;三笔提交在远端齐全;工作区干净。

沉淀(跨项目可用):

  • 🔴 白名单 .gitignore 模式下,新加仓级文件(如 .gitattributes)必须同步放行 —— 否则"写了但没入库", 而且 git status 里它显示为 ??(未跟踪),很容易被误读成"已经生效了"。
  • 🔴 行尾符规范化的验收判据 = --ignore-cr-at-eol 后零差异 + 增删行数对称。 只看文件数会误判(246 个文件听着很吓人,实际一个字都没改)。
  • 🔴 验证脚本要先自证:解析器有 bug 时会产出"看起来很严重"的假红(本轮就撞了一次)。

两个看板整包搬进会话技能 boards/(用户令「1 boards 2 A」)

用户原话:「把这两个看板都纳入会话技能中 ,分两个看板文件夹 放在assert中(判断是否应该放在这里)」 →「1 肯定是整包啊 2 就两个看板 对应两个文件夹」→「但是当前情况 是两个看板最好代码和页面放一起, 要不单独开个 workshop 文件夹 放两个看板」→「这里面有html代码适合放到 scripts中吗」→「1 boards 2 A」。

拍板结果:文件夹名 boards(⛔ 不是 workshop);A = 本区部署与计划任务本轮一起改。

新增第三层分层(判据,已写进 SKILL.md):

  • scripts/ = 可执行程序(被计划任务/hook 调)|assets/ = 静态素材(样式、模板)
  • 🔴 boards/ = 自带页面的看板程序(一个看板一个文件夹,页面 + 脚本同处一包)
  • ⛔ HTML 不进 scripts/:那层是可执行程序层(现有全是 .py), session-rules-check.py/install.py 会按脚本扫(语法检查、副本比对)⇒ HTML 进去会被误判。

搬了什么:

  • boards/会话看板/(6 文件):board.py(150141B)/ board_ext.py / board-launch.py / board-launch.py.tpl / board.html(151811B)/ board-render-probe.js(端口 20099)
  • boards/项目管理界面/(3 文件):project-board.py / project-board.html(54102B)/ project-board-launch.py(端口 20100)

🔴 搬迁带出的真缺陷(最重要的一条): board.py 里 importlib 按 Path(__file__).parent / "collabd.py" 取判据 —— 「与 collabd.py 同目录」这个前提搬走后不成立 ⇒ 静默落到 except 分支里本地抄的那份判据。 危害:判据可能漂(同族"共用同一实现"当场 FAIL),但看板照跑、肉眼完全看不出来。 修法:候选表(同目录 → 包内 scripts/ → DSH_SKILL_ROOT/scripts/)+ 兜底分支 写 stderr 留痕(⛔ 不许静默降级)。CFG_P 同病同治(对齐 collabd.py::load_cfg() 查找顺序)。

selftest.py 的三处假设修正(都源于"一个路径指两处"):

  • t_scope_parity 的子进程代码用一个 H 同时指 board.py 与 collabd.py ⇒ 拆成 H/B 两参数
  • selftest.py 里 HERE / "board.py" 27 处 ⇒ board_py()(带旧位置兜底)
  • HERE.parent / "assets" / "board.html" 15 处 ⇒ BOARD_DIR / "board.html"

顺带修一条既有 FAIL(与搬迁无关):判据锚点没跟文档改 —— supervise-persistence.md 的措辞在 053a6a6 已改成「排期按点拉起主会话这条路不存在」, 判据仍锚旧句「排期代替不了载体」(那句现在只在 SKILL.md 里)⇒ 恒红。

验收读数:

  • --filter 看板:PASS 110 / FAIL 0(另 1 条报告型不计入)。迭代轨迹 92/18 → 104/6 → 105/5 → 109/1 → 110/0。
  • 全量:PASS 91 / FAIL 1(同一条报告型「产物落点」,实质 FAIL 0)。 ⚠️ 「合计」行缺失的真因=safe-delete 安全闸在收尾时拦了 64 个清理目标(> 阈值 50) ⇒ 本机工具层策略,⛔ 与技能无关。
  • 会话看板 board.py --out ⇒ 50515 字节快照,无「回落本地判据」告警(证明真从 collabd.py 取)。
  • 项目管理界面 --once ⇒ 4 个项目全列出;HTTP / 200 · 54102 字节 (=技能包真身那份 ⇒ 证明工作区侧确实是转发);/api/projects 出数据;/healthz ok。

工作区侧改成薄转发(⛔ 不留第二份实现):

  • 交付物/项目管理界面/project-board.py:改成薄转发(原 38380B 完整实现 → 转发壳)
  • 交付物/项目管理界面/project-board.html:改成路标页(54KB 真身 → 说明指向)
  • 交付物/项目管理界面/project-board-launch.py:上一轮已改薄转发
  • 原件备份 ⇒ tmp/pm-board-bak-20261014/
  • 🔴 计划任务不用改:动作指向工作区启动器(薄转发会转到技能包真身)⇒ 稳定路径正是留这一层的目的。 重起后 pid 35080 → 23596,命令行仍是 pythonw.exe ".../project-board-launch.py"。

入库:提交 52566c7(9 新文件 + 3 改名 + 6 修改),推送 5d16c6d..52566c7 成功。

  • 🔴 .gitignore 无需放行 boards/ —— 白名单放的是整包 !/session-mechanism/,包内新增目录自动被包含。
  • 凭据扫描:token 命中全是文案里的普通词(「0 token」「SyntaxError: Unexpected token」),无真凭据。
  • 行尾符:git ls-files --eol 全部 i/lf w/lf attr/text eol=lf ✅。

沉淀(跨项目可用):

  • 🔴 搬家会静默破坏"同目录"隐式前提 —— 这类 import 都有 except 兜底, 于是行为看着正常、判据却已漂。判据=「搬迁后必须有一条用例专门验『真的从模块取、不是本地抄』」; 正解 = 兜底分支必须留痕(⛔ 宁可见地坏,不可静默降级)。
  • 🔴 一个路径常量指两处 = 下一次搬家的地雷。判据:凡 HERE / "x.py" 里 HERE 是"包根/脚本目录" 这类位置代称,就该问一句"另一个文件搬家时它还成立吗"。
  • 🔴 判据锚点必须逐字抄原文(本轮第三次栽):文档改措辞后判据恒红, ⛔ 别把"判据红"直接读成"实现坏了"——先问"是不是锚点过期了"。

第二件:会话技能包内代码一律相对路径(用户令)

原话:「会话技能中 能用相对路径的都要用相对路径」

新立判据(SKILL.md §0 + 第 11 条禁令)—— 问一句「这个根能不能从『我自己的位置』推出来?」:

  • 能(本包自己的目录)⇒ 从 __file__ 上跳推导(Path(__file__).resolve().parent 上跳 N 层)。
  • 不能(工作区根/配置目录/文档库根 —— 与技能包无任何相对关系)⇒ 读本包 env/roots.env。
  • 推不出来 ⇒ 如实报错 + 留痕。⛔ 不许静默扫空;⛔ 也不许回落到一个猜的盘符。
  • ⚠️ 文档/注释里的绝对路径不在此列 —— 举例、历史取证照写,那是证据。

落的四处改动:

  • boards/项目看板/project-board.py:新增 _sm_load_roots()(range(5)); _SKILL_ROOT 从 HERE.parent.parent 推导;SKILL_BOARD_DIR / SKILL_SCRIPTS 同源推导。
  • WS_ROOTS 从常量改函数 _ws_roots():DSH_PM_BOARD_ROOTS → DSH_AIROOT → 由自身工作区推父目录, 三级回落;全空 ⇒ to_project_view() 里如实报错 + 写 logs/project-board.err.log 后返回,⛔ 不静默扫空。
  • scripts/selftest.py:生产区定位改扫兄弟区(去写死区名,靠 DSH_AIROOT 遍历找 .workbuddy/collab/collabd.config.json); 两处测试假路径去盘符(__selftest_no_such_ws__ / __selftest_ws__)。
  • env/roots.env.example 入库,真文件由 install.py --apply 生成、不入库。 键:DSH_WS_ROOT / DSH_AIROOT / WB_RESULT_HOOK_WS / COLLABD_PROD_CONFIG / CODEBUDDY_CONFIG_DIR / DSH_DOCS_ROOT。注入用 setdefault(宿主 env 优先,本文件兜底)。

换机场景实测(两条都对):

  • 只带包、不带 env/ ⇒ 如实报错(不瞎猜盘符)。⚠️ 我第一次"模拟换机"只清 env 变量是假验证 ——env/roots.env 文件还在,照样读得到 ⇒ 必须把包拷到 /tmp 且不带 env/ 才验得出真行为。
  • 只给 DSH_AIROOT ⇒ 正常扫到 4 个项目。

禁令条数:「十条禁令」→「十一条禁令」(含第 0 步里那句"在十一条禁令之前"一并订正)。


第三件:两个看板定名(用户令)

原话:「会话技能的两个看板 改名 会话看板 和 项目看板」

定名:会话看板(boards/会话看板/,端口 20099)|项目看板(boards/项目看板/,端口 20100)。 原「项目管理界面」这个叫法全链退场。

改了什么:

  • 技能包目录:boards/项目管理界面/ → boards/项目看板/
  • 工作区目录:交付物/项目管理界面/ → 交付物/项目看板/(三份薄转发/路标页跟随)
  • 包内三处路径引用 + 展示名 6 处(模块 docstring、错误日志前缀、argparse description、启动横幅); project-board.html 的 <title> → 项目看板 · 执行任务、<div class="brand"> → 项目看板。
  • 工作区三文件内「项目管理界面」全改(含功能性路径 BOARD_DIR/TARGET),remain=0。
  • 计划任务 dsh-project-board-keepalive:动作与 CWD 同步指向 交付物/项目看板/project-board-launch.py。
  • references/manifest.md:目录名 + 四行 md5/尺寸更新。

端到端验收:服务重起 pid 23596 → 40776 · HTTP 200 · 54102 字节 · <title>项目看板 · 执行任务</title> · /api/projects 出数据 · /healthz ok。 --filter 看板:PASS 110 / FAIL 0。

入库:

  • 技能仓提交 fe22f4c(git 识别 R096/R099/R089 重命名;凭据扫描无命中;行尾全 lf)。
  • 工作区仓提交 6b783aa,推送 df9dcfb..6b783aa。 (暂存清单 R092/R082/R093 —— 三文件全被识别为重命名,非删+增。)

沉淀:

  • 🔴 改名是链式动作,不是 mv 一下 —— 完整链:改目录 → 改包内引用 → 改展示名 → 改 HTML 标题/brand → 改工作区薄转发 → 改计划任务动作与 CWD → 重起服务验端到端 → 入库。 漏掉"重起"这一步,改名只落在磁盘上、线上还是旧名(HTTP 标题是最硬的验收点)。
  • 🔴 工作区侧留薄转发层的价值这轮兑现了 —— 计划任务指工作区稳定路径, 技能包真身怎么搬、怎么改名,任务都不用动。

第四件:提交前复核 ⇒ 抓出「相对路径」又漏 2 处(用户令:「检查确认修改无误 功能正常后 提交仓库」)

复核方法(可复用):不是重看自己改了什么,而是用判据反查全包 —— git grep -nE '"[A-Za-z]:/|[A-Za-z]:\\\\' HEAD -- 'session-mechanism/**/*.py', 再逐条判读(注释/docstring 里的举例属禁令例外,真代码常量才算)。

判读结果:20 处命中里 18 处是注释/docstring 举例(照写正确),2 处是真漏网:

① 🔴 scripts/hooks/wb-result-hook.py 的 CARE_PREFIX —— 真代码常量 原 = "e:/programdata/aiproject/"(盘符写进代码)。它是域外过滤的判据: 判错的后果不是"慢",而是把自家会话判成"域外"⇒ 不读转录,且从外面看毫无异常。 修法:新增 _sm_load_roots()(scripts/hooks/ 到包根上跳三级), CARE_PREFIX 改从 DSH_AIROOT 推导;取不到 ⇒ 空串, 由 classify() 如实报错 + 打 stderr 留痕(⛔ 不静默把全部会话判成域外)。

② ⚠️ scripts/selftest.py:6875 假样本 "workspace": "E:/selftest" ⇒ 改 "__selftest_ws__" (与同段已改的两处同族,属"测试假数据也别带盘符"的一致性收口)。

⚠️ 我中途自己踩的坑(记下来):第一次改 CARE_PREFIX 拼成 "/" + …(多一个前导斜杠), 于是 startswith 一条都匹配不上 ⇒ 场景①全部判成 outside。 若不复核"逐样本对账"、只看"跑通了 rc=0",这个错误会直接进库。 ⇒ 判据:改路径拼装后必须逐样本对账输出(本机 5 个样本 + 1 个空值),⛔ 别以 rc=0 为准。

验证(三层,缺一不可):

  • 有 DSH_AIROOT ⇒ 分类逐样本与改前硬编码完全一致 (home / line 客户端线 / line ai1net_ui / other contentm_agent / outside)。
  • 包不带 env/(拷到别处)⇒ 如实报错 + stderr 留痕,⛔ 不静默(真·换机场景)。
  • 清空 DSH_AIROOT/WB_RESULT_HOOK_WS/COLLABD_PROD_CONFIG 后用真实 payload 干跑 hook ⇒ rc=0 且正常输出 JSON(证明 _sm_load_roots() 从包内 env/roots.env 兜底成功 —— 这正是加它的目的:宿主不给 env 也能跑)。

顺带查清两件"看着像问题、实则是设计"的事:

  • .gitignore:54 明确排除 references/manifest.md ⇒ 它是本机生成产物(各机 md5 可能不同), 工具 install.py --manifest 可重算(本轮重算 80 份 / 语法失败 0),但不入库(⛔ 别指望它进提交)。
  • references/pitfalls.md:3449 命中「项目管理界面」⇒ 那是 P0-89 的历史取证记录(2026-10-06 发生的事), 按第 11 条禁令的例外照写不改。
  • 工作区那 78 项脏改动与本轮无关:49 个 M 是纯行尾符(i/lf ↔ w/crlf, insertions == deletions 即内容零变化,前序遗留、.gitattributes 不存在); 其余是 automations/*/ 运行时产物与别路工作留下的未跟踪目录。

入库:技能仓提交 7a08cd3,推送 fe22f4c..7a08cd3(仅 2 文件 / +49 −3)。 自测全量 rc=0 · PASS 110 / FAIL 0 · 语法失败 0。

沉淀(可直接复用):

  • 🔴🔴 "能用相对路径"这条令,一轮清不干净 —— 必须用 grep 反查全包,并逐条判读。 本轮 20 处命中里混着 18 处"合法举例",只有靠人判断才能把 2 处真漏网挑出来; 纯自动替换会误伤注释(把证据也改掉)。
  • 🔴 路径拼装改完必须"逐样本对账" —— 我这轮自己多拼了个前导 /, rc=0、不报错、看着一切正常,只有逐样本比对才照出来。
  • 🔴 setdefault 对空串不生效(DSH_AIROOT="" 时不会被 env/roots.env 覆盖)—— 这是全包统一口径(十余处同写法)⇒ 属既有设计,⛔ 不单方面改一处(会破坏单一事实源)。
  • 🔴 宿主不给 env 也要能跑:hook/常驻这类由宿主起的进程,env/roots.env 兜底是必需项, 且可用"清空相关 env 后干跑"直接验证。

第五件:清理 tmp/ 一次性残留(用户令:「历史无效内容 改删除就删除」)

清理原则(判据):只删一次性验证残留与过期快照; 凡活区(程序正在读写的)或实质内容(该入库的)一律保留。

删掉的(tmp/ 顶层 34 项 → 3 项):

  • 今日探针与日志 16 份(board-*.json / probe-board.json / pid35080.json / pm-port-check.txt / task-*.txt / selftest-*.log / _idle_hook_probe.jsonl 等)
  • 临时脚本 11 份(procchain*.py / restart*.py / asar-*.js / verify_launch.py 等)
  • 过期备份/中间态快照 5 个目录(pm-board-bak-20261014/ / bak-* / envsplit-{before,after}/)
  • 过期日志与截图 4 份(selftest-round3.txt / board-serve.err.log / 两张看板截图)
  • 探查笔记 1 份(会话批量归档删除-排查-20261014.md)

保留的(关键判读,⛔ 差点误删):

  • 🔴 tmp/supervise-inbox/ —— 活区(board.json / collabd-state.json / _token-deliver.* 每分钟在写),43 项。
  • 🔴 tmp/supervise-bg.log —— 常驻程序的运行日志(含 safe-delete 拦截记录、collabd.py 告警),仍在追加。
  • 🔴 tmp/selftest/ —— 自测工作区。
  • 🔴🔴 docs/pm/ai1net-plugin/research/(9 份产品调研四段式文档:1a 需求/1b 竞品/1c 画像/1d 策略/1e 场景) 与 执行会话/目标-本机协作-2f3331/(152 份执行产物)—— 看着像"未跟踪残留",实则是"该入库但还没入库"的实质内容。 两处都未被 gitignore。⛔ 绝不能当无效内容删。

工具限制(本机实测):

  • ⛔ 回收站两条路都被安全策略拦:Add-Type("compiles and loads .NET code at runtime") 与 New-Object -ComObject Shell.Application("COM object instantiation can run arbitrary code")。 ⇒ 退回直删(符合既定口径:「仅有强制拦截的沙箱场景才走直删」);且都是 tmp/ 内(.gitignore:5 排除区) ⇒ 不影响仓库,删前逐项核对过内容。

验收:清理后 tmp/ 只剩 3 项(全是活的);项目看板 :20100 HTTP 200 · /healthz {"ok":true,…}; git status 仍是 78 项(与本轮清理无关)。

沉淀:

  • 🔴 "这是残留还是内容"要按"它在不在入库范围"判,⛔ 不按"它长得像不像临时文件"判。 本轮 docs/pm/research/(9 份正式调研文档)与 执行会话/…(152 份产物)都是未被 ignore 的未跟踪目录, 长得像残留、实则是待入库的成果 —— 若按"看着像垃圾"删,等于删掉别路的工作成果。
  • 🔴 未跟踪(??)分两种:① 运行时产物(automations/*/,可删)② 待入库成果(docs/pm/,⛔ 不可删)。 判据=git check-ignore 有没有命中 —— 命中的是运行时产物,没命中的是该由人决定去留的。
  • 🔴 删前必查"它是不是活区" —— 本轮 supervise-inbox/(每分钟在写)与 supervise-bg.log(追加中) 都长得像"一堆老文件",实则是机制正在用的。判据=看 mtime 有没有近期变动。

第六件:技能根绝对路径清零(用户令)

用户原话:「SKILL.md:189 判据源写 E:/ProgramData/.workbuddy/skills/ 严格禁止技能中写这里绝对路径(除非跨根目录因引用),跨环境全部报错」

定位过程(⚠️ 记下,因为绕了弯):用户只给了「SKILL.md:189」, 但本包(session-mechanism)的 189 行不是这个内容(是「机制新建会话用哪个模型」)。 ⇒ 逐个技能包扫「各 SKILL.md 的第 189 行」,在 product-planning/SKILL.md:189 命中: 「判据来源(单一可信源…):humanizer…、humanizer-zh…。两份技能都在 E:/ProgramData/.workbuddy/skills/」 —— 「判据源」正是用户说的那三个字。教训:用户给的行号 + 关键词同时指认时,先按关键词全仓找,别只认行号。

判据(用户给的两分法,全仓统一):

  • 技能根(本包自己 + 同根技能)⇒ 一律不写绝对路径,写 <技能根>/<技能名>/;
  • 跨根引用(工作区根 / 文档库根 / 本机软件安装位 / 宿主数据目录)⇒ 才落到具体位置,优先读环境变量。

🔴 这条推翻了昨天的口径:session-mechanism SKILL.md 原写「⚠️ 文档/注释里的绝对路径不在此列(举例、历史取证照写 —— 那是证据)」 ⇒ 用户新令把例外收窄到只剩「跨根引用」,文档/注释/留痕里的技能根路径同样不许写。已改写该条。

改了什么(进仓 10 文件 · 19 增 18 删 · 提交 2f65609,推送 7a08cd3..2f65609):

  • product-planning:SKILL.md(判据来源 / video-capture 位置 / 本技能位置 + 新增「路径写相对或占位」规则)、 references/_留痕/SKILL-总入口-20261007改说人话前.md、stage-delivery/assets/video-capture/SKILL.md、 scripts/check_naming.py(注释)
  • session-mechanism:SKILL.md(反例改占位 + 例外条款改写)、references/deploy.md(安装目录)、 scripts/session-rules-check.py(docstring)、boards/会话看板/board-launch.py.tpl(模板占位符)
  • video-capture/ATTRIBUTION.md、仓根 改包纪律.md(3 条 cd 命令)

另外改了但⛔ 不入仓的(15 处):dsh-knowledge(5+1+1)、dsh-workflow(5+1+2) —— 🔴 这两个包被 .gitignore:9 的 /* 全挡,且没有自己的 .git ⇒ 改动只落在本机,不进任何仓。

🔴🔴 本轮最大的坑(必记):Grep 工具(ripgrep)默认跳过被 .gitignore 排除的文件 —— 我第一遍用它扫「技能根路径」,报「No matches / 已清零」,结论是错的; 改用 grep -rn … --exclude-dir=.venv 重扫,才照出 dsh-knowledge / dsh-workflow 里那 15 处。 ⇒ 判据:核实"某形态在技能包里清干净没有",一律用 grep -r,⛔ 别用会尊重 ignore 的搜索器 (被 ignore 的文件恰恰是"不在仓里、最容易失控"的那批)。

验收:

  • product-planning 命名闸门:通过 2060 / 失败 0
  • session-mechanism 全量自测:PASS 110 / FAIL 0(确认没有判据锚定我改写的那句例外条款)
  • 全仓 grep -r 反查:清零。剩余两处均属正当例外 —— session-mechanism/install.log(运行时产物,.gitignore:32 已 ignore)、 browser-harness/.venv/**/direct_url.json(pip editable 安装元数据,改了会破坏 pip 认知)。
  • ⚠️ 编辑器把 3 个文件写成了 CRLF ⇒ 已转回 LF(3 个文件各 260~440 处 CRLF,说明它们原本就是整文件 CRLF, 但 diff 未受污染 —— .gitattributes 的 text eol=lf 在比较时做了规范化,diff 仍只有我改的那几行)。

沉淀(可复用):

  • 🔴 "不写死路径"这条令,落地要分两个根:自己的根(可推导 ⇒ 禁止写)与别人的根(推不出来 ⇒ 允许外置引用)。 判据一句话:这个根能不能从「我自己的位置」推出来。
  • 🔴 清一类坏形态,必须先"全量反查"再动手 —— 且反查工具的过滤行为本身就是变量(见上面 ripgrep 那条)。
  • 🔴 改完规则文件要跑全量自测:本轮改写了 SKILL.md 的例外条款,若某判据锚定那句就会恒红 (同族坑今天已栽过两次)—— 实测未被锚定,全量 PASS 110。