前端行为验收并入 reviewWithFixLoop(v2 设计:per-FE + fix 循环,已作废)
状态:SUPERSEDED。当前实现依据见
2026-06-05-frontend-behavior-stage-gate.md(v3:行为验收回迁阶段末尾,整个前端阶段只跑一次,保留 fix 循环)。 本文保留作历史依据:v3 沿用了本文的失败分层 / 两层断言 / locator A-B 分级 / 作用域小节(依赖 C)/ 骨架占位(依赖 A)等机制,仅把触发时机从 per-FE approve 子门改回阶段级一次。 上游:本设计曾取代docs/design/2026-06-02-frontend-behavior-gate.md的「阶段级末尾只读 halt 门」形态。 运行时红线(不可违反):禁用 time/random builtin(Date.now()/Math.random()/new Date());顶层return是结果通道;agent/phase/parallel/log/adjudicate/recordDecisions是注入全局;后端 featureLoop 分支逐字不变。
0. 用户拍板的方向(不可推翻)
- 行为验收并入 per-FE reviewWithFixLoop,与静态 code-reviewer 并列为另一个验收维度。
- 行为发现的硬问题可 fix(带 locator 的 must-fix),驱动 fix→重验循环,不再 halt。
- 仅前端 FE 有此维度;后端 REQ 分支(无 UI)逐字不变。
- 接受每个 FE 起一次(或少数几次)全栈的代价。
本设计在守住上述方向的前提下,落实了 5 维评审里 确凿的 blocker:中途可构建性(头号)、起栈成本笛卡尔积爆炸、起栈不可跨子会话复用、locator 不可靠降级=放行、缺 locator 硬问题被现有 filter 静默吞、删阶段门后 report 失去绿前置锚点。
1. 关键架构决策:行为验收是 reviewer-approve 的「approve 子门」,不是每轮都起栈
这是本设计相对「种子设计」最重要的调整,一次性化解 3 个 blocker(成本笛卡尔积 / 不可复用栈 / 中途构建脆弱性被乘以 N×round)。
原种子设计:每个 review round 的 step2 跑一次行为验收、fix 后 step5 再跑一次 → 单 FE 最坏 REVIEW_HARD_ROUNDS(10) × 2 = 20 次全栈起栈,N 个 FE 串行 → N×20,墙钟在最坏路径不收敛。
v2 决策:把行为验收从「每个 review round 内」解耦为「reviewer 即将 approve 时才触发的 approve 前置子门」:
reviewWithFixLoop(FE):
┌─ 静态 review→fix 循环(与现状几乎不变;后端逐字不变)
│ round 1..N: reviewer 判 → request-changes 则 filter locator must-fix → fix → reverify(功能测试) → 再 review
│
└─ 当某轮 reviewer 判 approve(现 1386 分支)→ 不立即 return,先进【行为 approve 子门】:
behaviorSubGate(FE):
for behaviorRound = 1..BEHAVIOR_FE_MAX(=3):
跑一次 per-FE 行为验收(runBehaviorGateOnce,内含 envError attempt 重试)
├─ envError / 空覆盖 → 内部 attempt 重试;确定性失败(build-failed) → 记 coverageGap 短路,不 retry 不 halt
├─ behaviorHard(interactionFailures + sentinel textIssues)为空 → 子门 green → break
└─ behaviorHard 非空:
· 有 locator 的 → 合并进 fixPrompt 的 issues,跑 fix(runStage)
· 软文字(i18n/literal/semantic) → adjudicate(continue 记 decisions / retry / halt),永不阻断 approve
· 无 locator 的 behaviorHard → adjudicate(allowContinue:false)(retry 重判/重跑 或 halt),绝不静默丢弃、绝不 approve
fix 后只重跑「本 FE 行为验收」(不必重跑功能 reverify,除非 fix 也动了功能逻辑——见 §6)
子门 BEHAVIOR_FE_MAX 轮仍未 green → throw HALT behavior-unresolved
→ 静态 approve ∧ 行为 green ⇒ 才 flipDocs08Checkbox + return{approved:true}
→ featureLoop:1344 在 return 后打 req-done(落点不动)
收益:
- 每 FE 行为起栈次数从
O(review rounds)降到O(行为 fix 轮),典型 1 次(一次过)到 最多 BEHAVIOR_FE_MAX=3 次。 - 静态 must-fix 反复震荡阶段不起全栈——只有静态已 clean、reviewer 认可后才付全栈代价,与用户「接受每 FE 起一次全栈」精确对齐(典型就是一次)。
- 中途构建脆弱性不再被
N×round放大,只在每 FE 的 approve 时刻面对一次(仍需 §2 骨架占位保证可构建)。
为何不冲突用户决策:用户要的是「行为验收是 reviewWithFixLoop 内的另一个验收维度、行为问题可 fix、有 fix→重验循环」。本设计完全满足:行为验收在 reviewWithFixLoop 函数内、是 approve 的合取前置、行为硬问题转 must-fix 喂 fix、fix 后重跑行为验收循环。它只是把「行为验收的触发时机」定在「reviewer approve 那一刻」而非「每个 round 顶」——这是控制流优化,不改变验收维度的存在与可 fix 性。
2. 实现前置依赖 A(blocker):前端骨架全量路由占位阶段——保证任意时刻 app 可构建可起
问题(已核实):skeleton-gen 只生成 docs/04 / scripts/.mjs / tokens.css / .gitignore,不生成任何 frontend/ 源码、router、路由占位*。frontend/ 全部源码由 coding 期 featureLoop 逐 FE 增量产出。验 FE-N 时 FE-N+1..M 的组件文件不存在 → eager import 路由表编译失败 / 共享 nav 指向未建路由 → 整 SPA 起不来,连 FE-N 页面都加载不出。per-FE 行为验收前移到「前端只建了一部分」的每一轮,直接踩头号风险。
v2 方案(必做,不是候选):在 featureLoop(frontend) 之前新增一个 coding 期 stage runFrontendSkeleton(module),由独立子代理依据 docs/08 §三 FE 清单 + frontend/ router 约定一次性生成:
-
App 外壳(
frontend/src/App.*+ 入口main.*,若不存在)。 -
router 全量路由表:每个 FE-NN 对应路由都声明,且全部 lazy import(
() => import(...))。未实现的 FE 路由指向一个最小占位组件FeStub(如frontend/src/views/_stub/FeStub.vue,渲染<div data-fe-stub="FE-NN">FE-NN 占位</div>)。 - 共享布局/导航:导航链接全部指向已声明的路由 path(不指向不存在的 path),保证任意时刻无悬空链接。
落点与时序:
- 在顶层循环
if (module.feItems.length)段、phase('Frontend')之后、featureLoop之前调用await runFrontendSkeleton(module)。 -
幂等:以检测 router 文件存在 + 全 FE 路由已声明为 ground truth;
fe-skeleton-done只作补记标记,避免陈旧 tag 跳过缺失骨架。子代理产出后自行 commit(沿用 commitBlock 习惯)。 - FE-N 实现时(tddPrompt),把对应路由的占位 import 替换为真组件——这要求 tddPrompt 前端分支补一句:「若 router 中本 FE 路由仍指向
FeStub,实现完成后把该路由 import 改为本 FE 真组件」(属 frontend/ 路径内,不破坏护栏)。
为何这是根因解:让 router 始终 lazy + 占位齐全 → 任意时刻
vite build/ dev server 可起、每个 FE 路由可达 → 把「中途起不来」从高频降为罕见 → per-FE 行为验收的 flake/误判面收敛、build-failed(依赖 B)成为罕见兜底而非常态。Plan 期 vs coding 期:放在 coding 期(而非改 skeleton-gen)的理由——FE 清单在 Plan 末尾才稳定、且骨架要落 frontend/ 源码属 coding 范畴;放 coding 期不触碰已锁定的 Plan 闸门,且能用 git tag 幂等。这是新增一个 Plan/coding 边界 stage 的代价,已被本设计接受并显式声明。
3. 实现前置依赖 B(blocker):BEHAVIOR_GATE_SCHEMA 增 build-failed kind + 确定性短路控制流
问题:中途构建失败是确定性编译错误(缺组件 / import 解析失败 / 类型错),但 envError.kind 枚举只有 port-conflict/stack-not-ready/seed-error/auth-failed/timeout/none。门面对 Cannot find module ./views/FE-07.vue 只能误归类:归 stack-not-ready → retry 空转到耗尽后 HALT(编译错永不自愈);归 interactionFailures → 假 must-fix 污染源码。两条路都失败。
v2 方案:
-
BEHAVIOR_GATE_SCHEMA.envError.kind枚举新增build-failed(确定性失败语义;route-not-buildable不单列,统一用build-failed+ detail 区分)。 -
控制流(在 per-FE 行为门 helper 内):
build-failed经轻量前置校验后才 green-by-skip 放行(既不 retry 也不 halt);不满足前置 = 「脏」build-failed → 过adjudicate(allowContinue:false)retry/halt,绝不静默放行。前置(评审加固,见下「评审加固」):(a) 必须有rootCausePath;(b) 不得同时携带交互/sentinel硬问题。干净放行时记coverageGap(reasonbuild-failed-sibling-unimpl)+ recordDecisions(「后续 FE 未实现」的预期中途态,非 FE-N 的 bug;§2 骨架占位让这种情况罕见)。 -
behaviorGatePrompt(per-FE 版)step0/step2 明确归因指令:先build/ 起 dev server;若失败,先用git/Grep判断报错根因文件路径——- 落在非本 FE 的 frontend/ 路径(兄弟 FE / 占位未覆盖)→ 判
envError.kind="build-failed"(预期中途态)。 - 落在本 FE 路径 → 才可能是本 FE 引入的真构建 bug → 归
interactionFailures[kind="js-error"]或带 locator must-fix。
- 落在非本 FE 的 frontend/ 路径(兄弟 FE / 占位未覆盖)→ 判
没有这层「确定性失败短路 + 根因归属」,per-FE 行为门无法落地——这是把行为门从「全 FE 已建的安全环境」迁到「部分 FE 已建的敌对环境」的必须保障。
4. 实现前置依赖 C(blocker):FE-NN → 路由 path 确定性映射,锁进 spec 产物
问题:per-FE 只验「本 FE 关联路由」,但 FE→路由关系当前只在 spec 顶部「关联原型」散文 + 子代理对 router 的 Grep 推断,无结构化真值。推窄→漏验(假绿);推宽→把别的未实现 FE 的死控件算到本 FE(误 must-fix)。且若 router 全量声明(依赖 A),routesPlanned = router 全部路由 会让覆盖率分母被未建路由污染。
v2 方案:
-
deriveSpecPrompt 前端分支强制在 spec 头部产出结构化小节(不只是散文):
## 行为验收作用域(per-FE 行为门唯一断言依据) - 关联路由: [/orders, /orders/:id] - 负责控件白名单: [data-testid 约定 或 page+DOM 选择器清单]并要求 fe-feature-review(code-reviewer)校验该小节存在且与 router 配置一致(缺失 / 不一致 → request-changes)。 -
behaviorGatePrompt 改为接收「本 FE 路由清单 + 控件白名单」入参(per-FE 版必需):
-
routesPlanned只数本 FE 关联路由(不是 router 全部),未建兄弟路由既不计入分母也不计 coverageGap。 - 行为门只对白名单内控件判 must-fix;白名单外控件 / 共享控件若属其它未 approve FE → 归
coverageGap(reasondeep-control-not-driven或build-failed-sibling-unimpl),绝不归本 FE 的 interactionFailure。
-
-
空覆盖兜底保留:
routesReached==0 || controlsEnumerated==0(针对本 FE 路由子集)仍归 envBlocked,绝不静默判 green。
没有这个确定性映射,per-FE 路由作用域无法界定,覆盖率与归因全失真。
5. DB reset 约束:不做测试库命名护栏
用户已明确:本项目触达的库均为开发或沙盒环境,因此 setup-test-db.mjs 不再按库名是否包含 test/_dev/_local 做 fail-closed,也不使用测试库命名相关的环境变量或运行时标记。
保留的仅是流程正确性:
-
database.schema为空时脚本失败(避免生成无意义 SQL)。 -
database.*仍含【人工填写】时脚本失败(配置未完成,继续连库只会制造错误起栈)。 - schema 作为 MySQL 标识符引用,反引号按 MySQL 规则转义,保证 DROP+CREATE 语句可正确构造。
-
setup-test-db.mjs失败统一归普通envError.kind="stack-not-ready",由runBehaviorGateOnce的 env retry / adjudicate 控制流处理。
6. reviewWithFixLoop 改造后的逐轮控制流(实现级)
仅 phase==frontend 改造;fe=isFrontend(phase) 现已存在(1373),后端分支逐字不变。
6.1 数据流:两类 must-fix 独立来源,schema 不合并、fix 入参合并
-
review-must-fix:reviewer 的
REVIEW_SCHEMA.issues,照现状 1392 的 locator filter(缺 locator 降级丢弃)。 -
behavior-hard:行为门返回的
BEHAVIOR_GATE_SCHEMA.interactionFailures+source=='sentinel'的textIssues。 -
schema 不杂交(采纳「schema 选型」维度的定调):行为验收保留独立
BEHAVIOR_GATE_SCHEMA返回,不压扁进REVIEW_SCHEMA.issues(否则丢失 envError / 空覆盖 / coverageGaps / source 软硬分流这三个赖以正确的区分)。仅在喂 fix 时把「有 locator 的 behavior-hard」降维成{summary, locator, severity}喂现有fixPrompt(fix 步天然吃这形状)。即:schema 不合并,fix 入参合并。
6.2 approve 闸(显式 AND,钉死落点)
approve 出口(现 1386 if (r.verdict==='approve') 分支)改为合取:
reviewer.verdict==='approve'
∧ behaviorSubGate(FE) 返回 green
其中 green ≡ behaviorHard.length===0 ∧ envError∈{none, build-failed(经前置校验)} ∧ 本FE覆盖非空
∧ 无未解释漏达路由(routesReached + 路由级 coverageGap ≥ routesPlanned) (或干净 build-failed 短路)
只有合取成立才 flipDocs08Checkbox + return {approved:true}。这保证(采纳「删阶段门」维度 blocker 的钉死):
- 行为 green 是 reviewWithFixLoop 的 return 前置条件——req-done tag 落点(featureLoop:1344)保持不动,语义自动升级为「静态过+行为过」。
- 无 locator 的 behavior-hard 绝不 approve(采纳「控制流/schema」维度 blocker#1):走 adjudicate(allowContinue:false) 决定 retry(重跑行为验收/重判)还是 halt,绝不被 1392 的 locator filter 静默吞掉。
- flipDocs08Checkbox 翻转自动晚于行为 green(checkbox 纯装饰、resume 只认 req-done tag,无视觉误导)。
6.3 behaviorSubGate(approve 子门)逐步
async function behaviorSubGate(id, specPath, feScope):
// feScope = {routes:[...], controlWhitelist:[...]}(来自 §4 spec 结构化小节)
for behaviorRound in 1..BEHAVIOR_FE_MAX(=3):
bg = await runBehaviorGateOnce(id, behaviorRound, feScope) // 见 §7,内含 envError attempt 重试
// 1) build-failed:经轻量前置校验后短路(依赖 B)。脏(无 rootCausePath / 携带交互|sentinel 硬问题) → adjudicate(allowContinue:false),绝不静默放行
if (bg.envError.kind === 'build-failed') {
if (dirty(bg)) { v=adjudicate(allowContinue:false); v!=='retry' → throw HALT; else 下一轮重跑 }
else { recordDecisions; return {green:true, skipped:true} } // 干净:兄弟未实现,green-by-skip
}
// 2) envError(其它) / 空覆盖:runBehaviorGateOnce 内部已 attempt 重试;到这里仍 blocked → adjudicate(allowContinue:false) retry/halt
if (envBlocked(bg)) { adjudicate; 仍 blocked → throw HALT }
// 3) 软文字:for-of 走 adjudicate;continue→recordDecisions + 加入跨轮 softPassed;sentinel→并入 behaviorHard;retry/halt 同现
processTextIssues(bg, softPassed) // softPassed 提升到 reviewWithFixLoop 顶层作用域,跨 behaviorRound 持久
// 3.6) 覆盖率对账(确定性兜底):未被不同路由级 coverageGap 解释的漏达路由(routesReached<routesPlanned) → adjudicate(allowContinue:false)
if (planned>0 && planned-reached-distinctRouteGapCount > 0) { v=adjudicate(allowContinue:false); v!=='retry' → throw HALT; else 下一轮重跑 }
// 4) behaviorHard = interactionFailures + sentinel textIssues
if (behaviorHard.length === 0) return {green:true}
// 5) 分流
const withLoc = behaviorHard.filter(有 locator)
const noLoc = behaviorHard.filter(无 locator)
if (noLoc.length) { v=adjudicate(allowContinue:false); v!=='retry' → throw HALT; else 下一轮重跑 }
if (withLoc.length) {
await runStage(fixPrompt(id, 'frontend', withLoc降维)) // 复用现有 fix 步
// fix 后:只重跑本 FE 行为验收(下一轮 behaviorRound);若 fix 同时改了功能逻辑,附带重跑功能 verify(见下)
}
throw HALT behavior-unresolved(BEHAVIOR_FE_MAX 轮仍未 green)
- softPassed Set 提升到 reviewWithFixLoop 顶层作用域(与 round 同寿命,跨 behaviorRound 持久)——直接照搬现 runBehaviorGate 的 softPassed 语义,否则文字层每轮重新消耗仲裁预算撞 ADJUDICATE_MAX。
- 行为软文字永不进 approve 闸,只 recordDecisions(采纳「控制流/schema」维度 high#3)。
-
fix 后的功能复验:behaviorSubGate 内的 fix 改的是 frontend/ UI 源码,可能引入功能回归。策略——fix 后先跑一次现有
verifyPrompt功能 reverify(allowContinue:false,复用 runStage),红则当功能回归(与现 reverify 同级硬边界),绿后再重跑行为验收。这把「fix 引入功能回归」纳入兜底,且功能 reverify 是 scoped 组件测试(不起全栈),成本低。
6.4 轮次预算与计数(二维,钉死防证据覆盖)
- 静态 review/fix 仍用
REVIEW_SOFT_ROUNDS=5/REVIEW_HARD_ROUNDS=10(现状不变)。 - 行为子门独立、更小预算:新增
BEHAVIOR_FE_MAX=3(每 FE 行为 fix 轮硬上限;超限 throw HALT)。不复用 review 的 10 轮驱动起栈,不让REVIEW_HARD_ROUNDS × BEHAVIOR_GATE_PASS_MAX隐式相乘到 120 量级。 - runBehaviorGateOnce 内部的 envError attempt 重试用独立小预算(沿用 testGate 的 attempt 1→2 + ADJUDICATE_MAX 思路;§7)。
-
二维计数表(采纳「控制流/schema」维度 medium#5):
-
behaviorRound:approve 子门内的行为 fix 轮(1..BEHAVIOR_FE_MAX)。 -
attempt:单次 runBehaviorGateOnce 内的环境 race 重试序号。 - 证据文件名用复合编号:
<date>-<FE>-behavior-r<behaviorRound>-a<attempt>.md,每次起栈独立证据不互相覆盖、不丢 flake 信号。
-
-
单 FE 行为起栈次数硬上界 =
BEHAVIOR_FE_MAX(3) × 每轮 attempt 上限(≤2 + ADJUDICATE_MAX 内),量级远小于种子设计的 10×12。典型一次过 = 1 次起栈。
6.5 contract 严格分离(采纳「控制流/schema」维度 medium#7)
同一 FE 循环内不同 stage 各自 contract,绝不混用:
- fix / review / verify stage:套
featureStageContract('frontend')(硬护栏:命中 backend//sql//scripts/ 即越界硬停)。 - 行为验收 stage:仍独立套
behaviorGateContract()(作用域例外:允许运行 setup-test-db / 起后端 / 跑 sql 种子 / 跑 playwright;唯一可写.tmp/behavior-gate/<FE>/...+ 证据)。 - behaviorGateContract 新增一条中途态豁免(采纳头号维度 low#6):「本门在 per-FE 模式下运行,frontend/ 中本 FE 之外的路由/组件可能尚未实现属预期;遇到指向未建路由的链接/404/编译缺件,一律记 coverageGap 或 envError.kind=build-failed,绝不归为本 FE 的 interactionFailure。本 FE 路由清单是唯一断言作用域。」
7. per-FE 行为验收子代理(runBehaviorGateOnce + behaviorGatePrompt per-FE 版)要点
runBehaviorGateOnce(id, behaviorRound, feScope):保留现 runBehaviorGate 的失败分层(不推倒重写——采纳「删阶段门」维度 high#4),但 scope 缩到单 FE:
- 复用现 enforceEnv 思路做内部 envError attempt 重试 + 空覆盖兜底(attempt 1→2,仍异常经 adjudicate(allowContinue:false) retry/halt)。
- 返回
BEHAVIOR_GATE_SCHEMA(含本 FE scope);把「interaction/sentinel 仍非空」作为「本轮未过」返回给 behaviorSubGate 外层。 - 不在 runBehaviorGateOnce 内嵌 BEHAVIOR_GATE_PASS_MAX 的多次 rerun 收敛(那是阶段级单次门的设计);交互/文字层 retry 限制为每 behaviorRound 至多 1 次重起,收敛靠外层 behaviorSubGate 推进(采纳成本维度 medium#6)。
behaviorGatePrompt per-FE 版(由整 app 阶段级改造):
-
id入参从写死frontend-phase改为本 FE id;新增入参specPath/behaviorRound/attempt/feScope。 - 起栈:runner 自起后端+前端(项目无既有 e2e webServer/playwright.config——已核实 F1,删除「复用既有 webServer」这条死路暗示,避免实现者照已证伪的假设做;只走「冷起栈」,明确写死 round 间不复用运行栈、无 HMR,这是现运行时硬约束,采纳成本维度 blocker#2)。
- 四段时序不变:空库→起后端等 Flyway 建 schema+健康就绪→sentinel 种子(FK 有序)→起前端 headless。DB reset 不做测试库命名护栏(§5),失败按普通起栈问题处理。
-
step0/step2 build 归因(依赖 B):先 build / 起 dev server,失败按根因路径归
build-failed(非本 FE) 或本 FE 真 bug。 -
step1 路由真值:
routesPlanned只数feScope.routes(本 FE 路由),不数 router 全部(依赖 C)。 -
枚举:只驱动
feScope.routes+feScope.controlWhitelist;非白名单 / 共享未 approve FE 控件 → coverageGap,不归本 FE。 -
行为硬问题带源码 locator(采纳 locator 维度 blocker#1 的拆分):
- A 类(可经 route→router 配置→view 组件文件反查到组件级文件路径):locator = 「组件文件 + DOM 选择器 + 失败 kind + 期望端点/期望 sentinel 值 + 实际渲染值」。fixPrompt 放宽:locator 允许「文件 + DOM 描述」而非强制 file:line,由 fix 子代理在该组件内 Grep 定位 handler/绑定。
- B 类(连组件文件都反查不出):不静默降级为放行——归 coverageGap 并计入未覆盖,使 behaviorSubGate 不能判 green(降级≠放行)。或归 envError(stack-not-ready) 走 retry。
-
起栈强制 dev/source-map 模式,runner 注入定位辅助(
data-testid约定 / Vue__file),把 page+selector 映射到组件文件作为契约前置。
- binding-garbage / sentinel-mismatch:locator 除组件文件外,附带 DOM 路径 + 绑定文本片段 + 期望 sentinel + 实际渲染值(写进 summary,不依赖 file:line),供 fix 在组件内 Grep 该绑定表达式。
-
临时件隔离 per-FE×per-behaviorRound:
.tmp/behavior-gate/<FE>/r<behaviorRound>/(采纳「删阶段门」维度 medium#5);每轮跑前清空本子目录,runnerfinally必须 kill 本 FE 起的全部子进程并按本 FE 端口集回收;FE 间起栈端口先探测占用 + 动态回退。每次 coding-start 首个 FE 行为验收前清一次.tmp/behavior-gate/整目录入口(去跨 resume 串味)。 -
证据:
docs/superpowers/reviews/<date>-<FE>-behavior-r<behaviorRound>-a<attempt>.md(与 review 报告同目录);截图归档到版本管理的 assets。 -
确定性端口/pid 回收前置(采纳安全维度 high#3):起栈前先按既知端口 +
.tmp/*.pid强制回收上一 attempt 残留(编排层 + runner 双保险),对反复 port-conflict 设独立硬上限直接 halt 提示人工清理,避免连环 retry 烧时间。
8. 删除 / 改写的阶段级门引用(实现级清单)
8.1 删除(顶层 frontend 段)
- 删
phase('Behavior')(1644)+await runBehaviorGate(module)(1645)。 -
runBehaviorGate(1465-1582)改造为 per-FE 的runBehaviorGateOnce+behaviorSubGate(被 reviewWithFixLoop 调用),不推倒重写——保留 enforceEnv / 空覆盖兜底 / interaction 分层 / 软文字 source 分流 / softPassed 语义,只把 scope 缩到单 FE、把「硬问题转 must-fix locator」作为新增出口。 -
meta.phases:彻底删除{ title: 'Behavior' }(12 行)——行为验收并入 Frontend phase 内,所有行为相关agent()/adjudicate()的 phase 入参从'Behavior'统一改为'Frontend'(与 reviewWithFixLoop 现有 grp 一致)。不保留 'Behavior' 作 UI 分组(否则成无phase()驱动的孤儿)(采纳「删阶段门」维度 medium#3)。
8.2 reportPrompt 前端分支改写(采纳「删阶段门」维度 high#2 + 控制流维度 medium#6)
-
删/改 1096 绿前置:原 Glob
frontend-phase-behavior-gate-r*.md「最后一份非 RED」整条删除(阶段级文件已不再产生,留之必断链或不确定 halt)。改为:对每个req-done/<FE>tag 视为行为已过(因 per-FE 行为 green 已是 req-done 前置,report 不必再独立校验行为绿,避免双真值)。可选加一句轻量校验:每个 FE 存在对应<date>-<FE>-behavior-r*-a*.md证据且最后一份非 RED。 -
改 1106 §⑤:把
frontend-phase-behavior-gate-r*.md汇总改为按 per-FE 证据目录docs/superpowers/reviews/<date>-FE-*-behavior-r*-a*.md汇总 flake / 环境 race / 文字 continue。 - 改 1107 §⑧:偏离清单的 behavior-gate coverageGaps / textIssues continue / 逐控件判定 / authState 来源,从阶段级文件改为 per-FE 证据汇总。
- 注意 testGate 的
frontend-phase-test-gate-r*.md绿前置(1094)保留不动——testGate(全量回归)不并入 per-FE 循环。
8.3 保留不变
- 阶段级 testGate(全量回归 vitest+playwright)保留(1642-1643)——职责正交,per-FE 行为验收不替代全量回归。
- 后端 featureLoop / featureLoop backend 分支 / runMilestone / runCrossModule / 顶层 backend 段:逐字不变。
9. README / SKILL 文案改动
- README(46-49 行):把「→ testGate(frontend) → 前端行为闸 behavior-gate(…逐路由枚举…)→ runMilestone」改为:「featureLoop(前端,FE-NN,每个 FE 在 review 循环内并入 per-FE 行为验收 approve 子门:reviewer approve 时起本 FE 全栈+sentinel 种子,枚举本 FE 路由控件/文字,硬问题转可 fix must-fix→重验,行为 green 才打 req-done) → testGate(frontend,全量回归) → runMilestone」。补一句「前端骨架占位阶段保证中途可构建」。
- coding-start SKILL(步骤 0 横幅,26 行):把「前端行为闸 behavior-gate(…逐路由枚举:交互失效→halt,文字不符→仲裁)」改为「前端功能循环内含 per-FE 行为验收(reviewer approve 时起本 FE 全栈验『按钮真生效/文字对』,硬问题可 fix 重验,不再是末尾独立门)」。
10. 残留风险(接受 / 已缓解)
- 冷起栈墙钟:round 间不复用栈是现运行时硬约束(无跨子会话常驻进程原语)。已用「approve 子门 + BEHAVIOR_FE_MAX=3」把起栈次数压到典型 1 次/FE、最坏 3 次/FE 来控成本,而非靠不可行的栈复用。每次起栈含全量 Flyway apply(随 migration 增多单调增长),计入墙钟预算。若 N 很大仍可能数小时——这是用户已接受「每 FE 起一次全栈」的直接代价,本设计已把它从 N×20 降到 N×(1~3)。
- locator 可靠性:A 类(组件文件级)映射可行;B 类不可降级放行而是计入未覆盖阻断 approve。仍可能出现 fix 在组件内 Grep 不中绑定行、多轮修不中逼近 BEHAVIOR_FE_MAX 后 halt 转人工——这是「运行时 DOM→源码」固有难度的残留,已用「附 DOM 路径+绑定片段+期望/实际值」最大化命中率、用比 review 更紧的 BEHAVIOR_FE_MAX=3 控制空转成本。
- 骨架占位覆盖不全:若 runFrontendSkeleton 漏建某 FE 的路由占位,验该 FE 前的某个兄弟 FE 时仍可能 build-failed;已用 build-failed 短路(不 halt、记证据)兜底,但会留覆盖盲点供人工。
- per-FE 库状态与阶段级 testGate 隔离:行为门入口 DROP+CREATE 自带空库语义,跑完不为 testGate 留状态(testGate 自带 setup-test-db 重置);二者共用同一物理测试库,时序上串行无并发争用。
- deriveSpec 的 FE→路由结构化小节依赖 LLM 正确产出 + reviewer 校验:若两者都失误,feScope 可能不准;已让 reviewer 把「小节存在且与 router 一致」作为 request-changes 项兜底,但非确定性。
11. 实现顺序建议
- 前置 C(deriveSpecPrompt FE→路由结构化小节 + reviewer 校验)——为 feScope 入参铺路。
- 前置 A(runFrontendSkeleton 骨架占位 stage + tddPrompt 占位替换指令)——保证中途可构建。
- 前置 B(BEHAVIOR_GATE_SCHEMA 增 build-failed + 归因控制流)。
- 主改造(reviewWithFixLoop 加 behaviorSubGate / runBehaviorGate→runBehaviorGateOnce per-FE / 二维计数 / softPassed 提升 / contract 分离)。
- 删除阶段级门 + reportPrompt 改写 + meta.phases 删 Behavior + phase 入参改 Frontend。
- README / coding-start SKILL 文案。
每步后端分支必须逐字不变(diff 校验);运行时红线(time/random builtin / 顶层 return / 注入全局)每步复核。
12. 评审加固(实现后多代理评审产出,已落地)
实现后用多代理评审(6 维 + 逐发现对抗复核)核验本设计的逻辑与目标达成。控制流(approve 合取不变量、fix 轮边界、无 stale-green、残留清除)与运行时红线(确定性 builtin / 顶层 return / schema 合法)全部通过。目标维度(「每控件可用 / 每文字正确」)判为 mostly-achieves,确认了若干 escapability 缺口,其中确定性、低风险的四项已加固:
-
build-failed短路加前置校验(头号 must-fix):behaviorSubGate在 green-by-skip 前校验 (a)rootCausePath非空、(b) 无交互/sentinel硬问题搭车;任一不满足 →adjudicate(allowContinue:false)retry/halt,绝不凭未校验的 LLM 归因静默放行。骨架(lazy router + FeStub)令合法的兄弟未实现 build-failed 极罕见,故一个 build-failed 更可能是本 FE 真共享代码回归——这是此前 comment §107-108 声称 load-bearing 却无 JS 兜底的边界。 -
sentinel 文字 locator 补齐:
textIssues[source="sentinel"]是客观绑定错字段,转 must-fix 时需要 locator;schema 与 prompt 现允许并要求携带 locator,反查不到则归coverageGaps[reason="locator-not-resolvable"]。 -
覆盖率对账兜底(§3.6):空覆盖此前只兜
==0;新增routesReached < routesPlanned且缺口未被「不同路由级 coverageGap」解释时 →adjudicate(allowContinue:false),堵住「部分覆盖假绿」(同一路由的重复 gap 不再能抵扣多个漏达路由)。 -
未分类 red 兜底:
status:red但没有 envError / interactionFailures / textIssues / coverageGaps 任一解释时,拒绝把未分类红灯判 green,进入adjudicate(allowContinue:false)retry/halt。
未改(确认为可接受的设计取舍,留作后续):白名单/路由作用域自证(非从 live router/DOM 独立枚举)、非数据文字按 source 软分流、disabled 控件仅提交类有 should-work 复核——均为有意取舍,记于此供后续权衡。