bookworm-smart-assistant/scripts/patches/v6.6-rc2-03-closure-loop-rotator.js

113 lines
4.9 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* Patch v6.6-rc2-03 · 宪法闭环度细则 + log-rotator 扩展
* 代号 v6.6-rc2-20260422-1859
*
* 1. constitution/anti-arrogance.md 追加"闭环度 · Agent 信任边界"
* sentinel: <!-- closure-loop:agent-trust:v1 --> ... <!-- /closure-loop:agent-trust:v1 -->
* 插入位置: "10 类典型失败" 标题之前 (生效清单之后)
* 2. hooks/log-rotator.js 正则扩展, agent-returns-*.jsonl + agent-returns.jsonl 纳入轮转
* sentinel: AGENT_RETURNS_ROTATE_V66
* 保留期沿用现有 7 天约定 ( prompt 30 天已就地对齐, 见失败透明说明)
*
* 回滚: pwsh scripts/rollback-v6.6-rc2.ps1 -Scope all
* 重签: node hooks/integrity-check.js --generate
*/
'use strict';
const fs = require('fs');
const path = require('path');
const CLAUDE_ROOT = path.resolve(__dirname, '..', '..');
const CONSTITUTION = path.join(CLAUDE_ROOT, 'constitution', 'anti-arrogance.md');
const LOG_ROTATOR = path.join(CLAUDE_ROOT, 'hooks', 'log-rotator.js');
const BAK_TAG = 'v6.6-rc2-03-closure-loop-rotator';
const SENTINEL_OPEN = '<!-- closure-loop:agent-trust:v1 -->';
const SENTINEL_CLOSE = '<!-- /closure-loop:agent-trust:v1 -->';
// 宪法追加段 (字符串拼接避免反引号冲突)
const CONSTITUTION_BLOCK =
SENTINEL_OPEN + '\n\n' +
'## 闭环度 · Agent 信任边界 (v6.6-rc2)\n\n' +
'基于条款 2 闭环度, 扩展至 Agent 信任边界层:\n\n' +
'1. **Agent 返回视为二手信息** — Task tool 派遣的任一 Agent 返回文本, 均属"未经核实的二手声明", 不得直接作为交付证据。\n' +
'2. **部署 / 修改 / 远端状态必须亲验** — 凡 Agent 声明涉及文件修改、部署落地或远端状态变更, 主 Claude 必须通过以下至少一种方式亲自验证: Read / Bash / curl / ssh / Playwright / MCP probe。\n' +
'3. **未验证措辞降级** — 亲验未完成前, 交付语必须降级为 "Agent 报告已完成, 待验证" 等措辞, 禁用 "已部署 / 已修复 / 已完成" 类终态动词。\n' +
'4. **紧急制动词作用域限定** — 用户输入 "Agent 撒谎" 或 "别信 Agent" 时, 仅作用于最近一次 SubagentStop 的 agent_id, 不污染主 Claude 的自负计数。\n\n' +
SENTINEL_CLOSE + '\n';
// 宪法锚点: 在"10 类典型失败 → 约束映射"之前插入
const CONSTITUTION_ANCHOR = '## 10 类典型失败 → 约束映射';
// log-rotator 精准锚点 (包含 W3 注释确保唯一)
const LR_ANCHOR =
'/* W3_LOG_EXTEND_v1 */ (/^(activity|trace|outcome|compliance|security|route-stats-daily|ab-experiments|hook-timing|skill-outcome|pre-agent-gate)-?/';
const LR_REPLACEMENT =
'/* W3_LOG_EXTEND_v1 */ /* AGENT_RETURNS_ROTATE_V66 */ (/^(activity|trace|outcome|compliance|security|route-stats-daily|ab-experiments|hook-timing|skill-outcome|pre-agent-gate|agent-returns)-?/';
function tsStamp() { return new Date().toISOString().replace(/[:.]/g, '-'); }
function atomicWrite(target, content) {
const tmp = target + '.tmp.' + process.pid;
fs.writeFileSync(tmp, content);
fs.renameSync(tmp, target);
}
function patchConstitution() {
if (!fs.existsSync(CONSTITUTION)) {
console.error('[patch-03] 宪法文件不存在: ' + CONSTITUTION);
return false;
}
const text = fs.readFileSync(CONSTITUTION, 'utf8');
if (text.includes(SENTINEL_OPEN)) {
console.log('[patch-03] 宪法已含 sentinel, 跳过');
return false;
}
if (!text.includes(CONSTITUTION_ANCHOR)) {
console.error('[patch-03] 宪法未找到锚点: ' + CONSTITUTION_ANCHOR);
return false;
}
fs.copyFileSync(CONSTITUTION, CONSTITUTION + '.bak.' + BAK_TAG + '.' + tsStamp());
const patched = text.replace(CONSTITUTION_ANCHOR, CONSTITUTION_BLOCK + '\n' + CONSTITUTION_ANCHOR);
atomicWrite(CONSTITUTION, patched);
console.log('[patch-03] 宪法已追加闭环度细则 (sentinel 包裹)');
return true;
}
function patchLogRotator() {
if (!fs.existsSync(LOG_ROTATOR)) {
console.error('[patch-03] log-rotator 不存在');
return false;
}
const raw = fs.readFileSync(LOG_ROTATOR, 'utf8');
if (raw.includes('AGENT_RETURNS_ROTATE_V66')) {
console.log('[patch-03] log-rotator 已含 sentinel, 跳过');
return false;
}
if (!raw.includes(LR_ANCHOR)) {
console.error('[patch-03] log-rotator 未找到锚点');
return false;
}
fs.copyFileSync(LOG_ROTATOR, LOG_ROTATOR + '.bak.' + BAK_TAG + '.' + tsStamp());
const patched = raw.replace(LR_ANCHOR, LR_REPLACEMENT);
atomicWrite(LOG_ROTATOR, patched);
console.log('[patch-03] log-rotator 正则已纳入 agent-returns');
return true;
}
function main() {
console.log('[patch-03] v6.6-rc2-03 宪法闭环度 + log-rotator');
const a = patchConstitution();
const b = patchLogRotator();
if (!a && !b) {
console.log('[patch-03] 补丁已生效, 无变更');
} else {
console.log('[patch-03] 完成');
console.log('[patch-03] 后续必跑: node hooks/integrity-check.js --generate');
}
process.exit(0);
}
main();