Prepare offline attended provisioning checklists and route remaining custody gates
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10391-6355-7b41-b0fa-3e14e874f091
This commit is contained in:
codex 2026-10-04 00:27:00 +02:00
parent 7c1a5c2516
commit 46eb8bea9e
6 changed files with 221 additions and 4 deletions

View file

@ -0,0 +1,13 @@
# Attended provisioning preparation contract
Owner: railiance-platform, RPF-WP-0050. Origin: CUST-WP-0075 P2/P3.
`python3 scripts/credential-change.py window-checklist --window-file <metadata.yaml> --workplan-root <owner/workplans>` joins open non-residual task blocks carrying the exact decision UUID to explicit CCR, lane and owner-receipt contracts. Repeat `--workplan-root` for each repo in scope. Roots are the authoritative file snapshot; Hub qualifiers are independently verified before any attended window. No implicit completeness claim exists for omitted roots.
The metadata manifest has `decision_id`, `date` (YYYY-MM-DD), and `entries`; each entry has `task_id`, `ccr`, `lane_doc`, `receipt_contract`. The three file references are relative to this platform repo and must exist; the receipt contract names the owner’s required positive/negative/revocation/cleanup evidence, never values. Empty manifests/decisions, missing joins or any non-approved CCR produce exit 2 and NOT READY. Duplicate IDs and stale task joins are refused. Applied CCRs are never replay candidates. Arbitrary CCR content is never printed.
Each item must additionally bind the exact reviewed revision/digest, expected result, native preflight, abort owner, verification and rollback in the existing CCR/supervision receipt. REVIEW PREPARED is a source-join result, not execution permission. Upstream owner gates and live readiness must be checked again in the approved dated window. The Custodian owns cadence; this renderer schedules nothing and executes nothing. A resolved packet calls for owner re-check, not unconditional task completion.
## P3 draft for founder ruling
The current rule remains attended value custody. Choose explicitly between (a) batched attended custody, (b) response-wrapped issuer delivery with one-time unwrap, and (c) a scoped MFA writer through a silent contained helper. Review exact actor/path/audience, TTL/single use, audit boundary, child-output suppression, crash cleanup, replay refusal and rollback before adopting b/c. Wrapped tokens are credentials and stay out of files, Hub, chat and logs. No option grants broad write or changes existing lanes. RPF-WP-0050-T02 is routed to D3; a generic D3 resolution still requires a specific value-path disposition, so lack of an explicit answer does not unblock it.

View file

@ -2240,6 +2240,12 @@ def build_parser() -> argparse.ArgumentParser:
)
applier_apply.set_defaults(func=command_applier_apply)
from provisioning_window import command_window
window = sub.add_parser("window-checklist", help="Render an offline attended-window preparation checklist; never execute")
window.add_argument("--window-file", required=True)
window.add_argument("--workplan-root", action="append", required=True)
window.set_defaults(func=command_window)
runbook = sub.add_parser(
"runbook",
help="Render or execute the attended operator apply/verify runbook",

View file

@ -0,0 +1,80 @@
"""Offline, metadata-only attended-window checklist; never executes a CCR."""
from pathlib import Path
import re
from uuid import UUID
import yaml
def task_sources(roots, decision_id):
selected = {}
for root in roots:
for path in sorted(Path(root).glob("*.md")):
text = path.read_text()
front = re.match(r"---\n(.*?)\n---", text, re.S)
meta = yaml.safe_load(front.group(1)) if front else {}
if meta and (meta.get("flavor") == "residual" or meta.get("status") in {"finished", "archived"}):
continue
for block in re.findall(r"```task\s*\n(.*?)\n```", text, re.S):
task = yaml.safe_load(block)
if str(task.get("decision_id", "")) == decision_id and task.get("status") not in {"done", "cancel"}:
task_id = task["id"]
if task_id in selected:
raise ValueError("duplicate task id: " + task_id)
selected[task_id] = task
return selected
def local_file(repo, value):
path = (repo / value).resolve()
if not path.is_relative_to(repo.resolve()) or not path.is_file():
raise ValueError("missing or outside-repository preparation file")
return path
def render_window(window_file, roots, repo):
window = yaml.safe_load(Path(window_file).read_text())
decision = str(UUID(str(window["decision_id"])))
date = str(window["date"])
# A dated manifest is preparation, never a scheduled event or approval.
if not re.fullmatch(r"\d{4}-\d{2}-\d{2}", date):
raise ValueError("window date must be YYYY-MM-DD")
tasks = task_sources(roots, decision)
entries = window.get("entries", [])
by_task = {}
for entry in entries:
tid = entry["task_id"]
if tid in by_task:
raise ValueError("duplicate window task")
by_task[tid] = entry
stale = set(by_task) - set(tasks)
if stale:
raise ValueError("manifest tasks absent from current decision: " + ", ".join(sorted(stale)))
lines = ["# Attended provisioning preparation: " + date,
"Decision: " + decision,
"Preparation only. Founder window approval and each exact CCR revision remain required."]
ready = bool(tasks)
for index, tid in enumerate(sorted(tasks), 1):
entry = by_task.get(tid)
if not entry:
lines.append(f"{index}. {tid}: NOT READY — missing CCR/lane/receipt join.")
ready = False
continue
ccr = yaml.safe_load(local_file(repo, entry["ccr"]).read_text())
local_file(repo, entry["lane_doc"])
local_file(repo, entry["receipt_contract"])
eligible = ccr.get("status") == "approved"
ready &= eligible
label = "REVIEW PREPARED" if eligible else "NOT READY (CCR is not approved; never replay applied custody)"
# Deliberately avoid rendering arbitrary CCR contents or secret-bearing values.
lines.extend([f"{index}. {tid}: {label}",
" CCR: " + entry["ccr"], " Lane: " + entry["lane_doc"],
" Owner receipt contract: " + entry["receipt_contract"],
" Revalidate exact digest, upstream gates and native readiness before attended execution."])
lines.append("Checklist complete: " + str(ready).lower())
return "\n".join(lines) + "\n", ready
def command_window(args):
output, ready = render_window(args.window_file, args.workplan_root, Path(__file__).resolve().parents[1])
print(output, end="")
return 0 if ready else 2

View file

@ -0,0 +1,55 @@
import importlib.util
from pathlib import Path
import tempfile
import unittest
import yaml
SPEC = importlib.util.spec_from_file_location("window", Path(__file__).resolve().parents[1] / "scripts/provisioning_window.py")
module = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(module)
D = "62aa95e4-3ccc-4867-8806-5e3db005b483"
class WindowTests(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.addCleanup(self.tmp.cleanup)
self.root = Path(self.tmp.name)
self.plans = self.root / "workplans"
self.plans.mkdir()
self.task = "```task\nid: EX-WP-0001-T01\nstatus: wait\ndecision_id: " + D + "\n```\n"
(self.plans / "plan.md").write_text(self.task)
for name in ("lane.md", "receipt.md"):
(self.root / name).write_text("non-secret contract")
self.window = {"decision_id": D, "date": "2026-10-03", "entries": [{"task_id": "EX-WP-0001-T01", "ccr": "ccr.yaml", "lane_doc": "lane.md", "receipt_contract": "receipt.md"}]}
self.ccr("approved")
def ccr(self, status):
(self.root / "ccr.yaml").write_text(yaml.safe_dump({"status": status, "secret": "NEVER_PRINT_THIS"}))
def render(self):
p = self.root / "window.yaml"
p.write_text(yaml.safe_dump(self.window))
return module.render_window(p, [self.plans], self.root)
def test_approved_join_and_no_values(self):
output, ready = self.render()
self.assertTrue(ready)
self.assertNotIn("NEVER_PRINT_THIS", output)
def test_applied_custody_cannot_replay(self):
self.ccr("applied")
self.assertFalse(self.render()[1])
def test_missing_join_stays_not_ready(self):
self.window["entries"] = []
self.assertFalse(self.render()[1])
def test_stale_join_refused(self):
(self.plans / "plan.md").write_text(self.task.replace("status: wait", "status: done"))
with self.assertRaises(ValueError): self.render()
def test_duplicate_authority_refused(self):
(self.plans / "duplicate.md").write_text(self.task)
with self.assertRaises(ValueError): self.render()
def test_outside_contract_refused(self):
self.window["entries"][0]["lane_doc"] = "../outside.md"
with self.assertRaises(ValueError): self.render()
def test_residual_not_selected(self):
(self.plans / "plan.md").write_text("---\nflavor: residual\n---\n" + self.task)
self.window["entries"] = []
self.assertFalse(self.render()[1])
if __name__ == "__main__": unittest.main()

View file

@ -71,7 +71,7 @@ issuer/audience/subject/claims and unrelated secret access fail, bounded TTL
and revocation are proven, the consumer explicitly opts into the verified
contract, and rollback/cleanup receipts contain no secret material.
2026-10-03: human gate routed to hub decision 62aa95e4-3ccc-4867-8806-5e3db005b483 (D1: Attended OpenBao provisioning window: one session clears the pending credential lanes) by CUST-WP-0075; resolving that decision raises C-40 here.
2026-10-03: the historical D1 routing was withdrawn because provision/delivery already completed. Owner routing now uses decision `ab85ae8a-12a0-4945-ae64-1d3d86d5b26f`, specifically independent archive readback and coordinated interim audit bearer revocation acceptance. `needs_human` and the wait remain; resolving a decision requires owner receipt re-check, not automatic completion.
2026-10-03 (CUST-WP-0075): gate check. SECRETS-WP-0007-T04 is done (2026-09-27), so the gate named in the design is open. It is not the last one: the design says to confirm the exact live client registration and protected client-side custody before writing, and positive-login proof needs a real KeyCape service JWT. KeyCape's contract (`openbao-service-auth-contract.md`) and KEY-WP-0009 claim source registration only; the live registration of `secrets-engine-openbao` and its client-credential custody are "a separate approved custody action" that no workplan task owns yet (SECRETS-WP-0008-T06 waits on this task). Re-qualified with `blocked_on: message-from:key-cape`; key-cape was asked for the owning task id, which will replace it as `depends_on`. The design stays proposed and no child command was written. Not approvable in D1 yet.
@ -108,7 +108,7 @@ only the exact matrix, negative/expiry/revocation checks pass, custody is seeded
through the separate writer, and the verified route has a safe handoff receipt.
The unattended adapter remains a separate demand and gets no operator session.
2026-10-03: human gate routed to hub decision 62aa95e4-3ccc-4867-8806-5e3db005b483 (D1: Attended OpenBao provisioning window: one session clears the pending credential lanes) by CUST-WP-0075; resolving that decision raises C-40 here.
2026-10-03: the historical D1 routing was withdrawn because provision/delivery already completed. Owner routing now uses decision `ab85ae8a-12a0-4945-ae64-1d3d86d5b26f`, specifically independent archive readback and coordinated interim audit bearer revocation acceptance. `needs_human` and the wait remain; resolving a decision requires owner receipt re-check, not automatic completion.
## Accept the needed signing lane and deliver it to the owning runtime
@ -432,7 +432,7 @@ receipts to HFACT-WP-0001-T03, SECRETS-WP-0009-T03 and APPROVAL-WP-0002-T01/T05.
Audit receiver/sender custody remains AUDIT-WP-0009-T09 and must not be folded
into this client-identity grant.
2026-10-03: human gate routed to hub decision 62aa95e4-3ccc-4867-8806-5e3db005b483 (D1: Attended OpenBao provisioning window: one session clears the pending credential lanes) by CUST-WP-0075; resolving that decision raises C-40 here.
2026-10-03: the historical D1 routing was withdrawn because provision/delivery already completed. Owner routing now uses decision `ab85ae8a-12a0-4945-ae64-1d3d86d5b26f`, specifically independent archive readback and coordinated interim audit bearer revocation acceptance. `needs_human` and the wait remain; resolving a decision requires owner receipt re-check, not automatic completion.
## Admit the two factory audit-sender custody lanes
@ -440,6 +440,7 @@ into this client-identity grant.
id: RPF-WP-0035-T08
status: wait
needs_human: true
decision_id: "ab85ae8a-12a0-4945-ae64-1d3d86d5b26f"
blocking_reason: "Provisioned and delivered 2026-09-11 (CCR-2026-0021/0022 applied); do not repeat. Receiver rollout with evidence_kind and native producer acceptance passed 2026-09-11. Remaining human acceptance: independent archive readback and revocation of the interim audit bearer tokens."
priority: high
assignee: railiance-platform
@ -469,7 +470,7 @@ Approval Engine's missing namespace remains its owner prerequisite.
[Preparation evidence](../docs/evidence/2026-09-11-factory-audit-custody.json).
Do not fold these senders into CCR-2026-0019 or the cancelled CCR-2026-0020.
2026-10-03: human gate routed to hub decision 62aa95e4-3ccc-4867-8806-5e3db005b483 (D1: Attended OpenBao provisioning window: one session clears the pending credential lanes) by CUST-WP-0075; resolving that decision raises C-40 here.
2026-10-03: the historical D1 routing was withdrawn because provision/delivery already completed. Owner routing now uses decision `ab85ae8a-12a0-4945-ae64-1d3d86d5b26f`, specifically independent archive readback and coordinated interim audit bearer revocation acceptance. `needs_human` and the wait remain; resolving a decision requires owner receipt re-check, not automatic completion.
## Dependency review — 2026-09-06

View file

@ -0,0 +1,62 @@
---
id: RPF-WP-0050
type: workplan
title: "Prepare generated attended windows and explicit value-custody choice"
domain: financials
repo: railiance-platform
status: active
owner: codex
topic_slug: platform-services
created: "2026-10-03"
updated: "2026-10-03"
flavor: implementation
---
Origin: Custodian proposal f84637fa-2355-4971-a2dc-7f1fd01710a8, CUST-WP-0075 P2/P3. Platform accepts bounded preparation; Custodian owns cadence, founder owns custody policy and live authorization. No recurring event is enabled.
## Render offline decision-task-CCR preparation checklist
```task
id: RPF-WP-0050-T01
status: done
priority: high
```
Implemented `credential-change.py window-checklist` and `scripts/provisioning_window.py`. Reads authoritative open non-residual task blocks and explicit CCR/lane/receipt contracts. Missing/stale joins and non-approved/applied CCRs prevent a complete checklist. No execution, scheduling, credential reads or printing arbitrary CCR contents. Contract: `docs/attended-provisioning-window.md`. Synthetic regression suite verifies actual decision joins and replay refusal. This completes the bounded source renderer, not a real attended session.
## Obtain exact founder value-custody choice
```task
id: RPF-WP-0050-T02
status: wait
priority: high
needs_human: true
decision_id: "1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa"
blocking_reason: "Founder must explicitly choose batched attended custody, response-wrapped issuer delivery, or scoped MFA silent writer, and approve its exact boundary. Generic D3 resolution without this disposition is insufficient."
```
P3 options and required review boundaries are drafted in the contract. Existing attended custody remains in force. No delivery helper, delegated value role or policy change is admitted by preparation.
## Bind a real dated window and owner receipt manifests
```task
id: RPF-WP-0050-T03
status: wait
priority: medium
blocked_on: message-from:the-custodian
blocking_reason: "Custodian must return the reviewed cadence/window date and current owner task scope before a real metadata manifest is assembled; exact founder execution authorization remains separate."
```
Generate a checklist from all named authoritative roots, verified Hub decision joins and owner CCR/lane/receipt mappings; revalidate each exact revision, upstream acceptance, native readiness and rollback. Applied custody such as RPF-WP-0035-T08 is excluded. Each session returns exact receipts for each owner; missing receipts leave waits intact. Do not activate schedules in this workplan.
## Accept platform seam for catalog lane scaffold proposal
```task
id: RPF-WP-0050-T04
status: todo
priority: medium
```
P4 accepted for platform-side source review and bounded scaffolding after Warden returns its contract. Catalog entry/scaffold never supplies credentials, reviews, admission or live authority. Keep owner acceptance explicit before implementation.
2026-10-03: Platform source review accepts the delivered Warden seam at `ops-warden/wiki/CatalogLaneScaffoldContract.md` (`ac112d8`). T04 is now a ready source implementation task for the platform-owned rendering and schema fixture. Warden T02 may progress independently; no mutual owner-return wait remains. This is source contract acceptance, not credential admission or production authorization.