Prepare the RPF-WP-0015-T03 synthetic E2 window: receipt generator and readiness record.
All checks were successful
CCR intake gate / gate (push) Successful in 13s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 236506@bnt-lap001
Assistant-Session: 09087018-91e1-48c5-ab2f-acfbb62c92e4
This commit is contained in:
codex 2026-11-03 14:29:57 +01:00
parent 7624cfcdca
commit a7392437e1
3 changed files with 107 additions and 1 deletions

View file

@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Write the RPF-WP-0015-T03 synthetic E2 approval receipt for one founder window.
Usage: scripts/rpf-wp-0015-e2-receipt.py OUTPUT.json [--start-in-min N]
The window is 15 minutes and starts now, or N minutes from now. Writing the
receipt and running the window is the founder GO for that window; D5
(2fd5849c) authorizes the exercise class only. The owner acknowledgements are
the round-9 approvals of review contract 014627d6 (audit-core 0d0da0d2,
rapp-postgres 6d047a08, railiance-platform c4a80a60). They stay valid only
while the pinned artifacts in interfaces/RAILIANCE-WP-0024-owner-reviews.json
are unchanged; the script refuses otherwise. The receipt holds no credential
values. It refuses to overwrite an existing file.
"""
import hashlib
import json
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
pins = json.loads((ROOT / "interfaces/RAILIANCE-WP-0024-owner-reviews.json").read_text())["artifact_sha256"]
drift = [p for p, h in pins.items() if hashlib.sha256((ROOT / p).read_bytes()).hexdigest() != h]
if drift:
sys.exit("owner-reviewed artifacts changed since contract 014627d6; new owner approvals needed: " + ", ".join(drift))
if len(sys.argv) < 2:
sys.exit(__doc__)
out = sys.argv[1]
delay = int(sys.argv[sys.argv.index('--start-in-min') + 1]) if '--start-in-min' in sys.argv else 0
start = datetime.now(timezone.utc).replace(second=0, microsecond=0) + timedelta(minutes=delay)
end = start + timedelta(minutes=15)
tag = start.strftime('%Y%m%dt%H%M')
z = lambda t: t.strftime('%Y-%m-%dT%H:%M:%SZ')
receipt = {
"procedure": "audit-core-database-lease-recovery",
"task_id": "RAILIANCE-WP-0024-T03",
"status": "approved",
"approval_id": f"rpf-wp-0015-t03-e2-{tag}z-founder-go",
"window": {"start": z(start), "end": z(end)},
"abort_operator": "founder (platform-root), attended",
"owners": {
"audit-core": {"acknowledged": True, "message_id": "0d0da0d2-36a1-4ffc-b7bf-7896fad691f6"},
"rapp-postgres": {"acknowledged": True, "message_id": "6d047a08-e9c8-4dbf-95b9-dca039791329"},
"railiance-platform": {"acknowledged": True, "message_id": "c4a80a60-a415-4f2b-8928-751407473641"},
},
"synthetic_load": {"contract_id": "RAILIANCE-WP-0024-T02-SYNTHETIC-01",
"driver_revision": "sha256:941ba251f638869626b06e9cbf430c70188c0bdf4715e3eff9c7610366f68662"},
"synthetic_sender": {"identity": f"t03-e2-synthetic-sender-{start:%Y%m%d}",
"source": f"t03-e2-synthetic-{start:%Y%m%d}",
"tenant": f"tenant:trial:t03-e2-synthetic-{start:%Y%m%d}",
"expires_at": z(end)},
}
with open(out, 'x') as f:
json.dump(receipt, f, indent=2); f.write('\n')
print(json.dumps({"approval_id": receipt["approval_id"], "window": receipt["window"],
"confirm": f"audit-core-synthetic-sender:{receipt['approval_id']}:attended"}))

View file

@ -0,0 +1,35 @@
"""The E2 receipt generator emits a receipt the sender lane accepts as T03."""
import importlib.util
import json
import subprocess
import sys
from datetime import UTC, datetime
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def lane():
sys.path.insert(0, str(ROOT / "scripts"))
spec = importlib.util.spec_from_file_location("lane_e2", ROOT / "scripts/audit_core_synthetic_sender_lane.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_receipt_is_accepted_by_the_lane_as_t03(tmp_path):
out = tmp_path / "approval.json"
printed = json.loads(subprocess.run([sys.executable, str(ROOT / "scripts/rpf-wp-0015-e2-receipt.py"), str(out)],
check=True, capture_output=True, text=True).stdout)
m = lane()
approval, spec = m.load_receipt(out, datetime.now(UTC), require_open_window=True)
assert approval["task_id"] == "RAILIANCE-WP-0024-T03"
assert printed["confirm"] == m.confirm_token(approval)
def test_refuses_to_overwrite(tmp_path):
out = tmp_path / "approval.json"
out.write_text("{}")
result = subprocess.run([sys.executable, str(ROOT / "scripts/rpf-wp-0015-e2-receipt.py"), str(out)],
capture_output=True, text=True)
assert result.returncode != 0 and out.read_text() == "{}"

View file

@ -9,7 +9,7 @@ flavor: residual
owner: codex
topic_slug: railiance
created: "2026-08-22"
updated: "2026-10-14"
updated: "2026-11-03"
related:
- AUDIT-WP-0008
- WH-ENG-20260822-AUDIT-E2-01
@ -535,3 +535,18 @@ and warden failed closed with self-revocation. Nothing was projected and audit-c
untouched. Next: sign out of KeyCape (or use a fresh private window), complete
password+OTP (aal2), reissue a near E2 receipt and run the launcher once. T03 stays
`wait` until that E2 evidence exists.
E2 window prepared 2026-11-03. No new owner round is needed. Receipt
acknowledgements are the owners' approval receipts for the review contract,
not per-window sign-offs; T02 used the `ea3f53d6` approvals the same way. All
pinned artifacts in `interfaces/RAILIANCE-WP-0024-owner-reviews.json` still
match round 9 (contract `014627d6`: audit-core `0d0da0d2`, rapp-postgres
`6d047a08`, railiance-platform `c4a80a60`). The load driver
(`audit-core/scripts/t02_synthetic_load_driver.py`) still has revision
`sha256:941ba251…`. The installed ops-warden contains `6e14300`.
`scripts/rpf-wp-0015-e2-receipt.py` writes the 15-minute receipt and prints
the confirm string. It refuses if any pinned artifact has changed, and the
sender lane accepts its receipt as T03
(`tests/test_rpf_wp_0015_e2_receipt.py`). The 2026-10-14/15 blocker was the
KeyCape session at `aal1`. The window needs a fresh password+OTP (`aal2`)
login, and its start must not fall within 20 minutes of 03:17 UTC.