# 识别「本次要同步的接口改动」

RAP 上没有代码仓库/分支信息，平台侧推不出本次范围。必须由用户给出代码来源，再由改动反推该同步哪些接口。**禁止从需求描述臆测改动**。

全程**只读**：不许 `checkout` / `pull` / `merge` / `stash` / `rebase`，不许改用户工作区。

## 方式 A：本地 git 仓库（默认，最准）

需要三个值，在第 0 步确认卡片里逐项回读：

| 变量 | 含义 | 默认 / 确认方式 |
|---|---|---|
| `REPO_PATH` | 服务代码仓库在本机的路径 | 必问。注意是**接口代码所在的那个仓库**——需求文档常在另一个仓库。先确认是 git 仓库（`git -C "$REPO_PATH" rev-parse --git-dir`） |
| `FEATURE_BRANCH` | 本次需求的开发分支 | 读当前分支并**回读确认**，见「⛔ 分支确认」 |
| `BASE_REF` | 对比基线 | **默认 `origin/master`**，无则 `origin/main`。只有用户明确说按发布分支/生产 tag 比时才换，换了要回读确认 |

### ⛔ 分支确认（开工前第一件事）

```bash
git -C "$REPO_PATH" rev-parse --abbrev-ref HEAD
```

拿到当前分支后**必须问用户**：「当前在 `xxx` 分支，这是本次需求的开发分支吗？」

- **是** → 记为 `FEATURE_BRANCH`，继续。
- **不是**（停在 `master`、上个需求的分支、detached HEAD）→ **让用户自己切过去，然后停下来等**：
  > 当前在 `master`，不是本次需求的开发分支。请你切到开发分支（`git checkout feature/xxx`）后告诉我，我再继续。
  - ⛔ **不许替用户执行 `git checkout` / `switch` / `pull` / `stash`**——工作区可能有未提交改动，切分支会丢东西。
  - ⛔ **不许绕过**：不许「那我直接按用户口头说的分支名去 diff」，也不许在 `master` 上硬取。等用户切完、重新读一次当前分支确认，再继续。

### 刷新基线

先看 remote 形态，它决定了一旦要认证时该怎么跟用户开口：

```bash
git -C "$REPO_PATH" remote get-url origin
```

- `git@…` / `ssh://…` 开头 → **SSH**：认证走本机 SSH key。⛔ 这种形态**没法用账号密码**，key 带 passphrase 时最容易弹出 OpenSSH 输入窗口，见下方硬门禁。
- `https://…` 开头 → **HTTPS**：可能要 GitLab 账号 / access token。

然后刷新基线。**两个开关都必须带**——它们的作用是让认证失败时**立刻报错**，而不是弹出任何输入窗口把用户晾在那儿：

```bash
GIT_TERMINAL_PROMPT=0 GIT_SSH_COMMAND='ssh -o BatchMode=yes' \
  git -C "$REPO_PATH" fetch origin --prune
```

- `GIT_TERMINAL_PROMPT=0`：禁掉 git 自己的交互式账号密码输入。
- `GIT_SSH_COMMAND='ssh -o BatchMode=yes'`：禁掉 SSH 的一切交互（含 OpenSSH 的 key passphrase 弹窗），认证不过就直接报错。

⛔ **不许省掉 BatchMode**：SSH key 带 passphrase 时，少了它 Windows 会弹出一个 OpenSSH 窗口让用户输入，用户根本不知道那窗口里该输什么。**本 skill 的任何步骤都不许弹出交互式输入窗口，要认证一律在对话里开口问**（见下节）。

仓库没有 `origin/master` → 退到 `origin/main`；都没有就问用户主干分支叫什么。

> 后面的 `git log ... --not origin/master` **不走网络**，`origin/master` 是本地的远程跟踪引用；只有上面这句 `fetch` 需要联网。

### ⛔ fetch 需要 GitLab 认证 → 在对话里直接问，说清原因，绝不弹窗口

报错含 `Authentication failed` / `could not read Username` / `terminal prompts disabled` / `Permission denied (publickey)` / `403` 时，**立即停下，在对话里直接向用户开口，并把原因说清楚**。不绕过、不重试，尤其：

- ⛔ **不许**去掉上面两个开关重跑 fetch，去触发 OpenSSH / Git Credential Manager 之类的交互窗口——用户不知道在窗口里该输什么。
- ⛔ **不许** `ssh-add`、动用户的 key、改 remote 地址，或替用户切换认证方式。

按 remote 形态这样问（**原因必须说**：刷新对比基线）：

| remote 形态 | 怎么问 |
|---|---|
| **HTTPS** | 「为了算准本次改了哪些接口，我需要先从 GitLab 刷新一次 `origin/master` 基线，避免把别人的改动算进来。这需要你提供 **GitLab 账号 + 密码（或 access token）**，只在本次内存里用、不落盘；或者你自己在仓库目录执行一次 `git fetch origin` 完成认证后告诉我。」 |
| **SSH**（`git@…`） | SSH 认证走的是你本机的 SSH key，**没法用账号密码，我也不会弹 OpenSSH 窗口让你输东西**。请你自己在仓库目录执行一次 `git fetch origin`（passphrase 在你自己的终端里输），完成后告诉我，我再继续。」 |

- 用户给了 HTTPS 凭据 → 用它完成 fetch。**凭据只在本次内存里用**：不回显、不写进任何文件、不落日志、不进确认卡片。
- 用户自己 fetch 完成 → 直接继续。
- 用户说「先不拉」→ 按用户说的做，用本地已有的引用，但**必须在最终确认卡片里注明「origin/master 未刷新，可能不是最新」**。

## 取材基准：三选一，按优先级

### ① 工作区未提交改动（默认，有就用）

```bash
git -C "$REPO_PATH" status --porcelain
```

状态码：`??` 未追踪、` M` 已改未暂存、`M ` 已暂存、`A ` 新增已暂存、`R ` 重命名。

挑出接口相关的 Java 文件（`*Controller.java`、`*DTO.java`、`*VO.java`、`*Request.java`、`*Response.java`、`*Req.java`、`*Resp.java`、`*Param.java`）：

| 状态 | 多半是 |
|------|--------|
| `??` / `A ` | 这次要**新增**的接口 |
| ` M` / `M ` | 这次要**更新**的接口 |

只是「多半」——最终以平台上按 method + 路径能否匹配到为准。一次同步里同时有新增和更新是常态。

有这类文件就用它们，**不必再问用户**。读**工作区当前内容**（未追踪文件没有 diff，直接读全文）；`git diff` / `git diff --cached` 只用来看清改了什么、哪些字段是新加的。

只改了 `pom.xml`、`.md`、配置文件之类 → 等同于「没有」，走 ②。

### ② 用户指定的提交（工作区没有未提交改动时）

**候选清单用「本分支自己的提交」，不是裸 `git log`** —— `origin/master` 上有别人不断合入的代码，裸 log 会把别人的提交摆进来让用户误选：

```bash
BASE=origin/master
git -C "$REPO_PATH" log --no-merges --format='%h %an %ad %s' --date=short "$FEATURE_BRANCH" --not "$BASE"
git -C "$REPO_PATH" log --no-merges --shortstat "$FEATURE_BRANCH" --not "$BASE" | tail -3
```

把分支名和这些提交列给用户，**等用户明确指定用哪次**（也可以说「这几条都要」「全部」）。禁止自己挑最新一条或按提交信息关键词猜。

- 这条命令输出**为空** → 分支相对基线没有任何自己的提交。**停下来问用户**：分支给错了？代码没推？还是这次本来就没改接口。
- 用户选定单次提交：

```bash
git -C "$REPO_PATH" show --stat <hash>                 # 这次动了哪些文件
git -C "$REPO_PATH" show <hash>:<文件相对路径>          # 该提交时点的文件全文 ← 按这个提取
```

- 用户选了多次提交或「全部」，权威文件清单用：

```bash
git -C "$REPO_PATH" log --no-merges --name-status --format='' "$FEATURE_BRANCH" --not "$BASE" | sort -u
```

⛔ 注意是 `git show <hash>:<path>`，读的是**那个提交时点的内容**，不是工作区当前内容。

### ③ 兜底：用户直接给清单

无本地仓库时，让用户按此格式贴：

```
A  src/main/java/.../QuotaController.java
M  src/main/java/.../CustInfoQueryDTO.java
```

或直接贴 `git diff` 全文。解析不出文件路径就停下来问，**不接受「我改了额度接口」这类口头描述**作为唯一输入。

## 交叉验证：② 与三点 diff 对照

用了基准 ② 且范围是整个分支时，拿三点 diff 对一遍：

```bash
git -C "$REPO_PATH" merge-base "$BASE" "$FEATURE_BRANCH"
git -C "$REPO_PATH" diff --name-status "$BASE...$FEATURE_BRANCH"
```

| 关系 | 含义 | 动作 |
|---|---|---|
| 一致 | 分支很干净 | 正常往下走 |
| 三点 diff 多出文件 | 分支合并过新 master | **以「本分支提交触碰的文件」为准**，把多出的列出来说明「这些是 master 上别人的改动，不计入本次同步」 |
| 本分支提交多出文件 | 改了又改回去（净变化 0） | 该文件**不同步**，但在清单里提一句 |

分支上出现**别人作者的提交**、或提交信息明显不属于本次需求 → 提出来让用户确认是否计入。

用基准 ① 时，如果分支上还有未推送的提交，提醒用户一句「这些提交里的接口改动没算进来，需要的话告诉我」。

## 改动 → 接口 映射

对每个改动文件按下表归类，**命中才同步，未命中一律不动**：

| 改动特征 | 处理 |
|---|---|
| `*Controller.java` 新增或改动了 `@RequestMapping` / `@*Mapping` 方法 | 每个方法一条接口，按 [code-extraction.md](code-extraction.md) 提取 |
| `*DTO.java` / `*VO.java` / `*Request.java` / `*Response.java` 改动 | **不是独立接口**，找出引用它的 Controller 方法，把那条接口纳入范围 |
| 只改了 Service / Mapper / 工具类，Controller 签名没变 | **不同步**——RAP 记的是接口契约，内部实现变化不影响 |
| 新增 Controller 但方法上没有任何 Mapping 注解 | 不是 HTTP 接口，跳过 |
| `pom.xml` / `.md` / 配置文件 / 单测 | 不同步 |

判不准归属（某 DTO 被多个 Controller 共用、改了会牵动哪些接口）→ **列出候选交用户定**，不自己拍板。

## 产出：改动清单表，取材后先给用户确认

读完代码、动手写 review.md 之前，先输出这张表让用户核对：

```
本次改动 → 待同步接口（请核对，多同步少同步都在这一步纠正）

取材基准：工作区未提交改动（分支 feature/quota，基线 origin/master 已刷新）

┌──────────────────────────────────┬──────┬───────────────────────────┬──────────────────┐
│ 改动文件                          │ 状态 │ 接口                       │ 依据             │
├──────────────────────────────────┼──────┼───────────────────────────┼──────────────────┤
│ QuotaController.java             │ ??   │ POST /api/cust/quota/query│ 新增 @PostMapping │
│ QuotaController.java             │ ??   │ POST /api/cust/quota/freeze│ 新增 @PostMapping│
│ CustInfoQueryDTO.java            │  M   │ POST /api/cust/info       │ DTO 加字段，被    │
│                                  │      │                           │ CustController 引用│
├──────────────────────────────────┼──────┼───────────────────────────┼──────────────────┤
│ 不同步：pom.xml、QuotaServiceImpl.java（内部实现，接口契约未变）                        │
└──────────────────────────────────┴──────┴───────────────────────────┴──────────────────┘
```

用户确认后再进入「提取字段 → 写 review.md → 选仓库模块」。**用户说漏了/多了，以用户为准。**
