---
date: 2026-05-19
tags: [robot-manager, lifecycle, guard, ui-backend-gap, debugging-pattern]
severity: medium
status: pattern-documented
---

# Stage guard + UI prereq 缺口：guard 写了、prereq endpoint 写了，但 UI 没 wire 起来

## 现象

用户在 robot-manager 详情页点「通过 → 进入改装」按钮，toast 报：
```
Guard 检查失败：功能测试未完成 / 未通过
```

按钮看上去应该可以推进，但无论点几次都失败。

## 根因（设计 gap，不是 bug）

robot-manager 的 stage transition 有 13 个 `Lifecycle Guard` 函数（`backend/src/modules/robot-manager/services/lifecycle-guards.service.ts`）。每个 guard 检查某个 stage→stage 是否满足前置条件（如对应的 InspectionRecord / PaymentRecord / DeliveryFulfillment 存在）。

这次撞的是 `checkFunctionTest`（W1 PDI → MODIFICATION）：
```ts
const ok = await tx.inspectionRecord.findFirst({
  where: { robotUnitId, resolvedAt: { not: null } },
});
return ok ? { ok: true } : { ok: false, reasons: ['功能测试未完成 / 未通过'] };
```

完整链路对比：

| 组件 | 状态 |
|---|---|
| Backend `checkFunctionTest` guard 实现 | ✓ |
| Backend `addInspection` service | ✓ |
| Backend `POST /:robotId/inspections` controller | ✓ |
| Frontend `inspectionApi` client method | **❌ 没暴露** |
| Frontend W1 PDI form 调 `addInspection` | **❌ 直接 changeStage** |
| Demo seed 造 InspectionRecord | ❌ |

UI 按钮看似工作，实际后端找不到 resolved InspectionRecord → 必报 guard fail。**Happy path 在 frontend 断了**——后端契约完整、demo data 缺一半、UI 集成漏一步。

## 诊断流程（快速套用）

撞到 "Guard 检查失败" toast 时：

```bash
# 1. 看哪个 stage→stage 触发了哪个 guard
grep -A 5 "fromStage === RobotLifecycleStage" backend/src/modules/robot-manager/services/lifecycle-guards.service.ts

# 2. 看 guard 检查什么实体
grep -B 2 -A 10 "checkFunctionTest\|checkConversionValidated\|checkG5Payment\|checkDeliveryValidation\|checkPGIReady\|checkRMAEligible\|checkQuoteApproved" backend/src/modules/robot-manager/services/lifecycle-guards.service.ts

# 3. 看 backend 有没有对应 service + endpoint 创建该实体
grep -rn "inspectionRecord.create\|paymentRecord.create\|qualityLabelRecord.create" backend/src/modules/robot-manager/

# 4. 看 frontend api/index.ts 有没有对应 client method
grep -n "Api =" frontend/src/app/\(modules\)/robot-manager/_lib/api/index.ts
```

如果 1-3 都 ✓ 但 4 没有 → **UI 集成断点**，按本次模式修：
1. 加 frontend `<entity>Api.add()` client method
2. 改对应 stage form：先调 add，再调 changeStage
3. curl + MCP UI 双重验证

## 修复模板（其他 stage guard 同样套用）

```tsx
// _lib/StageActionPanel.tsx 各 Form 组件
const advance = async (toStage) => {
  setBusy(true);
  try {
    // 1) 先创建 guard 要求的 prereq 实体
    await someEntityApi.add(unit.id, {
      ...required fields,
      resolvedAt: new Date().toISOString(),  // 或对应 status="DONE" 等
    });
    // 2) 再切 stage（guard 检查通过）
    await robotApi.changeStage(unit.id, { toStage, reason });
    toast.success(rm.messages.advanced);
    onChanged();
  } catch (e) {
    toast.error(errMsg(e, rm.messages.advanceFailed));
  }
};
```

## 还有哪些 stage guard 可能有同样 gap（待审）

`lifecycle-guards.service.ts` 总共 13 个 guard。本次只修了 `checkFunctionTest`。其它疑似有 UI prereq 断点的：

- `checkConversionValidated` (W2 → BRANDED_READY)：要求 7 个 VERIFIED QualityLabel + RobotPackageReadiness.completedAt。`QualityLabelRecord` / `RobotPackageReadiness` 创建 UI 在哪？
- `checkG5Payment` (SALES_RESERVED → PAYMENT_VALIDATED)：要 PaymentRecord(paymentStatus=PAID)
- `checkDeliveryValidation` + `checkPGIReady` (DELIVERY_READY → DELIVERED)：要 DeliveryFulfillment.signedFormStatus=SIGNED + PAID
- `checkRMAEligible` / `checkQuoteApproved` / `checkRentalNeedsContract` / `checkClosedNeedsDisposal`

需要按 stage 流程图逐个检查 frontend 是否有对应表单 + 是否调对应 prereq endpoint。

## 经验

- **看到 "Guard 检查失败" 不要先猜 user 操作错**——先看 stage 流程图和 guard 实现，confirm 是否 UI 漏了 prereq
- **Backend guard + endpoint 完整 ≠ 流程能跑通**——必须 UI 集成把 prereq 串起来
- **stage transition UI 设计要点**：每个会触发 guard 的 stage 切换按钮，前置必须有"自动创建 prereq 实体"或"提示用户先操作 prereq 表单"逻辑，不能直接点按钮 = changeStage
