From 1867be86d3daa788e4150e85a21f58ead965cb6e Mon Sep 17 00:00:00 2001 From: homelab-runtime-publisher Date: Tue, 15 Sep 2026 08:28:43 +0000 Subject: [PATCH] runtime: publish SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-NORMALIZED-CONFIG-CONTRACT-RCA-414R1 --- ...-NORMALIZED-CONFIG-CONTRACT-RCA-414R1.json | 38 +++++++++++ ...C-NORMALIZED-CONFIG-CONTRACT-RCA-414R1.txt | 66 +++++++++++++++++++ runtime/latest.json | 22 +++---- runtime/latest.txt | 53 +++++++-------- 4 files changed, 142 insertions(+), 37 deletions(-) create mode 100644 runtime/history/SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-NORMALIZED-CONFIG-CONTRACT-RCA-414R1.json create mode 100644 runtime/history/SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-NORMALIZED-CONFIG-CONTRACT-RCA-414R1.txt diff --git a/runtime/history/SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-NORMALIZED-CONFIG-CONTRACT-RCA-414R1.json b/runtime/history/SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-NORMALIZED-CONFIG-CONTRACT-RCA-414R1.json new file mode 100644 index 00000000..46eccd3d --- /dev/null +++ b/runtime/history/SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-NORMALIZED-CONFIG-CONTRACT-RCA-414R1.json @@ -0,0 +1,38 @@ +{ + "schema_version": 1, + "channel": "homelab-runtime", + "command_id": "SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-NORMALIZED-CONFIG-CONTRACT-RCA-414R1", + "status": "OK", + "rc": 0, + "host": "pve01", + "mode": "read-only", + "component": "infra-dr-backupv2-pve-lxc-normalized-config-contract-rca", + "started_at_utc": "2026-09-15T08:28:34Z", + "finished_at_utc": "2026-09-15T08:28:42Z", + "reference_register_checked": true, + "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", + "error_register_checked": true, + "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", + "command_sha256": "a87375b4ea471776b15e40b0552f4c81f0e5fe90045197a0518f734beaced8ce", + "duplicate_failed_command_blocked": false, + "block_reason": null, + "execution_started": true, + "change_declared": false, + "result_contract_valid": true, + "result_contract_status": null, + "result_contract_error": null, + "command_rc": 0, + "changes_made": false, + "rollback_started": false, + "rollback_restored": null, + "mutation_outcome": "NO_MUTATION", + "sanitized": true, + "secrets_included": false, + "private_addresses_included": false, + "raw_evidence_retained_locally": true, + "raw_evidence_sha256": "08973e4da91e940bb3efd84f780674c18ca3e100b67cd2c51b47767ad09105f4", + "sanitized_output_sha256": "08973e4da91e940bb3efd84f780674c18ca3e100b67cd2c51b47767ad09105f4", + "output_truncated_in_json": false, + "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", + "output": "COMMAND_ID=SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-NORMALIZED-CONFIG-CONTRACT-RCA-414R1\nMODE=READ_ONLY_PVE_LXC_NORMALIZED_CONFIG_CONTRACT_RCA\nNO_BACKUP_EXECUTION=YES\nNO_VZDUMP_EXECUTION=YES\nNO_CLOUD_ACCESS=YES\nNO_SYSTEMD_MUTATION=YES\nNO_PRODUCTION_PROMOTION=YES\nPRODUCTION_BASELINE_UNCHANGED=PASS\nANCHOR_411_BINDING=PASS\nANCHOR_413_BINDING=PASS\nANCHOR_413_COMMIT=69fb84a1e4be2e9cff9fe96a33f8dab0bf6c9bc5\nNORMALIZED_CONFIG_CONTRACT={\"need_calls\":[{\"code\":\"CONFIG_SCHEMA\",\"line\":2,\"predicate\":\"type(config) is dict\"}],\"normalized_calls\":[],\"signature\":\"(config)\",\"source\":\"def normalized_config(config):\\n need(type(config) is dict,'CONFIG_SCHEMA')\\n out=dict(config)\\n for k,v in out.items():\\n if DISK_RE.fullmatch(k) and type(v) is str:\\n if v.startswith('file='):out[k]=v[5:]\\n elif v.startswith('volume='):out[k]=v[7:]\\n return out\\n\",\"source_sha256\":\"7f0338f9334bc22fa6840124f31fac14499120430bae7df15ed8900431ca1b4e\"}\nMAKE_PLAN_CONTRACT={\"need_calls\":[{\"code\":\"NODE_NAME\",\"line\":2,\"predicate\":\"np.valid_id(node)\"},{\"code\":\"SPACE_VALUE\",\"line\":3,\"predicate\":\"type(staging_free) is int and 0 <= staging_free <= 2 ** 63 - 1\"},{\"code\":\"PLAN_TIME\",\"line\":4,\"predicate\":\"type(now) is int and now >= 0\"},{\"code\":\"CONFIGS_SCHEMA\",\"line\":5,\"predicate\":\"type(configs) is dict\"},{\"code\":\"DUPLICATE_STORAGE\",\"line\":7,\"predicate\":\"len({x['storage'] for x in ss}) == len(ss)\"},{\"code\":\"GUEST_CONFIG_MISSING\",\"line\":11,\"predicate\":\"g['vmid'] in configs\"}],\"normalized_calls\":[{\"call\":\"normalized_config(original)\",\"line\":12}],\"signature\":\"(rows, configs, storage_rows, defaults, staging_free, now, node='pve01')\",\"source\":\"def make_plan(rows,configs,storage_rows,defaults,staging_free,now,node='pve01'):\\n need(np.valid_id(node),'NODE_NAME')\\n need(type(staging_free) is int and 0<=staging_free<=2**63-1,'SPACE_VALUE')\\n need(type(now) is int and now>=0,'PLAN_TIME')\\n need(type(configs) is dict,'CONFIGS_SCHEMA')\\n guests=np.guests(rows,node);ss=np.storages(storage_rows)\\n need(len({x['storage'] for x in ss})==len(ss),'DUPLICATE_STORAGE')\\n stores={x['storage']:x for x in ss};global_summary=defaults_summary(defaults)\\n jobs=[]\\n for g in guests:\\n need(g['vmid'] in configs,'GUEST_CONFIG_MISSING')\\n original=configs[g['vmid']];cfg=normalized_config(original);summary=np.config_summary(cfg,g['type'])\\n summary['config_sha256']=t.sha(core.canonical_bytes(original))\\n block=set(summary['blockers'])|set(global_summary['blockers']);sizes=[]\\n if not summary['disks']:block.add('NO_BACKUP_DISKS')\\n if g['template']:block.add('TEMPLATE_REQUIRES_SEPARATE_POLICY')\\n if g['status']!='running':block.add('GUEST_NOT_RUNNING')\\n if any(k=='args' or re.match(r'hostpci\\\\d+|usb\\\\d+|virtiofs\\\\d+|dev\\\\d+|lxc\\\\.',k) for k in cfg):\\n block.add('EXTERNAL_DEVICE_OR_CUSTOM_CONFIG')\\n for disk in summary['disks']:\\n slot=disk['slot'];opts={}\\n for field in cfg[slot].split(',')[1:]:\\n k,sep,v=field.partition('=')\\n if sep:opts[k]=v\\n if 'backup' in opts and opts['backup'] not in ('0','1','yes','no','on','off','true','false'):\\n block.add('BACKUP_FLAG_UNKNOWN')\\n if disk['backup_setting']=='default_unproven':\\n if g['type']=='qemu':disk['backup_setting']='included_default_qemu'\\n elif slot=='rootfs':disk['backup_setting']='included_default_rootfs'\\n st=stores.get(disk['storage'])\\n if st is None or st['active'] is not True or st['enabled'] is not True:block.add('STORAGE_NOT_AVAILABLE')\\n elif g['type']=='lxc' and st['type'] not in ('lvmthin','zfspool'):block.add('LXC_SNAPSHOT_SUPPORT_UNPROVEN')\\n if disk['configured_size_bytes'] is not None:sizes.append(disk['configured_size_bytes'])\\n total=sum(sizes)\\n if total>stream.MAX_BYTES:block.add('GUEST_SIZE_EXCEEDS_CORE_BOUND')\\n # Policy ceiling, not an estimate of compression ratio. Actual byte cap is enforced at read/write.\\n configured_bound=min(stream.MAX_BYTES,total+(total+9)//10+64*1024**2) if total else 0\\n capacity=max(0,(staging_free-LOCAL_RESERVE)//4)\\n cap=min(configured_bound,capacity)\\n if cap<=0:block.add('STAGING_CAPACITY_ZERO')\\n summary['blockers']=sorted(block)\\n job={**g,**summary,'observed_at':now,'global_defaults_sha256':global_summary['sha256'],\\n 'configured_disk_bytes':total,'configured_archive_bound_bytes':configured_bound,\\n 'archive_cap_bytes':cap,'configured_full_cycle_fits':bool(configured_bound and 4*configured_bound+LOCAL_RESERVE<=staging_free),\\n 'compression_dependent':bool(configured_bound and cap=0,'PLAN_TIME')\n need(type(configs) is dict,'CONFIGS_SCHEMA')\n guests=np.guests(rows,node);ss=np.storages(storage_rows)\n need(len({x['storage'] for x in ss})==len(ss),'DUPLICATE_STORAGE')\n stores={x['storage']:x for x in ss};global_summary=defaults_summary(defaults)\n jobs=[]\n for g in guests:\n need(g['vmid'] in configs,'GUEST_CONFIG_MISSING')\n original=configs[g['vmid']];cfg=normalized_config(original);summary=np.config_summary(cfg,g['type'])\n summary['config_sha256']=t.sha(core.canonical_bytes(original))\n block=set(summary['blockers'])|set(global_summary['blockers']);sizes=[]\n if not summary['disks']:block.add('NO_BACKUP_DISKS')\n if g['template']:block.add('TEMPLATE_REQUIRES_SEPARATE_POLICY')\n if g['status']!='running':block.add('GUEST_NOT_RUNNING')\n if any(k=='args' or re.match(r'hostpci\\d+|usb\\d+|virtiofs\\d+|dev\\d+|lxc\\.',k) for k in cfg):\n block.add('EXTERNAL_DEVICE_OR_CUSTOM_CONFIG')\n for disk in summary['disks']:\n slot=disk['slot'];opts={}\n for field in cfg[slot].split(',')[1:]:\n k,sep,v=field.partition('=')\n if sep:opts[k]=v\n if 'backup' in opts and opts['backup'] not in ('0','1','yes','no','on','off','true','false'):\n block.add('BACKUP_FLAG_UNKNOWN')\n if disk['backup_setting']=='default_unproven':\n if g['type']=='qemu':disk['backup_setting']='included_default_qemu'\n elif slot=='rootfs':disk['backup_setting']='included_default_rootfs'\n st=stores.get(disk['storage'])\n if st is None or st['active'] is not True or st['enabled'] is not True:block.add('STORAGE_NOT_AVAILABLE')\n elif g['type']=='lxc' and st['type'] not in ('lvmthin','zfspool'):block.add('LXC_SNAPSHOT_SUPPORT_UNPROVEN')\n if disk['configured_size_bytes'] is not None:sizes.append(disk['configured_size_bytes'])\n total=sum(sizes)\n if total>stream.MAX_BYTES:block.add('GUEST_SIZE_EXCEEDS_CORE_BOUND')\n # Policy ceiling, not an estimate of compression ratio. Actual byte cap is enforced at read/write.\n configured_bound=min(stream.MAX_BYTES,total+(total+9)//10+64*1024**2) if total else 0\n capacity=max(0,(staging_free-LOCAL_RESERVE)//4)\n cap=min(configured_bound,capacity)\n if cap<=0:block.add('STAGING_CAPACITY_ZERO')\n summary['blockers']=sorted(block)\n job={**g,**summary,'observed_at':now,'global_defaults_sha256':global_summary['sha256'],\n 'configured_disk_bytes':total,'configured_archive_bound_bytes':configured_bound,\n 'archive_cap_bytes':cap,'configured_full_cycle_fits':bool(configured_bound and 4*configured_bound+LOCAL_RESERVE<=staging_free),\n 'compression_dependent':bool(configured_bound and cap=0,'PLAN_TIME')\\n need(type(configs) is dict,'CONFIGS_SCHEMA')\\n guests=np.guests(rows,node);ss=np.storages(storage_rows)\\n need(len({x['storage'] for x in ss})==len(ss),'DUPLICATE_STORAGE')\\n stores={x['storage']:x for x in ss};global_summary=defaults_summary(defaults)\\n jobs=[]\\n for g in guests:\\n need(g['vmid'] in configs,'GUEST_CONFIG_MISSING')\\n original=configs[g['vmid']];cfg=normalized_config(original);summary=np.config_summary(cfg,g['type'])\\n summary['config_sha256']=t.sha(core.canonical_bytes(original))\\n block=set(summary['blockers'])|set(global_summary['blockers']);sizes=[]\\n if not summary['disks']:block.add('NO_BACKUP_DISKS')\\n if g['template']:block.add('TEMPLATE_REQUIRES_SEPARATE_POLICY')\\n if g['status']!='running':block.add('GUEST_NOT_RUNNING')\\n if any(k=='args' or re.match(r'hostpci\\\\d+|usb\\\\d+|virtiofs\\\\d+|dev\\\\d+|lxc\\\\.',k) for k in cfg):\\n block.add('EXTERNAL_DEVICE_OR_CUSTOM_CONFIG')\\n for disk in summary['disks']:\\n slot=disk['slot'];opts={}\\n for field in cfg[slot].split(',')[1:]:\\n k,sep,v=field.partition('=')\\n if sep:opts[k]=v\\n if 'backup' in opts and opts['backup'] not in ('0','1','yes','no','on','off','true','false'):\\n block.add('BACKUP_FLAG_UNKNOWN')\\n if disk['backup_setting']=='default_unproven':\\n if g['type']=='qemu':disk['backup_setting']='included_default_qemu'\\n elif slot=='rootfs':disk['backup_setting']='included_default_rootfs'\\n st=stores.get(disk['storage'])\\n if st is None or st['active'] is not True or st['enabled'] is not True:block.add('STORAGE_NOT_AVAILABLE')\\n elif g['type']=='lxc' and st['type'] not in ('lvmthin','zfspool'):block.add('LXC_SNAPSHOT_SUPPORT_UNPROVEN')\\n if disk['configured_size_bytes'] is not None:sizes.append(disk['configured_size_bytes'])\\n total=sum(sizes)\\n if total>stream.MAX_BYTES:block.add('GUEST_SIZE_EXCEEDS_CORE_BOUND')\\n # Policy ceiling, not an estimate of compression ratio. Actual byte cap is enforced at read/write.\\n configured_bound=min(stream.MAX_BYTES,total+(total+9)//10+64*1024**2) if total else 0\\n capacity=max(0,(staging_free-LOCAL_RESERVE)//4)\\n cap=min(configured_bound,capacity)\\n if cap<=0:block.add('STAGING_CAPACITY_ZERO')\\n summary['blockers']=sorted(block)\\n job={**g,**summary,'observed_at':now,'global_defaults_sha256':global_summary['sha256'],\\n 'configured_disk_bytes':total,'configured_archive_bound_bytes':configured_bound,\\n 'archive_cap_bytes':cap,'configured_full_cycle_fits':bool(configured_bound and 4*configured_bound+LOCAL_RESERVE<=staging_free),\\n 'compression_dependent':bool(configured_bound and cap=0,'PLAN_TIME')\n need(type(configs) is dict,'CONFIGS_SCHEMA')\n guests=np.guests(rows,node);ss=np.storages(storage_rows)\n need(len({x['storage'] for x in ss})==len(ss),'DUPLICATE_STORAGE')\n stores={x['storage']:x for x in ss};global_summary=defaults_summary(defaults)\n jobs=[]\n for g in guests:\n need(g['vmid'] in configs,'GUEST_CONFIG_MISSING')\n original=configs[g['vmid']];cfg=normalized_config(original);summary=np.config_summary(cfg,g['type'])\n summary['config_sha256']=t.sha(core.canonical_bytes(original))\n block=set(summary['blockers'])|set(global_summary['blockers']);sizes=[]\n if not summary['disks']:block.add('NO_BACKUP_DISKS')\n if g['template']:block.add('TEMPLATE_REQUIRES_SEPARATE_POLICY')\n if g['status']!='running':block.add('GUEST_NOT_RUNNING')\n if any(k=='args' or re.match(r'hostpci\\d+|usb\\d+|virtiofs\\d+|dev\\d+|lxc\\.',k) for k in cfg):\n block.add('EXTERNAL_DEVICE_OR_CUSTOM_CONFIG')\n for disk in summary['disks']:\n slot=disk['slot'];opts={}\n for field in cfg[slot].split(',')[1:]:\n k,sep,v=field.partition('=')\n if sep:opts[k]=v\n if 'backup' in opts and opts['backup'] not in ('0','1','yes','no','on','off','true','false'):\n block.add('BACKUP_FLAG_UNKNOWN')\n if disk['backup_setting']=='default_unproven':\n if g['type']=='qemu':disk['backup_setting']='included_default_qemu'\n elif slot=='rootfs':disk['backup_setting']='included_default_rootfs'\n st=stores.get(disk['storage'])\n if st is None or st['active'] is not True or st['enabled'] is not True:block.add('STORAGE_NOT_AVAILABLE')\n elif g['type']=='lxc' and st['type'] not in ('lvmthin','zfspool'):block.add('LXC_SNAPSHOT_SUPPORT_UNPROVEN')\n if disk['configured_size_bytes'] is not None:sizes.append(disk['configured_size_bytes'])\n total=sum(sizes)\n if total>stream.MAX_BYTES:block.add('GUEST_SIZE_EXCEEDS_CORE_BOUND')\n # Policy ceiling, not an estimate of compression ratio. Actual byte cap is enforced at read/write.\n configured_bound=min(stream.MAX_BYTES,total+(total+9)//10+64*1024**2) if total else 0\n capacity=max(0,(staging_free-LOCAL_RESERVE)//4)\n cap=min(configured_bound,capacity)\n if cap<=0:block.add('STAGING_CAPACITY_ZERO')\n summary['blockers']=sorted(block)\n job={**g,**summary,'observed_at':now,'global_defaults_sha256':global_summary['sha256'],\n 'configured_disk_bytes':total,'configured_archive_bound_bytes':configured_bound,\n 'archive_cap_bytes':cap,'configured_full_cycle_fits':bool(configured_bound and 4*configured_bound+LOCAL_RESERVE<=staging_free),\n 'compression_dependent':bool(configured_bound and cap