app.services.approval_view

Approval part of the record detail page (read-only).

Builds ApprovalSummary for one record from its identity siblings and decision events: the state of the current digest (app.domain.approval.evaluate()), the full decision history of the identity (each event marked current_digest), divergence between published and accepted/candidate content, competing candidates, and whether the caller may decide now (app.services.decisions.decision_blocker(), the same rules as the decision endpoint).

Divergence (divergence, at most one):

  • published_not_accepted – the record is published, its current digest is not accepted, and another digest of the identity was accepted (typically an accepted candidate whose content differs from what was published). other_record_ids lists the rows currently holding that accepted digest (may be empty when no row holds it any more).

  • published_differs – the record is a candidate and the identity’s published row holds different content.

Public API: PolicySummary, DecisionProvenance, DecisionSummary, DivergenceKind, DivergenceInfo, CompetingCandidate, ApprovalSummary, is_approvable(), decision_summary(), build_approval().

class app.services.approval_view.ApprovalSummary(*, state, needs_approval, digest, policy, history, approvable, approval_blocker, last_accepted_digest, can_decide, decide_blocked_code, decide_blocked_reason, divergence, competing_candidates)

Bases: object

Approval state of a record’s current content.

Parameters:
  • state (ApprovalStateCode) – The state code.

  • needs_approval (bool) – Whether the record is in “Needs approval”.

  • digest (str) – The current digest the state applies to.

  • policy (PolicySummary) – The applicable policy summary.

  • history (tuple[DecisionSummary, ...]) – All decisions of this identity, oldest first.

  • approvable (bool) – Whether the content can be decided at all.

  • approval_blocker (str | None) – Why not, when not approvable.

  • last_accepted_digest (str | None) – Latest accepted other digest, set for changed_since_acceptance.

  • can_decide (bool) – Whether the caller may decide now.

  • decide_blocked_code (str | None) – Stable code when the caller cannot.

  • decide_blocked_reason (str | None) – Reader-facing reason when not.

  • divergence (DivergenceInfo | None) – Published/accepted divergence, if any.

  • competing_candidates (tuple[CompetingCandidate, ...]) – Other candidates of the identity with different content.

state: ApprovalStateCode
needs_approval: bool
digest: str
policy: PolicySummary
history: tuple[DecisionSummary, ...]
approvable: bool
approval_blocker: str | None
last_accepted_digest: str | None
can_decide: bool
decide_blocked_code: str | None
decide_blocked_reason: str | None
divergence: DivergenceInfo | None
competing_candidates: tuple[CompetingCandidate, ...]
class app.services.approval_view.CompetingCandidate(*, id, ref, path, digest, approval_state, approvable)

Bases: object

Another candidate of the identity with different content.

Parameters:
  • id (UUID) – Row id.

  • ref (str) – Branch/ref of the candidate.

  • path (str) – File path.

  • digest (str) – Its digest.

  • approval_state (ApprovalStateCode) – Approval state of its digest.

  • approvable (bool) – Whether its content can be decided at all.

id: UUID
ref: str
path: str
digest: str
approval_state: ApprovalStateCode
approvable: bool
class app.services.approval_view.DecisionProvenance(*, record_id, kind, source_id, project, ref, path, source_url)

Bases: object

The decided row as it was at decision time.

Parameters:
  • record_id (str | None) – Row id.

  • kind (str | None) – published or candidate.

  • source_id (str | None) – Registration id.

  • project (str | None) – Project path.

  • ref (str | None) – Branch/ref.

  • path (str | None) – File path.

  • source_url (str | None) – Link to the content at the source.

record_id: str | None
kind: str | None
source_id: str | None
project: str | None
ref: str | None
path: str | None
source_url: str | None
class app.services.approval_view.DecisionSummary(*, id, digest, current_digest, action, actor_issuer, actor_subject, actor_display, comment, policy_id, policy_version, provenance, created_at)

Bases: object

One append-only decision event of the identity.

The reviewer’s email is not exposed to readers.

Parameters:
  • id (UUID) – Stable event id.

  • digest (str) – Digest the decision applies to.

  • current_digest (bool) – Whether digest is the viewed record’s current digest.

  • action (DecisionAction) – Accepted or rejected.

  • actor_issuer (str) – Reviewer token issuer.

  • actor_subject (str) – Reviewer token subject.

  • actor_display (str | None) – Reviewer display name, if known.

  • comment (str | None) – Optional reviewer comment.

  • policy_id (str) – Applied policy id.

  • policy_version (int) – Applied policy version.

  • provenance (DecisionProvenance) – The decided row at decision time.

  • created_at (datetime) – Server timestamp of the decision.

id: UUID
digest: str
current_digest: bool
action: DecisionAction
actor_issuer: str
actor_subject: str
actor_display: str | None
comment: str | None
policy_id: str
policy_version: int
provenance: DecisionProvenance
created_at: datetime
class app.services.approval_view.DivergenceInfo(*, kind, other_digest, other_record_ids, other_accepted, message)

Bases: object

Content divergence within the identity.

Parameters:
  • kind (DivergenceKind) – Which divergence.

  • other_digest (str) – The accepted (or published) digest that differs from the current one.

  • other_record_ids (tuple[UUID, ...]) – Rows currently holding other_digest.

  • other_accepted (bool) – Whether other_digest is accepted.

  • message (str) – Reader-facing explanation.

kind: DivergenceKind
other_digest: str
other_record_ids: tuple[UUID, ...]
other_accepted: bool
message: str
class app.services.approval_view.DivergenceKind(*values)

Bases: StrEnum

Kinds of published/accepted divergence (module docstring).

PUBLISHED_NOT_ACCEPTED = 'published_not_accepted'
PUBLISHED_DIFFERS = 'published_differs'
class app.services.approval_view.PolicySummary(*, status, id, version, required, allow_self_approval)

Bases: object

The applicable approval policy, or why there is none.

Parameters:
  • status (PolicyStatus) – Configured, missing or invalid.

  • id (str | None) – Policy id, when configured.

  • version (int | None) – Policy version, when configured.

  • required (bool | None) – Whether approval is required, when configured.

  • allow_self_approval (bool | None) – Whether authors may decide their own content, when configured.

status: PolicyStatus
id: str | None
version: int | None
required: bool | None
allow_self_approval: bool | None
app.services.approval_view.build_approval(*, record, siblings, events, policy, policy_status, reviewer)

Build the approval summary of one record for one caller.

Parameters:
Return type:

ApprovalSummary

Returns:

The summary.

app.services.approval_view.decision_summary(event, current_digest)

Convert a stored event for the API.

Parameters:
  • event (DecisionEvent) – The stored event.

  • current_digest (str) – Digest of the viewed record.

Return type:

DecisionSummary

Returns:

The summary.

app.services.approval_view.is_approvable(record)

Tell whether a stored row’s content may be decided at all.

Parameters:

record (Approvable) – The row.

Return type:

bool

Returns:

True without identity or encoding errors.