Skip to content

v0.2.6

Choose a tag to compare

@MichaelShii MichaelShii released this 02 Oct 16:28
· 138 commits to main since this release

TeamFlow v0.2.6

中文 | English

Release — 「枚举不再被截断 + 大文档不再被裸 clip + 宿主升到 0.2.0」版本(23 笔新提交 / 47 个文件 / +3450 −139 行,v0.2.5..HEAD 全量对比)。无破坏性:宿主窗口由 >=0.1.7-alpha.1 <0.2.0 放宽为并集区间(含 0.2.0 预览版);journal / 任务夹契约 / teamflow_* 工具参数全部向后兼容。

中文

⬆️ 升级要点

  • 🐛 安装阻塞修复(发版前最后一步拦下的):postinstall: lefthook install → prepare。postinstall 在消费者从 registry 安装时也会跑,而 lefthook 是 devDependency(不在 tarball 里)⇒ 安装直接失败。
    • 反证(同一 tarball,只改这一个字段):
      版本 npm install <tarball> 结果
      postinstall(v0.2.5 形态) ❌ npm error command … lefthook install / 'lefthook' 不是内部或外部命令
      prepare(本版) ✅ added 1 package in 9s,lib/ 齐全
    • prepare 只在本地 install / pack / publish 时跑 ⇒ git hook 仍自动装,发布不再炸。
    • 门禁:test/smoke.js 断言 preinstall/install/postinstall 必须为空、prepare 必须是 lefthook install。
  • 宿主窗口放宽到 dsh 0.2.0-rc.2(预览版)。区间是并集 ">=0.1.7-alpha.1 <0.3.0 || >=0.2.0-rc.1 <0.3.0"——⚠ 上界不能简单放宽:semver 的「预发布只匹配同 tuple 区间」规则下,把 <0.2.0 直接改成 <0.3.0,0.2.0-rc.1 / 0.2.0-rc.2 仍判 fail(semver 7.7.4 实测)。
  • 桌面版(dsh 0.2.0 起)需要单独安装一次:它用独立的 desktop profile(~/.dsh/profiles/desktop),与 web 各自一份 node_modules。只装在 web 上,桌面版里看不到本插件(不是插件坏了,是那份 profile 里没装)。且 CLI 明确拒绝 --profile desktop(error: profile "desktop" is managed exclusively by the Electron application)⇒ 桌面版请用应用内的插件管理界面安装。
  • 升完重启 dsh --profile web(桌面版重启桌面应用)生效。
  • ⚠ prompt 变长了:host/prompts/index.ts 净增 76 行(craft 五条款 + 四处磁盘路径指引)。本仓的 instruction-budget 门禁只管 AGENTS.md、不管 prompt,所以这部分未量化(增量以百字符计,单次调用多几十到几百 tok)。
  • 新增日志键 log.hostScanTruncated。

① 交付枚举不再被上限截断(本版头条)

取证:tf-mupnk8h0-1otbl2(工作区 E:\Code\Probe\tf-stpanel-r1)——工作区里有 pnpm 的 .pnpm-store/,里面是几千个无扩展名 blob,而旧的跳过名单漏了 .pnpm-store,且它在 readdir 顺序里排在 AGENTS.md 之前 ⇒ 瞬间吃满 400 上限 ⇒ 返回的 400 条里源文件 0 个、HTML 0 个 ⇒ 四项检查同时静默跳过:

host 侧加载检查:本次未执行(no-html;根=E:\Code\Probe\tf-stpanel-r1)
host 侧接口核对:本次未执行(skip;源文件 0 个)
host 侧冒烟:本次未执行(no-html)
QA 验证证据观察:交付未见可执行入口(扫到 400 个交付文件),本项跳过

而交付其实完好:28 个交付文件 / 13 个源文件 / 1 个 index.html。机制复现:同目录形状 + 1000 个 store blob ⇒ 不跳点目录时「400 条 / 源文件 0 / HTML 0」;跳过点目录后恢复「23 条 / 源文件 11 / HTML 1」。用户是在浏览器里点不动才把它暴露出来的。

改动:scanDeliverableFiles(root, limit) 返回 { files, truncated, limit },点目录一律不遍历(它们从不是交付物,又最易吃满配额);listDeliverableFiles 退为薄包装(签名与旧行为不变)。三项检查任一 truncated ⇒ 追加一条 log.hostScanTruncated 撤回提示。

⚠ 措辞要说准:那三条「未执行 / 源文件 0 个 / no-html」的原文保留(检查确实没执行),撤回靠的是后一条 warn 点名它们不是事实 ⇒ 顺序也是契约(编排测试第 ⑨ 组钉住,实测 neg@13 / warn@16)。

门禁两层:test/smoke-check.test.js ②b 段(扫描器层)、test/orchestration.test.js 第 ⑨ 组(接线层,430 个无扩展名文件顶满 400)。变异实测:短路 if (scanTruncated) ⇒ 4 条立刻红。

② 大文档注入:裸 clip → 「小 teaser + 磁盘完整路径」

按 256 份真实任务夹文档实测:TECHNICAL.md 中位 19075 / 最大 35547;DESIGN.md 中位 9338;PRD 中位 6334;QA-REPORT 中位 4430。

文档 原阈值 会截断 处置
TECHNICAL.md(dev / qaFix) 12000 47/50(94%) 4000 + 磁盘路径
DESIGN.md(scaffold / tech) 10000 11/31(35%) 4000 + 磁盘路径
PRD.md(design / tech / architect) 15000 / 12000 36/56(511%) 阈值不动,只补路径
QA-REPORT.md(qaFix) 12000 2/51(4%) 阈值不动,只补路径

⚠ 一个反直觉结论:TECHNICAL 中位 19075 ⇒ 连 4000 的 teaser 也截 94% ⇒ 磁盘路径才是修复主体,teaser 价值有限——缺了它,模型根本不知道后面还有内容。

明确不动的两处:clip(scaffold, 10000) 与 clip(devSummary, 8000)——它们不是磁盘文件,无路径可指,加提示就是空话。

门禁:clip 纪律源码锁 6 道(tech / design 各一条「只用 4000」+ 四条「路径指引覆盖 N 处」:TECHNICAL 2 处、DESIGN 3 处、PRD 3 处、QA 1 处)。

③ craft 引导通道(design → dev 接通)

来源是「流水线 vs 裸跑」对照的机制归因:designPrompt 写出的 DESIGN.md 到不了 dev 手里(devPrompt 签名没有 design 参数)⇒ 流水线在「做好看」这件事上结构性输给单 agent。

  • devPrompt(task, tech, prd, design, …) 新增 design 入参,注入视觉规格 + 磁盘路径,明写「视觉规格写在 DESIGN.md 开头,先读它再动手做任何可见的东西」。
  • 开关 TEAMFLOW_CRAFT:未设置 / 空串 = 开,只有 0 / false / off / no 才关;经 util.envFlagOn → options.craft → state.__runCtx.craft。不做工具参数(工具 description 每次都进主会话上下文),resume 读 journal 快照不读环境变量。
  • 定位收窄为交互反馈下限 + 状态可见性,不做审美裁决;QA 只记 P3 observation、绝不阻断(弱/本地模型产不出 craft,硬门禁会让插件对它们不可用)。
  • ⚠ 净收益至今无定论:三组样本方向互不相同,全被 QA 返工轮 / dev 任务拆分数 / host 检查静默跳过盖住。详见 docs/benchmarks/craft-ab-pomodoro.md。

④ 新增脚本

  • scripts/artifact-selfcheck.mjs —— 产物自洽探针(静态、零浏览器依赖):[geo] 几何自洽 / [bind] 未绑定按钮与歧义选择器 / [craft] 反馈态计数 / [state] 死快照 / [persist] 持久化缺口。[state] 来自一次真实误判:某产物「滑块点不动」,而此前所有静态指标(反馈态规则数 10 vs 9、AC 数、bind/geo)全绿——真问题(入口把 value 写死成常量)没有任何门禁看得见。
  • scripts/hi-report.mjs —— 人工介入归因巡检(只读)。动机是本仓曾长期按「humanIntervention 8/23 = 35%」排优先级,而那是早于 09-17 的样本。分层实测 91 个 run:09-17 之前 58 个(20.7%、failed 12);09-17 之后 33 个(9.1%、failed 0),且剩下 3 次全部是 knownIssuesAcceptance(QA 复验超限 → 产品决策刻意交给人,不是缺陷)。
    ⇒ 没有可复跑的度量,旧数字就会一直驱动错误的优先级——本脚本就是那个度量。

⑤ 护栏 / 编排 / 客户端(本轮前段)

  • 澄清闸门埋点(ADR-0010 第 1 步)。
  • env-unavailable 不再误杀「文件级可自愈冲突」,并加进展豁免(用成功调用判定,不用 lastMutationAt——后者在 tool/call 就更新,反复失败的写会让检测永不触发)。
  • 「工具在这一层不适用」不再判死(子代理无 Agent Team 身份 / 无委派深度时调用必失败,但那不代表工作区全废)+ 禁调协作工具 + dev 端到端证据。
  • 可续跑终态不归档暂存日志——resume 要靠上一轮的 checker 当回归契约。
  • 消除「验证脚本会随 run 丢弃」的误导定性,禁止拆 checker 重建任务。
  • 客户端窄屏:画布支持触摸、缩放下限不再写死、窄容器默认列表(issue #10);⚠ 缩放下限保留 0.5(0.12 时卡片只有 37×7px,糊成一团;上一版把「能看全」当目标是错的,已 revert);详情浮层修窄屏左侧溢出;画布提示改设备中性。移动端不在支持范围(宿主 dsh 自身未做)——记进 docs/TODO.md ⛔ 已否决清单,不写 README。

⑥ 语料 / 测试

  • corpus/qa-defect-rich-columns.md:补「富列 + 列序由表头决定(严重级刻意放最后一列)+ 转义 | 还原」。此前 5 份缺陷语料全走 parseDefects 投影 ⇒ reproduce/expected/actual/ac/check/criterion 与整张 columns 从未被断言。变异实测:不认转义时只有新语料变红——那份 note 自称「R3-2 转义回归门禁」的旧语料照旧全绿 ⇒ 一条语料断言了什么只能由变异测试回答,不能由它的 note 回答。
  • corpus/tech-blueprint-valid.md / tech-blueprint-unrepairable.md:补 extractBlueprint 的正常路径与「不可抢救 ⇒ null」,连同既有那条构成合法 / 可抢救 / 不可抢救三态(conformance 24 → 26)。
  • test/locale.test.js 新增占位符覆盖门禁(224 处带参调用)。起因是桌面版首跑 journal 里出现字面量 host 侧冒烟:{entry} 真浏览器加载后…——模板有 {entry} 而调用只传了 {draw, raf, mut, ms}。同型漏传全仓 2 处(另一处 guard.envProgress 缺 label)。⚠ 参数表必须括号配平地取:实调用常嵌 t(..., { n }),用 [^}]* 会把「已传」误判成「漏传」。

⑦ 宿主兼容核对(dsh 0.2.0-rc.2)

逐项核对:未发现破坏,七条证据(完整版见 CHANGELOG.md 对应节):

  1. tsc --noEmit 直接对着 profile 里已装的 0.2.0-rc.2 编译(tsconfig.json 的 paths 指向 ~/.dsh/profiles/node_modules/@deepseek-ai/*)且 0 错;host 源码 12036 行里只有 14 行用 : any(0.1%)⇒「类型过」不是靠 any 糊过去的。
  2. 实际 import 的 @deepseek-ai/* 只有 4 个包、每包 1 个符号,运行时导出逐个核对 ⇒ 0 缺失。
  3. dsh.client.inject 语义未变。⚠ manifest 类型里那句 "Informational … not Cordis service injection" 指的是对 Cordis 而言,运行时仍用 manifest 的 inject 排 factory 到达顺序——别把它误读成语义变更。
  4. manifest schema 兼容;新增的 external 是可选的,本插件不需要。
  5. cordis.patch.yml 的 insert: 行仍被支持(0.2.0 的新能力是新增「bundle 自列其 rows」)。
  6. 宿主只校验 peerDependencies、不读 engines.dsh。
  7. dsh 侧无 CHANGELOG;docs/upgrade-guide/ 尚无 0.2.0 篇。

⚠ 以上全是静态核对:运行时(cordis 服务在 activate 时是否仍在、插件能否在桌面版 profile 正常加载)需重启宿主实测。

验证

  • 桌面版首次验证:tf-mur2p4y0-9qkpqh —— completed / humanIntervention=false / 12m04s / input 1.43M / 49 calls。分诊判成 lite(design / scaffold 未经历,符合「小活别上流水线」);三项 host 检查全部执行(接口核对是 skip;源文件 0 个 —— 不足两个源文件,属有明确原因的合理 skip,不是静默跳过);ACCEPTANCE.md 末行 验收结论:✅ 通过,46 条断言 0 失败。
  • 全量:npm test / tsc --noEmit / oxlint --deny-warnings / conformance 26/26 / instruction-budget(19 条锚点、无孤儿)全绿;web 与 desktop 两个 profile 副本逐字节一致。

English

⬆️ Upgrade notes

  • Host window widened to dsh 0.2.0-rc.2 (preview). The range is a union, ">=0.1.7-alpha.1 <0.3.0 || >=0.2.0-rc.1 <0.3.0" — ⚠ the upper bound cannot simply be widened: under semver's rule that a prerelease only matches a range on the same tuple, changing <0.2.0 to <0.3.0 still fails for 0.2.0-rc.1 / 0.2.0-rc.2 (measured with semver 7.7.4).
  • The desktop app (dsh 0.2.0+) needs its own install: it runs from a separate desktop profile with its own node_modules. Installed only into web, the desktop app will not show the plugin (nothing is broken — that profile simply doesn't have it). The CLI also refuses --profile desktop (managed exclusively by the Electron application) ⇒ install it from the desktop app's own plugin management UI.
  • Restart dsh --profile web (or the desktop app) after upgrading.
  • ⚠ Prompts got longer: host/prompts/index.ts grows by 76 lines net (five craft clauses + four disk-path pointers). This repo's instruction-budget gate covers AGENTS.md only, not prompts, so the cost is unquantified (increment is in the hundreds of characters per prompt).
  • New log key: log.hostScanTruncated.

① The deliverable scan no longer gets cut off (headline)

Evidence: tf-mupnk8h0-1otbl2 — the workspace held a pnpm .pnpm-store/ with thousands of extension-less blobs, and the old skip list missed it while readdir returns it before AGENTS.md ⇒ the 400-file cap was consumed instantly ⇒ the returned 400 entries contained 0 source files and 0 HTML ⇒ all four host checks (load / interface / smoke / evidence observation) silently skipped and the journal read as "the delivery contains nothing" — while the delivery was intact (28 files / 13 sources / 1 index.html). It surfaced only when the user found the demo page's slider un-clickable in the browser.

Fix: scanDeliverableFiles(root, limit) returns { files, truncated, limit } and never walks dot-directories (they are never deliverables and are the usual cap-eaters); listDeliverableFiles becomes a thin wrapper (same signature, same behaviour). If any of the three checks reports truncated, a log.hostScanTruncated retraction line is appended.

⚠ Wording matters: the three "not executed / 0 source files / no-html" lines are kept verbatim (the checks genuinely didn't run) — the retraction is a following warn that names them as not facts ⇒ order is part of the contract (pinned by orchestration test group ⑨: neg@13 / warn@16).

Two test layers: scanner level (test/smoke-check.test.js §2b) and wiring level (test/orchestration.test.js group ⑨ — 430 extension-less files saturating the 400 cap). Mutation check: short-circuiting if (scanTruncated) turns 4 assertions red.

② Large-document injection: bare clip → small teaser + on-disk full path

Measured over 256 real task-folder documents: TECHNICAL.md median 19075 / max 35547; DESIGN.md median 9338; PRD median 6334; QA-REPORT median 4430.

Document Old cap Truncated Action
TECHNICAL.md (dev / qaFix) 12000 47/50 (94%) 4000 + disk path
DESIGN.md (scaffold / tech) 10000 11/31 (35%) 4000 + disk path
PRD.md (design / tech / architect) 15000 / 12000 3–6/56 (5–11%) cap unchanged, path added
QA-REPORT.md (qaFix) 12000 2/51 (4%) cap unchanged, path added

⚠ Counter-intuitive: with a median of 19075, even a 4000-char teaser is truncated 94% of the time ⇒ the disk path is the actual fix; the teaser is marginal — without it the model has no idea more content exists.

Deliberately untouched: clip(scaffold, 10000) and clip(devSummary, 8000) — neither is an on-disk file, so there is nothing to point at; a pointer would be noise.

Gate: 6 source-level locks (tech / design "only 4000" + four "path pointer covers N sites": TECHNICAL 2, DESIGN 3, PRD 3, QA 1).

③ craft guidance channel (design → dev connected)

Born from the mechanism analysis of the "pipeline vs bare agent" comparison: the DESIGN.md written by designPrompt never reached dev (devPrompt had no design parameter), so the pipeline structurally lost to a single agent on "make it look good".

  • devPrompt(task, tech, prd, design, …) gained the design parameter, injects the visual spec + disk path, and states that the visual spec is written first in DESIGN.md and must be read before writing anything visible.
  • Switch TEAMFLOW_CRAFT: unset / empty = on; only 0 / false / off / no turn it off; wired util.envFlagOn → options.craft → state.__runCtx.craft. Not a tool parameter (tool descriptions enter the main session context every time); resume reads the journal snapshot, not the environment variable.
  • Narrowed to an interaction-feedback floor plus state visibility, not an aesthetic verdict; QA records P3 observations only, never blocking (weak/local models cannot produce craft — a blocking gate would make the plugin unusable for them).
  • ⚠ Net benefit is still unproven: three samples point in different directions, all confounded by QA rework rounds / dev task granularity / silently-skipped host checks. See docs/benchmarks/craft-ab-pomodoro.md.

④ New scripts

  • scripts/artifact-selfcheck.mjs — static artifact self-check (no browser): [geo] geometric consistency, [bind] unbound buttons and ambiguous selectors, [craft] feedback-state count, [state] dead snapshot, [persist] persistence gaps. [state] came from a real false negative: a deliverable whose slider did nothing, while every static metric (feedback rules 10 vs 9, AC count, bind/geo) was green.
  • scripts/hi-report.mjs — read-only human-intervention attribution. Motivation: the repo had been prioritising against "humanIntervention 8/23 = 35%", a figure from samples older than 09-17. Measured over 91 runs: 58 before 09-17 (20.7%, 12 failed) vs 33 after (9.1%, 0 failed), and all 3 remaining cases are knownIssuesAcceptance (QA rework limit reached ⇒ the product decision is deliberately handed to a human, not a defect).
    ⇒ Without a repeatable measurement, stale numbers keep driving priorities — this script is that measurement.

⑤ Guards / orchestration / client (earlier part of this cycle)

  • Clarification-gate telemetry (ADR-0010 step 1).
  • env-unavailable no longer kills "file-level self-healing conflicts"; a progress exemption was added (judged by successful calls, not lastMutationAt).
  • "Tool not applicable at this layer" is no longer fatal (a subagent without an Agent Team identity can't call those tools, but the workspace is fine) + collaboration tools banned in prompts + dev end-to-end evidence.
  • A resumable terminal state no longer archives the staging logs — resume relies on the previous round's checker as the regression contract.
  • Removed the misleading "verification scripts get dropped with the run" framing; splitting checkers into "rebuild" tasks is now forbidden.
  • Client narrow-screen: touch on the pipeline canvas, zoom floor no longer hard-coded, list view by default on narrow containers (issue #10); ⚠ the zoom floor stays at 0.5 (at 0.12 cards are 37×7px and blur into one blob — an earlier attempt mistook "see everything" for the goal and was reverted); fixed the detail popover's left overflow on narrow containers; canvas hints are device-neutral. Mobile is out of scope (the dsh host itself doesn't do it) — recorded as a ⛔ rejected item in docs/TODO.md, not in the README.

⑥ Corpus / tests

  • corpus/qa-defect-rich-columns.md: adds rich columns + header-driven column order (severity deliberately last) + escaped-pipe restoration. The five existing defect corpora all go through the parseDefects projection, so reproduce/expected/actual/ac/check/criterion and the whole columns map were never asserted. Mutation check: with escaping disabled only the new corpus turns red — the older sample whose note calls itself "the R3-2 escaped-pipe regression gate" stays green ⇒ what a corpus asserts can only be answered by mutation testing, not by its note.
  • corpus/tech-blueprint-valid.md / tech-blueprint-unrepairable.md: complete extractBlueprint's three states (valid / repairable / unrepairable ⇒ null); conformance 24 → 26.
  • test/locale.test.js gained a placeholder-coverage gate (224 parameterised calls). Origin: the first desktop run's journal printed a literal host 侧冒烟:{entry} … because the template has {entry} while the call site passed only {draw, raf, mut, ms}. Two such misses existed repo-wide (the other is guard.envProgress missing label). ⚠ The argument table must be extracted with balanced braces: call sites often embed t(..., { n }), and [^}]* truncates at the nested } and reports "already passed" as "missing".

⑦ Host compatibility audit (dsh 0.2.0-rc.2)

Item-by-item audit: no breakage found (seven evidence items in the CHANGELOG.md section for this release). ⚠ All of it is static: runtime behaviour (whether cordis services survive plugin activation, whether the plugin loads in the desktop profile) needs a host restart to verify.

Verification

  • First desktop-profile verification: tf-mur2p4y0-9qkpqh — completed / humanIntervention=false / 12m04s / input 1.43M / 49 calls. Triage chose lite (no design/scaffold, matching "don't run the pipeline for small work"); all three host checks executed (the interface check reported skip; 0 source files — fewer than two source files, a legitimate skip with a stated reason, not a silent one); ACCEPTANCE.md ends with 验收结论:✅ 通过, 46 assertions, 0 failures.
  • Full suite green: npm test / tsc --noEmit / oxlint --deny-warnings / conformance 26/26 / instruction-budget (19 anchors, no orphans); the web and desktop profile copies are byte-identical.