2026-06-15-frontend-overlap-jsdom.md 8.22 KB

前端 jsdom-only 重叠编排 — 实施计划(待评审)

目标:让前端不依赖后端的工作(骨架 + spec + plan + jsdom 驱动的组件代码)与后端波次循环并发, 把所有依赖后端的工作(e2e tdd 任务、e2e verify、静态 review→req-done、行为门、测试闸、里程碑) 延后到后端全部完成之后。这是前后端重叠唯一安全的形态——重叠期前端只跑代码 + jsdom(无栈、无固定 端口、无 DB),不碰后端 lane 的端口/库,补 8/9/10 不变量全部保住。

总开关 parallel.frontendOverlap(缺省 false,完全回退现行串行终波)。一切失败 fail-open 降级现行路径, 绝不因重叠机制新增 halt 点。

一、现状与目标流程

现状runModule(frontend-phase),终波宽 1 主根,依赖全部后端):

branchSetup → skeleton → featureLoop(frontend: 逐FE spec→plan→tdd(jsdom+e2e)→verify(unit+e2e)→review→req-done)
            → behaviorGate → testGate(frontend) → report → milestone

目标(frontendOverlap 开启时):

Phase 1(重叠,与后端波次并发,跑在前端 worktree):
  branchSetup(fe) → skeleton → 逐FE [ spec → plan → tdd(仅 jsdom 任务) → verify(仅 vitest) → tag fe-code-done/<FE> ]
        ↑ 不打 req-done(e2e+review 未做)

  ─── 屏障:等后端全部 done(后端波次循环结束) ───

Phase 2(后端 done 后,前端分支并入已完成的 default,主根跑全栈):
  逐FE [ tdd(仅延后的 e2e 任务) → verify(unit+e2e) → review → tag req-done/<FE> ]
        → behaviorGate → testGate(frontend) → report → milestone

二、改动清单(函数级)

1. 新增执行序拆分(核心)

  • runFrontendPhase1(feModule, feCtx):branchSetup + skeleton + featureLoopFE(items, 'jsdom', feCtx)
  • runFrontendPhase2(feModule, c)featureLoopFE(items, 'e2e', c) + behaviorGate + testGate + report + milestone。
  • 现行 runModule 的前端段(3078–3096)在 frontendOverlap 关时一字不动;开时改走上面两段。

2. featureLoop 拆 mode(最小侵入:加形参,不动串行语义)

  • featureLoop(items, phase, c) 内部按 FE 链已抽出共用闭包(isDone/ensureSpec/runPlanStage/runImplChain/tagDone)。
  • 新增 feStage 参数('jsdom' | 'e2e' | 'all',缺省 'all'=现行):
    • jsdom:spec→plan→runImplChain(jsdom)tag fe-code-done review、 req-done)。
    • e2e:入口 dedup 读 fe-code-done(必须已存在,否则回退 all)→ runImplChain(e2e)→review→tag req-done
    • all:现行完整链。

3. tddPrompt(id, phase, planPath, c, feStage='all')

  • 新增 feStage
    • jsdom:只处理 plan 中 测试先行类型 = jsdom 的任务;e2e 任务显式跳过并记账(输出 deferred 列表); FeStub→真组件替换(554)在此完成(组件由 jsdom 任务建出)。测试命令只用 frontend.test_command(vitest), 绝不触发 pnpm e2e:ci / Playwright。
    • e2e:只处理 = e2e 的任务(jsdom 已在 phase1 绿);e2e 基线约束(551)照旧。
    • all:现行文案,一字不动。
  • 「c 为末位参数」的第三个 opts 特例(先例 deriveSpecPrompt {batch} / reviewWithFixLoop {flipDocs08})。

4. verifyPrompt(id, phase, implSummary, specPath, round, c, feStage='all')

  • jsdom:只跑 unit(vitest),不跑 e2e;红则 halt。
  • e2e / all:现行 unit+e2e(595),一字不动。

5. 新增中间 tag fe-code-done/<FE>

  • 语义:phase1 完成(组件代码 + jsdom 绿)。createFeCodeDoneTagPromptM + check 复用 checkReqDoneTagPromptM 同构。
  • resume 真值链:fe-code-done/<FE> 存在 → phase1 跳过该 FE;req-done/<FE> 存在 → phase2 跳过。 二者皆缺 → phase1 重跑该 FE。

6. 主循环并发点(while (remaining.length) 段)

  • frontendOverlap 开 存在 frontend-phase 模块时:
    • 从波次图剔除 frontend-phase(todo/remaining 仅后端;frontend 不再经 annotateDeps 硬依赖)。
    • const [beResults, fe1] = await Promise.allSettled([ 后端波次循环(), runFrontendPhase1(feModule, feCtx) ])
    • 两者独立 try/catch,互不 break:前端 phase1 halt 不停后端,反之亦然(各自结构化 halted 入 results)。
    • 屏障后:仅当后端全部 done phase1 全部 fe-code-done → runFrontendPhase2
    • 后端有 halt/pending → phase2 不跑(e2e 需完整后端);phase1 产物(fe-code-done)已落 tag,resume 续跑。
  • frontendOverlap 关 → 现行路径(frontend-phase 留在波次图当终波),零改动

7. worktree / 分支隔离(重叠期)

  • phase1 跑在前端专属 worktree(分支 frontend-phase,路径 <ROOT>-lanes/frontend-phase 同 lane 命名族); 与后端主根/lane 的 git index 互不干扰。phase1 merge、 milestone → 不取 withMainRootLock。
  • phase1→phase2 边界:后端已全部 milestone 到 default。phase2 起手 merge default → frontend-phase (把完成的后端代码并进前端树,e2e 才打得到真后端)。路径互斥(frontend/ vs backend/)→ 冲突概率极低; 冲突按现行 milestone merge 同口径「留树 halt 给人工」。phase2 完成后照现行 runMilestone 合回 default。

8. 开关透传 + 入口

  • coding-start/SKILL.md 步骤 4 的 parallel 不传 frontendOverlap(与 features 闸同纪律:留作显式评估开关, 收益验证通过后才一行加上)。文档补一句逃生口说明。
  • README 阶段 B 同步一句。

三、不变量与安全性

不变量 是否保住 依据
补 8/10 行为门/骨架 lane DB 注入豁免 重叠期前端只跑 jsdom(无 DB);行为门仍在 phase2 后端 done 后、主根、无后端 lane 时跑
补 9 起栈互斥(固定端口) 重叠期前端不起栈不占端口;phase2 全栈门在后端 lane 全清后跑
fail-open 不新增 halt 点 frontendOverlap 失败/关 → 现行终波路径;phase1 halt 不停后端
req-done 语义(静态过) 仍在 phase2 review 后打;phase1 用独立 fe-code-done
resume 经 tag 续跑 fe-code-done + req-done 双 tag 链

四、风险

  1. 契约漂移返工:前端对 docs/05 写代码,后端编码期若偏离契约 → 错误拖到 phase2 的 e2e/行为门才暴露、返工。 (任何重叠固有代价;用户已接受「先把能做的做了」。)
  2. 任务分类错配:plan 把 e2e 任务误标 jsdom → phase1 误跑 e2e 触后端。缓解:tddPrompt jsdom 模式硬禁 Playwright/e2e 命令,命中即 halt(不静默放行)。
  3. 组件依赖后端类型:前端应只依赖 docs/05 契约类型,不依赖后端代码。若个别 FE 的 jsdom 测试隐性需要真后端 → 该 FE 的该任务在 phase1 红 → 按 tdd 账本上限 halt(结构化),phase1 跳过它、其余 FE 照常,resume 续跑。
  4. phase1→phase2 merge 冲突:路径互斥,概率极低;冲突走现行 milestone 同口径 halt。

五、验证

  • coding.mjs 无法 node --test:纯函数/流程以 .tmp/ 临时脚本 stub agent/parallel/phase/log 跑确定性断言 (①overlap 关=现行终波 ②overlap 开+后端全 done+phase1 全 fe-code-done→phase2 跑 ③后端 halt→phase2 不跑、 phase1 tag 保留 ④phase1 halt 不停后端 ⑤tddPrompt jsdom 模式不含 e2e 命令字样)。即跑即删不入库。
  • node lib/check-workflow-syntax.mjs workflows/coding.mjs 必过。

六、落地顺序(建议分提交)

  1. tddPrompt / verifyPrompt 加 feStage(纯 prompt,行为默认不变)+ fe-code-done tag builder。
  2. featureLoop 加 feStage 分流 + runFrontendPhase1/2 拆分(overlap 关时等价现行)。
  3. 主循环并发点 + worktree 隔离 + phase1→phase2 merge。
  4. coding-start/README 开关透传(不默认开)。
  5. 临时脚本验证 + 语法门。

待你确认:范围是否就按以上五类改动?有无要砍/加的?确认后我从第 1 步开始实现,每步过语法门。