dsh-plugin-teamflow 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  > 本插件首次公开发布版本为 **v0.1.0**;发布前的内部迭代(v0.3~v0.13)记录于 `AGENTS.md` §5,对外统一归到 v0.1.0。
4
4
 
5
+ ## [0.1.9] - 2026-09-16
6
+
7
+ ### 新增
8
+ - **host 侧响应与产物语言跟随界面语言(中英双语,P2)**:英文用户此前看到的工具返回、完成汇报、流水线日志与产物正文(PRD/QA-REPORT/ACCEPTANCE)仍是中文,产品呈现割裂。语言源链=**客户端推送**(浏览器当前语言/系统探测,经既有 Remote 面 `teamflow/setLocale`)> **宿主用户显式选择** > **`en` 默认**;run 起跑时解析一次并落 run 级快照(`journal.locale`)——**同 run 语言一致、断点续跑沿用、切语言只影响之后新起的 run**;headless(无客户端)走兜底链不报错。覆盖面:11 个 prompt 工厂 + AGENTS.md/memory 模板 + 产物语言指令 + 工具返回/分支决策/完成汇报/日志/诊断/state 注入块/triage 理由。**判据层只增不改**:验收结论行 zh 四档逐字保留并新增 en 四档(`✅ Pass / ⚠️ Conditional pass / ❌ Fail / 📝 Not applicable`;解析器同时兼收 `Not passed` 等价写法)、缺陷表新增 en 表头、triage 关键词新增英文项(中文项一字不动,实测 19 个中文样本档位 0 漂移)、L2 语料只增 6 条 en 样例。仓库 `docs/` 与代码注释**不做双语**(注释统一中文)。独立验收 46/46:11 工厂 + 2 模板 en 产出 **CJK 0 处**、zh 侧逐字不回归、语言链异常不冒泡
9
+ - **ADR-0009:不做插件级用户记忆层(否决记录)**(`docs/adr/0009-user-layer-memory.md`)——曾设计插件自建跨产品线用户层(`_user/{memory.md,index.json}` + 流水线启动时注入索引 + acceptance write-back),**经复核否决**。理由:① **场所错位**——用户偏好是多轮对话的产物,流水线是「一需求一链路」批处理,不存在该场所;② **输入不足**——流水线任一时刻只有当前产品线上下文,「跨产品可复用」的普适性只能靠猜;③ **替代路径已够且已实现**——跨产品用户约定写宿主原生的 `$DSH_HOME/AGENTS.md`(`dsh-agent-instructions` 已无条件注入,插件零代码),插件硬规矩直接进 prompt/模板(`TOKEN_HYGIENE` / `LOG-LAYOUT-SCOPED` / `DOC-BOUNDARY-POLICY` 等);④ **突破 ADR-0002 写域边界**。**零运行时改动**,仅留决策记录与再评估触发信号。同步 ADR 索引:README / README.en / CONTRIBUTING / AGENTS 四处(AGENTS 原为 `0001~0007`,漏了 0008,一并补齐)
10
+ - **工作区 key 迁移演练脚本(只读)**:`scripts/migrate-workspace-key-dryrun.mjs`——扫描 `$DSH_HOME/teamflow/<key>/`,从 `runs/*.json` 的 `workspacePath` 反推来源路径,用与 `store.ts` 同源的 `slugPath` 重算期望 key,产出「逐目录判定(OK/DRIFT/LEGACY/EMPTY/FALLBACK)+ 按路径分组的合并计划 + 同名文件内容冲突检测」。**不写任何数据**(`--out <file>` 只写报表)。用于给「`workspaceScopeOf` 的 UUID 分支不可达 → key 绑路径字符串」这个已知风险定价:本机实测 11 个目录 → 自洽 4、**漂移 0**、旧格式 1、空壳 6,故该风险尚未造成数据分裂,迁移优先级按风险而非损失排(详见 `docs/TODO.md`)
11
+
12
+ ### 修复
13
+ - **缺陷卡点开看不出「缺陷是什么」**:QA 报告里写好的复现步骤/期望行为/实际行为/关联验收项四列此前**被整列丢弃**(解析只留编号/严重级/模块,建卡时四个字段硬编码空串,下发面也没有这几个字段)→ backlog 里点开一张缺陷卡,只看到「QA 缺陷:R3-1」+ 关联 run 的原始需求。现:解析新增富行(四列 + 原始表头映射),缺陷卡存全并幂等刷新、标题自解释(`R3-1 · 日志/注入面`)、下发面补齐字段,工作台两处详情抽屉都新增「缺陷详情」块;**并让 QA 打回修复时的缺陷清单也带上完整描述**(dev 直接看到复现/期望/实际)。附带:QA 表格单元格内的字面量 `|` 现在要求转义 `\|`(不转义会切开单元格、后续列错位)
14
+ - **英文界面下「人看得见的三处」仍是中文(首次英文 run 实测发现)**:① **阶段子代理的回复语言**此前无人约束(只约束了产物文件的语言)→ 文件是英文、工作台里「阶段性产物」却是中文(子代理跟着中文上下文回复);现由 `productCtx`(11 个阶段共用前缀)统一声明 `[Reply language]`,一处覆盖全部阶段。② **给模型的工具返回/会话注入**没点名回复语言 → 主会话模型仍用中文答复用户;现显式写入「用当前语言回复用户」。③ **团队名/描述**来自用户数据 `teams.json`(只有中文)→ 英文界面下拉里是中文;现支持可选 `nameEn`/`descriptionEn`(**不改写存量文件**,内置团队按 id 回落英文),并由宿主按当前语言本地化后下发。顺带:run 日志里的阶段名改走已有的 `phaseLabel`(原先直出 `teams.json` 的中文 label)
15
+ - **QA 缺陷解析误登记(一条流水线因此被误停线)**:旧 `parseDefects` 按**列位置**认缺陷(任一含 `|` 的行 + 第 2 格 ∈ P0-P3),于是 QA 报告里的「round-2 缺陷**复验对照**」表(第 3 格是结论「已关闭」)被登记成一个新的 P2 缺陷 → 每轮复验都重生一个阻断缺陷 → **第 3 轮必然超限停线并跳过产品验收**(自我实现的停线,与交付质量无关)。现改为**按表头认表**:只有表头显式声明严重级列(`严重级(P0/P1/P2/P3)` / `Severity (P0/P1/P2/P3)`)的表格才解析,列位置由表头决定,无严重级表头的表格整表跳过。回归门禁:L2 语料新增真实停线形态 + `test/verdict.test.js` 5 条断言(conformance 18/18 → 19/19)
16
+ - **未验收的 run 不再邀请合回 main**:完成汇报的合回指引旧条件只有「状态 completed 且无 error」——QA 超限时流程提前结束、验收被跳过,status 仍是 completed,汇报照样写「验收已通过,请询问用户是否合回」,会诱导用户在未验收时合回。现要求「已完成 + **无人工介入** + **验收阶段真的 done**」,并在需人工介入且验收未跑时显式提示「本轮未完成产品验收…不要据此合回 main」
17
+ - **en 文案残留的全角标点**:护栏中止摘要的连接符(`runner.ts`)改走词典(en 出半角冒号);en 验收档位行的全角 `/` 改半角 ` / `(zh 侧逐字不变);`store.ts` 运行日志**文件头**随 run 语言;`RETRY_SUFFIX` 补 `attempt N`(词典 `dev.taskRetry` 的实际产出,无 `taskKey` 的兜底路径上任务标题归一不再漏剥离)
18
+ - **收口提交静默失效 4 天:run 跑完却没有 commit(2026-09-11 → 09-15 全部如此)**:英文 lite run 验收时发现产物**全是 staged 但没有 commit**(`git log` 空、`.git` 无 reflog / 无 `COMMIT_EDITMSG`,而 12 个 blob 与索引的写入时间正是 run 收尾那一刻),而 journal 里 `commitDone`/`commitSkip`/`commitFail` **三条一条都没有**——故障完全不可见。根因:上一版「收口提交面」的两处写法互相拆台——`ensureLogGitignore()` 先把 `logs/teamflow/` 写进 `.gitignore`,紧随其后的 `tfAddArgs()` 又用**负 pathspec** `:(exclude)logs/teamflow` **点名**这个「显式点名且被忽略」的路径,git 直接报错**退出 1**(`The following paths are ignored by one of your .gitignore files`;索引其实已写好 → 现场就是「文件 staged 但没有 commit」);而 `add === null ? null : git commit(...)` 用 add 的结果**短路**了提交,`if (cm !== null) … else if (add !== null) …` 又让三条日志一条都不触发。`:(exclude)logs/teamflow/**`、`:(exclude,glob)`、`:(exclude,literal)`、`-c advice.addIgnoredFile=false` 四种变体实测同样 exit 1。**修复(①+③)**:① `tfAddArgs()` 收敛为 `git add -A -- .`(忽略交给刚写好的 `.gitignore`,它才是整树 add 的唯一依赖),并新增 `sanity.tfUnstageArgs()`(`git rm -r --cached --ignore-unmatch -- logs/teamflow`,只动索引不删文件)作为**索引兜底**紧随 add 执行——真摘出东西会记 `log.logsUnstaged` warn(那是 `.gitignore` 防线失效的信号);③ 提交不再被 add 结果短路:永远尝试提交,由提交结果分派 `commitDone`/`commitSkip`/`commitFail`(「无事可做」先用 `git status --porcelain` 空判定,`GIT_NOTHING_TO_COMMIT` 兜措辞),`gitCmd` 之外新增 **`gitRun`**(`{ok, out, error}`,失败原因进日志)——**失败可以处置,但不能不可见**。回归门禁:新增 **`test/commit-path.test.js`(真 git 集成)**——负 pathspec exit 1 的防回退锁、写规则→整树 add→索引兜底→commit 真跑通(断言提交树里没有 `logs/teamflow`)、兜底真摘出东西、失败分类;`test/gitignore.test.js` 与 smoke 断言同步改到新机制(smoke 新增「零回退」断点)。**部署后需重启 `dsh --profile web`**;此前失效期间产生的未提交 run(assetd 09-11、slugkit-en、durparse-en)需人工 `git add -A -- . && git commit`(插件不再回头补提交)
19
+
20
+ ### 改进
21
+ - **QA 轮次收敛埋点(D 方案先测量再立法)**:52 个历史 run 的实测显示「真正需要第 3 轮修复」**从未发生**(打回 7/52、触达上限 1/52,且唯一那次是幻影),而「同一缺陷原样复现就早停」这条判据**按缺陷 id 判不出来**——QA 每轮重新编号(r9 三轮分别 `QA-*` / `R2-*` / `R3-*`)。所以不急着动状态机,先把数据攒起来:QA 循环每轮把阻断集合的**稳定身份**与增/减/停滞计数写进 `journal.qaRounds`(`{round, seq, blocking, p3, defects:[{id,sev,module,fp}], withCheck, withCriterion, qaCalls, fixCalls, gate, newFps, repeats, resolved, outcome}`,留最近 12 轮)。**身份优先级**:缺陷行自带的**检测命令**(B 方案起 QA 必填,机器写给机器看,最稳)> 模块+实际行为文本 > 缺陷 id(最不稳)。配套**只读读侧** `node scripts/qa-rounds-report.mjs`:逐 run 轮次表 + 聚合(收敛 vs 停滞次数、检测命令可用率 = B 落地率、门禁落地率 = A 落地率、单轮成本),让「要不要把 `QA_REWORK_LIMIT` 换成收敛判据」这个问题有数据可答。**埋点只记录、不改变任何行为**(纯函数 `util.defectFingerprint`/`compareDefectRounds`/`qaRoundEntry`,可单测)
22
+ - **QA 打回超限时不再「验收整段跳过」(E 方案:已知问题只读验收)**:旧行为是 QA 复验超限 → 跳过产品验收 → 人工只拿到一个 needs-human 旗标,**任务夹里连 `ACCEPTANCE.md` 都没有**(实锤 tf-mu2ioilr-95l4th:那份验收记录是维护者事后手写的)。现在改成:超限时仍以 **「已知问题」只读模式**跑一次验收,产出交付级视图(逐条 AC 核对表 + **未闭环阻断缺陷清单**,含各自的检测命令)。**硬约束(信息而非判定)**:结论一律**强制为需人工裁定**——prompt 明写"结论只能是 ⚠️/❌,且本 run 不会被提交或合回",host 侧照旧 `humanIntervention=true`、`mergeEligible` 保持 false(`accepted` 永不放行),验收失败也**不改变 run 结局**(只记 warn,保持 completed + needs-human)。汇报里新增 `report.knownIssuesNoMerge`:显式说「结论被强制为需人工裁定,不要据此合回 main」——否则 `acceptanceDone` 变真会让旧的「验收未跑、不要合回」警告消失(这正是这条改动最容易踩的坑)。定价依据(52 个真实 run):验收阶段 **p50 10 / p90 21 calls ≈ 一个 run 的 6%**,而「需人工介入且验收未跑」历史发生 1 次——便宜且不是假设。回归:L1 新增 `ACCEPTANCE-KNOWN-ISSUES`/`ACCEPTANCE-NORMAL-NO-KNOWN-ISSUES`(后者保证常规验收 prompt 零回归),smoke 新增 5 条;`log.accSkipped`(旧的「跳过验收」文案)随之删除
23
+ - **QA 打回闭环:治「修复不完整」而不是「少发现缺陷」(A/C/B 三件套)**:先纠正一个前提——维护者问「打回→修→打回→修 有没有解」,实测 r9 那次的停线**是幻影**(报告第 3 节的「round-2 缺陷复验对照」表被旧位置判定读成新 P2 缺陷;用修好后的解析器现场重放该真实报告 → **0 阻断**),真实轨迹是 **6 → 1 → 0 在收敛**,唯一那轮真实返工的病因是**修复不完整 / 扫描面不完整**(round-1 只改了看得见的实例,同类 4 处留在 `prompts/index.ts`)。故落地三件套:**A. 类别门禁**——`qaFixPrompt` 要求每个 P0–P2 修复落**永久可执行门禁**(verify 套件断言,或随检查一起提交的 grep/脚本断言:修复前失败、修复后通过)+ 证据块给 `gate:` 与 `class sweep:`(类别命中数 before→after);policy 级,缺失由新增的 `FIX_GATE_PATTERN` 记 warn 留痕(host 证明不了门禁真存在,不做硬失败,避免形式主义)。**B. 缺陷的「可执行定义」**——QA 缺陷表新增 `检测命令` + `通过判据` 两列(P0–P2 必填):`检测命令` 是**现在就能失败**的那条命令、`通过判据` 是修好后的期望输出;修复方据此验收、复验方据此回归、**误报用它当场证伪**(r9 的 R2-2a QA 误报在 round-3 就是靠一条命令自证的)。同步:富行解析新增两字段(`parseDefects` 瘦身投影形状不变、旧报告零回归)、缺陷卡持久化两列、`itemDetail` 下发、两处详情抽屉渲染;新增 L2 语料 `qa-report-check-columns.md`(conformance 19/19 → **20/20**)。**顺带修一个真 bug**:表格行原先无条件 `split('|')`——QA 按契约转义的 `\|` 照样被切开(R3-2 的「实际」串进「关联验收项」的实锤根因),而检测命令列几乎必然含 `|`;现改为**按未转义管道符切分并还原 `\|`**。**C. 复验复用**——复验轮经 `state.__runCtx.qaReverify` 显式声明(`QAREVERIFY`,不改 11 个工厂签名):**先原样重跑上一轮探针**(就在 `logs/teamflow/<runId>/scripts/`,正是日志收口决定归档留存的那一类)**再补上一轮没覆盖的面并说明漏在哪**,必须重跑缺陷行自带的检测命令、**不得重造已有基线**(r9 实测后一轮重做了一整份 HEAD 副本 = 50 文件/1 MB)。回归:L1 新增 `FIX-CLASS-GATE`/`QA-DEFECT-EXECUTABLE-DEFINITION`/`QA-REVERIFY-REUSE`/`QA-FIRST-PASS-NO-REVERIFY-NOISE` + `test/verdict.test.js` 新增 6 条断言(含转义管道符与错列门禁)。**未动流程语义**(轮次上限、QA 独立探针要求保持原样——质量第一约束,见 `docs/TODO.md`)
24
+ - **运行日志的根离开用户项目 + 只留有用的(`logs/teamflow` 不再长期躺在你的仓库里)**:此前命令日志直接落在项目内**没有任何清理逻辑**(实测 `products/tetris` 累计 1042 文件/17.3 MB、本插件仓 136 文件/3.0 MB),文件树遍历类工具(eslint/prettier/IDE 索引/agent 全局 grep/无 `.npmignore` 的 npm publish)都会看到它。为什么不干脆一步写进 `$DSH_HOME`:**子代理受 DSH 文件沙箱约束**——`workspace-write` 只允许写会话工作区 + 平台临时区,写 `$DSH_HOME` 直接 `FS_SANDBOX_DENIED`(实测:子代理写 `C:\Users\<u>\.dsh\...` 三步全拒,同构命令写工作区内 exit 0;`$env:TEMP` 又是**每会话**子目录、不可依赖),而 host 进程不受该约束。故改为**两段式**:子代理在工作区暂存(`logs/teamflow/<runId>/`)→ **run 终态由 host 过滤归档到 `$DSH_HOME/teamflow/<workspace>/logs/<runId>/` 并删除项目内副本**(host 自身事件日志 `run.log` 直接落归档位)。**归档只留有用的**:维护者一句「logs 里的文件基本都是没用的吧」促使实测——一次真实 run 130 文件/3.03 MB 里 **93% 是可重跑的命令输出或 git 里一模一样的源码快照**(41% `regression-*.log`+`*.out`、35% `probe/head/**` 快照、17% prompt JSON dump),唯一不可重跑且真被用过的 7% 就是检查脚本。因此归档面收敛为**白名单**:code 扩展名(`.mjs/.cjs/.js/.sh/.ps1/.py/.md`)+ `captures.json` 保留,命令输出(`*.log`/`*.out`/`*.txt`)与快照**一律丢弃**(命令输出在运行期仍有价值——把几百行输出挡在上下文之外;但**不是审计资产**,durable claim 是回复里的 `[Verification evidence]` 块)。配套:① **自愈清扫**——run 起跑按同一白名单处理上次崩溃/被 kill 残留的暂存目录与历史散落的 `<runId>.log`/笔记(`<runId>.log` 直接丢弃:内容与 journal 同源);② **保留 K 次**——每个工作区只留最近 `LOG_ARCHIVE_KEEP=20` 次 run,按 mtime 淘汰;③ 正在运行的 run(暂存目录与归档)一律跳过;④ 归档失败只 warn,暂存留待下次自愈,**run 收尾绝不被日志管理打断**。prompt 侧新增 `[Log lifecycle · policy]`(三处 `[Log discipline]` + `TOKEN_HYGIENE` + 资源表 + AGENTS/memory 模板同源),明确「项目内只是暂存、只留脚本与笔记、dump 不留存、不得提交/自行清理/当项目产物」。收口提交面的两道防线(pathspec 排除 + `.gitignore` 幂等补写)保留,覆盖「run 进行中用户自己提交」的窗口。**存量已按新白名单就地瘦身**:本插件仓归档 136 文件/3.03 MB → **43 文件/278 KB**(丢弃 93 个可重跑输出/快照,2.7 MB)。
25
+ - **生成量纪律:不再制造输出 dump(改由宿主截尾 + spill 承担)**:维护者追问「**为啥会产生这么多文件?是我们流水线带来的,还是 DSH 本身也会有?**」——实测两边都有,但性质不同:**DSH 原生**会把过大的工具结果截成 tail 并把全文 **spill 到会话临时区**(`dsh-spill-*`/`dsh-subprocess-*`,不进你的仓库,`dsh-spill-local` 有启动 TTL 清扫;本机 temp 里 149 个 `dsh-*` 目录共 8.4 MB,主要是 Node 编译缓存);**我们的流水线额外要求**每个执行体把命令输出与临时脚本**落到项目内**。真正制造数量的是**执行体数量**:r9 一次 full run 起了 **10 个子代理**(PRD 1 + 技术 1 + 开发 5 = T1–T5 三个并发 + 两轮 QA 打回修复 + QA 首轮 + 两轮复验;**718 次调用 / 59 分钟,其中 61% 花在打回闭环**),每个都按旧 prompt 把整套件输出 dump 成 `regression-*.log`(一次 443 KB、同一套件重复两遍)与 23 个 per-command `.out`。故**删掉旧约定、改为不落盘**:`TOKEN_HYGIENE` 新增 `[No dump manufacturing]`(**禁止**把命令/套件输出重定向进文件;长输出由宿主截尾并把全文 spill 到你被报告的路径,需要细节时读那里),`logs/teamflow/<runId>/` 只放**要留存**的三类——一次性检查脚本 `scripts/`、不可重跑的命令载荷 `captures.json`、结论 `.md`;并要求**同一用途不得新增编号变体**(`-run2`/`dbg-repro2`/`dbg-scan3` 一律就地覆盖)、**改动前基线只物化一次**(`probe/head/` 共享,实测一次 run 因后一阶段重做而留下两整份 50 文件/1 MB)。少掉的正是「41% 的 `.out` + `regression-*.log`(1.2 MB)」与「35% 的源码快照」这两类。回归:L1 契约改写为 `LOG-NO-DUMP-MANUFACTURE`/`LOG-KEEP-ONLY-DURABLE`/`LOG-DISCIPLINE-NO-REDIRECT`(含**防回退**断点:旧写法 `APPENDED on re-run`、`regression-dev.log` 等出现即失败);执行体数量的收敛留 `docs/TODO.md`(需人决策,先定判据)。回归:新增 `test/runlogs.test.js`(60 断言:路径契约/白名单矩阵/过滤归档/合并语义/自愈清扫/活跃 run 豁免/K 次淘汰/永不抛)+ `test/journal.test.js` 与 smoke 断言改到新落点 + L1 契约 `LOG-LIFECYCLE-ARCHIVED`/`LOG-LIFECYCLE-FILTERED`
26
+ - **客户端界面中英双语(P1:客户端展示层,走宿主 locale 服务)**:工作台文案跟随宿主语言(设置 → 通用 → 语言)**实时切换、无需重启**,机制全部复用宿主能力——`ctx.locale.register(NS, {zh,en})` 注册词典 + `ctx.locale.bind(NS)` 取翻译函数 + slot 注册项声明 `locale: NS`(宿主切语言时重渲染每个 outlet;`sidebar.panellist` / `conversation.view` 的名称用 thunk `() => t(...)`,宿主读时求值)。落地:新增 `client/locales.ts`(247 条 key,zh/en 逐条同形)+ `scripts/i18n-client-codemod.mjs`(一次性改写脚本,186 处字面量 → `t('key')`,保留映射留痕);`client/shared.tsx` 词表**函数化**(`runStatusText`/`kindTitle`/`roleChip`/`stText`,删掉切语言后会变陈旧的模块级常量表)+ `localeTag()` 取代硬编码 `toLocaleTimeString('zh-CN')`;`apply()` 把翻译函数注入纯函数层(词表/格式化/折叠件拿不到组件 prop,只能走模块注入)。**门禁**:smoke 新增「词典 zh/en key 集合逐条一致(en 是兜底语言,漏 key 会让用户看到 raw key)+ 客户端除 console 诊断与存量 phaseKeyOf 映射外零中文字面量 + 5 处 slot 声明 locale + 名称 thunk」。**范围边界(刻意未做)**:host 生成的完成汇报/工具返回/流水线日志、以及流水线产物文档(PRD/QA-REPORT/ACCEPTANCE…)仍是中文——产物语言与「验收结论」字面量是 host 解析契约(`util.parseAcceptanceVerdict` + 冻结语料只增不改),属 P3,见 `docs/TODO.md`
27
+
28
+ ### 已知待办
29
+ - 沿用 [0.1.8] 的待办项,并新增本轮产物,见 `docs/TODO.md`
30
+
31
+ ## [0.1.8] - 2026-09-12
32
+
33
+ ### 新增
34
+ - **全局团队工作台(`sidebar.panellist` + `main`)**:工作台从「某个会话里的一个 tab」升级为应用级主面板——左侧边栏多一个图标(inline SVG,跟随选中态),点开中央主区即整块换成 TeamFlow:左栏是**产品线**列表(`$DSH_HOME/teamflow/<key>` 扫描,含 run 计数/活跃数/最近需求与验收结论/磁盘路径),右栏是该产品线的 **run 列表 + backlog 分组**(需求/任务/缺陷,含按角色 token)。**不依附会话**:面板在 root scope(无 `useSession`/`useProjection`),所以数据面新增按**产品线 key** 寻址的 remote 方法(`products` / `productView` / `productRunDetail` / `productStageDetail` / `productItemDetail`),与会话内工作台同源装配(同一批 journal 与 state.json,非新数据模型)
35
+ - **run 详情进右侧栏 tab**:注册 `teamflow-run` tab 类型(认领 `dsh-resource://teamflow/run/**`),在会话内点 run 即在该会话右侧栏打开完整详情(阶段表 + 官方口径 token + 阶段详情/尝试聚合/验证证据/产出/日志)。地址由 host 生成(client 不拼地址)——与产物预览同一条原则。**右侧栏的会话内容只在对话视图存在**(宿主 `RightbarRoot` 门控),所以全局面板里点 run 默认在**面板内联**显示;要并排看就点「对话右栏」——它会切回对话再打开右栏(seat 在切换后才 bind,故带小步重试);任何一步不可用都降级面板内联并给出**可见提示**(不静默失败)
36
+ - **状态徽章可点筛选(多选)**:分组行上的每个状态计数徽章升级为可点 `filterChip`(选中态实心 + 状态色边框),**多选 toggle**——真实问法是「还没结束的有哪些」(进行中 / 待验收 / 需人工的并集),单选会逼人来回点。**筛选优先于折叠**:选中含终态时自动展开(否则点了「已验收 19」却看不到卡片),清掉筛选回到默认折叠;行尾显示「筛选中 N 项 · 显示 x/y × 清除」,无筛选时不出现(不加噪音)。作用域:backlog **每组独立**,run 标签加同款一行(7 个状态),run 的折叠(最近 8 条 + 进行中置顶)**只作用于筛选结果**;切产品线清空筛选与展开态(面板不重挂载,显式 reset)。**纯客户端过滤**,host 数据面与 slice 上限一律不动(数据不丢,清除即见全部)
37
+ - **右栏入口改为「去发起会话」**:右侧栏是**会话级**的(`RightbarRoot` 只渲染当前会话),旧「切回对话」跳回的仍是用户来时的会话、与 run 无关。改为 `goOwnerSessionAndOpen(target)`:host 侧 `runBrief`/`snapshotOf`/`itemDetail.runInfo` **新增 `ownerSession` 透出**(journal 早有该字段)+ 产物地址的会话段改用 run 的发起会话,client 先 `sessions.open(ownerSession)`、等 `sessions.list.getSnapshot().current` 真的切过去**且**对话 seat 挂载 bind 后再 `openResource`(带就绪判据的小步重试);会话已清理时只提示不跳转,老数据无该字段退回旧行为
38
+
39
+ ### 修复
40
+ - **交付判定信号分级(`judgeDeliverable`)**:dev/qa 产出判定由「全文拒绝词命中即否决」改为三级——① 客观形态(非空 + 阶段长度下限)→ ② **真交付信号**(`DELIVERY_EVIDENCE_PATTERN`:prompt 强制的 `[Verification evidence]` 块 = 命令 + 退出码 + 断言计数)→ ③ 措辞兜底(`REFUSAL_PATTERN` 仅在**无证据块**时才否决)。**修「如实汇报环境限制被判未交付」**:子代理自述「7 个用例与 26 项校验无法执行,属环境性失败」因命中「无法执行」被判 `insubstantial` → 提测门禁停线 → 人工 resume + 重复补跑(已完成任务被重做)。修后命中拒绝词**但有证据块** → 判交付 + 记 warn 留痕(措辞只作诊断,不再是门禁)。删 `hasSubstance`
41
+ - **熔断改用「新增」口径(`freshTokensOf`)**:熔断预算 = `input + cacheWrite + output`(**排除 `cacheRead`**),阈值 `FRESH_TOKEN_BUDGET`(默认 200k)。旧口径把缓存重放计入——实测某 dev 任务 `totalTokens` 1,885,583 ≥ 60k 触发熔断,**真实新增仅 55,439** ⇒ **任何任务失败一次都立刻熔断、`RETRY_LIMIT` 连一次重试都走不到**。修后重试优先于熔断恢复。**汇报/展示口径 `totalTokensOf`(官方 billed)不变**,两套口径不得合并(已在 AGENTS §4/§5 锚定)
42
+ - **收口提交面排除自有日志**:`sanity.tfAddArgs()` = `git add -A -- . ':(exclude)logs/teamflow'`(magic pathspec 强制排除,**不依赖目标仓库有没有配 `.gitignore`**;`-- .` 同时把提交面收敛到工作区),两处提交点(收口提交 + `preAction=commit`)统一走它,**禁止再出现裸 `add -A`**;新增 `util.mergeGitignore()` + `pipeline.ensureLogGitignore()` **提交前幂等补写**工作区 `.gitignore`(覆盖判定含更宽规则 `logs/`、`logs/**`;`changed=false` 时不落盘,不留无谓 diff;写失败只 warn——pathspec 仍兜底)。**修一次收口提交 227 文件里 208 个(92%)是自有日志**(真交付仅 19)——子代理 git 纪律无问题(交付报告写「logs/ remain untracked」当时属实),是 host 在最后一刻扫进去的
43
+ - **prompt 日志布局收口**:`TOKEN_HYGIENE` 新增 `[Log layout · policy]`——**每用途一个文件**:套件输出 → `regression-<phase>.log` 且**重跑时追加**带 `--- <timestamp> <task> ---` 表头(禁止 `-run2`/`-nopipe`/`-shim` 同名变体)、一次性校验脚本 → `scripts/`、命令载荷 → 合并进 `captures.json`、探针/草稿 → `probe/`;dev/qa/qaFix 三处 `[Log discipline]` 指向该布局。**实测消灭 51 份重复套件输出(占 `.log` 78%,267.5 KB)**,同一沙箱绕行被各 agent 重新发明 6+ 次的问题一并收敛
44
+ - **日志布局路径作用域**:上条的四条路径写成**未限定相对昵称**(`scripts/`、`probe/`)→ 模型按「最像项目约定」解析成**项目根** → 在仓库根建了 `scripts/`(5) 与 `probe/`(1) 且被收口提交扫进去(32 文件里占 6 个)。修复:四条路径全部改写为**完整限定** `logs/teamflow/<runId>/...`,段首加粗「never create scripts/ or probe/ at the project root」,三处 `[Log discipline]` 各自重申;L1 契约新增 `LOG-LAYOUT-SCOPED`(断言完整路径 + 项目根禁令,`exclude` 未限定旧写法防回退)。**教训:给模型指路径必须给完整限定路径,不能给通用昵称**
45
+ - **右栏 run tab 卡在「读取中」**:正文读地址必须用宿主绑定的 **`useTabInfo`**(slot 声明 `hooks: { tabInfo }` 会被渲染器改名为 `use<Name>`),prop 名写成 `tabInfo` 取不到 `tab.navigation.address || tab.contentId`
46
+ - **二次 unwrap / 空信封静默失败**:去掉阶段详情 / 条目详情 / 面板内联 run 详情的**二次 unwrap**(首层已解包,二次解包取到 `undefined` → 表现为「读取中」或空白);`unwrap` 对 `ok=true` 但无 `value` 的**空信封显式报错**,不再静默返回 `undefined`
47
+ - **React #310(hook 归属错位)**:`FoldableText` 被当普通函数调用(`FoldableText(...)`)而非作为组件渲染 → hook 挂到父组件,叠加条件渲染导致**每次渲染 hook 数变化**。改为组件用法
48
+ - **工作台顶出外层页面滚动条**:面板改用宿主 `.viewArea` **高度契约**布局(不再用 `100vh` 一类硬高度),消除「页面级滚动条 + 面板内滚动条」双层滚动;并恢复看板**列内滚动**(限高 340)+ 列头/分组标题 sticky
49
+ - **同值点击产品线卡在「读取产品线数据中…」**:重复点击同一产品线不再无响应——`viewTick` 重载 + 选中态提示
50
+ - **窄列卡片内容溢出**:等宽数字行(token/耗时)在窄列顶破面板 → 收敛为可换行/截断
51
+ - **详情浮层单一事实源**:修「run 详情与 backlog 详情同时存在、要关两次」——详情状态收敛为单一来源
52
+ - **分栏改用 `grid auto-fit`**:修 `flex-wrap` 多行 flex 行高随内容 → 列被撑高、`overflow` 永不触发导致「展开后无法滚动」
53
+
54
+ ### 改进
55
+ - **客户端展示层收拢**:主题 token / 状态词表 / 格式化(token 官方口径、时间、耗时、折叠文本)从 1286 行的 `client/index.tsx` 抽到 `client/shared.tsx`,会话内工作台与全局面板共用一份——两处展示语言不会再各自漂移
56
+ - **宿主 slot 契约对齐**:`dsh.client.inject` 补 3 个 slot owner 包(`ui-layout` / `ui-sidebar` / `ui-sidebar-right`,注册进谁的 slot 就列谁)+ 对应 optional peer 声明,避免加载顺序不确定导致的「slot 不存在」
57
+ - **全局面板第三版布局 —— 主区标签页 + 详情覆盖式浮层**:第二版把 **rail + run 栏 + backlog 栏 + 详情栏**四栏并排并叠了 `grid auto-fit` 自适应,在 1100–1400px 窗口**必然超载**(卡片被压到 ~200px、run 行折成多行、详情栏还和列表抢宽度),并触发连环故障(`flex-wrap` 行高随内容 → 列被撑高、`overflow` 永不触发 → 展开后无法滚动)。第三版**做减法**:① 主区改为**标签页**(🚀 流水线 run N | 📋 Backlog M),一次只显示一个列表——宽度全给它、只剩一个滚动区;② 详情改为**覆盖式浮层**(绝对定位 + 独立滚动,与会话内两个抽屉同款),不再参与横向宽度分配;③ 删掉 panel 级 grid/flex 两栏自适应(backlog 卡片自身的 `auto-fill` 网格保留)。折叠 / 进行中置顶 / 终态收起 / 需人工不折等已确认行为全部保留。**教训:并排面板数量必须由可用宽度决定,不是由信息架构决定**
58
+
59
+ ### 已知待办
60
+ - 全局面板目前**只读**(未提供 backlog 流转写路径);run tab 未注册 `sidebar.right.pane.tab.title` seat(chip 标题取自类型定义);两处渲染组件仍分叉(`shared.tsx` 只统一了词表/格式化)。见 `docs/TODO.md`
61
+ - 熔断阈值(`FRESH_TOKEN_BUDGET`,默认 200k)仍是常量,未做成 service Config;护栏**复读检测**仍读已弃用的事件读取器(提醒通道与挂死判据已迁官方投影)。见 `docs/TODO.md`
62
+
5
63
  ## [0.1.7] - 2026-09-11
6
64
 
7
65
  ### 新增
package/README.en.md CHANGED
@@ -42,19 +42,21 @@ requirement → PRD (based on existing patterns / product memory, archived to pr
42
42
 
43
43
  ## Core Features
44
44
 
45
- - **Anti-fake-delivery**: ① Substantive validationoutputs containing rejection phrases ("I cannot complete", etc.) or below the per-stage length floor are treated as undelivered and routed to retry / human intervention; ② Token circuit breaker — a single stage accumulating 60k budget stops retries; ③ Context-exhaustion failures are not retried (retrying the same prompt likely reproduces); ④ Product-level concurrency lock — only one active pipeline per product at a time, preventing requirement state from stepping on itself; ⑤ Full stage outputs are retained (memory + disk) for the detail drawer and checkpoint resume.
45
+ - **Anti-fake-delivery**: ① Delivery is judged by **tiered signals** objective shape (non-empty + per-stage length floor) → real-delivery signal (a `[Verification evidence]` block) → **wording only as a fallback** (rejection phrases like "I cannot complete" count as undelivered **only when there is no evidence block**; a matched phrase with an evidence block is logged as a diagnostic and never vetoes honestly reporting environment limits is no longer a false failure); ② Token circuit breaker — new tokens accumulated per call (`input+cacheWrite+output`, cache hits excluded) beyond 200k stop retries and require human intervention; ③ Context-exhaustion failures are not retried (retrying the same prompt likely reproduces); ④ Product-level concurrency lock — only one active pipeline per product at a time, preventing requirement state from stepping on itself; ⑤ Full stage outputs are retained (memory + disk) for the detail drawer and checkpoint resume.
46
46
  - **Auto completion report to main thread**: when a pipeline ends (success / failure / cancel / interrupt), it automatically delivers a summary (status / stage stats / total token / backlog / next-step guidance) to the initiating session's Agent — wakes on idle (followup), injects next-step context when busy (inject), using the same mechanism as DSH's background-task notifications (tool-jobs mode, but independently implemented and not dependent on the web-disabled tool-jobs). The user need not watch the panel; the model relays the result or continues per guidance (claim defects / transition / resume from checkpoint).
47
47
  - **Resume from checkpoint**: every stage checkpoint persists to `$DSH_HOME/teamflow/runs/<runId>.json` (LangGraph checkpointer semantics); after a process crash / restart it is auto-marked `interrupted`, and `teamflow_resume` / the panel's "↻ resume from checkpoint" continues from the first unfinished stage (skipping completed stages, reusing full stage outputs).
48
48
  - **Backlog persistence (workspace-isolated since v0.1.0)** under `$DSH_HOME/teamflow/<workspace>/backlog/` as `requirements.json` / `tasks.json` / `bugs.json`, surviving restarts; backlog is isolated per "workspace (project)" — one workspace is one product line, and different workspaces each see their own Team Workspace.
49
49
  - **Single-task model **: one requirement = one rotating task card (no longer split by role); the task card records `devAssign` / `qaAssign` / acceptor, with state rotation: todo → developing → to-test → testing → to-accept → accepted | bounced | needs-human; the delivered frontend page also shows each role's **real token usage** spent on that task.
50
- - **Artifact consolidation **: pipeline docs (PRD / design / architecture / tech spec / QA / memory / history) all consolidate into `docs/teamflow/`, command run logs into `logs/teamflow/<runId>/`, so the host `docs/<role>/` and project root are no longer polluted by TeamFlow; the host-side run log likewise lands in `<workspace>/logs/teamflow/<runId>.log`.
50
+ - **Artifact consolidation **: pipeline docs (PRD / design / architecture / tech spec / QA / memory / history) all consolidate into `docs/teamflow/`, command run logs are staged into `logs/teamflow/<runId>/` while the run is live, so the host `docs/<role>/` and project root are no longer polluted by TeamFlow.
51
+ - **The log root lives in `$DSH_HOME`, not in your project (v0.1.9)**: sub-agents are confined by the DSH file sandbox (`workspace-write` can only write the session workspace), so they stage inside the project first; **as soon as the run ends the host archives what is worth keeping to `$DSH_HOME/teamflow/<workspace>/logs/<runId>/` and deletes the in-project copy** (the host's own event log `run.log` is written straight to the archive). **No output dumps are manufactured**: sub-agents are told *not* to redirect command/suite output into files — long output is already truncated to its tail by the host, which spills the full text to a temp path it reports (native DSH behaviour). What survives is therefore only your **checkers** (`scripts/`), **non-derivable payloads** (`captures.json`) and conclusion notes (`.md`); everything else (`*.log`/`*.out`/`*.txt`, source snapshots) is **dropped at archive time**. Measured on a real run, 93% of the bytes were rerunnable output or an exact copy of something already in git. A crashed/killed run leaves no residue (the next run in the same workspace applies the same rule — self-healing), and **each workspace keeps only the latest 20 runs**.
52
+ - **Delivery surface vs. noise**: only **code + the `docs/teamflow/` task folder** go into the closing commit (one commit per run); `logs/teamflow/` is the plugin's own run log (including sub-agent scratch verification scripts) and is **not a deliverable** — before committing, the host appends that rule to the workspace `.gitignore` (idempotent, visible in the same commit) and, right after the whole-tree `git add`, **unstages the directory again** (`git rm -r --cached --ignore-unmatch` — index only, your files stay put), so **your project needs no pre-configured .gitignore** (these two guards cover the window where you commit yourself while a run is live). If a repo already committed that noise, run `git rm -r --cached logs/teamflow` there to untrack it (local files are kept).
51
53
  - **State machine + event log**: requirement (initiated → in-progress → to-accept → accepted), task (todo → developing → to-test → testing → to-accept → done | bounced | needs-human), defect (to-claim → in-progress → fixed-to-verify → closed).
52
54
  - **Bounce-back threshold**: 2 consecutive Agent failures in a single stage auto-retry; still failing → `needs-human`, requiring human intervention.
53
55
  - **Concurrency pool**: dev tasks run in parallel by `maxConcurrency` (default 3, max 8).
54
56
  - **QA defect registration**: the QA report outputs in a fixed table → auto-parsed into bugs entering the backlog.
55
57
  - **Token metering (official semantics)**: each stage records `usage` = **cache-miss input / cache-hit input / write-cache / output + call count** (accumulated per event by the sub-agent session) + **cache hit rate** (cacheRead / (input + cacheRead)). Workspace cards / task cards / completion reports all display in this basis — model-agnostic and consistent with the official bill.
56
- - **lite mode **: lightweight micro-features — `teamflow_start(lite:true)` skips the standalone tech-spec doc stage (PRD is the contract) and goes straight **PRD → dev → QA → acceptance**; with `needDesign:true` it **keeps the UI/UX design stage**. Measured ~64% time / ~88% token savings vs the full 7-stage run.
57
- - **Token circuit breaker**: when a stage's official total consumption (input + cacheRead + cacheWrite + output accumulated) exceeds `STAGE_TOKEN_BUDGET` (default 60k), retries stop and human intervention is required.
58
+ - **lite mode**: lightweight micro-features — `teamflow_start(lite:true)` skips the standalone tech-spec doc stage (PRD is the contract) and goes straight **PRD → dev → QA → acceptance**; with `needDesign:true` it **keeps the UI/UX design stage**. The point is trimming the stage set to match requirement size instead of running a full waterfall on a micro-feature (the `patch` tier is smaller still: single-point confirmation + dev).
59
+ - **Token circuit breaker**: when the **new tokens** accumulated per call (`input + cacheWrite + output`, **cache hits excluded**) exceed `FRESH_TOKEN_BUDGET` (default 200k), retries stop and human intervention is required; reporting/display still uses the official billed basis (`totalTokensOf`). Cache hits are cheap replays — counting them here would mean "any single failure trips the breaker, making auto-retry dead code" (see `docs/devlog.md` entry 15).
58
60
  - **🏭 Team Workspace (Web tab)**: a session-header tab alongside chat / trace, containing:
59
61
  - Pipeline graph (stage swimlanes + node cards: status / duration / token / sub-agent session, 2s live refresh)
60
62
  - **Backlog drag-drop kanban** (requirement / task / defect three status swimlanes, cards dragged to transition, native HTML5 DnD zero-dep)
@@ -62,6 +64,8 @@ requirement → PRD (based on existing patterns / product memory, archived to pr
62
64
  - Human-intervention center (needs-human items aggregated + one-click terminal state)
63
65
  - History run switching + product switching
64
66
 
67
+ - **Bilingual UI — Chinese / English (v0.1.9, P1 client surface)**: the workbench follows the host language (Settings → General → Language) and **switches live, no restart** — it rides the host `ctx.locale` service (the plugin registers dictionaries and declares `locale` on its slot entries, so every outlet re-renders on a switch) rather than a bespoke i18n layer. Status/phase/role/token-metering vocabularies and time formatting all go through one lookup table; all 247 keys are paired zh↔en, guarded by `test/smoke.js` (the two key sets must match, and no Chinese copy may remain in the client outside `console` diagnostics). **Scope boundary**: the **client display layer only**. Host-generated completion reports, tool results, pipeline logs and artifact documents (PRD/QA-REPORT/ACCEPTANCE…) remain Chinese — artifact language and the acceptance-verdict line are a host parsing contract, tracked as P3 (see `docs/TODO.md`).
68
+
65
69
  ## AGENTS.md minimal-invasion principle (important)
66
70
 
67
71
  AGENTS.md is unconditionally injected into every session by the harness; it is **team assets**. TeamFlow follows separation of concerns:
@@ -78,7 +82,7 @@ web profile host composition
78
82
  ├── teamflow-host (dsh-plugin-teamflow/host) Cordis service `teamflow`
79
83
  │ └── TeamflowService extends TypertRemoteService
80
84
  │ ├── ctx.typert.register(strict descriptors) ← 17 Remote methods
81
- │ ├── ctx.tools.register(teamflow_*) ← 11 model tools
85
+ │ ├── ctx.tools.register(teamflow_*) ← 12 model tools
82
86
  │ └── node:fs → $DSH_HOME/teamflow/...
83
87
  └── teamflow-client (dsh-plugin-teamflow/client, auto-scanned) ← package.json declares dsh.client,
84
88
  └── ctx.remote.$mount(TEAMFLOW_REMOTE_CONTRIBUTION) no patch line needed, clientModules auto-registers
@@ -113,6 +117,14 @@ dsh-plugin-teamflow/
113
117
  - Node.js ≥ 22.18;
114
118
  - Relies on host-provided `@deepseek-ai/dsh-*` and `react` (peerDependencies, injected by the host — no separate install needed).
115
119
 
120
+ ### Version anchor (dsh host compatibility)
121
+
122
+ This plugin is developed and verified against **dsh v0.1.5-rc.2 (2026-09-10, tag `dsh-v0.1.5-rc.2`)**; on npm the `@deepseek-ai/dsh` package has `next`=0.1.5-rc.2 and `latest`=0.1.5-rc.1 (`latest` lags behind `next` — do not use `latest` to judge the release line). `peerDependencies` stay at `*` (host-injected, deliberately loose), and `package.json` declares the compatibility window **`engines.dsh: ">=0.1.5-rc.2 <0.2.0"`** plus **`dsh.manifestVersion: 1`** — dsh does not read or validate either field today (they exist as types only), so they are declarative author metadata.
123
+
124
+ Compatibility check of 2026-09-10 (dsh 0.1.5-rc.2): plugin panel slots (the old `conversation` root slot → the `conversation` key under `main`), session format V3 + Session lifecycle (`SessionHandle`, async `agentLoop.create()`, session locks), removal of `ctx.agent` and typed Inbox, adjusted default tools for SDK/Headless/ACP, subprocess handles without pid — **the plugin is compatible with all of them** (it uses none of the changed interfaces; the `conversation.view` / `conversation.input.right` declarations are unchanged and no slot was removed). One follow-up item:
125
+
126
+ - **The synchronous session event readers are deprecated** (`session.eventAt()` / `snapshotEvents()` / `ownEvents()`; since 2026-09-09 the host allows existing calls but forbids new ones, aiming to stop keeping the full event sequence resident in memory): **token metering now prefers the official Session projection** (`ctx.sessionProjections.stateOf(session,'tokenUsage')` for the four buckets + `'sessionStats'.steps` for the call count), with event scanning degraded to a fallback for hosts without projections; **the guard reminder channel moved to the official `Agent.inject()` and the stall check to the official `subagentTiming` projection's `active.through`** (long silent tools are still exempted by the agent-activity guard). Only **repeat detection** still reads events (it needs streaming text; the official replacement — subscribing to `'session/event'` post-commit delivery — requires an equivalent predicate first, see `docs/TODO.md`).
127
+
116
128
  ## Install (for users)
117
129
 
118
130
  ```bash
@@ -124,7 +136,7 @@ dsh plugin --profile web add file:./plugins/dsh-plugin-teamflow
124
136
  ```
125
137
 
126
138
  After install, **restart** `dsh --profile web` for the host `teamflow-host` to take effect:
127
- - The model side gains 11 `teamflow_*` tools: `start / triage / status / backlog / claim / update / assign / cancel / resume / pause / resume_session`;
139
+ - The model side gains 12 `teamflow_*` tools: `start / triage / status / backlog / claim / update / assign / cancel / resume / pause / resume_session / merge`;
128
140
  - The browser session header shows the "🏭 Team Workspace" tab;
129
141
  - Backlog is written to `$DSH_HOME/teamflow/<product>/backlog/*.json`.
130
142
 
@@ -160,7 +172,7 @@ node --check lib/host.mjs lib/client.js lib/store.mjs lib/descriptors.mjs
160
172
  npm run bundle # build client (tsdown → lib/client.js, __ModuleLoader__.load registers)
161
173
  ```
162
174
 
163
- **For plugin developers** (the local dev loop of THIS plugin): see [`AGENTS.md`](./AGENTS.md) and [`docs/adr/`](./docs/adr) in the repo — deployment sync (`node deploy.mjs` → restart `dsh --profile web`), the "running web loads the host from the profile deployment copy, building source alone does not take effect" caveat, design decision records (ADR-0001~0008) and benchmarks (`docs/benchmarks/`). All repo source is TS/TSX and must be built first (`pnpm bundle`) to run (`strip-types` does not apply under `node_modules`).
175
+ **For plugin developers** (the local dev loop of THIS plugin): see [`AGENTS.md`](./AGENTS.md) and [`docs/adr/`](./docs/adr) in the repo — deployment sync (`node deploy.mjs` → restart `dsh --profile web`), the "running web loads the host from the profile deployment copy, building source alone does not take effect" caveat, design decision records (ADR-0001~0009) and benchmarks (`docs/benchmarks/`). All repo source is TS/TSX and must be built first (`pnpm bundle`) to run (`strip-types` does not apply under `node_modules`).
164
176
 
165
177
  Note: `lib/` is excluded by `.gitignore` but must ship with the package (`files` whitelist includes `lib/`; `exports["./client"]` points to `./lib/client.js`).
166
178
 
package/README.md CHANGED
@@ -42,26 +42,32 @@ TeamFlow 团队研发流水线 —— DeepSeek Harness 可分发插件(`dsh pl
42
42
 
43
43
  ## 核心特性
44
44
 
45
- - **防假交付**:① 实质校验——拒绝措辞("我无法完成"等)或低于阶段长度下限的输出视为未交付,走重试/需人工;② token 熔断——单阶段累计 60k 预算,超限停止重试;③ 上下文耗尽类失败不重试(重试同一 prompt 大概率复现);④ 产品级并发锁——同一产品同时只允许一条活跃流水线,防需求状态互踩;⑤ 阶段产物全文保留(内存 + 磁盘,供详情抽屉与断点续跑读取)。
45
+ - **防假交付**:① 交付判定按**信号分级**——客观形态(非空 + 阶段长度下限)→ 真交付信号(`[Verification evidence]` 证据块)→ **措辞兜底**(仅在**无证据块**时才把"我无法完成"等拒绝措辞视为未交付;命中措辞但已带证据块只记诊断、不否决——如实汇报环境限制不再被误杀);② token 熔断——单次调用累计**新增**消耗(`input+cacheWrite+output`,不含缓存命中)超 200k 停止重试转人工;③ 上下文耗尽类失败不重试(重试同一 prompt 大概率复现);④ 产品级并发锁——同一产品同时只允许一条活跃流水线,防需求状态互踩;⑤ 阶段产物全文保留(内存 + 磁盘,供详情抽屉与断点续跑读取)。
46
46
  - **完成汇总自动汇报主线程**:流水线结束(成功/失败/取消/中断)后自动把汇总(状态/阶段统计/token 总计/backlog/后续操作指引)投递给发起会话的 Agent——空闲时唤醒(followup),忙碌时注入下一步上下文(inject),与 DSH 后台任务通知同款机制(tool-jobs 模式,但独立实现,不依赖 web 面被禁用的 tool-jobs)。用户无需盯面板,模型会转述结果或按指引继续(认领缺陷/流转/断点重跑)。
47
47
  - **断点续跑**:每阶段 checkpoint 落盘 `$DSH_HOME/teamflow/runs/<runId>.json`(LangGraph checkpointer 语义);进程崩溃/重启后自动标记 `interrupted`,可用 `teamflow_resume` / 面板「↻ 从断点重跑」从第一个未完成阶段继续(跳过已完成阶段,复用阶段产物全文)。
48
48
  - **backlog 持久化(v0.1.0 起按工作区隔离)到 `$DSH_HOME/teamflow/<workspace>/backlog/`**
49
49
  `requirements.json` / `tasks.json` / `bugs.json`,跨重启不丢;backlog 按「工作区(项目)」隔离——一个工作区就是一条项目线,不同工作区各看各的团队工作台。
50
50
  - **单任务模型**:一个需求 = 一张轮转任务卡(不再按角色拆任务),任务卡记录 `devAssign` / `qaAssign` / 验收人,状态轮转:待办→开发中→待测试→测试中→待验收→已验收|打回|需人工;交付前端页面同时展示每个角色花在该任务上的**真实 token usage**。
51
- - **产物收口**:流水线文档(PRD/设计/架构/技术方案/QA/记忆/历史)全部收口到 `docs/teamflow/`,命令运行日志收口到 `logs/teamflow/<runId>/`,宿主 `docs/<职责>/` 与项目根不再被 TeamFlow 污染;host 端 run 日志同样落 `<工作区>/logs/teamflow/<runId>.log`。
51
+ - **产物收口**:流水线文档(PRD/设计/架构/技术方案/QA/记忆/历史)全部收口到 `docs/teamflow/`,命令运行日志在 run 期间暂存到 `logs/teamflow/<runId>/`,宿主 `docs/<职责>/` 与项目根不再被 TeamFlow 污染。
52
+ - **日志根在 `$DSH_HOME`,不在你的项目里(v0.1.9)**:子代理受 DSH 文件沙箱约束(`workspace-write` 只能写会话工作区),所以只能先在项目内暂存;**run 一结束 host 就把其中值得留的东西归档到 `$DSH_HOME/teamflow/<workspace>/logs/<runId>/` 并删掉项目内副本**(host 自身的事件日志 `run.log` 直接落归档位)。**不制造输出 dump**:子代理被要求**不要**把命令/套件输出重定向进文件——长输出本来就由宿主截成 tail、全文 spill 到它报告的临时路径(这是 DSH 原生能力),所以要留存的只有**检查脚本**(`scripts/`)、**不可重跑的命令载荷**(`captures.json`)与结论笔记(`.md`),其余(`*.log`/`*.out`/`*.txt`/源码快照)**归档时一律丢弃**。实测一次真实 run 里 93% 是可重跑输出或 git 里已有的副本。崩溃/被 kill 的 run 不留残渣:下次同工作区起跑按同一标准处理残留(自愈),**每个工作区只保留最近 20 次 run**。
53
+ - **交付面与噪音隔离**:只有**代码 + `docs/teamflow/` 任务夹**进收口提交(一个 run 一个 commit);`logs/teamflow/` 是插件自己的运行日志(含子代理的临时验证脚本),**不属于交付物**——host 提交前先把这条规则**幂等写进工作区 `.gitignore`**(随本次提交可见),整树 `git add` 之后再**把该目录从索引里摘掉**(`git rm -r --cached --ignore-unmatch`,只动索引、不删你的文件),因此**目标项目不需要预先配置 .gitignore**(这两道防线覆盖的是「run 进行中你自己提交」的窗口)。若你的仓库已经提交过这批日志,可在目标仓库执行 `git rm -r --cached logs/teamflow` 移出(本地文件保留)。
52
54
  - **状态机 + 事件日志**:需求(立项→进行中→待验收→已验收)、任务(待办→开发中→待测试→测试中→待验收→完成|打回|需人工)、缺陷(待认领→处理中→已修复待验→已关闭)。
53
55
  - **打回阈值**:单阶段连续 2 次 Agent 失败自动重试,仍失败 → `needs-human`,需人工介入。
54
56
  - **并发池**:开发任务按 `maxConcurrency`(默认 3,最大 8)并行执行。
55
57
  - **QA 缺陷登记**:QA 报告按固定表格输出 → 自动解析成 Bug 进入 backlog。
56
58
  - **token 计量(官方口径)**:每阶段记录 `usage` = **输入(缓存未命中)/输入(缓存命中)/写缓存/输出 + 调用数**(由子代理会话逐事件累计)+ **缓存命中率**(cacheRead/(input+cacheRead))。工作台卡片/任务卡/完成汇报均按此口径展示,模型无关、与官方账单一致。
57
- - **lite 模式**:微功能轻量——`teamflow_start(lite:true)` 跳过独立技术方案文档阶段(PRD 即契约),直接 **PRD → 开发 → QA → 验收**;配套 `needDesign:true` 时**保留 UI/UX 设计阶段**。实测较完整 7 段省 ~64% 时间、~88% token。
58
- - **token 熔断**:单阶段官方总消耗(input+cacheRead+cacheWrite+output 累计)超 `STAGE_TOKEN_BUDGET`(默认 60k)时停止重试、需人工介入。
59
- - **🏭 团队工作台(Web tab)**:与 chat / 轨迹并列的会话头部 tab,含:
60
- - 流水线图形工作流(阶段泳道 + 节点卡片:状态/耗时/token/子代理会话,2s 实时刷新)
61
- - **Backlog 拖拽看板**(需求/任务/缺陷三组状态泳道,卡片拖拽流转,原生 HTML5 DnD 零依赖)
62
- - 成本中心(每阶段 token + 总计 + 运行时长)
63
- - 人工介入中心(needs-human 项聚合 + 一键终态)
64
- - 历史 run 切换 + 产品切换
59
+ - **lite 模式**:微功能轻量——`teamflow_start(lite:true)` 跳过独立技术方案文档阶段(PRD 即契约),直接 **PRD → 开发 → QA → 验收**;配套 `needDesign:true` 时**保留 UI/UX 设计阶段**。用「按需求规模裁剪阶段集」换流程重量,避免一个微功能套完整瀑布(`patch` 档更小:单点确认 + 开发)。
60
+ - **token 熔断**:单次调用累计**新增**消耗(`input+cacheWrite+output`,**不含缓存命中**)超 `FRESH_TOKEN_BUDGET`(默认 200k)时停止重试、需人工介入;汇报/展示仍按官方 billed 口径(`totalTokensOf`)。缓存命中是廉价重放,把它计入熔断会让「任何任务失败一次就熔断、自动重试形同虚设」——见 `docs/devlog.md` 补 15。
61
+ - **🏭 团队工作台(双入口)**:
62
+ - **会话内 tab**:与 chat / 轨迹并列的会话头部 tab,含:
63
+ - 流水线图形工作流(阶段泳道 + 节点卡片:状态/耗时/token/子代理会话,2s 实时刷新)
64
+ - **Backlog 拖拽看板**(需求/任务/缺陷三组状态泳道,卡片拖拽流转,原生 HTML5 DnD 零依赖)
65
+ - 成本中心(每阶段 token + 总计 + 运行时长)
66
+ - 人工介入中心(needs-human 项聚合 + 一键终态)
67
+ - 历史 run 切换 + 产品切换 + 「⇥ 右栏打开 run 详情」
68
+ - **全局面板**(v0.1.8):侧边栏图标 → 中央主区整块切换为**产品线视角**——左栏产品线列表(`$DSH_HOME/teamflow/<key>`,含 run 计数/活跃数/最近需求与结论),右栏该产品线的 run 列表 + backlog 分组(不依附会话,跨会话可用)。点 run 在**面板内联**看详情(阶段/尝试/验证证据/产出/日志);要并排看产物就点 run 行的「对话右栏」= 切回对话并在右侧栏打开(**右侧栏的会话内容宿主只在对话视图渲染**,这是宿主设计,不是面板缺陷;任何一步不可用都会降级为面板内联并给出可见提示)
69
+
70
+ - **界面中英双语(v0.1.9,P1 客户端面)**:工作台文案跟随宿主语言(设置 → 通用 → 语言)**实时切换、无需重启**——走宿主 `ctx.locale` 服务(插件注册词典 + slot 注册项声明 `locale`,宿主切语言时重渲染每个 outlet),不自建 i18n;状态/阶段/角色/token 口径/时间格式等词表统一查表,247 条 key 中英逐条对齐(`test/smoke.js` 断言守门:两侧 key 集合必须一致,且客户端除 console 诊断外不得残留中文字面量)。**范围边界**:仅**客户端展示层**;host 生成的完成汇报/工具返回/流水线日志与产物文档(PRD/QA-REPORT/ACCEPTANCE…)仍是中文——产物语言与「验收结论」是 host 解析契约,属 P3(见 `docs/TODO.md`)。
65
71
 
66
72
  ## AGENTS.md 最小侵入原则(重要)
67
73
 
@@ -78,12 +84,14 @@ AGENTS.md 会被 harness 无条件注入每个会话,是**团队资产**。Tea
78
84
  web profile 宿主组合
79
85
  ├── teamflow-host (dsh-plugin-teamflow/host) Cordis service `teamflow`
80
86
  │ └── TeamflowService extends TypertRemoteService
81
- │ ├── ctx.typert.register(strict descriptors) ← 17 个 Remote 方法
82
- │ ├── ctx.tools.register(teamflow_*) ← 11 个模型工具
87
+ │ ├── ctx.typert.register(strict descriptors) ← 22 个 Remote 方法
88
+ │ ├── ctx.tools.register(teamflow_*) ← 12 个模型工具
83
89
  │ └── node:fs → $DSH_HOME/teamflow/...
84
90
  └── teamflow-client (dsh-plugin-teamflow/client,自动扫描) ← package.json 声明 dsh.client,
85
91
  └── ctx.remote.$mount(TEAMFLOW_REMOTE_CONTRIBUTION) 无需 patch 行,clientModules 自动注册
86
- └── conversation.view tab「🏭 团队工作台」
92
+ ├── conversation.view tab「🏭 团队工作台」(会话内)
93
+ ├── sidebar.panellist + main/teamflow(全局产品线面板)
94
+ └── sidebarRightTabs「teamflow-run」(右栏 run 详情 tab)
87
95
  ```
88
96
 
89
97
  **为什么不用 @Remote 装饰器**:宿主插件以纯 JS 分发,避免装饰器语法/TS 编译要求;
@@ -106,8 +114,12 @@ dsh-plugin-teamflow/
106
114
  descriptors.ts # Remote 描述符(纯数据,host/client 共用)
107
115
  store.ts # 持久化层:原子写/备份/损坏自愈 + journal 序列化/加载(可独立测试)
108
116
  host/index.ts # TeamflowService(TS;构建为 lib/host.mjs 供宿主加载)
109
- client/index.tsx # 团队工作台(TSX;构建为 lib/client.js)
117
+ host/core/products.ts # 产品线装配(全局面板数据面:清单/摘要/地址)
118
+ client/index.tsx # 会话内团队工作台 + 全部 slot 注册(TSX;构建为 lib/client.js)
119
+ client/panel.tsx # 全局面板(sidebar.panellist + main)+ 右栏 run 详情 tab
120
+ client/shared.tsx # 共享展示层(主题 token / 状态词表 / 格式化)
110
121
  test/smoke.js # 无依赖 smoke 测试(描述符/模块结构/安全加固)
122
+ test/product-scope.test.js # 产品线装配测试(地址/白名单/过滤/摘要/空态)
111
123
  test/journal.test.js # journal 行为测试(直跑 store.ts 源码)
112
124
  ```
113
125
 
@@ -145,8 +157,8 @@ dsh plugin --profile web add file:./plugins/dsh-plugin-teamflow
145
157
  ```
146
158
 
147
159
  安装后**重启** `dsh --profile web`,宿主行 `teamflow-host` 生效:
148
- - 模型侧出现 11 个 `teamflow_*` 工具:`start / triage / status / backlog / claim / update / assign / cancel / resume / pause / resume_session`;
149
- - 浏览器侧会话头部出现「🏭 团队工作台」tab
160
+ - 模型侧出现 12 个 `teamflow_*` 工具:`start / triage / status / backlog / claim / update / assign / cancel / resume / pause / resume_session / merge`;
161
+ - 浏览器侧:会话头部「🏭 团队工作台」tab(会话内)+ **左侧边栏「团队工作台」图标**(全局面板,产品线视角);
150
162
  - backlog 写入 `$DSH_HOME/teamflow/<product>/backlog/*.json`。
151
163
 
152
164
  > 注意:`@deepseek-ai/*` 为宿主私有包,运行需 DeepSeek Harness(dsh)宿主环境;本包不发布也无法独立运行。
@@ -155,7 +167,7 @@ dsh plugin --profile web add file:./plugins/dsh-plugin-teamflow
155
167
 
156
168
  1. **选团队**:会话输入框旁点「🏭」按钮,选择团队(或选「无团队」= 不走流水线,直接对话);
157
169
  2. **发需求**:直接说需求,模型会自动调用 `teamflow_start`(自动分诊模式:patch / lite / tech / medium / full)——也可以用「直接跑 medium 模式做这个」等指定档位;
158
- 3. **看进展**:会话头部切到「🏭 团队工作台」tab——流水线图实时刷新(每阶段 token / 耗时 / 子代理会话),Backlog 看板可拖拽流转、点卡片看详情;
170
+ 3. **看进展**:会话头部切到「🏭 团队工作台」tab——流水线图实时刷新(每阶段 token / 耗时 / 子代理会话),Backlog 看板可拖拽流转、点卡片看详情;点「⇥ 右栏打开」把该 run 详情放到右侧栏(与任务夹产物并排)。想看**跨会话/全局**的情况,点左侧边栏「团队工作台」图标(产品线视角:产品线 → run 列表 + backlog);
159
171
  4. **收结果**:流水线完成后自动向当前会话汇报(状态 / 阶段统计 / token / 后续指引);中断/失败的运行可「↻ 从断点重跑」。
160
172
 
161
173
  > 使用规则提醒:`teamflow_start` 调用后**主线程不要自行改代码或跑验证**——实现、QA、汇报由流水线各阶段子代理完成(避免与流水线抢活)。
@@ -181,7 +193,7 @@ node --check lib/host.mjs lib/client.js lib/store.mjs lib/descriptors.mjs
181
193
  npm run bundle # 构建 client(tsdown → lib/client.js,__ModuleLoader__.load 注册)
182
194
  ```
183
195
 
184
- **插件开发者**(本插件的本地开发链路)见仓库内 [`AGENTS.md`](./AGENTS.md) 与 [`docs/adr/`](./docs/adr)——含部署同步(`node deploy.mjs` → 重启 `dsh --profile web`)、生效前提(运行中 web 从 profile 部署副本加载 host,只构建源码不生效)、设计决策记录(ADR-0001~0008)与基准对比(`docs/benchmarks/`)。本仓库其余源码均为 TS/TSX,需先 `pnpm bundle` 构建后再运行(`node_modules` 下 strip-types 不生效)。
196
+ **插件开发者**(本插件的本地开发链路)见仓库内 [`AGENTS.md`](./AGENTS.md) 与 [`docs/adr/`](./docs/adr)——含部署同步(`node deploy.mjs` → 重启 `dsh --profile web`)、生效前提(运行中 web 从 profile 部署副本加载 host,只构建源码不生效)、设计决策记录(ADR-0001~0009)与基准对比(`docs/benchmarks/`)。本仓库其余源码均为 TS/TSX,需先 `pnpm bundle` 构建后再运行(`node_modules` 下 strip-types 不生效)。
185
197
 
186
198
  注意:`lib/` 被 `.gitignore` 排除,但发布必须带上构建产物(`files` 白名单已含 `lib/`;`exports["./client"]` 指向 `./lib/client.js`)。
187
199