aeat.application.user_profile._orchestration module

WorkflowState-aware orchestration for profile lifecycle services.

The lifecycle service handles secure-DB persistence via a ProfileLifecycleService, which wraps a SecureObjectRepository and emits bucket events to BucketEventHistoryRepository per profile. This module threads active-profile selection through WorkflowState, the plaintext BucketPointer, and the workflow-level WorkflowEvent audit stream around those calls so CLI surfaces do not duplicate that wiring.

Profile identity is an immutable UUIDv4 minted at creation. The bucket directory, keystore directory, secure-object key, and active-profile pointer all key on that UUID; the operator-chosen display name is a fully decoupled mutable label carried in the BucketManifest.

See also

ProfileRepository

Sole writer for the cross-store profile aggregate.

profile_create_storage_span()

Create-time bucket session used before the encrypted record exists.

profile_storage_session()

Existing-profile bucket session used for application-owned writes.

build_lifecycle_service(*, bucket_id, secure_objects=None, schema=None)[source]

Construct a lifecycle service for one bucket.

Returns a ProfileLifecycleService.

secure_objects is an optional SecureObjectRepository override; a per-bucket store is resolved when None.

The profile aggregate AND the bucket-event-history catalogue both belong to the named bucket’s own database. When no repository is injected, a single per-bucket secure-object store is resolved and handed to both the UserProfileLifecycleRepository and the BucketEventHistoryRepository, so the audit trail can never split from the records it describes. The prior wiring left the event-history repository on the process-global engine, which had no URL until an active profile existed - every register then crashed before its first event landed.

Return type:

ProfileLifecycleService

Parameters:
profile_create_storage_span(profile_id)[source]

Open the first-profile storage span for a bucket being created.

The span writes the provisional BucketPointer needed to resolve the create-time bucket route, then enters activate_master_key_provider() with DEK enrollment enabled. If bucket setup fails before or outside create() owns rollback, the prior pointer bytes are restored and provisional bucket/DEK artifacts minted by this span are removed.

Parameters:

profile_id (str)

profile_storage_session(profile_id)[source]

Open a storage session scoped to profile_id for application-owned writes.

Existing-profile operations use this span to bind the active BucketPointer route and master-key session before repositories open encrypted bucket-local storage.

Parameters:

profile_id (str)

exception ProfileAlreadyRegisteredError(message=None, *, context=None, suggestion=None, translated_message=None)[source]

Bases: ProfileNotFoundError

Raised when profile create targets a name that already has a manifest.

Inherits from ProfileNotFoundError so existing exception handlers that catch the broader family also catch this case; the CLI decorator translates it to a typed refusal that names config switch as the next action.

Parameters:
  • message (str | None)

  • context (Mapping[str, object] | None)

  • suggestion (str | None)

  • translated_message (str | None)

Return type:

None

code: ClassVar[ErrorCode]
register_active_profile(state, *, profile_id, display_name, facts=(), secure_objects=None, schema=None, enforce_unique_tax_id=True, routing_profile_id=None)[source]

Atomically register a new profile and make it the active one.

Parameters:
  • state (WorkflowState) – The current WorkflowState; the returned state carries the registration event appended to the audit stream.

  • profile_id (str) – Immutable UUIDv4 profile identity, minted by the caller (see new_profile_id()).

  • display_name (str) – Operator-chosen label carried in the bucket manifest and the encrypted record; plays no role in any key or path.

  • facts (tuple[UserProfileFact, ...]) – Initial profile facts to persist alongside the registration.

  • secure_objects (SecureObjectRepository | None) – Optional SecureObjectRepository override for the encrypted profile store.

  • schema (ProfileSchemaDefinition | None) – Optional profile schema definition override.

  • enforce_unique_tax_id (bool) – When True, refuses if another live profile already carries the same tax id.

  • routing_profile_id (str | None) – When set, wires a cross-bucket routing entry so the new profile can inherit data from an existing bucket.

Return type:

WorkflowState

This function is a thin WorkflowState coordinator: the entire cross-store write — bucket directory, manifest, encrypted record, AND the active-profile pointer — plus the duplicate-label refusal and the all-or-nothing rollback are a single unit of work owned by create(). This function delegates that create and threads the workflow-level event audit stream onto the supplied WorkflowState.

The repository owns every store write, the pointer included. A caller that performs the cold-start pointer write early (so the workflow-state engine can resolve before this function runs inside workflow_state_repository().update) is responsible for restoring that early pointer if the surrounding span fails before or after create - capture_active_profile_pointer() and restore_active_profile_pointer() cover the steps the repository’s own rollback cannot see (engine open, master-key activation).

select_profile_with_lifecycle_span(profile_id)[source]

Select profile_id inside an application-owned bucket session.

Opens profile_storage_session(), delegates the pointer write to select_profile(), then appends the bucket-level activation event.

Return type:

None

Parameters:

profile_id (str)

delete_profile_with_lifecycle_span(profile_id)[source]

Tombstone profile_id inside an application-owned bucket session.

Returns the deleted UserProfileRecord from delete().

Return type:

UserProfileRecord

Parameters:

profile_id (str)

reactivate_profile_with_lifecycle_span(profile_id)[source]

Restore a tombstoned profile_id inside an application-owned bucket session.

Symmetric inverse of delete_profile_with_lifecycle_span(). Returns the reactivated UserProfileRecord from reactivate().

Return type:

UserProfileRecord

Parameters:

profile_id (str)

logout_active_profile()[source]

Clear the active profile pointer and return the profile that was logged out.

Return type:

str | None

capture_active_profile_pointer()[source]

Return the raw active-profile pointer text, or None if absent.

A cold-start caller — one that must write the active-profile pointer early so workflow_state_repository() can resolve its per-bucket engine before register_active_profile() runs — captures the genuine pre-write pointer with this helper, then restores it in a try/except if the create span fails. This closes the window the repository’s own rollback cannot reach: a failure between the early pointer write and create() (engine open, master-key activation) would otherwise strand the pointer at a profile whose record was never persisted.

Return type:

str | None

restore_active_profile_pointer(prior_text)[source]

Restore the active-profile pointer to a previously captured state.

Counterpart to capture_active_profile_pointer(). A cold-start caller calls this from the except arm of the span it wraps: if there was no prior pointer the early write is removed, otherwise the captured bytes are written back, so a failed create leaves the pointer exactly as it was found.

Return type:

None

Parameters:

prior_text (str | None)

refuse_duplicate_label(display_name)[source]

Refuse a profile create when a live profile carries the label.

Display names (labels) are unique among live profiles, compared case-insensitively. A bucket manifest already carrying display_name is an existence claim; recreating in place would silently mix new profile data into the existing bucket, so the create is refused and the operator is routed to switch or delete. The live-label lookup is read_profile_bucket().

Return type:

None

Parameters:

display_name (str)

require_registered_label(display_name)[source]

Refuse a profile edit when no live profile carries the label.

Symmetric to refuse_duplicate_label(): profile edit re-runs the wizard against an existing profile, so an unknown label is an operator error, not an implicit create. The registered label authority is read_profile_bucket().

Return type:

None

Parameters:

display_name (str)

remove_profile_bucket_directory(profile_id)[source]

Trash-rename and remove a profile’s on-disk bucket directory.

Used both by atomic-create rollback and by reset_config(). The directory is first renamed to a trash-prefix sibling so a crashed removal leaves a recoverable on-disk trace, then recursively deleted. When the rename is refused - Windows denies renaming a directory whose SQLite file was only just closed - the directory is removed in place so the bucket does not survive the reset.

Raises OSError if the in-place removal also fails and the bucket directory genuinely survives on disk, so a caller (config reset in particular) never reports a removed profile while the bucket is still present. The atomic-create rollback caller wraps this call best-effort, since a residual directory there must not mask the original registration failure.

Return type:

None

Parameters:

profile_id (str)

select_profile(state, *, profile_id, secure_objects=None, schema=None)[source]

Select an existing profile as active.

secure_objects is an optional SecureObjectRepository override.

Raises ProfileNotFoundError if the profile does not already exist; registration is the explicit path (register_active_profile()).

This function is a thin WorkflowState coordinator: the profile load + integrity check + active-profile pointer write live solely in select().

Return type:

WorkflowState

Parameters:
set_active_field(state, fact, *, secure_objects=None, schema=None)[source]

Upsert one fact on the active profile and append a workflow event.

Parameters:
Return type:

WorkflowState

Returns:

The updated WorkflowState.

set_active_fields(state, facts, *, secure_objects=None, schema=None)[source]

Upsert several facts on the active profile in sequence.

secure_objects is an optional SecureObjectRepository override.

Returns a WorkflowState.

Return type:

WorkflowState

Parameters:
remove_active_profile(state, *, secure_objects=None, schema=None)[source]

Tombstone the active profile and clear the active pointer.

secure_objects is an optional SecureObjectRepository override.

The bucket directory and BucketManifest stay on disk so audit and history reads can still resolve the tombstoned bucket; selecting the tombstoned profile via select_profile() will raise because the record is no longer live.

This function is a thin WorkflowState coordinator: the cross-store tombstone (encrypted-record tombstone + active-profile pointer clear) lives solely in delete().

Return type:

WorkflowState

Parameters:
fact_value(record, path)[source]

Return the live string-rendered value of one fact path on record.

Parameters:
Return type:

str | None

Returns None when record is None, when the path has no fact, or when the recorded value is None. Repeated facts at the same path (effective-dated windows) resolve to the chronologically last UserProfileFact.valid_from.

rename_profile(*, profile_id, new_label, secure_objects=None, schema=None)[source]

Rename a profile by updating its display label only.

Parameters:
Return type:

UserProfileRecord

The profile identity (profile_id), bucket directory, keystore directory, and secure-object key are immutable and never move. A rename is a pure label edit across the two stores that hold a copy of the label - the encrypted UserProfileRecord display_name and the plaintext BucketManifest label.

This function is a thin coordinator: the cross-store label write - record AND manifest - lives solely in rename(). Refuses if new_label is already carried by another live profile.

Returns the updated UserProfileRecord after the label change is persisted.