refactor(pipeline): 路径直接传输 — 统一 ctx 字段名/panel key/step 形参名
This commit is contained in:
141
.qwen/skills/code_replacement_state_audit/SKILL.md
Normal file
141
.qwen/skills/code_replacement_state_audit/SKILL.md
Normal file
@ -0,0 +1,141 @@
|
||||
---
|
||||
name: 代码替换请求的现状审计
|
||||
description: 处理用户"代码替换/新增"指令时,先审计磁盘真实状态再用 ask_user_question 确认——避免覆盖已落盘的高版本代码
|
||||
source: auto-skill
|
||||
extracted_at: '2026-06-03T05:36:58.746Z'
|
||||
---
|
||||
|
||||
# 代码替换请求的现状审计
|
||||
|
||||
## 适用场景
|
||||
|
||||
用户给出"代码替换"或"按某版本代码新增"指令,但**没有提供与磁盘当前状态对比信息**时。典型触发:
|
||||
|
||||
- 用户贴了一段代码说"请帮我写/替换这个"
|
||||
- 用户引用某个文档/旧版本/旧 chat 说"按这个来"
|
||||
- 之前的 `state_snapshot` / `memory` / `git log` 描述可能与磁盘现状不一致
|
||||
|
||||
## 核心原则
|
||||
|
||||
**永远不要盲信"用户给的代码是最新版本"**——磁盘上的代码可能已经是更完善的版本(用户或其他 agent 已迭代过)。覆盖 = 丢功能。
|
||||
|
||||
直接覆盖的代价不一定是显式 bug,也可能是"丢失用户已批准的设计决策"(如 duck-type 探测 / ctx 抽象 / 信号协议 / 二次确认窗 / 错误定位)。
|
||||
|
||||
## 5 步标准操作
|
||||
|
||||
### 1. 确认文件存在
|
||||
|
||||
`glob` 或 `list_directory` 看目标文件是否已存在:
|
||||
|
||||
- 不存在 → 新建
|
||||
- 存在 → 进入第 2 步审计
|
||||
|
||||
### 2. grep 关键符号 + 读关键段
|
||||
|
||||
- 找"用户贴的代码"里的 3-5 个关键符号(函数名 / 类名 / 关键常量 / import)
|
||||
- 在磁盘文件里 grep 同样的符号
|
||||
- `read_file` 关键段(行号从 grep 结果直接拿)
|
||||
|
||||
### 3. 构造差异对照表
|
||||
|
||||
列出:
|
||||
|
||||
```
|
||||
| 目标文件 | 用户贴的版本 | 磁盘现有版本 | 直接覆盖会丢失 |
|
||||
```
|
||||
|
||||
**关键列**:"直接覆盖会丢失什么"——让用户判断成本。具体粒度到"功能模块 / 设计决策 / 防御层 / 入口协议",不要写"代码差异"这种空话。
|
||||
|
||||
### 4. ask_user_question 让用户拍板
|
||||
|
||||
3 个标准选项(措辞可调,但**必须给出现状 + 三选一**):
|
||||
|
||||
- **A. 保留现状**(推荐,磁盘已是更新版)—— 直接进 Smoke Test
|
||||
- **B. 强制覆盖到旧版** —— 写明丢什么 + 备份建议(git stash / 复制到 `_old.py`)
|
||||
- **C. 混合:只取某段增量** —— 见第 5 步
|
||||
|
||||
**不要在第 1 次 ask 时就列具体的"哪段增量"**——先让用户在 A/B/C 之间选。如果选 C,再做第 5 步。
|
||||
|
||||
### 5. 若用户选 C,识别"真正增量"
|
||||
|
||||
对比 1.0 vs 2.0,识别 1.0 真正独有的部分(2.0 没有的):
|
||||
|
||||
- ❌ 排除 1.0 比 2.0 简单的(2.0 是超集 / 工厂分层 / 多了 CLI)
|
||||
- ❌ 排除 1.0 整体被 2.0 工厂分层超越的(_make_objective vs _build_model + _get_search_space)
|
||||
- ✅ 关注 1.0 独有的功能层(即使 2.0 不"明显"需要)
|
||||
|
||||
对每个候选增量,再问一次"采纳哪段",让用户具体选(multiSelect=false,一次只选 1 段最稳)。
|
||||
|
||||
## 落地原则
|
||||
|
||||
执行"采纳 1.0 某段增量到 2.0"时:
|
||||
|
||||
- **最小化外科手术式编辑**:只动需要动的文件,只改需要改的段
|
||||
- **保留 2.0 的设计决策**(duck-type 探测 / ctx 抽象 / 信号协议 / 二次确认窗 / 错误定位)
|
||||
- **顶部 import 增量用 `replace_all=False` 单点插入**,避免破坏其他 import 顺序
|
||||
- **同名变量全链路替换**(如 `self.config` → `clean_config`)要贯穿 ctx 构造 / v2 调用 / v1 fallback,避免双源差异
|
||||
- **单步模式不一定要清洗**(不走 panel 完整 config,与清洗器无关)
|
||||
- **清洗器这种"防患于未然"的代码要给日志**(`self.log_message.emit(f"[清洗器] 已删除 N 个未知 key")`)让运行时可见
|
||||
|
||||
## 验证三件套
|
||||
|
||||
落地后必跑:
|
||||
|
||||
1. **AST 语法检查**:`ast.parse(open(p, encoding='utf-8-sig').read())` 对 5 个核心文件
|
||||
- 必加 `utf-8-sig`:WQ_GUI 的 water_quality_gui.py line 1 是 BOM,plain `utf-8` 必挂
|
||||
2. **关键符号 grep**:确认新代码的关键符号(import / 关键函数调用)都命中,hit 数符合预期
|
||||
3. **顶层导入测试**:用 mock PyQt5 + `sys.path.insert(0, 'src/gui/core')`,验证模块整体可加载
|
||||
- PyQt5 mock 模板见下方"参考代码"
|
||||
- Windows 环境调 Python:用 conda env 的 `python.exe` 全路径,不要靠 PATH
|
||||
|
||||
## 反例(不要做)
|
||||
|
||||
- ❌ "按用户贴的代码原封不动写入"——1.0 简化版的覆盖陷阱
|
||||
- ❌ "保留 state_snapshot 描述"——state snapshot 可能不准确(写的是意图,磁盘才是事实)
|
||||
- ❌ "用 git log 反推当前状态"——git log 不能反映工作区未提交改动
|
||||
- ❌ "靠 memory 推断当前状态"——memory 可能是 22 天前的(已确认过期)
|
||||
- ❌ "磁盘和用户给的代码看起来一样就不审计"——一行之差可能就是"防弹层"丢失
|
||||
|
||||
## 参考代码
|
||||
|
||||
### PyQt5 mock 模板(worker_thread.py 顶层导入测试)
|
||||
|
||||
```python
|
||||
import os, sys
|
||||
os.environ['GDAL_FILENAME_IS_UTF8'] = 'YES'
|
||||
os.environ['SHAPE_ENCODING'] = 'UTF-8'
|
||||
sys.path.insert(0, 'src/gui/core')
|
||||
|
||||
import types
|
||||
pyqt5 = types.ModuleType("PyQt5")
|
||||
qtc = types.ModuleType("PyQt5.QtCore")
|
||||
class _QThread:
|
||||
def __init__(self, *a, **kw): pass
|
||||
class _Signal:
|
||||
def __init__(self, *a, **kw): pass
|
||||
qtc.QThread = _QThread
|
||||
qtc.pyqtSignal = _Signal
|
||||
qtc.Qt = type("Qt", (), {"QueuedConnection": 1, "UserRole": 0})()
|
||||
sys.modules["PyQt5"] = pyqt5
|
||||
sys.modules["PyQt5.QtCore"] = qtc
|
||||
|
||||
import worker_thread
|
||||
# 副作用: check_pipeline_dependencies() 会打印依赖检查日志(可忽略)
|
||||
```
|
||||
|
||||
### Windows 上跑 conda env python
|
||||
|
||||
```bat
|
||||
cmd /c "D:\xxx\anconda\envs\XXX\python.exe D:\path\to\script.py"
|
||||
```
|
||||
|
||||
PowerShell 单行 `python -c "..."` 在中文路径 / 双引号 / 单引号嵌套时易翻车,**写临时 .py 文件再用 `cmd /c` 调**最稳。
|
||||
|
||||
## 案例来源(2026-06-03 WQ_GUI 路线 B MVP)
|
||||
|
||||
- 用户贴 1.0 简化版:300 行 automl_trainer / 简化 worker_thread.run() / 简化 on_run_all_clicked
|
||||
- 磁盘上 2.0 落盘版:545 行 automl_trainer(_build_model + _get_search_space 工厂 / argparse CLI)/ duck-type 探测 v2 + PipelineContext 抽象 / 完整二次确认窗 / 失败步骤 _focus_step 定位 / [DEPRECATED] stop 保留
|
||||
- 1.0 唯一真增量 = **"防弹级参数清洗器"**(method_map 14 项 + inspect.signature 过滤未知 key + has_kwargs 豁免 + 未知 key 数量日志)
|
||||
- 落地:worker_thread.py:run() 内 set_callback 之后插入 53 行清洗器,self.config 6 处替换为 clean_config
|
||||
- 验证:5 文件 AST 全通过 + 关键符号 7 项命中 + PyQt5 mock 下 import 成功
|
||||
- 净增行数:407 → 457(+50 行)
|
||||
Reference in New Issue
Block a user