INFD-WP-0010: send approval_action to the PDP for present-existing memos (compact-sitting v14)

access-engine v14 (2de0f86) admits requester service:nk-bind-executor only
when the context carries approval_action == bounded-execution. Intake records
the verified binding.action; the intake context emits it for present-existing
memos only (create-path requests keep the v13 shape); build_request admits the
key only for a non-informed-decision requester; review refuses a changed live
binding.action as binding_changed before asking the PDP. Deployed pin (v13)
unchanged.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5502@bnt-lap001
Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
This commit is contained in:
tegwick 2026-11-01 04:02:49 +01:00
parent 8787e1a14a
commit 80ef8408ed
5 changed files with 97 additions and 9 deletions

View file

@ -192,7 +192,9 @@ class IntakeService:
metadata = {"memo_class": submission.memo_class, "review_group": submission.review_group,
"drafted_by": submission.drafted_by, "serves": submission.serves}
if profile is not None:
metadata.update(profile="present_existing", approval_requester=profile.approval_actor)
# approval_action: the verified binding.action, for the PDP (compact-sitting v14).
metadata.update(profile="present_existing", approval_requester=profile.approval_actor,
approval_action=submission.approval_binding["action"])
recovered = None
# Reserve before any network side effect. SQLite serializes concurrent poses.
with self.store._transaction() as db:

View file

@ -57,12 +57,18 @@ def submitted_digest(request):
return "sha256:" + hashlib.sha256(encoded.encode()).hexdigest()
INTAKE_CONTEXT = frozenset({"review_group", "memo_class", "intake_submitter", "approval_requester",
"approval_human_control"})
def build_request(session: HumanSession, memo, action, policy_version, *, intake_context=None):
if action not in ACTIONS:
raise ValueError("unsupported review action")
extra = {} if intake_context is None else dict(intake_context)
if extra and set(extra) != {"review_group", "memo_class", "intake_submitter", "approval_requester", "approval_human_control"}:
if extra and set(extra) not in (INTAKE_CONTEXT, INTAKE_CONTEXT | {"approval_action"}):
raise ValueError("exact intake policy context required")
if "approval_action" in extra and extra["approval_requester"] == "informed-decision":
raise ValueError("approval_action is sent only for a present-existing requester")
if extra and action == "list":
extra["review_recipient"] = memo.binding.principal.id
return {"id": str(uuid.uuid4()), "tenant": "tenant:platform",

View file

@ -85,7 +85,9 @@ class ReviewController:
if context is not None:
current = self._approval(session, memo)
if (current['binding'].get('actor') != context['approval_requester']
or current['binding'].get('human_control') is not True):
or current['binding'].get('human_control') is not True
or ('approval_action' in context
and current['binding'].get('action') != context['approval_action'])):
raise ReviewError(409, 'binding_changed')
request = build_request(session, memo, action, self.policy.version, intake_context=context)
observation = self.policy.check(request)

View file

@ -281,11 +281,17 @@ class Store:
if (receipt['approval_id'] != memo.approval_id
or receipt['approval_binding_digest'] != memo.approval_binding_digest):
raise Conflict('intake receipt does not match memo')
return {'review_group': metadata['review_group'], 'memo_class': metadata['memo_class'],
'intake_submitter': row['submitter'],
# present_existing names the approval's own actor; the create path is informed-decision.
'approval_requester': metadata.get('approval_requester', 'informed-decision'),
'approval_human_control': True}
context = {'review_group': metadata['review_group'], 'memo_class': metadata['memo_class'],
'intake_submitter': row['submitter'],
# present_existing names the approval's own actor; the create path is informed-decision.
'approval_requester': metadata.get('approval_requester', 'informed-decision'),
'approval_human_control': True}
if metadata.get('profile') == 'present_existing':
# compact-sitting v14 admits a present-existing requester only for a named action.
# A row recorded before this key existed fails closed at the PDP instead of guessing.
if 'approval_action' in metadata:
context['approval_action'] = metadata['approval_action']
return context
def intake_context(self, memo):
with self._connection() as db: