Skip to content

v0.2.7

Choose a tag to compare

@MichaelShii MichaelShii released this 11 Oct 15:11
· 94 commits to main since this release

TeamFlow v0.2.7

中文 | English

Release — 「澄清闸门 + PRD 确认单落地 + 机器契约彻底离开人类可读通道」版本(43 笔新提交 / 36 个文件 / +3485 −277 行,v0.2.6..HEAD 全量对比)。无破坏性:teamflow_start 的参数对象是 additionalProperties: true,本版摘出工具 schema 的两个字段(needConfirm / clarifyStrength)即使被传入也只被忽略、不会报错;journal / 任务夹契约 / teamflow_* 其余参数全部向后兼容。

中文

⬆️ 升级要点

  • 本版主线三条:① 澄清闸门 + PRD 确认单(ADR-0010 / ADR-0011,最大一块);② resume / 断点系列修复(四处,其中两处是「机制上听起来对、实际全错」的伪代理判据);③ 机器契约彻底离开人类可读通道(state 块与蓝图块双双改走独立文件)。
  • 产品路径改由内容驱动,不再靠开关:needConfirm / clarifyStrength 摘出公开工具 schema,改环境变量(TEAMFLOW_CONFIRM / TEAMFLOW_CLARIFY)。
    • 理由:一个「用户不会主动设、工具描述里也没说何时该设」的参数 = 功能不存在(用户原话:「用户又不知道这参数」)。
    • 兼容性:参数对象 additionalProperties: true ⇒ 老调用传入不报错,仅被忽略。
  • PRD 确认单会主动暂停:PRD 里出现主观品味项(美术风格 / 交互形态 / 人设等)时,流水线停在 PRD 后等你批准,纯信息缺口不阻断(保住无人值守)。
  • 升完重启 dsh --profile web(桌面版重启桌面应用)生效。

① 澄清闸门与 PRD 确认单(本版头条)

目的判据(用户拍板):不是「防返工」,而是让用户确认自己要实现的东西 —— 不能靠模型能力强度替代,因为产品主观性强的东西必须让用户自己创造迭代(否则「用户说实现贪吃蛇,就给他一个简单贪吃蛇」)。

  • 确认单改「内容驱动」:旧实现用 needConfirm 显式开关(用户得自己知道要敲它);现改为有主观项才停。配套:PRD 假设段从「只有信息不足」扩一维到主观决策项(7e2cf0a),openQuestions 每条加 kind: 'fact' | 'taste'(有 taste 才停)。
  • AC 抽取三层(ADR-0011 C):结构化优先 → 正文兜底 → 来源必须自曝(空壳比空更糟)。真实语料逼出两条规则:故事表 | 用户故事 | AC-1: … |(AC 在第二列,只锚行首会漏 4/4)、区间引用行不得造垃圾条目。
  • 确认单断点开放「回退重跑 PRD」(4135abb):原先选项文案写着「我再更新确认单后开工」而后端明确拒绝 supplement ⇒ 假承诺(用户以为能改、点完才发现要么重开要么白跑)。现在断点开放回退重跑,判据落在内容上(stages 除 prd 外无记录 ⇒ 作废面为零)。
  • 澄清协议归 ctx 手册(814472d):clarifyStrength 从未影响过流水线(只选 ctx 文案变体)⇒ 整体移出 PipelineOptions,默认档写进手册。判别式:若一个参数的唯一作用是在几个文案变体里挑一个,它就该是文案的默认值,不是参数。
  • 开工前对齐也走 AUQ(f96852d):手册补 ask_user_question 指令。
  • 失效可见化(a144138):澄清闸门失效时不再静默。
  • 分诊侧:blocker 增 guess 软字段(模型自己的倾向,供用户快速作答)。

⚠️ 本版最重要的一条 bug(P0):731fd42 —— journal.options 白名单漏了一个字段,导致 needConfirm 与 clarifyStrength 在真实路径下根本不生效,而测试全绿(因为 runScenario 直调 executePipeline,绕过了白名单)。⇒ 新增 PipelineOptions 字段时必须同时问「白名单收不收它」。

② resume / 断点系列修复(四处,两处是伪代理判据)

提交 事故 教训
9a97cd4 确认单暂停后无法续跑 无断点判据用了「已记录阶段全 done」这条伪代理
a298242 同一 run 内 state 从不刷新 mergeStateBlock 返回新对象而调用点丢返回值 ⇒ 本 run 后续阶段看不见 extra.*
9388cc6 断点定位第二层 「启用过但从未尝试」的阶段也应算断点
05f9fa3 零阶段 run 断点修正 +续跑补录入口
1f1c4fa resume 文案把「从未尝试」说成「失败」 !st || !st.done 同时含两种形态 ⇒ 两个数分开报

③ 机器契约彻底离开人类可读通道

一句话:机器契约(state 块 / 蓝图块)不再出现在任何用户会读的文本里 —— 既不在回复正文,也不在交付文档,连标记包裹都去掉。

  • b96b44e 机器契约块不得出现在展示面(用户截图实锤:子任务卡把整段 <!-- state -->{…} 摊在用户眼前)。
  • 049961b state 块改文件通道:机器契约不再走人类可读通道。
  • 9958cec 蓝图改独立文件通道 + 截断抢救 + 原文落档(本版收口):
    • 蓝图从「TECHNICAL.md 末尾的块」改为 logs/teamflow/<runId>/blueprint.json。Tech 文档是 user-facing deliverable,而蓝图块真机实测占其 31% 字符(5403/17583)。解析链三级回退:回复正文 → 独立文件 → TECHNICAL.md(历史通道)。
    • 中转文件读完即删,清理与「通道是否命中」解耦 —— 模型「既回显又写文件」时不得漏删(否则可续跑终态不归档 ⇒ 永久残留)。
    • 截断抢救 closeTruncated:实锤 tf-mv3op3i2-0ok7nb,模型写了 5937 字符完整蓝图却缺最外层 1 个 },既有 repairBlueprintJson 只治「提前闭合」救不了 ⇒ 12 个任务退化成 1 个整体开发。现按栈逆序补齐未闭合的 {/[;⚠️ 仅当截断不在字符串内、且转义已收尾时才补 —— 断在字符串中间硬补会得到「语法合法但语义缺失」的对象,比直接失败更危险。
    • 原文落档到 $DSH_HOME/teamflow/<ws>/blueprints/<runId>.json(无条件,成败都留)。此前蓝图三处去向都不留原文(render 只在注入 prompt 那一刻、journal.blueprint 只存 {modules,tasks}、中转文件读完即删)⇒ 「模型到底设计成什么架构」无从复查。
  • 标记形态修正(9958cec):<!-- state --> / <!-- blueprint --> 是自闭合注释 ⇒ 渲染器把整条消化掉,紧随其后的 JSON 成为裸文本,在 Typora / VS Code 预览里直接显示给用户。open 去掉尾部 -->;解析器 (?:-->)? 两种都认(兼容存量产物与模型旧习惯)。
  • state 块改纯 JSON:文件是独立 .json,整份就是那个 JSON,标记纯属冗余(还会多出「写漏关闭标记」这类失败)。新增 stateFromText(先按标记、失败再 JSON.parse);extractStateBlock 保持不动 —— pipeline 与 dev.ts 的回落是在自然语言回复里找块,标记仍是唯一锚。顺带删掉 state.memory 死字段。
  • 9036446 dev 任务标签:if (resume) 分支原先无条件写「(补跑)」⇒ 哪怕 dev 一次都没跑过(确认单暂停后续跑)12 个任务也全被标成补跑,读起来像「上次跑挂了」。判据改为两路取或:断点在 dev 或更晚 / journal 里有该任务记录。

⚠️ 本版新增的两条通用纪律:

  • 凡「用完即删」的中转物,必须留一条事后取证旁路(否则同类问题会变成「无法诊断」——实锤只能靠解压 dsh 会话轨迹、从 write 的 tool/call 参数里抠出模型写了什么)。
  • 测「函数正确」≠ 测「调用点接线正确」:stateFromText 单测全绿,而 readStateBlockFile 若没接上它,文件通道会静默返回 null。接线必须单独断言。

④ 其它修复

  • 5ed35d2 可自愈的文件防护门禁失败不再计入 env-unavailable(issue #15)。
  • 7bdf8a1 「工具在这一层不适用」补入 unknown tool + 宿主文案漂移检查。
  • ffb0c86 蓝图 tasks[] 限定实现范围 —— 不许把 QA / 验收的活划成 dev 任务。
  • b7072ab 确认单改两步(先导总问单独一次);认可补录语义改成「固化进 PRD」。

⑤ 文档

  • ADR-0010(澄清闸门)、ADR-0011(访谈协议 + PRD 确认门)、ADR-0006(蓝图形态与通道订正)。
  • docs/TODO.md 重排为三段结构(真待办 / 观察中 / 已否决)。
  • docs/devlog.md 改按发布批次归并(非逐 commit)。

验证

  • 门禁:npm test 全绿(含 golden corpus 的零 LLM 确定性回归)。
  • 真机:本版三条主线各有实机 run 实证(tf-mv3op3i2-0ok7nb / tf-mv3rdt0y-qmlxl3 / tf-mv3upkft-2oggfu 等)。
  • ⏳ 截断抢救尚未被真机触发(单测用真机那个 5937 字符截断样本,另有部署产物验证;三次真机 run 的蓝图均为合法 JSON)——如实标注,不作「已验证」声明。

English

Release — "clarification gate + PRD confirm sheet + machine contracts fully out of human-readable channels" (43 commits / 36 files / +3485 −277 vs v0.2.6). No breaking changes: teamflow_start's parameter object is additionalProperties: true, so the two fields dropped from the tool schema (needConfirm / clarifyStrength) are silently ignored, never an error.

⬆️ Upgrade notes

  • Three main threads: ① clarification gate + PRD confirm sheet (ADR-0010/0011); ② four resume / checkpoint fixes (two were "sounds right, actually wrong" proxy criteria); ③ machine contracts fully out of human-readable channels (both the state block and the blueprint block moved to dedicated files).
  • Product paths are content-driven now, not switch-driven: needConfirm / clarifyStrength were removed from the public tool schema in favour of env vars (TEAMFLOW_CONFIRM / TEAMFLOW_CLARIFY). Rationale: a parameter the user would never set, and whose description never says when to set it, is a feature that does not exist.
  • The PRD confirm sheet pauses on subjective items (art style / interaction / persona). Pure information gaps do not block, preserving unattended operation.
  • Restart dsh --profile web (or the desktop app) after upgrading.

① Clarification gate & PRD confirm sheet (headline)

The purpose is letting the user confirm what they are actually getting built — not "preventing rework". Model strength cannot substitute for it: highly subjective product decisions must be left to the user to iterate on.

  • Content-driven confirm sheet: the old explicit needConfirm switch is gone; the pipeline stops only when a subjective item exists. The PRD assumption section gained a "subjective decisions" dimension, and each openQuestions entry now carries kind: 'fact' | 'taste' (only taste blocks).
  • Three-layer AC extraction: structured first → prose fallback → source must self-disclose. Two rules came from real corpora: the story table puts ACs in the second column; range-reference rows must not produce junk entries.
  • The confirm-sheet checkpoint can now re-run the PRD: the previous UI text promised "I'll update the sheet then start", while the backend rejected supplement — a false promise. Now the checkpoint supports going back.
  • Clarification protocol moved into the ctx manual: clarifyStrength never affected the pipeline (it only picked a ctx wording variant) and was removed from PipelineOptions. Rule of thumb: if a parameter's only job is to pick one of several wording variants, it belongs in the wording defaults, not in a parameter.
  • 分诊 gained a guess soft field on blockers.

⚠️ Most important bug this cycle (P0): an journal.options whitelist missed one field, so needConfirm and clarifyStrength never took effect on the real path — while tests stayed green, because runScenario calls executePipeline directly and bypasses the whitelist.

② Resume / checkpoint fixes

Commit Incident Lesson
9a97cd4 Could not resume after a confirm-sheet pause Checkpoint criterion used "all recorded stages done" — a proxy
a298242 State never refreshed within a run mergeStateBlock returns a new object; the call site dropped it ⇒ later stages never saw extra.*
9388cc6 Checkpoint detection, second layer "Enabled but never attempted" also counts as a checkpoint
05f9fa3 Zero-stage run checkpoint fix plus a resume backfill entry
1f1c4fa Resume wording called "never attempted" a "failure" the !st || !st.done predicate covers both shapes ⇒ report two numbers

③ Machine contracts out of human-readable channels

Machine contracts (state block / blueprint block) no longer appear in any text a user reads — neither in the reply body nor in delivered documents, not even as wrapped markers.

  • b96b44e Machine-contract blocks must not appear on the presentation surface (user screenshot: a subtask card displayed the whole <!-- state -->{…}).
  • 049961b The state block moved to a file channel.
  • 9958cec The blueprint moved to a dedicated file, plus truncation rescue and raw-text archiving:
    • logs/teamflow/<runId>/blueprint.json — the Tech document is a user-facing deliverable, and the blueprint block measured 31% of its characters (5403/17583) on a real run. Parse chain falls back three levels: reply body → dedicated file → TECHNICAL.md (legacy).
    • The transient file is deleted after reading, with cleanup decoupled from "was the channel hit" — a model that both echoes and writes must not leave the file behind (resumable terminal states are never archived ⇒ it would linger forever).
    • Truncation rescue closeTruncated: a real run wrote a 5937-character complete blueprint missing the outermost }, and the existing repairBlueprintJson (which only handles premature closing) could not save it — 12 tasks collapsed into 1 whole-requirement task. Now unclosed {/[ are closed in reverse stack order; ⚠️ only when the truncation is not inside a string literal and escaping is complete — force-closing mid-string yields a "syntactically valid but semantically missing" object, worse than a clean failure.
    • Raw-text archiving to $DSH_HOME/teamflow/<ws>/blueprints/<runId>.json (unconditional). Previously none of the blueprint's three destinations kept the raw text, so "what architecture did the model actually design" was unreviewable.
  • Marker form fix: <!-- state --> / <!-- blueprint --> are self-closing comments — renderers swallow the whole comment and the following JSON becomes bare text, shown directly to users in Typora / VS Code preview. The --> is dropped from the open marker; parsers accept both forms.
  • State block now pure JSON: no markers in the file at all. New stateFromText (marker first, then JSON.parse); extractStateBlock unchanged — the fallback path still searches natural-language replies, where markers remain the only anchor.
  • 9036446 dev task labels: the if (resume) branch unconditionally wrote "(rerun)" ⇒ even a run where dev never executed had all 12 tasks labelled as reruns, reading like "the last run crashed". The criterion is now a two-way OR.

⚠️ Two general rules added this cycle:

  • Anything transient that is deleted after use must keep an after-the-fact forensic path — otherwise the same class of problem becomes undiagnosable (in practice the only recovery was decompressing the dsh session trace and digging the model's write tool/call arguments).
  • Testing that a function is correct ≠ testing that the call site is wired: stateFromText unit tests were green, while readStateBlockFile failing to use it would make the file channel silently return null. Wiring needs its own assertion.

④ Other fixes

  • 5ed35d2 Self-healing file-guard failures no longer count as environment-unavailable (issue #15).
  • 7bdf8a1 unknown tool added to "this tool does not apply at this layer", plus a host-wording drift check.
  • ffb0c86 Blueprint tasks[] are scoped to implementation — QA/acceptance work must not be handed to dev tasks.
  • b7072ab Confirm sheet split into two steps.

⑤ Docs

  • ADR-0010, ADR-0011, ADR-0006 (blueprint form and channel amendment).
  • docs/TODO.md restructured into three sections (real TODOs / under observation / rejected).
  • docs/devlog.md now consolidated per release batch rather than per commit.

Verification

  • Gates: npm test fully green (including the zero-LLM deterministic golden-corpus regression).
  • Real runs: each of the three main threads has real-run evidence (tf-mv3op3i2-0ok7nb / tf-mv3rdt0y-qmlxl3 / tf-mv3upkft-2oggfu).
  • ⏳ Truncation rescue has not yet triggered on a real run (its unit test uses the real 5937-char truncated sample, plus deployment-artifact verification). Stated as-is; not claimed as verified.