app.services.record_relationships

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

Builds a RelationshipIndex for one detail request and converts the record’s view into API-ready values (outbound/inbound links and errata with titles, display status and read-time relationship issues).

Design decisions worth recording for later phases:

  • Candidate participation in relationships. The index is built from the published rows ( list_relationship_summaries()) plus the identity siblings of the record whose detail is requested ( list_identity_siblings(), which always includes the queried record itself, published or candidate). Siblings are marked published or candidate, so the domain treats rows of one identity as revisions (never as duplicate_uuid, never as an ambiguous link target). A candidate’s outbound links therefore resolve against published rows and its own identity’s other rows only; links authored by other candidates (of a different identity) are never resolved against anything. This also means duplicate_uuid detection for a candidate’s detail view does not see duplicates held only by unrelated candidates. Revisit if a later phase needs candidates to participate more broadly.

  • Cost of the detail endpoint. Phase 09 benchmarking at 20,000 published records measured the naive “fetch every published row” index build at ~780 ms per detail request, of which ~630 ms was spent fetching and converting all 20,000 rows to RecordSummary in Python – the Postgres scan itself took ~80 ms (see docs/developer/performance.rst). Since this one request only ever looks at one record’s view of the index, list_relationship_candidates() fetches only the published rows that can possibly affect that view (sharing the record’s UUID, matching one of its own link targets, or linking back to it) instead of every published row; see its docstring for why the result is exactly the summary set the full fetch would have narrowed the index to for this one record. Looking up the title of every related record is one query per related id (never per catalog row), so it stays bounded by the small number of links a record has, not by catalog size.

  • Which errata belong to a record. Errata are fetched by the record’s UUID ( list_errata_for(), published and candidate rows from every registration) and added to the index with only their corrects link. An erratum belongs to the record when that link resolves – with the usual same-project, same-registration, global tiers – to a row of the record’s own identity (the published row, or the newest candidate when nothing is published). So an erratum in another project is not shown when a decision with the same UUID exists closer to it, an ambiguous erratum-for belongs to no record, and the errata of a decision show on its published row and on its candidate revisions alike. As for other links, candidates of other identities are not link targets.

  • The erratum’s own target. On an erratum’s page the corrects link is reported as ErratumForInfo and dropped from the outbound links.

  • Errata of errata. An erratum never lists errata of its own (its page has no Errata section and a zero summary): errata correct decisions. An erratum whose erratum-for resolves to another erratum keeps erratum_for.resolved but gets the read-time warning erratum_target_is_erratum.

Public API: OutboundLinkInfo, InboundLinkInfo, ErratumForInfo, RecordRelationships, build_relationships().

class app.services.record_relationships.ErratumForInfo(*, uuid, id, title, resolved)

Bases: object

The decision an erratum corrects (its erratum-for).

Parameters:
  • uuid (UUID) – The authored target UUID.

  • id (UUID | None) – Resolved target record id, or None.

  • title (str | None) – Resolved target title, or None.

  • resolved (bool) – Whether the link resolved to exactly one record.

uuid: UUID
id: UUID | None
title: str | None
resolved: bool
class app.services.record_relationships.InboundLinkInfo(*, link_type, source_id, source_title)

Bases: object

A link from another record to the one being viewed.

Parameters:
  • link_type (InboundType) – updated_by or obsoleted_by.

  • source_id (UUID) – Id of the linking record.

  • source_title (str) – Title of the linking record.

source_id: UUID
source_title: str
class app.services.record_relationships.OutboundLinkInfo(*, link_type, target_uuid, target_id, target_title, resolution, ambiguous, candidate_ids)

Bases: object

One outbound link and how it resolved.

Parameters:
  • link_type (LinkType) – The authored relationship.

  • target_uuid (UUID) – The authored target UUID.

  • target_id (UUID | None) – Resolved target record id, or None if unresolved.

  • target_title (str | None) – Resolved target title, or None.

  • resolution (Resolution | None) – Tier used, or None when unresolved.

  • ambiguous (bool) – Whether several targets matched equally well.

  • candidate_ids (tuple[UUID, ...]) – Ids of the equally good targets when ambiguous.

target_uuid: UUID
target_id: UUID | None
target_title: str | None
resolution: Resolution | None
ambiguous: bool
candidate_ids: tuple[UUID, ...]
class app.services.record_relationships.RecordRelationships(*, outbound_links, inbound_links, errata, erratum_for, status, issues)

Bases: object

Relationship facts of one record, ready for the detail page.

Parameters:
  • outbound_links (tuple[OutboundLinkInfo, ...]) – Resolved outbound links, authored order; without an erratum’s own corrects link.

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

  • errata (tuple[ErratumRow, ...]) – Erratum rows (published first, then candidates) that belong to the record; see the module docstring.

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

  • status (DisplayStatus) – Derived display status.

  • issues (tuple[Issue, ...]) – Read-time relationship issues (missing_target, ambiguous_target, duplicate_uuid, erratum_target_is_erratum).

errata: tuple[ErratumRow, ...]
erratum_for: ErratumForInfo | None
status: DisplayStatus
issues: tuple[Issue, ...]
app.services.record_relationships.build_relationships(store, record, siblings)

Resolve the relationships of one record for its detail page.

Parameters:
Return type:

RecordRelationships

Returns:

Links, errata, status and read-time issues.