From c33306318574ee19c3de9184962648b9ba7ae460 Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 15 Sep 2026 00:47:11 +0200 Subject: [PATCH] Request a create-only sitting requester for INFD-WP-0002-T03. Names informed-decision-sitting-requester (approval:create only, no approve/consume, no redirect). Eight binding intents are drafted and not posted; c01 stays undecided vs secrets-engine-requester. No secret, no live registration, no bind. Assistant: grok Assistant-Session: 01a09dc1-b21e-77e1-919e-fcad2f82b267 --- docs/batches/2026-09-14/OPERATOR.md | 8 +- docs/batches/2026-09-14/README.md | 3 + .../2026-09-14/approval-create-intents.json | 134 ++++++++++++++++++ ...eycape-sitting-requester-registration.json | 21 +++ .../keycape-sitting-requester-registration.md | 79 +++++++++++ tests/test_sitting_requester_registration.py | 47 ++++++ .../INFD-WP-0002-compact-signoff-batches.md | 9 ++ 7 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 docs/batches/2026-09-14/approval-create-intents.json create mode 100644 docs/keycape-sitting-requester-registration.json create mode 100644 docs/keycape-sitting-requester-registration.md create mode 100644 tests/test_sitting_requester_registration.py diff --git a/docs/batches/2026-09-14/OPERATOR.md b/docs/batches/2026-09-14/OPERATOR.md index 0478c3d..1c0fb1d 100644 --- a/docs/batches/2026-09-14/OPERATOR.md +++ b/docs/batches/2026-09-14/OPERATOR.md @@ -31,8 +31,12 @@ Evidence: `docs/evidence/2026-09-14-infd-0002-t03-accept-reopened.json`. denied. Request shape: `policy-request.md`. Do not expand the T03 mandate in place. 2. **No `approval:create` requester** whose `binding.actor` matches these - acts. Do **not** reuse `secrets-engine-requester` for WSL2, clock - ownership, mason plan, or warden seal. + acts. Requested, **not applied**: + `docs/keycape-sitting-requester-registration.md`. Do **not** reuse + `secrets-engine-requester` for WSL2, clock ownership, mason plan, or + warden seal. Create intents (not posted): + `approval-create-intents.json`. `c01` is undecided between this client + and `secrets-engine-requester`. 3. Drafts still have `approval_id: null` and principal `pending-human-session`. Live review refuses `missing_act_binding` / `wrong_recipient`. diff --git a/docs/batches/2026-09-14/README.md b/docs/batches/2026-09-14/README.md index 403973e..ae8d3f0 100644 --- a/docs/batches/2026-09-14/README.md +++ b/docs/batches/2026-09-14/README.md @@ -26,3 +26,6 @@ Those memos are not this sitting. Live accept reopened 2026-09-14 22:16 UTC (`/readyz` 200). This sitting stays `draft-unsigned` until native receipts, a live KeyCape subject, a new Flex Auth package, and a human bind. Attach (does not bind): `uv run python tools/attach_compact_bindings.py`. +Sitting requester (requested, not applied): +`docs/keycape-sitting-requester-registration.md`. Create intents (not posted): +`approval-create-intents.json`. diff --git a/docs/batches/2026-09-14/approval-create-intents.json b/docs/batches/2026-09-14/approval-create-intents.json new file mode 100644 index 0000000..b4152c8 --- /dev/null +++ b/docs/batches/2026-09-14/approval-create-intents.json @@ -0,0 +1,134 @@ +{ + "kind": "informed-decision-approval-create-intents", + "sitting_id": "infd-sitting-2026-09-14", + "status": "draft-not-posted", + "posted": false, + "requester_client": "informed-decision-sitting-requester", + "human_control": true, + "pdp_path": false, + "required_count": 1, + "note": "Intents only. Do not POST until the sitting requester exists and key-cape/approval-engine have accepted actor/principal. Digests are computed by approval-engine at create; do not invent them here.", + "intents": [ + { + "memo_id": "infd-20260914-c01", + "create_client": "undecided", + "reason": "May be secrets-engine-requester if a later consume is in-scope.", + "binding": { + "action": "admit", + "actor": "informed-decision", + "principal": "secrets-engine", + "purpose": "Admit real native OpenRouter delivery for intelligence-radar on the existing reviewed lane", + "target": { + "id": "catalog:openrouter-llm-connect", + "type": "secret-catalog-lane", + "system": "secrets-engine" + } + } + }, + { + "memo_id": "infd-20260914-c02", + "create_client": "informed-decision-sitting-requester", + "binding": { + "action": "accept", + "actor": "informed-decision", + "principal": "railiance-platform", + "purpose": "Accept provisioning of the secrets-engine service JWT login on the reviewed credential lane", + "target": { + "id": "rpf-wp-0035-t02", + "type": "credential-lane", + "system": "railiance-platform" + } + } + }, + { + "memo_id": "infd-20260914-c03", + "create_client": "informed-decision-sitting-requester", + "binding": { + "action": "apply", + "actor": "informed-decision", + "principal": "netkingdom", + "purpose": "Apply the live OpenBao role addition already specified for the operator-tunneled browser callback", + "target": { + "id": "nk-wp-0032-t03", + "type": "openbao-role", + "system": "netkingdom" + } + } + }, + { + "memo_id": "infd-20260914-c04", + "create_client": "informed-decision-sitting-requester", + "binding": { + "action": "attend", + "actor": "informed-decision", + "principal": "ops-warden", + "purpose": "Attend the graded lockdown / break-glass seal for ops-warden trust-root work", + "target": { + "id": "warden-wp-0027-t02", + "type": "trust-root", + "system": "ops-warden" + } + } + }, + { + "memo_id": "infd-20260914-d01", + "create_client": "informed-decision-sitting-requester", + "binding": { + "action": "retire", + "actor": "informed-decision", + "principal": "the-custodian", + "purpose": "After HA failover and restore drills pass, retire WSL2 as a State Hub fallback", + "target": { + "id": "cust-wp-0038-t08", + "type": "operating-model", + "system": "the-custodian" + } + } + }, + { + "memo_id": "infd-20260914-d02", + "create_client": "informed-decision-sitting-requester", + "binding": { + "action": "confirm", + "actor": "informed-decision", + "principal": "helixforge-factory", + "purpose": "Confirm the existing operator-group claim for CCR-2026-0019 as required by the factory identity/audit/approval path", + "target": { + "id": "ccr-2026-0019", + "type": "ccr", + "system": "helixforge-factory" + } + } + }, + { + "memo_id": "infd-20260914-d03", + "create_client": "informed-decision-sitting-requester", + "binding": { + "action": "accept", + "actor": "informed-decision", + "principal": "mason", + "purpose": "Accept the fluid-telegram operator credential lane plan so construction may proceed", + "target": { + "id": "mason-wp-0005", + "type": "workplan", + "system": "mason" + } + } + }, + { + "memo_id": "infd-20260914-d04", + "create_client": "informed-decision-sitting-requester", + "binding": { + "action": "confirm", + "actor": "informed-decision", + "principal": "railiance-clock", + "purpose": "Confirm ecosystem ownership and the security role for Railiance Clock as specified", + "target": { + "id": "rclk-wp-0002-t01", + "type": "workplan", + "system": "railiance-clock" + } + } + } + ] +} diff --git a/docs/keycape-sitting-requester-registration.json b/docs/keycape-sitting-requester-registration.json new file mode 100644 index 0000000..f839369 --- /dev/null +++ b/docs/keycape-sitting-requester-registration.json @@ -0,0 +1,21 @@ +{ + "clientId": "informed-decision-sitting-requester", + "displayName": "Informed Decision compact-sitting approval requester", + "audience": "approval-engine", + "allowedScopes": [ + "approval:create" + ], + "grantTypes": [ + "client_credentials" + ], + "clientType": "confidential", + "secretRef": "env:KEYCAPE_INFORMED_DECISION_SITTING_REQUESTER_CLIENT_SECRET", + "serviceSubject": "informed-decision", + "tenant": "tenant:platform", + "roles": [ + "informed-decision-sitting-requester" + ], + "tokenLifetime": "15m", + "status": "requested", + "applied": false +} diff --git a/docs/keycape-sitting-requester-registration.md b/docs/keycape-sitting-requester-registration.md new file mode 100644 index 0000000..1255bf8 --- /dev/null +++ b/docs/keycape-sitting-requester-registration.md @@ -0,0 +1,79 @@ +# Sitting requester — create-only client (requested, not applied) + +**Workplan task:** `INFD-WP-0002-T03` +**For:** `key-cape` (same registration shape as `secrets-engine-requester`) +**Copied to:** `approval-engine` (`docs/keycape-service-registrations.md` — + “no requester identity has been settled for `approval:create`”) +**Status:** requested 2026-09-14. **Not registered. Not in OpenBao. No secret +is in this repository.** + +This is the missing presenter for compact Decision Memo sittings whose +protected side effect is a **work-record update** (`INFD-WP-0002-T04`), not a +PEP consume. + +## Why a second client + +`informed-decision-approver` is the **human** browser client +(`approval:read` + `approval:approve`). Humans cannot `POST /v1/approvals`. +`secrets-engine-requester` is create-only with `sub=secrets-engine` and must +not draft WSL2 retirement, clock ownership, or mason plan accepts — +`binding.actor` has to match the authenticated subject. + +This client names the presenter for *this* sitting. + +## The strings + +| Field | Value | +| --- | --- | +| `client_id` | `informed-decision-sitting-requester` | +| Grant | `client_credentials` | +| Client type | confidential | +| Audience | `approval-engine` (resource server, never the OAuth client id) | +| Scopes | `approval:create` **only** | +| `serviceSubject` / token `sub` | `informed-decision` | +| `tenant` | `tenant:platform` (exact) | +| `principal_type` | `service` | +| Role | `informed-decision-sitting-requester` | +| Token lifetime | 15m | +| Redirect URI | **none** — this is not a browser client | + +Machine-readable copy: `keycape-sitting-requester-registration.json`. +`secretRef` is a custody *name*. It is not a credential. + +## Anti-requirements + +- **No** `approval:approve`. That stays on the human PKCE client. +- **No** `approval:consume`. Consumption belongs to a PEP. This sitting's + T04 path is repo files + `fix-consistency`. +- **No** `approval:read` on this client. The human already has read. +- Do **not** reuse `approval-engine-operator` (cancelled bundle, too wide). +- Do **not** reuse `secrets-engine-requester` for unrelated acts. +- Do **not** register this client until `key-cape` owns the row and + `railiance-platform` owns attended secret custody. This document is not + admission. + +## Binding rules at create time + +`POST /v1/approvals` requires `binding.actor ==` the token `sub`, so actor is +always `informed-decision`. + +| Binding field | Value for this sitting | +| --- | --- | +| `actor` | `informed-decision` | +| `principal` | owning system of the work record (not the human, not this client by default) | +| `action` / `purpose` / `target` | one memo, one act — see `docs/batches/2026-09-14/approval-create-intents.json` | +| `human_control` | `true` | +| `pdp_path` | `false` — no consume PEP on T04 | +| `required_count` | 1 | + +`infd-20260914-c01` (SECRETS-WP-0010 native delivery) may belong on +`secrets-engine-requester` instead if a later consume is in-scope. It is +marked undecided in the intents file. Do not POST it on this client until +that is stated. + +## What this does not do + +It does not bind. It does not admit Flex Auth. It does not load the review +store. After KeyCape registers the row and platform materializes custody, +an attended create can mint the eight (or seven) objects; then +`tools/attach_compact_bindings.py` with the operator's live KeyCape `sub`. diff --git a/tests/test_sitting_requester_registration.py b/tests/test_sitting_requester_registration.py new file mode 100644 index 0000000..9f02438 --- /dev/null +++ b/tests/test_sitting_requester_registration.py @@ -0,0 +1,47 @@ +import json +from pathlib import Path + +from informed_decision.records import memo_from + +ROOT = Path(__file__).resolve().parents[1] +REG = json.loads((ROOT / "docs" / "keycape-sitting-requester-registration.json").read_text()) +INTENTS = json.loads((ROOT / "docs" / "batches" / "2026-09-14" / "approval-create-intents.json").read_text()) + + +def test_sitting_requester_is_create_only_and_unapplied(): + assert REG["clientId"] == "informed-decision-sitting-requester" + assert REG["audience"] == "approval-engine" + assert REG["allowedScopes"] == ["approval:create"] + assert REG["grantTypes"] == ["client_credentials"] + assert REG["clientType"] == "confidential" + assert REG["serviceSubject"] == "informed-decision" + assert REG["tenant"] == "tenant:platform" + assert REG["applied"] is False + assert "redirect" not in json.dumps(REG).lower() + forbidden = {"approval:approve", "approval:consume", "approval:read", "openid"} + assert forbidden.isdisjoint(REG["allowedScopes"]) + assert not REG["secretRef"].startswith("s.") and ":" in REG["secretRef"] + + +def test_create_intents_cover_the_sitting_and_are_not_posted(): + assert INTENTS["posted"] is False + assert INTENTS["human_control"] is True + assert INTENTS["pdp_path"] is False + ids = [row["memo_id"] for row in INTENTS["intents"]] + sitting_ids = [] + batch_root = ROOT / "docs" / "batches" / "2026-09-14" + for name in ("credentials", "decisions"): + index = json.loads((batch_root / name / "index.json").read_text()) + sitting_ids.extend(row["memo_id"] for row in index["ordinal"]) + assert ids == sitting_ids + undecided = [row for row in INTENTS["intents"] if row["create_client"] == "undecided"] + assert [row["memo_id"] for row in undecided] == ["infd-20260914-c01"] + for row in INTENTS["intents"]: + binding = row["binding"] + assert set(binding) == {"action", "actor", "principal", "purpose", "target"} + assert binding["actor"] == "informed-decision" + assert "digest" not in binding + batch = "credentials" if "-c0" in row["memo_id"] else "decisions" + memo = memo_from(json.loads((batch_root / batch / f"{row['memo_id']}.memo.json").read_text())) + assert binding["target"]["id"] == memo.binding.target.id + assert memo.question.startswith(binding["purpose"][:20]) or binding["purpose"] in memo.question or memo.question.rstrip("?") in binding["purpose"] diff --git a/workplans/INFD-WP-0002-compact-signoff-batches.md b/workplans/INFD-WP-0002-compact-signoff-batches.md index 8876219..7b8df9a 100644 --- a/workplans/INFD-WP-0002-compact-signoff-batches.md +++ b/workplans/INFD-WP-0002-compact-signoff-batches.md @@ -144,6 +144,15 @@ create approvals or dispositions. Evidence: `docs/evidence/2026-09-14-infd-0002-t03-accept-reopened.json`. Task stays `wait`. +2026-09-14 22:44 UTC — **sitting requester requested, not registered.** +`docs/keycape-sitting-requester-registration.md` asks key-cape for a +create-only confidential client (`informed-decision-sitting-requester`, +`sub=informed-decision`, scope `approval:create` only, no approve/consume, +no redirect). Intents for the eight bindings are in +`docs/batches/2026-09-14/approval-create-intents.json` (`posted: false`; +`c01` create-client undecided). No secret, no POST, no bind. Task stays +`wait`. + ## Feed outcomes back to State Hub without hub-authoring ```task