bookworm-smart-assistant/scripts/patches/patch-w2-legacy-actualskill-gate.js

129 lines
5.2 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* W2 补丁 2026-04-16 audit-2026-04-16 WARNING
*
* 问题: H1 修复后 actual-skills.jsonl 已成主路径 actual-skill.json 双写/双读残留
* 占用 I/O 与代码复杂度直接移除风险高 feature flag 安全开关
*
* 修复方案: 引入环境变量 BOOKWORM_LEGACY_ACTUAL_SKILL 作为 feature flag
* 默认 (未设置): 跳过旧 json 写入 + 跳过旧 json 回退读取
* 设为 '1': 保留双写+双读 (紧急回滚路径)
*
* 两处修改:
* - hooks/route-compliance-gate.js L264-266 写入路径前加环境变量守卫
* - hooks/route-auditor.js L119-128 回退读取前加环境变量守卫
*
* 清理: 补丁运行成功后删除 debug/actual-skill.json (若存在)
*
* 回滚: set BOOKWORM_LEGACY_ACTUAL_SKILL=1 即可恢复旧行为
* 彻底移除计划: 稳定运行 1 周后 (2026-04-23) 出补丁 W2b 删除 flag 与代码
*
* 幂等: sentinel = 'BOOKWORM_LEGACY_ACTUAL_SKILL'
*/
'use strict';
const fs = require('fs');
const path = require('path');
const HOOKS_DIR = path.join(__dirname, '..', '..', 'hooks');
const GATE_FILE = path.join(HOOKS_DIR, 'route-compliance-gate.js');
const AUDITOR_FILE = path.join(HOOKS_DIR, 'route-auditor.js');
const LEGACY_DATA = path.join(__dirname, '..', '..', 'debug', 'actual-skill.json');
const SENTINEL = 'BOOKWORM_LEGACY_ACTUAL_SKILL';
function patchGate() {
const before = fs.readFileSync(GATE_FILE, 'utf8');
if (before.includes(SENTINEL)) {
console.log('[patch-w2] route-compliance-gate.js 已打过补丁,跳过');
return false;
}
const anchor =
' fs.appendFileSync(actualJsonl, line);\n' +
' // 向后兼容: 仍写旧单文件,让未迁移的 auditor 能降级读取\n' +
' const actualFile = path.join(DEBUG_DIR, \'actual-skill.json\');\n' +
' fs.writeFileSync(actualFile, line);';
const replacement =
' fs.appendFileSync(actualJsonl, line);\n' +
' // W2 (2026-04-16): 旧 actual-skill.json 默认不写,由 feature flag 恢复\n' +
' // 回滚: set BOOKWORM_LEGACY_ACTUAL_SKILL=1\n' +
' if (process.env.BOOKWORM_LEGACY_ACTUAL_SKILL === \'1\') {\n' +
' const actualFile = path.join(DEBUG_DIR, \'actual-skill.json\');\n' +
' fs.writeFileSync(actualFile, line);\n' +
' }';
if (!before.includes(anchor)) {
console.error('[patch-w2] route-compliance-gate.js 锚点未匹配');
return false;
}
const after = before.replace(anchor, replacement);
const backup = GATE_FILE + '.bak.w2.' + Date.now();
fs.writeFileSync(backup, before);
const _tmp = GATE_FILE + '.tmp.' + process.pid;
fs.writeFileSync(_tmp, after);
fs.renameSync(_tmp, GATE_FILE);
console.log('[patch-w2] ✓ route-compliance-gate.js 双写路径已加 flag 守卫');
return true;
}
function patchAuditor() {
const before = fs.readFileSync(AUDITOR_FILE, 'utf8');
if (before.includes(SENTINEL)) {
console.log('[patch-w2] route-auditor.js 已打过补丁,跳过');
return false;
}
const anchor =
' // 回退: 旧单文件 actual-skill.json\n' +
' if (!actualSkill) {\n' +
' const actualFile = path.join(DEBUG_DIR, \'actual-skill.json\');\n' +
' if (fs.existsSync(actualFile)) {\n' +
' const actualData = JSON.parse(fs.readFileSync(actualFile, \'utf8\'));\n' +
' if (actualData.traceId === state.traceId && actualData.actualSkill) {\n' +
' actualSkill = actualData.actualSkill;\n' +
' }\n' +
' }\n' +
' }';
const replacement =
' // 回退: 旧单文件 actual-skill.json (W2: 默认跳过,由 flag 恢复)\n' +
' if (!actualSkill && process.env.BOOKWORM_LEGACY_ACTUAL_SKILL === \'1\') {\n' +
' const actualFile = path.join(DEBUG_DIR, \'actual-skill.json\');\n' +
' if (fs.existsSync(actualFile)) {\n' +
' const actualData = JSON.parse(fs.readFileSync(actualFile, \'utf8\'));\n' +
' if (actualData.traceId === state.traceId && actualData.actualSkill) {\n' +
' actualSkill = actualData.actualSkill;\n' +
' }\n' +
' }\n' +
' }';
if (!before.includes(anchor)) {
console.error('[patch-w2] route-auditor.js 锚点未匹配');
return false;
}
const after = before.replace(anchor, replacement);
const backup = AUDITOR_FILE + '.bak.w2.' + Date.now();
fs.writeFileSync(backup, before);
const _tmp = AUDITOR_FILE + '.tmp.' + process.pid;
fs.writeFileSync(_tmp, after);
fs.renameSync(_tmp, AUDITOR_FILE);
console.log('[patch-w2] ✓ route-auditor.js 回退路径已加 flag 守卫');
return true;
}
function cleanupLegacyData() {
try {
if (fs.existsSync(LEGACY_DATA)) {
fs.unlinkSync(LEGACY_DATA);
console.log('[patch-w2] ✓ 删除历史数据文件 debug/actual-skill.json');
}
} catch (e) {
console.log('[patch-w2] ! 清理历史文件失败: ' + e.message);
}
}
try {
const a = patchGate();
const b = patchAuditor();
if (a || b) cleanupLegacyData();
console.log('[patch-w2] 完成。回滚: set BOOKWORM_LEGACY_ACTUAL_SKILL=1');
} catch (e) {
console.error('[patch-w2] 异常:', e.message);
process.exit(99);
}