From 9673503e6a17b09c9d84de189fd3f9b02f873014 Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 07:16:05 +0800 Subject: [PATCH 01/13] feat(recovery): inspect full backups in isolated inert workspaces Signed-off-by: song --- .../dashboard/src/data/state-recovery.ts | 39 ++ .../configuration-backup-settings.tsx | 11 +- .../state-recovery-settings.css | 33 ++ .../state-recovery-settings.tsx | 122 ++++++ .../configuration-backup.mjs | 76 +++- loopx/cli_commands/support_control_backup.py | 26 ++ loopx/control_plane/effect_program.ts | 4 +- loopx/control_plane/effect_runtime.py | 2 + .../control_plane/effect_runtime_handlers.ts | 3 + loopx/control_plane/effect_runtime_server.ts | 2 + loopx/control_plane/runtime_decode.ts | 3 +- loopx/control_plane/state_recovery.ts | 286 ++++++++++++ .../control_plane/state_recovery_contract.ts | 81 ++++ loopx/control_plane/state_recovery_worker.ts | 20 + loopx/presentation/configuration_api.py | 8 + loopx/presentation/state_recovery_api.py | 152 +++++++ .../project_registry_io_manifest_v1.json | 2 +- loopx/semantics/vocabulary_v0.json | 31 +- loopx/state_recovery.py | 414 ++++++++++++++++++ tests/control_plane_ts/state_recovery.test.ts | 75 ++++ tests/test_state_recovery.py | 366 ++++++++++++++++ 21 files changed, 1742 insertions(+), 14 deletions(-) create mode 100644 apps/presentation/dashboard/src/data/state-recovery.ts create mode 100644 apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.css create mode 100644 apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx create mode 100644 loopx/control_plane/state_recovery.ts create mode 100644 loopx/control_plane/state_recovery_contract.ts create mode 100644 loopx/control_plane/state_recovery_worker.ts create mode 100644 loopx/presentation/state_recovery_api.py create mode 100644 loopx/state_recovery.py create mode 100644 tests/control_plane_ts/state_recovery.test.ts create mode 100644 tests/test_state_recovery.py diff --git a/apps/presentation/dashboard/src/data/state-recovery.ts b/apps/presentation/dashboard/src/data/state-recovery.ts new file mode 100644 index 0000000000..f19f05d44b --- /dev/null +++ b/apps/presentation/dashboard/src/data/state-recovery.ts @@ -0,0 +1,39 @@ +import {requestJson} from "./chat"; +import {decodeStateRecoveryAudit, type StateRecoveryAudit} from "../../../../../loopx/control_plane/state_recovery_contract"; + +export type StateRecoveryResult = StateRecoveryAudit & {checkpoint_ref: string; historical_readback?: boolean}; +export type RecoveryHistoryItem = { + checkpoint_ref: string; available: boolean; observed_at?: string; + summary?: StateRecoveryAudit["summary"]; +}; +const endpoint = "/api/chat/state-recovery"; + +export async function stateRecoveryProfile() { + const result = await requestJson<{supported: boolean; activation_available: false}>(`${endpoint}/profile`); + if (typeof result.supported !== "boolean" || result.activation_available !== false) throw new Error("Recovery profile is invalid"); + return result; +} + +export async function uploadRecoveryInput(file: File, kind: "archive" | "manifest") { + return requestJson<{input_ref: string; checkpoint_ref?: string}>(`${endpoint}/${kind}`, { + method: "POST", headers: {"Content-Type": "application/octet-stream"}, body: file, + }); +} +function result(value: unknown): StateRecoveryResult { + const audit = decodeStateRecoveryAudit(value); + if (typeof audit.checkpoint_ref !== "string" || !/^[a-f0-9]{32}$/.test(audit.checkpoint_ref)) { + throw new Error("Recovery checkpoint reference is invalid"); + } + return audit as StateRecoveryResult; +} +export async function inspectStateBackup(archiveRef: string, manifestRef: string) { + return result(await requestJson(`${endpoint}/verify`, {method: "POST", + body: JSON.stringify({archive_ref: archiveRef, manifest_ref: manifestRef, execute: true})})); +} +export async function readStateRecovery(checkpointRef: string) { + return result(await requestJson(`${endpoint}/read`, {method: "POST", + body: JSON.stringify({checkpoint_ref: checkpointRef})})); +} +export function listStateRecoveries() { + return requestJson<{items: RecoveryHistoryItem[]; has_more: boolean}>(endpoint); +} diff --git a/apps/presentation/dashboard/src/features/personal-workspace/configuration-backup-settings.tsx b/apps/presentation/dashboard/src/features/personal-workspace/configuration-backup-settings.tsx index 071078dce2..d5847f62ff 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/configuration-backup-settings.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/configuration-backup-settings.tsx @@ -1,6 +1,7 @@ import {useEffect, useState} from "react"; import {exportConfigurationBackup, restoreConfigurationCheckpoint, type ConfigurationBackupResult} from "../../data/chat"; import {useWorkspaceI18n} from "./i18n"; +import {StateRecoverySettings} from "./state-recovery-settings"; export function ConfigurationBackupSettings({goalId}: {goalId: string | null}) { const {locale} = useWorkspaceI18n(); @@ -9,7 +10,8 @@ export function ConfigurationBackupSettings({goalId}: {goalId: string | null}) { const [result, setResult] = useState(null); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); - useEffect(() => {setBackup(null); setResult(null); setError("");}, [goalId]); + const [kind, setKind] = useState<"configuration" | "state">("configuration"); + useEffect(() => {setBackup(null); setResult(null); setError(""); setKind("configuration");}, [goalId]); async function perform(action: () => Promise) { setBusy(true); setError(""); try {await action();} catch (failure) {setError(String(failure));} @@ -17,6 +19,12 @@ export function ConfigurationBackupSettings({goalId}: {goalId: string | null}) { } return
{zh ? "配置备份与恢复" : "Configuration backup and recovery"} + {goalId === null ?
+ {zh ? "备份类型" : "Backup type"} + + +
: null} + {kind === "configuration" || goalId !== null ? <>

{zh ? "备份包含私有路径、Goal 设置及设备默认值,需自行保管和审查。凭据文件不包含在此组件中。恢复只建立隔离检查点;启用仍在原配置页面办理。" : "Keep and review this private backup: it includes Goal settings, paths and device defaults. Credential files are outside this component. Recovery creates an isolated checkpoint; activate settings through their existing editors."}

; } diff --git a/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.css b/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.css new file mode 100644 index 0000000000..e45cca2479 --- /dev/null +++ b/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.css @@ -0,0 +1,33 @@ +.personal-recovery-kind { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin: 0.75rem 0; + padding: 0; + border: 0; +} +.personal-recovery-kind legend { margin-bottom: 0.5rem; } +.personal-recovery-kind label { display: inline-flex; align-items: center; gap: 0.375rem; min-height: 2.75rem; } +.personal-recovery { display: grid; gap: 0.75rem; min-width: 0; } +.personal-detail-card .personal-recovery p, +.personal-detail-card .personal-recovery label, +.personal-detail-card .personal-recovery button { font-size: var(--text-body); line-height: var(--leading-body); } +.personal-detail-card .personal-recovery p { margin: 0.375rem 0; color: var(--pw-text); } +.personal-recovery-files { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; } +.personal-recovery-files label { display: grid; gap: 0.5rem; min-width: 0; } +.personal-recovery-files input { width: 100%; max-width: 100%; min-width: 0; font: inherit; } +.personal-recovery-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; } +.personal-recovery-actions > button { width: auto; min-height: 2.75rem; padding: 0.5rem 0.75rem; } +.personal-recovery-result { display: grid; gap: 0.5rem; } +.personal-detail-card .personal-recovery-counts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem; margin: 0.5rem 0; max-width: 28rem; } +.personal-detail-card .personal-recovery-counts > div { display: block; } +.personal-detail-card .personal-recovery-counts dt { color: var(--pw-muted); font-size: var(--text-caption); } +.personal-detail-card .personal-recovery-counts dd { margin: 0.25rem 0 0; font-size: var(--text-heading); font-weight: 600; } +.personal-recovery-components { list-style: none; margin: 0; padding: 0; } +.personal-recovery-components > li { padding: 0.75rem 0; border-top: 1px solid var(--pw-line); } +.personal-recovery code { overflow-wrap: anywhere; font-size: var(--text-caption); } +@media (max-width: 640px) { + .personal-recovery-files { grid-template-columns: minmax(0, 1fr); } + .personal-recovery-actions { display: grid; } + .personal-recovery-actions > button { width: 100%; } +} diff --git a/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx b/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx new file mode 100644 index 0000000000..677ece6b37 --- /dev/null +++ b/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx @@ -0,0 +1,122 @@ +import {useEffect, useRef, useState} from "react"; +import {inspectStateBackup, listStateRecoveries, readStateRecovery, stateRecoveryProfile, uploadRecoveryInput, + type RecoveryHistoryItem, type StateRecoveryResult} from "../../data/state-recovery"; +import {useWorkspaceI18n} from "./i18n"; +import {ChatApiError} from "../../data/chat"; +import "./state-recovery-settings.css"; + +const ownerNames: Record = { + configuration: ["配置", "Configuration"], coordination_authority: ["Goal 工作与历史", "Goal work and history"], + sqlite_storage: ["数据库", "Database"], recovery: ["其他保存的状态", "Other saved state"], +}; +const nextActions: Record = { + retain_isolated_configuration: ["保留隔离配置,通过原设置页另行启用。", "Keep the isolated configuration; use its existing editor for any later activation."], + review_goal_identity_before_adoption: ["继续执行前,先由 Goal owner 核对身份与未完成工作。", "Before resuming, have the Goal owner review identity and unfinished work."], + inspect_captured_authority_with_owner: ["请存储 owner 检查保存的工作记录。", "Have the storage owner inspect the saved work records."], + use_provider_recovery_owner: ["通过该存储服务的恢复流程检查。", "Use this storage service's recovery procedure."], + retain_historical_database: ["保留数据库副本,暂不用于执行。", "Keep the database copy as historical data."], + inspect_database_with_owner: ["请数据库 owner 检查完整性和格式。", "Have the database owner check integrity and format."], + inspect_configuration_with_owner: ["请配置 owner 检查备份版本和内容。", "Have the configuration owner check the saved format and contents."], + classify_remaining_owner_state: ["启用前,需分别确认其他状态的归属、会话、租约和未决操作。", "Identify the remaining state owners, sessions, leases and pending operations before any activation."], +}; +const errors: Record = { + archive_digest_mismatch: ["归档与清单不匹配,请选择同一次备份的两个文件。", "The archive and manifest do not match. Choose the pair from the same backup."], + manifest_disagreement: ["内外清单不一致,请重新选择原始备份。", "The embedded and external manifests disagree. Choose the original backup pair."], + recovery_destination_exists: ["这份检查已有隔离副本,请读取历史结果。", "An isolated copy already exists for this inspection. Read its saved result."], + recovery_capacity_exceeded: ["备份超过当前检查容量,请使用较小的备份;未建立新副本。", "This backup exceeds the inspection limits. Use a smaller backup; no new copy was published."], + recovery_input_unavailable: ["上传文件已不可用,请重新选择归档与清单。", "The uploaded files are unavailable. Select the archive and manifest again."], + recovery_owner_timeout: ["离线检查超时,未发布副本;可使用较小的备份重试。", "Offline verification timed out without publishing a copy. Retry with a smaller backup."], + member_witness_mismatch: ["保存的文件与清单记录不符,已停止检查。", "Saved files differ from their manifest witnesses. Inspection stopped."], +}; + +export function StateRecoverySettings() { + const {locale} = useWorkspaceI18n(), zh = locale === "zh-CN", index = zh ? 0 : 1; + const [archive, setArchive] = useState(null); + const [manifest, setManifest] = useState(null); + const [audit, setAudit] = useState(null); + const [history, setHistory] = useState(null); + const [more, setMore] = useState(false); + const [busy, setBusy] = useState(false); + const [error, setError] = useState(""); + const [pending, setPending] = useState(null); + const [supported, setSupported] = useState(null); + const generation = useRef(0); + useEffect(() => { + let active = true; + void stateRecoveryProfile().then(profile => {if (active) setSupported(profile.supported);}) + .catch(() => {if (active) setSupported(false);}); + return () => {active = false;}; + }, []); + async function perform(operation: () => Promise) { + setBusy(true); setError(""); + try { await operation(); } catch (failure) { + const code = failure instanceof ChatApiError ? String(failure.payload.error_code ?? "") : ""; + setError(errors[code]?.[index] ?? (zh ? "检查未完成,请核对文件或稍后重试。" : "Inspection did not finish. Check the files or retry later.")); + } + finally { setBusy(false); } + } + function choose(file: File | null, kind: "archive" | "manifest") { + generation.current++; setAudit(null); setError(""); setPending(null); + if (kind === "archive") setArchive(file); else setManifest(file); + } + return
+ {supported === false ?

{zh ? "当前服务或平台不支持完整归档检查。配置快照仍可使用。" : "This service or platform does not support full archive inspection. Configuration snapshots remain available."}

: null} +

{zh ? "选择完整归档和配套清单,建立私有隔离副本并查看可读内容。此处不会恢复 Goal 运行;旧备份缺少一致性信息时会明确保留未知。" + : "Choose an archive and its matching manifest to inspect a private, isolated copy. This does not resume a Goal; missing consistency information stays unknown."}

+
+
+
+ + +
+ {error ?

{error}

: null} + {error && pending ? : null} + {audit ?
+ {audit.historical_readback + ? (zh ? "已读取历史检查记录;尚未重新验证副本。" : "Historical inspection loaded; the copy has not been reverified.") + : (zh ? "隔离副本已核对;Goal 尚未启用。" : "Isolated copy inspected; Goal execution remains inactive.")} +

{zh ? "检查时刻" : "Observed at"}:

+
+
{zh ? "可读组件" : "Readable components"}
{audit.summary.readable}
+
{zh ? "仅历史组件" : "Historical components"}
{audit.summary.historical_only}
+
{zh ? "未知组件" : "Unknown components"}
{audit.summary.unknown}
+
+

{zh ? "旧格式没有完整的捕获版本与一致性声明,以上结果只证明记录时的读取,不证明可以安全继续执行。" + : "This legacy archive has no complete capture-revision or consistency declaration. These observations do not authorize resuming work."}

+
    {audit.components.map(row =>
  • + {ownerNames[row.owner_kind]?.[index] ?? (zh ? "待核对组件" : "Component requiring review")} + {typeof row.facts.goal_id === "string" ? · {row.facts.goal_id} : null} + {" · "}{row.state === "readable" ? (zh ? "可读" : "Readable") : row.state === "historical_only" ? (zh ? "仅历史" : "Historical only") : (zh ? "未知" : "Unknown")} + {typeof row.facts.todo_count === "number" ?

    {zh ? "保存的任务" : "Recorded tasks"}: {row.facts.todo_count} · {zh ? "历史提交" : "Historical commits"}: {String(row.facts.history_commits)}

    : null} +

    {nextActions[row.next_action]?.[index] ?? (zh ? "请对应 owner 核对。" : "Ask the owning module to review this component.")}

    +
  • )}
+
{zh ? "核对信息" : "Verification details"}{audit.recovery_id} +
    {audit.components.map(row =>
  • {row.component_id}: {row.reason_code}
  • )}
+
+
: null} + {history ?
+ {history.length === 0 ?

{zh ? "尚无检查结果。" : "No saved inspection yet."}

: null} + {history.map(row =>

)} + {more ?

{zh ? "显示最近 10 份检查。" : "Showing the 10 latest inspections."}

: null} +
: null} +
; +} diff --git a/examples/personal-workspace-browser/configuration-backup.mjs b/examples/personal-workspace-browser/configuration-backup.mjs index 0c9bd8d366..f7a70ffa84 100644 --- a/examples/personal-workspace-browser/configuration-backup.mjs +++ b/examples/personal-workspace-browser/configuration-backup.mjs @@ -10,14 +10,27 @@ import {resolveTestPython} from "../../scripts/test-python.mjs"; // Only the backup routes use a real disposable server. Other workspace state // uses the shared browser fixture; no Host, model or live runtime is contacted. const serverCode = ` -import json,pathlib,sys,loopx +import json,pathlib,sys,loopx,subprocess from loopx.chat_server import ChatHTTPServer,ChatRequestHandler +from loopx.state_backup import build_state_backup_plan,execute_state_backup_plan +from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection r=pathlib.Path(sys.argv[1]); runtime=r/'runtime'; registry=r/'registry.json' registry.write_text(json.dumps({'common_runtime_root':str(runtime),'goals':[{'id':'fixture','repo':str(r),'control_plane':{'optional':{'context':'complete '*10000}}}]})) p=runtime/'machine/configuration.json';p.parent.mkdir(parents=True) p.write_text(json.dumps({'schema_version':'loopx_machine_configuration_v0','namespaces':{'goal_storage':{'schema_version':'loopx_goal_storage_defaults_v0','new_goal_provider':'sqlite'}}})) +source_project=r/'source-project';source_project.mkdir() +source_runtime=r/'source-runtime';source_registry=r/'source-registry.json' +(source_project/'ACTIVE.md').write_text('# Original Goal\\n\\nPreserve every unfinished commitment.\\n') +source_registry.write_text(json.dumps({'common_runtime_root':str(source_runtime),'goals':[{'id':'recovery-fixture','repo':str(source_project),'state_file':'ACTIVE.md','coordination':{'registered_agents':['worker']}}]})) +projection=build_todo_runtime_shadow_projection(goal_id='recovery-fixture',todos=[{'schema_version':'todo_item_v0','todo_id':'todo_recovery_work','role':'agent','status':'open','done':False,'archive_state':'active','source_section':'Agent Todo','text':'Preserve the original commitment','task_class':'advancement_task'}],handoff_mode='soft_claim') +provider=(pathlib.Path(loopx.__file__).parent/'control_plane/coordination/local_authority_provider.ts').as_uri() +script='import {selectLocalSqliteAuthority,openLocalAuthorityStore} from '+json.dumps(provider)+';let input="";for await(const c of process.stdin)input+=c;const r=JSON.parse(input);await selectLocalSqliteAuthority(r.root,r.goal,true);const s=await openLocalAuthorityStore(r.root,r.goal);const x=await s.commitAuthority({expected_provider_revision:null,operation_id:"original-work",events:[],receipts:[],next_projection:r.projection});if(x.status!=="applied")throw new Error(JSON.stringify(x));' +subprocess.run(['node','--no-warnings','--experimental-sqlite','--experimental-strip-types','--input-type=module','-e',script],input=json.dumps({'root':str(source_runtime),'goal':'recovery-fixture','projection':projection}),text=True,check=True,capture_output=True) +saved=execute_state_backup_plan(build_state_backup_plan(project=source_project,runtime_root=source_runtime,registry_path=source_registry,output_dir=r/'source-backup',backup_id='recovery-fixture',include_registry_projects=False,include_automations=False,include_skills=False)) +source_project.rename(r/'unavailable-project');source_runtime.rename(r/'unavailable-runtime');source_registry.rename(r/'unavailable-registry.json') +assert not source_project.exists() and not source_runtime.exists() and not source_registry.exists() s=ChatHTTPServer(('127.0.0.1',0),ChatRequestHandler);s.registry_path=registry;s.runtime_root=runtime;s.verbose=False -print(json.dumps({'port':s.server_address[1],'python':sys.executable,'module_path':loopx.__file__}),flush=True);s.serve_forever() +print(json.dumps({'port':s.server_address[1],'python':sys.executable,'module_path':loopx.__file__,'archive':saved['archive_path'],'manifest':saved['manifest_path'],'original_sources_absent':True}),flush=True);s.serve_forever() `; export const configurationBackupScenario = { @@ -70,6 +83,11 @@ export const configurationBackupScenario = { const response = await route.fetch({url: `http://127.0.0.1:${port}${path}`}); await route.fulfill({response}); }); + await page.route("**/api/chat/state-recovery**", async route => { + const path = new URL(route.request().url()).pathname; + const response = await route.fetch({url: `http://127.0.0.1:${port}${path}`}); + await route.fulfill({response}); + }); }}); const {page} = context; await page.getByRole("button", {name: "设置", exact: true}).click(); @@ -90,9 +108,61 @@ export const configurationBackupScenario = { await input.setInputFiles({name: "bad.json", mimeType: "application/json", buffer: Buffer.from(JSON.stringify({...backup, sha256: "changed"}))}); await panel.getByRole("alert").waitFor(); assert.equal(await panel.getByRole("button", {name: "恢复为隔离检查点"}).count(), 0); + await panel.getByRole("radio", {name: "完整归档检查", exact: true}).check(); + const recovery = panel.getByRole("region", {name: "完整备份检查"}); + await recovery.scrollIntoViewIfNeeded(); + await page.screenshot({path: resolve(outputDir, "state-recovery-ready.png"), animations: "disabled"}); + await recovery.getByLabel("完整归档", {exact: true}).setInputFiles(backend.archive); + await recovery.getByLabel("配套清单", {exact: true}).setInputFiles(backend.manifest); + await recovery.getByLabel("配套清单", {exact: true}).focus(); + await page.keyboard.press("Tab"); + assert.equal(await page.evaluate(() => document.activeElement.textContent), "验证并建立隔离副本"); + await page.keyboard.press("Enter"); + await recovery.getByText("隔离副本已核对;Goal 尚未启用。", {exact: true}).waitFor(); + await recovery.getByText("recovery-fixture", {exact: false}).first().waitFor(); + await recovery.getByText("保存的任务: 1", {exact: false}).waitFor(); + assert.equal(backend.original_sources_absent, true); + const registryAfter = JSON.parse(await readFile(join(root, "registry.json"), "utf8")); + assert.deepEqual(registryAfter.goals.map(goal => goal.id), ["fixture"], "inspection must not register the restored Goal"); + await recovery.getByRole("status").scrollIntoViewIfNeeded(); + await page.screenshot({path: resolve(outputDir, "state-recovery-audit.png"), animations: "disabled"}); + await recovery.getByLabel("完整归档", {exact: true}).setInputFiles({name: "damaged.tar.gz", mimeType: "application/gzip", buffer: Buffer.from("damaged")}); + await recovery.getByRole("button", {name: "验证并建立隔离副本", exact: true}).click(); + await recovery.getByRole("alert").waitFor(); + assert.equal(await recovery.getByText("保存的任务: 1", {exact: false}).count(), 0, "a changed input must clear the old success view"); + await recovery.getByRole("button", {name: "历史检查结果", exact: true}).click(); + const history = recovery.getByLabel("历史备份检查", {exact: true}); + await history.getByRole("button").click(); + await recovery.getByText("已读取历史检查记录;尚未重新验证副本。", {exact: true}).waitFor(); await page.setViewportSize({width: 390, height: 844}); assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > window.innerWidth), false); - return {coverageEntries: context.coverageEntries, errors: context.errors, backend, verified: "Packaged download, full real-backend checkpoint recovery, damaged-file rejection and narrow viewport"}; + await recovery.getByRole("status").scrollIntoViewIfNeeded(); + await page.screenshot({path: resolve(outputDir, "state-recovery-mobile.png"), animations: "disabled"}); + await recovery.getByRole("button", {name: "历史检查结果", exact: true}).click(); + await recovery.getByLabel("历史备份检查", {exact: true}).getByRole("button").click(); + await page.setViewportSize({width: 1512, height: 982}); + await page.reload(); + await page.getByRole("button", {name: "设置", exact: true}).or(page.locator(".personal-settings-tabs")).first().waitFor(); + if (await page.getByRole("button", {name: "设置", exact: true}).count()) await page.getByRole("button", {name: "设置", exact: true}).click(); + await page.locator(".personal-settings-tabs").getByRole("button", {name: "能力中心", exact: true}).click(); + const reopened = page.locator("details").filter({has: page.getByText("配置备份与恢复", {exact: true})}); + await reopened.locator("summary").click(); + await reopened.getByRole("radio", {name: "完整归档检查", exact: true}).check(); + await reopened.getByRole("button", {name: "历史检查结果", exact: true}).click(); + await reopened.getByLabel("历史备份检查", {exact: true}).getByRole("button").click(); + await reopened.getByText("已读取历史检查记录;尚未重新验证副本。", {exact: true}).waitFor(); + await page.getByRole("button", {name: "语言", exact: true}).click(); + await page.getByRole("radio", {name: "English", exact: true}).check(); + await page.locator(".personal-settings-tabs").getByRole("button", {name: /Capabilit/}).click(); + const english = page.locator("details").filter({has: page.getByText("Configuration backup and recovery", {exact: true})}); + await english.locator("summary").click(); + await english.getByRole("radio", {name: "Full archive inspection", exact: true}).check(); + await english.getByRole("button", {name: "Previous inspections", exact: true}).click(); + await english.getByLabel("Previous backup inspections", {exact: true}).getByRole("button").click(); + await english.getByText("Historical inspection loaded; the copy has not been reverified.", {exact: true}).waitFor(); + await english.getByRole("status").scrollIntoViewIfNeeded(); + await page.screenshot({path: resolve(outputDir, "state-recovery-english.png"), animations: "disabled"}); + return {coverageEntries: context.coverageEntries, errors: context.errors, backend, verified: "Packaged configuration recovery and full archive inspection; real SQLite head/history after original-source loss, no Goal registration, keyboard, corrupt-input recovery, immutable history reload and narrow viewport"}; } finally { try {await context?.close();} finally { if (!closed) { diff --git a/loopx/cli_commands/support_control_backup.py b/loopx/cli_commands/support_control_backup.py index f584e18456..f3bcbed42f 100644 --- a/loopx/cli_commands/support_control_backup.py +++ b/loopx/cli_commands/support_control_backup.py @@ -30,6 +30,11 @@ def register_backup_state_command( help="Preview or create a private local archive of LoopX state.", ) add_subcommand_format(parser) + parser.add_argument("backup_action", nargs="?", choices=["verify"], + help="Verify a full archive and inspect an inert copy; never reactivate it.") + parser.add_argument("--archive", help="For verify, the private full-state tar.gz archive.") + parser.add_argument("--manifest", help="For verify, the matching private external manifest.") + parser.add_argument("--destination", help="For verify, a new inert directory below a physical parent.") parser.add_argument( "--project", default=".", @@ -76,6 +81,27 @@ def handle_backup_state_command( print_payload: PrintPayload, output_format: FormatSelector, ) -> int: + if getattr(args, "backup_action", None) == "verify": + from ..state_recovery import StateRecoveryError, render_state_recovery_markdown, verify_state_backup + + try: + if not all(getattr(args, key, None) for key in ("archive", "manifest", "destination")): + raise StateRecoveryError("recovery_inputs_required") + if args.output_dir or args.backup_id or args.no_automations or args.no_skills or args.current_project_only or args.project != ".": + raise StateRecoveryError("recovery_creation_options_invalid") + payload = verify_state_backup(archive=Path(args.archive).expanduser(), + manifest=Path(args.manifest).expanduser(), destination=Path(args.destination).expanduser().absolute(), + execute=bool(args.execute)) + except StateRecoveryError as exc: + payload = {"ok": False, "error_code": exc.code, "execution_authority_granted": False} + except OSError: + payload = {"ok": False, "error_code": "recovery_io_unavailable", "execution_authority_granted": False} + print_payload(payload, output_format(args), render_state_recovery_markdown) + return 0 if payload.get("ok") else 1 + if any(getattr(args, key, None) for key in ("archive", "manifest", "destination")): + payload = {"ok": False, "error": "archive, manifest and destination require backup-state verify"} + print_payload(payload, output_format(args), render_state_backup_markdown) + return 1 try: runtime_root = ( Path(args.runtime_root).expanduser() if args.runtime_root else None diff --git a/loopx/control_plane/effect_program.ts b/loopx/control_plane/effect_program.ts index 1c5cfe4402..13864e0dcc 100644 --- a/loopx/control_plane/effect_program.ts +++ b/loopx/control_plane/effect_program.ts @@ -1,5 +1,5 @@ import { EffectRuntimeRequestError } from "./effect_runtime_errors.ts"; -import { isStringLiteral } from "./runtime_decode.ts"; +import { isStringLiteral, type JsonObject } from "./runtime_decode.ts"; import { decodeInteractionContract } from "./work_items/interaction_contract.ts"; export { @@ -25,7 +25,7 @@ export const SETTLEMENT_PLAN_SCHEMA_VERSION = "quota_settlement_plan_v1"; export const SETTLEMENT_RECEIPT_SCHEMA_VERSION = "quota_settlement_receipt_v1"; -export type JsonObject = Record; +export type {JsonObject} from "./runtime_decode.ts"; export interface EffectRequest { kind: string; diff --git a/loopx/control_plane/effect_runtime.py b/loopx/control_plane/effect_runtime.py index 52a4068f12..e7f01fcfd5 100644 --- a/loopx/control_plane/effect_runtime.py +++ b/loopx/control_plane/effect_runtime.py @@ -48,6 +48,8 @@ "goal.checkpoint_read_context.commit", "goal.checkpoint_read_context.inspect_replay", "performance_diagnosis.inspect", + "state.recovery.archive.verify", + "state.recovery.manifest.verify", }) MAX_STARTUP_DIAGNOSTIC_BYTES = 8 * 1024 STARTUP_LOCK_TIMEOUT_SECONDS = 15.0 diff --git a/loopx/control_plane/effect_runtime_handlers.ts b/loopx/control_plane/effect_runtime_handlers.ts index 9558769848..ac9b4a6552 100644 --- a/loopx/control_plane/effect_runtime_handlers.ts +++ b/loopx/control_plane/effect_runtime_handlers.ts @@ -407,6 +407,9 @@ export function createEffectRuntimeHandlers( ["coordination.local_authority.promote", lazyHandler(() => import("./coordination/local_authority_runtime.ts"), ({promoteLocalCoordinationAuthority}) => promoteLocalCoordinationAuthority)], ["coordination.authority_archive.manage", lazyHandler(() => import("./coordination/local_authority_archive.ts"), ({manageLocalAuthorityArchive}) => manageLocalAuthorityArchive)], ["configuration.backup", lazyHandler(() => import("./configuration_backup.ts"), ({configurationBackupOperation}) => configurationBackupOperation)], + ["state.recovery.archive.verify", lazyHandler(() => import("./state_recovery.ts"), ({verifyStateRecoveryArchive}) => verifyStateRecoveryArchive)], + ["state.recovery.manifest.verify", lazyHandler(() => import("./state_recovery.ts"), ({verifyStateRecoveryManifest}) => verifyStateRecoveryManifest)], + ["state.recovery.audit.readback", lazyHandler(() => import("./state_recovery.ts"), ({verifySavedStateRecoveryAudit}) => verifySavedStateRecoveryAudit)], ["coordination.sqlite_backup.snapshot", lazyHandler(() => import("./coordination/sqlite_backup.ts"), ({snapshotSqliteBackup}) => snapshotSqliteBackup)], ["coordination.local_authority.new_goal_storage", lazyHandler(() => import("./coordination/local_authority_defaults.ts"), ({manageNewGoalStorage}) => manageNewGoalStorage)], ["coordination.local_authority.promotion_review", lazyHandler(() => Promise.all([import("./coordination/local_authority_runtime.ts"), import("./coordination/source_transfer.ts")]), ([{reviewLocalCoordinationAuthorityPromotion}, {withCoordinationSourceTransfer}]) => withCoordinationSourceTransfer("coordination.local_authority.promotion_review", reviewLocalCoordinationAuthorityPromotion))], diff --git a/loopx/control_plane/effect_runtime_server.ts b/loopx/control_plane/effect_runtime_server.ts index 39139eb382..8110bc3917 100644 --- a/loopx/control_plane/effect_runtime_server.ts +++ b/loopx/control_plane/effect_runtime_server.ts @@ -35,6 +35,8 @@ const LOCAL_SNAPSHOT_METHODS = new Set([ "goal.checkpoint_read_context.commit", "goal.checkpoint_read_context.inspect_replay", "performance_diagnosis.inspect", + "state.recovery.archive.verify", + "state.recovery.manifest.verify", ]); const DEFAULT_IDLE_MS = 5 * 60 * 1_000; // Bounds for LOOPX_EFFECT_RUNTIME_IDLE_MS. The upper bound is the largest diff --git a/loopx/control_plane/runtime_decode.ts b/loopx/control_plane/runtime_decode.ts index 8245e18c62..975b7766aa 100644 --- a/loopx/control_plane/runtime_decode.ts +++ b/loopx/control_plane/runtime_decode.ts @@ -1,6 +1,7 @@ -import type { JsonObject } from "./effect_program.ts"; import { EffectRuntimeRequestError } from "./effect_runtime_errors.ts"; +export type JsonObject = Record; + export function jsonObject(value: unknown): JsonObject | null { return typeof value === "object" && value !== null && !Array.isArray(value) ? (value as JsonObject) diff --git a/loopx/control_plane/state_recovery.ts b/loopx/control_plane/state_recovery.ts new file mode 100644 index 0000000000..468ce4351f --- /dev/null +++ b/loopx/control_plane/state_recovery.ts @@ -0,0 +1,286 @@ +/** Offline backup inspection. Existing owners decide readability, never activation. */ +import {createHash} from "node:crypto"; +import {lstat, readFile} from "node:fs/promises"; +import {join, relative} from "node:path"; +import type {JsonObject} from "./effect_program.ts"; +import {requireJsonObject} from "./runtime_decode.ts"; +import {canonicalAuthoritySha256, AuthorityStoreProtocolError} from "./coordination/authority_store_codec.ts"; +import {EffectRuntimeRequestError} from "./effect_runtime_errors.ts"; +import {verifyConfigurationBackup} from "./configuration_backup.ts"; +import {decodeLocalAuthoritySelection, localAuthorityProviderPaths, + openLocalAuthorityStoreHandle} from "./coordination/local_authority_provider.ts"; +import {sqliteAuthorityRuntime} from "./coordination/sqlite_runtime.ts"; +import {sqliteAuthorityPath} from "./coordination/sqlite_authority_store.ts"; +import {canonicalTodoCollection} from "./coordination/local_authority_read.ts"; +import {STATE_RECOVERY_AUDIT_SCHEMA, decodeStateRecoveryAudit, isRecoveryDigest, recoveryGoalLabel, + type RecoveryComponent, type RecoveryComponentState} from "./state_recovery_contract.ts"; + +const MAX_GOALS = 128; +const MAX_SQLITE_FILES = 128; +const MAX_COMMITS = 20_000; +const hash = (value: string) => createHash("sha256").update(value).digest("hex"); +const digest = isRecoveryDigest; +class RecoveryInputError extends Error { + readonly code: string; + constructor(code: string) { super(code); this.code = code; } +} +function check(value: unknown, code: string): asserts value { + if (!value) throw new RecoveryInputError(code); +} +function reject(code: string): JsonObject { + return {ok: false, error_code: code, execution_authority_granted: false}; +} +function manifestCore(value: JsonObject): JsonObject { + const execution = {...requireJsonObject(value.execution, "backup execution")}; + execution.archive_sha256 = null; + execution.archive_size_bytes = null; + delete execution.archive_to_logical_ratio; + return {...value, execution}; +} + +function externalEvidence(request: JsonObject): {external: JsonObject; execution: JsonObject} { + const external = requireJsonObject(request.external_manifest, "external backup manifest"); + check(external.schema_version === "loopx_state_backup_v0", "backup_schema_unsupported"); + check(external.dry_run === false && external.execute_requested === true && external.ok === true, + "backup_not_committed"); + check(typeof external.backup_id === "string" && external.backup_id.length > 0, "backup_identity_missing"); + const execution = requireJsonObject(external.execution, "backup execution"); + check(digest(request.archive_sha256) && execution.archive_sha256 === request.archive_sha256, + "archive_digest_mismatch"); + check(Number.isSafeInteger(request.archive_size_bytes) && execution.archive_size_bytes === request.archive_size_bytes, + "archive_size_mismatch"); + return {external, execution}; +} +function inputFailure(error: unknown): JsonObject { + if (error instanceof RecoveryInputError) return reject(error.code); + if (error instanceof EffectRuntimeRequestError || error instanceof AuthorityStoreProtocolError) { + return reject("backup_manifest_invalid"); + } + throw error; +} + +/** Reject a mismatched archive before its untrusted tar structure is decoded. */ +export function verifyStateRecoveryArchive(value: unknown): JsonObject { + try { + externalEvidence(requireJsonObject(value, "recovery archive verification")); + return {ok: true, archive_digest_verified: true, execution_authority_granted: false}; + } catch (error) { return inputFailure(error); } +} + +/** Host IO supplies observed bytes; input manifests never supply observed facts. */ +export function verifyStateRecoveryManifest(value: unknown): JsonObject { + try { + const request = requireJsonObject(value, "recovery manifest verification"); + const {external, execution} = externalEvidence(request); + const embedded = requireJsonObject(request.embedded_manifest, "embedded backup manifest"); + check(embedded.schema_version === external.schema_version, + "backup_schema_unsupported"); + check(canonicalAuthoritySha256(manifestCore(external)) === canonicalAuthoritySha256(manifestCore(embedded)), + "manifest_disagreement"); + check(Array.isArray(request.files), "member_inventory_invalid"); + check(Number.isSafeInteger(request.sqlite_member_count) && Number(request.sqlite_member_count) >= 0 + && Number(request.sqlite_member_count) <= MAX_SQLITE_FILES, "sqlite_inventory_capacity"); + const observed = new Map(); + for (const item of request.files) { + const file = requireJsonObject(item, "observed member"); + check(typeof file.archive_path === "string" && !observed.has(file.archive_path) + && digest(file.sha256) && Number.isSafeInteger(file.size_bytes) && Number(file.size_bytes) >= 0, + "member_inventory_invalid"); + observed.set(file.archive_path, file); + } + const witnesses = execution.file_members; + if (witnesses !== undefined) { + check(Array.isArray(witnesses) && witnesses.length === observed.size, "member_witness_mismatch"); + const seen = new Set(); + for (const item of witnesses) { + const witness = requireJsonObject(item, "member witness"); + const path = String(witness.archive_path); + check(!seen.has(path), "member_witness_mismatch"); + seen.add(path); + const file = observed.get(path); + check(file && file.sha256 === witness.sha256 && file.size_bytes === witness.size_bytes, + "member_witness_mismatch"); + } + } + check(digest(request.external_manifest_sha256), "manifest_digest_invalid"); + return {ok: true, schema_version: STATE_RECOVERY_AUDIT_SCHEMA, + recovery_id: hash(`${request.archive_sha256}:${request.external_manifest_sha256}`), + backup: {schema_version: external.schema_version, archive_sha256: request.archive_sha256, + external_manifest_sha256: request.external_manifest_sha256}, + file_count: observed.size, file_witnesses_verified: witnesses !== undefined, + manifest_verified: true, capture_profile: "legacy_best_effort", execution_authority_granted: false}; + } catch (error) { + return inputFailure(error); + } +} + +function component(id: string, owner: string, state: RecoveryComponentState, reason: string, + next: string, facts: JsonObject = {}, dependencies: string[] = []): RecoveryComponent { + return {component_id: id, owner_kind: owner, state, reason_code: reason, + next_action: next, facts, dependency_ids: dependencies}; +} +async function regular(path: string): Promise { + const stat = await lstat(path); + check(stat.isFile() && !stat.isSymbolicLink(), "owner_source_not_regular"); +} + +async function inspectGoal(root: string, goalId: string, standaloneSqlite: Set): Promise { + const id = `goal:${hash(goalId).slice(0, 20)}`; + const paths = localAuthorityProviderPaths(root, goalId); + const goalFacts = recoveryGoalLabel(goalId) === null ? {} : {goal_id: goalId}; + try { + let selection: ReturnType | null = null; + try { + await regular(paths.marker); + selection = decodeLocalAuthoritySelection(JSON.parse(await readFile(paths.marker, "utf8")), goalId); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; + } + if (selection?.provider === "postgresql") return component(id, "coordination_authority", "unknown", + "offline_provider_unsupported", "use_provider_recovery_owner", goalFacts, ["configuration"]); + if (selection?.provider === "sqlite") check(standaloneSqlite.has(sqliteAuthorityPath(paths.sqlite, goalId)), + "sqlite_requires_owner_recovery"); + // The existing owner retains legacy File reads and rejects missing selected + // storage or a lost SQLite selector. existingOnly forbids initializing either. + const handle = await openLocalAuthorityStoreHandle(root, goalId, {}, {existingOnly: true}); + const loaded = await handle.store.loadAuthority(); + check(loaded.status === "loaded", "authority_head_unavailable"); + // Byte-valid storage alone is not a readable Goal work model. Reuse the + // same full-record, lease and acceptance decoder as canonical Todo reads. + const collection = canonicalTodoCollection(loaded.head, goalId, true); + let cursor: string | null = null; + let count = 0; + let final: JsonObject | null = null; + let finalRevision: string | null = null; + while (true) { + const page = await handle.store.scanCommitted(cursor, 200); + check(page.status === "page", "authority_history_unavailable"); + count += page.transactions.length; + check(count <= MAX_COMMITS, "authority_history_capacity"); + for (const row of page.transactions) { final = row.projection; finalRevision = row.provider_revision; } + if (!page.has_more) break; + check(page.next_cursor !== null && page.next_cursor !== cursor && page.transactions.length > 0, + "authority_history_cursor_invalid"); + cursor = page.next_cursor; + } + check(String(count) === loaded.cursor && final !== null && finalRevision === loaded.provider_revision + && canonicalAuthoritySha256(final) === canonicalAuthoritySha256(loaded.head), "authority_history_head_mismatch"); + const readback = await handle.store.loadAuthority(); + check(readback.status === "loaded" && readback.provider_revision === loaded.provider_revision, + "authority_changed_during_audit"); + return component(id, "coordination_authority", "readable", "historical_head_and_history_readable", + "review_goal_identity_before_adoption", {...goalFacts, provider: handle.provider, + head_digest: canonicalAuthoritySha256(loaded.head), history_commits: count, + todo_count: collection.projection.todo_ids.length, + historical_lease_count: collection.leaseIndex?.lease_todo_ids.length ?? 0}, ["configuration"]); + } catch (error) { + return component(id, "coordination_authority", "unknown", + error instanceof RecoveryInputError ? error.code : "authority_source_unavailable", + "inspect_captured_authority_with_owner", goalFacts, ["configuration"]); + } +} + +/** Reads only a host-validated inert directory; never registers or repairs a store. */ +async function auditOwners(value: unknown): Promise { + const request = requireJsonObject(value, "recovery owner audit"); + const basis = requireJsonObject(request.verified, "verified recovery basis"); + check(basis.ok === true && basis.manifest_verified === true && basis.schema_version === STATE_RECOVERY_AUDIT_SCHEMA, + "recovery_basis_invalid"); + check(typeof request.data_root === "string" && Array.isArray(request.sqlite_members), "recovery_owner_request_invalid"); + const dataRoot = request.data_root; + check(request.sqlite_members.length <= MAX_SQLITE_FILES, "sqlite_inventory_capacity"); + const components: RecoveryComponent[] = []; + let configuration: JsonObject | null = null; + try { + const path = join(dataRoot, "configuration-backup.json"); + await regular(path); + const candidate = requireJsonObject(JSON.parse(await readFile(path, "utf8")), "captured configuration"); + const verified = verifyConfigurationBackup(candidate); + configuration = candidate; + components.push(component("configuration", "configuration", "readable", "configuration_digest_verified", + "retain_isolated_configuration", {goal_count: verified.goal_count})); + } catch { + components.push(component("configuration", "configuration", "unknown", "configuration_unavailable", + "inspect_configuration_with_owner")); + } + check(Array.isArray(request.standalone_sqlite_members), "recovery_owner_request_invalid"); + const standaloneSqlite = new Set(request.standalone_sqlite_members.map(raw => join(dataRoot, String(raw)))); + const ownerReadableSqlite = new Set(); + for (const raw of request.sqlite_members) { + const path = String(raw); + // The IO adapter has already verified all names and refused symlink ancestors. + check(!path.startsWith("/") && !path.split("/").includes(".."), "sqlite_member_invalid"); + const id = `sqlite:${hash(path).slice(0, 20)}`; + try { + const fullPath = join(dataRoot, path); + check(relative(dataRoot, fullPath) === path && standaloneSqlite.has(fullPath), "sqlite_requires_owner_recovery"); + await regular(fullPath); + const {driver} = sqliteAuthorityRuntime(); + const limits = new driver.DatabaseSync(":memory:"); + try { + // SQLite's heap limit is process-wide; this module is invoked only in + // the disposable verifier, never in the shared Effect daemon. + limits.exec("PRAGMA hard_heap_limit=67108864"); + const actual = Number(limits.prepare("PRAGMA hard_heap_limit").get()?.hard_heap_limit); + check(actual > 0 && actual <= 64 * 1024 * 1024, "sqlite_memory_limit_unavailable"); + } finally { limits.close(); } + const database = new driver.DatabaseSync(fullPath, {readOnly: true}); + try { + database.exec("PRAGMA query_only=ON; PRAGMA trusted_schema=OFF"); + const tables = database.prepare("PRAGMA table_list").all(); + const physicalTablesOnly = tables.every(row => row.type === "table"); + check(physicalTablesOnly, "sqlite_schema_requires_owner_recovery"); + const rows = database.prepare("PRAGMA integrity_check").all(); + check(rows.length === 1 && rows[0]?.integrity_check === "ok", "sqlite_integrity_failed"); + // Live store readers assume their own physical tables. Never evaluate + // a restored view or virtual table as an authority table. + ownerReadableSqlite.add(fullPath); + } finally { database.close(); } + components.push(component(id, "sqlite_storage", "historical_only", "sqlite_bytes_verified", + "retain_historical_database")); + } catch (error) { + components.push(component(id, "sqlite_storage", "unknown", + error instanceof RecoveryInputError ? error.code : "sqlite_verification_failed", + "inspect_database_with_owner")); + } + } + if (configuration !== null) { + const goals = (configuration.data as JsonObject).goals as JsonObject[]; + check(goals.length <= MAX_GOALS, "goal_inventory_capacity"); + for (const goal of goals) components.push(await inspectGoal(join(dataRoot, "runtime-root"), String(goal.goal_id), ownerReadableSqlite)); + } + // A v0 file inventory does not identify every session/effect/extension owner. + // Retained bytes must not masquerade as a successful owner-local read. + components.push(component("remaining_state", "recovery", "unknown", "legacy_owner_inventory_incomplete", + "classify_remaining_owner_state")); + const audit = {...basis, status: "held", written: true, observed_at: new Date().toISOString(), + workspace: {isolation: "inert", registered: false}, components, + findings: [{reason_code: "legacy_manifest_incomplete", owner_kind: "backup_capture", + next_action: "review_capture_consistency"}], + summary: {readable: components.filter(row => row.state === "readable").length, + historical_only: components.filter(row => row.state === "historical_only").length, + unknown: components.filter(row => row.state === "unknown").length}, + activation_eligible: false, execution_authority_granted: false}; + return decodeStateRecoveryAudit({...audit, audit_sha256: canonicalAuthoritySha256(audit)}); +} + +export async function auditStateRecoveryOwners(value: unknown): Promise { + try { return await auditOwners(value); } + catch (error) { return inputFailure(error); } +} + +export function verifySavedStateRecoveryAudit(value: unknown): JsonObject { + try { + const audit = decodeStateRecoveryAudit(value); + const {audit_sha256, ...body} = audit; + check(canonicalAuthoritySha256(body) === audit_sha256, "recovery_audit_digest_mismatch"); + const backup = requireJsonObject(audit.backup, "recovery backup basis"); + check(audit.recovery_id === hash(`${backup.archive_sha256}:${backup.external_manifest_sha256}`), + "recovery_audit_identity_mismatch"); + return audit; + } catch (error) { + if (error instanceof RecoveryInputError) return reject(error.code); + if (error instanceof EffectRuntimeRequestError || error instanceof AuthorityStoreProtocolError) return reject("recovery_audit_invalid"); + throw error; + } +} diff --git a/loopx/control_plane/state_recovery_contract.ts b/loopx/control_plane/state_recovery_contract.ts new file mode 100644 index 0000000000..776c2c47c2 --- /dev/null +++ b/loopx/control_plane/state_recovery_contract.ts @@ -0,0 +1,81 @@ +/** Shared readback contract for the offline owner and packaged client. */ +import {EffectRuntimeRequestError} from "./effect_runtime_errors.ts"; +import {requireJsonObject, type JsonObject} from "./runtime_decode.ts"; + +export const STATE_RECOVERY_AUDIT_SCHEMA = "loopx_state_recovery_audit_v0"; +export const RECOVERY_COMPONENT_STATES = ["readable", "historical_only", "unknown"] as const; +export type RecoveryComponentState = typeof RECOVERY_COMPONENT_STATES[number]; +export interface RecoveryComponent extends JsonObject { + component_id: string; + owner_kind: string; + state: RecoveryComponentState; + reason_code: string; + next_action: string; + facts: JsonObject; + dependency_ids: string[]; +} +export interface StateRecoveryAudit extends JsonObject { + schema_version: typeof STATE_RECOVERY_AUDIT_SCHEMA; + recovery_id: string; + audit_sha256: string; + observed_at: string; + status: "held"; + execution_authority_granted: false; + activation_eligible: false; + components: RecoveryComponent[]; + findings: Array<{reason_code: string; owner_kind: string; next_action: string}>; + summary: Record; +} +function requireValue(condition: unknown): asserts condition { + if (!condition) throw new EffectRuntimeRequestError("State recovery audit contract is invalid", "recovery_audit_invalid"); +} +function token(value: unknown): boolean { + return typeof value === "string" && /^[a-z0-9_:.~-]{1,128}$/.test(value); +} +export function isRecoveryDigest(value: unknown): value is string { + return typeof value === "string" && /^[a-f0-9]{64}$/.test(value); +} +/** Display only; unsafe labels retain their opaque component identity. */ +export function recoveryGoalLabel(value: string): string | null { + return /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/.test(value) ? value : null; +} +export function decodeStateRecoveryAudit(value: unknown): StateRecoveryAudit { + const audit = requireJsonObject(value, "state recovery audit"); + requireValue(audit.ok === true && audit.schema_version === STATE_RECOVERY_AUDIT_SCHEMA + && audit.status === "held" && audit.written === true && audit.manifest_verified === true + && audit.execution_authority_granted === false && audit.activation_eligible === false + && isRecoveryDigest(audit.recovery_id) && isRecoveryDigest(audit.audit_sha256)); + requireValue(typeof audit.observed_at === "string" && audit.observed_at.length <= 40 + && Number.isFinite(Date.parse(audit.observed_at))); + const workspace = requireJsonObject(audit.workspace, "inert recovery workspace"); + requireValue(workspace.isolation === "inert" && workspace.registered === false); + const backup = requireJsonObject(audit.backup, "recovery backup basis"); + requireValue(backup.schema_version === "loopx_state_backup_v0" + && isRecoveryDigest(backup.archive_sha256) && isRecoveryDigest(backup.external_manifest_sha256)); + requireValue(Array.isArray(audit.components) && audit.components.length <= 512); + const ids = new Set(); + const counts = {readable: 0, historical_only: 0, unknown: 0}; + for (const value of audit.components) { + const row = requireJsonObject(value, "recovery component"); + requireValue(typeof row.component_id === "string" && token(row.component_id) && !ids.has(row.component_id) + && token(row.owner_kind) && token(row.reason_code) && token(row.next_action) + && RECOVERY_COMPONENT_STATES.includes(row.state as RecoveryComponentState) + && Array.isArray(row.dependency_ids) && row.dependency_ids.every(token)); + const facts = requireJsonObject(row.facts, "recovery component facts"); + requireValue(facts.goal_id === undefined || (typeof facts.goal_id === "string" && recoveryGoalLabel(facts.goal_id) === facts.goal_id)); + ids.add(row.component_id); + counts[row.state as RecoveryComponentState]++; + } + for (const value of audit.components) { + const row = value as RecoveryComponent; + requireValue(row.dependency_ids.every(id => ids.has(id) && id !== row.component_id)); + } + const summary = requireJsonObject(audit.summary, "recovery summary"); + requireValue(RECOVERY_COMPONENT_STATES.every(state => summary[state] === counts[state])); + requireValue(Array.isArray(audit.findings)); + for (const value of audit.findings) { + const finding = requireJsonObject(value, "recovery finding"); + requireValue(token(finding.reason_code) && token(finding.owner_kind) && token(finding.next_action)); + } + return audit as StateRecoveryAudit; +} diff --git a/loopx/control_plane/state_recovery_worker.ts b/loopx/control_plane/state_recovery_worker.ts new file mode 100644 index 0000000000..be0aaf590d --- /dev/null +++ b/loopx/control_plane/state_recovery_worker.ts @@ -0,0 +1,20 @@ +/** Disposable offline verifier: untrusted database work cannot stall the daemon. */ +import {auditStateRecoveryOwners} from "./state_recovery.ts"; + +try { + let size = 0; + const chunks: Buffer[] = []; + for await (const chunk of process.stdin) { + const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); + size += data.length; + if (size > 2 * 1024 * 1024) throw new Error("input capacity"); + chunks.push(data); + } + const result = await auditStateRecoveryOwners(JSON.parse(Buffer.concat(chunks).toString("utf8"))); + process.stdout.write(JSON.stringify(result)); + process.exitCode = result.ok === true ? 0 : 1; +} catch { + process.stdout.write(JSON.stringify({ok: false, error_code: "recovery_owner_unavailable", + execution_authority_granted: false})); + process.exitCode = 1; +} diff --git a/loopx/presentation/configuration_api.py b/loopx/presentation/configuration_api.py index bfc3110351..be03234550 100644 --- a/loopx/presentation/configuration_api.py +++ b/loopx/presentation/configuration_api.py @@ -3,6 +3,7 @@ from collections.abc import Callable from . import configuration_backup_api as backup_api +from . import state_recovery_api as recovery_api from . import goal_ownership_api as ownership_api from . import goal_storage_api as storage_api from .. import chat_usage_statistics_api as usage_api @@ -21,11 +22,14 @@ class ChatConfigurationRequestMixin( machine_api.MachineConfigurationRequestMixin, operator_api.OperatorProviderRequestMixin, backup_api.ConfigurationBackupRequestMixin, + recovery_api.StateRecoveryRequestMixin, ): """Expose machine and Goal configuration through one route registry.""" def _configuration_get_routes(self) -> dict[str, Callable[[], None]]: return { + recovery_api.STATE_RECOVERY_PATH: self._state_recovery_list, + f"{recovery_api.STATE_RECOVERY_PATH}/profile": self._state_recovery_profile, storage_api.CHAT_GOAL_STORAGE_PATH: self._storage_inspect, ownership_api.CHAT_GOAL_OWNERSHIP_PATH: self._ownership_inspect, cadence_api.CHAT_AUTOMATION_CADENCE_PATH: self._cadence_read, @@ -37,6 +41,10 @@ def _configuration_get_routes(self) -> dict[str, Callable[[], None]]: def _configuration_post_routes(self) -> dict[str, Callable[[], None]]: return { + f"{recovery_api.STATE_RECOVERY_PATH}/archive": lambda: self._state_recovery_upload("archive"), + f"{recovery_api.STATE_RECOVERY_PATH}/manifest": lambda: self._state_recovery_upload("manifest"), + f"{recovery_api.STATE_RECOVERY_PATH}/verify": self._state_recovery_verify, + f"{recovery_api.STATE_RECOVERY_PATH}/read": self._state_recovery_read, f"{storage_api.CHAT_GOAL_STORAGE_PATH}/preview": lambda: self._storage_update(action="plan-migration"), f"{storage_api.CHAT_GOAL_STORAGE_PATH}/apply": lambda: self._storage_update(action="migrate"), f"{storage_api.CHAT_GOAL_STORAGE_PATH}/recover": lambda: self._storage_update(action="migration-readback"), diff --git a/loopx/presentation/state_recovery_api.py b/loopx/presentation/state_recovery_api.py new file mode 100644 index 0000000000..9f596399d9 --- /dev/null +++ b/loopx/presentation/state_recovery_api.py @@ -0,0 +1,152 @@ +"""Loopback-only backup upload and immutable inert-audit readback.""" +from __future__ import annotations + +import hashlib +import os +from pathlib import Path +import re +import shutil +import tempfile +import time +from uuid import uuid4 + +from ..state_recovery import ( + MAX_ARCHIVE_BYTES, MAX_MANIFEST_BYTES, StateRecoveryError, + read_state_recovery_audit, verify_state_backup, +) +from ..status_server import is_loopback_host + +STATE_RECOVERY_PATH = "/api/chat/state-recovery" +_UPLOAD_TTL_SECONDS = 3600 +_REF = re.compile(r"^(archive|manifest):([a-f0-9]{32})$") + + +class StateRecoveryRequestMixin: + def _recovery_root(self, *, create: bool = False) -> Path: + if os.name != "posix": + raise StateRecoveryError("recovery_platform_unqualified") + if not is_loopback_host(str(self.server.server_address[0])): + raise StateRecoveryError("recovery_requires_loopback_server") + root = self.server.runtime_root / "backups" / "state-recovery" + if root.absolute() != root.resolve(): + raise StateRecoveryError("recovery_storage_not_physical") + if create: + root.mkdir(parents=True, exist_ok=True, mode=0o700) + (root / "inputs").mkdir(exist_ok=True, mode=0o700) + (root / "checkpoints").mkdir(exist_ok=True, mode=0o700) + for directory in (root, root / "inputs", root / "checkpoints"): + if directory.exists() or directory.is_symlink(): + info = directory.lstat() + if directory.is_symlink() or not directory.is_dir() or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise StateRecoveryError("recovery_storage_not_private") + return root + + def _state_recovery_profile(self) -> None: + supported = os.name == "posix" and is_loopback_host(str(self.server.server_address[0])) + self._send_json({"ok": True, "supported": supported, "activation_available": False, + "maximum_archive_bytes": MAX_ARCHIVE_BYTES, "maximum_manifest_bytes": MAX_MANIFEST_BYTES}) + + def _recovery_failure(self, error: Exception) -> None: + code = error.code if isinstance(error, StateRecoveryError) else "recovery_unavailable" + self._send_error("Backup inspection did not finish. Check the archive, manifest and available disk space.", + status=400 if isinstance(error, ValueError) else 503, error_code=code) + + def _state_recovery_upload(self, kind: str) -> None: + temporary: Path | None = None + previous_timeout = self.connection.gettimeout() + try: + if os.name != "posix": + raise StateRecoveryError("recovery_platform_unqualified") + maximum = MAX_ARCHIVE_BYTES if kind == "archive" else MAX_MANIFEST_BYTES + lengths = self.headers.get_all("Content-Length") or [] + if len(lengths) != 1 or not lengths[0].isdigit() or self.headers.get("Transfer-Encoding"): + raise StateRecoveryError("recovery_upload_length_invalid") + length = int(lengths[0]) + if not 0 < length <= maximum: + raise StateRecoveryError("recovery_capacity_exceeded") + root = self._recovery_root(create=True) + if shutil.disk_usage(root).free < length + MAX_MANIFEST_BYTES: + raise StateRecoveryError("recovery_disk_capacity") + inputs = root / "inputs" + # These are disposable upload inputs, not backups or audit receipts. + for old in inputs.iterdir(): + if re.fullmatch(r"(?:archive|manifest)-[a-f0-9]{32}", old.name) and not old.is_symlink(): + if old.stat().st_mtime < time.time() - _UPLOAD_TTL_SECONDS: + old.unlink() + fd, name = tempfile.mkstemp(prefix=".upload-", dir=inputs) + temporary = Path(name) + digest, remaining = hashlib.sha256(), length + self.connection.settimeout(30) + with os.fdopen(fd, "wb") as output: + while remaining: + chunk = self.rfile.read(min(256 * 1024, remaining)) + if not chunk: + raise StateRecoveryError("recovery_upload_incomplete") + output.write(chunk) + digest.update(chunk) + remaining -= len(chunk) + output.flush() + os.fsync(output.fileno()) + identifier = uuid4().hex + destination = inputs / f"{kind}-{identifier}" + os.link(temporary, destination) + self._send_json({"ok": True, "input_ref": f"{kind}:{identifier}", + "sha256": digest.hexdigest(), "size_bytes": length, + **({"checkpoint_ref": identifier} if kind == "archive" else {})}) + except (ValueError, OSError, RuntimeError) as error: + self.close_connection = True + self._recovery_failure(error) + finally: + self.connection.settimeout(previous_timeout) + if temporary is not None: + temporary.unlink(missing_ok=True) + + def _recovery_input(self, ref: object, kind: str) -> tuple[Path, str]: + match = _REF.fullmatch(ref) if isinstance(ref, str) else None + if match is None or match.group(1) != kind: + raise StateRecoveryError("recovery_input_ref_invalid") + root = self._recovery_root() + return root / "inputs" / f"{kind}-{match.group(2)}", match.group(2) + + def _state_recovery_verify(self) -> None: + try: + body = self._read_json() + if set(body) != {"archive_ref", "manifest_ref", "execute"} or type(body["execute"]) is not bool: + raise StateRecoveryError("recovery_request_invalid") + archive, identifier = self._recovery_input(body["archive_ref"], "archive") + manifest, _ = self._recovery_input(body["manifest_ref"], "manifest") + destination = self._recovery_root() / "checkpoints" / identifier + audit = verify_state_backup(archive=archive, manifest=manifest, destination=destination, + execute=body["execute"]) + self._send_json({**audit, "checkpoint_ref": identifier}) + except (ValueError, OSError, RuntimeError) as error: + self._recovery_failure(error) + + def _state_recovery_read(self) -> None: + try: + body = self._read_json() + ref = body.get("checkpoint_ref") + if set(body) != {"checkpoint_ref"} or not isinstance(ref, str) or not re.fullmatch(r"[a-f0-9]{32}", ref): + raise StateRecoveryError("recovery_checkpoint_ref_invalid") + audit = read_state_recovery_audit(self._recovery_root() / "checkpoints" / ref) + self._send_json({**audit, "checkpoint_ref": ref, "historical_readback": True}) + except (ValueError, OSError, RuntimeError) as error: + self._recovery_failure(error) + + def _state_recovery_list(self) -> None: + try: + parent = self._recovery_root() / "checkpoints" + candidates = [] if not parent.exists() else [path for path in parent.iterdir() + if re.fullmatch(r"[a-f0-9]{32}", path.name) and path.is_dir() and not path.is_symlink()] + candidates.sort(key=lambda path: path.stat().st_mtime_ns, reverse=True) + items = [] + for path in candidates[:10]: + try: + audit = read_state_recovery_audit(path) + items.append({"checkpoint_ref": path.name, "available": True, + "observed_at": audit["observed_at"], "summary": audit["summary"]}) + except (ValueError, OSError, RuntimeError): + items.append({"checkpoint_ref": path.name, "available": False}) + self._send_json({"ok": True, "items": items, "has_more": len(candidates) > 10}) + except (ValueError, OSError, RuntimeError) as error: + self._recovery_failure(error) diff --git a/loopx/semantics/project_registry_io_manifest_v1.json b/loopx/semantics/project_registry_io_manifest_v1.json index e076e23f56..14a6f75388 100644 --- a/loopx/semantics/project_registry_io_manifest_v1.json +++ b/loopx/semantics/project_registry_io_manifest_v1.json @@ -895,7 +895,7 @@ }, { "site": "loopx/cli_commands/support_control_backup.py::.handle_backup_state_command::codec_read:load_registry#1", - "line": 84, + "line": 110, "column": 49, "kind": "codec_read", "api": "load_registry", diff --git a/loopx/semantics/vocabulary_v0.json b/loopx/semantics/vocabulary_v0.json index 5b9a4df1d2..0a3fc1000d 100644 --- a/loopx/semantics/vocabulary_v0.json +++ b/loopx/semantics/vocabulary_v0.json @@ -13,7 +13,7 @@ "formal_model": { "schema_version": "loopx_semantic_formal_model_v0", "universes": { - "vocabularies": "V: registered vocabulary identifiers; Producers(V) ⊆ V is exactly the subset declaring a producers key (including compatibility-only entries with an empty list). Kernel(V) ⊆ Producers(V). Producers(V) contains 6 vocabularies: 5 kernel and 1 outside the kernel tier. The 20 cross_runtime vocabularies outside Producers(V) are unverified.", + "vocabularies": "V: registered vocabulary identifiers; Producers(V) ⊆ V is exactly the subset declaring a producers key (including compatibility-only entries with an empty list). Kernel(V) ⊆ Producers(V). Producers(V) contains 6 vocabularies: 5 kernel and 1 outside the kernel tier. The 21 cross_runtime vocabularies outside Producers(V) are unverified.", "values": "U(v): ambient runtime values; S(v): registered admitted values", "sites": "L: source locations that define, produce, consume, interpret, pass through, project, or persist values", "scopes": "Scope: global or bounded_context(context_id); ScopeDeclarations are the forked names the registry declares as bounded contexts", @@ -73,25 +73,25 @@ "invariants": [ { "id": "F1_producer_closedness", - "statement": "∀v ∈ Producers(V): Produced_scan(v) ⊆ S(v) ⊆ U(v), where Produced_scan(v) is the production the fixed forms observe inside the code-owned scan reach. Producers(V) contains 6 vocabularies: 5 kernel and 1 outside the kernel tier. The 20 cross_runtime vocabularies outside Producers(V) are unverified. Production outside the scan reach is unverified rather than proven closed.", + "statement": "∀v ∈ Producers(V): Produced_scan(v) ⊆ S(v) ⊆ U(v), where Produced_scan(v) is the production the fixed forms observe inside the code-owned scan reach. Producers(V) contains 6 vocabularies: 5 kernel and 1 outside the kernel tier. The 21 cross_runtime vocabularies outside Producers(V) are unverified. Production outside the scan reach is unverified rather than proven closed.", "enforcement": "m0_5", "evidence": "bounded production-form AST scan over the PRODUCER_ROOTS/PRODUCER_FILES reach in loopx/semantics/production.py; check_producers visits only vocabularies that declare producers, and validate_production claims no whole-program closedness; unresolved dynamic sites are reported, not treated as proven safe", "domain": { "quantifies_over": "vocabularies[producers].producers", "verified": 6, - "registered": 26, + "registered": 27, "evidence_bound": "producer_scan_reach" } }, { "id": "F2_canonical_value_liveness", - "statement": "∀v ∈ Producers(V): Canonical(v) ⊆ Produced_scan(v) ∪ CompatibilityOnly(v). Producers(V) contains 6 vocabularies: 5 kernel and 1 outside the kernel tier. The 20 cross_runtime vocabularies outside Producers(V) are unverified. Liveness outside the walked set is not proven.", + "statement": "∀v ∈ Producers(V): Canonical(v) ⊆ Produced_scan(v) ∪ CompatibilityOnly(v). Producers(V) contains 6 vocabularies: 5 kernel and 1 outside the kernel tier. The 21 cross_runtime vocabularies outside Producers(V) are unverified. Liveness outside the walked set is not proven.", "enforcement": "m0_5", "evidence": "every value of a vocabulary in Producers(V) has a producer recognised inside the scan reach, an executable input witness, or an explicit compatibility-only reason; a value whose only producer lies outside the reach would be reported as dead, not silently accepted", "domain": { "quantifies_over": "vocabularies[producers].producers", "verified": 6, - "registered": 26, + "registered": 27, "evidence_bound": "producer_scan_reach" } }, @@ -103,7 +103,7 @@ "domain": { "quantifies_over": "vocabularies[*]", "verified": 0, - "registered": 26, + "registered": 27, "evidence_bound": "inventory_only" } }, @@ -941,6 +941,25 @@ "control_repair": "Appended when the turn is routed to a control-plane repair instead of delivery: stall self-repair, a capability gate asking to repair the bridge, a workspace guard being present, or a required automation-prompt upgrade. The repair is control-plane work, so it displaces the delivery it would otherwise run alongside.", "delivery_not_allowed": "The catch-all appended last when normal delivery is off for a reason none of the four specific causes names, such as a monitor-only work mode, a settled receipt-bound monitor, a monitor-quiet skip or an external-evidence wait. Because it is appended last it only surfaces as the chosen reason when no more specific cause applies." } + }, + "state_recovery_component_state": { + "meaning": "Offline owner-local readability of a component in an inert recovery audit; never execution or adoption authority.", + "tier": "cross_runtime", + "status": "canonical", + "owners": { + "python": null, + "typescript": "loopx/control_plane/state_recovery_contract.ts::RECOVERY_COMPONENT_STATES" + }, + "values": [ + "readable", + "historical_only", + "unknown" + ], + "value_notes": { + "readable": "An existing configuration verifier or the selected File/SQLite authority and full Todo/history readers accepted the captured component. This is historical readability, not a current Goal identity, permission or execution grant.", + "historical_only": "A standalone SQLite copy passed its bounded offline structural integrity check, but its bytes have not been adopted by a current domain owner. Restored provider identities and leases remain historical data.", + "unknown": "No supported owner inventory exists for remaining v0 state, or a component failed format, availability, qualified-runtime or owner-read validation. Unknown never becomes readable merely because archive or file hashes matched." + } } }, "relations": { diff --git a/loopx/state_recovery.py b/loopx/state_recovery.py new file mode 100644 index 0000000000..b006e5635a --- /dev/null +++ b/loopx/state_recovery.py @@ -0,0 +1,414 @@ +"""Bounded tar/filesystem adapter for the typed, inert recovery audit owner. + +No restored Python, plugin, shell, credential or live registry is loaded. +Semantic manifest agreement and owner readback remain in TypeScript. +""" +from __future__ import annotations + +from contextlib import contextmanager +from collections.abc import Callable +import gzip +import hashlib +import io +import json +import os +from pathlib import Path, PurePosixPath +import shutil +import stat +import subprocess +import tarfile +import tempfile +import unicodedata +import zlib +from typing import Any, BinaryIO, Iterator + +from .control_plane.effect_runtime import _node_executable, effect_runtime_result, effect_runtime_request_scope + +MAX_ARCHIVE_BYTES = 128 * 1024 * 1024 +MAX_EXPANDED_BYTES = 512 * 1024 * 1024 +MAX_MANIFEST_BYTES = 16 * 1024 * 1024 +MAX_MEMBERS = 20_000 +MAX_EXPANSION_RATIO = 2000 +_CHUNK = 256 * 1024 +_OWNER_TIMEOUT_SECONDS = 90 + + +class StateRecoveryError(ValueError): + def __init__(self, code: str): + self.code = code + super().__init__(code) + + +def _require(condition: bool, code: str) -> None: + if not condition: + raise StateRecoveryError(code) + + +def _json(data: bytes) -> dict[str, Any]: + def pairs(items: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in items: + _require(key not in result, "manifest_duplicate_key") + result[key] = value + return result + + def constant(_value: str) -> None: + raise StateRecoveryError("manifest_invalid_json") + + try: + value = json.loads(data, object_pairs_hook=pairs, parse_constant=constant) + except (UnicodeError, json.JSONDecodeError, RecursionError) as exc: + raise StateRecoveryError("manifest_invalid_json") from exc + if not isinstance(value, dict): + raise StateRecoveryError("manifest_invalid_json") + pending = [(value, 0)] + while pending: + item, depth = pending.pop() + _require(depth <= 64, "recovery_capacity_exceeded") + if isinstance(item, dict): + pending.extend((child, depth + 1) for child in item.values()) + elif isinstance(item, list): + pending.extend((child, depth + 1) for child in item) + return value + + +def _runtime(method: str, request: dict[str, Any]) -> dict[str, Any]: + value = effect_runtime_result(method, request, timeout=90, + large_local_snapshot=method in {"state.recovery.archive.verify", "state.recovery.manifest.verify"}) + if not isinstance(value, dict): + raise StateRecoveryError("recovery_owner_result_invalid") + if value.get("ok") is not True: + code = value.get("error_code") + raise StateRecoveryError(code if isinstance(code, str) else "recovery_owner_result_invalid") + return value + + +def _audit_owners(request: dict[str, Any]) -> dict[str, Any]: + command = [_node_executable(), "--no-warnings", "--max-old-space-size=512", + "--experimental-sqlite", "--experimental-strip-types", + str(Path(__file__).parent / "control_plane/state_recovery_worker.ts")] + try: + completed = subprocess.run(command, input=json.dumps(request), text=True, encoding="utf-8", + capture_output=True, timeout=_OWNER_TIMEOUT_SECONDS, check=False) + except subprocess.TimeoutExpired as exc: + # subprocess.run kills and waits for this child only, never the daemon. + raise StateRecoveryError("recovery_owner_timeout") from exc + _require(bool(completed.stdout.strip()), "recovery_owner_unavailable") + _require(len(completed.stdout) <= 2 * 1024 * 1024, "recovery_owner_result_invalid") + result = _json(completed.stdout.encode()) + if result.get("ok") is not True: + raise StateRecoveryError(str(result.get("error_code") or "recovery_owner_unavailable")) + _require(completed.returncode == 0, "recovery_owner_unavailable") + return result + + +@contextmanager +def _regular_input(path: Path, limit: int) -> Iterator[BinaryIO]: + try: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + except OSError as exc: + raise StateRecoveryError("recovery_input_unavailable") from exc + with os.fdopen(fd, "rb") as handle: + info = os.fstat(handle.fileno()) + _require(stat.S_ISREG(info.st_mode), "recovery_input_not_regular") + _require(info.st_uid == os.getuid() and not info.st_mode & 0o077, "recovery_input_not_private") + _require(0 < info.st_size <= limit, "recovery_capacity_exceeded") + yield handle + + +def _sha(handle: BinaryIO) -> tuple[str, int]: + handle.seek(0) + digest, count = hashlib.sha256(), 0 + while chunk := handle.read(_CHUNK): + count += len(chunk) + _require(count <= MAX_ARCHIVE_BYTES, "recovery_capacity_exceeded") + digest.update(chunk) + handle.seek(0) + return digest.hexdigest(), count + + +class _BoundedGzip(io.RawIOBase): + def __init__(self, handle: BinaryIO, archive_size: int): + super().__init__() + self.source = gzip.GzipFile(fileobj=handle, mode="rb") + self.limit = min(MAX_EXPANDED_BYTES, archive_size * MAX_EXPANSION_RATIO) + self.total = 0 + + def read(self, size: int = -1) -> bytes: + _require(size >= 0, "archive_unbounded_read") + data = self.source.read(min(size, self.limit - self.total + 1)) + self.total += len(data) + _require(self.total <= self.limit, "recovery_capacity_exceeded") + return data + + +class _RecoveryTarInfo(tarfile.TarInfo): + def _proc_member(self, archive: tarfile.TarFile) -> tarfile.TarInfo | None: + # Bound extended metadata before tarfile allocates/decodes its body. + if self.type in (tarfile.XHDTYPE, tarfile.XGLTYPE, tarfile.GNUTYPE_LONGNAME, tarfile.GNUTYPE_LONGLINK): + _require(self.size <= 128 * 1024, "archive_metadata_too_large") + if isinstance(archive, _RecoveryTarFile): + archive.recovery_metadata_bytes += self.size + _require(archive.recovery_metadata_bytes <= MAX_MANIFEST_BYTES, "archive_metadata_too_large") + # The stdlib hook is absent from typeshed; keep its real bound signature + # here rather than weakening the public stream/return types. + process: Callable[[tarfile.TarFile], tarfile.TarInfo | None] = getattr(super(), "_proc_member") + return process(archive) + + +class _RecoveryTarFile(tarfile.TarFile): + recovery_metadata_bytes = 0 + + +def _member_name(raw: str) -> str: + _require(bool(raw) and len(raw) <= 1024 and "\\" not in raw and "\x00" not in raw, + "archive_path_invalid") + path = PurePosixPath(raw) + _require(bool(path.parts) and len(path.parts) <= 64 and not path.is_absolute() + and ".." not in path.parts and ":" not in path.parts[0] + and all(len(part.encode("utf-8")) <= 255 for part in path.parts), + "archive_path_invalid") + normalized = str(path) + _require(normalized not in ("", "."), "archive_path_invalid") + return normalized + + +def _physical_ancestors(destination: Path) -> None: + _require(destination.is_absolute() and str(destination) == os.path.normpath(destination), + "recovery_destination_invalid") + for parent in destination.parents: + try: + info = parent.lstat() + except OSError as exc: + raise StateRecoveryError("recovery_destination_parent_unavailable") from exc + _require(stat.S_ISDIR(info.st_mode) and not stat.S_ISLNK(info.st_mode), + "recovery_destination_parent_not_physical") + + +def _physical_parent(destination: Path) -> None: + _physical_ancestors(destination) + _require(not destination.exists() and not destination.is_symlink(), "recovery_destination_exists") + + +def _directories(root: Path, parts: tuple[str, ...]) -> Path: + current = root + for part in parts: + current /= part + current.mkdir(mode=0o700, exist_ok=True) + _require(current.is_dir() and not current.is_symlink(), "archive_parent_not_directory") + return current + + +def _check_links(links: list[tuple[str, str]]) -> None: + by_name = {unicodedata.normalize("NFC", name).casefold(): target for name, target in links} + for name, target in links: + pending = list(PurePosixPath(name).parent.parts) + list(PurePosixPath(target).parts) + resolved: list[str] = [] + hops = 0 + while pending: + part = pending.pop(0) + if part == "..": + _require(bool(resolved), "archive_link_escapes") + resolved.pop() + elif part != ".": + resolved.append(part) + key = unicodedata.normalize("NFC", "/".join(resolved)).casefold() + if key in by_name: + hops += 1 + _require(hops <= 40, "archive_link_cycle") + resolved.pop() + pending = list(PurePosixPath(by_name[key]).parts) + pending + + +def _scan(handle: BinaryIO, archive_size: int, output: Path | None = None) -> dict[str, Any]: + handle.seek(0) + stream = _BoundedGzip(handle, archive_size) + names: dict[str, str] = {} + folded: dict[str, str] = {} + ancestors: set[str] = set() + links: list[tuple[str, str]] = [] + files, sqlite_members, standalone = [], [], [] + embedded: dict[str, Any] | None = None + logical = 0 + try: + with _RecoveryTarFile.open(fileobj=stream, mode="r|", tarinfo=_RecoveryTarInfo) as archive: + for member in archive: + name = _member_name(member.name) + key = unicodedata.normalize("NFC", name).casefold() + _require(name not in names and key not in folded, "archive_name_collision") + _require(len(names) < MAX_MEMBERS, "recovery_capacity_exceeded") + _require(member.sparse is None, "archive_sparse_unsupported") + kind = "file" if member.isfile() else "directory" if member.isdir() else "symlink" if member.issym() else None + _require(kind is not None, "archive_member_type_unsupported") + names[name], folded[key] = str(kind), name + for parent in PurePosixPath(name).parents: + parent_key = unicodedata.normalize("NFC", str(parent)).casefold() + ancestors.add(parent_key) + _require(names.get(folded.get(parent_key, ""), "directory") == "directory", + "archive_parent_not_directory") + if kind != "directory": + _require(key not in ancestors, "archive_parent_not_directory") + if kind == "symlink": + target = member.linkname + _require(bool(target) and len(target) <= 1024 and "\x00" not in target + and not PurePosixPath(target).is_absolute() and "\\" not in target, + "archive_link_escapes") + resolved = os.path.normpath(str(PurePosixPath(name).parent / target)) + _require(resolved != ".." and not resolved.startswith("../"), "archive_link_escapes") + links.append((name, target)) + continue + if output is not None: + _directories(output, PurePosixPath(name).parts if kind == "directory" else PurePosixPath(name).parts[:-1]) + if kind == "directory": + continue + logical += member.size + _require(0 <= member.size <= MAX_EXPANDED_BYTES and logical <= MAX_EXPANDED_BYTES, + "recovery_capacity_exceeded") + if name == "manifest.json": + _require(member.size <= MAX_MANIFEST_BYTES, "archive_metadata_too_large") + source = archive.extractfile(member) + if source is None: + raise StateRecoveryError("archive_member_unreadable") + data, prefix = bytearray(), bytearray() + digest, size = hashlib.sha256(), 0 + destination = None + try: + if output is not None: + destination = open(output / name, "xb") + os.chmod(output / name, 0o600) + while chunk := source.read(_CHUNK): + size += len(chunk) + _require(size <= member.size, "archive_member_size_mismatch") + digest.update(chunk) + prefix.extend(chunk[:max(0, 100 - len(prefix))]) + if name == "manifest.json": + data.extend(chunk) + if destination is not None: + destination.write(chunk) + _require(size == member.size, "archive_member_size_mismatch") + if destination is not None: + destination.flush() + os.fsync(destination.fileno()) + finally: + source.close() + if destination is not None: + destination.close() + if name == "manifest.json": + embedded = _json(bytes(data)) + else: + files.append({"archive_path": name, "sha256": digest.hexdigest(), "size_bytes": size}) + if prefix[:16] == b"SQLite format 3\0": + sqlite_members.append(name) + if prefix[18:20] == b"\x01\x01": + standalone.append(name) + # tar EOF must not conceal an appended archive or unparsed payload. + while trailing := archive.fileobj.read(_CHUNK): + _require(not trailing.strip(b"\0"), "archive_trailing_data") + # Consume gzip EOF too: truncated CRC/trailing expansion is not a valid tar. + while stream.read(_CHUNK): + pass + except (tarfile.TarError, EOFError, gzip.BadGzipFile, OSError, zlib.error) as exc: + raise StateRecoveryError("archive_unreadable") from exc + finally: + stream.source.close() + _require(embedded is not None, "embedded_manifest_missing") + _check_links(links) + # Links are materialized last and no member may live beneath a link. + if output is not None: + for name, target in links: + _directories(output, PurePosixPath(name).parts[:-1]) + os.symlink(target, output / name) + return {"embedded_manifest": embedded, "files": files, "logical_bytes": logical, + "sqlite_members": sqlite_members, "standalone_sqlite_members": standalone, + "entry_count": len(names)} + + +@effect_runtime_request_scope() +def verify_state_backup( + *, archive: Path, manifest: Path, destination: Path, execute: bool = False, +) -> dict[str, Any]: + """Preview or publish one inert audit. This API has no live-adoption operation.""" + _require(os.name == "posix", "recovery_platform_unqualified") + _physical_parent(destination) + with _regular_input(manifest, MAX_MANIFEST_BYTES) as manifest_handle: + manifest_bytes = manifest_handle.read(MAX_MANIFEST_BYTES + 1) + _require(len(manifest_bytes) <= MAX_MANIFEST_BYTES, "recovery_capacity_exceeded") + external = _json(manifest_bytes) + with _regular_input(archive, MAX_ARCHIVE_BYTES) as archive_handle: + archive_sha, archive_size = _sha(archive_handle) + _runtime("state.recovery.archive.verify", {"external_manifest": external, + "archive_sha256": archive_sha, "archive_size_bytes": archive_size}) + observation = _scan(archive_handle, archive_size) + verified = _runtime("state.recovery.manifest.verify", { + "external_manifest": external, "embedded_manifest": observation["embedded_manifest"], + "archive_sha256": archive_sha, "archive_size_bytes": archive_size, + "external_manifest_sha256": hashlib.sha256(manifest_bytes).hexdigest(), + "files": observation["files"], + "sqlite_member_count": len(observation["sqlite_members"]), + }) + _require(shutil.disk_usage(destination.parent).free >= observation["logical_bytes"] + MAX_MANIFEST_BYTES, + "recovery_disk_capacity") + if not execute: + return {**verified, "status": "planned", "written": False, + "logical_bytes": observation["logical_bytes"], "entry_count": observation["entry_count"], + "activation_eligible": False} + staging = Path(tempfile.mkdtemp(prefix=".loopx-recovery-", dir=destination.parent)) + reserved = False + try: + data = staging / "data" + data.mkdir(mode=0o700) + copied = _scan(archive_handle, archive_size, data) + _require(copied == observation and _sha(archive_handle) == (archive_sha, archive_size), + "recovery_input_changed") + audit = _audit_owners({"data_root": str(data), "verified": verified, + "sqlite_members": observation["sqlite_members"], + "standalone_sqlite_members": observation["standalone_sqlite_members"]}) + audit = _runtime("state.recovery.audit.readback", audit) + for member in observation["files"]: + with (data / member["archive_path"]).open("rb") as handle: + digest = hashlib.file_digest(handle, "sha256").hexdigest() + _require(digest == member["sha256"], "recovery_owner_mutated_source") + audit_path = staging / "recovery-audit.json" + with audit_path.open("x", encoding="utf-8") as handle: + os.chmod(audit_path, 0o600) + json.dump(audit, handle, ensure_ascii=False, sort_keys=True, indent=2) + handle.write("\n") + handle.flush() + os.fsync(handle.fileno()) + _require(_json(audit_path.read_bytes()) == audit, "recovery_audit_readback_mismatch") + _physical_parent(destination) + destination.mkdir(mode=0o700) + reserved = True + os.rename(staging, destination) + reserved = False + return read_state_recovery_audit(destination) + finally: + if reserved: + destination.rmdir() + shutil.rmtree(staging, ignore_errors=True) + + +def read_state_recovery_audit(destination: Path) -> dict[str, Any]: + path = destination / "recovery-audit.json" + _physical_ancestors(path) + with _regular_input(path, MAX_MANIFEST_BYTES) as handle: + audit = _json(handle.read(MAX_MANIFEST_BYTES + 1)) + return _runtime("state.recovery.audit.readback", audit) + + +def render_state_recovery_markdown(payload: dict[str, Any]) -> str: + if payload.get("ok") is not True: + return f"Backup inspection failed: {payload.get('error_code', 'recovery_unavailable')}. No execution authority was granted." + if payload.get("status") == "planned": + return "\n".join([ + "Archive and manifest checks passed. No copy has been created and owner records have not been inspected.", + f"Saved files: {payload['file_count']}; logical bytes: {payload['logical_bytes']}", + "Repeat with --execute to create and inspect a new inert copy; this never activates a Goal.", + ]) + summary = payload.get("summary", {}) + return "\n".join([ + f"Backup inspection: {payload['status']}", + f"Recovery: {payload['recovery_id']}", + f"Readable: {summary.get('readable', 0)}; historical only: {summary.get('historical_only', 0)}; unknown: {summary.get('unknown', 0)}", + "The copy is inert. Goal execution and live settings were not activated.", + ]) diff --git a/tests/control_plane_ts/state_recovery.test.ts b/tests/control_plane_ts/state_recovery.test.ts new file mode 100644 index 0000000000..bd2eb40d66 --- /dev/null +++ b/tests/control_plane_ts/state_recovery.test.ts @@ -0,0 +1,75 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import {canonicalAuthoritySha256} from "../../loopx/control_plane/coordination/authority_store_codec.ts"; +import {createEffectRuntimeHandlers, dispatchEffectRuntimeMethod} from "../../loopx/control_plane/effect_runtime_handlers.ts"; +import {verifyStateRecoveryArchive, verifyStateRecoveryManifest} from "../../loopx/control_plane/state_recovery.ts"; +import {decodeStateRecoveryAudit, STATE_RECOVERY_AUDIT_SCHEMA} from "../../loopx/control_plane/state_recovery_contract.ts"; + +const member = {archive_path: "runtime-root/retained", size_bytes: 1, sha256: "c".repeat(64)}; +function request() { + const external = {schema_version: "loopx_state_backup_v0", ok: true, dry_run: false, + execute_requested: true, backup_id: "fixture", private_hint: "private-marker", + execution: {archive_sha256: "a".repeat(64), archive_size_bytes: 500, + file_members: [member], archive_to_logical_ratio: 0.5}}; + const embedded = structuredClone(external) as Record; + embedded.execution.archive_sha256 = embedded.execution.archive_size_bytes = null; + delete embedded.execution.archive_to_logical_ratio; + return {external_manifest: external, embedded_manifest: embedded, files: [member], + archive_sha256: "a".repeat(64), archive_size_bytes: 500, external_manifest_sha256: "b".repeat(64), + sqlite_member_count: 0}; +} + +test("archive and manifest checks bind observed bytes without projecting private capture metadata", () => { + const input = request(), before = structuredClone(input); + const first = verifyStateRecoveryArchive(input); + assert.equal(first.ok, true); + const verified = verifyStateRecoveryManifest(input); + assert.equal(verified.ok, true); + assert.equal(verified.file_witnesses_verified, true); + assert.equal(verified.manifest_verified, true); + assert.equal(verified.execution_authority_granted, false); + assert.equal(JSON.stringify(verified).includes("private-marker"), false); + assert.deepEqual(input, before); + assert.equal(verifyStateRecoveryArchive({...input, archive_sha256: "d".repeat(64)}).error_code, "archive_digest_mismatch"); + assert.equal(verifyStateRecoveryManifest({...input, files: [{...member, size_bytes: 2}]}).error_code, "member_witness_mismatch"); + assert.equal(verifyStateRecoveryManifest({...input, embedded_manifest: {...input.embedded_manifest, backup_id: "other"}}).error_code, "manifest_disagreement"); + assert.equal(verifyStateRecoveryManifest({...input, sqlite_member_count: 129}).error_code, "sqlite_inventory_capacity"); +}); + +test("legacy absent member witnesses stay unqualified rather than becoming verified by an empty list", () => { + const input = request(); + delete (input.external_manifest.execution as Record).file_members; + delete input.embedded_manifest.execution.file_members; + const result = verifyStateRecoveryManifest(input); + assert.equal(result.ok, true); + assert.equal(result.file_witnesses_verified, false); + assert.equal(result.capture_profile, "legacy_best_effort"); +}); + +function audit() { + const body = {ok: true, schema_version: STATE_RECOVERY_AUDIT_SCHEMA, + recovery_id: "e".repeat(64), status: "held", written: true, manifest_verified: true, + observed_at: "2026-10-12T00:00:00.000Z", execution_authority_granted: false, activation_eligible: false, + backup: {schema_version: "loopx_state_backup_v0", archive_sha256: "a".repeat(64), external_manifest_sha256: "b".repeat(64)}, + workspace: {isolation: "inert", registered: false}, + components: [{component_id: "configuration", owner_kind: "configuration", state: "readable", + reason_code: "configuration_digest_verified", next_action: "retain_isolated_configuration", facts: {}, dependency_ids: []}], + summary: {readable: 1, historical_only: 0, unknown: 0}, findings: []}; + return {...body, audit_sha256: canonicalAuthoritySha256(body)}; +} + +test("the packaged client and owner reject permission, unknown-state and summary contradictions", () => { + const input = audit(); + assert.doesNotThrow(() => decodeStateRecoveryAudit(input)); + for (const delta of [{execution_authority_granted: true}, {activation_eligible: true}, + {workspace: {isolation: "inert", registered: true}}, {summary: {...input.summary, readable: 2}}]) { + assert.throws(() => decodeStateRecoveryAudit({...input, ...delta})); + } + assert.throws(() => decodeStateRecoveryAudit({...input, components: [{...input.components[0], state: "active"}]})); + assert.throws(() => decodeStateRecoveryAudit({...input, components: [{...input.components[0], dependency_ids: ["missing"]}]})); +}); + +test("untrusted owner inspection has no shared-daemon dispatch", async () => { + const handlers = createEffectRuntimeHandlers({fingerprint: "recovery-isolation", requestShutdown() {}}); + await assert.rejects(() => dispatchEffectRuntimeMethod(handlers, "state.recovery.owners.audit", {}), /unsupported Effect runtime method/); +}); diff --git a/tests/test_state_recovery.py b/tests/test_state_recovery.py new file mode 100644 index 0000000000..49926c3d79 --- /dev/null +++ b/tests/test_state_recovery.py @@ -0,0 +1,366 @@ +from __future__ import annotations + +import hashlib +import io +import http.client +import json +import os +from pathlib import Path +import subprocess +import sqlite3 +import sys +import tarfile +import threading + +import pytest + +from loopx.state_backup import build_state_backup_plan, execute_state_backup_plan +from loopx.state_recovery import StateRecoveryError, read_state_recovery_audit, render_state_recovery_markdown, verify_state_backup +from tests.control_plane.canonical_authority_fixture import promoted_create_fixture + + +def _cli(*args: object) -> tuple[int, dict]: + result = subprocess.run([sys.executable, "-m", "loopx.cli", "--format", "json", *map(str, args)], + capture_output=True, text=True, env={**os.environ, "LOOPX_USAGE_PING": "0"}, timeout=90) + return result.returncode, json.loads(result.stdout) + + +def _backup(root: Path, provider: str | None = None) -> tuple[Path, Path]: + if provider: + registry, runtime, state = promoted_create_fixture(root, provider=provider) + project = state.parents[3] + code, result = _cli("--registry", registry, "--runtime-root", runtime, "todo", "add", + "--goal-id", "goal-a", "--role", "agent", "--claimed-by", "agent-a", + "--operation-id", "retained-work", "--text", "Keep this unfinished commitment") + assert code == 0, result + state.write_text(state.read_text() + "\n## Original commitments\nPreserve the full Goal, including unfinished acceptance.\n") + else: + project, runtime = root / "project", root / "runtime" + project.mkdir() + runtime.mkdir() + registry = root / "registry.json" + registry.write_text(json.dumps({"goals": []})) + (runtime / "retained.txt").write_text("saved data") + result = execute_state_backup_plan(build_state_backup_plan(project=project, runtime_root=runtime, + registry_path=registry, output_dir=root / "backups", backup_id="source", include_registry_projects=False, + include_automations=False, include_skills=False)) + return Path(result["archive_path"]), Path(result["manifest_path"]) + + +def _rewrite(archive: Path, manifest: Path, extra: tarfile.TarInfo | None = None, + body: bytes = b"", replacements: dict[str, bytes] | None = None) -> None: + with tarfile.open(archive, "r:gz") as source: + records = [(row, source.extractfile(row).read() if row.isfile() else None) for row in source] + with tarfile.open(archive, "w:gz") as target: + for row, data in records: + if replacements and row.name in replacements: + data = replacements[row.name] + row.size = len(data) + target.addfile(row, io.BytesIO(data) if data is not None else None) + if extra is not None: + target.addfile(extra, io.BytesIO(body) if extra.isfile() else None) + data = json.loads(manifest.read_text()) + data["execution"]["archive_sha256"] = hashlib.sha256(archive.read_bytes()).hexdigest() + data["execution"]["archive_size_bytes"] = archive.stat().st_size + manifest.write_text(json.dumps(data)) + + +@pytest.mark.parametrize("provider", ["file", "sqlite"]) +def test_real_backup_recovers_readable_authority_after_original_sources_disappear(tmp_path, provider): + archive, manifest = _backup(tmp_path, provider) + (tmp_path / "project").rename(tmp_path / "unavailable-project") + (tmp_path / "runtime").rename(tmp_path / "unavailable-runtime") + (tmp_path / "registry.json").rename(tmp_path / "unavailable-registry.json") + original = archive.read_bytes(), manifest.read_bytes() + destination = tmp_path / "inert" + preview = verify_state_backup(archive=archive, manifest=manifest, destination=destination) + assert preview["status"] == "planned" and preview["written"] is False + preview_text = render_state_recovery_markdown(preview) + assert "owner records have not been inspected" in preview_text and "Readable: 0" not in preview_text + assert not destination.exists() + code, audit = _cli("backup-state", "verify", "--archive", archive, "--manifest", manifest, + "--destination", destination, "--execute") + assert code == 0, audit + assert audit["execution_authority_granted"] is False and audit["activation_eligible"] is False + assert audit["workspace"] == {"isolation": "inert", "registered": False} + assert audit["status"] == "held" + [goal] = [row for row in audit["components"] if row["owner_kind"] == "coordination_authority"] + assert goal["state"] == "readable", goal + assert goal["facts"]["todo_count"] == 1 + assert goal["facts"]["history_commits"] == 2 + assert goal["facts"]["provider"] == provider + captures = json.loads(manifest.read_text())["included"] + state_path = next(row["archive_path"] for row in captures if row["key"] == "registry_active_state:goal-a") + assert "Preserve the full Goal, including unfinished acceptance." in (destination / "data" / state_path).read_text() + assert (archive.read_bytes(), manifest.read_bytes()) == original + assert json.loads((destination / "recovery-audit.json").read_text()) == audit + assert str(tmp_path) not in json.dumps(audit) + assert not (destination / "registry.global.json").exists() + for path in [destination, destination / "data", destination / "recovery-audit.json"]: + assert path.stat().st_mode & 0o077 == 0 + before = (destination / "recovery-audit.json").read_bytes() + with pytest.raises(StateRecoveryError, match="recovery_destination_exists"): + verify_state_backup(archive=archive, manifest=manifest, destination=destination, execute=True) + assert (destination / "recovery-audit.json").read_bytes() == before + + +def test_valid_internal_symlink_stays_inert_and_chained_escape_is_refused(tmp_path): + archive, manifest = _backup(tmp_path) + link = tarfile.TarInfo("readable-link") + link.type, link.linkname = tarfile.SYMTYPE, "runtime-root/retained.txt" + _rewrite(archive, manifest, link) + result = verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "valid", execute=True) + assert result["execution_authority_granted"] is False + assert (tmp_path / "valid/data/readable-link").is_symlink() + alias = tarfile.TarInfo("alias") + alias.type, alias.linkname = tarfile.SYMTYPE, "." + _rewrite(archive, manifest, alias) + escape = tarfile.TarInfo("escape-link") + escape.type, escape.linkname = tarfile.SYMTYPE, "alias/../outside" + _rewrite(archive, manifest, escape) + with pytest.raises(StateRecoveryError, match="archive_link_escapes"): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "invalid", execute=True) + assert not (tmp_path / "invalid").exists() + + +def test_capacity_and_private_input_fail_before_publication(tmp_path, monkeypatch): + import loopx.state_recovery as recovery + + archive, manifest = _backup(tmp_path) + original = archive.stat().st_mode + archive.chmod(0o644) + with pytest.raises(StateRecoveryError, match="input_not_private"): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "invalid", execute=True) + archive.chmod(original) + monkeypatch.setattr(recovery, "MAX_MEMBERS", 1) + with pytest.raises(StateRecoveryError, match="capacity_exceeded"): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "invalid", execute=True) + assert not (tmp_path / "invalid").exists() + + +def test_large_manifest_uses_existing_private_snapshot_transport(tmp_path): + archive, manifest = _backup(tmp_path) + external = json.loads(manifest.read_text()) + with tarfile.open(archive) as source: + embedded = json.load(source.extractfile("manifest.json")) + # Complete metadata exceeds the ordinary 2 MiB RPC; no wire ceiling is raised. + external["warnings"] = embedded["warnings"] = ["synthetic warning " * 150000] + manifest.write_text(json.dumps(external)) + _rewrite(archive, manifest, replacements={"manifest.json": json.dumps(embedded).encode()}) + result = verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "inert") + assert result["manifest_verified"] is True and not result["written"] + + +def test_changed_audit_and_true_execution_flag_are_not_trusted_on_readback(tmp_path): + archive, manifest = _backup(tmp_path) + destination = tmp_path / "inert" + result = verify_state_backup(archive=archive, manifest=manifest, destination=destination, execute=True) + path = destination / "recovery-audit.json" + changed = {**result, "observed_at": "2020-01-01T00:00:00.000Z"} + path.write_text(json.dumps(changed)) + with pytest.raises(StateRecoveryError, match="audit_digest_mismatch"): + read_state_recovery_audit(destination) + changed["execution_authority_granted"] = True + path.write_text(json.dumps(changed)) + with pytest.raises(StateRecoveryError, match="audit_invalid"): + read_state_recovery_audit(destination) + + +@pytest.mark.parametrize("provider", ["file", "sqlite"]) +def test_missing_selected_storage_is_unknown_without_creation_or_fallback(tmp_path, provider): + archive, manifest = _backup(tmp_path, provider) + destination = tmp_path / "inert" + original = verify_state_backup(archive=archive, manifest=manifest, destination=destination, execute=True) + from loopx.state_recovery import _audit_owners + + selected = destination / "data/runtime-root/authority" / f"{provider}-v0" + sqlite_files = [path.relative_to(destination / "data").as_posix() for path in selected.glob("*.sqlite")] + selected.rename(tmp_path / "unavailable-store") + observed = _audit_owners({"data_root": str(destination / "data"), + "verified": {key: original[key] for key in ("ok", "schema_version", "recovery_id", "backup", "file_count", + "file_witnesses_verified", "manifest_verified", "capture_profile", "execution_authority_granted")}, + "sqlite_members": sqlite_files, "standalone_sqlite_members": sqlite_files}) + [goal] = [row for row in observed["components"] if row["owner_kind"] == "coordination_authority"] + assert goal["state"] == "unknown" + assert observed["execution_authority_granted"] is False + assert not selected.exists() + if provider == "sqlite": + assert not (destination / "data/runtime-root/authority/file-v0").exists() + + +def test_restored_code_remains_unclassified_data_and_is_never_loaded(tmp_path): + archive, manifest = _backup(tmp_path) + # Re-capture through the real producer so the hostile file has valid witnesses. + marker = tmp_path / "executed" + (tmp_path / "runtime/untrusted_plugin.py").write_text(f"from pathlib import Path\nPath({str(marker)!r}).touch()\n") + result = execute_state_backup_plan(build_state_backup_plan(project=tmp_path / "project", runtime_root=tmp_path / "runtime", + registry_path=tmp_path / "registry.json", output_dir=tmp_path / "with-code", backup_id="code", + include_registry_projects=False, include_automations=False, include_skills=False)) + audit = verify_state_backup(archive=Path(result["archive_path"]), manifest=Path(result["manifest_path"]), + destination=tmp_path / "inert-code", execute=True) + assert not marker.exists() + [remaining] = [row for row in audit["components"] if row["component_id"] == "remaining_state"] + assert remaining["state"] == "unknown" and audit["execution_authority_granted"] is False + + +def test_sqlite_views_are_never_evaluated_as_authority_tables(tmp_path): + archive, manifest = _backup(tmp_path, "sqlite") + with tarfile.open(archive) as source: + database_member = next(row.name for row in source if row.name.endswith(".sqlite")) + database_bytes = source.extractfile(database_member).read() + embedded = json.load(source.extractfile("manifest.json")) + database_path = tmp_path / "view.sqlite" + database_path.write_bytes(database_bytes) + with sqlite3.connect(database_path) as database: + database.executescript("ALTER TABLE metadata RENAME TO saved_metadata; " + "CREATE VIEW metadata AS WITH RECURSIVE spin(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM spin) " + "SELECT *, (SELECT count(*) FROM spin) AS expensive FROM saved_metadata;") + changed = database_path.read_bytes() + external = json.loads(manifest.read_text()) + for envelope in (external, embedded): + witness = next(row for row in envelope["execution"]["file_members"] if row["archive_path"] == database_member) + witness.update(sha256=hashlib.sha256(changed).hexdigest(), size_bytes=len(changed)) + manifest.write_text(json.dumps(external)) + _rewrite(archive, manifest, replacements={database_member: changed, "manifest.json": json.dumps(embedded).encode()}) + result = verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "view-inert", execute=True) + [goal] = [row for row in result["components"] if row["owner_kind"] == "coordination_authority"] + assert goal["state"] == "unknown" + assert goal["reason_code"] == "sqlite_requires_owner_recovery" + + +def test_offline_worker_timeout_does_not_leave_a_copy_or_stop_shared_runtime(tmp_path, monkeypatch): + import loopx.state_recovery as recovery + from loopx.control_plane.effect_runtime import effect_runtime_result + + archive, manifest = _backup(tmp_path) + monkeypatch.setattr(recovery, "_OWNER_TIMEOUT_SECONDS", 0.001) + with pytest.raises(StateRecoveryError, match="recovery_owner_timeout"): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "timed-out", execute=True) + assert not (tmp_path / "timed-out").exists() + assert not list(tmp_path.glob(".loopx-recovery-*")) + # This is still the real shared helper, not a fabricated availability result. + normal = effect_runtime_result("configuration.backup", {"action": "capture", "data": {"machine_configuration": None, "goals": []}}) + assert normal["activation_performed"] is False + + +def test_digest_refusal_precedes_tar_decoding_and_writes_nothing(tmp_path): + archive, manifest = _backup(tmp_path) + archive.write_bytes(b"not a tar archive") + with pytest.raises(StateRecoveryError, match="archive_digest_mismatch"): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "inert", execute=True) + assert not (tmp_path / "inert").exists() + assert not list(tmp_path.glob(".loopx-recovery-*")) + + +def test_external_manifest_cannot_relabel_the_embedded_capture(tmp_path): + archive, manifest = _backup(tmp_path) + data = json.loads(manifest.read_text()) + data["backup_id"] = "different-backup" + manifest.write_text(json.dumps(data)) + with pytest.raises(StateRecoveryError, match="manifest_disagreement"): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "inert", execute=True) + assert not (tmp_path / "inert").exists() + + +def test_recomputed_archive_digest_does_not_hide_member_corruption(tmp_path): + archive, manifest = _backup(tmp_path) + _rewrite(archive, manifest, replacements={"runtime-root/retained.txt": b"altered data"}) + with pytest.raises(StateRecoveryError, match="member_witness_mismatch"): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "inert", execute=True) + + +@pytest.mark.parametrize("name,kind,target,reason", [ + ("../escape", tarfile.REGTYPE, "", "archive_path_invalid"), + ("/absolute", tarfile.REGTYPE, "", "archive_path_invalid"), + ("runtime-root/RETAINED.txt", tarfile.REGTYPE, "", "archive_name_collision"), + ("runtime-root/retained.txt", tarfile.REGTYPE, "", "archive_name_collision"), + ("fifo", tarfile.FIFOTYPE, "", "archive_member_type_unsupported"), + ("hardlink", tarfile.LNKTYPE, "runtime-root/retained.txt", "archive_member_type_unsupported"), + ("outside", tarfile.SYMTYPE, "../escape", "archive_link_escapes"), + ("runtime-root", tarfile.SYMTYPE, "project", "archive_name_collision"), +]) +def test_archive_paths_and_member_types_fail_before_destination_creation(tmp_path, name, kind, target, reason): + archive, manifest = _backup(tmp_path) + member = tarfile.TarInfo(name) + member.type, member.linkname = kind, target + _rewrite(archive, manifest, member) + with pytest.raises(StateRecoveryError, match=reason): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "inert", execute=True) + assert not (tmp_path / "inert").exists() + assert not (tmp_path.parent / "escape").exists() + + +def test_large_pax_metadata_is_refused_before_tarfile_decodes_its_body(tmp_path): + archive, manifest = _backup(tmp_path) + member = tarfile.TarInfo("extended-metadata") + member.pax_headers = {"comment": "x" * (128 * 1024 + 1)} + _rewrite(archive, manifest, member) + with pytest.raises(StateRecoveryError, match="archive_metadata_too_large"): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "invalid", execute=True) + assert not (tmp_path / "invalid").exists() + + +def test_symlink_parent_and_occupied_directory_are_not_recovery_targets(tmp_path): + archive, manifest = _backup(tmp_path) + link = tmp_path / "alias" + link.symlink_to(tmp_path / "project", target_is_directory=True) + with pytest.raises(StateRecoveryError, match="parent_not_physical"): + verify_state_backup(archive=archive, manifest=manifest, destination=link / "new", execute=True) + occupied = tmp_path / "occupied" + occupied.mkdir() + (occupied / "keep").write_text("unchanged") + with pytest.raises(StateRecoveryError, match="destination_exists"): + verify_state_backup(archive=archive, manifest=manifest, destination=occupied, execute=True) + assert (occupied / "keep").read_text() == "unchanged" + + +def test_real_http_upload_verify_reload_and_corrupt_input_keep_live_state_unchanged(tmp_path): + from loopx.chat_server import ChatHTTPServer, ChatRequestHandler + + archive, manifest = _backup(tmp_path) + runtime = tmp_path / "replacement-runtime" + runtime.mkdir() + server = ChatHTTPServer(("127.0.0.1", 0), ChatRequestHandler) + server.runtime_root, server.registry_path, server.verbose = runtime, tmp_path / "absent-registry.json", False + worker = threading.Thread(target=server.serve_forever, daemon=True) + worker.start() + + def request(path, body=None, *, binary=False): + connection = http.client.HTTPConnection("127.0.0.1", server.server_address[1], timeout=90) + encoded = body if binary else json.dumps(body).encode() if body is not None else None + connection.request("POST" if body is not None else "GET", "/api/chat/state-recovery" + path, + body=encoded, headers={"Content-Type": "application/octet-stream" if binary else "application/json"}) + response = connection.getresponse() + payload = json.loads(response.read()) + status = response.status + connection.close() + return status, payload + + try: + assert request("")[1]["items"] == [] + assert not (runtime / "backups").exists() + _, first = request("/archive", archive.read_bytes(), binary=True) + _, second = request("/manifest", manifest.read_bytes(), binary=True) + inputs = {"archive_ref": first["input_ref"], "manifest_ref": second["input_ref"], "execute": True} + status, result = request("/verify", inputs) + assert status == 200, result + assert result["execution_authority_granted"] is False + assert not server.registry_path.exists() + status, rows = request("") + assert status == 200 and rows["items"][0]["checkpoint_ref"] == result["checkpoint_ref"] + read = request("/read", {"checkpoint_ref": result["checkpoint_ref"]})[1] + assert read["historical_readback"] is True and read["audit_sha256"] == result["audit_sha256"] + duplicate_status, duplicate = request("/verify", inputs) + assert duplicate_status == 400 and duplicate["error_code"] == "recovery_destination_exists" + checkpoint = runtime / "backups/state-recovery/checkpoints" / result["checkpoint_ref"] + before = (checkpoint / "recovery-audit.json").read_bytes() + _, damaged = request("/archive", b"damaged archive", binary=True) + status, rejected = request("/verify", {**inputs, "archive_ref": damaged["input_ref"]}) + assert status == 400 and rejected["error_code"] == "archive_digest_mismatch" + assert (checkpoint / "recovery-audit.json").read_bytes() == before + assert len(list((runtime / "backups/state-recovery/checkpoints").iterdir())) == 1 + assert not server.registry_path.exists() + finally: + server.shutdown() + worker.join(timeout=5) + server.server_close() From f1164455d12c596658ca1ed551d9f0b7384424fb Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 07:16:05 +0800 Subject: [PATCH 02/13] docs(recovery): describe offline inspection and retained owner holds Signed-off-by: song --- .../rfcs/complete-state-recovery-v0.md | 14 +++- .../rfcs/complete-state-recovery-v0.zh-CN.md | 11 ++- docs/reference/configuration-backup.md | 82 +++++++++++++++++++ docs/reference/glossary.md | 8 ++ 4 files changed, 113 insertions(+), 2 deletions(-) diff --git a/docs/architecture/rfcs/complete-state-recovery-v0.md b/docs/architecture/rfcs/complete-state-recovery-v0.md index a3e23b57f7..a4be3504bf 100644 --- a/docs/architecture/rfcs/complete-state-recovery-v0.md +++ b/docs/architecture/rfcs/complete-state-recovery-v0.md @@ -2,7 +2,7 @@ - **RFC status:** Accepted - **Supersedes / closes:** none -- **Delivery maturity:** Proposal; backup creation and configuration-only isolated recovery exist, complete state verification and reactivation do not +- **Delivery maturity:** Partial: bounded M1 full-archive verification and inert File/SQLite inspection implemented; controlled reactivation remains open - **Authors / owners:** Control-plane state, reliability, and affected domain maintainers - **Created:** 2026-10-08 - **Last normative revision:** 2026-10-08 @@ -24,6 +24,18 @@ transfer, or a default change. ## 1. Decision summary +### Current implementation checkpoint + +The bounded M1 path now has additive `backup-state verify`, loopback HTTP upload +and immutable report readback, and a packaged Settings/Capability Center journey. +It verifies real producer archives and reads captured configuration plus selected +File/SQLite Todo/history through existing owners after original sources become +unavailable. Every v0 audit remains held for incomplete capture/owner metadata; +unknown state is not relabeled recoverable, and execution authority is always +false. Limits, rollback and reproduction are in the [backup guide](../../reference/configuration-backup.md#inspect-a-complete-backup-without-activating-it). +This checkpoint does not qualify M2/M3, live adoption, remote providers or the G1 +team journey. No original identity or receipt is rewritten. + ### First supported user outcome The first bounded user promise is recovery of one local Goal after its source diff --git a/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md b/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md index 712253f715..b3699465cc 100644 --- a/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md +++ b/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md @@ -2,7 +2,7 @@ - **RFC 状态:** 已接受 - **替代 / 关闭:** 无 -- **交付成熟度:** 提案;备份创建和仅配置的隔离恢复已存在,完整状态验证与重新激活尚不存在 +- **交付成熟度:** 部分实现:M1 完整归档验证与 File/SQLite 隔离检查已实现;受控重新启用仍开放 - **作者 / 负责人:** 控制面状态、可靠性及受影响领域的维护者 - **创建:** 2026-10-08 - **最近规范修订:** 2026-10-08 @@ -20,6 +20,15 @@ RFC 只授权有界实现切片,不授权真实恢复、provider 晋升、凭 ## 1. 决策摘要 +### 当前实现检查点 + +M1 的有界路径已包含增量 `backup-state verify`、loopback HTTP 上传及不可变报告读回, +以及打包设置/能力中心交互。真实生产归档在原来源不可达后,通过既有配置与选定的 +File/SQLite Todo/history owner 检查。v0 缺少完整捕获/owner 元数据,因此所有审计仍为 +held,未知状态不改称可恢复,执行权限始终为 false。限制与回退见[备份指南](../../reference/configuration-backup.md#inspect-a-complete-backup-without-activating-it)。 +本检查点不证明 M2/M3、live adoption、远端 provider 或 G1 团队验收,不重写原身份或回执。 + + ### 首个支持的用户结果 首个有界用户承诺是:一个本地 Goal 的来源工作区不可用或不再可信后,用户可以恢复 diff --git a/docs/reference/configuration-backup.md b/docs/reference/configuration-backup.md index e7568cf42c..cfbc64f09f 100644 --- a/docs/reference/configuration-backup.md +++ b/docs/reference/configuration-backup.md @@ -130,6 +130,88 @@ register it as a second live Goal or start a Host using an old grant. Explicit identity/path adoption, pending-effect disposition and authorized reactivation, including their App journey, remain separate acceptance work. +## Inspect a complete backup without activating it + +In **Settings → Capability Center → device defaults → Configuration backup and +recovery**, choose **Full archive inspection**. Select the original `.tar.gz` +and matching external `.manifest.json`, then choose **Verify and create isolated +copy**. The existing configuration-snapshot option remains separate. Full-archive +inspection is a machine-level operation; it is not shown in single-Goal scope. +Unsupported services/platforms keep the operation unavailable. + +The local server streams the two uploads into private temporary inputs. It checks +the archive digest before decoding tar data, then checks embedded/external +manifest agreement, paths, links, member hashes and extraction capacity. Only a +new inert checkpoint can be published. It reads configuration and selected +File/SQLite authority through their existing owners, including the complete Todo +read model and committed history. Original registry paths are never followed. + +The result separates **readable**, **historical only** and **unknown** components, +with an owner and one next action. SQLite bytes alone do not prove a readable Goal +work model. Remaining v0 state has no complete owner inventory, so it stays +unknown; every v0 audit carries `legacy_manifest_incomplete`, stays `held`, and +reports `execution_authority_granted=false` and `activation_eligible=false`. +No Goal is registered, no provider is selected, and no Host, lease, timer or +pending external operation is resumed. This is the M1 inspection path in the +[complete recovery RFC](../architecture/rfcs/complete-state-recovery-v0.md), +not controlled reactivation. + +Use **Previous inspections** after reloading to read the saved, digest-checked +report. A historical report describes its recorded observation time; reading it +again does not reverify copied data. A damaged new input clears the prior success +view and leaves existing checkpoints intact. If a response was lost, **Check for +an existing result** reads the original checkpoint; a repeated write to that +occupied target is refused. + +The additive CLI route preserves existing backup creation syntax: + +```bash +uv run --extra test loopx --format json backup-state verify \ + --archive BACKUP.tar.gz --manifest BACKUP.manifest.json \ + --destination NEW_PHYSICAL_DIRECTORY + +# Explicitly create and inspect that new inert copy. +uv run --extra test loopx --format json backup-state verify \ + --archive BACKUP.tar.gz --manifest BACKUP.manifest.json \ + --destination NEW_PHYSICAL_DIRECTORY --execute +``` + +Preview checks archive/manifest bytes and capacity; owner records are not yet +read and no zero-readability result is invented. Execution writes `data/` and +`recovery-audit.json` below the new destination. Source archive and manifest must +be private regular files; the destination must not exist and its ancestors must +be physical directories. Existing targets are never replaced. Browser callers +use opaque upload/checkpoint refs and cannot supply filesystem destinations. +Uploads expire after an hour and are cleaned by a later upload; completed audits +are retained. Removing these inert copies cannot undo or authorize live state. + +The first profile is POSIX with local File/SQLite state. Bounds are 128 MiB +compressed archive, 512 MiB expanded tar stream, 16 MiB external/embedded +manifest, 20,000 entries, expansion ratio 2,000, 128 KiB per extended tar header +and 16 MiB total extended metadata. Paths have at most 64 components, 1,024 +characters and 255 UTF-8 bytes per component; JSON nesting is bounded at 64. +Owner inspection permits 128 Goals, 128 SQLite files and 20,000 committed +transactions per Goal. Overflow refuses publication rather than truncating a +required inventory. Internal symlinks remain inert; escaping/cyclic links, +hard links, sparse or special files and normalized name collisions reject. + +Archive parsing never loads restored code. Database/domain inspection runs in a +disposable verifier with a 90-second deadline and a 512 MiB V8 heap ceiling. +SQLite gets a 64 MiB process-local [hard heap limit](https://www.sqlite.org/pragma.html#pragma_hard_heap_limit); +this is why that verifier is not dispatched in the shared Effect daemon. Views +and virtual tables cannot masquerade as authority tables. WAL-mode copies and +unsupported/service-owned providers remain unknown and need their existing +owner; PostgreSQL is never contacted. The normal 2 MiB RPC wire is unchanged; +only the two complete-manifest checks opt into the existing 64 MiB private-file +transport. These bounds qualify this inspection profile, not a production SLO. + +中文:在设置的能力中心选择设备范围,打开既有备份入口并切到“完整归档检查”, +选择同一份备份的归档与清单,一次明确操作建立隔离副本。可读、仅历史和未知分别 +显示;旧格式始终保留一致性/owner 缺口,不能据此启动 Goal。重载后可读历史报告, +但历史读回不等于重新验证副本。CLI 不带 `--execute` 只做计划,不虚构 owner 读取结果。 +当前支持本地 POSIX File/SQLite 检查;Windows、服务端 PostgreSQL、身份接纳和实际 +重新启用仍未取得此路径的资格。备份内可能包含私有数据和凭据,副本及上传始终私有。 + ## Privacy, consistency and limits Backups are **private by default**. Full Goal rows can contain private paths, diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md index 3abbf0a74d..6e0ffd3307 100644 --- a/docs/reference/glossary.md +++ b/docs/reference/glossary.md @@ -184,6 +184,14 @@ Effect-program settlement step executed for one Turn. - typescript: [`SETTLEMENT_STEP_KINDS`](../../loopx/control_plane/effect_program.ts). - Values / 值: `validation`, `durable_writeback`, `quota_spend`, `terminal_closeout`. +## state_recovery_component_state + +Offline owner-local readability of a component in an inert recovery audit; never execution or adoption authority. + +- Tier / 层级: `cross_runtime`; status / 状态: `canonical`. +- typescript: [`RECOVERY_COMPONENT_STATES`](../../loopx/control_plane/state_recovery_contract.ts). +- Values / 值: `readable`, `historical_only`, `unknown`. + ## task_lease_lifecycle_operation Operation accepted by the shipped native lease lifecycle request decoder; acquisition has its own transaction. From 1bd828ba27e4d8947685c769bdae791c708f2041 Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 07:28:59 +0800 Subject: [PATCH 03/13] test(recovery): qualify installed File and SQLite inspection journeys Signed-off-by: song --- .../rfcs/loopx-overall-roadmap-v0.md | 13 ++++++++++--- .../rfcs/loopx-overall-roadmap-v0.zh-CN.md | 6 +++++- docs/reference/configuration-backup.md | 9 +++++++++ .../state-recovery/before-configuration.png | Bin 0 -> 132197 bytes .../state-recovery/historical-audit-phone.png | Bin 0 -> 60812 bytes .../state-recovery/inert-audit-desktop.png | Bin 0 -> 125930 bytes .../configuration-backup.mjs | 13 ++++++++----- 7 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 docs/reference/images/state-recovery/before-configuration.png create mode 100644 docs/reference/images/state-recovery/historical-audit-phone.png create mode 100644 docs/reference/images/state-recovery/inert-audit-desktop.png diff --git a/docs/architecture/rfcs/loopx-overall-roadmap-v0.md b/docs/architecture/rfcs/loopx-overall-roadmap-v0.md index 9ac95f0b1c..957395f221 100644 --- a/docs/architecture/rfcs/loopx-overall-roadmap-v0.md +++ b/docs/architecture/rfcs/loopx-overall-roadmap-v0.md @@ -1050,9 +1050,16 @@ bytes. Readback runs with the original project/runtime unreachable; missing selected-provider storage fails without replacement or File fallback. The pre-import backup separately retains original Markdown before later projection. This qualifies independent data/history recovery, not identity adoption, pending -effect disposition or live Host reactivation. Continue those boundaries and the -packaged App recovery journey through the existing owners. Do not retire the last -normal Markdown writer or claim the released SQLite default from this checkpoint. +effect disposition or live Host reactivation. The bounded complete-recovery M1 +inspection now adds `backup-state verify` and a packaged Settings journey: a new +inert copy, existing File/SQLite Todo/history readers, and explicit readable, +historical and unknown components. Corrupt inputs, occupied targets and unsafe +archive paths refuse; a bounded offline worker isolates database verification. +Source and installed-wheel journeys retain old Goal bytes and never register or +activate the copy. Legacy capture/owner gaps keep every v0 audit held. Continue +identity, pending-effect disposition and authorized adoption through the existing +owners; do not retire the last normal Markdown writer or claim the released +SQLite default from this inspection checkpoint. The qualified shadow→canonical archive→isolated File/SQLite restore path retains committed dependency archives, terminal lease facts and later canonical writes. diff --git a/docs/architecture/rfcs/loopx-overall-roadmap-v0.zh-CN.md b/docs/architecture/rfcs/loopx-overall-roadmap-v0.zh-CN.md index 7a7ee368fb..a614fcba7c 100644 --- a/docs/architecture/rfcs/loopx-overall-roadmap-v0.zh-CN.md +++ b/docs/architecture/rfcs/loopx-overall-roadmap-v0.zh-CN.md @@ -671,7 +671,11 @@ File/SQLite 完整 head、全部 committed journal、未引用的归档要求、 capture/rollback/已释放 lease 字节。回读前让原项目与 runtime 不可访问;选定 provider 存储缺失时明确失败,不新建替代库或回退 File。导入前备份另行保留投影前的原始 Markdown。这资格化独立数据与历史回读,不代表身份采用、pending effect 处置或 -Host 重新激活。继续既有 R5/T4 的这些边界和 packaged App 恢复旅程,通过后才退役 +Host 重新激活。有界的完整恢复 M1 检查现增加 `backup-state verify` 与打包设置旅程: +建立新隔离副本,复用 File/SQLite Todo/history reader,区分可读、仅历史与未知。 +损坏输入、已有目标及危险路径被拒绝,数据库验证在有时间/内存边界的独立进程内进行。 +源码与隔离 wheel 旅程保留原 Goal 字节,不注册或启用副本;v0 的 capture/owner 缺口使 +审计保持 held。继续既有 R5/T4 的身份、pending effect 与授权采用边界,通过后才退役 受影响旧 writer;D2 与发布默认值继续开放。详见 [全量状态恢复边界](../../reference/configuration-backup.md#full-state-recovery-after-cold-import)。 diff --git a/docs/reference/configuration-backup.md b/docs/reference/configuration-backup.md index cfbc64f09f..597a927c9e 100644 --- a/docs/reference/configuration-backup.md +++ b/docs/reference/configuration-backup.md @@ -156,6 +156,15 @@ pending external operation is resumed. This is the M1 inspection path in the [complete recovery RFC](../architecture/rfcs/complete-state-recovery-v0.md), not controlled reactivation. +The following public synthetic scene uses a built wheel and the real File +backend. Before this change, the entry only restored configuration snapshots. +The new path reads the saved Goal work after its original source was removed; +it does not register that Goal. The phone view is explicitly a historical report. + +| Before: configuration only | Inert full-archive inspection | Phone historical readback | +| --- | --- | --- | +| ![Configuration-only entry](images/state-recovery/before-configuration.png) | ![Inert backup audit](images/state-recovery/inert-audit-desktop.png) | ![Historical audit on phone](images/state-recovery/historical-audit-phone.png) | + Use **Previous inspections** after reloading to read the saved, digest-checked report. A historical report describes its recorded observation time; reading it again does not reverify copied data. A damaged new input clears the prior success diff --git a/docs/reference/images/state-recovery/before-configuration.png b/docs/reference/images/state-recovery/before-configuration.png new file mode 100644 index 0000000000000000000000000000000000000000..3e958ae301750842bb9428f89330fc429a5f6af9 GIT binary patch literal 132197 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%0A~l?H=|o;fB!`| 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 literal 0 HcmV?d00001 diff --git a/docs/reference/images/state-recovery/historical-audit-phone.png b/docs/reference/images/state-recovery/historical-audit-phone.png new file mode 100644 index 0000000000000000000000000000000000000000..632b5186a6e6726f4b858353a6fec09aca709a85 GIT binary patch literal 60812 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 literal 0 HcmV?d00001 diff --git a/docs/reference/images/state-recovery/inert-audit-desktop.png b/docs/reference/images/state-recovery/inert-audit-desktop.png new file mode 100644 index 0000000000000000000000000000000000000000..fcfe6b6ffadd45b6b1b7cddd01a5c71e46c602a7 GIT binary patch literal 125930 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Ž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�M8|EsA0az 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 {ZEe+ejj(dW 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%0Afcm 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 document.documentElement.scrollWidth > window.innerWidth), false); await recovery.getByRole("status").scrollIntoViewIfNeeded(); await page.screenshot({path: resolve(outputDir, "state-recovery-mobile.png"), animations: "disabled"}); + await recovery.getByRole("status").evaluate(element => element.scrollIntoView({block: "start"})); + await page.screenshot({path: resolve(outputDir, "state-recovery-mobile-result.png"), animations: "disabled"}); await recovery.getByRole("button", {name: "历史检查结果", exact: true}).click(); await recovery.getByLabel("历史备份检查", {exact: true}).getByRole("button").click(); await page.setViewportSize({width: 1512, height: 982}); @@ -162,7 +165,7 @@ export const configurationBackupScenario = { await english.getByText("Historical inspection loaded; the copy has not been reverified.", {exact: true}).waitFor(); await english.getByRole("status").scrollIntoViewIfNeeded(); await page.screenshot({path: resolve(outputDir, "state-recovery-english.png"), animations: "disabled"}); - return {coverageEntries: context.coverageEntries, errors: context.errors, backend, verified: "Packaged configuration recovery and full archive inspection; real SQLite head/history after original-source loss, no Goal registration, keyboard, corrupt-input recovery, immutable history reload and narrow viewport"}; + return {coverageEntries: context.coverageEntries, errors: context.errors, backend, verified: `Packaged configuration recovery and full archive inspection; real ${backend.source_provider} head/history after original-source loss, no Goal registration, keyboard, corrupt-input recovery, immutable history reload and narrow viewport`}; } finally { try {await context?.close();} finally { if (!closed) { From 81cb60f5100d9551a6b5412afc7d63a03b1ef5d0 Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 07:35:21 +0800 Subject: [PATCH 04/13] docs(recovery): distinguish the design baseline from current inspection Signed-off-by: song --- docs/architecture/rfcs/complete-state-recovery-v0.md | 10 +++++----- .../rfcs/complete-state-recovery-v0.zh-CN.md | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/architecture/rfcs/complete-state-recovery-v0.md b/docs/architecture/rfcs/complete-state-recovery-v0.md index a4be3504bf..d3d84ea6b7 100644 --- a/docs/architecture/rfcs/complete-state-recovery-v0.md +++ b/docs/architecture/rfcs/complete-state-recovery-v0.md @@ -207,11 +207,11 @@ Other files and directories are traversed while the source may remain live. The command therefore has component-local SQLite snapshot semantics and best-effort file capture, not a cross-owner point-in-time guarantee. -`loopx backup-state` supports plan and create. It does not verify an existing -archive, safely materialize one, classify state owners, reconcile references, -or reactivate a runtime. Current tests extract with the Python tar API, validate -SQLite integrity and content, and verify publication/permission behavior. They -do not establish a restore contract. +At the design baseline, `loopx backup-state` supported plan/create only, and +tests manually extracted archives. The current bounded M1 route in Section 1 +adds verified inert extraction and existing-owner readback through CLI and the +packaged UI. It does not adopt identity, reconcile pending effects or reactivate +a runtime; those later owner boundaries remain open. Configuration backup separately supports export, verify, and isolated restore. Its checkpoint is inert: it creates no live registry, provider selection, diff --git a/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md b/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md index b3699465cc..f6116a7ef1 100644 --- a/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md +++ b/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md @@ -172,10 +172,10 @@ digest 和大小,并排除 WAL/SHM sidecar。其他文件和目录可能在来 因此,该命令只具备组件本地 SQLite snapshot 语义和 best-effort 文件捕获,不具备 跨 owner point-in-time 保证。 -`loopx backup-state` 支持 plan 和 create。它不能验证已有归档、安全物化归档、 -分类状态 owner、reconcile reference 或重新激活 runtime。当前测试用 Python tar -API 提取,验证 SQLite integrity 与内容,并检查发布/权限行为;它们没有建立 -restore 契约。 +设计基线中的 `loopx backup-state` 只有 plan/create,测试通过 Python tar API 手工解包。 +第 1 节记录的当前有界 M1 路径已通过 CLI 和打包 UI 提供验证后的隔离解包及既有 owner +读回。它不采用身份、不处置 pending effect、不重新激活 runtime;这些后续 owner 边界 +仍保持开放。 配置备份另行支持 export、verify 和 isolated restore。其 checkpoint 是惰性的: 它不会创建 live registry、provider selection、writer fence、lease、Host session、 From 0078cc23073bd30e5da5ec8dd5ae0444750fb2bf Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 07:50:52 +0800 Subject: [PATCH 05/13] fix(recovery): reject sparse decoding and preserve failure cleanup Signed-off-by: song --- loopx/state_recovery.py | 29 +++++++++++---- tests/test_state_recovery.py | 70 ++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/loopx/state_recovery.py b/loopx/state_recovery.py index b006e5635a..20d63b307e 100644 --- a/loopx/state_recovery.py +++ b/loopx/state_recovery.py @@ -20,7 +20,7 @@ import tempfile import unicodedata import zlib -from typing import Any, BinaryIO, Iterator +from typing import Any, BinaryIO, Iterator, NoReturn from .control_plane.effect_runtime import _node_executable, effect_runtime_result, effect_runtime_request_scope @@ -143,9 +143,20 @@ def read(self, size: int = -1) -> bytes: class _RecoveryTarInfo(tarfile.TarInfo): + def _reject_sparse(self, *_args: object) -> NoReturn: + raise StateRecoveryError("archive_sparse_unsupported") + + # The stdlib invokes these hooks before yielding the final member. Refuse + # unsupported extents before their metadata/body is decoded or expanded. + _proc_gnusparse_00 = _reject_sparse + _proc_gnusparse_01 = _reject_sparse + _proc_gnusparse_10 = _reject_sparse + def _proc_member(self, archive: tarfile.TarFile) -> tarfile.TarInfo | None: + _require(self.type != tarfile.GNUTYPE_SPARSE, "archive_sparse_unsupported") # Bound extended metadata before tarfile allocates/decodes its body. - if self.type in (tarfile.XHDTYPE, tarfile.XGLTYPE, tarfile.GNUTYPE_LONGNAME, tarfile.GNUTYPE_LONGLINK): + if self.type in (tarfile.XHDTYPE, tarfile.XGLTYPE, tarfile.SOLARIS_XHDTYPE, + tarfile.GNUTYPE_LONGNAME, tarfile.GNUTYPE_LONGLINK): _require(self.size <= 128 * 1024, "archive_metadata_too_large") if isinstance(archive, _RecoveryTarFile): archive.recovery_metadata_bytes += self.size @@ -307,7 +318,10 @@ def _scan(handle: BinaryIO, archive_size: int, output: Path | None = None) -> di # Consume gzip EOF too: truncated CRC/trailing expansion is not a valid tar. while stream.read(_CHUNK): pass - except (tarfile.TarError, EOFError, gzip.BadGzipFile, OSError, zlib.error) as exc: + except StateRecoveryError: + raise + except (tarfile.TarError, EOFError, gzip.BadGzipFile, OSError, zlib.error, + ValueError, OverflowError, KeyError, RecursionError) as exc: raise StateRecoveryError("archive_unreadable") from exc finally: stream.source.close() @@ -383,9 +397,12 @@ def verify_state_backup( reserved = False return read_state_recovery_audit(destination) finally: - if reserved: - destination.rmdir() - shutil.rmtree(staging, ignore_errors=True) + try: + if reserved: + # Never recursively remove a destination occupied meanwhile. + destination.rmdir() + finally: + shutil.rmtree(staging, ignore_errors=True) def read_state_recovery_audit(destination: Path) -> dict[str, Any]: diff --git a/tests/test_state_recovery.py b/tests/test_state_recovery.py index 49926c3d79..53c01db42f 100644 --- a/tests/test_state_recovery.py +++ b/tests/test_state_recovery.py @@ -1,6 +1,7 @@ from __future__ import annotations import hashlib +import gzip import io import http.client import json @@ -300,6 +301,75 @@ def test_large_pax_metadata_is_refused_before_tarfile_decodes_its_body(tmp_path) assert not (tmp_path / "invalid").exists() +@pytest.mark.parametrize("kind,headers,decoder", [ + (tarfile.GNUTYPE_SPARSE, {}, "_proc_sparse"), + (tarfile.REGTYPE, {"GNU.sparse.size": "invalid"}, "_proc_gnusparse_00"), + (tarfile.REGTYPE, {"GNU.sparse.map": "invalid"}, "_proc_gnusparse_01"), + (tarfile.REGTYPE, {"GNU.sparse.major": "1", "GNU.sparse.minor": "0"}, "_proc_gnusparse_10"), +]) +def test_sparse_records_are_refused_before_stdlib_extent_decoding(tmp_path, monkeypatch, kind, headers, decoder): + archive, manifest = _backup(tmp_path) + member = tarfile.TarInfo("unsupported-sparse") + member.type, member.pax_headers = kind, headers + _rewrite(archive, manifest, member) + + # Malformed sparse metadata must produce the same bounded refusal as a + # valid unsupported record, rather than leaking a decoder exception. + with pytest.raises(StateRecoveryError, match="^archive_sparse_unsupported$"): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "inert", execute=True) + + def unexpected_decode(*_args): + pytest.fail("unsupported sparse extents reached the stdlib decoder") + + monkeypatch.setattr(tarfile.TarInfo, decoder, unexpected_decode) + with pytest.raises(StateRecoveryError, match="^archive_sparse_unsupported$"): + verify_state_backup(archive=archive, manifest=manifest, destination=tmp_path / "inert", execute=True) + assert not (tmp_path / "inert").exists() + assert not list(tmp_path.glob(".loopx-recovery-*")) + + +@pytest.mark.parametrize("kind", [tarfile.XHDTYPE, tarfile.XGLTYPE, tarfile.SOLARIS_XHDTYPE]) +def test_oversized_pax_variants_are_refused_without_reading_the_body(kind): + from loopx.state_recovery import _scan + + member = tarfile.TarInfo("oversized-pax") + member.type, member.size = kind, 128 * 1024 + 1 + compressed = gzip.compress(member.tobuf()) + with pytest.raises(StateRecoveryError, match="^archive_metadata_too_large$"): + _scan(io.BytesIO(compressed), len(compressed)) + + +def test_malformed_pax_record_length_is_a_bounded_archive_error(): + from loopx.state_recovery import _scan + + payload = b"9" * 5000 + b" comment=invalid\n" + member = tarfile.TarInfo("malformed-pax") + member.type, member.size = tarfile.XHDTYPE, len(payload) + raw = member.tobuf() + payload + b"\0" * (-len(payload) % 512 + 1024) + compressed = gzip.compress(raw) + with pytest.raises(StateRecoveryError, match="^archive_unreadable$"): + _scan(io.BytesIO(compressed), len(compressed)) + + +def test_failed_publication_cleans_staging_even_when_destination_becomes_occupied(tmp_path, monkeypatch): + import loopx.state_recovery as recovery + + archive, manifest = _backup(tmp_path) + destination = tmp_path / "inert" + + def failed_rename(source, target): + assert target == destination and (source / "recovery-audit.json").is_file() + (destination / "foreign-file").write_text("preserve this") + raise OSError("publication interrupted") + + monkeypatch.setattr(recovery.os, "rename", failed_rename) + with pytest.raises(OSError): + verify_state_backup(archive=archive, manifest=manifest, destination=destination, execute=True) + assert (destination / "foreign-file").read_text() == "preserve this" + assert not (destination / "recovery-audit.json").exists() + assert not list(tmp_path.glob(".loopx-recovery-*")) + + def test_symlink_parent_and_occupied_directory_are_not_recovery_targets(tmp_path): archive, manifest = _backup(tmp_path) link = tmp_path / "alias" From f5a8b76dd31d34dac43bccca9fd4ce35747724a1 Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 07:50:52 +0800 Subject: [PATCH 06/13] docs(recovery): retain the operational qualification boundary Signed-off-by: song --- docs/architecture/rfcs/complete-state-recovery-v0.md | 3 +++ docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md | 2 ++ docs/reference/configuration-backup.md | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/architecture/rfcs/complete-state-recovery-v0.md b/docs/architecture/rfcs/complete-state-recovery-v0.md index d3d84ea6b7..5974767845 100644 --- a/docs/architecture/rfcs/complete-state-recovery-v0.md +++ b/docs/architecture/rfcs/complete-state-recovery-v0.md @@ -33,6 +33,9 @@ File/SQLite Todo/history through existing owners after original sources become unavailable. Every v0 audit remains held for incomplete capture/owner metadata; unknown state is not relabeled recoverable, and execution authority is always false. Limits, rollback and reproduction are in the [backup guide](../../reference/configuration-backup.md#inspect-a-complete-backup-without-activating-it). +M1 operational qualification remains partial: per-audit verification duration +and data-size reporting, and measurements across the full capacity profile, +still belong to the recovery owner under Section 10. This checkpoint does not qualify M2/M3, live adoption, remote providers or the G1 team journey. No original identity or receipt is rewritten. diff --git a/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md b/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md index f6116a7ef1..e5d95351ed 100644 --- a/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md +++ b/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md @@ -26,6 +26,8 @@ M1 的有界路径已包含增量 `backup-state verify`、loopback HTTP 上传 以及打包设置/能力中心交互。真实生产归档在原来源不可达后,通过既有配置与选定的 File/SQLite Todo/history owner 检查。v0 缺少完整捕获/owner 元数据,因此所有审计仍为 held,未知状态不改称可恢复,执行权限始终为 false。限制与回退见[备份指南](../../reference/configuration-backup.md#inspect-a-complete-backup-without-activating-it)。 +M1 的运行资格仍不完整:逐份审计的验证耗时和数据大小报告、完整容量范围测量, +继续由第 10 节的 recovery owner 承接。 本检查点不证明 M2/M3、live adoption、远端 provider 或 G1 团队验收,不重写原身份或回执。 diff --git a/docs/reference/configuration-backup.md b/docs/reference/configuration-backup.md index 597a927c9e..4f8fc0b46b 100644 --- a/docs/reference/configuration-backup.md +++ b/docs/reference/configuration-backup.md @@ -212,7 +212,9 @@ and virtual tables cannot masquerade as authority tables. WAL-mode copies and unsupported/service-owned providers remain unknown and need their existing owner; PostgreSQL is never contacted. The normal 2 MiB RPC wire is unchanged; only the two complete-manifest checks opt into the existing 64 MiB private-file -transport. These bounds qualify this inspection profile, not a production SLO. +transport. These bounds define this inspection profile; full-capacity +qualification and per-audit duration/data-size reporting remain open under the +recovery RFC's operational contract. No production SLO is claimed. 中文:在设置的能力中心选择设备范围,打开既有备份入口并切到“完整归档检查”, 选择同一份备份的归档与清单,一次明确操作建立隔离副本。可读、仅历史和未知分别 From f72de6facea12fca642e8eb95643b1d1d61f5f99 Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 08:05:38 +0800 Subject: [PATCH 07/13] feat(recovery): retain observed size and verification phase durations Signed-off-by: song --- .../state-recovery-settings.tsx | 3 +++ .../configuration-backup.mjs | 8 ++++++++ loopx/control_plane/state_recovery.ts | 6 +++++- loopx/control_plane/state_recovery_contract.ts | 17 +++++++++++++++++ loopx/state_recovery.py | 15 +++++++++++++-- tests/control_plane_ts/state_recovery.test.ts | 12 ++++++++++++ tests/test_state_recovery.py | 11 +++++++++++ 7 files changed, 69 insertions(+), 3 deletions(-) diff --git a/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx b/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx index 677ece6b37..a14b2dc852 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx @@ -107,6 +107,9 @@ export function StateRecoverySettings() {

{nextActions[row.next_action]?.[index] ?? (zh ? "请对应 owner 核对。" : "Ask the owning module to review this component.")}

)}
{zh ? "核对信息" : "Verification details"}{audit.recovery_id} + {audit.measurements ?

{zh ? "归档字节" : "Archive bytes"}: {audit.measurements.archive_bytes.toLocaleString(locale)} · {zh ? "普通文件字节" : "Regular file bytes"}: {audit.measurements.regular_file_bytes.toLocaleString(locale)}
+ {zh ? "归档检查与复制" : "Archive checks and copy"}: {audit.measurements.archive_processing_ms.toLocaleString(locale)} ms · {zh ? "记录读取" : "Owner reads"}: {audit.measurements.owner_read_ms.toLocaleString(locale)} ms

+ :

{zh ? "此历史报告未记录数据量和阶段耗时。" : "This historical report has no recorded size or phase durations."}

}
    {audit.components.map(row =>
  • {row.component_id}: {row.reason_code}
  • )}
: null} diff --git a/examples/personal-workspace-browser/configuration-backup.mjs b/examples/personal-workspace-browser/configuration-backup.mjs index d9c431bf88..a40e8b8014 100644 --- a/examples/personal-workspace-browser/configuration-backup.mjs +++ b/examples/personal-workspace-browser/configuration-backup.mjs @@ -122,6 +122,11 @@ export const configurationBackupScenario = { await recovery.getByText("隔离副本已核对;Goal 尚未启用。", {exact: true}).waitFor(); await recovery.getByText("recovery-fixture", {exact: false}).first().waitFor(); await recovery.getByText("保存的任务: 1", {exact: false}).waitFor(); + const verificationDetails = recovery.locator("details"); + await verificationDetails.locator("summary").click(); + await verificationDetails.getByText("归档字节:", {exact: false}).waitFor(); + const recordedMeasurements = await verificationDetails.locator("p").innerText(); + await verificationDetails.locator("summary").click(); assert.equal(backend.original_sources_absent, true); const registryAfter = JSON.parse(await readFile(join(root, "registry.json"), "utf8")); assert.deepEqual(registryAfter.goals.map(goal => goal.id), ["fixture"], "inspection must not register the restored Goal"); @@ -154,6 +159,9 @@ export const configurationBackupScenario = { await reopened.getByRole("button", {name: "历史检查结果", exact: true}).click(); await reopened.getByLabel("历史备份检查", {exact: true}).getByRole("button").click(); await reopened.getByText("已读取历史检查记录;尚未重新验证副本。", {exact: true}).waitFor(); + await reopened.locator(".personal-recovery-result details summary").click(); + assert.equal(await reopened.locator(".personal-recovery-result details p").innerText(), recordedMeasurements, + "historical readback must retain original measured values, not time the reload"); await page.getByRole("button", {name: "语言", exact: true}).click(); await page.getByRole("radio", {name: "English", exact: true}).check(); await page.locator(".personal-settings-tabs").getByRole("button", {name: /Capabilit/}).click(); diff --git a/loopx/control_plane/state_recovery.ts b/loopx/control_plane/state_recovery.ts index 468ce4351f..b089d5db80 100644 --- a/loopx/control_plane/state_recovery.ts +++ b/loopx/control_plane/state_recovery.ts @@ -12,7 +12,7 @@ import {decodeLocalAuthoritySelection, localAuthorityProviderPaths, import {sqliteAuthorityRuntime} from "./coordination/sqlite_runtime.ts"; import {sqliteAuthorityPath} from "./coordination/sqlite_authority_store.ts"; import {canonicalTodoCollection} from "./coordination/local_authority_read.ts"; -import {STATE_RECOVERY_AUDIT_SCHEMA, decodeStateRecoveryAudit, isRecoveryDigest, recoveryGoalLabel, +import {STATE_RECOVERY_AUDIT_SCHEMA, decodeRecoveryMeasurements, decodeStateRecoveryAudit, isRecoveryDigest, recoveryGoalLabel, type RecoveryComponent, type RecoveryComponentState} from "./state_recovery_contract.ts"; const MAX_GOALS = 128; @@ -182,7 +182,10 @@ async function inspectGoal(root: string, goalId: string, standaloneSqlite: Set { + const started = performance.now(); const request = requireJsonObject(value, "recovery owner audit"); + const measurements = decodeRecoveryMeasurements({ + ...requireJsonObject(request.measurements, "host recovery measurements"), owner_read_ms: 0}); const basis = requireJsonObject(request.verified, "verified recovery basis"); check(basis.ok === true && basis.manifest_verified === true && basis.schema_version === STATE_RECOVERY_AUDIT_SCHEMA, "recovery_basis_invalid"); @@ -254,6 +257,7 @@ async function auditOwners(value: unknown): Promise { components.push(component("remaining_state", "recovery", "unknown", "legacy_owner_inventory_incomplete", "classify_remaining_owner_state")); const audit = {...basis, status: "held", written: true, observed_at: new Date().toISOString(), + measurements: {...measurements, owner_read_ms: Math.floor(performance.now() - started)}, workspace: {isolation: "inert", registered: false}, components, findings: [{reason_code: "legacy_manifest_incomplete", owner_kind: "backup_capture", next_action: "review_capture_consistency"}], diff --git a/loopx/control_plane/state_recovery_contract.ts b/loopx/control_plane/state_recovery_contract.ts index 776c2c47c2..3e82d168ed 100644 --- a/loopx/control_plane/state_recovery_contract.ts +++ b/loopx/control_plane/state_recovery_contract.ts @@ -5,6 +5,12 @@ import {requireJsonObject, type JsonObject} from "./runtime_decode.ts"; export const STATE_RECOVERY_AUDIT_SCHEMA = "loopx_state_recovery_audit_v0"; export const RECOVERY_COMPONENT_STATES = ["readable", "historical_only", "unknown"] as const; export type RecoveryComponentState = typeof RECOVERY_COMPONENT_STATES[number]; +export interface RecoveryMeasurements extends JsonObject { + archive_bytes: number; + regular_file_bytes: number; + archive_processing_ms: number; + owner_read_ms: number; +} export interface RecoveryComponent extends JsonObject { component_id: string; owner_kind: string; @@ -25,6 +31,7 @@ export interface StateRecoveryAudit extends JsonObject { components: RecoveryComponent[]; findings: Array<{reason_code: string; owner_kind: string; next_action: string}>; summary: Record; + measurements?: RecoveryMeasurements; } function requireValue(condition: unknown): asserts condition { if (!condition) throw new EffectRuntimeRequestError("State recovery audit contract is invalid", "recovery_audit_invalid"); @@ -39,6 +46,13 @@ export function isRecoveryDigest(value: unknown): value is string { export function recoveryGoalLabel(value: string): string | null { return /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/.test(value) ? value : null; } +export function decodeRecoveryMeasurements(value: unknown): RecoveryMeasurements { + const measurements = requireJsonObject(value, "recovery measurements"); + for (const field of ["archive_bytes", "regular_file_bytes", "archive_processing_ms", "owner_read_ms"]) { + requireValue(Number.isSafeInteger(measurements[field]) && Number(measurements[field]) >= 0); + } + return measurements as RecoveryMeasurements; +} export function decodeStateRecoveryAudit(value: unknown): StateRecoveryAudit { const audit = requireJsonObject(value, "state recovery audit"); requireValue(audit.ok === true && audit.schema_version === STATE_RECOVERY_AUDIT_SCHEMA @@ -49,6 +63,9 @@ export function decodeStateRecoveryAudit(value: unknown): StateRecoveryAudit { && Number.isFinite(Date.parse(audit.observed_at))); const workspace = requireJsonObject(audit.workspace, "inert recovery workspace"); requireValue(workspace.isolation === "inert" && workspace.registered === false); + // Earlier reports remain historical observations; absent measurements are + // unknown, never synthesized from their timestamp or today's runtime. + if (audit.measurements !== undefined) decodeRecoveryMeasurements(audit.measurements); const backup = requireJsonObject(audit.backup, "recovery backup basis"); requireValue(backup.schema_version === "loopx_state_backup_v0" && isRecoveryDigest(backup.archive_sha256) && isRecoveryDigest(backup.external_manifest_sha256)); diff --git a/loopx/state_recovery.py b/loopx/state_recovery.py index 20d63b307e..8afa6fc959 100644 --- a/loopx/state_recovery.py +++ b/loopx/state_recovery.py @@ -18,6 +18,7 @@ import subprocess import tarfile import tempfile +import time import unicodedata import zlib from typing import Any, BinaryIO, Iterator, NoReturn @@ -342,6 +343,7 @@ def verify_state_backup( *, archive: Path, manifest: Path, destination: Path, execute: bool = False, ) -> dict[str, Any]: """Preview or publish one inert audit. This API has no live-adoption operation.""" + started = time.monotonic_ns() _require(os.name == "posix", "recovery_platform_unqualified") _physical_parent(destination) with _regular_input(manifest, MAX_MANIFEST_BYTES) as manifest_handle: @@ -365,6 +367,7 @@ def verify_state_backup( if not execute: return {**verified, "status": "planned", "written": False, "logical_bytes": observation["logical_bytes"], "entry_count": observation["entry_count"], + "archive_processing_ms": (time.monotonic_ns() - started) // 1_000_000, "activation_eligible": False} staging = Path(tempfile.mkdtemp(prefix=".loopx-recovery-", dir=destination.parent)) reserved = False @@ -375,6 +378,9 @@ def verify_state_backup( _require(copied == observation and _sha(archive_handle) == (archive_sha, archive_size), "recovery_input_changed") audit = _audit_owners({"data_root": str(data), "verified": verified, + "measurements": {"archive_bytes": archive_size, + "regular_file_bytes": copied["logical_bytes"], + "archive_processing_ms": (time.monotonic_ns() - started) // 1_000_000}, "sqlite_members": observation["sqlite_members"], "standalone_sqlite_members": observation["standalone_sqlite_members"]}) audit = _runtime("state.recovery.audit.readback", audit) @@ -423,9 +429,14 @@ def render_state_recovery_markdown(payload: dict[str, Any]) -> str: "Repeat with --execute to create and inspect a new inert copy; this never activates a Goal.", ]) summary = payload.get("summary", {}) - return "\n".join([ + lines = [ f"Backup inspection: {payload['status']}", f"Recovery: {payload['recovery_id']}", f"Readable: {summary.get('readable', 0)}; historical only: {summary.get('historical_only', 0)}; unknown: {summary.get('unknown', 0)}", "The copy is inert. Goal execution and live settings were not activated.", - ]) + ] + measurements = payload.get("measurements") + if isinstance(measurements, dict): + lines.append(f"Archive: {measurements['archive_bytes']} bytes; regular files: {measurements['regular_file_bytes']} bytes; " + f"archive checks/copy: {measurements['archive_processing_ms']} ms; owner reads: {measurements['owner_read_ms']} ms.") + return "\n".join(lines) diff --git a/tests/control_plane_ts/state_recovery.test.ts b/tests/control_plane_ts/state_recovery.test.ts index bd2eb40d66..f8ecc23265 100644 --- a/tests/control_plane_ts/state_recovery.test.ts +++ b/tests/control_plane_ts/state_recovery.test.ts @@ -73,3 +73,15 @@ test("untrusted owner inspection has no shared-daemon dispatch", async () => { const handlers = createEffectRuntimeHandlers({fingerprint: "recovery-isolation", requestShutdown() {}}); await assert.rejects(() => dispatchEffectRuntimeMethod(handlers, "state.recovery.owners.audit", {}), /unsupported Effect runtime method/); }); + +test("recorded measurements reject invalid values and absent historical measurements remain absent", () => { + const input = audit(); + const measurements = {archive_bytes: 500, regular_file_bytes: 256, archive_processing_ms: 3, owner_read_ms: 2}; + assert.equal(decodeStateRecoveryAudit(input).measurements, undefined); + assert.deepEqual(decodeStateRecoveryAudit({...input, measurements}).measurements, measurements); + for (const field of Object.keys(measurements)) { + for (const value of [-1, 1.5, "3", null, Number.NaN, Number.MAX_SAFE_INTEGER + 1]) { + assert.throws(() => decodeStateRecoveryAudit({...input, measurements: {...measurements, [field]: value}})); + } + } +}); diff --git a/tests/test_state_recovery.py b/tests/test_state_recovery.py index 53c01db42f..2aaa57749b 100644 --- a/tests/test_state_recovery.py +++ b/tests/test_state_recovery.py @@ -85,6 +85,11 @@ def test_real_backup_recovers_readable_authority_after_original_sources_disappea assert audit["execution_authority_granted"] is False and audit["activation_eligible"] is False assert audit["workspace"] == {"isolation": "inert", "registered": False} assert audit["status"] == "held" + assert audit["measurements"]["archive_bytes"] == archive.stat().st_size + assert audit["measurements"]["regular_file_bytes"] == preview["logical_bytes"] + assert all(type(value) is int and value >= 0 for value in audit["measurements"].values()) + assert read_state_recovery_audit(destination)["measurements"] == audit["measurements"] + assert "archive checks/copy:" in render_state_recovery_markdown(audit) [goal] = [row for row in audit["components"] if row["owner_kind"] == "coordination_authority"] assert goal["state"] == "readable", goal assert goal["facts"]["todo_count"] == 1 @@ -157,6 +162,11 @@ def test_changed_audit_and_true_execution_flag_are_not_trusted_on_readback(tmp_p destination = tmp_path / "inert" result = verify_state_backup(archive=archive, manifest=manifest, destination=destination, execute=True) path = destination / "recovery-audit.json" + altered_measurement = {**result, "measurements": {**result["measurements"], + "owner_read_ms": result["measurements"]["owner_read_ms"] + 1}} + path.write_text(json.dumps(altered_measurement)) + with pytest.raises(StateRecoveryError, match="audit_digest_mismatch"): + read_state_recovery_audit(destination) changed = {**result, "observed_at": "2020-01-01T00:00:00.000Z"} path.write_text(json.dumps(changed)) with pytest.raises(StateRecoveryError, match="audit_digest_mismatch"): @@ -178,6 +188,7 @@ def test_missing_selected_storage_is_unknown_without_creation_or_fallback(tmp_pa sqlite_files = [path.relative_to(destination / "data").as_posix() for path in selected.glob("*.sqlite")] selected.rename(tmp_path / "unavailable-store") observed = _audit_owners({"data_root": str(destination / "data"), + "measurements": original["measurements"], "verified": {key: original[key] for key in ("ok", "schema_version", "recovery_id", "backup", "file_count", "file_witnesses_verified", "manifest_verified", "capture_profile", "execution_authority_granted")}, "sqlite_members": sqlite_files, "standalone_sqlite_members": sqlite_files}) From cd764786e0c85e836398e1dde27370a5885e952e Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 08:05:38 +0800 Subject: [PATCH 08/13] docs(recovery): define observed cost and historical measurement limits Signed-off-by: song --- .../architecture/rfcs/complete-state-recovery-v0.md | 5 +++-- .../rfcs/complete-state-recovery-v0.zh-CN.md | 4 ++-- docs/reference/configuration-backup.md | 13 +++++++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/architecture/rfcs/complete-state-recovery-v0.md b/docs/architecture/rfcs/complete-state-recovery-v0.md index 5974767845..00bee22b7f 100644 --- a/docs/architecture/rfcs/complete-state-recovery-v0.md +++ b/docs/architecture/rfcs/complete-state-recovery-v0.md @@ -33,8 +33,9 @@ File/SQLite Todo/history through existing owners after original sources become unavailable. Every v0 audit remains held for incomplete capture/owner metadata; unknown state is not relabeled recoverable, and execution authority is always false. Limits, rollback and reproduction are in the [backup guide](../../reference/configuration-backup.md#inspect-a-complete-backup-without-activating-it). -M1 operational qualification remains partial: per-audit verification duration -and data-size reporting, and measurements across the full capacity profile, +Reports retain measured archive/regular-file bytes and archive-processing and +owner-read durations; historical readback does not retime them. M1 operational +qualification remains partial: measurements across the full capacity profile still belong to the recovery owner under Section 10. This checkpoint does not qualify M2/M3, live adoption, remote providers or the G1 team journey. No original identity or receipt is rewritten. diff --git a/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md b/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md index e5d95351ed..2f2e9d9af5 100644 --- a/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md +++ b/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md @@ -26,8 +26,8 @@ M1 的有界路径已包含增量 `backup-state verify`、loopback HTTP 上传 以及打包设置/能力中心交互。真实生产归档在原来源不可达后,通过既有配置与选定的 File/SQLite Todo/history owner 检查。v0 缺少完整捕获/owner 元数据,因此所有审计仍为 held,未知状态不改称可恢复,执行权限始终为 false。限制与回退见[备份指南](../../reference/configuration-backup.md#inspect-a-complete-backup-without-activating-it)。 -M1 的运行资格仍不完整:逐份审计的验证耗时和数据大小报告、完整容量范围测量, -继续由第 10 节的 recovery owner 承接。 +报告保留实际归档/普通文件字节数及归档处理、owner 读取耗时;历史读回不重新计时。 +M1 的运行资格仍不完整:完整容量范围测量继续由第 10 节的 recovery owner 承接。 本检查点不证明 M2/M3、live adoption、远端 provider 或 G1 团队验收,不重写原身份或回执。 diff --git a/docs/reference/configuration-backup.md b/docs/reference/configuration-backup.md index 4f8fc0b46b..72675ec0a6 100644 --- a/docs/reference/configuration-backup.md +++ b/docs/reference/configuration-backup.md @@ -172,6 +172,15 @@ view and leaves existing checkpoints intact. If a response was lost, **Check for an existing result** reads the original checkpoint; a repeated write to that occupied target is refused. +**Verification details** retain the archive byte size, copied regular-file +bytes (including the embedded manifest), and two measured phases. Archive +processing covers input checks, both scans and copying; owner reads cover the +isolated configuration/storage audit. Durations use monotonic clocks and exclude +HTTP upload, verifier process startup, the final source rehash/report publication, +and historical readback. They describe processing cost, not end-to-end recovery +time or a deadline guarantee. Earlier reports without measurements show that +absence instead of zero. Reading history preserves the original measurements. + The additive CLI route preserves existing backup creation syntax: ```bash @@ -213,8 +222,8 @@ unsupported/service-owned providers remain unknown and need their existing owner; PostgreSQL is never contacted. The normal 2 MiB RPC wire is unchanged; only the two complete-manifest checks opt into the existing 64 MiB private-file transport. These bounds define this inspection profile; full-capacity -qualification and per-audit duration/data-size reporting remain open under the -recovery RFC's operational contract. No production SLO is claimed. +qualification remains open under the recovery RFC's operational contract. +No production SLO is claimed. 中文:在设置的能力中心选择设备范围,打开既有备份入口并切到“完整归档检查”, 选择同一份备份的归档与清单,一次明确操作建立隔离副本。可读、仅历史和未知分别 From 1f45a9819ab41d05eaff9a375760c1526393a2e1 Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 08:06:10 +0800 Subject: [PATCH 09/13] test(recovery): capture rendered verification measurements Signed-off-by: song --- examples/personal-workspace-browser/configuration-backup.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/personal-workspace-browser/configuration-backup.mjs b/examples/personal-workspace-browser/configuration-backup.mjs index a40e8b8014..3db34ccc3b 100644 --- a/examples/personal-workspace-browser/configuration-backup.mjs +++ b/examples/personal-workspace-browser/configuration-backup.mjs @@ -126,6 +126,8 @@ export const configurationBackupScenario = { await verificationDetails.locator("summary").click(); await verificationDetails.getByText("归档字节:", {exact: false}).waitFor(); const recordedMeasurements = await verificationDetails.locator("p").innerText(); + await verificationDetails.scrollIntoViewIfNeeded(); + await page.screenshot({path: resolve(outputDir, "state-recovery-measurements.png"), animations: "disabled"}); await verificationDetails.locator("summary").click(); assert.equal(backend.original_sources_absent, true); const registryAfter = JSON.parse(await readFile(join(root, "registry.json"), "utf8")); From 884eae425c77ae6db9d8042bd98ee19663e7bdd4 Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 08:18:00 +0800 Subject: [PATCH 10/13] fix(recovery): abort publication when authority history exceeds capacity Signed-off-by: song --- .../state-recovery-settings.tsx | 1 + loopx/control_plane/state_recovery.ts | 6 +++- tests/test_state_recovery.py | 34 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx b/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx index a14b2dc852..38c332bed7 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx @@ -26,6 +26,7 @@ const errors: Record = { recovery_capacity_exceeded: ["备份超过当前检查容量,请使用较小的备份;未建立新副本。", "This backup exceeds the inspection limits. Use a smaller backup; no new copy was published."], recovery_input_unavailable: ["上传文件已不可用,请重新选择归档与清单。", "The uploaded files are unavailable. Select the archive and manifest again."], recovery_owner_timeout: ["离线检查超时,未发布副本;可使用较小的备份重试。", "Offline verification timed out without publishing a copy. Retry with a smaller backup."], + authority_history_capacity: ["单个 Goal 的历史超过 20,000 次提交,未发布副本。请保留完整备份,通过存储 owner 的恢复流程检查。", "One Goal exceeds the 20,000-commit inspection limit; no copy was published. Keep the complete backup and use the storage owner's recovery procedure."], member_witness_mismatch: ["保存的文件与清单记录不符,已停止检查。", "Saved files differ from their manifest witnesses. Inspection stopped."], }; diff --git a/loopx/control_plane/state_recovery.ts b/loopx/control_plane/state_recovery.ts index b089d5db80..89f24d401e 100644 --- a/loopx/control_plane/state_recovery.ts +++ b/loopx/control_plane/state_recovery.ts @@ -4,7 +4,7 @@ import {lstat, readFile} from "node:fs/promises"; import {join, relative} from "node:path"; import type {JsonObject} from "./effect_program.ts"; import {requireJsonObject} from "./runtime_decode.ts"; -import {canonicalAuthoritySha256, AuthorityStoreProtocolError} from "./coordination/authority_store_codec.ts"; +import {canonicalAuthoritySha256, AuthorityStoreProtocolError, parseAuthorityCursor} from "./coordination/authority_store_codec.ts"; import {EffectRuntimeRequestError} from "./effect_runtime_errors.ts"; import {verifyConfigurationBackup} from "./configuration_backup.ts"; import {decodeLocalAuthoritySelection, localAuthorityProviderPaths, @@ -145,6 +145,7 @@ async function inspectGoal(root: string, goalId: string, standaloneSqlite: Set Date: Mon, 12 Oct 2026 08:26:58 +0800 Subject: [PATCH 11/13] test(recovery): exercise native history overflow in the packaged journey Signed-off-by: song --- .../configuration-backup.mjs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/examples/personal-workspace-browser/configuration-backup.mjs b/examples/personal-workspace-browser/configuration-backup.mjs index 3db34ccc3b..01c5d74ad0 100644 --- a/examples/personal-workspace-browser/configuration-backup.mjs +++ b/examples/personal-workspace-browser/configuration-backup.mjs @@ -138,6 +138,17 @@ export const configurationBackupScenario = { await recovery.getByRole("button", {name: "验证并建立隔离副本", exact: true}).click(); await recovery.getByRole("alert").waitFor(); assert.equal(await recovery.getByText("保存的任务: 1", {exact: false}).count(), 0, "a changed input must clear the old success view"); + // Optional real capacity fixture, produced through the normal backup + // command. The ordinary browser smoke does not seed 20,001 transactions. + if (process.env.LOOPX_RECOVERY_HISTORY_OVERFLOW_ARCHIVE) { + assert.ok(process.env.LOOPX_RECOVERY_HISTORY_OVERFLOW_MANIFEST); + await recovery.getByLabel("完整归档", {exact: true}).setInputFiles(process.env.LOOPX_RECOVERY_HISTORY_OVERFLOW_ARCHIVE); + await recovery.getByLabel("配套清单", {exact: true}).setInputFiles(process.env.LOOPX_RECOVERY_HISTORY_OVERFLOW_MANIFEST); + await recovery.getByRole("button", {name: "验证并建立隔离副本", exact: true}).click(); + await recovery.getByRole("alert").filter({hasText: "20,000"}).waitFor(); + assert.equal(await recovery.getByText("保存的任务: 1", {exact: false}).count(), 0); + await page.screenshot({path: resolve(outputDir, "state-recovery-history-capacity.png"), animations: "disabled"}); + } await recovery.getByRole("button", {name: "历史检查结果", exact: true}).click(); const history = recovery.getByLabel("历史备份检查", {exact: true}); await history.getByRole("button").click(); From 729287c1a08de687febb15058b24249b7ed37349 Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 08:26:58 +0800 Subject: [PATCH 12/13] docs(recovery): record measured local capacity and qualification boundaries Signed-off-by: song --- .../rfcs/complete-state-recovery-v0.md | 8 +-- .../rfcs/complete-state-recovery-v0.zh-CN.md | 4 +- docs/reference/configuration-backup.md | 53 +++++++++++++++++-- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/docs/architecture/rfcs/complete-state-recovery-v0.md b/docs/architecture/rfcs/complete-state-recovery-v0.md index 00bee22b7f..ba3fe4bf69 100644 --- a/docs/architecture/rfcs/complete-state-recovery-v0.md +++ b/docs/architecture/rfcs/complete-state-recovery-v0.md @@ -34,9 +34,11 @@ unavailable. Every v0 audit remains held for incomplete capture/owner metadata; unknown state is not relabeled recoverable, and execution authority is always false. Limits, rollback and reproduction are in the [backup guide](../../reference/configuration-backup.md#inspect-a-complete-backup-without-activating-it). Reports retain measured archive/regular-file bytes and archive-processing and -owner-read durations; historical readback does not retime them. M1 operational -qualification remains partial: measurements across the full capacity profile -still belong to the recovery owner under Section 10. +owner-read durations; historical readback does not retime them. The backup guide +records individual native capacity cases, including exact member/history +boundaries and 128 File/SQLite Goals. History overflow refuses publication. +M1 operational qualification remains partial: the Section 12 maintainer freeze, +compound worst cases and broader platform/provider profiles remain open. This checkpoint does not qualify M2/M3, live adoption, remote providers or the G1 team journey. No original identity or receipt is rewritten. diff --git a/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md b/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md index 2f2e9d9af5..b5f6cfe9cd 100644 --- a/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md +++ b/docs/architecture/rfcs/complete-state-recovery-v0.zh-CN.md @@ -27,7 +27,9 @@ M1 的有界路径已包含增量 `backup-state verify`、loopback HTTP 上传 File/SQLite Todo/history owner 检查。v0 缺少完整捕获/owner 元数据,因此所有审计仍为 held,未知状态不改称可恢复,执行权限始终为 false。限制与回退见[备份指南](../../reference/configuration-backup.md#inspect-a-complete-backup-without-activating-it)。 报告保留实际归档/普通文件字节数及归档处理、owner 读取耗时;历史读回不重新计时。 -M1 的运行资格仍不完整:完整容量范围测量继续由第 10 节的 recovery owner 承接。 +备份指南记录了独立的真实容量案例,包括精确成员/历史边界及 128 个 File/SQLite Goal; +历史超限在发布前拒绝。M1 的运行资格仍不完整:第 12 节维护者冻结、多个上限组合的 +最坏情形及更广的平台/provider 资格继续开放。 本检查点不证明 M2/M3、live adoption、远端 provider 或 G1 团队验收,不重写原身份或回执。 diff --git a/docs/reference/configuration-backup.md b/docs/reference/configuration-backup.md index 72675ec0a6..7137b5c094 100644 --- a/docs/reference/configuration-backup.md +++ b/docs/reference/configuration-backup.md @@ -221,9 +221,56 @@ and virtual tables cannot masquerade as authority tables. WAL-mode copies and unsupported/service-owned providers remain unknown and need their existing owner; PostgreSQL is never contacted. The normal 2 MiB RPC wire is unchanged; only the two complete-manifest checks opt into the existing 64 MiB private-file -transport. These bounds define this inspection profile; full-capacity -qualification remains open under the recovery RFC's operational contract. -No production SLO is claimed. +transport. These candidate bounds define this inspection profile. The RFC's +Section 12 maintainer freeze, compound worst-case qualification and broader +platform/provider qualification remain open. No production SLO is claimed. + +### Measured local capacity cases + +At source `884eae425c77ae6db9d8042bd98ee19663e7bdd4`, disposable native backups +were inspected through the real `backup-state verify --execute` CLI after hiding +their original project/runtime/registry. Measurements used macOS arm64, Python +3.11.7 and Node 22.22.3. Times below include the verifier command, not backup +creation; peak RSS is the macOS `time -l` command metric, not a simultaneous +process-tree total. These are individual synthetic observations under local +development load, not latency targets or proof that all maxima combine. + +| Native input / boundary | Result | Wall seconds | Peak RSS MiB | +| --- | --- | ---: | ---: | +| 511 MiB zero-filled regular file | Held audit; captured configuration readable | 4.941 | 97.8 | +| 512 MiB regular file plus archive metadata | Refused: expanded capacity | 1.931 | 88.2 | +| 127 MiB seeded incompressible file; 133,212,049 archive bytes | Held audit | 1.284 | 100.6 | +| Archive input 134,217,729 bytes | Refused before decoding: archive capacity | 0.521 | 81.5 | +| External manifest padded with JSON whitespace to 16 MiB | Held audit; bounds input bytes, not maximum structured metadata | 0.945 | 113.8 | +| External manifest 16 MiB + 1 byte | Refused: manifest capacity | 0.520 | 81.4 | +| Exactly 20,000 archive members | Complete inventory and held audit | 8.698 | 225.8 | +| Exactly 20,001 archive members | Refused: member capacity | 1.888 | 143.8 | +| 128 native File Goals, one retained task/commit each | All 128 Goal records readable | 0.843 | 103.5 | +| 128 native SQLite Goals/databases, one retained task/commit each | All 128 Goal records readable; database bytes historical-only | 1.041 | 107.1 | +| 129 File Goals | Refused: Goal inventory capacity | 0.738 | 96.8 | +| 129 SQLite databases | Refused: SQLite inventory capacity | 0.534 | 83.7 | + +All refusal cases left no destination or staging directory. The real SQLite +history regression additionally proves that **20,000 committed transactions +are fully read**, while **20,001 refuse publication**. An earlier implementation +incorrectly turned that overflow into an unknown component and published the +copy. The owner now checks the canonical cursor before scanning and propagates +the capacity error instead of treating it as unreadable source state: + +```bash +uv run --extra test python -m pytest tests/test_state_recovery.py \ + -k real_sqlite_history_limit -q +``` + +Keep an over-limit backup complete and use the owning storage recovery process; +do not trim historical transactions to force inspection through this profile. +The optional browser capacity case accepts a native over-limit archive pair via +`LOOPX_RECOVERY_HISTORY_OVERFLOW_ARCHIVE` and +`LOOPX_RECOVERY_HISTORY_OVERFLOW_MANIFEST`, alongside the existing +`configuration-backup` scenario. It must show refusal and retain the earlier +historical report. Installed-wheel checks on Python 3.12.13 and 3.13.12 also +passed normal source-loss inspection and early GNU/PAX sparse/metadata refusal; +they do not expand the capacity observations or qualify other platforms. 中文:在设置的能力中心选择设备范围,打开既有备份入口并切到“完整归档检查”, 选择同一份备份的归档与清单,一次明确操作建立隔离副本。可读、仅历史和未知分别 From 13dd7eca02c1cd240fa9a68bf51e6e917e0f6aed Mon Sep 17 00:00:00 2001 From: song Date: Mon, 12 Oct 2026 08:29:56 +0800 Subject: [PATCH 13/13] fix(recovery): distinguish capacity refusal from a lost inspection response Signed-off-by: song --- .../src/features/personal-workspace/state-recovery-settings.tsx | 1 + examples/personal-workspace-browser/configuration-backup.mjs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx b/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx index 38c332bed7..6e3b0c6412 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/state-recovery-settings.tsx @@ -52,6 +52,7 @@ export function StateRecoverySettings() { setBusy(true); setError(""); try { await operation(); } catch (failure) { const code = failure instanceof ChatApiError ? String(failure.payload.error_code ?? "") : ""; + if (code === "authority_history_capacity") setPending(null); setError(errors[code]?.[index] ?? (zh ? "检查未完成,请核对文件或稍后重试。" : "Inspection did not finish. Check the files or retry later.")); } finally { setBusy(false); } diff --git a/examples/personal-workspace-browser/configuration-backup.mjs b/examples/personal-workspace-browser/configuration-backup.mjs index 01c5d74ad0..cd6753ff0e 100644 --- a/examples/personal-workspace-browser/configuration-backup.mjs +++ b/examples/personal-workspace-browser/configuration-backup.mjs @@ -146,6 +146,8 @@ export const configurationBackupScenario = { await recovery.getByLabel("配套清单", {exact: true}).setInputFiles(process.env.LOOPX_RECOVERY_HISTORY_OVERFLOW_MANIFEST); await recovery.getByRole("button", {name: "验证并建立隔离副本", exact: true}).click(); await recovery.getByRole("alert").filter({hasText: "20,000"}).waitFor(); + assert.equal(await recovery.getByRole("button", {name: "检查是否已有结果", exact: true}).count(), 0, + "a definitive pre-publication refusal must not suggest a lost response"); assert.equal(await recovery.getByText("保存的任务: 1", {exact: false}).count(), 0); await page.screenshot({path: resolve(outputDir, "state-recovery-history-capacity.png"), animations: "disabled"}); }