bookworm-smart-assistant/scripts/rollback-v6.6-rc2.ps1
Bookworm Admin b7a8e29d21 release: v6.7.0 - OTA E2E test release
- VERSION file as authoritative version source
- export.mjs reads VERSION with package.json fallback
- bw-ota.ps1 DryRun mode for safe testing
- auto-setup.ps1 bumped to v3.2.0 (Phase 8 OTA)
2026-04-27 17:59:44 +08:00

201 lines
8.9 KiB
PowerShell

#Requires -Version 5.1
<#
.SYNOPSIS
Bookworm Smart Assistant v6.6-rc2 Agent 信任边界体系一键回滚
.DESCRIPTION
代号: v6.6-rc2-20260422-1859
按分级回滚 P3 → P2 → P1 → P0 各阶段,最终重签 HMAC 完整性链。
数据文件 (logs/agent-returns.jsonl, state/claim-mismatches.jsonl) 不删除,保留审计。
Linux/WSL 用户: 逐步等价 bash 版待 port。核心动作参见每步注释的 bash 等价。
.PARAMETER Scope
回滚范围:
all -- 全量回滚到 v6.5.1 (默认)
P3-only -- 仅移除 agent-claim-cross-verify.js
P2-down -- 移除 P3 + Dashboard + 诚实度统计
P1-down -- 移除 P3 + P2 + verifier + snapshot-taker (保留 P0 观察)
.PARAMETER DryRun
预演模式: 只打印 "将移除 / 将恢复" 提示, 不实际执行任何删除/写入,
不调用 integrity-check --generate, 不追加 evolution-log.
真回滚前强烈建议先跑一次 -DryRun 审视预期动作。
.EXAMPLE
pwsh .\rollback-v6.6-rc2.ps1
pwsh .\rollback-v6.6-rc2.ps1 -Scope P3-only
pwsh .\rollback-v6.6-rc2.ps1 -DryRun # 预演全量回滚
pwsh .\rollback-v6.6-rc2.ps1 -Scope P1-down -DryRun
.NOTES
每一步都是幂等的 (Test-Path 先查再删)。多次运行无副作用。
成功执行后 evolution-log.jsonl 追加一条 rollback 事件。
#>
param(
[ValidateSet('all','P3-only','P2-down','P1-down')]
[string]$Scope = 'all',
[switch]$DryRun
)
$ErrorActionPreference = 'Stop'
$CLAUDE_ROOT = Join-Path $env:USERPROFILE '.claude'
$HOOKS = Join-Path $CLAUDE_ROOT 'hooks'
$SCRIPTS = Join-Path $CLAUDE_ROOT 'scripts'
$STATE = Join-Path $CLAUDE_ROOT 'state'
$CONFIG = Join-Path $CLAUDE_ROOT 'config'
$CONSTITUTION = Join-Path $CLAUDE_ROOT 'constitution\anti-arrogance.md'
$SETTINGS = Join-Path $CLAUDE_ROOT 'settings.json'
$EVOLUTION = Join-Path $CLAUDE_ROOT 'debug\evolution-log.jsonl'
$CODE = 'v6.6-rc2-20260422-1859'
function Write-Step { param([string]$Msg) Write-Host "[rollback] $Msg" -ForegroundColor Cyan }
function Remove-IfExists {
param([string]$Path, [string]$Tag)
if (Test-Path -LiteralPath $Path) {
if ($script:DryRun) {
Write-Step "$Tag [DRY-RUN] 将移除: $Path"
} else {
Remove-Item -LiteralPath $Path -Force
Write-Step "$Tag 已移除: $Path"
}
} else {
Write-Step "$Tag 不存在 (跳过): $Path"
}
}
function Restore-LatestBak {
param([string]$Target, [string]$BakPrefix, [string]$Tag)
$dir = Split-Path $Target -Parent
$name = Split-Path $Target -Leaf
$pattern = "$name.$BakPrefix*"
$bak = Get-ChildItem -LiteralPath $dir -Filter $pattern -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($bak) {
if ($script:DryRun) {
Write-Step "$Tag [DRY-RUN] 将从 $($bak.Name) 恢复"
} else {
Copy-Item -LiteralPath $bak.FullName -Destination $Target -Force
Write-Step "$Tag 已从 $($bak.Name) 恢复"
}
} else {
Write-Step "$Tag 未找到 $pattern 备份 (可能未曾应用该阶段)"
}
}
# Scope → 各阶段是否执行 (PowerShell 无 goto, 用标志位分级控制流)
$runP3 = $true
$runP2 = ($Scope -ne 'P3-only')
$runP1 = ($Scope -notin @('P3-only','P2-down'))
$runP0 = ($Scope -eq 'all')
$modeTag = if ($DryRun) { ' [DRY-RUN 预演模式]' } else { '' }
Write-Host "============================================================" -ForegroundColor Yellow
Write-Host " Bookworm v6.6-rc2 回滚$modeTag · Scope=$Scope" -ForegroundColor Yellow
Write-Host " 阶段执行: P3=$runP3 P2=$runP2 P1=$runP1 P0=$runP0" -ForegroundColor Yellow
Write-Host "============================================================" -ForegroundColor Yellow
# ------------------------------------------------------------------
# P3 层 (T12)
# ------------------------------------------------------------------
if ($runP3) {
Write-Step '--- Phase P3 ---'
# bash: rm -f $CLAUDE_ROOT/hooks/agent-claim-cross-verify.js
Remove-IfExists (Join-Path $HOOKS 'agent-claim-cross-verify.js') '[T12]'
Remove-IfExists (Join-Path $CONFIG 'cross-verify-budget.json') '[T12]'
}
# ------------------------------------------------------------------
# P2 层 (T09, T10, T11)
# ------------------------------------------------------------------
if ($runP2) {
Write-Step '--- Phase P2 ---'
Remove-IfExists (Join-Path $HOOKS 'agent-honesty-stats.js') '[T09]'
Remove-IfExists (Join-Path $CLAUDE_ROOT 'dashboard\agent-honesty.html') '[T11]'
# T10 对 daily-health-snapshot.js 是扩展, 走 sentinel 撤销 (待 T10 补丁填充 undo 段)
Write-Step '[T10] TODO: 调用 scripts/patches/v6.6-rc2-04-health-snapshot-ext.js --undo (T10 落地后填充)'
Remove-IfExists (Join-Path $CONFIG 'health-weights.json') '[T09/T10 参数]'
}
# ------------------------------------------------------------------
# P1 层 (T05, T06, T07, T08)
# ------------------------------------------------------------------
if ($runP1) {
Write-Step '--- Phase P1 ---'
Remove-IfExists (Join-Path $HOOKS 'agent-claim-verifier.js') '[T07]'
Remove-IfExists (Join-Path $HOOKS 'agent-snapshot-taker.js') '[T06]'
# T05 对 subagent-route-injector.js 追加契约模板, 回滚到 P0 版本
Restore-LatestBak (Join-Path $HOOKS 'subagent-route-injector.js') 'bak.v6.6-rc2-03-claim-contract' '[T05]'
}
# ------------------------------------------------------------------
# P0 层 (T02, T03, T04)
# ------------------------------------------------------------------
if ($runP0) {
Write-Step '--- Phase P0 ---'
# T02: 卸载 agent-claim-observer.js + 撤销 SubagentStop 挂载
Remove-IfExists (Join-Path $HOOKS 'agent-claim-observer.js') '[T02 hook]'
Restore-LatestBak $SETTINGS 'bak.v6.6-rc2-01-register-subagent-stop' '[T02 settings]'
# T03: subagent-route-injector 恢复 v5.2 版本 (P0 traceId 注入)
Restore-LatestBak (Join-Path $HOOKS 'subagent-route-injector.js') 'bak.v6.6-rc2-02-inject-traceid' '[T03]'
# T04: 宪法追加段删除 + log-rotator 配置回退
Write-Step '[T04] 宪法 sentinel 段删除:'
if (Test-Path -LiteralPath $CONSTITUTION) {
$text = Get-Content -LiteralPath $CONSTITUTION -Raw -Encoding UTF8
$pattern = '(?s)<!-- closure-loop:agent-trust:v1 -->.*?<!-- /closure-loop:agent-trust:v1 -->'
if ($text -match $pattern) {
if ($DryRun) {
Write-Step ' [DRY-RUN] 将移除 sentinel 段 (原子写)'
} else {
$cleaned = [regex]::Replace($text, $pattern, '').TrimEnd() + "`n"
# 原子写
$tmp = "$CONSTITUTION.tmp.rollback.$PID"
[IO.File]::WriteAllText($tmp, $cleaned, [Text.UTF8Encoding]::new($false))
Move-Item -LiteralPath $tmp -Destination $CONSTITUTION -Force
Write-Step ' sentinel 段已移除 (原子写)'
}
} else {
Write-Step ' sentinel 段不存在 (跳过)'
}
}
Restore-LatestBak (Join-Path $HOOKS 'log-rotator.js') 'bak.v6.6-rc2-04-log-rotator' '[T04]'
# 数据文件 保留审计 (原 prompt 第 8 条: 保留 logs/ 和 state/ 数据)
Write-Step 'logs/agent-returns.jsonl 和 state/claim-mismatches.jsonl 按审计要求保留'
}
# ------------------------------------------------------------------
# 最后: HMAC 完整性链重签 (所有 scope 都执行)
# ------------------------------------------------------------------
Write-Step '--- Reseal HMAC ---'
if ($DryRun) {
Write-Step '[DRY-RUN] 将调用: node hooks/integrity-check.js --generate'
Write-Step '[DRY-RUN] 将追加 evolution-log.jsonl rollback 事件'
} else {
# bash: node $CLAUDE_ROOT/hooks/integrity-check.js --generate
& node (Join-Path $HOOKS 'integrity-check.js') --generate
if ($LASTEXITCODE -ne 0) {
throw "integrity-check --generate 失败, exit=$LASTEXITCODE"
}
# 追加 evolution-log
try {
$entry = @{
ts = (Get-Date).ToString('o')
event = 'rollback'
code = $CODE
scope = $Scope
operator = "$env:USERNAME@$env:COMPUTERNAME"
} | ConvertTo-Json -Compress
Add-Content -LiteralPath $EVOLUTION -Value $entry -Encoding UTF8
Write-Step 'evolution-log.jsonl 已追加 rollback 事件'
} catch {
Write-Step "evolution-log 追加失败 (非阻塞): $_"
}
}
$finishTag = if ($DryRun) { '预演完成 (未实际变更)' } else { '回滚完成' }
Write-Host ''
Write-Host '============================================================' -ForegroundColor Green
Write-Host " $finishTag · Scope=$Scope" -ForegroundColor Green
if (-not $DryRun) {
Write-Host " 下一步验证: node $HOOKS\integrity-check.js (PostToolUse 模拟)" -ForegroundColor Green
}
Write-Host '============================================================' -ForegroundColor Green