#!/usr/bin/env node /** * Patch C1: stop-dispatcher Batch3 预算控制 * Batch3 串行 5 阶段累计最高 14800ms,远超 Stop hook 5000ms 预算。 * 引入 deadline = _perf_start + BUDGET_MS,超时后跳过剩余阶段。 * 每阶段超时自动截断为 min(原预算, deadline - now)。 * 幂等: sentinel C1_BATCH3_BUDGET_v1 */ const fs = require('fs'); const path = require('path'); const TARGET = path.join(__dirname, '..', '..', 'hooks', 'stop-dispatcher.js'); const SENTINEL = 'C1_BATCH3_BUDGET_v1'; function main() { const src = fs.readFileSync(TARGET, 'utf8'); if (src.includes(SENTINEL)) { console.error('[patch-c1] already applied'); process.exit(0); } // 查找 Batch3 头部锚点 const anchor = " // ─── Batch 3 · 尾部串行 (明确依赖顺序) ───\n"; if (!src.includes(anchor)) { console.error('[patch-c1] batch3 header anchor missing'); process.exit(3); } // 原 Batch3 5 个阶段 (consistency-sentinel/auto-cleanup/log-rotator/auto-backup/auto-git-push) const oldBlock = " // ─── Batch 3 · 尾部串行 (明确依赖顺序) ───\n" + " // sentinel append evolution-log → cleanup 才能 truncate\n" + " // auto-backup → auto-git-push (快照先于远端推送)\n" + " _stageRecords.push(await race('consistency-sentinel', () => runConsistencySentinel(), 1000));\n" + " _stageRecords.push(await race('auto-cleanup', () => {\n" + " const ac = require('../scripts/auto-cleanup.js');\n" + " if (ac.main) ac.main({ execute: true, ifStale: 86400 });\n" + " }, 5000));\n" + " _stageRecords.push(await race('log-rotator', () => {\n" + " const lr = require('./log-rotator.js');\n" + " if (lr.runRotation) lr.runRotation();\n" + " }, 800));\n" + " _stageRecords.push(await race('auto-backup', () => {\n" + " const backup = require('../scripts/auto-backup.js');\n" + " backup();\n" + " }, 3000));\n" + " _stageRecords.push(await race('auto-git-push', () => {\n" + " const sync = require('../scripts/auto-git-sync.js');\n" + " sync.pushChanges();\n" + " }, 5000));\n"; if (!src.includes(oldBlock)) { console.error('[patch-c1] batch3 body anchor missing'); process.exit(4); } const newBlock = " // ─── Batch 3 · 尾部串行 (明确依赖顺序) ───\n" + " // " + SENTINEL + ": 总预算硬截断,防止 Stop hook 整体超过 5000ms 宿主 kill\n" + " // sentinel append evolution-log → cleanup 才能 truncate\n" + " // auto-backup → auto-git-push (快照先于远端推送)\n" + " const _BUDGET_MS = 4200;\n" + " const _deadline = _perf_start + _BUDGET_MS;\n" + " const _budgetRace = async (name, fn, origMs) => {\n" + " const remaining = _deadline - Date.now();\n" + " if (remaining <= 100) {\n" + " _stageRecords.push({ name, ok: false, ms: 0, skipped: true, reason: 'budget-exhausted' });\n" + " return;\n" + " }\n" + " _stageRecords.push(await race(name, fn, Math.min(origMs, remaining)));\n" + " };\n" + " await _budgetRace('consistency-sentinel', () => runConsistencySentinel(), 1000);\n" + " await _budgetRace('auto-cleanup', () => {\n" + " const ac = require('../scripts/auto-cleanup.js');\n" + " if (ac.main) ac.main({ execute: true, ifStale: 86400 });\n" + " }, 5000);\n" + " await _budgetRace('log-rotator', () => {\n" + " const lr = require('./log-rotator.js');\n" + " if (lr.runRotation) lr.runRotation();\n" + " }, 800);\n" + " await _budgetRace('auto-backup', () => {\n" + " const backup = require('../scripts/auto-backup.js');\n" + " backup();\n" + " }, 3000);\n" + " await _budgetRace('auto-git-push', () => {\n" + " const sync = require('../scripts/auto-git-sync.js');\n" + " sync.pushChanges();\n" + " }, 5000);\n"; const patched = src.replace(oldBlock, newBlock); if (patched === src) { console.error('[patch-c1] no change'); process.exit(5); } const bakDir = path.join(path.dirname(TARGET), 'archive'); if (!fs.existsSync(bakDir)) fs.mkdirSync(bakDir, { recursive: true }); fs.writeFileSync(path.join(bakDir, 'stop-dispatcher.js.bak.c1.' + Date.now()), src); const tmp = TARGET + '.tmp.' + process.pid; fs.writeFileSync(tmp, patched); fs.renameSync(tmp, TARGET); console.error('[patch-c1] applied'); } main();