Record functional pilot walkthrough and prepare attended inventory handoff
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1abf1-7e95-7141-87c9-ee78332bf790
This commit is contained in:
tegwick 2026-11-05 22:50:58 +01:00
parent 177a85d05f
commit fab81f0086
5 changed files with 221 additions and 6 deletions

View file

@ -0,0 +1,29 @@
{
"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 in the active conversation",
"evidence_kind": "user_reported",
"fixture": {"ausschreibung_id": 1, "aufgabe_id": 1, "dokument_id": 1},
"cross_account_task_editing_works": true,
"user_reports_flow_completed": true,
"final_recorded_phase_user_label": "Zuschlag/Nachlese",
"user_assessment": "The flow needs work, but the functionality present seems to work quite well.",
"functional_walkthrough_status": "confirmed_with_ui_followup",
"ui_review_status": "open",
"individual_approval_records_independently_verified": false,
"native_inventory_complete": false,
"populated_restart_or_recovery_verified": false,
"session_or_otp_policy_changed": false,
"runtime_mutations_by_agent": false,
"remaining_work": [
"Attended native release/storage inventory",
"File checksum and direct anonymous-download refusal",
"Owner-run inactive-session denial",
"Populated pod-replacement verification",
"Coherent PostgreSQL/media/issue-state off-host recovery point",
"Isolated restored workflow and compatible rollback",
"Backup and incident/support ownership, cadence, retention and recovery objectives"
]
}

View file

@ -36,6 +36,36 @@ 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.
## Founder-terminal handoff
The user has now confirmed cross-account task edits and the functional flow
through Zuschlag/Nachlese, with UI follow-up. The remaining first step is native
inventory. A restricted SSH check as `custodian-agt` was refused at authentication;
the agent did not try an elevated account or issue its own certificate.
From a synced `railiance-apps` checkout on **railiance01**, run:
```bash
python3 tools/vergabe_pilot_inventory_attended.py --submit
```
The launcher asks for the **path** to the existing gexec approver key. The normal
gexec review/signature step follows. It stages the fixed proposal in the existing
exchange directory, then uses `sudo -u custodian-agt` only to enqueue the signed
request under the required UID and read its sanitized result. Kubernetes access
runs through gexec's configured `kube:railiance01` target. The founder needs the
existing exchange access and ability to use that restricted account via sudo.
No new key, standing grant or privileged kubeconfig is installed. Keep the
approver key in its existing custody. If it is held only on the workstation,
sign the action there with the existing gexec CLI and supply that public signed
approval on the node with `--submit --approval /path/to/approval.json`; do not
copy the private key to the node. Gexec verifies the signature and its expiry.
Share the final result JSON. A pending result retains its request ID; inspect
that request before submitting again. The launcher defaults to local packet
review when `--submit` is omitted and refuses a changed action digest. The
existing gated executor remains responsible for signature/nonce/profile checks.
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,

View file

@ -10,8 +10,12 @@ 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. 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.
binds Ausschreibung 1, task 1 and document 1 from their supplied URLs. The user
now confirms cross-account task editing and the flow through documented
**Zuschlag/Nachlese**, with further UI work needed; see the
[functional-flow return](evidence/2026-11-05-vergabe-functional-flow-return.json).
The functional walkthrough is confirmed with UI follow-up. The instructions
below remain the reproduction sequence for the populated restart/restore drill.
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
@ -107,8 +111,8 @@ and [deployment binding](vergabe-demo-company-binding.md). Its receipt must cove
Existing database-only backup/restore targets default to historical `vergabe_db`
and omit both file stores. They cannot establish this three-store recovery claim.
Two-account sign-in and cross-account visibility of newly created records are
user-confirmed; the remaining shared workflow is pending. The attended restart,
The two-account functional walkthrough is user-confirmed with UI follow-up.
The attended restart,
off-host capture, isolated restore and rollback action has not been prepared or executed.
Logged-in account deactivation remains a separate owner-run access-control check
from the pilot contract. No account is disabled as part of these walkthrough steps.

View file

@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""Founder-terminal review, signing and submission of the fixed pilot inventory.
Run on railiance01 from a synced railiance-apps checkout. Default only reviews
the packet. --submit uses the existing approver key and gated executor; the
request is staged as custodian-agt so the executor's submitter check is retained.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import stat
import subprocess
import uuid
from pathlib import Path
EXPECTED = "sha256:2014e62a84b0875fec155dbfd1be26963d9a39ab931092d26c5dfb9e627ffbd7"
PACKET = (Path(__file__).resolve().parents[1]
/ "docs/proposals/2026-11-05-vergabe-recovery-preflight/action.json")
GEXEC = Path("/usr/local/bin/gexec")
EXCHANGE = Path("/var/spool/gexec/exchange")
SPOOL = "/var/spool/gexec"
READ_RESULT = """
import json, pathlib, re, sys, time
request_id = sys.argv[1]
if not re.fullmatch(r'vergabe-inventory-[0-9a-f]{16}', request_id):
raise SystemExit(1)
directory = pathlib.Path('/var/spool/gexec/results')
for attempt in range(60):
matches = list(directory.glob(request_id + '.*.json'))
if len(matches) == 1:
result = json.loads(matches[0].read_text())
print(json.dumps(result, indent=2))
raise SystemExit(0 if result.get('status') == 'executed'
and result.get('exit_code') == 0 else 1)
if len(matches) > 1:
print(json.dumps({'status': 'ambiguous_result', 'request_id': request_id}))
raise SystemExit(1)
time.sleep(1)
print(json.dumps({'status': 'result_pending', 'request_id': request_id,
'instruction': 'Preserve this request id; inspect its result before resubmitting.'}))
raise SystemExit(2)
"""
def validated_packet(path=PACKET):
packet = json.loads(path.read_text())
encoded = json.dumps(packet["action"], sort_keys=True, separators=(",", ":"),
ensure_ascii=False, allow_nan=False).encode()
digest = "sha256:" + hashlib.sha256(encoded).hexdigest()
if digest != EXPECTED or packet.get("digest") != EXPECTED:
raise ValueError("packet_digest_mismatch")
return packet
def submit(packet, key=None, approval_file=None):
info = GEXEC.resolve(strict=True).stat()
if info.st_uid != 0 or not stat.S_ISREG(info.st_mode) or info.st_mode & 0o022:
raise ValueError("admitted_root_owned_gexec_required")
if EXCHANGE.is_symlink() or not EXCHANGE.is_dir():
raise ValueError("admitted_exchange_required")
request_id = "vergabe-inventory-" + uuid.uuid4().hex[:16]
proposal = EXCHANGE / (request_id + ".action.json")
approval = EXCHANGE / (request_id + ".approval.json")
fd = os.open(proposal, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o644)
with os.fdopen(fd, "w") as output:
json.dump(packet, output, indent=2)
if approval_file is not None:
signed = json.loads(approval_file.read_text())
if signed.get("payload", {}).get("digest") != EXPECTED:
raise ValueError("approval_digest_mismatch")
fd = os.open(approval, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o644)
with os.fdopen(fd, "w") as output:
json.dump(signed, output)
else:
# The existing CLI displays the exact action and asks the founder to sign.
approved = subprocess.run([
str(GEXEC), "approve", str(proposal), "--key", str(key),
"--approver", "bernd", "--ttl", "900", "--out", str(approval),
], check=False)
if approved.returncode:
print(json.dumps({"status": "not_submitted", "request_id": request_id}))
return 1
queued = subprocess.run([
"/usr/bin/sudo", "-u", "custodian-agt", str(GEXEC), "submit",
"--spool", SPOOL, "--proposal", str(proposal), "--approval", str(approval),
"--request-id", request_id,
], check=False)
if queued.returncode:
print(json.dumps({"status": "submission_unconfirmed", "request_id": request_id,
"instruction": "Inspect this request before creating another."}))
return 1
return subprocess.run([
"/usr/bin/sudo", "-u", "custodian-agt", "/usr/bin/python3", "-c",
READ_RESULT, request_id,
], check=False).returncode
def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--submit", action="store_true")
signing = parser.add_mutually_exclusive_group()
signing.add_argument("--key", type=Path, help="Path to the existing gexec approver key")
signing.add_argument("--approval", type=Path, help="Existing signed approval; keeps key in its custody")
args = parser.parse_args(argv)
packet = validated_packet()
print(json.dumps({"status": "prepared", "digest": EXPECTED,
"scope": "Read-only Vergabe deployment/storage inventory",
"target": "kube:railiance01", "runtime_mutations": False}), flush=True)
if not args.submit:
return 0
if not GEXEC.is_file():
raise ValueError("admitted_gexec_missing")
if args.key is None and args.approval is None:
args.key = Path(input("Path to your existing gexec approver key: ").strip()).expanduser()
return submit(packet, args.key, args.approval)
if __name__ == "__main__":
try:
raise SystemExit(main())
except ValueError as error:
failure = str(error)
if failure not in {"packet_digest_mismatch", "approval_digest_mismatch", "admitted_gexec_missing",
"admitted_root_owned_gexec_required", "admitted_exchange_required"}:
failure = "invalid_packet_or_runtime"
print(json.dumps({"status": "launcher_failed", "failure": failure}))
raise SystemExit(1)
except Exception:
print(json.dumps({"status": "launcher_failed", "failure": "attended_runtime_unavailable"}))
raise SystemExit(1)

View file

@ -196,10 +196,10 @@ id: RAPPS-WP-0014-T03
status: wait
needs_human: true
decision_id: d06307e5-2737-461a-a2f4-a12c3b492a9a
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."
intervention_note: "Two-account functional walkthrough through Zuschlag/Nachlese is user-confirmed with UI follow-up (2026-11-05); task edits, document and LOKAL issue work. Fixture: Ausschreibung/task/document 1. Next: run the prepared attended native inventory; current caller lacks native reads and restricted agent SSH authentication was refused. Populated restart/off-host recovery, isolated restore, rollback, access evidence and operating ownership remain open."
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."
blocking_reason: "Two-account functional walkthrough is confirmed with UI follow-up. Await attended native inventory, populated restart, coherent PostgreSQL/media/issue-state off-host recovery, isolated restored workflow, rollback/access evidence and operating ownership. September 25 proves database-only restore and restart."
state_hub_task_id: "dd069c6d-fcc1-5bac-b233-976f2f0d5cd1"
```
@ -321,6 +321,23 @@ 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.
2026-11-05 functional-flow return: the user confirms task editing works and
reports completing the flow through documented Zuschlag/Nachlese. Existing
functionality works quite well in their review, while UI work remains. Receipt:
`docs/evidence/2026-11-05-vergabe-functional-flow-return.json`. The two-account
functional walkthrough is now confirmed with UI follow-up; no repeat baseline
walkthrough is requested. Native recovery, byte/access checks and operating
ownership remain open, and the broader decision/task are not closed.
The routed restricted SSH check as custodian-agt was refused at authentication;
no elevated account was attempted. Route `agt-custodian-ssh-cert` identifies
supervisor-only issuance. Prepared
`tools/vergabe_pilot_inventory_attended.py` for the founder terminal on
railiance01: it validates the exact existing proposal, obtains the normal gexec
signature, submits as custodian-agt to preserve the request UID check, and
returns the sanitized result. Default mode only reviews the packet. This
prepares an attended read-only inventory, not permission for a snapshot/restart.
## Validate synthetic sizing against the exact release and shared contention