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
This commit is contained in:
tegwick 2026-09-15 00:47:11 +02:00
parent 6289ecbb68
commit c333063185
7 changed files with 299 additions and 2 deletions

View file

@ -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`.

View file

@ -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`.

View file

@ -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"
}
}
}
]
}

View file

@ -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
}

View file

@ -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`.

View file

@ -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"]

View file

@ -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