"""Strict pydantic v2 record for the active-profile pointer file.The pointer file lives at ``<aeat-root>/active-profile`` and carries thecanonical default for the active-profile precedence chain(flag > env > pointer). This record is the typed wrapper around thepointer file's plaintext content: :class:`BucketPointer`. The storage-layerterm ``bucket`` is preserved on the record's ``bucket_id`` field because thebucket is the encrypted storage slice the operator profile sits on; the file'soperator-visible name is ``active-profile`` to match the verb noun.The on-disk representation is single-document TOML keyed by``bucket_id`` and ``schema_version``. An atomic write-then-renamehelper, :func:`write_pointer`, materialises the pointer; the:func:`resolve_active_bucket_id` resolver consumes it at startup. InvalidTOML and pydantic validation failures are deliberate hard failures for``read_pointer``; they are not treated as "no active profile" because thatwould silently fall back to root storage.This module is also distinct from the application-layer manifest scanners::class:`~application.workflow.ProfileBucketPointer` records are derivedfrom ``buckets/*/manifest.toml`` and prove registered profile metadata. A``BucketPointer`` only names the intended active bucket; it does not prove thebucket exists, read a manifest, or validate lifecycle status. The companion:mod:`core._bucket_pointer_io` module owns the file boundary, while thismodule owns strict validation and deterministic serialisation only."""from__future__importannotationsimporttomllibfrompydanticimportBaseModel,Fieldfrom..coreimportSTRICT_FROZEN_CONFIGas_STRICT_FROZEN
[docs]classBucketPointer(BaseModel):"""Plaintext pointer to the active bucket id. The value object carries only ``bucket_id`` and ``schema_version``. It does not read settings, open storage, inspect manifests, or resolve precedence; those operations belong to :func:`read_pointer`, :func:`write_pointer`, and :func:`resolve_active_bucket_id`. Attributes: bucket_id: Encrypted profile bucket id selected by the pointer. schema_version: Pointer document schema version. Values start at ``1``. """model_config=_STRICT_FROZENbucket_id:str=Field(min_length=1)schema_version:int=Field(ge=1)
[docs]defto_toml(self)->str:"""Serialise the pointer to its single-document TOML representation. Emits a deterministic two-line document keyed by ``bucket_id`` and ``schema_version``. The output ends with a trailing newline so the atomic write-then-rename helper produces a POSIX-clean file. The inverse parser is :meth:`from_toml`. """# Hand-formatted to keep the dependency footprint minimal and the# output deterministic; the format is fixed at two scalar keys.bucket_id_escaped=self.bucket_id.replace("\\","\\\\").replace('"','\\"')returnf'bucket_id = "{bucket_id_escaped}"\nschema_version = {self.schema_version}\n'
[docs]@classmethoddeffrom_toml(cls,text:str)->BucketPointer:"""Parse the single-document TOML representation back into a record. The parser accepts the exact schema emitted by :meth:`to_toml` and then delegates to pydantic validation, so unknown keys, blank bucket ids, and invalid schema versions fail before pointer IO can accept the file. Args: text: Raw TOML string to parse, expected to carry ``bucket_id`` and ``schema_version`` scalar keys. Returns: A validated :class:`BucketPointer` instance. Raises: tomllib.TOMLDecodeError: If ``text`` is not valid TOML. pydantic.ValidationError: If the TOML payload violates the strict pointer schema. """payload=tomllib.loads(text)returncls.model_validate(payload)