# AGENTS.md

旨在减少常见LLM编码错误的行为指南。必要时可与项目特定说明合并。

**权衡：** 这些指南偏向谨慎而非速度。对于琐碎任务，请自行判断。

## 1. 编码前先思考

**不要假设。不要隐藏困惑。明确权衡。**

在实现之前：
- 明确陈述你的假设。如果不确定，请提问。
- 如果存在多种解释，请全部列出——不要偷偷选择其中一种。
- 如果有更简单的方法，请说出来。有理由时提出反对。
- 如果有不清楚的地方，停下来。指出困惑之处。提问。

## 2. 简洁优先

**用最少的代码解决问题。不要写投机代码。**

- 不要添加超出需求的功能。
- 不要为单次使用的代码引入抽象。
- 不要添加未被要求的“灵活性”或“可配置性”。
- 不要为不可能出现的场景处理错误。
- 如果你写了200行代码而实际上只需50行，请重写。

请问自己：“资深工程师会觉得这过于复杂吗？”如果是，请简化。

## 3. 精准修改

**只动必须改的地方。只清理你自己的改动带来的问题。**

修改现有代码时：
- 不要“改进”相邻的代码、注释或格式。
- 不要重构没有问题的部分。
- 保持与现有风格一致，即使你个人有不同的偏好。
- 如果你注意到无关的死代码，可以提出来——但不要删除它。

当你的改动产生未使用的代码时：
- 删除因**你的改动**而不再使用的导入、变量或函数。
- 除非被要求，否则不要移除之前就已存在的死代码。

检验标准：每一行改动都应该直接对应到用户的需求。

## 4. 目标驱动执行

**定义成功标准。循环验证直到通过。**

将任务转化为可验证的目标：
- “添加校验” → “编写针对无效输入的测试，然后让它们通过”
- “修复Bug” → “编写能够复现该Bug的测试，然后让它通过”
- “重构X” → “确保重构前后测试均通过”

对于多步骤任务，简要说明计划：

1.[步骤] → 验证：[检查]

2.[步骤] → 验证：[检查]

3.[步骤] → 验证：[检查]


强有力的成功标准让你可以独立循环迭代。弱标准（“让它能用”）则需要不断澄清。

---

## 5. 明确项目性质，禁止生产级项目使用模拟数据

**在项目开始前，必须先向用户提问：** 这是一个**演示/概念验证（Demo/PoC）**，还是**生产级项目（Production-grade）**？

- 如果是**生产级项目**，**禁止使用模拟数据（mock data）来填充前后端功能**。  
  所有功能必须基于真实数据源（数据库、API、文件等）实现，并确保端到端的真实可用性。
- 模拟数据仅允许用于演示原型、UI草图或早期交互设计，且必须在项目开始时明确约定为“仅演示用途”。
- 对于生产级项目，任何使用硬编码假数据代替真实接口或数据库的行为，都会被视为不可交付的缺陷，必须在开发阶段杜绝。

**原因：** 生产系统必须依赖真实数据流，模拟数据会掩盖接口设计、性能、异常处理等关键问题，导致后续返工和信任损失。

---

## 6. 文件拆分与模块化（硬性规则）

**单文件膨胀是可维护性下降的首要信号。当文件承载过多职责时，阅读、修改、测试成本都会急剧上升。以下规则不可协商。**

### 6.1 行数上限

| 文件类型 | 硬性上限 | 超限必须拆分 |
|----------|----------|--------------|
| UI 组件（`.tsx` / `.vue` / `.jsx`） | **400 行** | 提取子组件到同目录子文件夹 |
| 状态管理（Store / Context / Reducer） | **500 行** | 按领域拆分 slice / module 文件 |
| 工具类 / 服务层 | **400 行** | 按职责拆分到同目录子模块 |
| 类型定义（`types.ts`） | **600 行** | 按域拆分到 `types/` 目录 |
| 其他 `.ts` / `.js` 模块 | **300 行** | 提取辅助函数到独立文件 |

**检验标准：** 任何 PR 中新增的文件如果超过上限，必须先拆分再合并。没有例外。

### 6.2 UI 组件拆分规则

当一个组件文件出现以下信号时，**立即拆分**，不要等"下次再改"：

- **信号 1：** 文件中包含 2 个以上 `export function` 组件 → 每个组件独立文件
- **信号 2：** 组件内嵌的弹出面板/对话框/选择器超过 50 行 → 提取为子组件
- **信号 3：** 组件内定义的常量数组/配置对象超过 30 行 → 提取到 `constants.ts`
- **信号 4：** 状态管理（Store / Context）的 selector 超过 15 个 → 子状态应通过子组件按需订阅

拆分后的目录结构规范：
```
components/
  SomeFeature/
    SomeFeature.tsx        # 主组件（编排层，<200 行）
    SubComponentA.tsx       # 子组件
    SubComponentB.tsx
    constants.ts            # 常量/配置
    types.ts                # 仅本目录使用的类型
```

### 6.3 状态管理拆分规则

当 Store / Reducer 文件接近上限时，按业务领域拆分为独立模块：

```
store/
  useStore.ts               # 主入口（核心状态 + 编排，<500 行）
  domainASlice.ts            # 领域 A
  domainBSlice.ts            # 领域 B
  domainCSlice.ts            # 领域 C
```

每个拆分模块必须满足：
- 导出类型声明和工厂函数（如 `interface XxxSlice` + `createXxxSlice`）
- 主入口通过展开合并各模块
- 模块之间不能互相 import 工厂函数（避免循环依赖）
- 模块需要调用其他模块方法时，通过主入口的 `get()` 或等价方式访问

### 6.4 类型定义规则

- **禁止在多个文件中重复定义同一个 interface。** 如果 2 个以上文件需要同一个类型，提取到共享类型文件
- 本目录专用的类型可以放在本地 `types.ts`，但必须确认共享类型文件中没有已定义的等价类型
- `as any` 是类型逃逸的最后手段，优先为返回值定义正确的 interface。如果第三方 API 返回值不确定，用 `Record<string, unknown>` + 可选链访问

### 6.5 导入路径规则

- **项目配置了路径别名时，跨目录导入优先使用别名**（如 `@/components`、`@shared/types`）
- **同目录内导入：** 使用相对路径 `./xxx`
- **主进程 / 后端引用共享类型：** 使用相对路径（后端通常无别名配置）
- **禁止混用：** 同一文件中不要同时使用别名和相对路径导入同一层级的模块

---

**这些指南生效的标志是：** diff中不必要的改动更少，因过度复杂导致的重写更少，澄清问题在实现之前而非犯错之后提出，新文件不再出现单文件超 400 行的巨石组件。