Files
dsh_ai1net_server/.workbuddy/memory/2026-10-11.md
T
admin d933181a55 记忆:技能仓已配上游并推送 d81f75f(用户选 A)
推前核对:顶层 6 个技能 + 改包纪律.md 符合白名单;远端 90cc431 → 本地 d81f75f 快进。
远端复核:origin/master = d81f75f;env/ 下只有两个 *.example,没有任何配置真身。
2026-10-11 21:53:29 +08:00

25 KiB
Raw Blame History

2026-10-11 工作日志

核对项目看板(20100)「执行中」列 vs 真源 —— 列映射没错,真源有陈旧值(10:2x)

用户问:「确认 项目看板中 执行中的状态对吗」

取数:看板 /api/projects 现取(10:17)+ 各工作区 tmp/supervise-inbox/goal.json 与 goals/*.json 逐条比对。

  • 看板「执行中」= 4 张,全部 state=active、life=进行中: · vibe-product「把产品规划技能整理成唯一形态」(10-05T18:04 声明;4 条验收判据全「未过」) · contentm_agent「左侧导航改回基准形态 64px 图标在上」(10-10T22:32;3 条判据全未过) · contentm_agent「逆向 Easel 热点雷达与选题链路 补齐现版逻辑」(10-10T22:39;3 条判据全未过) · aigc-idea-impression「获取B站博主AI生成界面与设计教程视频…」(10-11T04:23;当前目标,5 条判据「本轮尚未开始」)
  • 真源里 lifecycle=进行中 的也是这 4 个 ⇒ 看板分列逻辑没错,它如实反映了真源。

根因(读 goalctl.py:669-677 坐实):--switch-goal 的「自动复位」作用在新目标(new.lifecycle="进行中"), 旧 goal.json 是原样归档进 goals/ ⇒ 归档件保留被替换那一刻的 lifecycle。 ⇒ 换目标时不收尾旧目标 ⇒ 没做完的旧目标带着「进行中」进归档 ⇒ 看板「执行中」列虚高(4 张里 3 张已无人推进)。 ⚠️ 只是显示问题:机制判「要不要拉检查会话」读的是当前目标 goal.json,⛔ 不受归档件影响。

🔴 我差点搞错的一处(记下,防再犯):归档件 lifecycle_why 写「换目标 ⇒ 复位(改前=已完成)」, 其中 改前 指前一个目标的状态(new 从旧 g 深拷贝后再复位),⛔ 不是这张卡自己的历史值。 我一度据此判「2 张原本已完成」,读代码后纠正 —— 这三张的 acceptance_state 全是「未过」,它们确实没做完。

待拍板:修法 A(goalctl 换目标时把未完成的旧目标置「已暂停」,看板补「已暂停」列)/ B(只改看板,归档件打「已归档」徽标)/C(不动,认为"没做完就该在执行中")。倾向 A。


✅ 方案 A 已落地:换目标 ⇒ 旧目标收尾「已暂停」+ 看板把它归入「待执行」(10:25–10:30)

用户定案(原话):「这类就属于暂停状态 可以归入到 待执行」+「1、方案A ,需要优化项目机制」。

改了三处:

  1. ~/.workbuddy/skills/session-mechanism/scripts/goalctl.py(--switch-goal 的归档步之前): 旧目标 lifecycle 不是「已完成」⇒ 置「已暂停」+写 lifecycle_at/by/why(why 里记「改前=…」); ⛔ 原本「已完成」的保持不动。改前只复位新目标、旧目标原样归档 ⇒ 状态失衡。
  2. 交付物/项目管理界面/project-board.py:LIFE_TO_COL 加 "已暂停": "waiting"; _LIFE_NOCOL 清空(原先装「已暂停」);注释与 life_unknown 口径同步。
  3. 存量回填(一次性):所有工作区归档件里未收尾的 3 份置「已暂停」 —— vibe-product ×1(把产品规划技能整理成唯一形态)、contentm_agent ×2(左侧导航改回基准形态/逆向 Easel 热点雷达); 原件备份在 tmp/bak-暂停态回填-20261011/(3 份)。

验证(实测读数):

  • py_compile 两文件通过;技能包全套自测 PASS 109 / FAIL 0(与基线一致,含「换目标 ⇒ 新目标复位进行中」那条)。
  • 看板服务已重启(新 pid 30252;20100 LISTENING + HTTP 200)—— 改的是服务端 .py,必须重启才生效。
  • 分列复核:ai1net-dsh-server active=0 | vibe-product active=0 waiting=1 done=3 | aigc-idea-impression active=1(当前目标,真在跑)| contentm_agent active=0 waiting=2 done=10。 ⇒ 「执行中」从 4 张降到 1 张,留下的那张是真在推进的。
  • 技能包清单重算:78 份文件、语法失败 0。

未做:技能仓未推(未明确要求 ⇒ 不 commit/push);工作区 MEMORY.md 状态层未动(余量只剩 1 字符)。 锁:已释放(dsh.py close 反序释放,--release-exec ✓)。


项目看板界面两处微调 + 答「删除按钮是做什么的」(10:30–10:35)

用户两条:①「E:/ProgramData/AIProject 工作区 这几个字可以删除」②「工作区下拉框后面的删除是做什么用的?」。

改动(交付物/项目管理界面/project-board.html —— 服务每次请求都 PAGE.read_bytes() ⇒ ⛔ 不用重启,curl 实测立刻生效):

  • 删头部根目录提示:<small id="ws"> 元素 + 它的 JS 赋值行(必须同时删 —— 只删元素会让 getElementById("ws").textContent 抛 null)。
  • 删下拉框的可见标签「工作区」;改用 title="选择工作区"(aria-label 本来就有)保住可用性。
  • 清死代码:CSS .brand small{…}。

答第 2 问(删除按钮):btndel 只在选中某个具体工作区时可用(选「全部」时 disabled,见 renderAll()); 点击后 prompt 选粒度 —— 输入 1 = 只删目标登记(工作区与台账保留、可逆、项目从列表消失); 输入 2 = 整个工作区送回收站(不永久删、可从回收站还原);不输入/取消 = 不做任何事。

流程瑕疵(自记,防再犯):本轮动文件没先抢锁(上一轮已 --release-exec,期间锁空闲)⇒ 事后补了一次 claim→release 记账。 当时锁况:另有一把属他人 —— [执行]-[B站AI设计教程采集]-承接队列(会话 7f09203a,域 aigc-idea-impression/执行会话,10:30 起), 按 R9 未动;它恰好印证上一轮改动是对的:看板「执行中」剩的那张卡,确实有执行会话在推。


🔴 交接:项目看板两项新需求(2026-10-11 11:15 · 本会话撞 220K 阈值,按 CODEBUDDY.md §G 交接)

本会话已完成(结论):

  • 看板「执行中」列失真已修:goalctl.py 换目标时把旧目标收尾为「已暂停」⇒ 看板把它归入「待执行」(用户拍板=方案 A)。 存量回填 3 份、看板服务已重启、「执行中」从 4 张降到 1 张;技能包自测 PASS 109 / FAIL 0。
  • 看板头部「E:/ProgramData/AIProject 工作区」两处文字已删(含那条 JS 赋值行与死 CSS .brand small)。

遗留(未做):技能仓未推(session-mechanism/scripts/goalctl.py 的改动只在本机);工作区未 commit。

下一步 = 用户 11:14 原话两条:

「所有目标项目都需要 加上时间(最后状态的时间)」 「已完成显示前8各,其余点击更多 到项目目标列表页查看」

  1. 所有目标卡加「最后状态时间」 —— 数据源已在接口里(/api/projects 的 cards[].life_at,形如 2026-10-05T12:35:11); ⚠️ 别拿 declared_at(登记时间)顶替,用户要的是最后状态的时间。落点与格式自定。
  2. 「已完成」列只显示前 8 张,第 9 张起收进「更多」入口 ⇒ 点它进「项目目标列表页」看全量。 📌 该列表页当前不存在(现形态=四列卡片 + 点卡开右侧抽屉),要新建一个列表视图;形态用户没细说,拿不准就先给一版最直接的。 🔴 只截「已完成」这一列,其余三列照旧全显。

涉及文件:交付物/项目管理界面/project-board.html(界面)/project-board.py(取数 to_project_view())。 服务:127.0.0.1:20100,由 project-board-launch.py 拉起 —— 改 .html 不用重启(服务每次请求读盘),改 .py 要重启。


✅ 看板两项新需求已落地:每卡加「最后状态时间」+「已完成」列收前 8 张 + 项目目标列表页(11:19–11:35)

用户原话(11:14):「所有目标项目都需要 加上时间(最后状态的时间)」/「已完成显示前8各,其余点击更多 到项目目标列表页查看」。

只改了 1 份文件:交付物/项目管理界面/project-board.html(⛔ project-board.py 零改动 ⇒ 不用重启服务)。 理由:cards[].life_at 早在接口里(to_project_view() 已给),列表页用页内视图 + 现有数据 ⇒ 不需要新接口。

  1. 最后状态时间:_whenBadge(t.life_at) 做成 .badge.when 小标挂在卡片 meta 行(fmtWhen ⇒ 2026-10-05 12:35)。 🔴 取 life_at(=goal.json::lifecycle_at),⛔ 不是 declared_at;实测两者能差好几天(contentm_agent 一张卡 登记 10-10 22:24 / 状态 10-10 09:35)。 ⚠️ 空值 ⇒ 显式印「未记录」(warn 色):实测 aigc-idea-impression 当前目标 lifecycle_at 就是空的 ⇒ ⛔ 不拿登记时间顶、⛔ 不静默留白。
  2. 「已完成」收前 8 张:DONE_LIMIT = 8;只对 key==="done" 生效,第 9 张起换成「更多(还有 N 个)→」。列头计数徽标仍是该列总数(14)。
  3. 项目目标列表页:新增页内视图 + URL hash 路由(#/goals/<列>),#cols 加 .aslist 切 block(⛔ 不加会被塞进 4 列 grid 的第 1 列)。 内容=状态筛选 chip(全部/待执行/执行中/有阻碍/已完成,计数从服务 cols 派生)+ 行(目标名/状态/当前/最后状态时间/工作区),按 life_at 倒序;点行开同一套详情抽屉;「← 返回看板」回 #/board(顺手 closeDetail(),否则遮罩盖着点不到)。 🔴 摊平逻辑抽成 _collectCards(),看板四列、单工作区视图、列表页共用这一份(⛔ 不各写一遍)。

验证(真实浏览器 · browser-harness,9223 已有实例,未新起):

  • 全部视图:四列等宽 403px(未动列宽);待执行3/执行中1/有阻碍0/已完成**8/14**;.more 只出现在已完成列;每张卡都有时间小标。
  • 未记录 兜底实测命中(执行中那张),计算色 rgb(240,160,32) ✓。
  • 点「更多」⇒ #/goals/done、14 行、chip 全部18/待执行3/执行中1/有阻碍0/已完成14;切「全部」⇒ #/goals/all、18 行;点行 ⇒ 抽屉开、标题对。
  • 单工作区视图(contentm_agent,done 10>8)⇒ 收 8 张+「更多(还有 2 个)」;列表页范围正确收窄到该工作区(10 行、范围 contentm_agent · 共 12 个目标)。
  • 非法 hash #/goals/zzz 不炸(回落 all);返回看板 ⇒ 4 列、aslist=false;跑满一轮 3s 轮询后 window.__errs 为空。
  • 内联 JS 过 vm.Script 语法编译(28398 字符)。
  • 截图:tmp/看板-已完成收起前8.png、tmp/项目目标列表页.png。

流程瑕疵(自记):scripts/dsh.py open 的预检 rc=1 拒开工 —— 它把 交付物/项目管理界面/* 判成【D】未归类 (preflight-lock.sh 的白名单只认 ^(src|poc|test|web|docs|scripts|交接单|skills)/)。判定属脚本白名单缺项(交付物本就属 ai1net-dsh-server/ 域), 未擅自改该脚本(它是【E】机制层、须独占),本轮按提示改为直接 handoff-guard.sh --claim-exec --domains ai1net-dsh-server/ 抢锁。 ⇒ 记一笔:凡动 交付物/ 下文件,入口预检必失败,可考虑给白名单补 交付物(属机制层改动,未做)。

锁:已释放(dsh.py close 反序释放 ✓)。未做:未 commit/push(未明确要求)。


去掉工作区下拉后面的「删除」按钮(2026-10-11 11:32–11:38)

用户令:「去掉工作区后的删除功能」。

改动(只有 交付物/项目管理界面/project-board.html 一份;⛔ .py 未动 ⇒ 不用重启 —— 改 .html 服务每次请求读盘):

  1. 删按钮元素 <button id="btndel" …>删除</button>(头部、下拉框右侧)。
  2. 删它的两处联动:renderAll() 里的 disabled = true、renderOne() 里的 disabled = false。
  3. 删 doDelete() 整个函数(原先按 prompt 选粒度:1=只删目标登记、2=整个工作区送回收站)。
  4. 删 btndel → doDelete 的事件绑定。
  5. 删 CSS button.danger{…}(全文件只这一个按钮用它)。 ⇒ 五处各留一行注释留痕。

保留未动:后端 /api/delete 端点与 act_delete() —— 界面已无入口,留着不影响;删它要动 .py 并重启服务,没必要。

验证:全文件只剩 3 行注释含 btndel/doDelete 字样、无活引用;线上页面(curl 实测)头部只剩 [下拉] … +新建目标 [刷新],删除按钮已消失。

流程:抢域锁 --domains ai1net-dsh-server/ → 改 → --release-exec 已释放。 并发确认:改的是接续会话 11:22 刚交付的同一份文件(它做完「加时间+已完成收前 8 +列表页」)⇒ 动手前先确认它的锁已释放、文件 11:22 后无新写入,无冲突。


技能库待提交清单盘点(18:53 · 只读)

用户问:「技能文件有哪些改动需要提交」。相对 HEAD fd9c902 共 8 项(7 个已跟踪文件 + 1 个未跟踪目录,合计 +59/−294):

  • browser-harness/SKILL.md(今天 10:19):标签页按页面类型分(一类页面一个、异类各开一个),依据 10-11 用户原话。
  • product-planning/SKILL.md(16:38)+ …/stage-delivery/assets/video-capture/SKILL.md(16:38,改写成「交接说明」)+ 删两脚本(extract_frames.py −156、fetch_video.py −85) ⇒ video-capture 今天独立成全局技能。
  • 未跟踪:product-planning/references/_留痕/video-capture-已独立-20261011/scripts/(上面两个脚本的原件留档)。
  • session-mechanism/scripts/collabctl.py(04:15,+2):登记 aigc-idea-impression 工作区的常驻。
  • session-mechanism/scripts/goalctl.py(10:26,+14):本会话改的 —— 换目标时旧目标收尾「已暂停」。

🔴 发现缺口(已作待拍板项交用户):skills/video-capture/ 已是独立技能,但技能仓白名单(.gitignore 只放行 6 个)不含它 ⇒ 照现状提交,仓库里的 product-planning 只剩一份指向仓外路径的交接说明,别处取仓拿不到该能力。 候选 A 加进白名单一起提交(倾向)/B 照现状提交、留已知缺口/C 压着 product-planning 那三项先不交。


🔴🔴 报障:技能包脚本把「这台机器」的路径与工作区清单写死在入库代码里(19:22 用户报,按 §G 交接整改)

用户原话:「session-mechanism/scripts/ 这里面的代码有问题,这个环境都是针对一台机器的,同步到别的机器把环境都搞坏了」。

根因:技能包本来就有「每台机器一份配置」的机制 —— install.py --apply 生成包根 roots.env(不入库,内含 DSH_WS_ROOT / DSH_DOCS_ROOT / CODEBUDDY_CONFIG_DIR / COLLABD_PROD_CONFIG),各脚本头部 _sm_load_roots() 以 setdefault 注入。 但下面这几处绕开了它,把本机路径与本机工作区清单写死在会入库的代码里:

  • scripts/collabctl.py:AIROOT = "E:/ProgramData/AIProject"(L63)|WORKSPACES 四个本机工作区(L67-72)| BOARD_PY 绝对路径(L75)|KEEPALIVE_TASKS 字典(L85-90)。
  • scripts/keepalive.py:PYW 写死 pythonw 版本路径(L46)|AIROOT(L47)|WS 字典(L50-53)|BOARD_PY(L55)。
  • scripts/supervise-launch.py:L28 CODEBUDDY_CONFIG_DIR = r"E:\ProgramData\.workbuddy"(setdefault ⇒ 兜底值本机化)。
  • scripts/board-launch.py:L20 DSH_WS_ROOT 兜底写死本工作区;L29 同上一处。

后果:同步到别的机器 ⇒ 这些路径全不存在;跑 collabctl.py on|ensure 会建出一批指向空路径的计划任务 —— 这就是"把环境搞坏"。

修法(照既有模式,逐条):

  1. AIROOT ⇒ 读 DSH_AIROOT(新键,落 roots.env 与 .example);兜底 = Path(DSH_WS_ROOT).parent。
  2. WORKSPACES ⇒ 自动发现:扫 AIROOT 下装了 .workbuddy/collab/ 的目录(与项目看板同一套扫法)。
  3. KEEPALIVE_TASKS ⇒ 由工作区名推导 collabd-keepalive-<basename>,不写死字典。
  4. BOARD_PY ⇒ Path(__file__).resolve().parent / "board.py"(技能包内相对定位)。
  5. PYW ⇒ 优先 sys.executable,其次宿主 env,兜底再探测。
  6. 两处 CODEBUDDY_CONFIG_DIR 默认值 ⇒ os.path.expanduser("~/.workbuddy") 形态。
  7. 改完跑 selftest.py(基线 PASS 109 / FAIL 0)+ install.py --manifest。

顺带要查的:用户说"同步到别的机器把环境都搞坏了" ⇒ 若那边已跑过 collabctl.py on|ensure,会留下指向空路径的计划任务,需一次清理。

属机制层 ⇒ 开工必须独占锁;改完跑自证。

✅ 用户 19:28 拍板(原话):「不用挪出 用env文件夹区分就行,重点是 脚本是脚本 配置是配置」 ⇒ 目录划分定为:技能包内建 env/ 放环境专属配置,scripts/ 只留通用代码;⛔ 不挪到宿主配置目录(我提的候选 A 被否)。 ⇒ 接续任务已按此改写并重排(automation df3dead8-37ba-4907-95c7-7896bea14383,改写后:建 env/、roots.env 与 .example 迁入、 .gitignore 挡 env/ 下非 .example、所有 _sm_load_roots() 与 install.py 的 ROOTS_ENV 改指 env/roots.env、再做去硬编码那七条)。 ⚠️ 中途曾把它 PAUSED 一次(怕它按旧方案白做),改写后已恢复 ACTIVE,排期 19:32。

✅ 19:29 / 19:30 用户再补两条口径: · 19:29「每个技能 需要环境配置的 都有自己的 env文件夹」⇒ 升格为全仓包结构规范,写进技能仓根 改包纪律.md; 并逐个检查其余技能(browser-harness / oil-ui-pro / product-planning / video-capture 各有 scripts;humanizer 两份是纯文档)。 · 19:30「修改完成后要仔细检查 避免调整后 各技能脚本运行失败」⇒ 验收按实跑算,七条:语法编译 / 全套自测 / 只读入口实跑(collabctl / board / board-launch / supervise-launch / keepalive)/ 4 个工作区常驻心跳与 20099·20100 两个看板活体 / 改动前后同命令对照 / 把 DSH_WS_ROOT 指到假目录验自适配 / 其他技能脚本同跑;任一条不过就回滚那一项。 任务经两次改写后重排 19:38。


✅ 19:56 收工:技能包「脚本是脚本、配置是配置」分层全绿落地(会话 [执行]-[技能包env分层]-脚本配置分界)

做成了什么

  • 目录:session-mechanism/env/ 立起来了 —— roots.env(本机真身·不入库)+ roots.env.example(入库样例)。install.py --apply 现在写 env/roots.env。
  • 去硬编码:collabctl.py / keepalive.py / supervise-launch.py / board-launch.py 里写死的本机路径与本机工作区清单全清,改为 DSH_AIROOT(兜底 dirname(DSH_WS_ROOT))+ 扫 <AIROOT>/*/.workbuddy/collab/ 自动发现;任务名按 collabd-keepalive-<basename> 推导。
  • 读配置:全包 23 个 脚本/hook 的 loader 统一成 _cands 写法 —— 向上 4 层 × (env/roots.env 优先 → 包根 roots.env 兼容),再兜底「技能库那份」<CODEBUDDY_CONFIG_DIR>/skills/session-mechanism/env/roots.env(工作区副本靠这条拿本机配置)。
  • .gitignore(技能仓根)三层规则:env/* + !env/*.example(含 */ 与 **/ 变体)⇒ 实测 git check-ignore -v 命中正确。
  • 全仓规矩:技能仓根 改包纪律.md 新增「四、包结构:脚本是脚本,配置是配置」;首个补 env 的是 browser-harness(env/bh.env + bh.env.example,bh_chrome_keepalive.py 全部默认值改 env 驱动)。

验收读数(七条全过)

  • py_compile:scripts/** 34 个 py 全过;bash -n preflight-lock.sh 过。
  • selftest.py:PASS 109 / FAIL 0(报告型 1 条不计)。
  • 逐文件实证 loader(AST 精确切分函数体、清空 DSH_WS_ROOT 后 exec):8/8 命中 env/roots.env;preflight-lock.sh 抽取片段在同目录执行 ⇒ DOCS_ROOT/WS_ROOT 均由该文件解出。
  • install.py --manifest:77 份、语法失败 0(MANIFEST_SKIP 加 env/roots.env ⇒ 入库清单不随机器变)。
  • 钩子冒烟:4 个改动过的 hook 空输入 rc=0;supervise-ensure-hook 真实载荷(cwd=ai1net)rc=0。
  • 自适配:DSH_AIROOT/DSH_WS_ROOT 皆空 ⇒ 优雅报错 rc=1;指假目录 ⇒ 名单空、不崩、不建错任务。
  • 活体:4 区常驻心跳读出;:20099 / :20100 HTTP 200。

遗留(已登记,未动)

  • 文档库那份 07-scripts/handoff-guard.sh(md5 4437d9da…)与包内那份(80d40d9f…)不同,且它仍按位置推导根目录 ⇒ 本次只登记差异,没改文档库那份(发源在技能包内)。
  • 3 个指向空路径的垃圾计划任务(collabd-supervise-agent-product / collabd-supervise-selftest / dsh-board-20099)已 Disabled(不删)。
  • 技能仓与工作区均未 commit/push(用户未提要求)。

✅ 21:50 异机验证 + 提交完成(用户:「确认不同机器环境都可独立配置,脚本运行正常就可以提交」+「提交的时候 需要忽略env文件夹」)

异机验证怎么做:把技能包拷成「新克隆」形态(删掉 env/roots.env,只留 roots.env.example), 另造一台假机器的两个工作区 ws-alpha / ws-beta(<MB>/AIProject/*),配置目录也换成 <MB>/conf。

读数(全部实测)

  • install.py --apply --workspace <MB>/AIProject/ws-alpha ⇒ 自生成该机的 env/roots.env (DSH_AIROOT=<MB>/AIProject),本机工作区名一个都没漏进去。
  • collabctl.py status / keepalive.py status 在假机器上自动发现的是 ws-alpha / ws-beta 两个, 任务名现推为 collabd-keepalive-ws-alpha / -ws-beta ⇒ 写死的本机工作区清单确已消除。
  • 部署副本桥接:把 collabctl.py 放到假工作区的 .workbuddy/collab/(本地无任何 roots.env), 只靠 CODEBUDDY_CONFIG_DIR 兜底 ⇒ 正确读到技能库那份 L0,认出本机 4 个区。
  • board-launch.py:DSH_WS_ROOT 指不存在的目录 ⇒ 拒跑不写文件;留空 ⇒ 优雅报错不崩(⛔ 无写死兜底)。
  • 整套 selftest.py 在假机器上:有宿主库时 PASS 108 / FAIL 1(唯一 FAIL = 「本区没有心跳文件 ⇒ 父链这条验不了(不假装通过)」,纯环境;本机常驻在跑时同一套是 PASS 109 / FAIL 0)。 裸机器(无宿主库):能跑,如实报「验不了」,⛔ 不假绿。
  • ⚠️ 沙箱踩坑:selftest 后台跑会被沙箱掐在宿主集成那几步(输出停在副本同步、无「合计」行); 前台跑才完整(会用 escalation 放行)。

提交

  • 技能仓 E:/ProgramData/.workbuddy/skills ⇒ d81f75f,37 文件、+762/−290。 只提本轮改动(.gitignore / 改包纪律.md / session-mechanism / browser-harness); ⛔ 没碰 product-planning 那笔(video-capture 独立化,属于别的任务)。
  • 发现:20:27 另一个会话的提交 90cc431 把我那次 git mv 的纯改名顺手卷进去了(内容 0 变化)—— 已核,无副作用。
  • env/ 忽略实证(按退出码判):session-mechanism/env/roots.env ⛔被忽略 / browser-harness/env/bh.env ⛔被忽略 / 两个 *.example ✅入库 / *.example.bak ⛔被忽略。头一次用「输出非空=被忽略」判错了(check-ignore -v 对 ! 反选也会打行),将来一律看退出码:0=忽略、1=入库。
  • 另补一条 .gitignore:roots.env(不带斜杠 ⇒ 按文件名匹配任意层)兜住「旧位置的包根那份」。
  • 全仓 git ls-files | grep "/env/" ⇒ 只剩两个 *.example。

遗留:技能仓 master 原先没有上游(git branch -vv 无 [origin/master])。

✅ 21:53 用户选 A ⇒ 技能仓已配上游并推送

  • 推前按技能总仓规矩核对:顶层目录 6 个技能 + 改包纪律.md(白名单符合); 远端当时停在 90cc431,本地 d81f75f 领先 1 个提交 ⇒ 快进,无冲突。
  • git push -u origin master ⇒ 90cc431..d81f75f master -> master,上游已设。
  • 远端复核:origin/master = d81f75f(与本地同一提交); git ls-tree -r origin/master | grep "/env/" ⇒ 只有两个 *.example, 没有任何 roots.env / bh.env 真身(这轮最要紧的一条:配置没跟着上远端)。
  • 工作区仓按惯例补提今日日志。