# 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 删,非本轮产物,疑似别的会话或早前轮次在途) ⇒ ⛔ **不代它提交**(无边界扫进来=伪造别人的工作)。 - ⚠️ 推送用 **`git@work.alotbuy.com: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 即可) ⇒ 三选一才谈得上下一步,⛔ 别猜。 --- ## 三十九、新建「项目管理界面」(照 dsh-task-board 模式 · 目标=项目)— 已交付在跑 ### 用户纠正我(逐字) > 「**不动现有任务会话看板**,**不是让你整体搬**,是照的他的模式做一个 **项目管理的界面** > 来管理 执行任务,**目标等于项目**」 ⇒ ⚠️ 我上一轮把问题问成"要不要移植/怎么移植"=**问错了方向**:用户要的是**新做一个界面**, ⛔ 不是搬迁、⛔ 不是改现有看板。 ### 交付物(新建,⛔ 现有看板零改动) 📁 `交付物/项目管理界面/`(**3 个文件**) | 文件 | 作用 | |---|---| | `project-board.py` | 只读服务 + 映射层(`--serve 20100`/`--once` 打摘要) | | `project-board.html` | 界面:项目下拉 + 四列卡片 + 点开抽屉 | | `project-board-launch.py` | **启动器**(计划任务动作指向它,⛔ 不直起业务脚本) | - **端口 20100**(现有看板 20099 **不动**);计划任务 **`dsh-project-board-keepalive`**(5 分钟判活)。 - ✅ 实测:20100 `HTTP 200`;`/api/projects` 返回 2 个项目/12 张卡片;20099 同时 `HTTP 200`(互不影响)。 ### 🔴 映射关系(唯一真源 = 现有看板的 `/board.json`,⛔ 不重算) | 它的概念 | 本机制 | 数据来源 | |---|---|---| | 项目 | **目标** | `goals[]`(`goal.short`/`tab_created`) | | 工作流分组 | **任务类别** | `labor[]` | | 卡片 | **台账条目** | `tasks{}`(`state` → 列) | | 点卡片 → 会话 | **会话跳转** | `sessions[].id8` | 🔴 **关键决定:⛔ 不直读台账、⛔ 不重算** —— 复用现有看板**已经算好的** `/board.json` (重算 = 把 `board.py` 那套逻辑抄第二份 ⇒ 同族 P0「一条规则两份实现」必漂)。 ### 🔴 列用**机制真实四态**,⛔ 不硬套它那五列 - 它:待规划/待办/进行中/已完成/已失败(5 列) - 本机制台账只有 **待执行/执行中/有阻碍/已完成**(4 态) - ⇒ 硬套 5 列会**凭空多出机制里不存在的「待规划」**(看着像有、其实永远空)⇒ ⛔ 不做。 - ⚠️ 用户问「什么工作流状态 干啥用的」⇒ 就是这一列的含义,已当面解释。 ### 🔴🔴 本轮踩的真坑:`pythonw.exe` 下 `sys.stdout is None`(终端好、计划任务死) - **症状**:计划任务 `Result=1`,**20100 起不来**;但**前台跑同一脚本一切正常**。 - **根因**:计划任务用 `pythonw.exe`(**无控制台**)⇒ **`sys.stdout is None`** ⇒ 我写的 `sys.stdout.flush()` 抛 `AttributeError: 'NoneType' object has no attribute 'flush'` ⇒ 进程**当场崩**。而 `Result=1` 只说"失败",**看不出为什么**。 - **诊断方法(✅ 可复用)**:加一个**只写异常到文件**的 wrapper(`launch-diag.py`), 指向计划任务动作 ⇒ 异常立刻现形(⛔ 别靠猜 `Result=1`)。 - **修法**:加 `_say()` —— **有 stdout 才写、没有就静默丢**;⛔ 常驻进程不许因为没地方打印就崩。 - 🔴 **同族铁律**:**`pythonw.exe` 下 `print`/`sys.stdout` 一律不可靠** ⇒ 凡"计划任务跑的脚本"⛔ 不许裸用 `print`。 ### ⚠️ 另一条弯路(别重蹈) - 我一度以为是**中文路径**(`交付物/项目管理界面`)导致计划任务失败 ⇒ 复制到 ASCII 路径 `tmp/pm-board` **对照** ⇒ **同样 `Result=1`** ⇒ 证明**与路径无关**,真因是上面那条。 - ⇒ 教训:**路径猜测**要有对照实验才下结论(我用了一轮,但及时纠正并清理了临时副本)。 ### 清理 - `tmp/pm-board/`(对照实验副本)**已删**;计划任务已**重指回正式交付目录**。