chore(hooks): OpenAPI 抓取 + Prompt 同步 提醒 hook (W1 / F2-1B)
历史教训 (F2-1A): OpenAPI 抓取脚本 2026-04-06 commit 779b2f6 被
Claude Opus 4.6 在批量清理 1155 个废弃文件时误归档到
_DEL/_DEL/scripts/ops/, 导致 docs/contracts/openapi/backend-api.json
28 天 stale (137 paths -> 实际 167 paths, 缺 30 端点)。
Neo 反馈: 建立机制不要让类似事件再发生。
F3-2C 配套需求: 建立 docs/ai/system-prompts/ 独立 MD 体系后,
维护流程依赖 3 步同步 (元信息日期 / 同步历史 / _INDEX.md 状态表),
容易遗漏造成漂移。
新增 2 个 PostToolUse hook (Edit|Write matcher, timeout 5s):
- post_edit_openapi_reminder.py: 改 apps/backend/app/routers/*.py
提醒重抓 OpenAPI 具体命令
(.venv/Scripts/python.exe scripts/ops/_export_openapi.py)
- post_edit_prompt_sync_reminder.py: 改 docs/ai/system-prompts/app*.md
提醒 3 步同步 (元信息 / 同步历史 / _INDEX.md 状态表)
settings.json 注册 2 个 hook 条目 (PostToolUse 末尾追加)。
spec-close.md 步骤 5 文档同步表:
- OpenAPI 行升级为具体命令 (避免手工同步漂移描述)
- 新增 docs/ai/system-prompts/app*.md 同步行
验证: stdin 模拟测试 3 场景全部符合预期 (匹配 router 输出 OpenAPI 提醒;
匹配 system-prompts MD 输出 prompt 同步提醒; 不匹配静默)。
详见 docs/audit/changes/2026-05-05__wave1_f2_1b_defense_hooks.md
This commit is contained in:
@@ -42,7 +42,8 @@
|
|||||||
|------|----------|
|
|------|----------|
|
||||||
| 模块 README | 模块内部结构变更时 |
|
| 模块 README | 模块内部结构变更时 |
|
||||||
| `apps/backend/docs/API-REFERENCE.md` | 新增/修改后端路由时 |
|
| `apps/backend/docs/API-REFERENCE.md` | 新增/修改后端路由时 |
|
||||||
| `docs/contracts/openapi/backend-api.json` | 新增/修改 API 端点时 |
|
| `docs/contracts/openapi/backend-api.json` | 新增/修改 API 端点时 — **执行命令重抓**:`.venv/Scripts/python.exe scripts/ops/_export_openapi.py`(避免手工同步漂移;hook `post_edit_openapi_reminder.py` 会在改 router 时自动提醒) |
|
||||||
|
| `docs/ai/system-prompts/app*.md` | 从百炼控制台同步 system prompt 时 — 改 §一 元信息"最后同步"+ §同步历史追加一行 + 同步 `_INDEX.md` §四 状态表(hook `post_edit_prompt_sync_reminder.py` 会自动提醒) |
|
||||||
| `docs/DOCUMENTATION-MAP.md` | 新增任何文档条目时 |
|
| `docs/DOCUMENTATION-MAP.md` | 新增任何文档条目时 |
|
||||||
|
|
||||||
### 步骤 6:变更审计收口(涉及高风险路径时必选)
|
### 步骤 6:变更审计收口(涉及高风险路径时必选)
|
||||||
|
|||||||
38
.claude/hooks/post_edit_openapi_reminder.py
Normal file
38
.claude/hooks/post_edit_openapi_reminder.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""PostToolUse hook: 编辑 backend router 后提醒重抓 OpenAPI
|
||||||
|
|
||||||
|
历史教训(2026-04-06 commit 779b2f6):
|
||||||
|
脚本 scripts/ops/_export_openapi.py 被 Claude Opus 4.6 误归档到 _DEL/,
|
||||||
|
导致 28 天内 docs/contracts/openapi/backend-api.json 与代码漂移
|
||||||
|
(137 paths stale → 实际 167 paths,缺 30 个端点)。
|
||||||
|
|
||||||
|
Wave 1 F2-1A 恢复脚本 + F2-1B(本 hook)建立防御机制,
|
||||||
|
任何 router 改动后提醒重抓,确保 OpenAPI 静态导出不再 stale。
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
except Exception:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
fp = (data.get("tool_input") or {}).get("file_path", "")
|
||||||
|
if not fp:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
rel = re.sub(r"^.*?NeoZQYY[/\\]", "", fp.replace("\\", "/"))
|
||||||
|
|
||||||
|
if re.search(r"^apps/backend/app/routers/.*\.py$", rel):
|
||||||
|
print(json.dumps({
|
||||||
|
"hookSpecificOutput": {
|
||||||
|
"hookEventName": "PostToolUse",
|
||||||
|
"additionalContext": (
|
||||||
|
f"[openapi-sync] 已编辑后端 router: {rel} — "
|
||||||
|
"如有新增/修改/删除端点,完成后须重抓 OpenAPI: "
|
||||||
|
"`.venv/Scripts/python.exe scripts/ops/_export_openapi.py` "
|
||||||
|
"(更新 docs/contracts/openapi/backend-api.json,避免静态导出 stale)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}))
|
||||||
40
.claude/hooks/post_edit_prompt_sync_reminder.py
Normal file
40
.claude/hooks/post_edit_prompt_sync_reminder.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""PostToolUse hook: 编辑 system prompt 独立 MD 后提醒同步状态表
|
||||||
|
|
||||||
|
背景(F3-2C / 2026-05-05):
|
||||||
|
百炼云端为 system prompt 权威源,docs/ai/system-prompts/ 各独立 MD 是本地 git 备份。
|
||||||
|
每次从云端同步后,需要更新:
|
||||||
|
- 当前 MD 的 §一 元信息表"最后同步"日期
|
||||||
|
- 当前 MD 的 §同步历史 追加一行
|
||||||
|
- _INDEX.md §四 同步状态表对应行
|
||||||
|
|
||||||
|
本 hook 在编辑 app*.md 后提醒上述同步动作。
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
except Exception:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
fp = (data.get("tool_input") or {}).get("file_path", "")
|
||||||
|
if not fp:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
rel = re.sub(r"^.*?NeoZQYY[/\\]", "", fp.replace("\\", "/"))
|
||||||
|
|
||||||
|
if re.search(r"^docs/ai/system-prompts/app\w+\.md$", rel):
|
||||||
|
print(json.dumps({
|
||||||
|
"hookSpecificOutput": {
|
||||||
|
"hookEventName": "PostToolUse",
|
||||||
|
"additionalContext": (
|
||||||
|
f"[prompt-sync] 已编辑 system prompt 独立 MD: {rel} — "
|
||||||
|
"若改动了 §四 System Prompt 章节(从云端同步),请同步:"
|
||||||
|
"(1) §一 元信息表'最后同步'改为今日;"
|
||||||
|
"(2) §同步历史 追加一行;"
|
||||||
|
"(3) docs/ai/system-prompts/_INDEX.md §四 同步状态表对应行更新"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}))
|
||||||
@@ -75,6 +75,26 @@
|
|||||||
"timeout": 10
|
"timeout": 10
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": "Edit|Write",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/post_edit_openapi_reminder.py\"",
|
||||||
|
"timeout": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": "Edit|Write",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/post_edit_prompt_sync_reminder.py\"",
|
||||||
|
"timeout": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Stop": [
|
"Stop": [
|
||||||
|
|||||||
128
docs/audit/changes/2026-05-05__wave1_f2_1b_defense_hooks.md
Normal file
128
docs/audit/changes/2026-05-05__wave1_f2_1b_defense_hooks.md
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Wave 1 F2-1B — OpenAPI 抓取 + Prompt 同步 防御机制 hook
|
||||||
|
|
||||||
|
| 字段 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 日期 | 2026-05-05 |
|
||||||
|
| Wave | 1 / Day 5 收尾(F2-1B + F3-2C 配套机制) |
|
||||||
|
| 范围 | 加 2 个 PostToolUse hook;升级 `.claude/commands/spec-close.md` L45 为具体命令 |
|
||||||
|
| 文件改动 | 新增 2 hook + 修改 settings.json + 修改 spec-close.md + 1 审计 |
|
||||||
|
|
||||||
|
## 一、起因
|
||||||
|
|
||||||
|
### F2-1A 历史教训(2026-04-06 commit 779b2f6)
|
||||||
|
|
||||||
|
OpenAPI 抓取脚本 `scripts/ops/_export_openapi.py` 被 Claude Opus 4.6 在批量清理 1155 个废弃文件时**误归档**到 `_DEL/_DEL/scripts/ops/`,导致:
|
||||||
|
- `docs/contracts/openapi/backend-api.json` 28 天**未更新**(2026-04-06 → 2026-05-04)
|
||||||
|
- 实际 router 已增 30 端点,但静态导出仍是 137 paths
|
||||||
|
- W1-T7 admin-web API PRD 总览基于 stale json 误写"151 端点"
|
||||||
|
- F2-1A 5/4 已恢复脚本 + 重抓(137 → 167 paths,详见 `2026-05-04__wave1_f2_1_openapi_script_restored.md`)
|
||||||
|
|
||||||
|
Neo 反馈:**"建立机制不要让类似事件再发生"** — 即 F2-1B。
|
||||||
|
|
||||||
|
### F3-2C 配套需求(2026-05-05)
|
||||||
|
|
||||||
|
F3-2C 建立了 `docs/ai/system-prompts/` 独立 MD 体系,但维护流程依赖:
|
||||||
|
- 改某 APP MD 后须同步 §一 元信息"最后同步" + §同步历史 + `_INDEX.md` §四
|
||||||
|
- 上述步骤容易遗漏,造成同步状态表与文件实际内容漂移
|
||||||
|
|
||||||
|
需要 hook 在改 `system-prompts/app*.md` 时自动提醒上述同步。
|
||||||
|
|
||||||
|
## 二、产出
|
||||||
|
|
||||||
|
### 2.1 新增 hook 1:`post_edit_openapi_reminder.py`
|
||||||
|
|
||||||
|
`.claude/hooks/post_edit_openapi_reminder.py`(38 行)
|
||||||
|
|
||||||
|
**触发**:Edit / Write 匹配 `apps/backend/app/routers/*.py`
|
||||||
|
**输出**:提醒重抓 OpenAPI 的具体命令 `.venv/Scripts/python.exe scripts/ops/_export_openapi.py`
|
||||||
|
|
||||||
|
### 2.2 新增 hook 2:`post_edit_prompt_sync_reminder.py`
|
||||||
|
|
||||||
|
`.claude/hooks/post_edit_prompt_sync_reminder.py`(36 行)
|
||||||
|
|
||||||
|
**触发**:Edit / Write 匹配 `docs/ai/system-prompts/app*.md`
|
||||||
|
**输出**:提醒 3 步同步动作(元信息日期 / 同步历史 / `_INDEX.md` 状态表)
|
||||||
|
|
||||||
|
### 2.3 修改 `.claude/settings.json`
|
||||||
|
|
||||||
|
PostToolUse 新增 2 个 hook 条目(matcher 都是 `Edit|Write`,timeout 5s)。
|
||||||
|
|
||||||
|
### 2.4 修改 `.claude/commands/spec-close.md`
|
||||||
|
|
||||||
|
§步骤 5 的文档同步表第 3 行升级:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
-| `docs/contracts/openapi/backend-api.json` | 新增/修改 API 端点时 |
|
||||||
|
+| `docs/contracts/openapi/backend-api.json` | 新增/修改 API 端点时 — 执行命令重抓:`.venv/Scripts/python.exe scripts/ops/_export_openapi.py`(避免手工同步漂移;hook `post_edit_openapi_reminder.py` 会在改 router 时自动提醒) |
|
||||||
|
```
|
||||||
|
|
||||||
|
并新增第 4 行:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
+| `docs/ai/system-prompts/app*.md` | 从百炼控制台同步 system prompt 时 — 改 §一 元信息"最后同步"+ §同步历史追加一行 + 同步 `_INDEX.md` §四 状态表(hook `post_edit_prompt_sync_reminder.py` 会自动提醒) |
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三、验证
|
||||||
|
|
||||||
|
模拟 stdin 测试 hook(`echo '{...}' | python <hook>.py`):
|
||||||
|
|
||||||
|
| 场景 | 预期 | 实际 |
|
||||||
|
|---|---|---|
|
||||||
|
| 改 `apps/backend/app/routers/admin_ai.py` | OpenAPI 提醒 | ✅ 输出提醒 JSON |
|
||||||
|
| 改 `docs/ai/system-prompts/app3_clue.md` | Prompt 同步提醒 | ✅ 输出提醒 JSON |
|
||||||
|
| 改 `README.md`(不匹配) | 静默 | ✅ 无输出 |
|
||||||
|
|
||||||
|
## 四、设计取舍
|
||||||
|
|
||||||
|
### 为什么是 PostToolUse 提醒而不是阻断?
|
||||||
|
|
||||||
|
提醒(`additionalContext`)比阻断(`exit 2`)合理:
|
||||||
|
- Hook 无法判断"是否新增/修改/删除端点"(只看 file_path),粗粒度阻断会误伤格式调整、注释修改
|
||||||
|
- 提醒强度足够:Claude 在下一轮看到 additionalContext,会按提醒执行重抓命令
|
||||||
|
- 阻断风格的 hook 应留给"绝对不能动"的场景(如 `_archived/` 目录 / `apps/demo-miniprogram/`)
|
||||||
|
|
||||||
|
### 为什么不在 CI 加 OpenAPI 一致性检查?
|
||||||
|
|
||||||
|
Neo F2-1 反馈"没有启动 Gitea Actions 的打算"(Wave 1 findings 第二轮),CI 加检查留 Wave 5 F2-2(tests/ 入仓 Gitea 简版)再考虑。当前阶段 hook 提醒 + spec-close.md 兜底已够用。
|
||||||
|
|
||||||
|
### 为什么不加月度对照云端 prompt 的定时机制?
|
||||||
|
|
||||||
|
定时提醒需要外部调度器(cron / Windows 任务计划),与 Claude Code 当前架构不衔接。可选项已在 `_INDEX.md` §八 标记"(可选)搭定时机制",留给后续 Wave。
|
||||||
|
|
||||||
|
## 五、关联
|
||||||
|
|
||||||
|
- F2-1A 恢复脚本 + 重抓:[`2026-05-04__wave1_f2_1_openapi_script_restored.md`](2026-05-04__wave1_f2_1_openapi_script_restored.md)
|
||||||
|
- F3-2C 系统 prompt 拆分:[`2026-05-05__wave1_f3_2c_system_prompts_split.md`](2026-05-05__wave1_f3_2c_system_prompts_split.md)
|
||||||
|
- W1 findings F2-1 / F2-1A / F2-1B 三轮反馈:`docs/_overview/wave1-findings/01-W1-findings-response.md`
|
||||||
|
|
||||||
|
## 六、风险与回滚
|
||||||
|
|
||||||
|
| 项 | 风险 | 回滚 |
|
||||||
|
|---|---|---|
|
||||||
|
| 2 个新 hook | 极低 — 仅 PostToolUse 提醒,不阻断;失败时 try/except 静默 sys.exit(0) | 删除 hook 文件 + settings.json 对应 entry |
|
||||||
|
| settings.json 改动 | 低 — 加 2 个 entry,不动现有 8 个 entry | git checkout 还原 |
|
||||||
|
| spec-close.md 改动 | 低 — 仅升级文档表格,加具体命令 + 1 行 | git checkout 还原 |
|
||||||
|
|
||||||
|
## 七、commit 建议
|
||||||
|
|
||||||
|
```
|
||||||
|
chore(hooks): 加 OpenAPI 抓取 + Prompt 同步 提醒 hook (W1 / F2-1B)
|
||||||
|
|
||||||
|
历史教训(F2-1A): OpenAPI 抓取脚本 2026-04-06 被误归档,
|
||||||
|
导致 docs/contracts/openapi/backend-api.json 28 天 stale。
|
||||||
|
F3-2C 建立 system prompt 独立 MD 体系后,同步状态表也需要
|
||||||
|
机制保证不漂移。
|
||||||
|
|
||||||
|
新增 2 个 PostToolUse hook:
|
||||||
|
- post_edit_openapi_reminder.py: 改 backend router 提醒重抓 OpenAPI
|
||||||
|
- post_edit_prompt_sync_reminder.py: 改 system-prompts/app*.md
|
||||||
|
提醒同步 §一 元信息日期 / §同步历史 / _INDEX.md §四 状态表
|
||||||
|
|
||||||
|
settings.json 注册 2 个 hook(matcher Edit|Write, timeout 5s)。
|
||||||
|
|
||||||
|
spec-close.md §步骤 5:
|
||||||
|
- OpenAPI 行升级为具体命令(避免手工同步漂移)
|
||||||
|
- 新增 system-prompts/app*.md 同步行
|
||||||
|
|
||||||
|
详见 docs/audit/changes/2026-05-05__wave1_f2_1b_defense_hooks.md
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user