cadrumo.application.user_profile.profile_summary module

The pure, non-authenticating profile listing boundary.

This module is the whole public surface for “which profiles exist and what are they called”. It is deliberately separate from cadrumo.application.user_profile.profile_repository: that module owns the authenticated aggregate, and reaching it pulls the transaction journal, the password material reader, and the label-head publisher – roughly a hundred modules, and a per-profile custody lock, to answer a question that needs neither.

Listing therefore never unwraps a DEK, derives a key, reads keyring or session state, opens a password or recovery envelope, authenticates a sentinel, reads encrypted facts, runs recovery, publishes a label head, or repairs a projection. Those all remain with the commands that own them.

class ProfileSummary(**data)[source]

Bases: BaseModel

Listing projection with no key, KDF, manifest, or recovery material.

Every field is read from the two records one anchored observation already proved coherent, so building a summary costs no further filesystem work and cannot carry another capsule’s provenance. There is deliberately nothing here that implies custody health: a summary says a capsule is committed and what it is called, never that it can be unlocked.

Parameters:
  • profile_id (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=36, pattern=^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$, ascii_only=None)])

  • label (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=160, pattern=None, ascii_only=None), AfterValidator(func=~cadrumo.core.identity.profile_label._reject_uuid_shaped_label)])

  • label_revision (int)

  • published_at (datetime)

  • publication_kind (Literal[ProfilePublicationKind.ENROLL, ProfilePublicationKind.RESTORE])

profile_id: ProfileId
label: ProfileLabel
label_revision: int
published_at: datetime
publication_kind: ProfilePublicationKindValue
class ProfileSummaryInventory(**data)[source]

Bases: BaseModel

The complete result of one non-authenticating listing observation.

A caller reads outcome before summaries. A degraded or concurrent observation carries no summaries at all rather than a partial set, because a partial listing is indistinguishable from a store that genuinely holds fewer profiles – and “your profile is gone” is the one wrong answer this boundary must never give.

Parameters:
outcome: ProfileSummaryOutcome
summaries: tuple[ProfileSummary, ...]
detail: str | None
property recognized: bool

Whether the observation produced a trustworthy complete listing.

summary_inventory_snapshot()[source]

Let everything inside one read-only command share one listing observation.

A single command asks which profiles exist from several layers – the session gate, the active-bucket resolver, the envelope’s profile label, the sandbox notice – and each paid a full anchored scan. Inside this scope the first recognized observation of a root answers the rest. The scope is the whole cache: nothing survives it, because the store belongs to other processes too, and a command that writes profile state must not open one. Nested scopes share the outermost observation.

Return type:

Generator[None]

summary_inventory(*, root=None)[source]

Project every committed capsule from recognized witnesses alone.

The adapter’s single anchored scan is the entire read: an empty store costs one directory enumeration, and a populated one costs two bounded reads per profile. Both failure endings are typed rather than raised, because a listing that cannot be trusted must still render – saying so – instead of aborting the command an operator ran to find out what they have.

Within summary_inventory_snapshot() a recognized observation is reused for the same root; a degraded or concurrent one never is, so a retry inside the command observes the store again.

Return type:

ProfileSummaryInventory

Parameters:

root (Path | None)

require_summaries(*, root=None)[source]

Return the summaries, refusing rather than reporting a degraded read.

summary_inventory() reports an unreadable store as a typed outcome with no rows, which is right for a surface that can show the operator why. A caller that only gets a sequence back cannot show anything, and an empty sequence there would mean “you have no profiles” – the opposite of the truth, and the answer that makes someone think their data is gone. Such a caller uses this instead and handles the refusal.

Return type:

tuple[ProfileSummary, ...]

Parameters:

root (Path | None)