diff --git a/docs/evidence/2026-11-05-vergabe-identified-page-gates.json b/docs/evidence/2026-11-05-vergabe-identified-page-gates.json new file mode 100644 index 0000000..c1b52d5 --- /dev/null +++ b/docs/evidence/2026-11-05-vergabe-identified-page-gates.json @@ -0,0 +1,35 @@ +{ + "schema": "railiance.vergabe-pilot-anonymous-page-probes.v1", + "observed_at": "2026-11-05T21:27:44.219543+00:00", + "runtime_mutations": false, + "reads": [ + { + "path": "/demo-company/ausschreibungen/1/", + "method": "GET", + "cookies_supplied": false, + "final_status": 200, + "final_host": "vergabe-teilnahme.coulomb.social", + "final_path": "/demo-company/accounts/login/", + "netkingdom_welcome_present": true + }, + { + "path": "/demo-company/ausschreibungen/1/aufgaben/1/", + "method": "GET", + "cookies_supplied": false, + "final_status": 200, + "final_host": "vergabe-teilnahme.coulomb.social", + "final_path": "/demo-company/accounts/login/", + "netkingdom_welcome_present": true + }, + { + "path": "/demo-company/ausschreibungen/1/dokumente/1/", + "method": "GET", + "cookies_supplied": false, + "final_status": 200, + "final_host": "vergabe-teilnahme.coulomb.social", + "final_path": "/demo-company/accounts/login/", + "netkingdom_welcome_present": true + } + ], + "binary_document_download_tested": false +} diff --git a/docs/evidence/2026-11-05-vergabe-local-issue-return.json b/docs/evidence/2026-11-05-vergabe-local-issue-return.json new file mode 100644 index 0000000..b08b186 --- /dev/null +++ b/docs/evidence/2026-11-05-vergabe-local-issue-return.json @@ -0,0 +1,23 @@ +{ + "schema": "railiance.vergabe-pilot-user-return.v1", + "reported_on": "2026-11-05", + "workplan": "RAPPS-WP-0014", + "task": "RAPPS-WP-0014-T03", + "evidence_source": "User confirmation and application URLs in the active conversation", + "evidence_kind": "user_reported", + "company": "demo-company", + "local_issue_badge_visible_in_both_accounts": true, + "exact_issue_key_independently_verified": false, + "fixture": { + "ausschreibung_id": 1, + "aufgabe_id": 1, + "dokument_id": 1, + "task_url": "https://vergabe-teilnahme.coulomb.social/demo-company/ausschreibungen/1/aufgaben/1/", + "document_detail_url": "https://vergabe-teilnahme.coulomb.social/demo-company/ausschreibungen/1/dokumente/1/" + }, + "document_ui_review": "pending", + "populated_restart_or_recovery_verified": false, + "personal_account_identifiers_recorded": false, + "credentials_or_tokens_recorded": false, + "runtime_mutations_by_agent": false +} diff --git a/docs/evidence/2026-11-05-vergabe-native-inventory.json b/docs/evidence/2026-11-05-vergabe-native-inventory.json new file mode 100644 index 0000000..985661d --- /dev/null +++ b/docs/evidence/2026-11-05-vergabe-native-inventory.json @@ -0,0 +1,20 @@ +{ + "schema": "railiance.vergabe-pilot-native-inventory.v1", + "observed_at": "2026-11-05T21:25:35.760984+00:00", + "runtime_mutations": false, + "native_inventory_complete": false, + "cluster_read": "ok", + "cluster_matches_expected": true, + "nodes_read": "forbidden", + "deployment_read": "forbidden", + "claims": { + "media": { + "read": "forbidden" + }, + "app-state": { + "read": "forbidden" + } + }, + "database_cluster_read": "forbidden", + "helm_read": "request_failed" +} diff --git a/docs/proposals/2026-11-05-vergabe-recovery-preflight/README.md b/docs/proposals/2026-11-05-vergabe-recovery-preflight/README.md new file mode 100644 index 0000000..43125b6 --- /dev/null +++ b/docs/proposals/2026-11-05-vergabe-recovery-preflight/README.md @@ -0,0 +1,42 @@ +# Vergabe native recovery inventory + +Prepared under RAPPS-WP-0014-T03. The user has identified Ausschreibung 1, +task 1 and document 1, and confirmed the local issue is visible to both accounts. +This packet prepares the native metadata read needed before a populated restart +and coherent three-store recovery action can be pinned. + +The [action](action.json) embeds the complete source of +[`tools/vergabe_pilot_inventory.py`](../../../tools/vergabe_pilot_inventory.py) +as one `python3 -c` argument, bound to `kube:railiance01`, profile `kube-admin`, +agent `custodian-agt`, cwd `/` and an empty proposed environment. + +Action digest: +`sha256:2014e62a84b0875fec155dbfd1be26963d9a39ab931092d26c5dfb9e627ffbd7`. +Source SHA256: +`b610059b5d7398138062de51edab3555b973cfbb61f02fa684184852805d7507`. + +The command reads the cluster UID, node conditions, the exact pilot Deployment, +the two named PVCs, `databases/apps-pg` status and Helm release summaries. It +checks the recorded immutable image, Recreate/single-writer readiness and exact +mounts. Child output/errors are captured; the receipt contains booleans, +read-status codes and Helm revision. It stops immediately on a different cluster. +There are no resource mutations, pod exec, application queries or file captures. + +The current caller's [inventory receipt](../../evidence/2026-11-05-vergabe-native-inventory.json) +is incomplete: the cluster UID matches, while node, Deployment, PVC and CNPG +reads are forbidden and Helm listing fails. This caller did not execute a +privileged proposal. The workstation has no installed `gexec` command; proposal +preparation used the existing gated-executor source CLI/action format only. + +For attended execution, review this exact JSON with `gexec show` in the admitted +founder runtime, sign it with that runtime's existing approver key and submit +through its configured spool. The environment orientation ยง6 requires the +gated executor for privileged actions. No signing key, elevated kubeconfig or +new standing agent grant is requested by the packet. The result must report +`native_inventory_complete: true` before using its expected release as the +baseline of a separately reviewed capture/restart operation. + +Validation: source compilation and synthetic command responses cover success, +immediate wrong-cluster refusal, image drift and exclusion of a private canary +from the receipt. Ruff is unavailable in the current shell. No native pass, +snapshot, restart, off-host publication or restore has occurred. diff --git a/docs/proposals/2026-11-05-vergabe-recovery-preflight/action.json b/docs/proposals/2026-11-05-vergabe-recovery-preflight/action.json new file mode 100644 index 0000000..fb28d82 --- /dev/null +++ b/docs/proposals/2026-11-05-vergabe-recovery-preflight/action.json @@ -0,0 +1,16 @@ +{ + "action": { + "version": "gexec.action/v1", + "agent_id": "custodian-agt", + "profile": "kube-admin", + "target": "kube:railiance01", + "argv": [ + "python3", + "-c", + "#!/usr/bin/env python3\n\"\"\"Read-only, value-free recovery inventory for the demo-company pilot.\n\nUses the caller's existing kubectl/Helm context for resource status and Helm\nrelease summaries. No Secret output, values, logs, pod exec or resource changes.\nChild output/errors stay private.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport datetime\nimport json\nimport subprocess\n\nCLUSTER_UID = \"a553c742-0115-43d4-99a4-a5ca56fe0786\"\nNAMESPACE = \"vergabe-demo-company\"\nRELEASE = \"vergabe-teilnahme\"\nIMAGE = (\n \"forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:\"\n \"a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91\"\n)\nCLAIMS = {\"media\": \"vergabe-teilnahme-media\", \"app-state\": \"vergabe-teilnahme-app-state\"}\n\n\ndef read_json(argv):\n try:\n result = subprocess.run(argv, capture_output=True, timeout=20, check=False)\n if result.returncode:\n return None, \"forbidden\" if b\"Forbidden\" in result.stderr else \"request_failed\"\n return json.loads(result.stdout), \"ok\"\n except (OSError, subprocess.TimeoutExpired, ValueError, UnicodeError):\n return None, \"unavailable_or_invalid_response\"\n\n\ndef kube(*args):\n return read_json([\"kubectl\", \"--request-timeout=12s\", *args, \"-o\", \"json\"])\n\n\ndef collect():\n receipt = {\n \"schema\": \"railiance.vergabe-pilot-native-inventory.v1\",\n \"observed_at\": datetime.datetime.now(datetime.timezone.utc).isoformat(),\n \"runtime_mutations\": False,\n \"native_inventory_complete\": False,\n }\n cluster, status = kube(\"get\", \"namespace\", \"kube-system\")\n receipt[\"cluster_read\"] = status\n if cluster is None:\n return receipt\n receipt[\"cluster_matches_expected\"] = cluster.get(\"metadata\", {}).get(\"uid\") == CLUSTER_UID\n if not receipt[\"cluster_matches_expected\"]:\n return receipt\n\n nodes, status = kube(\"get\", \"nodes\")\n receipt[\"nodes_read\"] = status\n if nodes is not None:\n matching = [\n n for n in nodes.get(\"items\", [])\n if any(a.get(\"address\") == \"92.205.62.239\"\n for a in n.get(\"status\", {}).get(\"addresses\", []))\n ]\n receipt[\"node_address_matches_expected\"] = len(matching) == 1\n if len(matching) == 1:\n conditions = {c.get(\"type\"): c.get(\"status\")\n for c in matching[0].get(\"status\", {}).get(\"conditions\", [])}\n receipt[\"node_ready\"] = conditions.get(\"Ready\") == \"True\"\n receipt[\"node_pressure_clear\"] = all(\n conditions.get(c) == \"False\"\n for c in (\"MemoryPressure\", \"DiskPressure\", \"PIDPressure\")\n )\n\n deployment, status = kube(\"get\", \"deployment\", RELEASE, \"-n\", NAMESPACE)\n receipt[\"deployment_read\"] = status\n if deployment is not None:\n spec = deployment.get(\"spec\", {})\n pod = spec.get(\"template\", {}).get(\"spec\", {})\n app = next((c for c in pod.get(\"containers\", []) if c.get(\"name\") == \"app\"), {})\n ready = deployment.get(\"status\", {})\n receipt[\"deployment\"] = {\n \"image_matches_expected\": app.get(\"image\") == IMAGE,\n \"replicas\": spec.get(\"replicas\"),\n \"ready_replicas\": ready.get(\"readyReplicas\", 0),\n \"updated_replicas\": ready.get(\"updatedReplicas\", 0),\n \"generation_observed\": ready.get(\"observedGeneration\", 0)\n >= deployment.get(\"metadata\", {}).get(\"generation\", 1),\n \"recreate\": spec.get(\"strategy\", {}).get(\"type\") == \"Recreate\",\n \"mounts_match_expected\": all(\n any(v.get(\"name\") == name\n and v.get(\"persistentVolumeClaim\", {}).get(\"claimName\") == claim\n for v in pod.get(\"volumes\", []))\n for name, claim in CLAIMS.items()\n ) and all(\n any(m.get(\"name\") == name and m.get(\"mountPath\") == path\n for m in app.get(\"volumeMounts\", []))\n for name, path in ((\"media\", \"/app/media\"), (\"app-state\", \"/app/.issue-facade\"))\n ),\n }\n\n receipt[\"claims\"] = {}\n for name, claim in CLAIMS.items():\n data, status = kube(\"get\", \"pvc\", claim, \"-n\", NAMESPACE)\n row = {\"read\": status}\n if data is not None:\n row[\"bound\"] = data.get(\"status\", {}).get(\"phase\") == \"Bound\"\n row[\"identity_present\"] = bool(data.get(\"metadata\", {}).get(\"uid\"))\n receipt[\"claims\"][name] = row\n\n database, status = kube(\"get\", \"cluster\", \"apps-pg\", \"-n\", \"databases\")\n receipt[\"database_cluster_read\"] = status\n if database is not None:\n db_status = database.get(\"status\", {})\n receipt[\"database_ready\"] = (\n db_status.get(\"instances\", 0) > 0\n and db_status.get(\"readyInstances\", 0) == db_status.get(\"instances\")\n and bool(db_status.get(\"currentPrimary\"))\n )\n\n releases, status = read_json([\n \"helm\", \"list\", \"--all\", \"--namespace\", NAMESPACE,\n \"--filter\", \"^vergabe-teilnahme$\", \"--output\", \"json\",\n ])\n receipt[\"helm_read\"] = status\n if isinstance(releases, list):\n rows = [r for r in releases if r.get(\"name\") == RELEASE]\n if len(rows) == 1:\n receipt[\"helm\"] = {\n \"deployed\": rows[0].get(\"status\") == \"deployed\",\n \"revision\": int(rows[0][\"revision\"]),\n \"chart_matches_expected\": rows[0].get(\"chart\") == \"vergabe-teilnahme-0.2.1\",\n }\n\n dep = receipt.get(\"deployment\", {})\n release = receipt.get(\"helm\", {})\n receipt[\"native_inventory_complete\"] = (\n receipt.get(\"node_address_matches_expected\") is True\n and receipt.get(\"node_ready\") is True\n and receipt.get(\"node_pressure_clear\") is True\n and dep.get(\"image_matches_expected\") is True\n and dep.get(\"replicas\") == dep.get(\"ready_replicas\") == dep.get(\"updated_replicas\") == 1\n and dep.get(\"generation_observed\") is True\n and dep.get(\"recreate\") is True and dep.get(\"mounts_match_expected\") is True\n and all(c.get(\"bound\") is True and c.get(\"identity_present\") is True\n for c in receipt[\"claims\"].values())\n and receipt.get(\"database_ready\") is True\n and release.get(\"deployed\") is True and release.get(\"chart_matches_expected\") is True\n )\n return receipt\n\n\ndef main():\n try:\n receipt = collect()\n except Exception:\n receipt = {\"schema\": \"railiance.vergabe-pilot-native-inventory.v1\",\n \"native_inventory_complete\": False, \"failure\": \"invalid_inventory_response\",\n \"runtime_mutations\": False}\n print(json.dumps(receipt, indent=2))\n return 0 if receipt[\"native_inventory_complete\"] else 1\n\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n" + ], + "cwd": "/", + "env": {} + }, + "digest": "sha256:2014e62a84b0875fec155dbfd1be26963d9a39ab931092d26c5dfb9e627ffbd7" +} diff --git a/docs/vergabe-demo-company-acceptance.md b/docs/vergabe-demo-company-acceptance.md index 9ae0456..6180e75 100644 --- a/docs/vergabe-demo-company-acceptance.md +++ b/docs/vergabe-demo-company-acceptance.md @@ -8,7 +8,10 @@ the other account after reload; see the [workflow return](evidence/2026-11-05-ve The user confirmed the uploaded file is available from the other account; see the [document return](evidence/2026-11-05-vergabe-document-return.json). The document UI still needs user review. File hash equality and signed-out download -refusal are unverified. Task edits, local-issue workflow and recovery remain pending. +refusal are unverified. The user also confirms the **LOKAL** issue badge is +visible in both accounts; the [issue return](evidence/2026-11-05-vergabe-local-issue-return.json) +binds Ausschreibung 1, task 1 and document 1 from their supplied URLs. Task +edits, domain approval/submission and recovery remain pending. The other session does not update automatically, and a fresh login was needed after a short interval. The user wants a full working day without renewed login @@ -74,6 +77,9 @@ confirms the recorded cluster UID, public health and the NetKingdom welcome gate. Node, Deployment and PVC reads are forbidden to the current agent identity; fresh image/release/claim status remains unverified. The binding's expected image and claims are historical reference values until a permitted native readback. +The [native inventory packet](proposals/2026-11-05-vergabe-recovery-preflight/README.md) +now provides the exact metadata-only action; its current-caller receipt remains +incomplete. It prepares the baseline read, rather than a snapshot or restart. After the populated workflow, prepare the exact attended recovery action from the existing [pilot recovery contract](vergabe-teilnahme-pilot.md#persistence-and-recovery) diff --git a/tools/vergabe_pilot_inventory.py b/tools/vergabe_pilot_inventory.py new file mode 100644 index 0000000..963e958 --- /dev/null +++ b/tools/vergabe_pilot_inventory.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +"""Read-only, value-free recovery inventory for the demo-company pilot. + +Uses the caller's existing kubectl/Helm context for resource status and Helm +release summaries. No Secret output, values, logs, pod exec or resource changes. +Child output/errors stay private. +""" + +from __future__ import annotations + +import datetime +import json +import subprocess + +CLUSTER_UID = "a553c742-0115-43d4-99a4-a5ca56fe0786" +NAMESPACE = "vergabe-demo-company" +RELEASE = "vergabe-teilnahme" +IMAGE = ( + "forgejo.coulomb.social/coulomb/vergabe-teilnahme@sha256:" + "a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91" +) +CLAIMS = {"media": "vergabe-teilnahme-media", "app-state": "vergabe-teilnahme-app-state"} + + +def read_json(argv): + try: + result = subprocess.run(argv, capture_output=True, timeout=20, check=False) + if result.returncode: + return None, "forbidden" if b"Forbidden" in result.stderr else "request_failed" + return json.loads(result.stdout), "ok" + except (OSError, subprocess.TimeoutExpired, ValueError, UnicodeError): + return None, "unavailable_or_invalid_response" + + +def kube(*args): + return read_json(["kubectl", "--request-timeout=12s", *args, "-o", "json"]) + + +def collect(): + receipt = { + "schema": "railiance.vergabe-pilot-native-inventory.v1", + "observed_at": datetime.datetime.now(datetime.timezone.utc).isoformat(), + "runtime_mutations": False, + "native_inventory_complete": False, + } + cluster, status = kube("get", "namespace", "kube-system") + receipt["cluster_read"] = status + if cluster is None: + return receipt + receipt["cluster_matches_expected"] = cluster.get("metadata", {}).get("uid") == CLUSTER_UID + if not receipt["cluster_matches_expected"]: + return receipt + + nodes, status = kube("get", "nodes") + receipt["nodes_read"] = status + if nodes is not None: + matching = [ + n for n in nodes.get("items", []) + if any(a.get("address") == "92.205.62.239" + for a in n.get("status", {}).get("addresses", [])) + ] + receipt["node_address_matches_expected"] = len(matching) == 1 + if len(matching) == 1: + conditions = {c.get("type"): c.get("status") + for c in matching[0].get("status", {}).get("conditions", [])} + receipt["node_ready"] = conditions.get("Ready") == "True" + receipt["node_pressure_clear"] = all( + conditions.get(c) == "False" + for c in ("MemoryPressure", "DiskPressure", "PIDPressure") + ) + + deployment, status = kube("get", "deployment", RELEASE, "-n", NAMESPACE) + receipt["deployment_read"] = status + if deployment is not None: + spec = deployment.get("spec", {}) + pod = spec.get("template", {}).get("spec", {}) + app = next((c for c in pod.get("containers", []) if c.get("name") == "app"), {}) + ready = deployment.get("status", {}) + receipt["deployment"] = { + "image_matches_expected": app.get("image") == IMAGE, + "replicas": spec.get("replicas"), + "ready_replicas": ready.get("readyReplicas", 0), + "updated_replicas": ready.get("updatedReplicas", 0), + "generation_observed": ready.get("observedGeneration", 0) + >= deployment.get("metadata", {}).get("generation", 1), + "recreate": spec.get("strategy", {}).get("type") == "Recreate", + "mounts_match_expected": all( + any(v.get("name") == name + and v.get("persistentVolumeClaim", {}).get("claimName") == claim + for v in pod.get("volumes", [])) + for name, claim in CLAIMS.items() + ) and all( + any(m.get("name") == name and m.get("mountPath") == path + for m in app.get("volumeMounts", [])) + for name, path in (("media", "/app/media"), ("app-state", "/app/.issue-facade")) + ), + } + + receipt["claims"] = {} + for name, claim in CLAIMS.items(): + data, status = kube("get", "pvc", claim, "-n", NAMESPACE) + row = {"read": status} + if data is not None: + row["bound"] = data.get("status", {}).get("phase") == "Bound" + row["identity_present"] = bool(data.get("metadata", {}).get("uid")) + receipt["claims"][name] = row + + database, status = kube("get", "cluster", "apps-pg", "-n", "databases") + receipt["database_cluster_read"] = status + if database is not None: + db_status = database.get("status", {}) + receipt["database_ready"] = ( + db_status.get("instances", 0) > 0 + and db_status.get("readyInstances", 0) == db_status.get("instances") + and bool(db_status.get("currentPrimary")) + ) + + releases, status = read_json([ + "helm", "list", "--all", "--namespace", NAMESPACE, + "--filter", "^vergabe-teilnahme$", "--output", "json", + ]) + receipt["helm_read"] = status + if isinstance(releases, list): + rows = [r for r in releases if r.get("name") == RELEASE] + if len(rows) == 1: + receipt["helm"] = { + "deployed": rows[0].get("status") == "deployed", + "revision": int(rows[0]["revision"]), + "chart_matches_expected": rows[0].get("chart") == "vergabe-teilnahme-0.2.1", + } + + dep = receipt.get("deployment", {}) + release = receipt.get("helm", {}) + receipt["native_inventory_complete"] = ( + receipt.get("node_address_matches_expected") is True + and receipt.get("node_ready") is True + and receipt.get("node_pressure_clear") is True + and dep.get("image_matches_expected") is True + and dep.get("replicas") == dep.get("ready_replicas") == dep.get("updated_replicas") == 1 + and dep.get("generation_observed") is True + and dep.get("recreate") is True and dep.get("mounts_match_expected") is True + and all(c.get("bound") is True and c.get("identity_present") is True + for c in receipt["claims"].values()) + and receipt.get("database_ready") is True + and release.get("deployed") is True and release.get("chart_matches_expected") is True + ) + return receipt + + +def main(): + try: + receipt = collect() + except Exception: + receipt = {"schema": "railiance.vergabe-pilot-native-inventory.v1", + "native_inventory_complete": False, "failure": "invalid_inventory_response", + "runtime_mutations": False} + print(json.dumps(receipt, indent=2)) + return 0 if receipt["native_inventory_complete"] else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/workplans/RAPPS-WP-0014-vergabe-invited-pilot.md b/workplans/RAPPS-WP-0014-vergabe-invited-pilot.md index a520a8c..4d0142c 100644 --- a/workplans/RAPPS-WP-0014-vergabe-invited-pilot.md +++ b/workplans/RAPPS-WP-0014-vergabe-invited-pilot.md @@ -196,7 +196,7 @@ id: RAPPS-WP-0014-T03 status: wait needs_human: true decision_id: d06307e5-2737-461a-a2f4-a12c3b492a9a -intervention_note: "Both accounts sign in; created records and uploaded document are visible from the other account (user-confirmed 2026-11-05). Next: remaining task/local-issue and negative-access checks, then populated restart, coherent off-host recovery, isolated restore, rollback and ownership. Document UI review stays pending; session/OTP refinement is deferred. railiance-platform owns storage/custody delivery." +intervention_note: "Both accounts sign in and see created records, the uploaded document and LOKAL issue (2026-11-05). Fixture: Ausschreibung/task/document 1. Remaining shared task edit, approval/submission and access checks precede populated restart/recovery. Exact metadata-only inventory packet is prepared; current caller lacks native reads. Off-host recovery, isolated restore, rollback and ownership remain open. Document UI review stays pending; session/OTP refinement is deferred." priority: high assignee: the-custodian blocking_reason: "Await populated two-user/document workflow, coherent PostgreSQL/media/issue-state off-host recovery point, isolated restored workflow, rollback evidence and support ownership. September 25 proves database restore and restart only." @@ -300,6 +300,27 @@ acceptance, byte/hash equality, anonymous refusal and populated recovery are not inferred. Requested the task's local issue badge/key check from both sessions next. T03 retains its remaining workflow, recovery and ownership scope. +2026-11-05 local-issue return: the user confirms the LOKAL badge is visible +from both accounts and supplies task/document URLs, binding Ausschreibung 1, +task 1 and document 1. Receipt: +`docs/evidence/2026-11-05-vergabe-local-issue-return.json`. Signed-out HTTP reads +of these identified tender/task/document pages reach the NetKingdom login page; +the document-detail URL is not the binary file URL, so direct file refusal +remains unverified. Requested the remaining shared task-edit and domain +approval/demo-submission results together. + +Prepared `tools/vergabe_pilot_inventory.py` and exact metadata-only action +`docs/proposals/2026-11-05-vergabe-recovery-preflight/action.json` for the +existing attended executor. Cluster UID matches, but the current caller's +native inventory remains incomplete (node/Deployment/PVC/CNPG reads forbidden, +Helm listing failed). No privileged proposal was executed; `gexec` is absent +from this workstation. Source compilation/synthetic success, wrong-cluster, +image-drift and output-canary checks pass. This is a preflight packet, not the +capture/restart operation. Existing database-only helpers omit both file stores; +the platform primary-archive helper currently has no Vergabe application profile. +Off-host publication/readback still needs its owner-bound storage/custody path. +T03 remains wait for the remaining workflow and operational recovery evidence. + ## Validate synthetic sizing against the exact release and shared contention