Преглед изворни кода

fix: 修复工作流步骤顺序 + 归档触发条件 + 原子性问题

🔴 Priority 1: 修复工作流步骤顺序(CRITICAL - 防止索引数据丢失)
- 调整 webnovel-write.md:
  - Step 4.5: 更新结构化索引(提前)✅
  - Step 4.6: 数据归档(延后)✅
- 原顺序:归档先执行 → 索引后执行 → 归档数据永远不会被索引
- 新顺序:索引先执行 → 归档后执行 → 所有数据都被索引后再清理

🟠 Priority 4: 修复归档文件大小触发条件
- archive_manager.py:75
  - file_size_trigger_mb: 1.0 MB → 0.5 MB
  - 理由:400 章场景仅产生 0.70 MB,1.0 MB 阈值永远不会触发
  - 预期:约 280 章时触发首次归档

🟡 Priority 6: 降低审查报告归档阈值
- archive_manager.py:74
  - review_old_threshold: 50 章 → 20 章
  - 理由:50 章阈值过高,导致 state.json 积累过多旧报告(57% 总容量)
  - 预期:保留最近 20 章报告,归档 95% 旧报告

🟡 Priority 7: 修复角色恢复操作原子性
- archive_manager.py:356-384
  - 反转操作顺序:先从归档移除 → 再添加到 state.json
  - 理由:即使崩溃,数据仍在归档中,可重新恢复,不会重复或丢失

📊 影响范围
- 工作流执行顺序改变(无数据破坏风险)
- 归档触发更早(200万字长跑保障)
- 恢复操作更安全(原子性保障)

🚀 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
lingfengQAQ пре 5 месеци
родитељ
комит
4bf5f06007

+ 49 - 48
.claude/commands/webnovel-write.md

@@ -335,56 +335,13 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py complete-step
 
 ---
 
-### Step 4.5: Data Archiving (AUTO-TRIGGERED)
+### Step 4.5: Update Structured Index (AUTO-TRIGGERED, 2 sub-steps)
 
-**CRITICAL**: After Step 4, **automatically run** archive check:
-
-```bash
-python .claude/skills/webnovel-writer/scripts/archive_manager.py --auto-check
-```
-
-**Purpose**: 防止 state.json 无限增长(200万字长跑保障)
-
-**Archiving Strategy**:
-- **角色归档**: 超过 50 章未出场的次要角色 → `archive/characters.json`
-- **伏笔归档**: status="已回收" 且超过 20 章的伏笔 → `archive/plot_threads.json`
-- **报告归档**: 超过 50 章的旧审查报告 → `archive/reviews.json`
-
-**Trigger Conditions** (满足任一即执行):
-- state.json 大小 ≥ 1 MB
-- 当前章节数是 10 的倍数(每 10 章检查一次)
-
-**Expected Output**:
-```
-✅ 无需归档(触发条件未满足)
-   文件大小: 0.35 MB (阈值: 1.0 MB)
-   当前章节: 7 (每 10 章触发)
-```
-
-**OR** (if archiving triggered):
-```
-✅ 归档完成:
-   角色归档: 12 → characters.json
-   伏笔归档: 8 → plot_threads.json
-   报告归档: 5 → reviews.json
-
-💾 文件大小: 1.2 MB → 0.8 MB (节省 0.4 MB)
-```
-
-**IMPORTANT**:
-- **不需要 workflow_manager 追踪**(归档是内部维护操作)
-- 如报错(如文件不存在),视为警告,不阻塞流程
-- 归档数据可随时使用 `--restore-character "角色名"` 恢复
-
----
-
-### Step 4.6: Update Structured Index (AUTO-TRIGGERED, 2 sub-steps)
-
-**CRITICAL**: After archiving, **automatically update** structured index in TWO steps:
+**CRITICAL**: After Step 4, **immediately update** structured index in TWO steps:
 
 ---
 
-#### Step 4.6.1: Extract Metadata with AI Agent
+#### Step 4.5.1: Extract Metadata with AI Agent
 
 **Use Task tool to call metadata-extractor agent**:
 
@@ -439,7 +396,7 @@ else:
 
 ---
 
-#### Step 4.6.2: Write to Index Database
+#### Step 4.5.2: Write to Index Database
 
 **Pass agent's JSON file to structured_index.py** (Windows-compatible):
 
@@ -477,7 +434,7 @@ os.unlink(metadata_file)  # Delete temporary file
 
 ---
 
-**Total Time**: Step 4.6.1 (~1-2s) + Step 4.6.2 (~10ms) = **~1-2s per chapter**
+**Total Time**: Step 4.5.1 (~1-2s) + Step 4.5.2 (~10ms) = **~1-2s per chapter**
 
 **Accuracy Improvement**:
 - **Before** (regex): Location = "未知" (60% accuracy)
@@ -521,6 +478,50 @@ python structured_index.py --stats
 
 ---
 
+### Step 4.6: Data Archiving (AUTO-TRIGGERED)
+
+**CRITICAL**: After indexing, **automatically run** archive check:
+
+```bash
+python .claude/skills/webnovel-writer/scripts/archive_manager.py --auto-check
+```
+
+**Purpose**: 防止 state.json 无限增长(200万字长跑保障)
+
+**Archiving Strategy**:
+- **角色归档**: 超过 50 章未出场的次要角色 → `archive/characters.json`
+- **伏笔归档**: status="已回收" 且超过 20 章的伏笔 → `archive/plot_threads.json`
+- **报告归档**: 超过 50 章的旧审查报告 → `archive/reviews.json`
+
+**Trigger Conditions** (满足任一即执行):
+- state.json 大小 ≥ 1 MB
+- 当前章节数是 10 的倍数(每 10 章检查一次)
+
+**Expected Output**:
+```
+✅ 无需归档(触发条件未满足)
+   文件大小: 0.35 MB (阈值: 1.0 MB)
+   当前章节: 7 (每 10 章触发)
+```
+
+**OR** (if archiving triggered):
+```
+✅ 归档完成:
+   角色归档: 12 → characters.json
+   伏笔归档: 8 → plot_threads.json
+   报告归档: 5 → reviews.json
+
+💾 文件大小: 1.2 MB → 0.8 MB (节省 0.4 MB)
+```
+
+**IMPORTANT**:
+- **不需要 workflow_manager 追踪**(归档是内部维护操作)
+- 如报错(如文件不存在),视为警告,不阻塞流程
+- 归档数据可随时使用 `--restore-character "角色名"` 恢复
+- **归档发生在索引之后**,确保所有数据都被索引后再清理
+
+---
+
 ### Step 5: Git Backup (MANDATORY)
 
 **Before executing Step 5**, **YOU MUST run**:

+ 7 - 6
.claude/skills/webnovel-writer/scripts/archive_manager.py

@@ -71,8 +71,8 @@ class ArchiveManager:
         self.config = {
             "character_inactive_threshold": 50,  # 角色超过 50 章未出场视为不活跃
             "plot_resolved_threshold": 20,       # 已回收伏笔超过 20 章后归档
-            "review_old_threshold": 50,          # 审查报告超过 50 章后归档
-            "file_size_trigger_mb": 1.0,         # state.json 超过 1MB 触发归档
+            "review_old_threshold": 20,          # 审查报告超过 20 章后归档(从 50 降至 20)
+            "file_size_trigger_mb": 0.5,         # state.json 超过 0.5MB 触发归档(从 1.0 降至 0.5)
             "chapter_trigger": 10                # 每 10 章检查一次
         }
 
@@ -372,14 +372,15 @@ class ArchiveManager:
         # 移除 archived_at 字段
         char_to_restore.pop("archived_at", None)
 
+        # ✅ 原子性修复:先从归档中移除,再添加到 state.json
+        # 理由:即使崩溃,数据仍在归档中,可重新恢复,不会丢失或重复
+        archived = [char for char in archived if char["name"] != name]
+        self.save_archive(self.characters_archive, archived)
+
         # 恢复到 state.json
         state["entities"]["characters"].append(char_to_restore)
         self.save_state(state)
 
-        # 从归档中移除
-        archived = [char for char in archived if char["name"] != name]
-        self.save_archive(self.characters_archive, archived)
-
         print(f"✅ 角色已恢复: {name}")
 
     def show_stats(self):