app.services.record_detail

Read-only record detail assembly.

get_record_detail() takes a CatalogStore (already inside a unit of work opened by the caller) plus plain values, and returns a plain RecordDetail; nothing here imports FastAPI or a source adapter. Routers convert it with RecordDetailOut.model_validate(value).

The relationship part comes from app.services.record_relationships, the errata part from app.services.errata_view, the approval part from app.services.approval_view; this module adds metadata, provenance, freshness, identity siblings and legacy sign-offs.

Public API: RecordMetadata, RecordProvenance, RecordFreshness, SiblingInfo, RecordDetail, get_record_detail().

class app.services.record_detail.RecordDetail(*, metadata, issues, rendered_html, provenance, freshness, outbound_links, inbound_links, errata, errata_summary, erratum, erratum_for, status, siblings, legacy_signoff_unverified, approval)

Bases: object

Everything the record detail page shows.

Parameters:
  • metadata (RecordMetadata) – Identity and authored metadata.

  • issues (tuple[Issue, ...]) – Stored validation issues followed by read-time relationship issues (missing_target, ambiguous_target, duplicate_uuid).

  • rendered_html (str) – Sanitized HTML rendering of the body.

  • provenance (RecordProvenance) – Where the content came from.

  • freshness (RecordFreshness) – Freshness of the owning registration.

  • outbound_links (tuple[OutboundLinkInfo, ...]) – Resolved outbound links, authored order.

  • inbound_links (tuple[InboundLinkInfo, ...]) – Inbound updated_by/obsoleted_by links.

  • errata (tuple[ErratumInfo, ...]) – Errata correcting this record (published first, then candidates), each with its own approval state.

  • errata_summary (ErrataSummary) – Counts for the top notice.

  • erratum (ErratumContentInfo | None) – For an erratum, its structured content.

  • erratum_for (ErratumForInfo | None) – For an erratum, the decision it corrects.

  • status (DisplayStatus) – Derived display status.

  • siblings (tuple[SiblingInfo, ...]) – Every row (published and candidates) of this record’s identity, including this one.

  • legacy_signoff_unverified (tuple[str, ...]) – Unverified legacy signed-off-by values; never an approval.

  • approval (ApprovalSummary) – Approval state, policy summary and history.

metadata: RecordMetadata
issues: tuple[Issue, ...]
rendered_html: str
provenance: RecordProvenance
freshness: RecordFreshness
errata: tuple[ErratumInfo, ...]
errata_summary: ErrataSummary
erratum: ErratumContentInfo | None
erratum_for: ErratumForInfo | None
status: DisplayStatus
siblings: tuple[SiblingInfo, ...]
legacy_signoff_unverified: tuple[str, ...]
approval: ApprovalSummary
class app.services.record_detail.RecordFreshness(*, last_attempt_at, last_success_at, last_status, stale, intake)

Bases: object

Freshness of a record’s owning registration.

Parameters:
  • last_attempt_at (datetime | None) – Start of the registration’s latest poll attempt.

  • last_success_at (datetime | None) – Start of its latest fully successful poll.

  • last_status (PollStatus) – Outcome of its latest poll.

  • stale (bool) – Whether the indexed content may not reflect the current published source (see is_stale()).

  • intake (tuple[IntakeKind, ...]) – The owning registration’s configured intake mechanisms (poll, push, or both); empty when the registration is unknown. A registration without poll intake is never polled, so staleness-by-polling does not apply to it.

last_attempt_at: datetime | None
last_success_at: datetime | None
last_status: PollStatus
stale: bool
intake: tuple[IntakeKind, ...]
class app.services.record_detail.RecordMetadata(*, id, adr_uuid, decision_date, decision_date_inferred, kind, record_kind, title, title_source, digest, previous_digest)

Bases: object

Identity and authored metadata of one record.

Parameters:
  • id (UUID) – Record id.

  • adr_uuid (UUID | None) – Authored UUID, if valid.

  • decision_date (date | None) – Displayed decision date.

  • decision_date_inferred (bool) – Whether the date is only inferred from the filename.

  • kind (RecordState) – Published or candidate.

  • record_kind (RecordKind) – Decision or erratum.

  • title (str) – Display title.

  • title_source (TitleSource) – Where the title came from.

  • digest (str) – Canonical digest of the current content.

  • previous_digest (str | None) – Digest before the latest content change.

id: UUID
adr_uuid: UUID | None
decision_date: date | None
decision_date_inferred: bool
kind: RecordState
record_kind: RecordKind
title: str
title_source: TitleSource
digest: str
previous_digest: str | None
class app.services.record_detail.RecordProvenance(*, source_id, project, path, ref, source_url, updated_at)

Bases: object

Where a record’s content came from.

Parameters:
  • source_id (str) – Registration id.

  • project (str) – Project path.

  • path (str) – File path inside the project.

  • ref (str) – Branch/ref the content came from.

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

  • updated_at (datetime) – When this row’s content digest last changed (for a candidate, effectively when it was last submitted).

source_id: str
project: str
path: str
ref: str
source_url: str | None
updated_at: datetime
class app.services.record_detail.SiblingInfo(*, id, kind, source_id, project, path, ref, digest, title, decision_date, decision_date_inferred, approval_state, approvable)

Bases: object

One published or candidate row sharing a record’s logical identity.

Parameters:
  • id (UUID) – Record id.

  • kind (RecordState) – Published or candidate.

  • source_id (str) – Registration id.

  • project (str) – Project path.

  • path (str) – File path inside the project.

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

  • digest (str) – Content digest.

  • title (str) – Display title.

  • decision_date (date | None) – Displayed decision date.

  • decision_date_inferred (bool) – Whether the date is only inferred.

  • approval_state (ApprovalStateCode) – Approval state of this sibling’s digest.

  • approvable (bool) – Whether this sibling’s content can be decided.

id: UUID
kind: RecordState
source_id: str
project: str
path: str
ref: str
digest: str
title: str
decision_date: date | None
decision_date_inferred: bool
approval_state: ApprovalStateCode
approvable: bool
app.services.record_detail.get_record_detail(store, record_id, *, policy, policy_status, now, stale_after_minutes, reviewer=None, config_sources=())

Assemble everything the record detail page shows.

Parameters:
  • store (CatalogStore) – Store of the caller’s unit of work.

  • record_id (UUID) – Record id.

  • policy (Policy | None) – The configured policy, or None if missing or invalid.

  • policy_status (PolicyStatus) – Configuration status of the policy.

  • now (datetime) – The current time (injected for testability).

  • stale_after_minutes (int) – Configured staleness threshold.

  • reviewer (Reviewer | None) – The caller, for approval.can_decide; None means nobody can decide.

  • config_sources (Sequence[SourceRegistration]) – The current configuration’s registrations, for freshness.intake (empty for a registration removed from catalog.yaml).

Return type:

RecordDetail | None

Returns:

The detail, or None if the record does not exist.