Surface the requesting party on the decision overview

The single-memo review page already fetched and labeled
approval-engine's binding.principal "Requesting party" -- the party
the approval was issued on behalf of, distinct from memo.binding.principal
(the reviewer being bound). The overview/list page fetched the same
live approval record for status but never surfaced this field.

OverviewRow gains requesting_party, populated only when the fetched
binding's digest matches the memo's pinned digest (same trust
condition status already uses); ui.py renders it as "Requested by: ...".
No new approval-engine call or flex-auth scope needed -- the record was
already being read.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 169987@bnt-lap001
Assistant-Session: 322ef1ef-9048-4021-8570-b6d6f6347999
This commit is contained in:
tegwick 2026-09-27 20:38:14 +02:00
parent 656301b659
commit 13ca3a5372
3 changed files with 27 additions and 2 deletions

View file

@ -47,6 +47,7 @@ class OverviewRow:
engine_status: str | None = None
reason: str | None = None
approved_at: str | None = None
requesting_party: str | None = None
history: tuple = () # ({"verb", "memo_version", "at", "state"}, ...), the person's own
@ -160,11 +161,12 @@ class ReviewController:
if exc.code == "session_expired":
raise
return OverviewRow(memo.id, "unavailable", reason=exc.code)
status, reason = None, None
status, reason, requesting_party = None, None, None
try:
current = self.approval_factory(session).get_approval(memo.approval_id)
if current["binding"]["digest"] == memo.approval_binding_digest:
status = current["status"]
requesting_party = current["binding"].get("principal")
else:
reason = "binding_changed"
except (ApprovalEngineError, ValueError):
@ -192,7 +194,7 @@ class ReviewController:
else:
group = "closed"
return OverviewRow(memo.id, group, memo.question, memo.version, status, reason,
intent.get("approved_at") if intent else None,
intent.get("approved_at") if intent else None, requesting_party,
tuple({"verb": d.verb.value, "memo_version": d.memo_version, "at": d.at,
"state": s["state"] if s else None} for d, s in history))

View file

@ -148,6 +148,8 @@ def _overview_row(row):
return (f'<div class="record"><strong>{text(row.memo_id)}</strong>'
f'<p class="muted">{text(reason)}</p></div>')
facts = [f'Memo {text(row.memo_id)} · version {row.version}']
if row.requesting_party:
facts.append(f'Requested by: {text(row.requesting_party)}')
if row.engine_status:
facts.append(f'Approval status: {text(row.engine_status)}')
elif row.reason: