Files
dsh_ai1net_server/.workbuddy/memory/2026-10-06.md
T

16 KiB
Raw Blame History

2026-10-06 工作日志(本工作区 ai1net-dsh-server)

承接 2026-10-05.md。本日主线:看板改名 + 队列按目标过滤(用户逐字驱动)+ 撤销上轮对 agent-product 的误建。


三十四、看板改名「检查程序」+ 队列按目标过滤(不再历史累积)

用户逐字(两条)

  1. 看板中 常驻程序 改为 目标检查,里面的队列 应该是随着目标的不是目标累积的
  2. (答我反问,纠正我复述的"目标检查")没有常驻程序的说法 ,改为 检查程序

⇒ ⚠️ 正名是 「检查程序」(⛔ 不是"目标检查")。我复述错了一次,被当场纠正。

一、改名:根因是「角色名 ↔ 运行形态」混成一个词(同族第三次)

  • 病根:「常驻」是运行形态(--supervise),不是角色名。把形态当角色 ⇒ 名字里没有"它是干什么的"。
  • 同族前科(同一类错,别只记这一条):
    • 2026-10-03:我把「上报」改成「常驻」,用户驳回逐字 「怎么又把上报 改成常驻了 常驻什么,不是 协作程序常驻吗」。
    • 本轮:用户又要求把「常驻程序」改掉 ⇒ 同一个词第三次被嫌。
  • 判据(以后选名先过这一条):名字必须答"这个位子负责什么",⛔ 不答"它怎么跑的"。
  • 改的范围:只改用户可见文案(标题/tooltip/chip/说明区/aria-label/peer 说明), ⛔ 代码标识符一律不动(prog/rt.prog/文件名)—— 改了会牵动判据与存储。

二、队列按目标过滤(这是真缺陷,非文案)

  • 病根:tasks.json 只增不减 ⇒ 看板那格报的是历史累计,读者看不出「当前目标在跑多少」。
  • 🔴 机制自己早就知道:goalctl.py:577 的「静默陷阱」注释逐字「判据不绑目标」—— 那次只警告没修(怕动存量数据)。⇒ 本条是同一病根的正面修法,⛔ 不是新发现。
  • 修法(四处,缺一不可):
    1. collabd.goal_fp() —— 目标指纹 = sha1(title)[:6] (与 goal_dir_name() 同源 ⇒ 目标目录名尾部的 -xxxxxx 与指纹逐字相同,三处可对着读)。
    2. task_report() 给新条目 setdefault("goal_fp", goal_fp()) —— ⛔ 只打新条目;存量不补(补 = 把历史条目标成"当前目标" = 伪造归属)。 ⚠️ setdefault 只写一次 ⇒ 同一条目后续上报不改归属(防"跑到一半换目标 ⇒ 整条倒戈")。
    3. board._queue(tasks, st, cur_fp) —— 只把 goal_fp == cur_fp 的算进 total/by/open。
    4. 无归属存量单列 legacy(看板显示「另有历史 N 件,不计入」),⛔ 不混进当前目标。
  • 🔴🔴 指纹必须从形参 g 现算(⛔ 不许读 INBOX/goal.json): _goal_block() 也渲染 peer 格(别的工作区的目标)⇒ 读本区文件 ⇒ 用本区指纹过滤对方台账 ⇒ 对方格队列恒为 0(跨工作区静默错,界面上看不出)。 ✅ 实测:vibe-product 格 fp=5d27fe(对方指纹)—— 若串区会显示本区的 c8154d。
  • 空指纹语义:cur_fp 为空(未声明目标)⇒ 如实把全部当 legacy,⛔ 不假装"队列为空" (那是把"没有归属信息"说成"没有活")。
  • ⚠️ cur_fp 的插入位置踩了两次坑:① 插进字典字面量里 ⇒ SyntaxError: ':' expected after dictionary key;② 插到 build() 的 warn = [] 之后 ⇒ 函数归属错(_goal_block() 的 warn 是形参、没有 warn = [] 这行)⇒ 正解是 _goal_block() 内、return { 之前、 _lb = _labor(...) 之后。

三、验收(都跑过,非声称)

  • selftest:PASS 97 / FAIL 0(改前 95/2);install.py --verify ✅ 全绿;--manifest 56 份/语法失败 0。
  • _queue 四情形单元验证全过:当前目标命中(total 4/legacy 3)|另一目标(total 1/legacy 6)| 空指纹(total 0/legacy 7)|无匹配(total 0/legacy 7)。 ⚠️ 第一版自己写错了期望值(把 open 当成 "pending+running")⇒ 实测 open = pending+running+blocked+other ⇒ 是测试错,不是代码错(差点误修代码)。
  • 看板重启后 board.json 实测:本区 fp=c8154d total=0 legacy=1;peer 格 fp=5d27fe。 ⛔ board.py 是代码改动 ⇒ 必须重启看板(只有 board.html 才是每请求实时读盘、不用重启)。

四、顺手修掉两处 selftest FAIL(都是我自己引入的)

  1. 技能里留了项目串:goal_fp() docstring 写了工作区名/历史线名/goal.json 的 id 值 ⇒ 判据「技能就是技能,谁用产生的文件放在他自己那里」报红 ⇒ 已泛化(留事实、去专名)。
  2. pitfalls.md 的 P0-86 重复了两份(3157 行与 3305 行,逐字相同,仅差一个尾空行) ⇒ 删后一份;并把留下的那份从 7.3 KB/密度 2.1 压缩到 ~5 KB(判据要点、变异表、教训一句不丢)。 ⚠️ 判据「经验不许写成流水账」的红线是超长 + 低证据密度(>6 KB 且 密度<4.0), 不是"长了就删" ⇒ 压缩时先保要点。

五、另一个坑:board.html 行尾被改成 CRLF(差点提交 1856 行纯噪音)

  • 现场:board.html 工作副本 CRLF(1856 个 CR),而 repo HEAD 版是 LF,core.autocrlf=false ⇒ git diff 整个文件重写(3544 行)。
  • ✅ 处置:按字节归一化回 LF 再提交(b.replace(b'\r\n', b'\n'))⇒ diff 从 3544 行降到 386 行(真内容)。
  • 🔴 判据(沿用既有铁律):行尾只信字节级(b.count(b'\r')),⛔ 不看编辑器显示。 ⚠️ 提交前若见"整文件重写",先查行尾,别当成真改动。

六、提交

5dc6ced session-mechanism: 看板改名「检查程序」+ 队列按目标过滤(不再历史累积) (4 files:board.html/board.py/collabd.py/pitfalls.md;⛔ 未推送)


三十五、撤销上轮对 agent-product 的误建(详情见 2026-10-05.md §33,此处只记结论)

  • 用户令逐字:删掉 让那个会话自己建立。
  • 删了 4 项 + 1 项连带:tmp/supervise-inbox/goal.json/交付物/任务图-会话协作自检.json/ 执行会话/目标-agent-product-3e3182//collabd-state.json 的 roles 主会话登记/ 配置里的 taskgraph 指针(删载体要连"谁指向它"一起查,否则常驻每 10 s 刷 Errno 2)。
  • 备份:ai1net-dsh-server/tmp/bak/agent-product-misbuild-20261005/。
  • 交接姿态:常驻故意没重起(用户令"让那个会话自己建立")⇒ 我不再插手该工作区。

三十六、⚠️ 技能总仓「提交/推送」发现历史分叉(未决 · 待用户拍板)

用户 05:0x 说「提交」⇒ 核实两仓后发现技能仓不能直接推:

仓 本地 远端 关系
工作区 ai1net-dsh-server 746e4f2 有上游 ✅ 正常可推
技能总仓 workbuddy_skills 5dc6ced(含 5 个未推提交) 19101ac 🔴 两边无共同祖先
  • 🔴 远端是"重建的干净库":远端唯一提交 19101ac init: workbuddy_skills 重建,仅收录 session-mechanism ⇒ 有人把远端历史重写过(git fetch 报 + fe6ce9f...19101ac master (forced update))。
  • 🔴 本地是旧的多提交历史:5dc6ced/e4cbd2f/935e423/4adc431/1eec42e + 更早。
  • ⇒ git merge-base HEAD origin/master 为空 ⇒ 无法快进、无法普通合并。 直接 push 会被拒(non-fast-forward);要上去只能 push --force(覆盖远端历史)。
  • ⚠️ 这是不可逆的对外动作(--force 会丢弃远端那个 init 提交)⇒ ⛔ 不擅自决定,必须用户拍板。
  • ⚠️ 另:技能仓还有 19 个文件的未提交改动(515 增/692 删,非本轮产物,疑似别的会话或早前轮次在途) ⇒ ⛔ 不代它提交(无边界扫进来=伪造别人的工作)。
  • ⚠️ 推送用 [email protected]:maogeigei/workbuddy_skills.git(work 域名,非 github) ⇒ 与 ~/.ssh/config 里的 github 密钥路由无关(别去翻那套)。

三十七、看板 tab 改「按创建时间」稳定排序(用户:「tab 的排列顺序 应该按照创建时间顺序来」)

为什么改(活跃度排序的真实毛病)

  • 旧键 tab_hot = 该块最近会话活动距今分钟数(age_min),10-04 定的「最新在运行的排前面」。
  • 🔴 病根:会话活动每次协作都会变 ⇒ tab 顺序自己会漂。 用户脑子里认的是「第几个 tab 是什么」⇒ 位置一变就要重新找,且无从判断为什么变了。
  • ✅ 新键 tab_created = goal.json 的 declared_at(目标声明时间)—— 目标建立后不动 ⇒ 顺序稳定。
  • ⚠️ 为什么不用 topics_declared_at:两者多数相同,但 declared_at 是目标本身的建立时间, topics_declared_at 是任务类别清单的确认时间(可能后补/缺失)。语义要取前者。
  • ⚠️ 缺 declared_at 的块垫底("~" 大于任何 ISO 时间串),⛔ 不许猜一个时间塞进去。

🔴🔴 本轮最大的坑:declared_at 被块丢掉了(排序静默失效)

  • 块里的 goal 子字典只留 3 键(title/short/acceptance,board.py:1795) ⇒ declared_at 不在里面。
  • 我第一版从 _b["goal"].get("declared_at") 取 ⇒ 恒为空 ⇒ 三块 tab_created 全是 '~' ⇒ 排序形同没做,而"升序"判据照样绿(因为三个都一样,升序恒真)。
  • ✅ 正解:在 _goal_block() 里单独带出 goal_declared_at 字段(从形参 g 取)。 ⚠️ 从 g 取而非回读 INBOX/goal.json:peer 块传的是对方的 goal ⇒ 取到对方时间,正确。
  • 🔴 同族教训(第 N 次):块是"精简过的视图",不是 goal 原样 ⇒ 任何"要参与逻辑的字段"都必须显式带出来,⛔ 不能指望它在块里躺着。

判据重写(附变异对照,证有牙)

  • 夹具改成时间乱序(t2 10-01 最老/t0 10-02/t3 10-03),且文件出现次序就是时间倒序 ⇒ 排序一旦失效,输出顺序立刻不同(⛔ 三个时间同序 ⇒ 判据恒绿,同族栽过)。
  • 新增真造第四块(没有 declared_at)⇒ 必须垫底且 tab_i 最后一位 (⛔ 不造这块 ⇒ 判据恒真 = 假绿)。
  • 新增防回退判据:源码里 ⛔ 不许再出现 tab_hot 当排序键。
  • ⚠️ 我自己写判据时取错层(short 在 goal 子字典里,⛔ 不在块顶层)⇒ 判据恒红,已修。 ⇒ 写判据也要先确认"字段在哪一层",⛔ 别凭印象。
  • 变异对照:A 排序 reverse=True ⇒ 红 ✅|B 排序键换回 tab_hot ⇒ 五条同时红 ✅|还原 ⇒ 绿 ✅。

验收

  • selftest PASS 97 / FAIL 0|install.py --verify ✅ 全绿|--manifest 56 份/语法失败 0。
  • 真起看板实测:i=0 本机协作(2026-10-01T22:29) → i=1 vibe-product(2026-10-05T18:04) ✅。
  • ⛔ board.py 代码改动 ⇒ 必须重启看板(走 dsh-board-keepalive 计划任务,⛔ 不手动裸起)。
  • 提交:5e77f0e(技能仓,未推送)。

⚠️ 顺带记录:本机裸 nohup python board-launch.py & 活不过工具调用边界

  • 实测:nohup ... & 起看板 ⇒ 打印了「看板已起」但进程随即消失(工具调用结束即被收走)。
  • ✅ 唯一可行起法 = 触发计划任务 dsh-board-keepalive(Start-ScheduledTask) ⇒ 与记忆里「常驻起来只有两条路:计划任务 / 会话后台任务+stdout 重定向」一致。

三十八、评估 dsh-task-board 能否移植进「任务会话技能」当工作台(结论:⛔ 不能移植,但可映射概念)

用户问:E:\github\dsh-web\packages\dsh-task-board 能否把这个功能移植到任务会话技能里当工作台。

它是什么(实读,⛔ 非推断)

  • @linxin666/dsh-client-ui-task-board v0.4.4 —— DSH(DeepSeek Harness)的 Web GUI 插件, src/ 下 80 个 TS/TSX 文件、20,825 行。
  • 挂载方式:cordis.patch.yml + dsh.bundle.patch profile 层 ⇒ 只在 DSH 宿主里活。
  • 🔴 依赖面全是 DSH 私有 SDK:@deepseek-ai/dsh-tools/dsh-llm/dsh-api-gateway/ dsh-workspace/dsh-host-webserver/dsh-client-ui-slots/dsh-api-session-controller… 20+ 个包, 另有 cordis(DSH 的插件内核)。

结论:⛔ 不能"移植" —— 宿主不同,不是"换个壳子"的问题

维度 dsh-task-board 本技能(WorkBuddy 侧)
宿主 DSH(DeepSeek Harness) WorkBuddy
插件内核 cordis(dsh.bundle.patch) 技能包 + .workbuddy/collab/ 脚本
界面座位 DSH shell 的 sidebar.panellist / main 座位 自建 board.py --serve(20099)+ 手写 board.html
数据面 DSH Host $DSH_HOME/task-board/ledger-v2.json tmp/supervise-inbox/{goal.json,tasks.json}
执行引擎 DSH /goal 目标驱动器 + dsh-llm-verifier collabd.py 常驻 + 排期拉会话
语言 TypeScript/React 插件 Python + 单页 HTML

⇒ 零修改侵入 DSH 源码是它的卖点,但反向也成立:脱离 DSH 它一行都跑不了。 移植 = 重写(20k 行 TS + React 换成 Python/HTML),⛔ 不是迁移。

✅ 但它有真价值:几处设计可直接映射到本技能的短板

它的机制 本技能的现状 可映射性
Host 权威账本(浏览器只是异步视图,关页面不停调度) 已有同思想(tasks.json 是唯一权威) ✅ 已一致
账本原子写(临时文件 + 原子 rename + revision) tasks.json 写法定过 ⚠️ 可对照补
任务验收门禁(update_goal(complete) 被宿主拦截,不信 agent 自述) 本技能正缺这一环(改完靠检查会话读,无"拦截完成声明") 🔴 最值得借
有界执行历史(每任务只留 20 条) tasks.json 只增不减(本轮刚处理"队列按目标过滤",病根同源) 🔴 值得借
8 个 agent 工具(task_board_list/create/run/schedule…) 机制侧靠 collabd.py --report 等 CLI ⚠️ 形态不同,思路可借
cron 调度器(5 段 + IANA 时区 + DST 语义) 本技能有 automation_update 排期 ⚠️ 已够用

我的判断(一句话)

⛔ 别移植代码(宿主不同、语言不同、依赖 20+ DSH 私有包); ✅ 借两个设计:① 验收门禁(不信自述、宿主侧拦截完成声明)② 有界历史(台账不许无限增长)。 若用户要的是"在 WorkBuddy 里有个像它那样的工作台" ⇒ 那是把现有看板(20099)做厚, ⛔ 不是搬这个包。

⚠️ 待用户确认的语义分叉(属"功能语义分叉",须提报)

用户说"移植到任务会话技能中当作工作台" —— "工作台"指哪个?

  • A:WorkBuddy 里一个看得见、能点的面板(=把现有看板做强)
  • B:只是借用它的任务/验收模型(=改机制,不加界面)
  • C:真要在 DSH 里用(那与本技能无关,装进 DSH 即可) ⇒ 三选一才谈得上下一步,⛔ 别猜。