---
title: frontend / backend 实施 agent 留「上游接口尚未提供」TODO 时，主进程必须 grep 验证再接受
date: 2026-05-20
tags: [workflow, agent, todo, false-blocker, sso, issue-334]
---

# 场景

issue #334 SSO 接入 frontend 实施阶段，agent 在用户详情页「Last SSO Sign-in」字段
留了如下 TODO（`members/[id]/page.tsx:766-770`）：

```typescript
// TODO(v2.4 二期 / backend follow-up #334): 后端尚未提供
//   `GET /api/v1/users/:id/last-sso-login` 或 user.lastSsoLoginAt 物化字段。
//   spec 06-data-model.md 写的是从 AuditLog SSO_LOGIN_SUCCESS 反查，
//   本期 frontend 不直接查 AuditLog（避免越界 + 复合权限），
//   待 backend 暴露字段后填进来；
```

硬编码字段为「Never signed in via SSO」，等 backend 二期补。

**实际上**：项目早有 `GET /api/v1/audit/logs?userId=X&action=Y&limit=1` 通用查询接口
（`backend/src/core/observability/audit/audit.controller.ts:63`，`audit:read` 权限）。
30 分钟改完，本期就能展示真实时间。

user 测试时直接问"为什么 Last SSO Sign-in 永远是 Never"，这是个本可以提前发现的功能缺陷。

# 元根因

按 CLAUDE.md「Bug 解决用第一性原理」3 层拆解：

| 层 | 内容 |
|---|---|
| 表面 | UI 永远显示 "Never signed in via SSO" |
| 直接 | frontend 硬编码 fallback 文案，没调 API |
| **元根因** | 主进程给 agent 的 prompt **错误暗示 `/users/:id/last-sso-login` 是唯一选项**：「此接口未在 07-api 列，本期可改用现有 `GET /api/v1/audit/logs?actorId=:id&action=SSO_LOGIN_SUCCESS&limit=1`；**如不存在**，加 TODO 注释，留给二期 / backend follow-up」。agent 信了主进程的假设，没主动 grep `audit/` 验证「真的没有吗」 |

doc-review Lens A「实现走查」也应该问"详情页的 SSO 信息块字段都从哪里来？"——
但当时 Lens A 关注点偏向 SSO 主流程（callback / token），没扫到详情页这个边缘字段。

# 行动准则

**前端 / 后端实施 agent 留 "X 尚未提供 / X 不存在 / X 待 backend follow-up" 的 TODO 时**，
主进程在 commit 前**必须 grep 验证**：

```bash
# agent 说"backend 没暴露 GET /users/:id/foo" → 主进程 grep 通用接口
grep -rE "@(Get|Post).*audit|@Get.*logs" backend/src/core/observability/audit/ backend/src/modules/

# agent 说"前端没有 SpinnerOverlay 组件" → 主进程 grep 看是不是有别名
grep -rE "Spinner|Loading|backdrop" frontend/src/components/

# agent 说"项目没有 isProduction helper" → 主进程 grep ConfigService 用法
grep -rE "isProduction|process.env.NODE_ENV" backend/src/common/utils/
```

**经验数据**：multi-module 项目里 agent 留的「上游接口不存在」TODO，**99% 是 agent
没 grep 通用 API 漏抓**，1% 才真的需要 backend 新加。默认假设是「项目里已有，agent
没找到」。

**主进程的 prompt 也别引导错误假设**：与其说「如果不存在，留 TODO 等 backend」，
应该说「先 grep 现有 audit 通用 API；找到 → 直接调；真没有再 TODO」。被动语气会让
agent 默认走 TODO 路径。

# 类似坑（同 PR 已抓到的）

- frontend agent 把双按钮 UI flip 实施得过于"主 SSO + 次密码"——这是 plan-feature
  阶段我的判断错（后续 user 反馈"业界都是密码主 + SSO 次"才纠正）
- frontend agent 把 spinner 做成全屏遮罩——也是没参考密码登录主按钮的体验做对照

这两个跟本 learning 同根：**agent 信主进程的设计假设，不主动挑战**。
**主进程提示要给方向 + 探索空间，不要给已经填错的细节当事实**。

# 关联

- `.learnings/2026-05-19-plan-feature-tech-params-are-placeholders.md`
  —— plan-feature 阶段的事实漂移，本 learning 是 frontend-main / backend-main 阶段
  的同根教训
- `.claude/skills/doc-review/SKILL.md` Lens A「实现走查」—— 应扩展到详情页 / UI
  边缘字段的字段来源审查
- `.claude/skills/frontend-main/SKILL.md` —— 可以加一条「留 TODO 前必先 grep 通用 API」
  检查项
