# index1 专用代码审查规则

写完 index1 相关代码后，必须使用 `index1-code-reviewer` skill 进行审查。

## 触发时机

- 新增/修改 corpus、cognition、resilience 模块代码后
- 新增/修改 MCP tool 后
- 新增/修改 Hook 处理器后
- 修改 SQLite 数据库操作后
- 修改进程模型（watcher、maintenance、observer）后
- PR 提交前

## 执行方式

使用 skill 调用：

```
Skill: index1-code-reviewer
```

## 审查要点速查

### P0 必查项（阻塞合并）

1. **容错原语** — DB 写必须 `safe_db_write()`，MCP tool 必须 `@mcp_error_boundary`
2. **Hook 延迟** — orient 阻塞 ~210ms 禁止网络调用，reflect 禁止等待 maintenance
3. **SQL 安全** — 参数化查询 `?`，禁止字符串拼接，批量写入用事务
4. **双 DB 隔离** — index.db 和 cognitive.db 操作独立，禁止跨库事务
5. **降级正确** — Ollama 挂→BM25-only，cognitive.db 不可写→熔断，Hook 崩溃→exit 0
6. **三层防御** — 有副作用的代码（subprocess/HTTP/DB 写/文件写入/LLM 调用）必须 L1 事前校验→L2 事中监控→L3 事后日志全覆盖，详见 SKILL.md 第七章

### P1 建议修复

- 外部服务调用用 `ServiceCooldown` 指数退避
- 跨进程写入用 `CircuitBreaker` 检查熔断
- FTS5/sqlite-vec 索引与数据表同步更新
- observe() 只处理 Write/Edit/Bash，过滤其他工具
- maintenance 每步独立 try-except，单步失败不阻断
- **Observer 模型名** — 必须引用 `config.OBSERVER_MODELS`，禁止在 provider 文件中硬编码模型名
- **httpx 代理合规** — 外部 API 的 httpx client 禁止 `transport=AsyncHTTPTransport()`（绕过代理）；localhost 服务可绕过但必须注释说明

## 降级策略速查表

```
故障场景              → 正确降级行为                    → 恢复条件
Ollama 挂了           → BM25-only 搜索              → Ollama 重启 + 冷却期过
cognitive.db 不可写    → observe 熔断，facts 写 buffer → 5 分钟后自动重试
index.db 被锁         → safe_db_write 返回 None      → 锁释放后下次成功
MCP tool 内部异常     → 返回 {"error": "..."} dict   → 自动（每次请求独立）
Hook 进程崩溃         → 静默 exit 0                  → 下次事件自动重启
maintenance 崩溃      → 苏醒指引降级为模板            → 下次 SessionEnd 重新触发
磁盘满                → 所有写入跳过                  → 清理磁盘后恢复
DB 损坏               → 写入返回 None, 读取返回空     → index1 doctor --fix
```

## 前端审查（修改 web.py 前端代码时必须执行）

### 触发条件

修改了 `web.py` 中以下任意内容时触发：
- `_HTML_TEMPLATE` 内的 HTML 结构
- 内联 CSS 样式
- 内联 JavaScript 代码
- Flask API 端点（影响前端数据）

### 审查流程（必须全部完成）

1. **index1-code-reviewer 审查** — 后端逻辑（SQL 安全、参数化查询、错误处理）
2. **Playwright 截图验证** — 前端视觉+交互
   ```python
   from playwright.sync_api import sync_playwright
   with sync_playwright() as p:
       browser = p.chromium.launch()
       page = browser.new_page(viewport={"width": 1280, "height": 800})
       page.goto("http://127.0.0.1:6888/")
       page.wait_for_timeout(2000)
       page.screenshot(path="/tmp/verify_home.png")
       # 点击修改涉及的 tab/功能
       page.locator("<修改涉及的元素>").click()
       page.wait_for_timeout(2000)
       page.screenshot(path="/tmp/verify_feature.png")
       browser.close()
   ```
3. **Read 截图确认** — 用 Read 工具查看截图，肉眼确认无异常

### 前端 P0 必查项

| 检查项 | 说明 |
|--------|------|
| XSS 防护 | 所有用户内容必须经 `esc()` 转义后才插入 HTML |
| localTime() | 所有时间显示必须经 `localTime()` 转换（UTC→本地时区） |
| Auto-refresh 资源浪费 | 定时器只在对应 tab 激活时启动，离开即停止 |
| COUNT 查询性能 | 带过滤条件的 API 其 COUNT 也必须带相同条件 |
| encodeURIComponent | URL 参数拼接必须用 `encodeURIComponent()` |

### 前端 P1 建议查项

| 检查项 | 说明 |
|--------|------|
| 键盘快捷键 | 新增 tab 后更新数字键范围（如 `'1'-'5'`） |
| 动画一致性 | 新卡片用 `factSlideIn` / `fadeIn`，与已有风格一致 |
| 响应式 | 新增元素使用 CSS 变量（`var(--bg-card)` 等），支持亮/暗主题 |
| 空状态 | 无数据时显示 `<div class="empty">` 提示 |
| 错误展示 | API 返回错误时用红色文字展示 `style="color:var(--red)"` |

## 禁止行为

- **禁止**用通用 `code-reviewer` 审查 index1 代码（必须用专用审查）
- **禁止**跳过审查直接提交
- **禁止**修改 resilience.py 后不检查所有调用方
- **禁止**在 hook 阻塞路径中添加网络调用
- **禁止**修改前端代码后不做 Playwright 截图验证
- **禁止**时间显示不经 `localTime()` 直接 substring
