oscal — API reference

version 3.2.2 · generated 2026-09-01T02:41:25Z · 12 modules, 30 classes, 669 methods, 32 functions

Machine-oriented API reference for automated (agentic) consumption. Every symbol carries a stable id (its fully-qualified name) and data-* attributes; docstrings are preserved verbatim.

Index

module oscal.oscal_support

oscal_support — OSCAL support-file database and singleton accessor.

Manages the local SQLite database of NIST-published OSCAL support files
(metaschemas and XML/JSON schemas) for every OSCAL version and model, and
provides the ``OSCALSupport`` class along with helpers for validation and
format conversion.

Always obtain the shared instance via ``get_support()`` (optionally configured
first with ``configure_support()`` / ``setup_support()``). This ensures a single
support instance is shared across the application. The OSCAL content classes rely
on this instance to perform validation and conversion.

More information: https://github.com/brian-ruf/oscal-class/blob/main/docs/SUPPORT_MODULE.md

Module constants:
    SUPPORT_DATABASE_DEFAULT_FILE (str): Default path to the support DB
        (``"./support/oscal_support.db"``), resolved relative to the runtime CWD.
    SUPPORT_DATABASE_DEFAULT_TYPE (str): Default database backend (``"sqlite3"``).
    COMPRESS_SUPPORT_FILES_IN_DATABASE (bool): Whether support files are stored
        compressed in the database.
    OSCAL_DEFAULT_XML_NAMESPACE (str): The NIST OSCAL XML namespace URI.
    NIST_OSCAL_EXTENSION_NAMESPACE (str): The NIST OSCAL property/extension namespace URI.
    NIST_RMF_EXTENSION_NAMESPACE (str): The NIST RMF extension namespace URI.
    OSCAL_FORMATS (list): Supported serialization formats
        (``["xml", "json", "yaml", "yml"]``).
    DEFAULT_EXCLUDE_VERSIONS (list): OSCAL release tags excluded by default
        (release candidates and milestones).
    METASCHEMA_MIN_VERSION (str): Earliest version with NIST-published resolved
        metaschema files (``"v1.1.1"``).
    METASCHEMA_INDEX_VERSION (str): Semantic version of the metaschema index schema
        (the parsed ``"processed"`` asset structure). Stamped onto every index built by
        this library and recorded per OSCAL version in ``oscal_versions.index_version``;
        used to detect/reconcile index-schema mismatches between a library and a shared
        support database. Current: ``"1.0.0"``.
    INDEX_REFRESH (int): Seconds before a cached metaschema index entry is stale
        (86400 = 24 hours).
    METASCHEMA_FILE_PATTERNS (dict): Filename-suffix → support-type map for
        metaschema files.
    SCHEMA_FILE_PATTERNS (dict): Filename-suffix → support-type map for XML/JSON
        schema files.
    OSCAL_SUPPORT_TABLES (dict): Schema definitions for the support database tables
        (``oscal_versions``, ``oscal_support``, ``filecache``).

class OSCALSupport

Access layer for the local OSCAL support-file database.

    Manages a SQLite database of NIST-published support files (metaschemas and
    XML/JSON schemas) for every OSCAL version and model, and exposes methods for
    querying supported versions/models, retrieving assets, building metaschema
    indexes, and updating content from NIST's GitHub releases.

    The ``datatypes`` attribute holds the OSCAL Metaschema data type definitions
    and their validation regexes (see also :meth:`get_datatype`), supporting
    field-level input validation.

    Prefer the module-level ``get_support()`` accessor over instantiating this
    class directly, so a single instance is shared across the application.

    Note:
        ``OSCAL_support`` is a backward-compatible alias for this class.

def __init__(self, db_conn='./support/oscal_support.db', db_type='sqlite3', db_init_mode='auto', db_compress_files=True)

Initialize OSCAL support and run startup (table checks / population).

        Args:
            db_conn (str, optional): Database connection string or file path.
                Defaults to ``SUPPORT_DATABASE_DEFAULT_FILE``.
            db_type (str, optional): Database backend type (e.g. "sqlite3").
                Defaults to ``SUPPORT_DATABASE_DEFAULT_TYPE``.
            db_init_mode (str, optional): Database initialization mode. Defaults to "auto".
                - "auto": Extract from packaged resources if the file is missing/empty,
                  otherwise use the existing file.
                - "extract": Always try to extract from resources; create empty if that fails.
                - "create": Always create an empty database from scratch.
            db_compress_files (bool, optional): Whether to store support files
                compressed in the database. Defaults to
                ``COMPRESS_SUPPORT_FILES_IN_DATABASE``.

def add_asset(self, oscal_version, model_name, asset_type, content, filename=None)

Add an asset to the support database. If the asset already exists, it will be replaced.
        This method supports both string and bytes content types.
        If the content is a string, it will be converted to bytes.
        If the content is already in bytes, it will be used as is.
        Args:
            oscal_version (str, required): The OSCAL version (e.g. "v1.0.0").
            model_name (str, required): The OSCAL model name (e.g. "system-security-plan").
            asset_type (str, required): The asset type (e.g. "xml-schema", "json-schema").
            content (str | bytes, required): The asset content; strings are encoded
                to UTF-8 bytes.
            filename (str, optional): Filename to record for the cached asset.
                Defaults to ``"{model_name}_{asset_type}"``.
        Returns:
            bool: True if the asset was added successfully, False otherwise.

def asset(self, oscal_version, model_name, asset_type)

Backward-compatible wrapper for :meth:`get_asset`.

        Args:
            oscal_version (str, required): The OSCAL version (e.g. "v1.0.0").
            model_name (str, required): The OSCAL model name (e.g. "system-security-plan").
            asset_type (str, required): The asset type (e.g. "xml-schema", "json-schema").

        Returns:
            Any: The asset content if found, otherwise None.

def download_schemas(self, support_dir: 'str', fetch: 'str' = 'all') -> 'bool'

Download XML and JSON schema files to the filesystem.

        Files are written to ``{support_dir}/{version}_schemas/`` directories and
        are not stored in the support database.

        Args:
            support_dir: Root directory under which per-version schema folders are created.
            fetch: ``"all"`` to download every known version, or a specific version
                   tag (e.g. ``"v1.2.2"``) to download only that version.
        Returns:
            True if all files were saved without error, False otherwise.

def ensure_version(self, version: 'str') -> 'tuple[Optional[str], str]'

Make support for OSCAL *version* available, acquiring or substituting it.

        Invoked when content declares an OSCAL version not present in the local support
        database. Resolution order:

        1. Already present locally → return it (``"exact"``).
        2. Merge that version from the library's bundled database, if present there
           (offline; logged INFO).
        3. Otherwise fetch it from the NIST OSCAL GitHub repository (logged INFO on success).
        4. If it still cannot be obtained → substitute the closest available version within
           the same OSCAL major (logged WARN; ``"closest-match"``).
        5. If nothing usable exists → ``"unavailable"`` (logged ERROR).

        Args:
            version (str, required): The requested OSCAL version tag (e.g. ``"v1.2.3"``).

        Returns:
            tuple[str | None, str]: ``(resolved_version, outcome)`` where ``outcome`` is
                ``"exact"``, ``"closest-match"``, or ``"unavailable"``. ``resolved_version``
                is None only when ``outcome`` is ``"unavailable"``.

def enumerate_models(self, version: 'str' = 'all') -> 'list[str]'

Backward-compatible wrapper for :meth:`list_models`.

        Args:
            version (str, optional): The OSCAL version to enumerate models for, or
                "all". Defaults to "all".

        Returns:
            list[str]: Supported model-name strings (may be empty).

def export_support_files(self, export_path='./support_files')

Export all cached support files to a directory tree, grouped by version.

        Args:
            export_path (str, optional): The directory to export support files to.
                Defaults to "./support_files".

        Returns:
            bool: True if the export was successful, False otherwise.

def get_asset(self, version, model, asset_type)

Returns the asset for the specified OSCAL version and model name.
        Args:
            version (str): The OSCAL version (e.g., "v1.0.0").
            model (str): The OSCAL model name (e.g., "system-security-plan").
            asset_type (str): The type of asset to retrieve (e.g., "xml-schema", "json-schema").
        Returns:
            The asset content if found, None otherwise.

def get_datatype(self, datatype_name: 'str') -> 'dict | None'

Return the OSCAL Metaschema definition for a named data type.

        Provides the datatype's validation patterns (``xml-pattern``,
        ``json-pattern``, ``recommended-pattern``), ``base-type``, documentation,
        and reference links — e.g. so a UI can validate a field's input against the
        regex for that field's declared OSCAL data type. The full table is also
        available as the ``datatypes`` attribute.

        Args:
            datatype_name (str, required): OSCAL data type name (e.g. "uuid",
                "date-time-with-timezone", "token").

        Returns:
            dict | None: A safe copy of the datatype definition, or None if the
                name is not a recognized OSCAL data type.

def get_latest_version(self)

Backward-compatible wrapper for :meth:`latest_version`.

        Returns:
            Optional[str]: The latest OSCAL version tag, or None if none are loaded.

def get_metaschema_index(self, version: 'str', model: 'str', index_version: 'str | None' = None) -> 'dict | None'

Return the parsed metaschema index dict for the given OSCAL version and model.

        Results are held in the module-level ``_metaschema_index_cache`` so that
        only one copy of each index lives in memory and survives across calls.
        A cached entry is reused until it is older than :data:`INDEX_REFRESH`
        seconds (24 hours), at which point it is refreshed from the database.

        The index is identified by ``(version, model, index_version)``: a stored index
        whose ``index_version`` has a **different major** than the requested one is
        incompatible with this library and is rebuilt from the raw metaschema (stamping
        the current :data:`METASCHEMA_INDEX_VERSION`); a same-major difference is trusted
        as backward compatible and used as-is.

        Args:
            version: OSCAL version string, e.g. ``"v1.1.3"``.
            model:   OSCAL model name, e.g. ``"catalog"``.
            index_version: Metaschema-index-schema version to require. Defaults to this
                instance's :attr:`active_index_version` (resolved at startup).

        Returns:
            The model-specific index dict on success, or ``None`` when the index
            is unavailable.

def is_model_valid(self, model_name, version='all') -> 'bool'

Backward-compatible wrapper for :meth:`is_valid_model`.

        Args:
            model_name (str, required): The OSCAL model name to check.
            version (str, optional): The OSCAL version to check against, or "all".
                Defaults to "all".

        Returns:
            bool: True if the model is valid for the version, False otherwise.

def is_valid_model(self, model, version='all') -> 'bool'

Check if the specified OSCAL model is valid for the given version.
        Args:
            model (str): The OSCAL model name to check (e.g., "system-security-plan").
            version (str): The OSCAL version to check against (e.g., "v1.0.0").
        Returns:
            bool: True if the model is valid for the specified version, False otherwise.

def is_valid_version(self, version) -> 'bool'

Check if the specified OSCAL version is valid and supported.
        Args:
            version (str): The OSCAL version to check (e.g., "v1.0.0").
        Returns:
            bool: True if the version is valid and supported, False otherwise.

def latest_version(self)

Return the latest supported OSCAL version.

        Returns:
            Optional[str]: The highest OSCAL version tag available in the support
                database, or None if none are loaded.

def list_models(self, version: 'str' = 'all') -> 'list[str]'

Enumerate the supported models for a given OSCAL version.
        Args:
            version (str): The OSCAL version to enumerate models for (e.g., "v1.0.0").
        Returns:
            list[str]: A list of model-name strings supported for the specified OSCAL version
                       (may be empty).

def load_file(self, name, binary=False, *, as_bytes=None)

Load a file bundled in the ``oscal.data`` package resources, with caching.

        Args:
            name (str, required): Filename of the resource within ``oscal.data``.
            binary (bool, optional): If True, return raw bytes; otherwise return
                UTF-8 decoded text. Defaults to False.
            as_bytes (bool, optional): Keyword-only alias for ``binary``; overrides
                it when provided. Defaults to None.

        Returns:
            str | bytes | None: The file contents (text or bytes), or None on failure.

def remove_asset(self, version: 'str | None' = None, model: 'str | None' = None, asset_type: 'str | None' = None) -> 'int'

Remove support assets matching any combination of version / model / asset_type.

        At least one of the three criteria must be supplied; the criteria are ANDed. Every
        matching ``oscal_support`` row is deleted, and each cached file it referenced is
        deleted from ``filecache`` **once it is no longer referenced by any surviving
        asset row** — a single cached file can back more than one asset row (e.g. a
        document model's ``metaschema`` and ``document-model`` rows share one
        ``filecache_uuid``), so orphan-checking prevents deleting a file another row still
        needs.

        Args:
            version (str | None, optional): OSCAL version tag (e.g. ``"v1.2.3"``).
            model (str | None, optional): Model name (e.g. ``"catalog"``).
            asset_type (str | None, optional): Asset type (e.g. ``"metaschema"``,
                ``"document-model"``, ``"processed"``).

        Returns:
            int: The number of ``oscal_support`` rows removed (0 when nothing matched or
                no criterion was supplied).

def remove_version(self, version: 'str') -> 'bool'

Delete all support content for a single OSCAL version from the database.

        This is a standalone deletion for withdrawing a version that no longer
        belongs in the local support set (e.g. one removed upstream). Unlike
        :meth:`update`, it performs no GitHub fetch — it only deletes local
        content and keeps in-memory state consistent with the database.

        Args:
            version (str): The OSCAL version tag to remove (e.g. "v1.2.3"). The
                leading "v" is required and the tag is matched case-insensitively.

        Returns:
            bool: True if the version was found and deleted, False if the tag was
                invalid, not present, or the deletion failed.

def resolve_index_version(self) -> 'str'

Select the metaschema-index-schema version this instance will use.

        Reads the distinct ``index_version`` values recorded in ``oscal_versions`` and keeps
        those in the compatible range ``[METASCHEMA_INDEX_VERSION, next-major)``. When at
        least one qualifies, the **lowest** in range is chosen (the most conservative
        compatible schema) and assigned to :attr:`active_index_version`. When none qualify,
        the library's bundled database is merged into the local one (which supplies indexes
        built at this library's index version) and the resolution is retried once. Returns
        the resolved value.

def set_version_index_version(self, version: 'str', index_version: 'str' = '1.0.0') -> 'bool'

Record the metaschema-index-schema version used to build *version*'s indexes.

        Writes ``index_version`` into the version's ``oscal_versions`` row and updates the
        in-memory registry. Called after a version's ``"processed"`` indexes are (re)built
        so the database records which index schema they conform to.

        Args:
            version (str, required): OSCAL version tag (e.g. ``"v1.2.3"``).
            index_version (str, optional): Index-schema version; defaults to the library's
                current :data:`METASCHEMA_INDEX_VERSION`.

        Returns:
            bool: True on success.

def startup(self, check_for_updates=False, refresh_all=False)

Perform startup tasks required to provide OSCAL support.

        Ensures the support database has the required tables and data, populating
        it from NIST's GitHub releases when empty, and sets ``self.ready``.

        Args:
            check_for_updates (bool, optional): Reserved flag to check for newer
                OSCAL versions during startup. Defaults to False.
            refresh_all (bool, optional): Reserved flag to force a full refresh of
                all support content. Defaults to False.

        Returns:
            bool: True if the support capability is ready, False otherwise.

        Process:
        1 Check for tables
          - If tables do not exist:
            - create tables
            - set state to "empty"
          - If tables exist, check for data
            - If no data, set state to "empty"
            - If data exists, set state to "populated"
        2 If state is "empty", check for connection to GitHub
         - If cannot connect to GitHub, EXIT (cannot proceed)
         - If connected to GitHub, update database
           - If update fails, EXIT (cannot proceed)
           - If update succeeds, set state to "populated"
        3 If state is "populated" set self.ready to True

def supported(self, oscal_version, assets)

Check whether the specified OSCAL version and assets are supported.

        Note:
            Currently not implemented; always returns False.

        Args:
            oscal_version (str, required): The OSCAL version to check (e.g. "v1.0.0").
            assets (list, required): The asset types to check for.

        Returns:
            bool: True if the version and assets are supported, False otherwise.

def update(self, mode='new', fetch=None, save_to_fs=False)

Update OSCAL support content based on a fetch directive.

        Args:
            mode (str, optional): The fetch directive. Defaults to "new".
                - "all": Clear and re-fetch all OSCAL versions and support files.
                - "latest"/"new": Check for new OSCAL versions and fetch any found.
                - "vX.Y.Z": Clear and re-fetch a specific OSCAL version.
            fetch (str, optional): Legacy alias for ``mode``; when provided it
                overrides ``mode``. Defaults to None.
            save_to_fs (bool, optional): When True, also emit the parsed
                metaschema index files to the local file system in addition to
                updating the database. When False (default), only the database
                is updated. Defaults to False.

        Returns:
            bool: True if the update was successful, False otherwise.

def vacuum(self) -> 'None'

Reclaim free space in the support database (SQLite ``VACUUM``).

        A public wrapper over the internal VACUUM; a no-op on non-SQLite backends. Useful
        after a bulk :meth:`remove_asset` to shrink the database file on disk.

def view_detail(self, version: 'str', model: 'str', format: 'str', reference_uuid: 'str') -> 'str'

Return an HTML ``<div>`` detail view of a single metaschema node.

        Given a node's reference id (as produced by :meth:`view_outline`), returns its
        formal name and description, a format-appropriate representation, data type and
        regex (where available), constraints, and its immediate parent and children —
        each parent/child clickable by its own reference id.

        Args:
            version (str, required): OSCAL version, e.g. ``"v1.1.3"``.
            model (str, required): OSCAL model name, e.g. ``"catalog"``.
            format (str, required): ``"xml"``, ``"json"``, or ``"yaml"``.
            reference_uuid (str, required): The node reference id to describe.

        Returns:
            str: A detail ``<div>`` fragment, or a ``<div class="ms-error">`` when the
                model/version/format or reference is unknown.

def view_outline(self, version: 'str', model: 'str', format: 'str') -> 'str'

Return an HTML ``<div>`` outline of a model's metaschema structure.

        The outline is a clickable tree rendered in the requested format's syntax
        (``"xml"``, ``"json"``, or ``"yaml"``), annotated with data types and
        cardinality. Each element links to its node by a stable reference id, for use
        with :meth:`view_detail`. Intended for a front-end: the HTML is a fragment
        (wrapped in a ``<div>``), never a full page.

        Args:
            version (str, required): OSCAL version, e.g. ``"v1.1.3"``.
            model (str, required): OSCAL model name, e.g. ``"catalog"``.
            format (str, required): ``"xml"``, ``"json"``, or ``"yaml"``.

        Returns:
            str: An outline ``<div>`` fragment, or a ``<div class="ms-error">`` when
                the model/version/format is unknown or the index is unavailable.

class OSCAL_support

Access layer for the local OSCAL support-file database.

    Manages a SQLite database of NIST-published support files (metaschemas and
    XML/JSON schemas) for every OSCAL version and model, and exposes methods for
    querying supported versions/models, retrieving assets, building metaschema
    indexes, and updating content from NIST's GitHub releases.

    The ``datatypes`` attribute holds the OSCAL Metaschema data type definitions
    and their validation regexes (see also :meth:`get_datatype`), supporting
    field-level input validation.

    Prefer the module-level ``get_support()`` accessor over instantiating this
    class directly, so a single instance is shared across the application.

    Note:
        ``OSCAL_support`` is a backward-compatible alias for this class.

def __init__(self, db_conn='./support/oscal_support.db', db_type='sqlite3', db_init_mode='auto', db_compress_files=True)

Initialize OSCAL support and run startup (table checks / population).

        Args:
            db_conn (str, optional): Database connection string or file path.
                Defaults to ``SUPPORT_DATABASE_DEFAULT_FILE``.
            db_type (str, optional): Database backend type (e.g. "sqlite3").
                Defaults to ``SUPPORT_DATABASE_DEFAULT_TYPE``.
            db_init_mode (str, optional): Database initialization mode. Defaults to "auto".
                - "auto": Extract from packaged resources if the file is missing/empty,
                  otherwise use the existing file.
                - "extract": Always try to extract from resources; create empty if that fails.
                - "create": Always create an empty database from scratch.
            db_compress_files (bool, optional): Whether to store support files
                compressed in the database. Defaults to
                ``COMPRESS_SUPPORT_FILES_IN_DATABASE``.

def add_asset(self, oscal_version, model_name, asset_type, content, filename=None)

Add an asset to the support database. If the asset already exists, it will be replaced.
        This method supports both string and bytes content types.
        If the content is a string, it will be converted to bytes.
        If the content is already in bytes, it will be used as is.
        Args:
            oscal_version (str, required): The OSCAL version (e.g. "v1.0.0").
            model_name (str, required): The OSCAL model name (e.g. "system-security-plan").
            asset_type (str, required): The asset type (e.g. "xml-schema", "json-schema").
            content (str | bytes, required): The asset content; strings are encoded
                to UTF-8 bytes.
            filename (str, optional): Filename to record for the cached asset.
                Defaults to ``"{model_name}_{asset_type}"``.
        Returns:
            bool: True if the asset was added successfully, False otherwise.

def asset(self, oscal_version, model_name, asset_type)

Backward-compatible wrapper for :meth:`get_asset`.

        Args:
            oscal_version (str, required): The OSCAL version (e.g. "v1.0.0").
            model_name (str, required): The OSCAL model name (e.g. "system-security-plan").
            asset_type (str, required): The asset type (e.g. "xml-schema", "json-schema").

        Returns:
            Any: The asset content if found, otherwise None.

def download_schemas(self, support_dir: 'str', fetch: 'str' = 'all') -> 'bool'

Download XML and JSON schema files to the filesystem.

        Files are written to ``{support_dir}/{version}_schemas/`` directories and
        are not stored in the support database.

        Args:
            support_dir: Root directory under which per-version schema folders are created.
            fetch: ``"all"`` to download every known version, or a specific version
                   tag (e.g. ``"v1.2.2"``) to download only that version.
        Returns:
            True if all files were saved without error, False otherwise.

def ensure_version(self, version: 'str') -> 'tuple[Optional[str], str]'

Make support for OSCAL *version* available, acquiring or substituting it.

        Invoked when content declares an OSCAL version not present in the local support
        database. Resolution order:

        1. Already present locally → return it (``"exact"``).
        2. Merge that version from the library's bundled database, if present there
           (offline; logged INFO).
        3. Otherwise fetch it from the NIST OSCAL GitHub repository (logged INFO on success).
        4. If it still cannot be obtained → substitute the closest available version within
           the same OSCAL major (logged WARN; ``"closest-match"``).
        5. If nothing usable exists → ``"unavailable"`` (logged ERROR).

        Args:
            version (str, required): The requested OSCAL version tag (e.g. ``"v1.2.3"``).

        Returns:
            tuple[str | None, str]: ``(resolved_version, outcome)`` where ``outcome`` is
                ``"exact"``, ``"closest-match"``, or ``"unavailable"``. ``resolved_version``
                is None only when ``outcome`` is ``"unavailable"``.

def enumerate_models(self, version: 'str' = 'all') -> 'list[str]'

Backward-compatible wrapper for :meth:`list_models`.

        Args:
            version (str, optional): The OSCAL version to enumerate models for, or
                "all". Defaults to "all".

        Returns:
            list[str]: Supported model-name strings (may be empty).

def export_support_files(self, export_path='./support_files')

Export all cached support files to a directory tree, grouped by version.

        Args:
            export_path (str, optional): The directory to export support files to.
                Defaults to "./support_files".

        Returns:
            bool: True if the export was successful, False otherwise.

def get_asset(self, version, model, asset_type)

Returns the asset for the specified OSCAL version and model name.
        Args:
            version (str): The OSCAL version (e.g., "v1.0.0").
            model (str): The OSCAL model name (e.g., "system-security-plan").
            asset_type (str): The type of asset to retrieve (e.g., "xml-schema", "json-schema").
        Returns:
            The asset content if found, None otherwise.

def get_datatype(self, datatype_name: 'str') -> 'dict | None'

Return the OSCAL Metaschema definition for a named data type.

        Provides the datatype's validation patterns (``xml-pattern``,
        ``json-pattern``, ``recommended-pattern``), ``base-type``, documentation,
        and reference links — e.g. so a UI can validate a field's input against the
        regex for that field's declared OSCAL data type. The full table is also
        available as the ``datatypes`` attribute.

        Args:
            datatype_name (str, required): OSCAL data type name (e.g. "uuid",
                "date-time-with-timezone", "token").

        Returns:
            dict | None: A safe copy of the datatype definition, or None if the
                name is not a recognized OSCAL data type.

def get_latest_version(self)

Backward-compatible wrapper for :meth:`latest_version`.

        Returns:
            Optional[str]: The latest OSCAL version tag, or None if none are loaded.

def get_metaschema_index(self, version: 'str', model: 'str', index_version: 'str | None' = None) -> 'dict | None'

Return the parsed metaschema index dict for the given OSCAL version and model.

        Results are held in the module-level ``_metaschema_index_cache`` so that
        only one copy of each index lives in memory and survives across calls.
        A cached entry is reused until it is older than :data:`INDEX_REFRESH`
        seconds (24 hours), at which point it is refreshed from the database.

        The index is identified by ``(version, model, index_version)``: a stored index
        whose ``index_version`` has a **different major** than the requested one is
        incompatible with this library and is rebuilt from the raw metaschema (stamping
        the current :data:`METASCHEMA_INDEX_VERSION`); a same-major difference is trusted
        as backward compatible and used as-is.

        Args:
            version: OSCAL version string, e.g. ``"v1.1.3"``.
            model:   OSCAL model name, e.g. ``"catalog"``.
            index_version: Metaschema-index-schema version to require. Defaults to this
                instance's :attr:`active_index_version` (resolved at startup).

        Returns:
            The model-specific index dict on success, or ``None`` when the index
            is unavailable.

def is_model_valid(self, model_name, version='all') -> 'bool'

Backward-compatible wrapper for :meth:`is_valid_model`.

        Args:
            model_name (str, required): The OSCAL model name to check.
            version (str, optional): The OSCAL version to check against, or "all".
                Defaults to "all".

        Returns:
            bool: True if the model is valid for the version, False otherwise.

def is_valid_model(self, model, version='all') -> 'bool'

Check if the specified OSCAL model is valid for the given version.
        Args:
            model (str): The OSCAL model name to check (e.g., "system-security-plan").
            version (str): The OSCAL version to check against (e.g., "v1.0.0").
        Returns:
            bool: True if the model is valid for the specified version, False otherwise.

def is_valid_version(self, version) -> 'bool'

Check if the specified OSCAL version is valid and supported.
        Args:
            version (str): The OSCAL version to check (e.g., "v1.0.0").
        Returns:
            bool: True if the version is valid and supported, False otherwise.

def latest_version(self)

Return the latest supported OSCAL version.

        Returns:
            Optional[str]: The highest OSCAL version tag available in the support
                database, or None if none are loaded.

def list_models(self, version: 'str' = 'all') -> 'list[str]'

Enumerate the supported models for a given OSCAL version.
        Args:
            version (str): The OSCAL version to enumerate models for (e.g., "v1.0.0").
        Returns:
            list[str]: A list of model-name strings supported for the specified OSCAL version
                       (may be empty).

def load_file(self, name, binary=False, *, as_bytes=None)

Load a file bundled in the ``oscal.data`` package resources, with caching.

        Args:
            name (str, required): Filename of the resource within ``oscal.data``.
            binary (bool, optional): If True, return raw bytes; otherwise return
                UTF-8 decoded text. Defaults to False.
            as_bytes (bool, optional): Keyword-only alias for ``binary``; overrides
                it when provided. Defaults to None.

        Returns:
            str | bytes | None: The file contents (text or bytes), or None on failure.

def remove_asset(self, version: 'str | None' = None, model: 'str | None' = None, asset_type: 'str | None' = None) -> 'int'

Remove support assets matching any combination of version / model / asset_type.

        At least one of the three criteria must be supplied; the criteria are ANDed. Every
        matching ``oscal_support`` row is deleted, and each cached file it referenced is
        deleted from ``filecache`` **once it is no longer referenced by any surviving
        asset row** — a single cached file can back more than one asset row (e.g. a
        document model's ``metaschema`` and ``document-model`` rows share one
        ``filecache_uuid``), so orphan-checking prevents deleting a file another row still
        needs.

        Args:
            version (str | None, optional): OSCAL version tag (e.g. ``"v1.2.3"``).
            model (str | None, optional): Model name (e.g. ``"catalog"``).
            asset_type (str | None, optional): Asset type (e.g. ``"metaschema"``,
                ``"document-model"``, ``"processed"``).

        Returns:
            int: The number of ``oscal_support`` rows removed (0 when nothing matched or
                no criterion was supplied).

def remove_version(self, version: 'str') -> 'bool'

Delete all support content for a single OSCAL version from the database.

        This is a standalone deletion for withdrawing a version that no longer
        belongs in the local support set (e.g. one removed upstream). Unlike
        :meth:`update`, it performs no GitHub fetch — it only deletes local
        content and keeps in-memory state consistent with the database.

        Args:
            version (str): The OSCAL version tag to remove (e.g. "v1.2.3"). The
                leading "v" is required and the tag is matched case-insensitively.

        Returns:
            bool: True if the version was found and deleted, False if the tag was
                invalid, not present, or the deletion failed.

def resolve_index_version(self) -> 'str'

Select the metaschema-index-schema version this instance will use.

        Reads the distinct ``index_version`` values recorded in ``oscal_versions`` and keeps
        those in the compatible range ``[METASCHEMA_INDEX_VERSION, next-major)``. When at
        least one qualifies, the **lowest** in range is chosen (the most conservative
        compatible schema) and assigned to :attr:`active_index_version`. When none qualify,
        the library's bundled database is merged into the local one (which supplies indexes
        built at this library's index version) and the resolution is retried once. Returns
        the resolved value.

def set_version_index_version(self, version: 'str', index_version: 'str' = '1.0.0') -> 'bool'

Record the metaschema-index-schema version used to build *version*'s indexes.

        Writes ``index_version`` into the version's ``oscal_versions`` row and updates the
        in-memory registry. Called after a version's ``"processed"`` indexes are (re)built
        so the database records which index schema they conform to.

        Args:
            version (str, required): OSCAL version tag (e.g. ``"v1.2.3"``).
            index_version (str, optional): Index-schema version; defaults to the library's
                current :data:`METASCHEMA_INDEX_VERSION`.

        Returns:
            bool: True on success.

def startup(self, check_for_updates=False, refresh_all=False)

Perform startup tasks required to provide OSCAL support.

        Ensures the support database has the required tables and data, populating
        it from NIST's GitHub releases when empty, and sets ``self.ready``.

        Args:
            check_for_updates (bool, optional): Reserved flag to check for newer
                OSCAL versions during startup. Defaults to False.
            refresh_all (bool, optional): Reserved flag to force a full refresh of
                all support content. Defaults to False.

        Returns:
            bool: True if the support capability is ready, False otherwise.

        Process:
        1 Check for tables
          - If tables do not exist:
            - create tables
            - set state to "empty"
          - If tables exist, check for data
            - If no data, set state to "empty"
            - If data exists, set state to "populated"
        2 If state is "empty", check for connection to GitHub
         - If cannot connect to GitHub, EXIT (cannot proceed)
         - If connected to GitHub, update database
           - If update fails, EXIT (cannot proceed)
           - If update succeeds, set state to "populated"
        3 If state is "populated" set self.ready to True

def supported(self, oscal_version, assets)

Check whether the specified OSCAL version and assets are supported.

        Note:
            Currently not implemented; always returns False.

        Args:
            oscal_version (str, required): The OSCAL version to check (e.g. "v1.0.0").
            assets (list, required): The asset types to check for.

        Returns:
            bool: True if the version and assets are supported, False otherwise.

def update(self, mode='new', fetch=None, save_to_fs=False)

Update OSCAL support content based on a fetch directive.

        Args:
            mode (str, optional): The fetch directive. Defaults to "new".
                - "all": Clear and re-fetch all OSCAL versions and support files.
                - "latest"/"new": Check for new OSCAL versions and fetch any found.
                - "vX.Y.Z": Clear and re-fetch a specific OSCAL version.
            fetch (str, optional): Legacy alias for ``mode``; when provided it
                overrides ``mode``. Defaults to None.
            save_to_fs (bool, optional): When True, also emit the parsed
                metaschema index files to the local file system in addition to
                updating the database. When False (default), only the database
                is updated. Defaults to False.

        Returns:
            bool: True if the update was successful, False otherwise.

def vacuum(self) -> 'None'

Reclaim free space in the support database (SQLite ``VACUUM``).

        A public wrapper over the internal VACUUM; a no-op on non-SQLite backends. Useful
        after a bulk :meth:`remove_asset` to shrink the database file on disk.

def view_detail(self, version: 'str', model: 'str', format: 'str', reference_uuid: 'str') -> 'str'

Return an HTML ``<div>`` detail view of a single metaschema node.

        Given a node's reference id (as produced by :meth:`view_outline`), returns its
        formal name and description, a format-appropriate representation, data type and
        regex (where available), constraints, and its immediate parent and children —
        each parent/child clickable by its own reference id.

        Args:
            version (str, required): OSCAL version, e.g. ``"v1.1.3"``.
            model (str, required): OSCAL model name, e.g. ``"catalog"``.
            format (str, required): ``"xml"``, ``"json"``, or ``"yaml"``.
            reference_uuid (str, required): The node reference id to describe.

        Returns:
            str: A detail ``<div>`` fragment, or a ``<div class="ms-error">`` when the
                model/version/format or reference is unknown.

def view_outline(self, version: 'str', model: 'str', format: 'str') -> 'str'

Return an HTML ``<div>`` outline of a model's metaschema structure.

        The outline is a clickable tree rendered in the requested format's syntax
        (``"xml"``, ``"json"``, or ``"yaml"``), annotated with data types and
        cardinality. Each element links to its node by a stable reference id, for use
        with :meth:`view_detail`. Intended for a front-end: the HTML is a fragment
        (wrapped in a ``<div>``), never a full page.

        Args:
            version (str, required): OSCAL version, e.g. ``"v1.1.3"``.
            model (str, required): OSCAL model name, e.g. ``"catalog"``.
            format (str, required): ``"xml"``, ``"json"``, or ``"yaml"``.

        Returns:
            str: An outline ``<div>`` fragment, or a ``<div class="ms-error">`` when
                the model/version/format is unknown or the index is unavailable.

def configure_support(support_file='./support/oscal_support.db', db_init_mode='auto', *, db_path: 'Optional[str]' = None, init_mode: 'Optional[str]' = None)

Configure and create the shared OSCAL support instance.

    Call this before ``get_support()`` and before any OSCAL content is loaded when
    non-default settings are needed. If the shared instance already exists, it is
    returned unchanged.

    Args:
        support_file (str, optional): Path to the support database file.
            Defaults to ``SUPPORT_DATABASE_DEFAULT_FILE``.
        db_init_mode (str, optional): Database initialization mode — ``"auto"``,
            ``"extract"``, or ``"create"``. Defaults to ``"auto"``.
        db_path (str, optional): Keyword-only alias for ``support_file``; overrides
            it when provided.
        init_mode (str, optional): Keyword-only alias for ``db_init_mode``;
            overrides it when provided.

    Returns:
        OSCALSupport: The shared support instance.

def get_support()

Return the shared OSCAL support instance, creating it if necessary.

    Creates the instance with default settings (via ``configure_support()``) if it
    does not already exist.

    Returns:
        OSCALSupport: The shared support instance.

def setup_support(support_file='./support/oscal_support.db', db_init_mode='auto')

Compatibility wrapper around ``configure_support()`` for update utility scripts.

    Args:
        support_file (str, optional): Path to the support database file.
            Defaults to ``SUPPORT_DATABASE_DEFAULT_FILE``.
        db_init_mode (str, optional): Database initialization mode
            (``"auto"``, ``"extract"``, or ``"create"``). Defaults to ``"auto"``.

    Returns:
        OSCALSupport: The shared support instance.

module oscal.oscal_helpers

oscal_helpers — model-agnostic helper functions for OSCAL JSON content.

Pure, stateless helpers that operate on plain OSCAL JSON dicts (and markup
text) without needing an ``OSCAL`` instance. Extracted from ``oscal_content``
to keep that module focused on the ``OSCAL`` base class and its content
lifecycle. Nothing here imports ``oscal_content``; ``oscal_content`` imports and
re-exports these names, so existing ``from .oscal_content import ...`` call sites
continue to work unchanged.

Contents:
    new_uuid / _is_valid_uuid       — UUID generation and validation.
    prune_tree_copy                 — depth-limited safe copy of node subtrees.
    _collect_ids / _find_part_by_id / _find_model_element
                                    — id lookups over catalog-shaped dicts.
    append_prop(s) / get_props      — prop read/write helpers.
    append_link(s)                  — link write helpers.
    oscal_markdown_to_html_tree / _format_table_helper
                                    — OSCAL markup → HTML helpers.

def append_prop(parent_obj: 'dict', prop: 'dict') -> 'dict'

Append a single prop dict to ``parent_obj["props"]``.

    Args:
        parent_obj (dict, required): OSCAL JSON object that will receive the prop.
        prop (dict, required): Property dict. Required keys: "name", "value".
            Optional keys: "uuid", "ns", "class", "group", "remarks".

    Returns:
        dict: The appended prop entry (filtered to recognized keys).

def append_props(parent_obj: 'dict', props: 'list') -> 'None'

Append multiple prop dicts to ``parent_obj["props"]``.

    Args:
        parent_obj (dict, required): OSCAL JSON object that will receive the props.
        props (list, required): Property dicts, each with at minimum "name" and "value".

    Returns:
        None

def get_props(parent_obj: 'dict', name: 'str | None' = None, uuid: 'str | None' = None, ns: 'str' = 'http://csrc.nist.gov/ns/oscal', class_: 'str | None' = None, group: 'str | None' = None) -> 'list'

Retrieve matching prop dicts from ``parent_obj["props"]``.

    Either ``name`` or ``uuid`` must be supplied. The return value is always a
    list — empty when nothing matches (or when the required parameters are
    missing) — never ``None``.

    An absent ``ns`` on a prop is treated as the OSCAL default namespace
    (``_OSCAL_NS``), matching the way ``ns`` defaults on this function's own
    ``ns`` parameter. Correct default-``ns`` handling is essential: a prop with
    no ``ns`` is considered to be in the OSCAL namespace.

    Matching behaviour:
      * ``uuid`` supplied: every prop whose ``uuid`` equals ``uuid`` is
        returned. If any descriptor parameter (``name``, a non-default ``ns``,
        ``class_`` or ``group``) is also supplied and does not match a returned
        prop, a warning is logged, but the prop is still returned.
      * ``uuid`` not supplied: ``name`` is required. Props are matched on
        ``name`` **and** effective ``ns``. When ``class_`` and/or ``group`` are
        supplied they must also match. When ``class_``/``group`` are *not*
        supplied, all name+ns matches are returned, ordered best match first:
        props carrying fewer of the un-queried qualifiers (``class``/``group``)
        sort ahead of more-specific props. Document order is preserved among
        equally specific matches.

    Args:
        parent_obj (dict, required): OSCAL JSON object holding a ``props`` list.
        name (str, optional): Prop ``name`` to match. Required if ``uuid`` is
            not given.
        uuid (str, optional): Prop ``uuid`` to match. Required if ``name`` is
            not given.
        ns (str, optional): Namespace to match; defaults to ``_OSCAL_NS``.
        class_ (str, optional): Prop ``class`` to match. Maps to the ``"class"``
            key (``class`` is a reserved word in Python).
        group (str, optional): Prop ``group`` to match.

    Returns:
        list: Matching prop dicts (possibly empty), ordered best match first.

def new_uuid() -> 'str'

Generate a new random (version 4) UUID string.

    Returns:
        str: A newly generated UUID in canonical string form.

def oscal_markdown_to_html_tree(markdown_text: 'str', multiline: 'bool' = True) -> 'Optional[ElementTree.Element]'

Convert OSCAL markdown text to an HTML ElementTree element.

    Calls ``oscal_markdown_to_html`` to format the markdown into HTML consistent
    with the OSCAL XML specification for markup-line / markup-multiline, then parses
    the resulting string into an XML element suitable for appending into a parent
    XML object.

    Args:
        markdown_text (str, required): The markdown text to convert.
        multiline (bool, optional): If True, handle markup-multiline (block elements);
            if False, handle markup-line (inline elements only). Defaults to True.

    Returns:
        Optional[ElementTree.Element]: The parsed XML element, or None if conversion fails.

def prune_tree_copy(node: 'dict | None', depth: 'int | None' = None, child_keys: 'tuple' = ('groups', 'controls')) -> 'dict | None'

Return a SAFE COPY of *node* with nested structural children limited to *depth*.

    Shared, model-agnostic helper for the node getters (catalog/profile groups and
    controls; assessment ``tasks`` once implemented). The returned value shares no
    references with *node*, so callers may read, mutate, or serialize it without
    affecting the source document — mutation of live content must go through the
    OSCAL-standard-enforcing methods, never through a getter's return value.

    Only the collections named in *child_keys* are treated as structural children
    subject to depth pruning. The node's own intrinsic content (e.g. ``props``,
    ``links``, ``params``, ``parts``, ``title``) is always copied in full.

        depth = None  -> unlimited: a full deep copy of the entire subtree (the
                         default; mirrors the historical getter behavior).
        depth = 0     -> node only: the *child_keys* collections are omitted.
        depth = N     -> N levels of structural children retained, each recursively
                         pruned at ``depth - 1``.

    Args:
        node (dict | None, required): The group/control/task dict to copy, or None.
        depth (int | None, optional): Structural-child depth limit. Defaults to None.
        child_keys (tuple, optional): Keys treated as structural children. Defaults
            to ("groups", "controls"). Use ("tasks",) for assessment tasks.

    Returns:
        dict | None: A detached copy, or None when *node* is None.

    Raises:
        ValueError: If *depth* is a negative integer.

module oscal.oscal_source

oscal_source — OSCAL source acquisition and reference/import resolution.

Standalone machinery for turning a source reference (path, URI, ref dict, or
``OscalRef``) into loaded content, plus the data types used by import
resolution. None of this depends on the OSCAL model classes: the dependency runs
one way — ``OSCAL`` methods in ``oscal_content`` call into here. ``oscal_content``
imports and re-exports these names, so existing
``from .oscal_content import load_content`` (etc.) call sites keep working.

Contents:
    OscalRef / _normalize_refs          — source reference model + normalization.
    ImportState / ImportFailureCode / ImportLoadError / ImportFailure
                                        — import-resolution status/failure types.
    load_content / load_source          — fetch/read content from a reference.
    classify_source                     — classify a reference by path/URI type.
    _resolve_href / _canonicalize_ref / _oscal_format_variants
                                        — href resolution/canonicalization helpers.
    _find_import_candidates / _pick_import_target / _remove_import_from_dict
                                        — import_list lookup and statement removal.
    _hrefs_from_dict_spec / _backmatter_resource
                                        — document href/back-matter extraction.

class ImportFailure

Structured record of a failed import, carrying enough context for a retry attempt.

    Retry sources the calling module may supply:
        - A URI fragment (#uuid) pointing to a back-matter resource
        - A full URI identifying an alternate location for the content
        - The content itself as an XML, JSON, or YAML string

property is_fragment_ref

True when the original import href is a back-matter fragment reference.

class ImportFailureCode

Typed reason codes describing why an OSCAL import could not be resolved.

    Grouped by failure category — fragment/back-matter, full-URI/file,
    content, and duplicate/retry. Members:
        FRAGMENT_INVALID_UUID (str): Fragment reference is not a valid UUID.
        RESOURCE_NOT_FOUND (str): No back-matter resource matches the UUID.
        RESOURCE_NO_VIABLE_CONTENT (str): Resource has neither rlinks nor base64 content.
        LOCAL_NOT_FOUND (str): Local file was not found.
        REMOTE_UNREACHABLE (str): Remote host could not be reached.
        REMOTE_AUTH_REQUIRED (str): Remote resource requires authentication.
        REMOTE_UNSUPPORTED (str): URI scheme is not supported.
        CONTENT_EMPTY (str): Source returned no content.
        CONTENT_INVALID (str): Content is not valid OSCAL.
        ALREADY_IMPORTED (str): Retry href resolves to a file already loaded elsewhere.

class ImportLoadError

Exception carrying a typed import failure code from ``load_source()`` to ``resolve_imports()``.

    Attributes:
        code (ImportFailureCode): The typed reason the import failed.
        uri (str): The URI that failed to load.

def __init__(self, code: 'ImportFailureCode', uri: 'str', message: 'str' = '')

Initialize the error.

        Args:
            code (ImportFailureCode, required): The typed import failure reason.
            uri (str, required): The URI that failed to load.
            message (str, optional): Human-readable detail; a default is derived from
                ``code`` and ``uri`` when omitted.

class ImportState

Resolution state of a single import entry in an OSCAL document's import_list.

    Members:
        READY (str): "ready" — content is valid and loaded.
        NOT_LOADED (str): "not-loaded" — content has not been loaded.
        INVALID (str): "invalid" — content could not be loaded or failed validation.
        EXPIRED (str): "expired" — content is valid but the cached copy has expired.
        DUPLICATE (str): "duplicate" — the resolved href is already loaded by an earlier import.
        IGNORED (str): "ignored" — the caller explicitly chose to ignore this import.
        CYCLIC (str): "cyclic" — this import resolves to one of its own ancestors; the
            ancestor stays valid and recursion stops here to prevent an infinite loop.

class OscalRef

A single OSCAL source reference: an href with optional media type and hashes.

    Attributes:
        href (str): The reference target (URI or path).
        media_type (str | None): Optional media type of the target.
        hashes (list[dict] | None): Optional integrity hashes for the target.
        source_type (str): Classified source type (set by classification; not an init arg).
        source_scheme (str): URI scheme of the source (not an init arg).
        source_supported (bool): Whether the source scheme can be fetched (not an init arg).

def classify_source(ref: 'OscalRef', only_oscal: 'bool' = False) -> 'bool'

Classify a source reference by path/URI type and Python stdlib accessibility.

    Sets the ``source_type``, ``source_scheme``, and ``source_supported`` fields on
    ``ref`` in place. Classification intentionally does not use file extensions,
    because many valid content endpoints (e.g. APIs) lack predictable suffixes.

    Args:
        ref (OscalRef, required): The reference to classify; mutated in place.
        only_oscal (bool, optional): Reserved for future content-shape validation;
            currently does not affect classification. Defaults to False.

    Returns:
        bool: True if the reference was classified (even if unsupported), False only
            when the href is empty.

def load_content(source: 'str | dict | OscalRef | list', media_type: 'str' = '', only_oscal: 'bool' = False, cache_directive: "'CacheDirective | None'" = None) -> 'str'

Load content from one or more sources and return the first successful payload.

    Args:
        source (str | dict | OscalRef | list, required): The source(s) to load, as a
            URI/path string, reference dict, ``OscalRef``, or a fallback list of these.
        media_type (str, optional): Expected media type hint. Defaults to "".
        only_oscal (bool, optional): When True, restrict acceptance to OSCAL content.
            Defaults to False.
        cache_directive (CacheDirective | None, optional): Caching directive applied
            to remote fetches. Defaults to the standard 24h behavior.

    Returns:
        str: The first successfully loaded content payload, or "" if none load and no
            typed error was raised.

    Raises:
        ImportLoadError: When a source fails with a typed reason (the last error is
            re-raised when every source in a list fails).

def load_source(ref: 'OscalRef', cache_directive: "'CacheDirective | None'" = None) -> 'str'

Fetch or read content from a classified ``OscalRef``.

    Args:
        ref (OscalRef, required): A reference that has already been classified
            (via :func:`classify_source`) to set its source type/scheme.
        cache_directive (CacheDirective | None, optional): Caching directive applied
            to remote (http/https) fetches. Defaults to the standard 24h behavior.

    Returns:
        str: The raw content as a string on success.

    Raises:
        ImportLoadError: With a typed ``ImportFailureCode`` on any load failure.

module oscal.oscal_content

oscal_content — OSCAL base class and shared content operations.

Defines the ``OSCAL`` base class used by all eight model classes for creating,
loading, manipulating, validating, and format-converting OSCAL content. All
published OSCAL versions, formats, and models can be validated and converted;
newly published versions can be "learned" by updating the OSCAL Support
database. This module also drives import resolution and Metapath/JSON query
support, and defines the library exception hierarchy (``OSCALError`` and its
subclasses, e.g. ``UnsupportedModelOperation``).

Two supporting modules are imported and re-exported here so existing
``from .oscal_content import ...`` call sites keep working:

* ``oscal_helpers`` — model-agnostic dict/markup helpers (props/links, UUID
  generation/validation, id lookups, depth-limited safe copies).
* ``oscal_source`` — source acquisition and reference/import resolution
  (``OscalRef``, ``load_content``/``load_source``, ``classify_source``, href
  resolution, and the ``ImportState``/``ImportFailureCode``/``ImportLoadError``/
  ``ImportFailure`` types).

See https://github.com/brian-ruf/oscal-class for more details.

Module constants:
    INDENT (int): Number of spaces used for indentation in pretty-printed output.
    OSCAL_DEFAULT_XML_NAMESPACE (str): The NIST OSCAL XML namespace URI
        (re-exported from ``oscal_support``).
    OSCAL_FORMATS (list): Supported serialization formats
        (re-exported from ``oscal_support``).
    OSCAL_DATATYPES (dict): OSCAL Metaschema data type definitions
        (re-exported from ``oscal_datatypes``).

class ContentState

Progressive content-processing state; each level implies all prior levels passed.

    Members (ordered by increasing progress):
        NONE (int): -1 — no content / uninitialized.
        NOT_AVAILABLE (int): 0 — content could not be acquired.
        ACQUIRED (int): 1 — content was acquired (non-empty string).
        WELL_FORMED (int): 2 — content is well-formed XML, JSON, or YAML.
        VALID (int): 3 — content passes OSCAL schema validation (minimum for view/edit).
        IMPORTS_RESOLVED (int): 4 — all imported OSCAL documents resolved successfully.

class ImportResult

Outcome of an :meth:`OSCAL.add_import` or :meth:`OSCAL.update_import` call.

    Attributes:
        status (str): One of "added", "replaced", "updated", "duplicate", "invalid", or
            "error". "added"/"replaced"/"updated" are the success cases (``ok`` is True):
            "added"/"replaced" come from :meth:`add_import` (new entry vs placeholder
            filled), while :meth:`update_import` returns "replaced" when it repointed the
            import at a new/other resource and "updated" when it modified the existing
            resource in place. "duplicate" means the href already appears among this
            document's own imports. "invalid" means the operation is not permitted by this
            model's import cardinality (e.g. a catalog has no imports). "error" is a
            bad-input or read-only failure.
        entry (dict | None): The import entry — the newly added/replaced/updated entry, or
            the conflicting existing import for "duplicate".
        resource (dict | None): The back-matter resource referenced by the import
            (created, reused, or updated). None for "duplicate"/"invalid"/"error".
        message (str): Human-readable detail, primarily for the non-success statuses.

property is_duplicate

bool: True when the href already matched one of this document's imports.

property is_invalid

bool: True when the model's import cardinality forbids the operation.

property is_updated

bool: True when an existing resource was modified in place (update_import).

property ok

bool: True when an import was added, replaced, or updated.

class OSCAL

Base class for all OSCAL model documents.

    Provides loading, saving, validation, format conversion (XML/JSON/YAML),
    import resolution, and query support shared by every OSCAL model. Content is
    held internally as a JSON-primary dict (``self._dict``); the XML tree
    (``self._tree``) is a transient, derived view built on demand for XML
    serialization and released afterward (it persists only in the degraded case
    where XML was loaded but could not be converted to a dict). Do not instantiate
    directly; use the factory classmethods ``load``, ``loads``, or ``new``, or a
    model subclass.

        Attributes (Content Location):
            href_original: The original href as provided (e.g., in an import statement)
            is_valid_href: True if the href is accessible and the content was loaded successfully
            href         : Working href (may differ from href_original after redirect/retry)

        Attributes (Class States):
            is_valid    : True if the content passed OSCAL validation, False otherwise
            is_local    : True if the source is a local file, False if it's remote (http/https)
            is_cached   : True if remote content has a local cache copy, False otherwise
            is_canonical: True if this is canonical/published content; forces read-only
            is_read_only: True if the content may not be mutated (property; True whenever
                          is_canonical is set, else reflects the loader/caller flag)
            is_unsaved  : True if there are unsaved modifications, False otherwise

        Attributes (Caching and Expiration):
            loaded: Timestamp of when the content was loaded (datetime object)
            ttl: Time to live for cached content in seconds (0 or less means never expire)

        Attributes (Content and Summary):
            content        : The raw content as a string in its original format
            original_format: The original format of the content (xml, json, yaml)
            model          : The identified OSCAL model (e.g., "catalog", "profile")
            oscal_version  : The OSCAL version from the metadata (if available)
            last_modified  : The last modified date from the metadata (if available)
            title          : The title from the metadata (if available)
            published      : The publication date from the metadata (if available)
            version        : The version from the metadata (if available)
            remarks        : Any remarks from the metadata (if available)

        Attributes (Processing Objects):
            self.import_list = [] # An array of dictionaries representing imported OSCAL content

        Properties:
            is_editable: True if the content can be modified, False otherwise

    Cross-model operation guard:
        Many mutation/query methods are only valid for certain models (e.g.
        ``resolve`` on a Profile, ``append_component`` on an SSP). Calling one on a
        model that does not define it does not raise a bare ``AttributeError``:
        ``__getattr__`` raises :class:`UnsupportedModelOperation` (an ``OSCALError``
        and ``AttributeError``) reporting which models — if any — define the name,
        logs that detail, and records it on ``self.errors``. Use :meth:`supports`
        to check before calling. Applications catch :class:`OSCALError` at their
        boundary and show the user ``err.user_message`` instead of internals.

classmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)

Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.

def add_import(self, href: 'str', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], version: 'str' = '', remarks: 'str' = '', *, include_all: 'bool' = False) -> 'ImportResult'

Add a first-level import to this document, backed by a back-matter resource.

        Uniform across every model; legality is governed by :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — ``invalid``.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR) — ``invalid``; set
          their single import with :meth:`retry_import` instead of add/remove.
        * Otherwise an import is added while the count of *real* imports is below the
          model's ``max`` (unbounded when ``max`` is None).

        The import references a back-matter ``resource`` by UUID fragment
        (``href="#<uuid>"``): if a resource whose ``rlink`` already targets ``href``
        exists it is reused, otherwise one is created via :meth:`append_resource` with
        which this method shares its resource parameters (``uuid``/``title``/
        ``description``/``props``/``remarks``). The ``href`` becomes the resource's
        single ``rlink`` (with a best-effort ``media-type`` inferred from it), and
        ``version`` is appended to ``props`` as a ``prop`` named ``"version"``. An empty
        placeholder import (href ``""``/``"#"``) is filled in place; otherwise the entry
        is appended (list models) or set (single-import models). After placement the
        import tree and any derived state are refreshed via :meth:`_after_imports_changed`.

        Args:
            href (str, required): Reference to the imported OSCAL file (XML/JSON/YAML);
                becomes the created resource's ``rlink`` href.
            uuid (str, optional): UUID for the created resource; generated when empty.
                Ignored when an existing resource is reused. Mirrors :meth:`append_resource`.
            title (str, optional): Title for the created resource.
            description (str, optional): Description for the created resource.
            props (list, optional): Property dicts for the created resource; ``version``
                (below) is appended to these. Mirrors :meth:`append_resource`.
            version (str, optional): Convenience — appended to ``props`` as a ``prop``
                named ``"version"`` (resources have no native version field).
            remarks (str, optional): Remarks (markdown) for the created resource.
            include_all (bool, optional): Keyword-only, profile-only. When True a new
                profile import selects all controls via ``include-all`` instead of the
                default empty ``include-controls``/``with-ids`` placeholder. Ignored by
                models whose imports carry no selection. Defaults to False.

        Returns:
            ImportResult: ``status`` of "added", "replaced", "duplicate", "invalid",
                or "error", with the relevant ``entry`` and ``resource``.

def append_child(self, path: 'str', child: 'dict') -> 'dict | None'

Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.

def append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'

Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.

def dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'

Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.
        Output keys/elements are emitted in canonical metaschema order (see :meth:`dumps`).

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.

def dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'

Serialize the current content to a string in the specified format.

        Keys/elements are emitted in canonical NIST metaschema order: XML element
        order is schema-required and always canonical; JSON/YAML key order is
        canonical on a best-effort basis (see :meth:`_ordered_dict`).

        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.

property duplicate_imports

Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.

property failed_imports

Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.

def find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'

Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id), ``party`` (by uuid), ``location`` (by uuid), or ``responsible-party`` (by
        role-id), or a ``control``/``group``/``param``/``part`` (by id). This walks
        ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.

classmethod from_string(cls, content: 'str', *, href: 'str | None' = None)

Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).

def get_location_by_uuid(self, location_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``location`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.locations`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a location with the given uuid
        is found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_oscal_object(self, uuid, _seen=None)

Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.

def get_parameter_by_id(self, param_id: 'str', with_source: 'bool' = False) -> 'Optional[dict]'

Return a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content. See :meth:`_lookup_in_scope` for the ``with_source`` locator form.

def get_party_by_uuid(self, party_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``party`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a party with the given uuid is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_resource_by_uuid(self, resource_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a back-matter resource defined anywhere in scope, or None.

        Looks in THIS document's ``back-matter`` first; on a local miss the search
        cascades out through the immediately imported documents and continues depth-first
        along every branch (de-duplicated and cycle-safe) until a ``resource`` whose
        ``uuid`` matches is found or every branch is exhausted. This lets a cross-reference
        (``href="#uuid"``) resolve even when the resource is defined in an imported
        document rather than locally. Subclasses may override to prefer resolved content.

        Args:
            resource_uuid (str, required): The bare resource UUID to resolve (no ``#``).
            with_source (bool, optional): Return the full locator (element + owning
                ``object_uuid``/``href``) instead of the bare resource; useful for
                resolving the resource's relative rlink hrefs. Defaults to False.
            local_only (bool, optional): Search only THIS document, never imports.
                Defaults to False.

        Returns:
            Optional[dict]: A safe copy of the matching resource (or locator), or None.

def get_responsible_party_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``responsible-party`` (by role-id) in scope, or None.

        A ``responsible-party`` is keyed by the ``role-id`` it fulfills. Looks in THIS
        document's ``metadata.responsible-parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until one with the given role-id is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

        Note: this targets metadata-level ``responsible-parties`` only. The
        ``responsible-role`` assemblies embedded throughout implementation/assessment
        models are model-specific and handled by a separate, later cascade.

def get_role_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``role`` (by id) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.roles`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a role with the given id is
        found or every branch is exhausted — so a ``role-id`` reference resolves even when
        the role is defined in an imported document. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def ignore_import(self, href: 'str') -> 'bool'

Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.

property import_tree

Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.

property imports_resolved

bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).

def initial_validation(self, content: 'str') -> 'bool'

Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.

property is_acquired

bool: True once content has been acquired (``content_state >= ACQUIRED``).

property is_cache_expired

True when remote cached content has exceeded its TTL.

property is_editable

Can this content be modified?

property is_fresh

True when content is local or cached and within its TTL.

property is_read_only

bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.

property is_remote

bool: True when the content originates from a remote source (not a local file).

property is_stale

True when remote cached content has exceeded its TTL.

property is_valid

bool: True when content passes OSCAL validation (``content_state >= VALID``).

property is_well_formed

bool: True when content is well-formed (``content_state >= WELL_FORMED``).

property json

Return the content as a JSON string in canonical key order.

def json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

classmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)

Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior (cf. ``json.load`` /
        ``pickle.load``): the source is a **local** path or a file-like object. Use
        ``loads(...)`` for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        sources (``http``/``https``/``file``/``ftp``/…) — ``load`` does not fetch remotely.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.

classmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)

Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.

classmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')

Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.

classmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)

Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.

property origin_state

Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.

def put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'

Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.

def query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

def reachable_ids(self, _seen=None) -> 'set'

Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.

def rebuild_import_tree(self) -> 'dict'

Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.

def remove_import(self, href: 'str') -> 'bool'

Remove a first-level import statement from this document.

        Operates only on this document's own imports, never on descendants. The
        import *statement* is deleted from ``self._dict``; any back-matter resource
        it referenced via a URI fragment (``href="#uuid"``) is intentionally
        preserved. The affected part of the import tree is refreshed and any
        model-specific derived state (e.g. a Profile's resolved catalog) is reset
        via :meth:`_after_imports_changed`.

        Cardinality is enforced from :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — invalid.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR require exactly
          one) — invalid; change that import with :meth:`retry_import` instead.
        * Otherwise the removal is rejected when it would drop the count of *real*
          imports (non-empty, non-``"#"`` href) below the model's minimum. Removing
          an empty placeholder is always allowed for variable-cardinality models.

        Args:
            href: Any href that identifies the import — its literal href (including
                a ``"#uuid"`` fragment or an empty ``""``/``"#"`` placeholder), or
                the resolved target href of the back-matter resource it references.

        Returns:
            True if an import was found and removed; False if not found, the
            cardinality forbids removal, or the content is read-only.

def resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'

Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-ap/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.

def retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def set_metadata(self, content: 'dict' = {}) -> 'bool'

Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.

def supports(self, name: 'str') -> 'bool'

Return True if this model exposes ``name`` as a method or attribute.

        Lets callers look before they leap when an operation is only valid for
        some OSCAL models::

            if doc.supports("add_control"):
                doc.add_control(...)

        Only class-level (shared, model-defined) members count; per-instance
        attributes set at runtime are ignored, so the check reflects the model's
        capabilities rather than incidental state.

        Args:
            name (str, required): The method/attribute name to test.

        Returns:
            bool: True if the model defines ``name``.

property unresolved_imports

Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.

def update_import(self, *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None, new_resource: 'bool' = True) -> 'ImportResult'

Modify the single import of a one-import model (SSP, AP, AR, or POA&M).

        These models carry exactly one import (POA&M: at most one), so :meth:`add_import`
        and :meth:`remove_import` do not apply — this is how their import is changed. It
        takes the same resource fields as :meth:`update_resource` (``title``,
        ``description``, ``props``, ``rlinks``, ``remarks`` — ``None`` leaves a field
        unchanged; arrays replace wholesale) plus ``new_resource``. The behavior depends
        on what the existing import points at:

        * **No import yet** (only possible for POA&M): the call is forwarded to
          :meth:`add_import` (``new_resource`` does not apply and is omitted); the import
          target's href is taken from the first supplied ``rlink``.
        * **Import is a direct URI** (or an empty ``""``/``"#"`` placeholder): a new
          back-matter resource is created (via :meth:`append_resource`) — its ``rlink`` is
          the supplied ``rlinks`` when given, otherwise the existing URI — and the import's
          href is repointed to that resource's ``#uuid``. (``new_resource`` does not apply:
          there is no backing resource to update.) Status: "replaced".
        * **Import is a ``#uuid`` fragment**:

          - ``new_resource=True`` (default): a brand-new resource with a new UUID is
            created via :meth:`append_resource` from the supplied fields, and the import's
            href is repointed to it. The prior resource is **left in place** — it is not
            deleted, because other content may reference it (including OSCAL documents not
            currently loaded that import this one and cite that resource by UUID). Because
            the new resource is built only from the fields you pass, supply ``rlinks`` (and
            any props) for the new target; read the old resource first with
            :meth:`get_resource_by_uuid` if you want to carry values forward. Status:
            "replaced".
          - ``new_resource=False``: the existing resource is edited in place via
            :meth:`update_resource` (same wholesale array-replacement semantics and
            data-loss caveats — see that method). The import's href is unchanged. Status:
            "updated".

        Args:
            title (str | None, optional): Resource title; ``""`` removes it.
            description (str | None, optional): Resource description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). Also the source of the import target's
                href when creating a resource or bootstrapping a POA&M import.
            remarks (str | None, optional): Resource remarks (markdown); ``""`` removes it.
            new_resource (bool, optional): When the import already references a ``#uuid``
                resource, True (default) creates a new resource and repoints; False edits
                the existing resource in place. Ignored for the direct-URI/placeholder and
                no-import cases. Defaults to True.

        Returns:
            ImportResult: ``status`` "added"/"replaced"/"updated" on success (``ok`` True),
                or "invalid"/"error" otherwise, carrying the import ``entry`` and the
                created/updated ``resource``.

def update_resource(self, uuid: 'str', *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None) -> 'dict | None'

Update fields of an existing ``back-matter`` resource, selected by ``uuid``.

        Only a resource defined in THIS document's own ``back-matter`` is editable
        (imported resources are not). Each field is optional and independent:

        * ``None`` (the default) leaves that field untouched.
        * A scalar (``title``/``description``/``remarks``) replaces the current value;
          an empty string removes the field entirely.
        * An array (``props``/``rlinks``) **replaces the existing array wholesale** — the
          old list is discarded and the supplied list becomes the new one. Passing an
          empty list removes the field.

        .. warning::
            Array replacement is destructive and total, not a merge. Whatever you pass
            for ``props`` or ``rlinks`` becomes the *complete* new list; every entry not
            present in your list is permanently dropped. A resource frequently carries
            entries you did not author and may not be aware of — for example multiple
            ``rlinks`` pointing at format variants (``.xml``/``.json``) of the same file,
            ``rlinks`` bearing ``hashes`` for integrity, a ``base64`` payload, or ``props``
            added by other tools or pipelines. Supplying a partial list here silently
            deletes all of those. There is no undo.

            **Recommended pattern:** read the current resource first with
            :meth:`get_resource_by_uuid`, mutate the copy it returns (append to / edit the
            existing ``props``/``rlinks`` rather than rebuilding them from scratch), then
            pass those full arrays back to this method. That way you extend the resource
            instead of overwriting it, and nothing you did not intend to touch is lost::

                res = doc.get_resource_by_uuid(uuid)          # safe copy of the whole resource
                res["rlinks"].append({"href": "catalog.json",
                                      "media-type": "application/json"})
                doc.update_resource(uuid, rlinks=res["rlinks"])   # full list, nothing dropped

        Args:
            uuid (str, required): UUID of the local back-matter resource to update.
            title (str | None, optional): New title; ``""`` removes it. ``None`` = unchanged.
            description (str | None, optional): New description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts (see
                :func:`append_props`). ``[]`` removes all props. ``None`` = unchanged.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). ``[]`` removes all rlinks.
            remarks (str | None, optional): New remarks (markdown); ``""`` removes them.

        Returns:
            dict | None: A safe copy of the updated resource, or None when the content is
                read-only, ``uuid`` is empty, or no local resource with that UUID exists.

def validate(self, format: 'str' = '') -> 'bool'

Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).

def walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')

Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None

property xml

Return the content as an XML string in canonical element order.

        Rebuilds from the current dict via the metaschema converter (reflecting
        the latest edits, in schema-required element order) and retains no tree.

property yaml

Return the content as a YAML string in canonical key order.

class OSCALError

Base class for every error intentionally raised by the oscal library.

    Catch this at an application boundary to handle any library-originated
    failure uniformly — typically to log the developer-facing detail and show
    the end user a friendly message — without also swallowing unrelated Python
    errors. Instances expose two messages:

    * ``developer_message`` — precise, actionable detail for logs/telemetry.
    * ``user_message`` — a safe, generic sentence suitable for end users.

    ``str(err)`` returns the developer message.

property developer_message

str: Precise, actionable detail for developers (the default ``str``).

property user_message

str: A safe, generic message that omits internal detail.

class OriginState

Origin/freshness state of a document's source (not progressive).

    Freshness is time-based and computed on demand rather than stored.

    Members:
        LOCAL (str): "local" — local file system source; always accessible.
        REMOTE_UNCACHED (str): "remote-uncached" — remote source with no local cache copy.
        REMOTE_FRESH (str): "remote-fresh" — remote source cached and within its TTL.
        REMOTE_STALE (str): "remote-stale" — remote source cached but past its TTL.

class UnsupportedModelOperation

Raised when a method/attribute valid for *some* OSCAL model is accessed on
    a model that does not define it (e.g. calling ``add_control`` on an SSP).

    Also raised — with an empty ``valid_on`` — for names no OSCAL model defines,
    which almost always indicates a typo.

    This subclasses :class:`AttributeError` as well as :class:`OSCALError` so that
    ``hasattr(obj, name)`` and ``getattr(obj, name, default)`` keep their normal
    semantics (return ``False`` / the default rather than propagating), while the
    error remains catchable as an ``OSCALError``.

    Attributes:
        method (str): The attribute/method name that was accessed.
        model (str): The model name of the instance it was accessed on.
        valid_on (list[str]): Model names that *do* define the name (may be empty).

def __init__(self, method: 'str', model: 'str', valid_on: "'list[str] | None'" = None)

Initialize the error.

        Args:
            method (str, required): The attribute/method name that was accessed.
            model (str, required): The model name of the instance it was used on.
            valid_on (list[str], optional): Model names that define ``method``.

property developer_message

str: Which model was called, the missing operation, and where it's valid.

property user_message

str: A safe, generic message that omits internal detail.

class VersionSupport

Whether the content's declared OSCAL version was supported as-is.

    Set during initial validation once the model/version are identified. A
    ``CLOSEST_MATCH`` or ``UNSUPPORTED`` result means the requested version was not
    available locally and could not be acquired from the bundled database or NIST.

    Members:
        EXACT (str): "exact" — the declared OSCAL version's support was available (or
            was successfully acquired); validation/conversion used that exact version.
        CLOSEST_MATCH (str): "closest-match" — the declared version was unavailable;
            the closest available version within the same OSCAL major was substituted.
            ``requested_oscal_version`` and ``resolved_oscal_version`` differ.
        UNSUPPORTED (str): "unsupported" — the declared version/model could not be
            supported at all; the document cannot advance past ``ACQUIRED``.

class _ReadableSource

Protocol for file-like objects that provide read().

def read(self, size: 'int' = -1) -> 'Any'

Read up to ``size`` bytes/characters from the source (``-1`` reads all).

def append_resource(oscal_obj: 'OSCAL', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'

Appends a resource to the back-matter section of the OSCAL JSON content.

    Args:
        oscal_obj:   The OSCAL document to modify.
        uuid:        Resource UUID; generated if not supplied.
        title:       Optional resource title.
        description: Optional resource description.
        props:       Optional list of prop dicts (see append_prop).
        rlinks:      Optional list of rlink dicts with "href" and optional "media-type"/"hashes".
        base64:      Not yet implemented; a warning is logged if supplied.
        remarks:     Optional remarks (OSCAL markup-multiline / markdown string).

    Returns:
        dict | None: The appended resource dict, or None on error.

def create_new_oscal_content(model_name: 'str', title: 'str', version: 'str' = '', published: 'str' = '', format: 'str' = 'xml') -> 'Optional[OSCAL]'

Returns a validated base OSCAL instance loaded from a template.
    Currently this is based on loading a template file from package data.
    In the future, this should be generated based on the latest metaschema definition.

    The supplied ``title`` (and ``version``/``published`` when given) overwrite the
    template's placeholder metadata, so ``Catalog.new("X")`` / ``Profile.new("X")`` set
    the document title as expected.

    The returned instance is always a base OSCAL object. Callers that need a
    specific model subclass (e.g. Catalog, Profile) are responsible for
    reassigning __class__ and calling _init_common() afterward.

    Args:
        model_name (str): The OSCAL model name (e.g., "catalog", "system-security-plan").
        title (str): The title for the new OSCAL content.
        version (str): Optional content version.
        published (str): Optional publication date.
        format (str): The desired format for the new content ("xml", "json", "yaml"). Defaults to "xml".

    Returns:
        Optional[OSCAL]: A base OSCAL instance loaded from template, or None on failure.

def current_actor() -> "'str | None'"

Return the current actor (view/session) id, or None when unset.

    Returns:
        str | None: The actor id activated by :func:`use_actor`, else None.

def if_update_successful(fn)

Decorator marking content dirty after a successful mutation.

    Wraps a mutation method; when it returns a non-None result, sets
    ``self.is_unsaved = True`` and updates ``self.last_modified``.

    Args:
        fn (Callable, required): The mutation method to wrap.

    Returns:
        Callable: The wrapped method.

def register_model(model_name: 'str', cls: 'type') -> 'None'

Register an OSCAL model subclass so factory methods return typed instances.

    Args:
        model_name (str, required): The OSCAL model name (e.g. "catalog").
        cls (type, required): The ``OSCAL`` subclass implementing that model.

def requires(**conditions)

Decorator factory gating a method on instance attribute/property values.

    The wrapped method runs only when every ``attr == expected`` condition holds
    on ``self``; otherwise it logs an error and returns None.

    Args:
        **conditions: Mapping of instance attribute/property name to its required
            value (e.g. ``writable=True``, ``is_remote=True``).

    Returns:
        Callable: A decorator that wraps the target method with the guard.

    Example:
        >>> @requires(is_read_only=False)
        ... def mutate(self): ...

def use_actor(actor: "'str | None'")

Set the current actor for the duration of the ``with`` block.

    Mutations performed inside the block are attributed to ``actor``; a document
    write-locked by a *different* actor is read-only within the block.

    Args:
        actor (str | None, required): The actor (view/session) id.

    Yields:
        str | None: The activated actor id.

module oscal.oscal_datatypes

oscal_datatypes — OSCAL Metaschema data type definitions and helpers.

Defines the OSCAL Metaschema primitive data types and their validation
patterns, and provides a helper for producing OSCAL-conformant timezone-aware
date-time strings.

Module constants:
    OSCAL_DATATYPES (dict): Mapping of OSCAL Metaschema data type name (str) to
        a definition dict. Each definition contains the keys ``base-type`` (str),
        ``xml-pattern`` (str regex), ``json-pattern`` (str regex),
        ``recommended-pattern`` (str regex), ``documentation`` (str),
        ``remarks`` (str), and ``links`` (list of {"title", "url"} dicts).
        Covers types such as ``string``, ``token``, ``uuid``, ``uri``,
        ``date-time-with-timezone``, ``integer``, ``boolean``, ``markup-line``,
        and ``markup-multiline``.

def normalize_uri_reference(value: str) -> str

Best-effort conversion of a non-conformant value into a valid URI-reference.

    Defensive coding for URI values that arrive with characters RFC 3986 does not permit
    unescaped — e.g. a Windows path with backslashes (``R:\a\b.json``), a raw space, or a
    non-ASCII (IRI) character. Backslashes are converted to forward slashes (the usual
    Windows-path intent) and every remaining disallowed character is percent-encoded using
    UTF-8, while URI-structural characters and existing ``%XX`` escapes are preserved. A
    value that is already conformant (and any non-string/empty input) is returned unchanged.

    Note this cannot invent a missing scheme, so it does not, on its own, make a bare
    ``#fragment`` a valid *absolute* ``uri`` — it only repairs the character set.

    Args:
        value (str): The candidate URI-reference.

    Returns:
        str: A character-set-conformant URI-reference.

def oscal_date_time_with_timezone(date_time=None, format='%Y-%m-%dT%H:%M:%SZ') -> str

Convert a date/time to UTC and format it as an OSCAL date-time-with-timezone string.

    Args:
        date_time (datetime | str, optional): The date and time to convert. May be a
            ``datetime`` object or an ISO-8601 string parseable into one. Naive values
            are assumed to be UTC. Defaults to the current date and time.
        format (str, optional): The ``strftime`` format string to apply.
            Defaults to ``"%Y-%m-%dT%H:%M:%SZ"`` (the OSCAL standard format).

    Returns:
        str: The formatted date-time string, or an empty string if parsing or
            formatting fails.

module oscal.oscal_controls

oscal_controls — OSCAL control-layer model classes.

Provides the editable model classes for the OSCAL control models: ``Catalog``
(defines controls), ``Profile`` (selects and tailors controls into baselines),
and ``Mapping`` (relates controls across frameworks). Each class subclasses
``OSCAL`` from ``oscal_content`` and adds model-specific navigation and
mutation helpers. ``ImportResult`` (the structured return value of
:meth:`OSCAL.add_import`) and the ``MEDIA_TYPES`` / ``_infer_media_type`` media-type
helpers are defined in ``oscal_content`` / ``oscal_helpers`` and re-exported here for
backward compatibility.

class Catalog

Editable OSCAL Catalog model.

    Subclasses ``OSCAL`` and adds methods for creating, editing, navigating, and
    removing controls and control groups. Read-only guards apply to mutation methods
    when the instance state is not editable.

    Attributes:
        controls_tree (list[dict]): A lightweight, nested view of the catalog's
            group/control hierarchy for UI tree navigation. Each node is
            ``{"id", "label", "title", "group", "children"}`` where ``group`` is True
            for groups and False for controls, and ``children`` holds nested groups
            and controls (control enhancements included). Built when the catalog is
            found to be valid OSCAL and refreshed on every structural or
            title/label change. See :meth:`_build_controls_tree`.

classmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)

Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.

def add_import(self, href: 'str', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], version: 'str' = '', remarks: 'str' = '', *, include_all: 'bool' = False) -> 'ImportResult'

Add a first-level import to this document, backed by a back-matter resource.

        Uniform across every model; legality is governed by :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — ``invalid``.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR) — ``invalid``; set
          their single import with :meth:`retry_import` instead of add/remove.
        * Otherwise an import is added while the count of *real* imports is below the
          model's ``max`` (unbounded when ``max`` is None).

        The import references a back-matter ``resource`` by UUID fragment
        (``href="#<uuid>"``): if a resource whose ``rlink`` already targets ``href``
        exists it is reused, otherwise one is created via :meth:`append_resource` with
        which this method shares its resource parameters (``uuid``/``title``/
        ``description``/``props``/``remarks``). The ``href`` becomes the resource's
        single ``rlink`` (with a best-effort ``media-type`` inferred from it), and
        ``version`` is appended to ``props`` as a ``prop`` named ``"version"``. An empty
        placeholder import (href ``""``/``"#"``) is filled in place; otherwise the entry
        is appended (list models) or set (single-import models). After placement the
        import tree and any derived state are refreshed via :meth:`_after_imports_changed`.

        Args:
            href (str, required): Reference to the imported OSCAL file (XML/JSON/YAML);
                becomes the created resource's ``rlink`` href.
            uuid (str, optional): UUID for the created resource; generated when empty.
                Ignored when an existing resource is reused. Mirrors :meth:`append_resource`.
            title (str, optional): Title for the created resource.
            description (str, optional): Description for the created resource.
            props (list, optional): Property dicts for the created resource; ``version``
                (below) is appended to these. Mirrors :meth:`append_resource`.
            version (str, optional): Convenience — appended to ``props`` as a ``prop``
                named ``"version"`` (resources have no native version field).
            remarks (str, optional): Remarks (markdown) for the created resource.
            include_all (bool, optional): Keyword-only, profile-only. When True a new
                profile import selects all controls via ``include-all`` instead of the
                default empty ``include-controls``/``with-ids`` placeholder. Ignored by
                models whose imports carry no selection. Defaults to False.

        Returns:
            ImportResult: ``status`` of "added", "replaced", "duplicate", "invalid",
                or "error", with the relevant ``entry`` and ``resource``.

def add_part(self, parent_id: str, name: str, title: str = '', prose: str = '', ns: str = '', part_class: str = '', part_id: str = '', props: list = [], links: list = [], parts: list = []) -> Optional[dict]

Add a part to an existing control, group, or part.

        Parts are valid on controls and groups (and nest inside other parts), so
        ``parent_id`` may identify any of those by id. There is no limit on how many
        parts of a given ``name`` a level may hold (e.g. multiple ``guidance`` parts).

        Args:
            parent_id (str, required): ID of the control, group, or part to add to.
            name (str, required): The part ``name`` token (e.g. "overview",
                "guidance", "example", "assessment-objective").
            title (str, optional): Part title (markup-line).
            prose (str, optional): Part prose (markup-multiline / markdown).
            ns (str, optional): Part namespace URI.
            part_class (str, optional): Part ``class`` value.
            part_id (str, optional): ID for the new part (needed to target it later,
                e.g. to nest a child part or set its title).
            props (list, optional): Property dicts to add.
            links (list, optional): Link dicts to add.
            parts (list, optional): Pre-built child part dicts to nest.

        Returns:
            Optional[dict]: The newly created part dict, or None on failure — including
                when the part would violate a "leaf part" rule (e.g. a ``guidance``
                part cannot contain child parts).

def append_child(self, path: 'str', child: 'dict') -> 'dict | None'

Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.

def append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'

Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.

def create_control(self, parent_id: str, id: str, title: str = '', params: list = [], props: list = [], links: list = [], label: str = '', sort_id: str = '', alt_identifier: str = '', overview: str = '', statements: list = [], guidance: str = '', example: str = '', objectives: list = [], objects: list = [], methods: list = [], remarks: str = '') -> Optional[dict]

Create a new control under the specified parent group or control.

        Args:
            parent_id (str, required): ID of the parent to add the control to —
                ``'[root]'`` (or an empty string) for the catalog top level, a group
                id, or a control id. Nesting a control under a control models a
                control enhancement (e.g. ``ac-2.1`` under ``ac-2``). The add fails if
                it would mix controls and groups at the same level (not allowed in OSCAL).
            id (str, required): ID of the new control.
            title (str, optional): Title of the new control. Defaults to the label,
                or the id, when empty.
            params (list, optional): Parameters to add. Items may be parameter id
                strings or full parameter dicts.
            props (list, optional): Additional property dicts to add.
            links (list, optional): Link dicts to add.
            label (str, optional): Value for the inline ``label`` property.
            sort_id (str, optional): Value for the inline ``sort-id`` property.
            alt_identifier (str, optional): Value for the inline ``alt-identifier`` property.
            overview (str, optional): Prose (markdown) for the ``overview`` part.
            statements (list, optional): Statement items — strings or
                ``{'id':..., 'prose':...}`` dicts.
            guidance (str, optional): Prose (markdown) for the ``guidance`` part.
            example (str, optional): Prose (markdown) for the ``example`` part.
            objectives (list, optional): Assessment objective items.
            objects (list, optional): Assessment object items.
            methods (list, optional): Assessment method items.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            Optional[dict]: The newly created control dict, or None on failure.

        Note:
            Refreshes ``controls_tree`` when a control is successfully added.

def create_control_group(self, parent_id: str, id: str, title: str = '', params: list = [], props: list = [], links: list = [], label: str = '', sort_id: str = '', alt_identifier: str = '', overview: str = '', instruction: str = '', remarks: str = '') -> Optional[dict]

Create a new catalog group.

        Args:
            parent_id (str, required): ID of the parent group, or ``'[root]'`` (or an
                empty string) for the catalog top level. The add fails if it would mix
                controls and groups at the same level (not allowed in OSCAL).
            id (str, required): ID of the new group.
            title (str, optional): Title of the new group.
            params (list, optional): Parameters to add to the group.
            props (list, optional): Additional property dicts to add.
            links (list, optional): Link dicts to add.
            label (str, optional): Value for the inline ``label`` property.
            sort_id (str, optional): Value for the inline ``sort-id`` property.
            alt_identifier (str, optional): Value for the inline ``alt-identifier`` property.
            overview (str, optional): Prose (markdown) for the ``overview`` part.
            instruction (str, optional): Prose (markdown) for the ``instruction`` part.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            Optional[dict]: The newly created group dict, or None on failure.

        Note:
            Refreshes ``controls_tree`` when a group is successfully added.

def dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'

Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.
        Output keys/elements are emitted in canonical metaschema order (see :meth:`dumps`).

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.

def dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'

Serialize the current content to a string in the specified format.

        Keys/elements are emitted in canonical NIST metaschema order: XML element
        order is schema-required and always canonical; JSON/YAML key order is
        canonical on a best-effort basis (see :meth:`_ordered_dict`).

        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.

property duplicate_imports

Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.

property failed_imports

Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.

def find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'

Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id), ``party`` (by uuid), ``location`` (by uuid), or ``responsible-party`` (by
        role-id), or a ``control``/``group``/``param``/``part`` (by id). This walks
        ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.

classmethod from_string(cls, content: 'str', *, href: 'str | None' = None)

Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).

def get_control_by_id(self, control_id: str, depth: Optional[int] = None) -> Optional[dict]

Retrieve a control by its ID as a safe copy, searching all groups recursively.

        The returned dict is a detached copy — mutating it does NOT change the catalog;
        use the catalog's mutation methods to make persistent changes. The control's own
        content (``parts``, ``props``, ``links``, ``params`` …) is always returned in full;
        ``depth`` limits only nested child controls (enhancements).

        Args:
            control_id (str, required): The ``id`` of the control to find.
            depth (int | None, optional): Nested-enhancement depth. ``None`` (default)
                returns the full subtree; ``0`` omits enhancements; ``N`` keeps N levels.

        Returns:
            Optional[dict]: A safe copy of the matching control, or None if not found.

def get_control_list(self) -> list

Return a flat list of every control in the catalog, at all levels, as safe copies.

        The returned list is detached from the document: each control is a copy, so
        mutating any element does NOT change the catalog. A single deep copy of the whole
        list preserves internal identity relationships (an enhancement nested inside its
        parent is the same object as its own standalone entry). Use the catalog's mutation
        methods to make persistent changes.

        Returns:
            list: Safe copies of all controls found across the catalog and its groups.

def get_group_by_id(self, group_id: str, depth: Optional[int] = None) -> Optional[dict]

Retrieve a group by its ID as a safe copy, searching nested groups recursively.

        The returned dict is a detached copy — mutating it does NOT change the catalog;
        use the catalog's mutation methods to make persistent changes. The group's own
        content (``props``, ``links`` …) is always returned in full; ``depth`` limits only
        nested child groups and controls.

        Args:
            group_id (str, required): The ``id`` of the group to find.
            depth (int | None, optional): Nested group/control depth. ``None`` (default)
                returns the full subtree; ``0`` omits child groups/controls; ``N`` keeps
                N levels.

        Returns:
            Optional[dict]: A safe copy of the matching group, or None if not found.

def get_location_by_uuid(self, location_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``location`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.locations`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a location with the given uuid
        is found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_oscal_object(self, uuid, _seen=None)

Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.

def get_parameter_by_id(self, param_id: 'str', with_source: 'bool' = False) -> 'Optional[dict]'

Return a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content. See :meth:`_lookup_in_scope` for the ``with_source`` locator form.

def get_party_by_uuid(self, party_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``party`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a party with the given uuid is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_resource_by_uuid(self, resource_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a back-matter resource defined anywhere in scope, or None.

        Looks in THIS document's ``back-matter`` first; on a local miss the search
        cascades out through the immediately imported documents and continues depth-first
        along every branch (de-duplicated and cycle-safe) until a ``resource`` whose
        ``uuid`` matches is found or every branch is exhausted. This lets a cross-reference
        (``href="#uuid"``) resolve even when the resource is defined in an imported
        document rather than locally. Subclasses may override to prefer resolved content.

        Args:
            resource_uuid (str, required): The bare resource UUID to resolve (no ``#``).
            with_source (bool, optional): Return the full locator (element + owning
                ``object_uuid``/``href``) instead of the bare resource; useful for
                resolving the resource's relative rlink hrefs. Defaults to False.
            local_only (bool, optional): Search only THIS document, never imports.
                Defaults to False.

        Returns:
            Optional[dict]: A safe copy of the matching resource (or locator), or None.

def get_responsible_party_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``responsible-party`` (by role-id) in scope, or None.

        A ``responsible-party`` is keyed by the ``role-id`` it fulfills. Looks in THIS
        document's ``metadata.responsible-parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until one with the given role-id is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

        Note: this targets metadata-level ``responsible-parties`` only. The
        ``responsible-role`` assemblies embedded throughout implementation/assessment
        models are model-specific and handled by a separate, later cascade.

def get_role_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``role`` (by id) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.roles`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a role with the given id is
        found or every branch is exhausted — so a ``role-id`` reference resolves even when
        the role is defined in an imported document. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def ignore_import(self, href: 'str') -> 'bool'

Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.

property import_tree

Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.

property imports_resolved

bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).

def initial_validation(self, content: 'str') -> 'bool'

Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.

def insert_control(self, parent_id: str, control: dict, validate: bool = True) -> Optional[dict]

Insert a pre-formed control subtree whole under a parent, as a safe copy.

        Unlike :meth:`create_control` (which authors a control from discrete parts),
        this inserts an already-formed control dict — including its nested enhancements,
        parts, props, params, and links — without reshaping it. It is the faithful-copy
        path used by profile resolution (and, later, alter directives) to move a control
        from a source catalog into a resolved one.

        The incoming dict is deep-copied before insertion, so the caller's object is not
        aliased into the catalog (getters still return detached copies).

        Args:
            parent_id (str, required): ID of the parent to add the control to —
                ``'[root]'`` (or an empty string) for the catalog top level, a group id,
                or a control id (control-under-control models an enhancement). The add
                fails if it would mix controls and groups at the same level.
            control (dict, required): The control subtree to insert. Must be a dict with
                a non-empty ``id``.
            validate (bool, optional): When True (default), the control is validated
                against the ``control`` metaschema node first; on any error the insert is
                rejected and the catalog is left unchanged.

        Returns:
            Optional[dict]: A safe copy of the inserted control, or None on failure —
                bad input, parent not found, an id collision with an existing control, a
                controls/groups mix, or failed validation.

def insert_group(self, parent_id: str, group: dict, shallow: bool = True, validate: bool = True) -> Optional[dict]

Insert a group node under a parent, as a safe copy.

        Companion to :meth:`insert_control` for faithful-copy workflows. By default the
        insert is *shallow*: the group's intrinsic content (title, params, props, links,
        parts) is inserted but its child ``groups``/``controls`` are dropped, to be filled
        in afterward via :meth:`insert_control`/:meth:`insert_group`. This lets callers
        (e.g. profile resolution) build a group hierarchy incrementally while keeping
        empty-group pruning and duplicate handling under their own control.

        Args:
            parent_id (str, required): ID of the parent group, or ``'[root]'`` (or an
                empty string) for the catalog top level. The add fails if it would mix
                controls and groups at the same level.
            group (dict, required): The group node to insert. Must be a dict with a
                non-empty ``id``.
            shallow (bool, optional): When True (default), drop the group's child
                ``groups`` and ``controls`` before insertion. When False, insert the
                group subtree whole.
            validate (bool, optional): When True (default), validate the (possibly
                shallow) group against the ``group`` metaschema node first; on any error
                the insert is rejected and the catalog is left unchanged.

        Returns:
            Optional[dict]: A safe copy of the inserted group, or None on failure — bad
                input, parent not found, an id collision with an existing group, a
                controls/groups mix, or failed validation.

property is_acquired

bool: True once content has been acquired (``content_state >= ACQUIRED``).

property is_cache_expired

True when remote cached content has exceeded its TTL.

property is_editable

Can this content be modified?

property is_fresh

True when content is local or cached and within its TTL.

property is_read_only

bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.

property is_remote

bool: True when the content originates from a remote source (not a local file).

property is_stale

True when remote cached content has exceeded its TTL.

property is_valid

bool: True when content passes OSCAL validation (``content_state >= VALID``).

property is_well_formed

bool: True when content is well-formed (``content_state >= WELL_FORMED``).

property json

Return the content as a JSON string in canonical key order.

def json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

classmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)

Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior (cf. ``json.load`` /
        ``pickle.load``): the source is a **local** path or a file-like object. Use
        ``loads(...)`` for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        sources (``http``/``https``/``file``/``ftp``/…) — ``load`` does not fetch remotely.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.

classmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)

Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.

classmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')

Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.

classmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)

Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.

property origin_state

Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.

def put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'

Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.

def query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

def reachable_ids(self, _seen=None) -> 'set'

Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.

def rebuild_import_tree(self) -> 'dict'

Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.

def remove(self, id: str, cascade: bool = False, ignore_references: bool = False) -> Optional[dict]

Remove a control or group (found by id) from the catalog.

        Two independent locks guard the delete; either can block it, and the return
        value makes the reason explicit:

          * **cascade** — when ``cascade`` is False, a node with *immediate* children
            (direct groups, controls, or parts) is not removed. The block report
            lists those immediate child ids under ``"children"``. Set ``cascade=True``
            to remove the node together with everything beneath it.
          * **referential integrity** — when ``ignore_references`` is False, the node
            is not removed if any id in its subtree (the node, nested groups/controls,
            or any part) is referenced by a link elsewhere in the catalog. The block
            report lists those referenced ids under ``"referenced_ids"``. Set
            ``ignore_references=True`` to delete anyway; the now-dangling references
            are then returned under ``"dangling_refs"``.

        Both conditions are evaluated; if both block, ``"blocked_by"`` contains both
        reasons and both detail lists are present. Nothing is modified when blocked.

        References in *other* documents (profiles, SSPs, mappings) cannot be seen or
        fixed from here; on a successful delete a warning notes they may now break.
        Refreshes ``controls_tree`` on success.

        Args:
            id (str, required): The id of the control or group to remove.
            cascade (bool, optional): Permit removing a node that has immediate
                children. Defaults to False.
            ignore_references (bool, optional): Permit removing a node that is
                referenced elsewhere in the catalog. Defaults to False.

        Returns:
            Optional[dict]:
                * ``None`` when no control/group has that id.
                * On block: ``{"removed": False, "blocked_by": [...],
                  "children": [...]?, "referenced_ids": [...]?}``.
                * On success: ``{"removed": True, "removed_ids": [...],
                  "dangling_refs": [...]}``.

def remove_import(self, href: 'str') -> 'bool'

Remove a first-level import statement from this document.

        Operates only on this document's own imports, never on descendants. The
        import *statement* is deleted from ``self._dict``; any back-matter resource
        it referenced via a URI fragment (``href="#uuid"``) is intentionally
        preserved. The affected part of the import tree is refreshed and any
        model-specific derived state (e.g. a Profile's resolved catalog) is reset
        via :meth:`_after_imports_changed`.

        Cardinality is enforced from :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — invalid.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR require exactly
          one) — invalid; change that import with :meth:`retry_import` instead.
        * Otherwise the removal is rejected when it would drop the count of *real*
          imports (non-empty, non-``"#"`` href) below the model's minimum. Removing
          an empty placeholder is always allowed for variable-cardinality models.

        Args:
            href: Any href that identifies the import — its literal href (including
                a ``"#uuid"`` fragment or an empty ``""``/``"#"`` placeholder), or
                the resolved target href of the back-matter resource it references.

        Returns:
            True if an import was found and removed; False if not found, the
            cardinality forbids removal, or the content is read-only.

def resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'

Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-ap/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.

def retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def set_label(self, id: str, label: str, class_: str = '', group: str = '') -> Optional[dict]

Set (or clear) the ``label`` property of a control or group, found by id.

        The targeted property is the ``label`` prop in the default OSCAL namespace
        whose ``class``/``group`` qualifiers match the arguments: by default the one
        with **no** class and **no** group — the same property the navigation tree
        reads. Supplying ``class_`` and/or ``group`` targets (or creates) the label
        carrying exactly those qualifiers instead.

        Behaviour:
          * A matching prop exists  → its value is updated (the first, if several).
          * No matching prop exists → one is created with the given qualifiers.
          * ``label`` is empty       → every matching prop is removed.

        Refreshes ``controls_tree`` on success.

        Args:
            id (str, required): The id of the control or group to modify.
            label (str, required): The new label value; an empty string removes the
                matching label property.
            class_ (str, optional): The prop ``class`` to target/create.
            group (str, optional): The prop ``group`` to target/create.

        Returns:
            Optional[dict]: The modified control/group dict, or None if no such id
                exists.

def set_metadata(self, content: 'dict' = {}) -> 'bool'

Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.

def set_part_title(self, part_id: str, title: str = '') -> Optional[dict]

Set or remove the title of an existing part.

        Args:
            part_id (str, required): ID of the part to modify. The part must carry an
                ``id`` to be targetable.
            title (str, optional): The new title. When empty, the part's ``title`` is
                removed.

        Returns:
            Optional[dict]: The modified part dict, or None if no part with that id
                is found.

def set_title(self, id: str, title: str) -> Optional[dict]

Set the title of a control or group, found by id.

        Refreshes ``controls_tree`` on success, since a node's ``title`` is drawn
        from the object's title.

        Args:
            id (str, required): The id of the control or group to modify.
            title (str, required): The new title. Must be non-empty — a control's
                title is required by OSCAL, so blanking it is rejected.

        Returns:
            Optional[dict]: The modified control/group dict, or None if no such id
                exists or ``title`` is empty.

def supports(self, name: 'str') -> 'bool'

Return True if this model exposes ``name`` as a method or attribute.

        Lets callers look before they leap when an operation is only valid for
        some OSCAL models::

            if doc.supports("add_control"):
                doc.add_control(...)

        Only class-level (shared, model-defined) members count; per-instance
        attributes set at runtime are ignored, so the check reflects the model's
        capabilities rather than incidental state.

        Args:
            name (str, required): The method/attribute name to test.

        Returns:
            bool: True if the model defines ``name``.

property unresolved_imports

Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.

def update_import(self, *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None, new_resource: 'bool' = True) -> 'ImportResult'

Modify the single import of a one-import model (SSP, AP, AR, or POA&M).

        These models carry exactly one import (POA&M: at most one), so :meth:`add_import`
        and :meth:`remove_import` do not apply — this is how their import is changed. It
        takes the same resource fields as :meth:`update_resource` (``title``,
        ``description``, ``props``, ``rlinks``, ``remarks`` — ``None`` leaves a field
        unchanged; arrays replace wholesale) plus ``new_resource``. The behavior depends
        on what the existing import points at:

        * **No import yet** (only possible for POA&M): the call is forwarded to
          :meth:`add_import` (``new_resource`` does not apply and is omitted); the import
          target's href is taken from the first supplied ``rlink``.
        * **Import is a direct URI** (or an empty ``""``/``"#"`` placeholder): a new
          back-matter resource is created (via :meth:`append_resource`) — its ``rlink`` is
          the supplied ``rlinks`` when given, otherwise the existing URI — and the import's
          href is repointed to that resource's ``#uuid``. (``new_resource`` does not apply:
          there is no backing resource to update.) Status: "replaced".
        * **Import is a ``#uuid`` fragment**:

          - ``new_resource=True`` (default): a brand-new resource with a new UUID is
            created via :meth:`append_resource` from the supplied fields, and the import's
            href is repointed to it. The prior resource is **left in place** — it is not
            deleted, because other content may reference it (including OSCAL documents not
            currently loaded that import this one and cite that resource by UUID). Because
            the new resource is built only from the fields you pass, supply ``rlinks`` (and
            any props) for the new target; read the old resource first with
            :meth:`get_resource_by_uuid` if you want to carry values forward. Status:
            "replaced".
          - ``new_resource=False``: the existing resource is edited in place via
            :meth:`update_resource` (same wholesale array-replacement semantics and
            data-loss caveats — see that method). The import's href is unchanged. Status:
            "updated".

        Args:
            title (str | None, optional): Resource title; ``""`` removes it.
            description (str | None, optional): Resource description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). Also the source of the import target's
                href when creating a resource or bootstrapping a POA&M import.
            remarks (str | None, optional): Resource remarks (markdown); ``""`` removes it.
            new_resource (bool, optional): When the import already references a ``#uuid``
                resource, True (default) creates a new resource and repoints; False edits
                the existing resource in place. Ignored for the direct-URI/placeholder and
                no-import cases. Defaults to True.

        Returns:
            ImportResult: ``status`` "added"/"replaced"/"updated" on success (``ok`` True),
                or "invalid"/"error" otherwise, carrying the import ``entry`` and the
                created/updated ``resource``.

def update_resource(self, uuid: 'str', *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None) -> 'dict | None'

Update fields of an existing ``back-matter`` resource, selected by ``uuid``.

        Only a resource defined in THIS document's own ``back-matter`` is editable
        (imported resources are not). Each field is optional and independent:

        * ``None`` (the default) leaves that field untouched.
        * A scalar (``title``/``description``/``remarks``) replaces the current value;
          an empty string removes the field entirely.
        * An array (``props``/``rlinks``) **replaces the existing array wholesale** — the
          old list is discarded and the supplied list becomes the new one. Passing an
          empty list removes the field.

        .. warning::
            Array replacement is destructive and total, not a merge. Whatever you pass
            for ``props`` or ``rlinks`` becomes the *complete* new list; every entry not
            present in your list is permanently dropped. A resource frequently carries
            entries you did not author and may not be aware of — for example multiple
            ``rlinks`` pointing at format variants (``.xml``/``.json``) of the same file,
            ``rlinks`` bearing ``hashes`` for integrity, a ``base64`` payload, or ``props``
            added by other tools or pipelines. Supplying a partial list here silently
            deletes all of those. There is no undo.

            **Recommended pattern:** read the current resource first with
            :meth:`get_resource_by_uuid`, mutate the copy it returns (append to / edit the
            existing ``props``/``rlinks`` rather than rebuilding them from scratch), then
            pass those full arrays back to this method. That way you extend the resource
            instead of overwriting it, and nothing you did not intend to touch is lost::

                res = doc.get_resource_by_uuid(uuid)          # safe copy of the whole resource
                res["rlinks"].append({"href": "catalog.json",
                                      "media-type": "application/json"})
                doc.update_resource(uuid, rlinks=res["rlinks"])   # full list, nothing dropped

        Args:
            uuid (str, required): UUID of the local back-matter resource to update.
            title (str | None, optional): New title; ``""`` removes it. ``None`` = unchanged.
            description (str | None, optional): New description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts (see
                :func:`append_props`). ``[]`` removes all props. ``None`` = unchanged.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). ``[]`` removes all rlinks.
            remarks (str | None, optional): New remarks (markdown); ``""`` removes them.

        Returns:
            dict | None: A safe copy of the updated resource, or None when the content is
                read-only, ``uuid`` is empty, or no local resource with that UUID exists.

def validate(self, format: str = '') -> bool

Validate the catalog, then (re)build ``controls_tree`` on success.

        Extends :meth:`OSCAL.validate` so the navigation tree is refreshed the
        moment the catalog is converted and found to be valid OSCAL. When the
        content is not valid the tree is emptied — an invalid catalog exposes no
        navigable hierarchy.

        Args:
            format (str, optional): Accepted for API compatibility with the base
                method; does not alter the validation path.

        Returns:
            bool: True when every validation phase passes.

def walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')

Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None

property xml

Return the content as an XML string in canonical element order.

        Rebuilds from the current dict via the metaschema converter (reflecting
        the latest edits, in schema-required element order) and retains no tree.

property yaml

Return the content as a YAML string in canonical key order.

class Mapping

Class representing an OSCAL Mapping object.
    Inherits common OSCAL functionality and adds mapping-specific methods
    for managing mappings between controls and other objects.

classmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)

Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.

def add_import(self, href: 'str', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], version: 'str' = '', remarks: 'str' = '', *, include_all: 'bool' = False) -> 'ImportResult'

Add a first-level import to this document, backed by a back-matter resource.

        Uniform across every model; legality is governed by :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — ``invalid``.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR) — ``invalid``; set
          their single import with :meth:`retry_import` instead of add/remove.
        * Otherwise an import is added while the count of *real* imports is below the
          model's ``max`` (unbounded when ``max`` is None).

        The import references a back-matter ``resource`` by UUID fragment
        (``href="#<uuid>"``): if a resource whose ``rlink`` already targets ``href``
        exists it is reused, otherwise one is created via :meth:`append_resource` with
        which this method shares its resource parameters (``uuid``/``title``/
        ``description``/``props``/``remarks``). The ``href`` becomes the resource's
        single ``rlink`` (with a best-effort ``media-type`` inferred from it), and
        ``version`` is appended to ``props`` as a ``prop`` named ``"version"``. An empty
        placeholder import (href ``""``/``"#"``) is filled in place; otherwise the entry
        is appended (list models) or set (single-import models). After placement the
        import tree and any derived state are refreshed via :meth:`_after_imports_changed`.

        Args:
            href (str, required): Reference to the imported OSCAL file (XML/JSON/YAML);
                becomes the created resource's ``rlink`` href.
            uuid (str, optional): UUID for the created resource; generated when empty.
                Ignored when an existing resource is reused. Mirrors :meth:`append_resource`.
            title (str, optional): Title for the created resource.
            description (str, optional): Description for the created resource.
            props (list, optional): Property dicts for the created resource; ``version``
                (below) is appended to these. Mirrors :meth:`append_resource`.
            version (str, optional): Convenience — appended to ``props`` as a ``prop``
                named ``"version"`` (resources have no native version field).
            remarks (str, optional): Remarks (markdown) for the created resource.
            include_all (bool, optional): Keyword-only, profile-only. When True a new
                profile import selects all controls via ``include-all`` instead of the
                default empty ``include-controls``/``with-ids`` placeholder. Ignored by
                models whose imports carry no selection. Defaults to False.

        Returns:
            ImportResult: ``status`` of "added", "replaced", "duplicate", "invalid",
                or "error", with the relevant ``entry`` and ``resource``.

def append_child(self, path: 'str', child: 'dict') -> 'dict | None'

Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.

def append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'

Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.

def dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'

Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.
        Output keys/elements are emitted in canonical metaschema order (see :meth:`dumps`).

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.

def dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'

Serialize the current content to a string in the specified format.

        Keys/elements are emitted in canonical NIST metaschema order: XML element
        order is schema-required and always canonical; JSON/YAML key order is
        canonical on a best-effort basis (see :meth:`_ordered_dict`).

        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.

property duplicate_imports

Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.

property failed_imports

Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.

def find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'

Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id), ``party`` (by uuid), ``location`` (by uuid), or ``responsible-party`` (by
        role-id), or a ``control``/``group``/``param``/``part`` (by id). This walks
        ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.

classmethod from_string(cls, content: 'str', *, href: 'str | None' = None)

Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).

def get_location_by_uuid(self, location_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``location`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.locations`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a location with the given uuid
        is found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_oscal_object(self, uuid, _seen=None)

Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.

def get_parameter_by_id(self, param_id: 'str', with_source: 'bool' = False) -> 'Optional[dict]'

Return a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content. See :meth:`_lookup_in_scope` for the ``with_source`` locator form.

def get_party_by_uuid(self, party_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``party`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a party with the given uuid is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_resource_by_uuid(self, resource_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a back-matter resource defined anywhere in scope, or None.

        Looks in THIS document's ``back-matter`` first; on a local miss the search
        cascades out through the immediately imported documents and continues depth-first
        along every branch (de-duplicated and cycle-safe) until a ``resource`` whose
        ``uuid`` matches is found or every branch is exhausted. This lets a cross-reference
        (``href="#uuid"``) resolve even when the resource is defined in an imported
        document rather than locally. Subclasses may override to prefer resolved content.

        Args:
            resource_uuid (str, required): The bare resource UUID to resolve (no ``#``).
            with_source (bool, optional): Return the full locator (element + owning
                ``object_uuid``/``href``) instead of the bare resource; useful for
                resolving the resource's relative rlink hrefs. Defaults to False.
            local_only (bool, optional): Search only THIS document, never imports.
                Defaults to False.

        Returns:
            Optional[dict]: A safe copy of the matching resource (or locator), or None.

def get_responsible_party_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``responsible-party`` (by role-id) in scope, or None.

        A ``responsible-party`` is keyed by the ``role-id`` it fulfills. Looks in THIS
        document's ``metadata.responsible-parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until one with the given role-id is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

        Note: this targets metadata-level ``responsible-parties`` only. The
        ``responsible-role`` assemblies embedded throughout implementation/assessment
        models are model-specific and handled by a separate, later cascade.

def get_role_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``role`` (by id) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.roles`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a role with the given id is
        found or every branch is exhausted — so a ``role-id`` reference resolves even when
        the role is defined in an imported document. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def ignore_import(self, href: 'str') -> 'bool'

Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.

property import_tree

Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.

property imports_resolved

bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).

def initial_validation(self, content: 'str') -> 'bool'

Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.

property is_acquired

bool: True once content has been acquired (``content_state >= ACQUIRED``).

property is_cache_expired

True when remote cached content has exceeded its TTL.

property is_editable

Can this content be modified?

property is_fresh

True when content is local or cached and within its TTL.

property is_read_only

bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.

property is_remote

bool: True when the content originates from a remote source (not a local file).

property is_stale

True when remote cached content has exceeded its TTL.

property is_valid

bool: True when content passes OSCAL validation (``content_state >= VALID``).

property is_well_formed

bool: True when content is well-formed (``content_state >= WELL_FORMED``).

property json

Return the content as a JSON string in canonical key order.

def json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

classmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)

Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior (cf. ``json.load`` /
        ``pickle.load``): the source is a **local** path or a file-like object. Use
        ``loads(...)`` for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        sources (``http``/``https``/``file``/``ftp``/…) — ``load`` does not fetch remotely.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.

classmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)

Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.

classmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')

Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.

classmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)

Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.

property origin_state

Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.

def put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'

Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.

def query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

def reachable_ids(self, _seen=None) -> 'set'

Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.

def rebuild_import_tree(self) -> 'dict'

Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.

def remove_import(self, href: 'str') -> 'bool'

Remove a first-level import statement from this document.

        Operates only on this document's own imports, never on descendants. The
        import *statement* is deleted from ``self._dict``; any back-matter resource
        it referenced via a URI fragment (``href="#uuid"``) is intentionally
        preserved. The affected part of the import tree is refreshed and any
        model-specific derived state (e.g. a Profile's resolved catalog) is reset
        via :meth:`_after_imports_changed`.

        Cardinality is enforced from :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — invalid.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR require exactly
          one) — invalid; change that import with :meth:`retry_import` instead.
        * Otherwise the removal is rejected when it would drop the count of *real*
          imports (non-empty, non-``"#"`` href) below the model's minimum. Removing
          an empty placeholder is always allowed for variable-cardinality models.

        Args:
            href: Any href that identifies the import — its literal href (including
                a ``"#uuid"`` fragment or an empty ``""``/``"#"`` placeholder), or
                the resolved target href of the back-matter resource it references.

        Returns:
            True if an import was found and removed; False if not found, the
            cardinality forbids removal, or the content is read-only.

def resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'

Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-ap/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.

def retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def set_metadata(self, content: 'dict' = {}) -> 'bool'

Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.

def supports(self, name: 'str') -> 'bool'

Return True if this model exposes ``name`` as a method or attribute.

        Lets callers look before they leap when an operation is only valid for
        some OSCAL models::

            if doc.supports("add_control"):
                doc.add_control(...)

        Only class-level (shared, model-defined) members count; per-instance
        attributes set at runtime are ignored, so the check reflects the model's
        capabilities rather than incidental state.

        Args:
            name (str, required): The method/attribute name to test.

        Returns:
            bool: True if the model defines ``name``.

property unresolved_imports

Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.

def update_import(self, *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None, new_resource: 'bool' = True) -> 'ImportResult'

Modify the single import of a one-import model (SSP, AP, AR, or POA&M).

        These models carry exactly one import (POA&M: at most one), so :meth:`add_import`
        and :meth:`remove_import` do not apply — this is how their import is changed. It
        takes the same resource fields as :meth:`update_resource` (``title``,
        ``description``, ``props``, ``rlinks``, ``remarks`` — ``None`` leaves a field
        unchanged; arrays replace wholesale) plus ``new_resource``. The behavior depends
        on what the existing import points at:

        * **No import yet** (only possible for POA&M): the call is forwarded to
          :meth:`add_import` (``new_resource`` does not apply and is omitted); the import
          target's href is taken from the first supplied ``rlink``.
        * **Import is a direct URI** (or an empty ``""``/``"#"`` placeholder): a new
          back-matter resource is created (via :meth:`append_resource`) — its ``rlink`` is
          the supplied ``rlinks`` when given, otherwise the existing URI — and the import's
          href is repointed to that resource's ``#uuid``. (``new_resource`` does not apply:
          there is no backing resource to update.) Status: "replaced".
        * **Import is a ``#uuid`` fragment**:

          - ``new_resource=True`` (default): a brand-new resource with a new UUID is
            created via :meth:`append_resource` from the supplied fields, and the import's
            href is repointed to it. The prior resource is **left in place** — it is not
            deleted, because other content may reference it (including OSCAL documents not
            currently loaded that import this one and cite that resource by UUID). Because
            the new resource is built only from the fields you pass, supply ``rlinks`` (and
            any props) for the new target; read the old resource first with
            :meth:`get_resource_by_uuid` if you want to carry values forward. Status:
            "replaced".
          - ``new_resource=False``: the existing resource is edited in place via
            :meth:`update_resource` (same wholesale array-replacement semantics and
            data-loss caveats — see that method). The import's href is unchanged. Status:
            "updated".

        Args:
            title (str | None, optional): Resource title; ``""`` removes it.
            description (str | None, optional): Resource description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). Also the source of the import target's
                href when creating a resource or bootstrapping a POA&M import.
            remarks (str | None, optional): Resource remarks (markdown); ``""`` removes it.
            new_resource (bool, optional): When the import already references a ``#uuid``
                resource, True (default) creates a new resource and repoints; False edits
                the existing resource in place. Ignored for the direct-URI/placeholder and
                no-import cases. Defaults to True.

        Returns:
            ImportResult: ``status`` "added"/"replaced"/"updated" on success (``ok`` True),
                or "invalid"/"error" otherwise, carrying the import ``entry`` and the
                created/updated ``resource``.

def update_resource(self, uuid: 'str', *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None) -> 'dict | None'

Update fields of an existing ``back-matter`` resource, selected by ``uuid``.

        Only a resource defined in THIS document's own ``back-matter`` is editable
        (imported resources are not). Each field is optional and independent:

        * ``None`` (the default) leaves that field untouched.
        * A scalar (``title``/``description``/``remarks``) replaces the current value;
          an empty string removes the field entirely.
        * An array (``props``/``rlinks``) **replaces the existing array wholesale** — the
          old list is discarded and the supplied list becomes the new one. Passing an
          empty list removes the field.

        .. warning::
            Array replacement is destructive and total, not a merge. Whatever you pass
            for ``props`` or ``rlinks`` becomes the *complete* new list; every entry not
            present in your list is permanently dropped. A resource frequently carries
            entries you did not author and may not be aware of — for example multiple
            ``rlinks`` pointing at format variants (``.xml``/``.json``) of the same file,
            ``rlinks`` bearing ``hashes`` for integrity, a ``base64`` payload, or ``props``
            added by other tools or pipelines. Supplying a partial list here silently
            deletes all of those. There is no undo.

            **Recommended pattern:** read the current resource first with
            :meth:`get_resource_by_uuid`, mutate the copy it returns (append to / edit the
            existing ``props``/``rlinks`` rather than rebuilding them from scratch), then
            pass those full arrays back to this method. That way you extend the resource
            instead of overwriting it, and nothing you did not intend to touch is lost::

                res = doc.get_resource_by_uuid(uuid)          # safe copy of the whole resource
                res["rlinks"].append({"href": "catalog.json",
                                      "media-type": "application/json"})
                doc.update_resource(uuid, rlinks=res["rlinks"])   # full list, nothing dropped

        Args:
            uuid (str, required): UUID of the local back-matter resource to update.
            title (str | None, optional): New title; ``""`` removes it. ``None`` = unchanged.
            description (str | None, optional): New description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts (see
                :func:`append_props`). ``[]`` removes all props. ``None`` = unchanged.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). ``[]`` removes all rlinks.
            remarks (str | None, optional): New remarks (markdown); ``""`` removes them.

        Returns:
            dict | None: A safe copy of the updated resource, or None when the content is
                read-only, ``uuid`` is empty, or no local resource with that UUID exists.

def validate(self, format: 'str' = '') -> 'bool'

Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).

def walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')

Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None

property xml

Return the content as an XML string in canonical element order.

        Rebuilds from the current dict via the metaschema converter (reflecting
        the latest edits, in schema-required element order) and retains no tree.

property yaml

Return the content as a YAML string in canonical key order.

class MergeStrategy

How a Profile reconstructs control hierarchy under an ``as-is`` merge directive.

    This is **not** the OSCAL merge directive. The directive itself — ``as-is``,
    ``flat``, or ``custom`` — is authored on the profile's ``merge`` element and is
    surfaced by :attr:`Profile.merge_directive`. ``MergeStrategy`` is a separate,
    library-only processing option (not serialized) that refines a *single* aspect of
    resolution and **applies only when that directive is ``as-is``**. Under a ``flat``
    or ``custom`` directive it is inert — flat has no nesting to reconstruct, and custom
    defines its own structure — so setting it then has no effect.

    Members:
        REFERENTIAL (str): "referential" — the primary/default logic. Reconstructs
            control containment from source provenance so a selected enhancement
            nests under its selected parent (e.g. ``ac-3.14`` under ``ac-3``) even
            when an intermediate profile selected the child without its parent. The
            NIST profile-resolution spec is silent on this; REFERENTIAL honors the
            hierarchy defined at the end of each import branch (the source catalog).
        POSITIONAL (str): "positional" — the secondary/legacy logic. Preserves each
            immediate import's presented structure; a child selected without its
            parent is promoted to its group and remains a peer of its parent.

class Profile

Editable OSCAL Profile model with tree-driven, lazy resolution.

    A profile selects and tailors controls from one or more imported catalogs/profiles.
    Resolution is split into a cheap load-time step and an on-demand heavy step:

    * **controls_tree (source of truth).** On load — and after any import/directive
      change — :meth:`_build_controls_tree` reads each imported object's own
      ``controls_tree`` and applies the profile's directives to produce this profile's
      ``controls_tree``: the authoritative scope and organization. It is lightweight
      (ids + hierarchy + an ``origin`` back to each node's immediate source); no control
      content is copied. Directives applied here: ``import``/``include``/``exclude``
      selection, ``merge`` (``as-is`` or ``flat``; ``custom`` is deferred and falls back
      to ``as-is``), and ``combine`` duplicate handling (``keep`` renames the node id,
      ``use-first`` drops later duplicates). When an ``as-is`` merge would place controls
      and groups together at the root, root controls are wrapped in a synthetic
      "ROOT CONTROLS" group.

    * **resolve() (heavy, cacheable).** :meth:`resolve` walks the tree and materializes a
      brand-new ``Catalog`` in :attr:`catalog`, fetching real content per node, applying
      ``modify`` directives (removes → adds → set-parameters) and full internal id
      renaming for duplicates, hoisting externally-defined cited parameters to the root,
      carrying forward referenced back-matter resources, and rewriting out-of-scope
      references to their source URIs. ``catalog`` is ``None`` until ``resolve`` is called.

    * **Read-only Catalog surface.** :meth:`get_control_by_id`, :meth:`get_group_by_id`,
      :meth:`get_control_list`, and :meth:`get_parameter_by_id` return safe copies from
      :attr:`catalog` when resolved, or materialize them on demand from the source
      (through the same code path, so the two agree) when unresolved. Content is changed
      via the profile's own directive methods and re-resolved, not by editing returned
      copies.

    * **Serialization.** :meth:`dumps_catalog`/:meth:`dump_catalog` serialize the resolved
      catalog to a string / file; :meth:`resolve_and_dumps_catalog`/
      :meth:`resolve_and_dump_catalog` run the full resolve-then-serialize in one call.

    Key attributes:
        catalog (Catalog | None): The resolved catalog, or None until :meth:`resolve`.
        controls_tree (list[dict]): Scope/organization nodes
            ``{id, label, title, group, origin, children}``.
        duplicates (dict): Controls/groups renamed or dropped by ``combine``, keyed by
            original id (see :meth:`_record_duplicate`).
        resolution_status (ResolutionStatus): UNRESOLVED / RESOLVING / RESOLVED / BLOCKED.

classmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)

Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.

def add_alter(self, control_id: str) -> Optional[dict]

Ensure a ``modify.alters`` entry exists for *control_id* and return a safe copy.

        Idempotent: creates the (initially empty) alter when absent, otherwise returns the
        existing one unchanged. The document is only marked unsaved when an alter is
        actually created. Used by :meth:`add_alter_adds` / :meth:`add_alter_removes`.

        Args:
            control_id (str, required): The control the alter targets.

        Returns:
            Optional[dict]: A safe copy of the alter, or None (read-only, or blank id).

def add_alter_adds(self, control_id: str, position: Optional[str] = None, by_id: Optional[str] = None, title: Optional[str] = None, params: Optional[list] = None, props: Optional[list] = None, links: Optional[list] = None, parts: Optional[list] = None) -> Optional[dict]

Add an ``adds`` (addition) to *control_id*'s alter, creating the alter if needed.

        The addition is built from the supplied fields, pruned/validated through the
        metaschema staging gate, then appended. At least one content field
        (``title``/``params``/``props``/``links``/``parts``) must be supplied. The alter is
        only created once the addition validates, so a rejected call leaves nothing behind.

        Args:
            control_id (str, required): The control the alter targets.
            position (str, optional): ``before``/``after``/``starting``/``ending``.
            by_id (str, optional): The id the addition is positioned relative to.
            title (str, optional): A title to add.
            params (list, optional): Parameter dicts to add.
            props (list, optional): Property dicts to add.
            links (list, optional): Link dicts to add.
            parts (list, optional): Part dicts to add.

        Returns:
            Optional[dict]: A safe copy of the addition, or None on failure.

def add_alter_removes(self, control_id: str, by_name: Optional[str] = None, by_class: Optional[str] = None, by_id: Optional[str] = None, by_item_name: Optional[str] = None, by_ns: Optional[str] = None) -> Optional[dict]

Add a ``removes`` (removal) to *control_id*'s alter, creating the alter if needed.

        The removal is built from the supplied ``by-*`` selectors (at least one required),
        validated through the metaschema staging gate, then appended. The alter is only
        created once the removal validates, so a rejected call leaves nothing behind.

        Args:
            control_id (str, required): The control the alter targets.
            by_name (str, optional): Match the named flag/field/part to remove.
            by_class (str, optional): Match by ``class``.
            by_id (str, optional): Match by ``id``.
            by_item_name (str, optional): Match by item (element) name.
            by_ns (str, optional): Match by namespace.

        Returns:
            Optional[dict]: A safe copy of the removal, or None on failure.

def add_import(self, href: 'str', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], version: 'str' = '', remarks: 'str' = '', *, include_all: 'bool' = False) -> 'ImportResult'

Add a first-level import to this document, backed by a back-matter resource.

        Uniform across every model; legality is governed by :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — ``invalid``.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR) — ``invalid``; set
          their single import with :meth:`retry_import` instead of add/remove.
        * Otherwise an import is added while the count of *real* imports is below the
          model's ``max`` (unbounded when ``max`` is None).

        The import references a back-matter ``resource`` by UUID fragment
        (``href="#<uuid>"``): if a resource whose ``rlink`` already targets ``href``
        exists it is reused, otherwise one is created via :meth:`append_resource` with
        which this method shares its resource parameters (``uuid``/``title``/
        ``description``/``props``/``remarks``). The ``href`` becomes the resource's
        single ``rlink`` (with a best-effort ``media-type`` inferred from it), and
        ``version`` is appended to ``props`` as a ``prop`` named ``"version"``. An empty
        placeholder import (href ``""``/``"#"``) is filled in place; otherwise the entry
        is appended (list models) or set (single-import models). After placement the
        import tree and any derived state are refreshed via :meth:`_after_imports_changed`.

        Args:
            href (str, required): Reference to the imported OSCAL file (XML/JSON/YAML);
                becomes the created resource's ``rlink`` href.
            uuid (str, optional): UUID for the created resource; generated when empty.
                Ignored when an existing resource is reused. Mirrors :meth:`append_resource`.
            title (str, optional): Title for the created resource.
            description (str, optional): Description for the created resource.
            props (list, optional): Property dicts for the created resource; ``version``
                (below) is appended to these. Mirrors :meth:`append_resource`.
            version (str, optional): Convenience — appended to ``props`` as a ``prop``
                named ``"version"`` (resources have no native version field).
            remarks (str, optional): Remarks (markdown) for the created resource.
            include_all (bool, optional): Keyword-only, profile-only. When True a new
                profile import selects all controls via ``include-all`` instead of the
                default empty ``include-controls``/``with-ids`` placeholder. Ignored by
                models whose imports carry no selection. Defaults to False.

        Returns:
            ImportResult: ``status`` of "added", "replaced", "duplicate", "invalid",
                or "error", with the relevant ``entry`` and ``resource``.

def append_child(self, path: 'str', child: 'dict') -> 'dict | None'

Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.

def append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'

Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.

def control(self, control_id: str, with_history: bool = False, depth: Optional[int] = None) -> Optional[dict]

Retrieve a control by its ID, as a safe copy (thin alias of
        :meth:`get_control_by_id`).

        Works whether or not the profile is resolved: resolved fetches from
        ``self.catalog``; unresolved materializes on demand from the source via the
        profile's controls_tree.

        Args:
            control_id (str, required): The ``id`` of the control to retrieve.
            with_history (bool, optional): Reserved for including tailoring history.
                Defaults to False.
            depth (int | None, optional): Nested-enhancement depth. ``None`` (default)
                returns the full subtree; ``0`` omits enhancements; ``N`` keeps N levels.

        Returns:
            Optional[dict]: A safe copy of the control, or None if not found.

def dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'

Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.
        Output keys/elements are emitted in canonical metaschema order (see :meth:`dumps`).

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.

def dump_catalog(self, filename: str = '', format: str = '', pretty_print: bool = False) -> bool

Write the resolved catalog to a file — wrapper over ``self.catalog.dump``.

        Requires the profile to be resolved (``self.catalog`` is None until
        :meth:`resolve`); returns False with a warning otherwise.

        Args:
            filename (str, optional): Path to write to; defaults to the catalog's original
                location when empty.
            format (str, optional): Output format ("xml", "json", "yaml"); defaults to the
                catalog's original format when empty.
            pretty_print (bool, optional): Whether to pretty-print. Defaults to False.

        Returns:
            bool: True on success, False when unresolved or the write fails.

def dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'

Serialize the current content to a string in the specified format.

        Keys/elements are emitted in canonical NIST metaschema order: XML element
        order is schema-required and always canonical; JSON/YAML key order is
        canonical on a best-effort basis (see :meth:`_ordered_dict`).

        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.

def dumps_catalog(self, format: str = '', pretty_print: bool = False) -> str

Serialize the resolved catalog to a string — wrapper over ``self.catalog.dumps``.

        Requires the profile to be resolved (``self.catalog`` is None until
        :meth:`resolve`); returns ``""`` with a warning otherwise.

        Args:
            format (str, optional): Target format ("xml", "json", "yaml"); defaults to the
                catalog's original format.
            pretty_print (bool, optional): Whether to pretty-print. Defaults to False.

        Returns:
            str: The serialized resolved catalog, or ``""`` when unresolved.

property duplicate_imports

Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.

property failed_imports

Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.

def find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'

Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id), ``party`` (by uuid), ``location`` (by uuid), or ``responsible-party`` (by
        role-id), or a ``control``/``group``/``param``/``part`` (by id). This walks
        ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.

classmethod from_string(cls, content: 'str', *, href: 'str | None' = None)

Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).

def get_alter(self, control_id: str) -> Optional[dict]

Return a safe copy of the ``modify.alters`` entry for *control_id*.

        Args:
            control_id (str, required): The ``control-id`` of the alter to fetch.

        Returns:
            Optional[dict]: A deep copy of the alter (with its ``adds``/``removes``), or
                None if no alter targets *control_id*.

def get_control_by_id(self, control_id: str, depth: Optional[int] = None) -> Optional[dict]

Retrieve a control as a safe copy — from the resolved catalog if resolved,
        otherwise materialized on demand from the source via the profile's controls_tree.

        Both paths return the same shape (the control with its in-scope enhancements
        nested per ``depth``, this profile's ``modify`` directives applied). When
        unresolved, the control is materialized from its origin source on each call.

        Args:
            control_id (str, required): The control id (as it appears in this profile's
                scope — a duplicate's suffixed id is valid).
            depth (int | None, optional): Enhancement depth (``None`` full).

        Returns:
            Optional[dict]: A safe copy of the control, or None when absent.

def get_control_list(self) -> list

Return safe copies of every in-scope control, at all levels.

        Resolved: pass-through to :meth:`Catalog.get_control_list`. Unresolved: each
        control node in the profile's controls_tree is materialized standalone (depth 0),
        mirroring the flat, enhancement-inclusive list a catalog returns.

def get_directives(self) -> dict

Return the profile's merge directives as a simple, normalized dict.

        A library-level view of the ``merge`` element that does not pass the raw OSCAL
        through. Always includes:

        * ``combine`` — ``"use-first"`` when that method is set; ``"keep"`` when ``keep``
          is set *or* no ``combine`` is present (the effective default); ``"invalid"`` for
          any other stored method (e.g. the OSCAL ``merge`` method, which this library does
          not model).
        * ``hierarchy`` — ``"flat"`` / ``"as-is"`` / ``"custom"`` from whichever directive
          is present (an ``as-is`` directive maps to ``"as-is"`` regardless of its boolean
          value). Defaults to ``"as-is"`` — OSCAL's default organization — when no
          directive (or no ``merge``) is present.

        And, only when ``hierarchy == "custom"``:

        * ``custom`` — a safe copy of the ``custom`` object (with its ``groups`` /
          ``insert-controls`` children).

        Returns:
            dict: ``{"combine": ..., "hierarchy": ...[, "custom": {...}]}``.

def get_group_by_id(self, group_id: str, depth: Optional[int] = None) -> Optional[dict]

Retrieve a group as a safe copy — from the resolved catalog if resolved,
        otherwise materialized on demand from the source via the profile's controls_tree.

        Args:
            group_id (str, required): The group id (as it appears in this profile's scope).
            depth (int | None, optional): Nested group/control depth (``None`` full).

        Returns:
            Optional[dict]: A safe copy of the group, or None when absent.

def get_import_selection(self, href: str) -> Optional[dict]

Return an import statement's control-selection structures as a safe copy.

        Fetches the ``include-all``, ``include-controls``, and ``exclude-controls``
        structures for the import identified by *href* (any tracked href form — see
        :meth:`_locate_import_statement`). Only the keys actually present are returned, so
        an import that selects via ``include-controls`` yields no ``include-all`` key.

        Args:
            href (str, required): Any href that identifies the import statement.

        Returns:
            Optional[dict]: A deep-copied dict containing whichever of ``include-all`` /
                ``include-controls`` / ``exclude-controls`` are present (``{}`` when the
                import carries none), or None when no import matches *href*.

def get_location_by_uuid(self, location_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``location`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.locations`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a location with the given uuid
        is found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_oscal_object(self, uuid, _seen=None)

Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.

def get_parameter_by_id(self, param_id: str) -> Optional[dict]

Return a parameter as a safe copy — from the resolved catalog if resolved,
        otherwise located in the import tree (its source, unmutated).

def get_party_by_uuid(self, party_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``party`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a party with the given uuid is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_resource_by_uuid(self, resource_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a back-matter resource defined anywhere in scope, or None.

        Looks in THIS document's ``back-matter`` first; on a local miss the search
        cascades out through the immediately imported documents and continues depth-first
        along every branch (de-duplicated and cycle-safe) until a ``resource`` whose
        ``uuid`` matches is found or every branch is exhausted. This lets a cross-reference
        (``href="#uuid"``) resolve even when the resource is defined in an imported
        document rather than locally. Subclasses may override to prefer resolved content.

        Args:
            resource_uuid (str, required): The bare resource UUID to resolve (no ``#``).
            with_source (bool, optional): Return the full locator (element + owning
                ``object_uuid``/``href``) instead of the bare resource; useful for
                resolving the resource's relative rlink hrefs. Defaults to False.
            local_only (bool, optional): Search only THIS document, never imports.
                Defaults to False.

        Returns:
            Optional[dict]: A safe copy of the matching resource (or locator), or None.

def get_responsible_party_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``responsible-party`` (by role-id) in scope, or None.

        A ``responsible-party`` is keyed by the ``role-id`` it fulfills. Looks in THIS
        document's ``metadata.responsible-parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until one with the given role-id is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

        Note: this targets metadata-level ``responsible-parties`` only. The
        ``responsible-role`` assemblies embedded throughout implementation/assessment
        models are model-specific and handled by a separate, later cascade.

def get_role_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``role`` (by id) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.roles`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a role with the given id is
        found or every branch is exhausted — so a ``role-id`` reference resolves even when
        the role is defined in an imported document. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_set_parameter(self, param_id: str) -> Optional[dict]

Return a safe copy of the ``modify.set-parameters`` entry for *param_id*.

        Args:
            param_id (str, required): The ``param-id`` of the set-parameter to fetch.

        Returns:
            Optional[dict]: A deep copy of the set-parameter, or None if none matches.

def ignore_import(self, href: 'str') -> 'bool'

Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.

property import_tree

Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.

property imports_resolved

bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).

def initial_validation(self, content: 'str') -> 'bool'

Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.

property is_acquired

bool: True once content has been acquired (``content_state >= ACQUIRED``).

property is_cache_expired

True when remote cached content has exceeded its TTL.

property is_editable

Can this content be modified?

property is_fresh

True when content is local or cached and within its TTL.

property is_read_only

bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.

property is_remote

bool: True when the content originates from a remote source (not a local file).

property is_stale

True when remote cached content has exceeded its TTL.

property is_valid

bool: True when content passes OSCAL validation (``content_state >= VALID``).

property is_well_formed

bool: True when content is well-formed (``content_state >= WELL_FORMED``).

property json

Return the content as a JSON string in canonical key order.

def json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

classmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)

Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior (cf. ``json.load`` /
        ``pickle.load``): the source is a **local** path or a file-like object. Use
        ``loads(...)`` for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        sources (``http``/``https``/``file``/``ftp``/…) — ``load`` does not fetch remotely.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.

classmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)

Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.

property merge_strategy

The control-tree assembly strategy (:class:`MergeStrategy`).

        Defaults to ``REFERENTIAL`` (reconstruct source hierarchy). Assigning a new
        value marks the controls_tree stale, drops any resolved catalog, and rebuilds
        the tree so subsequent reads reflect the chosen strategy.

classmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')

Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.

classmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)

Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.

property origin_state

Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.

def put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'

Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.

def query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

def reachable_ids(self, _seen=None) -> 'set'

Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.

def rebuild_import_tree(self) -> 'dict'

Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.

def remove_alter_adds(self, control_id: str, by_id: str, position: Optional[str] = None) -> bool

Remove ``adds`` from *control_id*'s alter that match the given selectors.

        Scope is the alter for *control_id*. ``by_id`` is required; ``position`` is
        optional. Every addition whose supplied selectors **all** match is removed (so
        ``by_id`` alone removes all additions with that ``by-id`` regardless of position;
        adding ``position`` narrows it). An emptied ``adds`` array, an alter left with no
        ``adds``/``removes``, an emptied ``alters`` array, and an emptied ``modify`` are all
        pruned.

        Args:
            control_id (str, required): The control whose alter is edited.
            by_id (str, required): Match additions with this ``by-id``.
            position (str, optional): Also require this ``position``.

        Returns:
            bool: True if at least one addition was removed, else False.

def remove_alter_removes(self, control_id: str, by_name: Optional[str] = None, by_class: Optional[str] = None, by_id: Optional[str] = None, by_item_name: Optional[str] = None, by_ns: Optional[str] = None) -> bool

Remove ``removes`` from *control_id*'s alter that match the given selectors.

        Scope is the alter for *control_id*. Every removal whose **supplied** ``by-*``
        selectors all match is deleted (unspecified selectors are ignored). With no
        selector supplied, every removal for the control is deleted. An emptied ``removes``
        array, an alter left with no ``adds``/``removes``, an emptied ``alters`` array, and
        an emptied ``modify`` are all pruned.

        Args:
            control_id (str, required): The control whose alter is edited.
            by_name, by_class, by_id, by_item_name, by_ns (str, optional): Selectors that
                must match (all supplied ones) for a removal to be deleted.

        Returns:
            bool: True if at least one removal was removed, else False.

def remove_import(self, href: 'str') -> 'bool'

Remove a first-level import statement from this document.

        Operates only on this document's own imports, never on descendants. The
        import *statement* is deleted from ``self._dict``; any back-matter resource
        it referenced via a URI fragment (``href="#uuid"``) is intentionally
        preserved. The affected part of the import tree is refreshed and any
        model-specific derived state (e.g. a Profile's resolved catalog) is reset
        via :meth:`_after_imports_changed`.

        Cardinality is enforced from :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — invalid.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR require exactly
          one) — invalid; change that import with :meth:`retry_import` instead.
        * Otherwise the removal is rejected when it would drop the count of *real*
          imports (non-empty, non-``"#"`` href) below the model's minimum. Removing
          an empty placeholder is always allowed for variable-cardinality models.

        Args:
            href: Any href that identifies the import — its literal href (including
                a ``"#uuid"`` fragment or an empty ``""``/``"#"`` placeholder), or
                the resolved target href of the back-matter resource it references.

        Returns:
            True if an import was found and removed; False if not found, the
            cardinality forbids removal, or the content is read-only.

def remove_set_parameter(self, param_id: str) -> bool

Remove the ``modify.set-parameters`` entry for *param_id*.

        Prunes an emptied ``set-parameters`` array and an emptied ``modify`` object.

        Args:
            param_id (str, required): The ``param-id`` of the set-parameter to remove.

        Returns:
            bool: True if a set-parameter was removed, else False (no match, blank
                ``param_id``, or read-only content).

def resolve(self) -> 'ResolutionStatus'

Materialize the profile's controls_tree into a fresh ``self.catalog``.

        Resolution is the cacheable heavy step: it walks the profile's
        controls_tree — the authoritative scope/organization built at load — and for each
        node fetches the real control/group content from its origin source, applies this
        profile's ``modify`` directives (removes → adds → set-parameters), applies full
        internal ``__<uuid>`` id renaming for duplicates, then inserts it into a brand-new
        ``Catalog``. Any previously resolved catalog is discarded and replaced. After
        placement it: hoists cited-but-externally-defined parameters to the catalog root
        (:meth:`_insert_shared_params`), assembles metadata, carries forward referenced
        back-matter resources (:meth:`_carry_backmatter`), rewrites references to
        out-of-scope ids to their source URIs (:meth:`_rewrite_out_of_scope_refs`), and
        validates.

        Because content is fetched through each source's own getters, imported *profiles*
        need not be pre-resolved — their load-time controls_tree and lazy getters suffice.

        Returns:
            ResolutionStatus: ``RESOLVED`` on success, or ``BLOCKED`` when content is
                missing or an import could not be resolved.

def resolve_and_dump_catalog(self, filename: str = '', format: str = '', pretty_print: bool = False) -> bool

Resolve the profile, then write the resolved catalog to a file.

        Convenience for the common one-shot case: it runs the (potentially expensive)
        :meth:`resolve` and then :meth:`dump_catalog`. For finer control — e.g. to resolve
        once and write several outputs, or to manage *when* the heavy resolution runs — call
        :meth:`resolve` and :meth:`dump_catalog` separately instead.

        Note: each call performs a full resolution (rebuilds ``self.catalog`` from scratch).

        Args:
            filename (str, optional): Path to write to; defaults to the catalog's original
                location when empty.
            format (str, optional): Output format ("xml", "json", "yaml").
            pretty_print (bool, optional): Whether to pretty-print. Defaults to False.

        Returns:
            bool: True on success, False if resolution was blocked or the write failed.

def resolve_and_dumps_catalog(self, format: str = '', pretty_print: bool = False) -> str

Resolve the profile, then serialize the resolved catalog to a string.

        Convenience for the common one-shot case: it runs the (potentially expensive)
        :meth:`resolve` and then :meth:`dumps_catalog`. For finer control — e.g. to resolve
        once and serialize many times, or to manage *when* the heavy resolution runs — call
        :meth:`resolve` and :meth:`dumps_catalog` separately instead.

        Note: each call performs a full resolution (rebuilds ``self.catalog`` from scratch).

        Args:
            format (str, optional): Target format ("xml", "json", "yaml").
            pretty_print (bool, optional): Whether to pretty-print. Defaults to False.

        Returns:
            str: The serialized resolved catalog, or ``""`` if resolution was blocked.

def resolve_duplicate(self, control_id: str, keep: Optional[str] = None, parent_id: Optional[str] = None, replacement: Optional[dict] = None) -> Optional[dict]

Manually collapse duplicate instances of a control in the resolved catalog.

        Requires a resolved catalog. Merging logic is out of scope (handled by the
        caller, e.g. a GUI); this keeps, relocates, or wholesale-replaces:

          * ``replacement`` given — remove every live variant (the original id and each
            tracked ``new_id``) and insert ``replacement`` (validated) under ``parent_id``
            (or the original variant's current parent).
          * ``keep`` given (or defaulted to ``control_id``) — remove every variant except
            ``keep``; if ``parent_id`` is given, relocate the kept control there.

        Args:
            control_id (str, required): The ORIGINAL (unsuffixed) control id.
            keep (str, optional): Which variant id to retain. Defaults to ``control_id``.
            parent_id (str, optional): Where to place the survivor. Defaults to in place.
            replacement (dict, optional): A full control dict superseding all variants.

        Returns:
            Optional[dict]: A safe copy of the surviving control, or None on failure.

def resolve_duplicate_group(self, group_id: str, keep: Optional[str] = None, parent_id: Optional[str] = None, replacement: Optional[dict] = None) -> Optional[dict]

Manually collapse duplicate instances of a group in the resolved catalog.

        The group analogue of :meth:`resolve_duplicate`. NOTE: removing a group removes
        its contained controls too (cascade), so prefer resolving duplicate *controls*
        first when both are tracked.

        Args:
            group_id (str, required): The ORIGINAL (unsuffixed) group id.
            keep (str, optional): Which variant group id to retain. Defaults to ``group_id``.
            parent_id (str, optional): Where to place the survivor. Defaults to in place.
            replacement (dict, optional): A full group dict superseding all variants.

        Returns:
            Optional[dict]: A safe copy of the surviving group, or None on failure.

def resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'

Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-ap/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.

def retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def set_directives(self, combine: Optional[str] = None, hierarchy: Optional[str] = None, custom: Optional[dict] = None) -> bool

Set the profile's merge directives without raw pass-through to OSCAL.

        Edits ``combine`` and/or the hierarchy directive independently; at least one of
        ``combine`` / ``hierarchy`` must be given. Changes are assembled in a **staging**
        copy and committed only when the whole result is valid — a rejected call changes
        nothing (roll back). The counterpart reader is :meth:`get_directives`.

        Args:
            combine (str, optional): ``"use-first"`` or ``"keep"``. Replaces
                ``merge.combine.method`` with that value. Any other value is rejected.
            hierarchy (str, optional): ``"flat"``, ``"as-is"``, or ``"custom"``.

                * ``"flat"`` — remove any ``as-is`` / ``custom``; add ``flat`` if absent.
                * ``"as-is"`` — remove any ``flat`` / ``custom``; set ``as-is`` to ``true``.
                * ``"custom"`` — requires the ``custom`` argument (below); remove any
                  ``as-is`` / ``flat``; set ``custom`` to the provided object.
            custom (dict, optional): Required when ``hierarchy == "custom"`` (ignored
                otherwise). Must be a dict following the OSCAL ``merge.custom`` syntax; it
                is pruned/validated through the shared metaschema staging gate
                (:meth:`_stage_against_index`). If it is not valid the hierarchy is left
                unchanged and the method returns ``False``.

        Returns:
            bool: ``True`` on success; ``False`` on any failure (read-only content, no
                directive supplied, an invalid ``combine``/``hierarchy`` value, a missing
                or invalid ``custom``, or a result with no hierarchy directive).

def set_import_selection(self, href: str, include_all: Optional[dict] = None, include_controls: Optional[list] = None, exclude_controls: Optional[list] = None) -> Optional[dict]

Replace an import statement's control-selection structures.

        Wholesale-replaces the ``include-all`` / ``include-controls`` / ``exclude-controls``
        of the import identified by *href* (any tracked href form). The resulting selection
        is exactly what is supplied: an argument left as ``None`` is *omitted* from the
        rewritten import (pass ``exclude_controls=[]`` to clear an exclusion while keeping
        it present as an empty array). ``href`` and any other schema-valid keys on the
        statement are carried through unchanged.

        Supplied structures are not stored blindly. The whole candidate import is assembled
        and checked in a **staging** copy via :meth:`_stage_against_index`, and the live
        import is only overwritten once every check passes — a rejected replacement **rolls
        back** to the prior content, changing nothing. The candidate is *pruned* to exactly
        the keys and nesting the OSCAL ``import`` schema permits (the metaschema represents
        OSCAL syntax in full and is authoritative, so anything it does not permit — at any
        depth — is dropped with a warning; a wrong array/scalar shape is rejected) and
        validated for *required* content and correctness: exactly one of ``include-all`` /
        ``include-controls`` must be present (the mutually-exclusive OSCAL choice —
        supplying neither fails), and datatypes, allowed values, required fields, and
        cardinality are enforced. On success the ``controls_tree`` is rebuilt and any
        resolved catalog is dropped, since scope has changed.

        Args:
            href (str, required): Any href that identifies the import statement.
            include_all (dict, optional): The ``include-all`` object (typically ``{}``).
                Mutually exclusive with ``include_controls``.
            include_controls (list, optional): A list of ``select-control-by-id`` dicts
                (e.g. ``[{"with-ids": ["ac-1"]}]``). Mutually exclusive with
                ``include_all``.
            exclude_controls (list, optional): A list of ``select-control-by-id`` dicts to
                exclude.

        Returns:
            Optional[dict]: A safe copy of the rewritten import statement, or None on
                failure — no matching import, a wrong argument type, or a replacement that
                fails metaschema validation (including supplying both/neither include form).

def set_merge(self, flat: bool = False, as_is: Optional[bool] = None, custom: Optional[dict] = None, combine: Optional[str] = None) -> Optional[dict]

Set the profile's ``merge`` directives (``combine`` plus flat/as-is/custom).

        .. deprecated::
            Prefer :meth:`set_directives` (paired with :meth:`get_directives`), which
            edits ``combine`` and the hierarchy directive independently and validates
            ``custom`` through the shared metaschema staging gate. This method writes the
            whole ``merge`` element as a direct pass-through and is retained for
            compatibility.

        The ``merge`` assembly instructs how imported controls are organized after
        profile resolution. Exactly one of ``flat``, ``as_is``, or ``custom`` must be
        chosen — they are mutually exclusive — while ``combine`` is optional and may
        accompany any of those choices.

        The ``custom`` object is accepted whole and validated against the ``custom``
        portion of the profile metaschema index; if it fails validation the profile is
        left unchanged. Fine-grained management of ``custom`` internals (its ``groups``
        and ``insert-controls``) is intentionally deferred to future methods, as custom
        merges are uncommon.

        Args:
            flat (bool, optional): When True, select the ``flat`` directive (resolved
                controls are flattened, without groups). Defaults to False.
            as_is (bool, optional): When set, select the ``as-is`` directive with this
                boolean value (True keeps the source organization). Defaults to None
                (not selected).
            custom (dict, optional): When set, select the ``custom`` directive using
                this object. Validated against the metaschema index. Defaults to None
                (not selected).
            combine (str, optional): The ``combine`` method — one of ``"use-first"``,
                ``"merge"``, or ``"keep"``. When None, no ``combine`` is written.

        Returns:
            Optional[dict]: The ``merge`` dict written to the profile, or None on
                failure — when not exactly one of flat/as-is/custom is given, an
                argument has the wrong type, ``combine`` is not a valid method, or the
                ``custom`` object fails metaschema validation.

def set_merge_directive(self, directive: str, custom: Optional[dict] = None, combine: Optional[str] = None) -> Optional[dict]

Change just the profile's merge *directive* (``as-is`` / ``flat`` / ``custom``).

        .. deprecated::
            Prefer :meth:`set_directives`, which changes the hierarchy directive (and/or
            ``combine``) independently and validates ``custom`` through the shared
            metaschema staging gate. Retained for compatibility.

        A focused convenience wrapper over :meth:`set_merge` for the common case of
        switching the organization directive without restating the other arguments. The
        existing ``combine`` method is preserved unless a new one is supplied, and when
        switching to ``custom`` the profile's existing ``custom`` object is reused unless
        one is provided. On success :attr:`merge_directive` and the ``controls_tree`` are
        refreshed (both handled by :meth:`set_merge`).

        Args:
            directive (str, required): One of ``"as-is"``, ``"flat"``, or ``"custom"``.
            custom (dict, optional): The ``custom`` object to use when
                ``directive == "custom"``. Defaults to the profile's current ``custom``
                object; required (here or already present) for a custom directive.
            combine (str, optional): A ``combine`` method to set. Defaults to the
                profile's current ``combine`` method (preserved).

        Returns:
            Optional[dict]: The ``merge`` dict written (a safe copy), or None on failure —
                an unknown ``directive``, a ``custom`` directive with no object available,
                or any failure surfaced by :meth:`set_merge`.

def set_metadata(self, content: 'dict' = {}) -> 'bool'

Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.

def set_parameter(self, param_id: str, class_: Optional[str] = None, props: Optional[list] = None, links: Optional[list] = None, label: Optional[str] = None, usage: Optional[str] = None, constraints: Optional[list] = None, guidelines: Optional[list] = None, values: Optional[list] = None, select_cardinality: Optional[str] = None, select_choices: Optional[list] = None) -> Optional[dict]

Create or update a ``modify.set-parameters`` entry (upsert, per-field merge).

        When no ``set-parameter`` with *param_id* exists one is created from the supplied
        fields. When one exists, each supplied field **overwrites** the corresponding
        content; a field left as ``None`` leaves any existing content untouched. Most
        commonly used to set a ``value`` or a ``constraint``.

        ``values`` and the ``select`` form (``select_cardinality`` / ``select_choices``)
        are the OSCAL mutually-exclusive choice: supplying both is rejected. Supplying
        ``values`` clears any existing ``select`` (and vice versa). The assembled entry is
        pruned/validated through the metaschema staging gate before it is committed, so an
        invalid input leaves the profile unchanged.

        Args:
            param_id (str, required): The ``param-id`` this setting targets.
            class_ (str, optional): The parameter ``class``.
            props (list, optional): Property dicts.
            links (list, optional): Link dicts.
            label (str, optional): Parameter label (markup-line).
            usage (str, optional): Usage prose (markup-multiline).
            constraints (list, optional): Constraint dicts.
            guidelines (list, optional): Guideline dicts.
            values (list, optional): Parameter value strings. Mutually exclusive with the
                ``select_*`` arguments.
            select_cardinality (str, optional): ``select.how-many`` (e.g. "one",
                "one-or-more").
            select_choices (list, optional): ``select.choice`` value strings.

        Returns:
            Optional[dict]: A safe copy of the written ``set-parameter``, or None on
                failure (missing/blank ``param_id``, both value and select supplied, a
                wrong argument type, or a result that fails metaschema validation).

def supports(self, name: 'str') -> 'bool'

Return True if this model exposes ``name`` as a method or attribute.

        Lets callers look before they leap when an operation is only valid for
        some OSCAL models::

            if doc.supports("add_control"):
                doc.add_control(...)

        Only class-level (shared, model-defined) members count; per-instance
        attributes set at runtime are ignored, so the check reflects the model's
        capabilities rather than incidental state.

        Args:
            name (str, required): The method/attribute name to test.

        Returns:
            bool: True if the model defines ``name``.

property unresolved_imports

Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.

def update_import(self, *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None, new_resource: 'bool' = True) -> 'ImportResult'

Modify the single import of a one-import model (SSP, AP, AR, or POA&M).

        These models carry exactly one import (POA&M: at most one), so :meth:`add_import`
        and :meth:`remove_import` do not apply — this is how their import is changed. It
        takes the same resource fields as :meth:`update_resource` (``title``,
        ``description``, ``props``, ``rlinks``, ``remarks`` — ``None`` leaves a field
        unchanged; arrays replace wholesale) plus ``new_resource``. The behavior depends
        on what the existing import points at:

        * **No import yet** (only possible for POA&M): the call is forwarded to
          :meth:`add_import` (``new_resource`` does not apply and is omitted); the import
          target's href is taken from the first supplied ``rlink``.
        * **Import is a direct URI** (or an empty ``""``/``"#"`` placeholder): a new
          back-matter resource is created (via :meth:`append_resource`) — its ``rlink`` is
          the supplied ``rlinks`` when given, otherwise the existing URI — and the import's
          href is repointed to that resource's ``#uuid``. (``new_resource`` does not apply:
          there is no backing resource to update.) Status: "replaced".
        * **Import is a ``#uuid`` fragment**:

          - ``new_resource=True`` (default): a brand-new resource with a new UUID is
            created via :meth:`append_resource` from the supplied fields, and the import's
            href is repointed to it. The prior resource is **left in place** — it is not
            deleted, because other content may reference it (including OSCAL documents not
            currently loaded that import this one and cite that resource by UUID). Because
            the new resource is built only from the fields you pass, supply ``rlinks`` (and
            any props) for the new target; read the old resource first with
            :meth:`get_resource_by_uuid` if you want to carry values forward. Status:
            "replaced".
          - ``new_resource=False``: the existing resource is edited in place via
            :meth:`update_resource` (same wholesale array-replacement semantics and
            data-loss caveats — see that method). The import's href is unchanged. Status:
            "updated".

        Args:
            title (str | None, optional): Resource title; ``""`` removes it.
            description (str | None, optional): Resource description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). Also the source of the import target's
                href when creating a resource or bootstrapping a POA&M import.
            remarks (str | None, optional): Resource remarks (markdown); ``""`` removes it.
            new_resource (bool, optional): When the import already references a ``#uuid``
                resource, True (default) creates a new resource and repoints; False edits
                the existing resource in place. Ignored for the direct-URI/placeholder and
                no-import cases. Defaults to True.

        Returns:
            ImportResult: ``status`` "added"/"replaced"/"updated" on success (``ok`` True),
                or "invalid"/"error" otherwise, carrying the import ``entry`` and the
                created/updated ``resource``.

def update_resource(self, uuid: 'str', *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None) -> 'dict | None'

Update fields of an existing ``back-matter`` resource, selected by ``uuid``.

        Only a resource defined in THIS document's own ``back-matter`` is editable
        (imported resources are not). Each field is optional and independent:

        * ``None`` (the default) leaves that field untouched.
        * A scalar (``title``/``description``/``remarks``) replaces the current value;
          an empty string removes the field entirely.
        * An array (``props``/``rlinks``) **replaces the existing array wholesale** — the
          old list is discarded and the supplied list becomes the new one. Passing an
          empty list removes the field.

        .. warning::
            Array replacement is destructive and total, not a merge. Whatever you pass
            for ``props`` or ``rlinks`` becomes the *complete* new list; every entry not
            present in your list is permanently dropped. A resource frequently carries
            entries you did not author and may not be aware of — for example multiple
            ``rlinks`` pointing at format variants (``.xml``/``.json``) of the same file,
            ``rlinks`` bearing ``hashes`` for integrity, a ``base64`` payload, or ``props``
            added by other tools or pipelines. Supplying a partial list here silently
            deletes all of those. There is no undo.

            **Recommended pattern:** read the current resource first with
            :meth:`get_resource_by_uuid`, mutate the copy it returns (append to / edit the
            existing ``props``/``rlinks`` rather than rebuilding them from scratch), then
            pass those full arrays back to this method. That way you extend the resource
            instead of overwriting it, and nothing you did not intend to touch is lost::

                res = doc.get_resource_by_uuid(uuid)          # safe copy of the whole resource
                res["rlinks"].append({"href": "catalog.json",
                                      "media-type": "application/json"})
                doc.update_resource(uuid, rlinks=res["rlinks"])   # full list, nothing dropped

        Args:
            uuid (str, required): UUID of the local back-matter resource to update.
            title (str | None, optional): New title; ``""`` removes it. ``None`` = unchanged.
            description (str | None, optional): New description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts (see
                :func:`append_props`). ``[]`` removes all props. ``None`` = unchanged.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). ``[]`` removes all rlinks.
            remarks (str | None, optional): New remarks (markdown); ``""`` removes them.

        Returns:
            dict | None: A safe copy of the updated resource, or None when the content is
                read-only, ``uuid`` is empty, or no local resource with that UUID exists.

def validate(self, format: str = '') -> bool

Validate the profile, then (re)build ``controls_tree`` on success.

        Mirrors :meth:`Catalog.validate`: the profile's authoritative scope and
        organization tree is refreshed the moment the content is converted and
        found to be valid OSCAL — no call to :meth:`resolve` is required. Building
        the tree resolves the profile's imports if they are not already resolved.
        When the content is not valid the tree is emptied and left marked stale so a
        later access rebuilds it.

        Args:
            format (str, optional): Accepted for API compatibility with the base
                method; does not alter the validation path.

        Returns:
            bool: True when every validation phase passes.

def walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')

Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None

property xml

Return the content as an XML string in canonical element order.

        Rebuilds from the current dict via the metaschema converter (reflecting
        the latest edits, in schema-required element order) and retains no tree.

property yaml

Return the content as a YAML string in canonical key order.

class ResolutionStatus

Lifecycle state of a Profile's control resolution.

    Members:
        UNRESOLVED (str): "unresolved" — imports have not yet been resolved.
        RESOLVING (str): "resolving" — resolution is in progress.
        RESOLVED (str): "resolved" — the resolved catalog is available.
        BLOCKED (str): "blocked" — resolution could not complete (e.g. missing import).
        EXPIRED (str): "expired" — a previously resolved catalog is stale.

def format_index_errors(errors: list) -> str

Render metaschema-walk errors (from ``_walk_instance``) as a compact one-liner.

    Args:
        errors (list, required): The structured error dicts collected by a walk.

    Returns:
        str: A ``"; "``-joined summary, one clause per error.

module oscal.oscal_implementation

oscal_implementation — OSCAL implementation-layer model classes and helpers.

Provides the model classes for the OSCAL implementation models:
``ComponentDefinition`` (reusable control implementations for components) and
``SSP`` (System Security Plan). Both subclass ``OSCAL`` from ``oscal_content``.
Module-level helper functions build the nested SSP assemblies (components,
implemented requirements, by-component statements, responsible roles) and are
also exposed as ``SSP`` methods where appropriate.

Module constants:
    (none exported)

class ComponentDefinition

OSCAL Component Definition (cDef) model.

    Represents reusable component definitions that describe how components
    satisfy controls. Subclasses ``OSCAL``.

classmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)

Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.

def add_import(self, href: 'str', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], version: 'str' = '', remarks: 'str' = '', *, include_all: 'bool' = False) -> 'ImportResult'

Add a first-level import to this document, backed by a back-matter resource.

        Uniform across every model; legality is governed by :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — ``invalid``.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR) — ``invalid``; set
          their single import with :meth:`retry_import` instead of add/remove.
        * Otherwise an import is added while the count of *real* imports is below the
          model's ``max`` (unbounded when ``max`` is None).

        The import references a back-matter ``resource`` by UUID fragment
        (``href="#<uuid>"``): if a resource whose ``rlink`` already targets ``href``
        exists it is reused, otherwise one is created via :meth:`append_resource` with
        which this method shares its resource parameters (``uuid``/``title``/
        ``description``/``props``/``remarks``). The ``href`` becomes the resource's
        single ``rlink`` (with a best-effort ``media-type`` inferred from it), and
        ``version`` is appended to ``props`` as a ``prop`` named ``"version"``. An empty
        placeholder import (href ``""``/``"#"``) is filled in place; otherwise the entry
        is appended (list models) or set (single-import models). After placement the
        import tree and any derived state are refreshed via :meth:`_after_imports_changed`.

        Args:
            href (str, required): Reference to the imported OSCAL file (XML/JSON/YAML);
                becomes the created resource's ``rlink`` href.
            uuid (str, optional): UUID for the created resource; generated when empty.
                Ignored when an existing resource is reused. Mirrors :meth:`append_resource`.
            title (str, optional): Title for the created resource.
            description (str, optional): Description for the created resource.
            props (list, optional): Property dicts for the created resource; ``version``
                (below) is appended to these. Mirrors :meth:`append_resource`.
            version (str, optional): Convenience — appended to ``props`` as a ``prop``
                named ``"version"`` (resources have no native version field).
            remarks (str, optional): Remarks (markdown) for the created resource.
            include_all (bool, optional): Keyword-only, profile-only. When True a new
                profile import selects all controls via ``include-all`` instead of the
                default empty ``include-controls``/``with-ids`` placeholder. Ignored by
                models whose imports carry no selection. Defaults to False.

        Returns:
            ImportResult: ``status`` of "added", "replaced", "duplicate", "invalid",
                or "error", with the relevant ``entry`` and ``resource``.

def append_child(self, path: 'str', child: 'dict') -> 'dict | None'

Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.

def append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'

Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.

def dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'

Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.
        Output keys/elements are emitted in canonical metaschema order (see :meth:`dumps`).

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.

def dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'

Serialize the current content to a string in the specified format.

        Keys/elements are emitted in canonical NIST metaschema order: XML element
        order is schema-required and always canonical; JSON/YAML key order is
        canonical on a best-effort basis (see :meth:`_ordered_dict`).

        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.

property duplicate_imports

Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.

property failed_imports

Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.

def find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'

Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id), ``party`` (by uuid), ``location`` (by uuid), or ``responsible-party`` (by
        role-id), or a ``control``/``group``/``param``/``part`` (by id). This walks
        ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.

classmethod from_string(cls, content: 'str', *, href: 'str | None' = None)

Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).

def get_location_by_uuid(self, location_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``location`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.locations`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a location with the given uuid
        is found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_oscal_object(self, uuid, _seen=None)

Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.

def get_parameter_by_id(self, param_id: 'str', with_source: 'bool' = False) -> 'Optional[dict]'

Return a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content. See :meth:`_lookup_in_scope` for the ``with_source`` locator form.

def get_party_by_uuid(self, party_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``party`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a party with the given uuid is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_resource_by_uuid(self, resource_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a back-matter resource defined anywhere in scope, or None.

        Looks in THIS document's ``back-matter`` first; on a local miss the search
        cascades out through the immediately imported documents and continues depth-first
        along every branch (de-duplicated and cycle-safe) until a ``resource`` whose
        ``uuid`` matches is found or every branch is exhausted. This lets a cross-reference
        (``href="#uuid"``) resolve even when the resource is defined in an imported
        document rather than locally. Subclasses may override to prefer resolved content.

        Args:
            resource_uuid (str, required): The bare resource UUID to resolve (no ``#``).
            with_source (bool, optional): Return the full locator (element + owning
                ``object_uuid``/``href``) instead of the bare resource; useful for
                resolving the resource's relative rlink hrefs. Defaults to False.
            local_only (bool, optional): Search only THIS document, never imports.
                Defaults to False.

        Returns:
            Optional[dict]: A safe copy of the matching resource (or locator), or None.

def get_responsible_party_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``responsible-party`` (by role-id) in scope, or None.

        A ``responsible-party`` is keyed by the ``role-id`` it fulfills. Looks in THIS
        document's ``metadata.responsible-parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until one with the given role-id is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

        Note: this targets metadata-level ``responsible-parties`` only. The
        ``responsible-role`` assemblies embedded throughout implementation/assessment
        models are model-specific and handled by a separate, later cascade.

def get_role_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``role`` (by id) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.roles`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a role with the given id is
        found or every branch is exhausted — so a ``role-id`` reference resolves even when
        the role is defined in an imported document. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def ignore_import(self, href: 'str') -> 'bool'

Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.

property import_tree

Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.

property imports_resolved

bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).

def initial_validation(self, content: 'str') -> 'bool'

Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.

property is_acquired

bool: True once content has been acquired (``content_state >= ACQUIRED``).

property is_cache_expired

True when remote cached content has exceeded its TTL.

property is_editable

Can this content be modified?

property is_fresh

True when content is local or cached and within its TTL.

property is_read_only

bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.

property is_remote

bool: True when the content originates from a remote source (not a local file).

property is_stale

True when remote cached content has exceeded its TTL.

property is_valid

bool: True when content passes OSCAL validation (``content_state >= VALID``).

property is_well_formed

bool: True when content is well-formed (``content_state >= WELL_FORMED``).

property json

Return the content as a JSON string in canonical key order.

def json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

classmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)

Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior (cf. ``json.load`` /
        ``pickle.load``): the source is a **local** path or a file-like object. Use
        ``loads(...)`` for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        sources (``http``/``https``/``file``/``ftp``/…) — ``load`` does not fetch remotely.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.

classmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)

Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.

classmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')

Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.

classmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)

Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.

property origin_state

Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.

def put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'

Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.

def query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

def reachable_ids(self, _seen=None) -> 'set'

Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.

def rebuild_import_tree(self) -> 'dict'

Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.

def remove_import(self, href: 'str') -> 'bool'

Remove a first-level import statement from this document.

        Operates only on this document's own imports, never on descendants. The
        import *statement* is deleted from ``self._dict``; any back-matter resource
        it referenced via a URI fragment (``href="#uuid"``) is intentionally
        preserved. The affected part of the import tree is refreshed and any
        model-specific derived state (e.g. a Profile's resolved catalog) is reset
        via :meth:`_after_imports_changed`.

        Cardinality is enforced from :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — invalid.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR require exactly
          one) — invalid; change that import with :meth:`retry_import` instead.
        * Otherwise the removal is rejected when it would drop the count of *real*
          imports (non-empty, non-``"#"`` href) below the model's minimum. Removing
          an empty placeholder is always allowed for variable-cardinality models.

        Args:
            href: Any href that identifies the import — its literal href (including
                a ``"#uuid"`` fragment or an empty ``""``/``"#"`` placeholder), or
                the resolved target href of the back-matter resource it references.

        Returns:
            True if an import was found and removed; False if not found, the
            cardinality forbids removal, or the content is read-only.

def resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'

Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-ap/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.

def retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def set_metadata(self, content: 'dict' = {}) -> 'bool'

Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.

def supports(self, name: 'str') -> 'bool'

Return True if this model exposes ``name`` as a method or attribute.

        Lets callers look before they leap when an operation is only valid for
        some OSCAL models::

            if doc.supports("add_control"):
                doc.add_control(...)

        Only class-level (shared, model-defined) members count; per-instance
        attributes set at runtime are ignored, so the check reflects the model's
        capabilities rather than incidental state.

        Args:
            name (str, required): The method/attribute name to test.

        Returns:
            bool: True if the model defines ``name``.

property unresolved_imports

Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.

def update_import(self, *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None, new_resource: 'bool' = True) -> 'ImportResult'

Modify the single import of a one-import model (SSP, AP, AR, or POA&M).

        These models carry exactly one import (POA&M: at most one), so :meth:`add_import`
        and :meth:`remove_import` do not apply — this is how their import is changed. It
        takes the same resource fields as :meth:`update_resource` (``title``,
        ``description``, ``props``, ``rlinks``, ``remarks`` — ``None`` leaves a field
        unchanged; arrays replace wholesale) plus ``new_resource``. The behavior depends
        on what the existing import points at:

        * **No import yet** (only possible for POA&M): the call is forwarded to
          :meth:`add_import` (``new_resource`` does not apply and is omitted); the import
          target's href is taken from the first supplied ``rlink``.
        * **Import is a direct URI** (or an empty ``""``/``"#"`` placeholder): a new
          back-matter resource is created (via :meth:`append_resource`) — its ``rlink`` is
          the supplied ``rlinks`` when given, otherwise the existing URI — and the import's
          href is repointed to that resource's ``#uuid``. (``new_resource`` does not apply:
          there is no backing resource to update.) Status: "replaced".
        * **Import is a ``#uuid`` fragment**:

          - ``new_resource=True`` (default): a brand-new resource with a new UUID is
            created via :meth:`append_resource` from the supplied fields, and the import's
            href is repointed to it. The prior resource is **left in place** — it is not
            deleted, because other content may reference it (including OSCAL documents not
            currently loaded that import this one and cite that resource by UUID). Because
            the new resource is built only from the fields you pass, supply ``rlinks`` (and
            any props) for the new target; read the old resource first with
            :meth:`get_resource_by_uuid` if you want to carry values forward. Status:
            "replaced".
          - ``new_resource=False``: the existing resource is edited in place via
            :meth:`update_resource` (same wholesale array-replacement semantics and
            data-loss caveats — see that method). The import's href is unchanged. Status:
            "updated".

        Args:
            title (str | None, optional): Resource title; ``""`` removes it.
            description (str | None, optional): Resource description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). Also the source of the import target's
                href when creating a resource or bootstrapping a POA&M import.
            remarks (str | None, optional): Resource remarks (markdown); ``""`` removes it.
            new_resource (bool, optional): When the import already references a ``#uuid``
                resource, True (default) creates a new resource and repoints; False edits
                the existing resource in place. Ignored for the direct-URI/placeholder and
                no-import cases. Defaults to True.

        Returns:
            ImportResult: ``status`` "added"/"replaced"/"updated" on success (``ok`` True),
                or "invalid"/"error" otherwise, carrying the import ``entry`` and the
                created/updated ``resource``.

def update_resource(self, uuid: 'str', *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None) -> 'dict | None'

Update fields of an existing ``back-matter`` resource, selected by ``uuid``.

        Only a resource defined in THIS document's own ``back-matter`` is editable
        (imported resources are not). Each field is optional and independent:

        * ``None`` (the default) leaves that field untouched.
        * A scalar (``title``/``description``/``remarks``) replaces the current value;
          an empty string removes the field entirely.
        * An array (``props``/``rlinks``) **replaces the existing array wholesale** — the
          old list is discarded and the supplied list becomes the new one. Passing an
          empty list removes the field.

        .. warning::
            Array replacement is destructive and total, not a merge. Whatever you pass
            for ``props`` or ``rlinks`` becomes the *complete* new list; every entry not
            present in your list is permanently dropped. A resource frequently carries
            entries you did not author and may not be aware of — for example multiple
            ``rlinks`` pointing at format variants (``.xml``/``.json``) of the same file,
            ``rlinks`` bearing ``hashes`` for integrity, a ``base64`` payload, or ``props``
            added by other tools or pipelines. Supplying a partial list here silently
            deletes all of those. There is no undo.

            **Recommended pattern:** read the current resource first with
            :meth:`get_resource_by_uuid`, mutate the copy it returns (append to / edit the
            existing ``props``/``rlinks`` rather than rebuilding them from scratch), then
            pass those full arrays back to this method. That way you extend the resource
            instead of overwriting it, and nothing you did not intend to touch is lost::

                res = doc.get_resource_by_uuid(uuid)          # safe copy of the whole resource
                res["rlinks"].append({"href": "catalog.json",
                                      "media-type": "application/json"})
                doc.update_resource(uuid, rlinks=res["rlinks"])   # full list, nothing dropped

        Args:
            uuid (str, required): UUID of the local back-matter resource to update.
            title (str | None, optional): New title; ``""`` removes it. ``None`` = unchanged.
            description (str | None, optional): New description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts (see
                :func:`append_props`). ``[]`` removes all props. ``None`` = unchanged.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). ``[]`` removes all rlinks.
            remarks (str | None, optional): New remarks (markdown); ``""`` removes them.

        Returns:
            dict | None: A safe copy of the updated resource, or None when the content is
                read-only, ``uuid`` is empty, or no local resource with that UUID exists.

def validate(self, format: 'str' = '') -> 'bool'

Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).

def walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')

Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None

property xml

Return the content as an XML string in canonical element order.

        Rebuilds from the current dict via the metaschema converter (reflecting
        the latest edits, in schema-required element order) and retains no tree.

property yaml

Return the content as a YAML string in canonical key order.

class SSP

OSCAL System Security Plan (SSP) model.

    Subclasses ``OSCAL`` and adds SSP-specific methods for managing system
    components, implemented requirements, and by-component statements.

classmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)

Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.

def add_by_component(self, implemented_requirement_uuid: 'str', component_uuid: 'str', description: 'str', by_component_uuid: 'str' = '', implementation_status: 'str' = 'implemented', remarks: 'str' = '') -> 'Optional[dict]'

Add a by-component statement to one of the SSP's implemented-requirements.

        The by-component is **built from the supplied scalar fields** — no caller-provided
        dict is stored verbatim — so it is schema-aligned by construction. It is appended
        to the implemented-requirement identified by *implemented_requirement_uuid* and
        returned as a safe copy (the live node stays in ``_dict``; further edits go through
        methods). Through the method decorators this also enforces the read-only guard and
        marks the document unsaved.

        Args:
            implemented_requirement_uuid (str, required): ``uuid`` of the target
                implemented-requirement under ``control-implementation``.
            component_uuid (str, required): UUID of the referenced system component.
            description (str, required): How the component satisfies the requirement.
            by_component_uuid (str, optional): UUID for the by-component; a new one is
                generated when empty.
            implementation_status (str, optional): ``implementation-status.state`` value.
                Defaults to "implemented".
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            Optional[dict]: A safe copy of the new by-component, or None when the SSP has
                no implemented-requirement with that uuid.

def add_import(self, href: 'str', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], version: 'str' = '', remarks: 'str' = '', *, include_all: 'bool' = False) -> 'ImportResult'

Add a first-level import to this document, backed by a back-matter resource.

        Uniform across every model; legality is governed by :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — ``invalid``.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR) — ``invalid``; set
          their single import with :meth:`retry_import` instead of add/remove.
        * Otherwise an import is added while the count of *real* imports is below the
          model's ``max`` (unbounded when ``max`` is None).

        The import references a back-matter ``resource`` by UUID fragment
        (``href="#<uuid>"``): if a resource whose ``rlink`` already targets ``href``
        exists it is reused, otherwise one is created via :meth:`append_resource` with
        which this method shares its resource parameters (``uuid``/``title``/
        ``description``/``props``/``remarks``). The ``href`` becomes the resource's
        single ``rlink`` (with a best-effort ``media-type`` inferred from it), and
        ``version`` is appended to ``props`` as a ``prop`` named ``"version"``. An empty
        placeholder import (href ``""``/``"#"``) is filled in place; otherwise the entry
        is appended (list models) or set (single-import models). After placement the
        import tree and any derived state are refreshed via :meth:`_after_imports_changed`.

        Args:
            href (str, required): Reference to the imported OSCAL file (XML/JSON/YAML);
                becomes the created resource's ``rlink`` href.
            uuid (str, optional): UUID for the created resource; generated when empty.
                Ignored when an existing resource is reused. Mirrors :meth:`append_resource`.
            title (str, optional): Title for the created resource.
            description (str, optional): Description for the created resource.
            props (list, optional): Property dicts for the created resource; ``version``
                (below) is appended to these. Mirrors :meth:`append_resource`.
            version (str, optional): Convenience — appended to ``props`` as a ``prop``
                named ``"version"`` (resources have no native version field).
            remarks (str, optional): Remarks (markdown) for the created resource.
            include_all (bool, optional): Keyword-only, profile-only. When True a new
                profile import selects all controls via ``include-all`` instead of the
                default empty ``include-controls``/``with-ids`` placeholder. Ignored by
                models whose imports carry no selection. Defaults to False.

        Returns:
            ImportResult: ``status`` of "added", "replaced", "duplicate", "invalid",
                or "error", with the relevant ``entry`` and ``resource``.

def append_child(self, path: 'str', child: 'dict') -> 'dict | None'

Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.

def append_component(self, component_type: 'str', component_title: 'str', component_description: 'str', op_status: 'str' = 'operational', component_uuid: 'str' = '', props: 'list' = [], links: 'list' = [], remarks: 'str' = '') -> 'Optional[dict]'

Add a component to the SSP's ``system-implementation`` section.

        Args:
            component_type (str, required): The component ``type`` (e.g. "software").
            component_title (str, required): The component title.
            component_description (str, required): The component description.
            op_status (str, optional): Operational ``status.state`` value.
                Defaults to "operational".
            component_uuid (str, optional): UUID for the component. A new UUID is
                generated when empty.
            props (list, optional): Property dicts to add.
            links (list, optional): Link dicts to add.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            Optional[dict]: The newly created component dict, or None on failure.

def append_impl_requirement(self, control_id: 'str', props: 'list' = [], links: 'list' = [], remarks: 'str' = '') -> 'Optional[dict]'

Add an implemented-requirement to the SSP's ``control-implementation`` section.

        Args:
            control_id (str, required): The ID of the control being implemented.
            props (list, optional): Property dicts to add.
            links (list, optional): Link dicts to add.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            Optional[dict]: The newly created implemented-requirement dict (with a
                generated UUID), or None on failure.

def append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'

Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.

def dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'

Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.
        Output keys/elements are emitted in canonical metaschema order (see :meth:`dumps`).

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.

def dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'

Serialize the current content to a string in the specified format.

        Keys/elements are emitted in canonical NIST metaschema order: XML element
        order is schema-required and always canonical; JSON/YAML key order is
        canonical on a best-effort basis (see :meth:`_ordered_dict`).

        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.

property duplicate_imports

Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.

property failed_imports

Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.

def find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'

Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id), ``party`` (by uuid), ``location`` (by uuid), or ``responsible-party`` (by
        role-id), or a ``control``/``group``/``param``/``part`` (by id). This walks
        ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.

classmethod from_string(cls, content: 'str', *, href: 'str | None' = None)

Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).

def get_location_by_uuid(self, location_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``location`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.locations`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a location with the given uuid
        is found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_oscal_object(self, uuid, _seen=None)

Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.

def get_parameter_by_id(self, param_id: 'str', with_source: 'bool' = False) -> 'Optional[dict]'

Return a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content. See :meth:`_lookup_in_scope` for the ``with_source`` locator form.

def get_party_by_uuid(self, party_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``party`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a party with the given uuid is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_resource_by_uuid(self, resource_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a back-matter resource defined anywhere in scope, or None.

        Looks in THIS document's ``back-matter`` first; on a local miss the search
        cascades out through the immediately imported documents and continues depth-first
        along every branch (de-duplicated and cycle-safe) until a ``resource`` whose
        ``uuid`` matches is found or every branch is exhausted. This lets a cross-reference
        (``href="#uuid"``) resolve even when the resource is defined in an imported
        document rather than locally. Subclasses may override to prefer resolved content.

        Args:
            resource_uuid (str, required): The bare resource UUID to resolve (no ``#``).
            with_source (bool, optional): Return the full locator (element + owning
                ``object_uuid``/``href``) instead of the bare resource; useful for
                resolving the resource's relative rlink hrefs. Defaults to False.
            local_only (bool, optional): Search only THIS document, never imports.
                Defaults to False.

        Returns:
            Optional[dict]: A safe copy of the matching resource (or locator), or None.

def get_responsible_party_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``responsible-party`` (by role-id) in scope, or None.

        A ``responsible-party`` is keyed by the ``role-id`` it fulfills. Looks in THIS
        document's ``metadata.responsible-parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until one with the given role-id is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

        Note: this targets metadata-level ``responsible-parties`` only. The
        ``responsible-role`` assemblies embedded throughout implementation/assessment
        models are model-specific and handled by a separate, later cascade.

def get_role_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``role`` (by id) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.roles`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a role with the given id is
        found or every branch is exhausted — so a ``role-id`` reference resolves even when
        the role is defined in an imported document. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def ignore_import(self, href: 'str') -> 'bool'

Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.

property import_tree

Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.

property imports_resolved

bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).

def initial_validation(self, content: 'str') -> 'bool'

Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.

property is_acquired

bool: True once content has been acquired (``content_state >= ACQUIRED``).

property is_cache_expired

True when remote cached content has exceeded its TTL.

property is_editable

Can this content be modified?

property is_fresh

True when content is local or cached and within its TTL.

property is_read_only

bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.

property is_remote

bool: True when the content originates from a remote source (not a local file).

property is_stale

True when remote cached content has exceeded its TTL.

property is_valid

bool: True when content passes OSCAL validation (``content_state >= VALID``).

property is_well_formed

bool: True when content is well-formed (``content_state >= WELL_FORMED``).

property json

Return the content as a JSON string in canonical key order.

def json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

classmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)

Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior (cf. ``json.load`` /
        ``pickle.load``): the source is a **local** path or a file-like object. Use
        ``loads(...)`` for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        sources (``http``/``https``/``file``/``ftp``/…) — ``load`` does not fetch remotely.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.

classmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)

Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.

classmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')

Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.

classmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)

Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.

property origin_state

Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.

def put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'

Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.

def query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

def reachable_ids(self, _seen=None) -> 'set'

Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.

def rebuild_import_tree(self) -> 'dict'

Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.

def remove_import(self, href: 'str') -> 'bool'

Remove a first-level import statement from this document.

        Operates only on this document's own imports, never on descendants. The
        import *statement* is deleted from ``self._dict``; any back-matter resource
        it referenced via a URI fragment (``href="#uuid"``) is intentionally
        preserved. The affected part of the import tree is refreshed and any
        model-specific derived state (e.g. a Profile's resolved catalog) is reset
        via :meth:`_after_imports_changed`.

        Cardinality is enforced from :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — invalid.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR require exactly
          one) — invalid; change that import with :meth:`retry_import` instead.
        * Otherwise the removal is rejected when it would drop the count of *real*
          imports (non-empty, non-``"#"`` href) below the model's minimum. Removing
          an empty placeholder is always allowed for variable-cardinality models.

        Args:
            href: Any href that identifies the import — its literal href (including
                a ``"#uuid"`` fragment or an empty ``""``/``"#"`` placeholder), or
                the resolved target href of the back-matter resource it references.

        Returns:
            True if an import was found and removed; False if not found, the
            cardinality forbids removal, or the content is read-only.

def resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'

Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-ap/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.

def retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def set_metadata(self, content: 'dict' = {}) -> 'bool'

Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.

def supports(self, name: 'str') -> 'bool'

Return True if this model exposes ``name`` as a method or attribute.

        Lets callers look before they leap when an operation is only valid for
        some OSCAL models::

            if doc.supports("add_control"):
                doc.add_control(...)

        Only class-level (shared, model-defined) members count; per-instance
        attributes set at runtime are ignored, so the check reflects the model's
        capabilities rather than incidental state.

        Args:
            name (str, required): The method/attribute name to test.

        Returns:
            bool: True if the model defines ``name``.

property unresolved_imports

Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.

def update_import(self, *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None, new_resource: 'bool' = True) -> 'ImportResult'

Modify the single import of a one-import model (SSP, AP, AR, or POA&M).

        These models carry exactly one import (POA&M: at most one), so :meth:`add_import`
        and :meth:`remove_import` do not apply — this is how their import is changed. It
        takes the same resource fields as :meth:`update_resource` (``title``,
        ``description``, ``props``, ``rlinks``, ``remarks`` — ``None`` leaves a field
        unchanged; arrays replace wholesale) plus ``new_resource``. The behavior depends
        on what the existing import points at:

        * **No import yet** (only possible for POA&M): the call is forwarded to
          :meth:`add_import` (``new_resource`` does not apply and is omitted); the import
          target's href is taken from the first supplied ``rlink``.
        * **Import is a direct URI** (or an empty ``""``/``"#"`` placeholder): a new
          back-matter resource is created (via :meth:`append_resource`) — its ``rlink`` is
          the supplied ``rlinks`` when given, otherwise the existing URI — and the import's
          href is repointed to that resource's ``#uuid``. (``new_resource`` does not apply:
          there is no backing resource to update.) Status: "replaced".
        * **Import is a ``#uuid`` fragment**:

          - ``new_resource=True`` (default): a brand-new resource with a new UUID is
            created via :meth:`append_resource` from the supplied fields, and the import's
            href is repointed to it. The prior resource is **left in place** — it is not
            deleted, because other content may reference it (including OSCAL documents not
            currently loaded that import this one and cite that resource by UUID). Because
            the new resource is built only from the fields you pass, supply ``rlinks`` (and
            any props) for the new target; read the old resource first with
            :meth:`get_resource_by_uuid` if you want to carry values forward. Status:
            "replaced".
          - ``new_resource=False``: the existing resource is edited in place via
            :meth:`update_resource` (same wholesale array-replacement semantics and
            data-loss caveats — see that method). The import's href is unchanged. Status:
            "updated".

        Args:
            title (str | None, optional): Resource title; ``""`` removes it.
            description (str | None, optional): Resource description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). Also the source of the import target's
                href when creating a resource or bootstrapping a POA&M import.
            remarks (str | None, optional): Resource remarks (markdown); ``""`` removes it.
            new_resource (bool, optional): When the import already references a ``#uuid``
                resource, True (default) creates a new resource and repoints; False edits
                the existing resource in place. Ignored for the direct-URI/placeholder and
                no-import cases. Defaults to True.

        Returns:
            ImportResult: ``status`` "added"/"replaced"/"updated" on success (``ok`` True),
                or "invalid"/"error" otherwise, carrying the import ``entry`` and the
                created/updated ``resource``.

def update_resource(self, uuid: 'str', *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None) -> 'dict | None'

Update fields of an existing ``back-matter`` resource, selected by ``uuid``.

        Only a resource defined in THIS document's own ``back-matter`` is editable
        (imported resources are not). Each field is optional and independent:

        * ``None`` (the default) leaves that field untouched.
        * A scalar (``title``/``description``/``remarks``) replaces the current value;
          an empty string removes the field entirely.
        * An array (``props``/``rlinks``) **replaces the existing array wholesale** — the
          old list is discarded and the supplied list becomes the new one. Passing an
          empty list removes the field.

        .. warning::
            Array replacement is destructive and total, not a merge. Whatever you pass
            for ``props`` or ``rlinks`` becomes the *complete* new list; every entry not
            present in your list is permanently dropped. A resource frequently carries
            entries you did not author and may not be aware of — for example multiple
            ``rlinks`` pointing at format variants (``.xml``/``.json``) of the same file,
            ``rlinks`` bearing ``hashes`` for integrity, a ``base64`` payload, or ``props``
            added by other tools or pipelines. Supplying a partial list here silently
            deletes all of those. There is no undo.

            **Recommended pattern:** read the current resource first with
            :meth:`get_resource_by_uuid`, mutate the copy it returns (append to / edit the
            existing ``props``/``rlinks`` rather than rebuilding them from scratch), then
            pass those full arrays back to this method. That way you extend the resource
            instead of overwriting it, and nothing you did not intend to touch is lost::

                res = doc.get_resource_by_uuid(uuid)          # safe copy of the whole resource
                res["rlinks"].append({"href": "catalog.json",
                                      "media-type": "application/json"})
                doc.update_resource(uuid, rlinks=res["rlinks"])   # full list, nothing dropped

        Args:
            uuid (str, required): UUID of the local back-matter resource to update.
            title (str | None, optional): New title; ``""`` removes it. ``None`` = unchanged.
            description (str | None, optional): New description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts (see
                :func:`append_props`). ``[]`` removes all props. ``None`` = unchanged.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). ``[]`` removes all rlinks.
            remarks (str | None, optional): New remarks (markdown); ``""`` removes them.

        Returns:
            dict | None: A safe copy of the updated resource, or None when the content is
                read-only, ``uuid`` is empty, or no local resource with that UUID exists.

def validate(self, format: 'str' = '') -> 'bool'

Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).

def walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')

Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None

property xml

Return the content as an XML string in canonical element order.

        Rebuilds from the current dict via the metaschema converter (reflecting
        the latest edits, in schema-required element order) and retains no tree.

property yaml

Return the content as a YAML string in canonical key order.

def append_component(ssp_obj: 'OSCAL', component_type: 'str', component_title: 'str', component_description: 'str', op_status: 'str' = 'operational', component_uuid: 'str' = '', props: 'list' = [], links: 'list' = [], remarks: 'str' = '') -> 'Optional[dict]'

Add a component to an SSP's ``system-implementation`` section.

    Args:
        ssp_obj (OSCAL, required): The SSP instance to modify.
        component_type (str, required): The component ``type`` (e.g. "software").
        component_title (str, required): The component title.
        component_description (str, required): The component description.
        op_status (str, optional): Operational ``status.state`` value.
            Defaults to "operational".
        component_uuid (str, optional): UUID for the component. A new UUID is
            generated when empty.
        props (list, optional): Property dicts to add.
        links (list, optional): Link dicts to add.
        remarks (str, optional): Remarks prose (markdown).

    Returns:
        Optional[dict]: The newly created component dict, or None on failure.

def append_impl_requirement(ssp_obj: 'OSCAL', control_id: 'str', props: 'list' = [], links: 'list' = [], remarks: 'str' = '') -> 'Optional[dict]'

Add an implemented-requirement to an SSP's ``control-implementation`` section.

    Args:
        ssp_obj (OSCAL, required): The SSP instance to modify.
        control_id (str, required): The ID of the control being implemented.
        props (list, optional): Property dicts to add.
        links (list, optional): Link dicts to add.
        remarks (str, optional): Remarks prose (markdown).

    Returns:
        Optional[dict]: The newly created implemented-requirement dict (with a
            generated UUID), or None on failure.

module oscal.oscal_assessment

oscal_assessment — OSCAL assessment-layer model classes.

Provides the model classes for the OSCAL assessment models: ``AssessmentPlan``
(Security Assessment Plan / SAP), ``AssessmentResults`` (Security Assessment
Results / SAR), and ``POAM`` (Plan of Action and Milestones). Each subclasses
``OSCAL`` from ``oscal_content`` and inherits its common load/save/validate and
query behavior.

Module constants:
    (none exported)

class AssessmentPlan

OSCAL Assessment Plan (AP / SAP) model.

    Represents an assessment plan that defines the scope, assets, activities,
    and tasks for a security assessment. Subclasses ``OSCAL``.

classmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)

Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.

def add_import(self, href: 'str', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], version: 'str' = '', remarks: 'str' = '', *, include_all: 'bool' = False) -> 'ImportResult'

Add a first-level import to this document, backed by a back-matter resource.

        Uniform across every model; legality is governed by :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — ``invalid``.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR) — ``invalid``; set
          their single import with :meth:`retry_import` instead of add/remove.
        * Otherwise an import is added while the count of *real* imports is below the
          model's ``max`` (unbounded when ``max`` is None).

        The import references a back-matter ``resource`` by UUID fragment
        (``href="#<uuid>"``): if a resource whose ``rlink`` already targets ``href``
        exists it is reused, otherwise one is created via :meth:`append_resource` with
        which this method shares its resource parameters (``uuid``/``title``/
        ``description``/``props``/``remarks``). The ``href`` becomes the resource's
        single ``rlink`` (with a best-effort ``media-type`` inferred from it), and
        ``version`` is appended to ``props`` as a ``prop`` named ``"version"``. An empty
        placeholder import (href ``""``/``"#"``) is filled in place; otherwise the entry
        is appended (list models) or set (single-import models). After placement the
        import tree and any derived state are refreshed via :meth:`_after_imports_changed`.

        Args:
            href (str, required): Reference to the imported OSCAL file (XML/JSON/YAML);
                becomes the created resource's ``rlink`` href.
            uuid (str, optional): UUID for the created resource; generated when empty.
                Ignored when an existing resource is reused. Mirrors :meth:`append_resource`.
            title (str, optional): Title for the created resource.
            description (str, optional): Description for the created resource.
            props (list, optional): Property dicts for the created resource; ``version``
                (below) is appended to these. Mirrors :meth:`append_resource`.
            version (str, optional): Convenience — appended to ``props`` as a ``prop``
                named ``"version"`` (resources have no native version field).
            remarks (str, optional): Remarks (markdown) for the created resource.
            include_all (bool, optional): Keyword-only, profile-only. When True a new
                profile import selects all controls via ``include-all`` instead of the
                default empty ``include-controls``/``with-ids`` placeholder. Ignored by
                models whose imports carry no selection. Defaults to False.

        Returns:
            ImportResult: ``status`` of "added", "replaced", "duplicate", "invalid",
                or "error", with the relevant ``entry`` and ``resource``.

def append_child(self, path: 'str', child: 'dict') -> 'dict | None'

Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.

def append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'

Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.

def dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'

Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.
        Output keys/elements are emitted in canonical metaschema order (see :meth:`dumps`).

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.

def dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'

Serialize the current content to a string in the specified format.

        Keys/elements are emitted in canonical NIST metaschema order: XML element
        order is schema-required and always canonical; JSON/YAML key order is
        canonical on a best-effort basis (see :meth:`_ordered_dict`).

        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.

property duplicate_imports

Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.

property failed_imports

Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.

def find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'

Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id), ``party`` (by uuid), ``location`` (by uuid), or ``responsible-party`` (by
        role-id), or a ``control``/``group``/``param``/``part`` (by id). This walks
        ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.

classmethod from_string(cls, content: 'str', *, href: 'str | None' = None)

Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).

def get_location_by_uuid(self, location_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``location`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.locations`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a location with the given uuid
        is found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_oscal_object(self, uuid, _seen=None)

Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.

def get_parameter_by_id(self, param_id: 'str', with_source: 'bool' = False) -> 'Optional[dict]'

Return a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content. See :meth:`_lookup_in_scope` for the ``with_source`` locator form.

def get_party_by_uuid(self, party_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``party`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a party with the given uuid is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_resource_by_uuid(self, resource_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a back-matter resource defined anywhere in scope, or None.

        Looks in THIS document's ``back-matter`` first; on a local miss the search
        cascades out through the immediately imported documents and continues depth-first
        along every branch (de-duplicated and cycle-safe) until a ``resource`` whose
        ``uuid`` matches is found or every branch is exhausted. This lets a cross-reference
        (``href="#uuid"``) resolve even when the resource is defined in an imported
        document rather than locally. Subclasses may override to prefer resolved content.

        Args:
            resource_uuid (str, required): The bare resource UUID to resolve (no ``#``).
            with_source (bool, optional): Return the full locator (element + owning
                ``object_uuid``/``href``) instead of the bare resource; useful for
                resolving the resource's relative rlink hrefs. Defaults to False.
            local_only (bool, optional): Search only THIS document, never imports.
                Defaults to False.

        Returns:
            Optional[dict]: A safe copy of the matching resource (or locator), or None.

def get_responsible_party_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``responsible-party`` (by role-id) in scope, or None.

        A ``responsible-party`` is keyed by the ``role-id`` it fulfills. Looks in THIS
        document's ``metadata.responsible-parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until one with the given role-id is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

        Note: this targets metadata-level ``responsible-parties`` only. The
        ``responsible-role`` assemblies embedded throughout implementation/assessment
        models are model-specific and handled by a separate, later cascade.

def get_role_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``role`` (by id) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.roles`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a role with the given id is
        found or every branch is exhausted — so a ``role-id`` reference resolves even when
        the role is defined in an imported document. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def ignore_import(self, href: 'str') -> 'bool'

Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.

property import_tree

Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.

property imports_resolved

bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).

def initial_validation(self, content: 'str') -> 'bool'

Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.

property is_acquired

bool: True once content has been acquired (``content_state >= ACQUIRED``).

property is_cache_expired

True when remote cached content has exceeded its TTL.

property is_editable

Can this content be modified?

property is_fresh

True when content is local or cached and within its TTL.

property is_read_only

bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.

property is_remote

bool: True when the content originates from a remote source (not a local file).

property is_stale

True when remote cached content has exceeded its TTL.

property is_valid

bool: True when content passes OSCAL validation (``content_state >= VALID``).

property is_well_formed

bool: True when content is well-formed (``content_state >= WELL_FORMED``).

property json

Return the content as a JSON string in canonical key order.

def json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

classmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)

Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior (cf. ``json.load`` /
        ``pickle.load``): the source is a **local** path or a file-like object. Use
        ``loads(...)`` for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        sources (``http``/``https``/``file``/``ftp``/…) — ``load`` does not fetch remotely.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.

classmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)

Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.

classmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')

Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.

classmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)

Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.

property origin_state

Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.

def put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'

Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.

def query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

def reachable_ids(self, _seen=None) -> 'set'

Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.

def rebuild_import_tree(self) -> 'dict'

Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.

def remove_import(self, href: 'str') -> 'bool'

Remove a first-level import statement from this document.

        Operates only on this document's own imports, never on descendants. The
        import *statement* is deleted from ``self._dict``; any back-matter resource
        it referenced via a URI fragment (``href="#uuid"``) is intentionally
        preserved. The affected part of the import tree is refreshed and any
        model-specific derived state (e.g. a Profile's resolved catalog) is reset
        via :meth:`_after_imports_changed`.

        Cardinality is enforced from :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — invalid.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR require exactly
          one) — invalid; change that import with :meth:`retry_import` instead.
        * Otherwise the removal is rejected when it would drop the count of *real*
          imports (non-empty, non-``"#"`` href) below the model's minimum. Removing
          an empty placeholder is always allowed for variable-cardinality models.

        Args:
            href: Any href that identifies the import — its literal href (including
                a ``"#uuid"`` fragment or an empty ``""``/``"#"`` placeholder), or
                the resolved target href of the back-matter resource it references.

        Returns:
            True if an import was found and removed; False if not found, the
            cardinality forbids removal, or the content is read-only.

def resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'

Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-ap/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.

def retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def set_metadata(self, content: 'dict' = {}) -> 'bool'

Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.

def supports(self, name: 'str') -> 'bool'

Return True if this model exposes ``name`` as a method or attribute.

        Lets callers look before they leap when an operation is only valid for
        some OSCAL models::

            if doc.supports("add_control"):
                doc.add_control(...)

        Only class-level (shared, model-defined) members count; per-instance
        attributes set at runtime are ignored, so the check reflects the model's
        capabilities rather than incidental state.

        Args:
            name (str, required): The method/attribute name to test.

        Returns:
            bool: True if the model defines ``name``.

property unresolved_imports

Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.

def update_import(self, *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None, new_resource: 'bool' = True) -> 'ImportResult'

Modify the single import of a one-import model (SSP, AP, AR, or POA&M).

        These models carry exactly one import (POA&M: at most one), so :meth:`add_import`
        and :meth:`remove_import` do not apply — this is how their import is changed. It
        takes the same resource fields as :meth:`update_resource` (``title``,
        ``description``, ``props``, ``rlinks``, ``remarks`` — ``None`` leaves a field
        unchanged; arrays replace wholesale) plus ``new_resource``. The behavior depends
        on what the existing import points at:

        * **No import yet** (only possible for POA&M): the call is forwarded to
          :meth:`add_import` (``new_resource`` does not apply and is omitted); the import
          target's href is taken from the first supplied ``rlink``.
        * **Import is a direct URI** (or an empty ``""``/``"#"`` placeholder): a new
          back-matter resource is created (via :meth:`append_resource`) — its ``rlink`` is
          the supplied ``rlinks`` when given, otherwise the existing URI — and the import's
          href is repointed to that resource's ``#uuid``. (``new_resource`` does not apply:
          there is no backing resource to update.) Status: "replaced".
        * **Import is a ``#uuid`` fragment**:

          - ``new_resource=True`` (default): a brand-new resource with a new UUID is
            created via :meth:`append_resource` from the supplied fields, and the import's
            href is repointed to it. The prior resource is **left in place** — it is not
            deleted, because other content may reference it (including OSCAL documents not
            currently loaded that import this one and cite that resource by UUID). Because
            the new resource is built only from the fields you pass, supply ``rlinks`` (and
            any props) for the new target; read the old resource first with
            :meth:`get_resource_by_uuid` if you want to carry values forward. Status:
            "replaced".
          - ``new_resource=False``: the existing resource is edited in place via
            :meth:`update_resource` (same wholesale array-replacement semantics and
            data-loss caveats — see that method). The import's href is unchanged. Status:
            "updated".

        Args:
            title (str | None, optional): Resource title; ``""`` removes it.
            description (str | None, optional): Resource description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). Also the source of the import target's
                href when creating a resource or bootstrapping a POA&M import.
            remarks (str | None, optional): Resource remarks (markdown); ``""`` removes it.
            new_resource (bool, optional): When the import already references a ``#uuid``
                resource, True (default) creates a new resource and repoints; False edits
                the existing resource in place. Ignored for the direct-URI/placeholder and
                no-import cases. Defaults to True.

        Returns:
            ImportResult: ``status`` "added"/"replaced"/"updated" on success (``ok`` True),
                or "invalid"/"error" otherwise, carrying the import ``entry`` and the
                created/updated ``resource``.

def update_resource(self, uuid: 'str', *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None) -> 'dict | None'

Update fields of an existing ``back-matter`` resource, selected by ``uuid``.

        Only a resource defined in THIS document's own ``back-matter`` is editable
        (imported resources are not). Each field is optional and independent:

        * ``None`` (the default) leaves that field untouched.
        * A scalar (``title``/``description``/``remarks``) replaces the current value;
          an empty string removes the field entirely.
        * An array (``props``/``rlinks``) **replaces the existing array wholesale** — the
          old list is discarded and the supplied list becomes the new one. Passing an
          empty list removes the field.

        .. warning::
            Array replacement is destructive and total, not a merge. Whatever you pass
            for ``props`` or ``rlinks`` becomes the *complete* new list; every entry not
            present in your list is permanently dropped. A resource frequently carries
            entries you did not author and may not be aware of — for example multiple
            ``rlinks`` pointing at format variants (``.xml``/``.json``) of the same file,
            ``rlinks`` bearing ``hashes`` for integrity, a ``base64`` payload, or ``props``
            added by other tools or pipelines. Supplying a partial list here silently
            deletes all of those. There is no undo.

            **Recommended pattern:** read the current resource first with
            :meth:`get_resource_by_uuid`, mutate the copy it returns (append to / edit the
            existing ``props``/``rlinks`` rather than rebuilding them from scratch), then
            pass those full arrays back to this method. That way you extend the resource
            instead of overwriting it, and nothing you did not intend to touch is lost::

                res = doc.get_resource_by_uuid(uuid)          # safe copy of the whole resource
                res["rlinks"].append({"href": "catalog.json",
                                      "media-type": "application/json"})
                doc.update_resource(uuid, rlinks=res["rlinks"])   # full list, nothing dropped

        Args:
            uuid (str, required): UUID of the local back-matter resource to update.
            title (str | None, optional): New title; ``""`` removes it. ``None`` = unchanged.
            description (str | None, optional): New description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts (see
                :func:`append_props`). ``[]`` removes all props. ``None`` = unchanged.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). ``[]`` removes all rlinks.
            remarks (str | None, optional): New remarks (markdown); ``""`` removes them.

        Returns:
            dict | None: A safe copy of the updated resource, or None when the content is
                read-only, ``uuid`` is empty, or no local resource with that UUID exists.

def validate(self, format: 'str' = '') -> 'bool'

Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).

def walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')

Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None

property xml

Return the content as an XML string in canonical element order.

        Rebuilds from the current dict via the metaschema converter (reflecting
        the latest edits, in schema-required element order) and retains no tree.

property yaml

Return the content as a YAML string in canonical key order.

class AssessmentResults

OSCAL Assessment Results (AR / SAR) model.

    Represents the findings, observations, and risks produced by executing an
    assessment plan. Subclasses ``OSCAL``.

classmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)

Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.

def add_import(self, href: 'str', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], version: 'str' = '', remarks: 'str' = '', *, include_all: 'bool' = False) -> 'ImportResult'

Add a first-level import to this document, backed by a back-matter resource.

        Uniform across every model; legality is governed by :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — ``invalid``.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR) — ``invalid``; set
          their single import with :meth:`retry_import` instead of add/remove.
        * Otherwise an import is added while the count of *real* imports is below the
          model's ``max`` (unbounded when ``max`` is None).

        The import references a back-matter ``resource`` by UUID fragment
        (``href="#<uuid>"``): if a resource whose ``rlink`` already targets ``href``
        exists it is reused, otherwise one is created via :meth:`append_resource` with
        which this method shares its resource parameters (``uuid``/``title``/
        ``description``/``props``/``remarks``). The ``href`` becomes the resource's
        single ``rlink`` (with a best-effort ``media-type`` inferred from it), and
        ``version`` is appended to ``props`` as a ``prop`` named ``"version"``. An empty
        placeholder import (href ``""``/``"#"``) is filled in place; otherwise the entry
        is appended (list models) or set (single-import models). After placement the
        import tree and any derived state are refreshed via :meth:`_after_imports_changed`.

        Args:
            href (str, required): Reference to the imported OSCAL file (XML/JSON/YAML);
                becomes the created resource's ``rlink`` href.
            uuid (str, optional): UUID for the created resource; generated when empty.
                Ignored when an existing resource is reused. Mirrors :meth:`append_resource`.
            title (str, optional): Title for the created resource.
            description (str, optional): Description for the created resource.
            props (list, optional): Property dicts for the created resource; ``version``
                (below) is appended to these. Mirrors :meth:`append_resource`.
            version (str, optional): Convenience — appended to ``props`` as a ``prop``
                named ``"version"`` (resources have no native version field).
            remarks (str, optional): Remarks (markdown) for the created resource.
            include_all (bool, optional): Keyword-only, profile-only. When True a new
                profile import selects all controls via ``include-all`` instead of the
                default empty ``include-controls``/``with-ids`` placeholder. Ignored by
                models whose imports carry no selection. Defaults to False.

        Returns:
            ImportResult: ``status`` of "added", "replaced", "duplicate", "invalid",
                or "error", with the relevant ``entry`` and ``resource``.

def append_child(self, path: 'str', child: 'dict') -> 'dict | None'

Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.

def append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'

Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.

def dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'

Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.
        Output keys/elements are emitted in canonical metaschema order (see :meth:`dumps`).

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.

def dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'

Serialize the current content to a string in the specified format.

        Keys/elements are emitted in canonical NIST metaschema order: XML element
        order is schema-required and always canonical; JSON/YAML key order is
        canonical on a best-effort basis (see :meth:`_ordered_dict`).

        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.

property duplicate_imports

Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.

property failed_imports

Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.

def find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'

Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id), ``party`` (by uuid), ``location`` (by uuid), or ``responsible-party`` (by
        role-id), or a ``control``/``group``/``param``/``part`` (by id). This walks
        ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.

classmethod from_string(cls, content: 'str', *, href: 'str | None' = None)

Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).

def get_location_by_uuid(self, location_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``location`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.locations`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a location with the given uuid
        is found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_oscal_object(self, uuid, _seen=None)

Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.

def get_parameter_by_id(self, param_id: 'str', with_source: 'bool' = False) -> 'Optional[dict]'

Return a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content. See :meth:`_lookup_in_scope` for the ``with_source`` locator form.

def get_party_by_uuid(self, party_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``party`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a party with the given uuid is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_resource_by_uuid(self, resource_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a back-matter resource defined anywhere in scope, or None.

        Looks in THIS document's ``back-matter`` first; on a local miss the search
        cascades out through the immediately imported documents and continues depth-first
        along every branch (de-duplicated and cycle-safe) until a ``resource`` whose
        ``uuid`` matches is found or every branch is exhausted. This lets a cross-reference
        (``href="#uuid"``) resolve even when the resource is defined in an imported
        document rather than locally. Subclasses may override to prefer resolved content.

        Args:
            resource_uuid (str, required): The bare resource UUID to resolve (no ``#``).
            with_source (bool, optional): Return the full locator (element + owning
                ``object_uuid``/``href``) instead of the bare resource; useful for
                resolving the resource's relative rlink hrefs. Defaults to False.
            local_only (bool, optional): Search only THIS document, never imports.
                Defaults to False.

        Returns:
            Optional[dict]: A safe copy of the matching resource (or locator), or None.

def get_responsible_party_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``responsible-party`` (by role-id) in scope, or None.

        A ``responsible-party`` is keyed by the ``role-id`` it fulfills. Looks in THIS
        document's ``metadata.responsible-parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until one with the given role-id is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

        Note: this targets metadata-level ``responsible-parties`` only. The
        ``responsible-role`` assemblies embedded throughout implementation/assessment
        models are model-specific and handled by a separate, later cascade.

def get_role_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``role`` (by id) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.roles`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a role with the given id is
        found or every branch is exhausted — so a ``role-id`` reference resolves even when
        the role is defined in an imported document. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def ignore_import(self, href: 'str') -> 'bool'

Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.

property import_tree

Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.

property imports_resolved

bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).

def initial_validation(self, content: 'str') -> 'bool'

Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.

property is_acquired

bool: True once content has been acquired (``content_state >= ACQUIRED``).

property is_cache_expired

True when remote cached content has exceeded its TTL.

property is_editable

Can this content be modified?

property is_fresh

True when content is local or cached and within its TTL.

property is_read_only

bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.

property is_remote

bool: True when the content originates from a remote source (not a local file).

property is_stale

True when remote cached content has exceeded its TTL.

property is_valid

bool: True when content passes OSCAL validation (``content_state >= VALID``).

property is_well_formed

bool: True when content is well-formed (``content_state >= WELL_FORMED``).

property json

Return the content as a JSON string in canonical key order.

def json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

classmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)

Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior (cf. ``json.load`` /
        ``pickle.load``): the source is a **local** path or a file-like object. Use
        ``loads(...)`` for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        sources (``http``/``https``/``file``/``ftp``/…) — ``load`` does not fetch remotely.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.

classmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)

Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.

classmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')

Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.

classmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)

Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.

property origin_state

Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.

def put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'

Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.

def query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

def reachable_ids(self, _seen=None) -> 'set'

Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.

def rebuild_import_tree(self) -> 'dict'

Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.

def remove_import(self, href: 'str') -> 'bool'

Remove a first-level import statement from this document.

        Operates only on this document's own imports, never on descendants. The
        import *statement* is deleted from ``self._dict``; any back-matter resource
        it referenced via a URI fragment (``href="#uuid"``) is intentionally
        preserved. The affected part of the import tree is refreshed and any
        model-specific derived state (e.g. a Profile's resolved catalog) is reset
        via :meth:`_after_imports_changed`.

        Cardinality is enforced from :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — invalid.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR require exactly
          one) — invalid; change that import with :meth:`retry_import` instead.
        * Otherwise the removal is rejected when it would drop the count of *real*
          imports (non-empty, non-``"#"`` href) below the model's minimum. Removing
          an empty placeholder is always allowed for variable-cardinality models.

        Args:
            href: Any href that identifies the import — its literal href (including
                a ``"#uuid"`` fragment or an empty ``""``/``"#"`` placeholder), or
                the resolved target href of the back-matter resource it references.

        Returns:
            True if an import was found and removed; False if not found, the
            cardinality forbids removal, or the content is read-only.

def resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'

Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-ap/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.

def retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def set_metadata(self, content: 'dict' = {}) -> 'bool'

Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.

def supports(self, name: 'str') -> 'bool'

Return True if this model exposes ``name`` as a method or attribute.

        Lets callers look before they leap when an operation is only valid for
        some OSCAL models::

            if doc.supports("add_control"):
                doc.add_control(...)

        Only class-level (shared, model-defined) members count; per-instance
        attributes set at runtime are ignored, so the check reflects the model's
        capabilities rather than incidental state.

        Args:
            name (str, required): The method/attribute name to test.

        Returns:
            bool: True if the model defines ``name``.

property unresolved_imports

Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.

def update_import(self, *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None, new_resource: 'bool' = True) -> 'ImportResult'

Modify the single import of a one-import model (SSP, AP, AR, or POA&M).

        These models carry exactly one import (POA&M: at most one), so :meth:`add_import`
        and :meth:`remove_import` do not apply — this is how their import is changed. It
        takes the same resource fields as :meth:`update_resource` (``title``,
        ``description``, ``props``, ``rlinks``, ``remarks`` — ``None`` leaves a field
        unchanged; arrays replace wholesale) plus ``new_resource``. The behavior depends
        on what the existing import points at:

        * **No import yet** (only possible for POA&M): the call is forwarded to
          :meth:`add_import` (``new_resource`` does not apply and is omitted); the import
          target's href is taken from the first supplied ``rlink``.
        * **Import is a direct URI** (or an empty ``""``/``"#"`` placeholder): a new
          back-matter resource is created (via :meth:`append_resource`) — its ``rlink`` is
          the supplied ``rlinks`` when given, otherwise the existing URI — and the import's
          href is repointed to that resource's ``#uuid``. (``new_resource`` does not apply:
          there is no backing resource to update.) Status: "replaced".
        * **Import is a ``#uuid`` fragment**:

          - ``new_resource=True`` (default): a brand-new resource with a new UUID is
            created via :meth:`append_resource` from the supplied fields, and the import's
            href is repointed to it. The prior resource is **left in place** — it is not
            deleted, because other content may reference it (including OSCAL documents not
            currently loaded that import this one and cite that resource by UUID). Because
            the new resource is built only from the fields you pass, supply ``rlinks`` (and
            any props) for the new target; read the old resource first with
            :meth:`get_resource_by_uuid` if you want to carry values forward. Status:
            "replaced".
          - ``new_resource=False``: the existing resource is edited in place via
            :meth:`update_resource` (same wholesale array-replacement semantics and
            data-loss caveats — see that method). The import's href is unchanged. Status:
            "updated".

        Args:
            title (str | None, optional): Resource title; ``""`` removes it.
            description (str | None, optional): Resource description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). Also the source of the import target's
                href when creating a resource or bootstrapping a POA&M import.
            remarks (str | None, optional): Resource remarks (markdown); ``""`` removes it.
            new_resource (bool, optional): When the import already references a ``#uuid``
                resource, True (default) creates a new resource and repoints; False edits
                the existing resource in place. Ignored for the direct-URI/placeholder and
                no-import cases. Defaults to True.

        Returns:
            ImportResult: ``status`` "added"/"replaced"/"updated" on success (``ok`` True),
                or "invalid"/"error" otherwise, carrying the import ``entry`` and the
                created/updated ``resource``.

def update_resource(self, uuid: 'str', *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None) -> 'dict | None'

Update fields of an existing ``back-matter`` resource, selected by ``uuid``.

        Only a resource defined in THIS document's own ``back-matter`` is editable
        (imported resources are not). Each field is optional and independent:

        * ``None`` (the default) leaves that field untouched.
        * A scalar (``title``/``description``/``remarks``) replaces the current value;
          an empty string removes the field entirely.
        * An array (``props``/``rlinks``) **replaces the existing array wholesale** — the
          old list is discarded and the supplied list becomes the new one. Passing an
          empty list removes the field.

        .. warning::
            Array replacement is destructive and total, not a merge. Whatever you pass
            for ``props`` or ``rlinks`` becomes the *complete* new list; every entry not
            present in your list is permanently dropped. A resource frequently carries
            entries you did not author and may not be aware of — for example multiple
            ``rlinks`` pointing at format variants (``.xml``/``.json``) of the same file,
            ``rlinks`` bearing ``hashes`` for integrity, a ``base64`` payload, or ``props``
            added by other tools or pipelines. Supplying a partial list here silently
            deletes all of those. There is no undo.

            **Recommended pattern:** read the current resource first with
            :meth:`get_resource_by_uuid`, mutate the copy it returns (append to / edit the
            existing ``props``/``rlinks`` rather than rebuilding them from scratch), then
            pass those full arrays back to this method. That way you extend the resource
            instead of overwriting it, and nothing you did not intend to touch is lost::

                res = doc.get_resource_by_uuid(uuid)          # safe copy of the whole resource
                res["rlinks"].append({"href": "catalog.json",
                                      "media-type": "application/json"})
                doc.update_resource(uuid, rlinks=res["rlinks"])   # full list, nothing dropped

        Args:
            uuid (str, required): UUID of the local back-matter resource to update.
            title (str | None, optional): New title; ``""`` removes it. ``None`` = unchanged.
            description (str | None, optional): New description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts (see
                :func:`append_props`). ``[]`` removes all props. ``None`` = unchanged.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). ``[]`` removes all rlinks.
            remarks (str | None, optional): New remarks (markdown); ``""`` removes them.

        Returns:
            dict | None: A safe copy of the updated resource, or None when the content is
                read-only, ``uuid`` is empty, or no local resource with that UUID exists.

def validate(self, format: 'str' = '') -> 'bool'

Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).

def walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')

Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None

property xml

Return the content as an XML string in canonical element order.

        Rebuilds from the current dict via the metaschema converter (reflecting
        the latest edits, in schema-required element order) and retains no tree.

property yaml

Return the content as a YAML string in canonical key order.

class POAM

OSCAL Plan of Action and Milestones (POA&M) model.

    Represents tracked security findings and their planned remediation
    milestones. Subclasses ``OSCAL``.

classmethod acquire(cls, source: 'str | dict | OscalRef | list', *, cache: "'CacheDirective | None'" = None)

Acquire OSCAL content from one or more URI/reference sources.

        The sources are treated as an ordered fallback list; the first that
        resolves successfully is used.

        Args:
            source (str | dict | OscalRef | list, required): The reference(s) to
                acquire. May be a URI/path string, an ``OscalRef``, a reference dict
                containing at least ``"href"``, or a list mixing any of these.
            cache (CacheDirective | None, optional): Caching directive applied to
                remote fetches (e.g. ``CacheDirective.never()``,
                ``CacheDirective.refresh_now()``). Keyword-only. Defaults to the
                standard 24h behavior.

        Returns:
            OSCAL: A new instance populated from the first resolvable source.

def add_import(self, href: 'str', uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], version: 'str' = '', remarks: 'str' = '', *, include_all: 'bool' = False) -> 'ImportResult'

Add a first-level import to this document, backed by a back-matter resource.

        Uniform across every model; legality is governed by :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — ``invalid``.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR) — ``invalid``; set
          their single import with :meth:`retry_import` instead of add/remove.
        * Otherwise an import is added while the count of *real* imports is below the
          model's ``max`` (unbounded when ``max`` is None).

        The import references a back-matter ``resource`` by UUID fragment
        (``href="#<uuid>"``): if a resource whose ``rlink`` already targets ``href``
        exists it is reused, otherwise one is created via :meth:`append_resource` with
        which this method shares its resource parameters (``uuid``/``title``/
        ``description``/``props``/``remarks``). The ``href`` becomes the resource's
        single ``rlink`` (with a best-effort ``media-type`` inferred from it), and
        ``version`` is appended to ``props`` as a ``prop`` named ``"version"``. An empty
        placeholder import (href ``""``/``"#"``) is filled in place; otherwise the entry
        is appended (list models) or set (single-import models). After placement the
        import tree and any derived state are refreshed via :meth:`_after_imports_changed`.

        Args:
            href (str, required): Reference to the imported OSCAL file (XML/JSON/YAML);
                becomes the created resource's ``rlink`` href.
            uuid (str, optional): UUID for the created resource; generated when empty.
                Ignored when an existing resource is reused. Mirrors :meth:`append_resource`.
            title (str, optional): Title for the created resource.
            description (str, optional): Description for the created resource.
            props (list, optional): Property dicts for the created resource; ``version``
                (below) is appended to these. Mirrors :meth:`append_resource`.
            version (str, optional): Convenience — appended to ``props`` as a ``prop``
                named ``"version"`` (resources have no native version field).
            remarks (str, optional): Remarks (markdown) for the created resource.
            include_all (bool, optional): Keyword-only, profile-only. When True a new
                profile import selects all controls via ``include-all`` instead of the
                default empty ``include-controls``/``with-ids`` placeholder. Ignored by
                models whose imports carry no selection. Defaults to False.

        Returns:
            ImportResult: ``status`` of "added", "replaced", "duplicate", "invalid",
                or "error", with the relevant ``entry`` and ``resource``.

def append_child(self, path: 'str', child: 'dict') -> 'dict | None'

Appends a child dict to the list at the given JSON path.

        Path segments are '/' separated, relative to the model root.  The leaf
        segment names the list key; it is created as an empty list if absent.

        Args:
            path (str):  Slash-separated path to the target list relative to the
                         model root, e.g. "metadata/props" or "back-matter/resources".
            child (dict): Dict to append to the list.

        Returns:
            dict | None: The appended child on success, None on failure.

def append_resource(self, uuid: 'str' = '', title: 'str' = '', description: 'str' = '', props: 'list' = [], rlinks: 'list' = [], base64: 'str' = '', remarks: 'str' = '') -> 'dict | None'

Append a resource to the document's ``back-matter`` section.

        Args:
            uuid (str, optional): Resource UUID. A new UUID is generated when empty.
            title (str, optional): Resource title.
            description (str, optional): Resource description.
            props (list, optional): Property dicts to add.
            rlinks (list, optional): Resource-link (``rlink``) dicts to add.
            base64 (str, optional): Base64-encoded inline content.
            remarks (str, optional): Remarks prose (markdown).

        Returns:
            dict | None: The newly created resource dict, or None on failure.

def dump(self, filename: 'str' = '', format: 'str' = '', pretty_print: 'bool' = False) -> 'bool'

Write the current OSCAL content to a file.
        With no parameters, saves to the original location in the original format.
        This will save to any valid filename, even if the file extension does not match the format.
        Output keys/elements are emitted in canonical metaschema order (see :meth:`dumps`).

        Args:
            filename (str, optional): Path to write to. Defaults to the original
                source location when empty.
            format (str, optional): Output format — one of ``OSCAL_FORMATS``
                ("xml", "json", "yaml", "yml"). Defaults to the original format when empty.
            pretty_print (bool, optional): Whether to pretty-print the output.
                Defaults to False.

        Returns:
            bool: True if the write succeeded, False otherwise.

def dumps(self, format: 'str' = '', pretty_print: 'bool' = False) -> 'str'

Serialize the current content to a string in the specified format.

        Keys/elements are emitted in canonical NIST metaschema order: XML element
        order is schema-required and always canonical; JSON/YAML key order is
        canonical on a best-effort basis (see :meth:`_ordered_dict`).

        Parameters:
        - format (str): The target format for serialization ("xml", "json", or "yaml")
            Defaults to the original format of the content if not specified.
        - pretty_print (bool): Whether to pretty-print the output. Defaults to False.

        Returns:
        - str: The serialized content as a string.

property duplicate_imports

Return import_list entries detected as duplicates of an earlier import.

        Duplicates are non-blocking — they do NOT prevent imports_resolved from
        becoming True — but they remain available for the caller to act on via
        retry_import (supply a different source), ignore_import, or remove_import.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        duplicate entry holds no document of its own (the original READY entry carries
        it), so for every entry here ``object_uuid`` is always ``None`` and the six
        summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are always ``""``.

property failed_imports

Return import_list entries that failed, each carrying a populated 'failure' field.

        These are blocking: while any failed import remains, content_state stays
        at VALID and imports_resolved is False.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception: a
        failed import never acquires its document, so for every entry here ``object_uuid``
        is always ``None`` and the six summary fields (``model``, ``title``,
        ``oscal_version``, ``version``, ``published``, ``last_modified``) are always ``""``.

def find_in_import_tree(self, fragment_id: 'str', kinds=None, _seen=None) -> 'Optional[dict]'

Resolve an id/uuid by searching this document and its import tree.

        OSCAL cross-references (``href="#..."``) can point at content that lives in an
        imported document — a back-matter ``resource`` (by uuid), a metadata ``role`` (by
        id), ``party`` (by uuid), ``location`` (by uuid), or ``responsible-party`` (by
        role-id), or a ``control``/``group``/``param``/``part`` (by id). This walks
        ``self`` first, then each imported document depth-first (de-duplicated,
        cycle-safe), and returns the first match together with the document that owns it.

        Args:
            fragment_id (str, required): The bare id/uuid to resolve (no leading ``#``).
            kinds (Iterable[str] | None, optional): Restrict the search to these element
                kinds (subset of :attr:`_RESOLVE_KINDS`); ``None`` searches all.
            _seen (set | None, optional): Internal cycle-guard.

        Returns:
            Optional[dict]: ``{"element", "kind", "id", "object_uuid", "href"}`` — a safe
                copy of the found element, its kind, the owning document's root uuid and
                resolved href — or None when not found anywhere in the tree.

classmethod from_string(cls, content: 'str', *, href: 'str | None' = None)

Explicit constructor for in-memory OSCAL string content.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): URI identifying the source. Keyword-only.
                Defaults to None.

        Returns:
            OSCAL: A new instance (delegates to :meth:`loads`).

def get_location_by_uuid(self, location_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``location`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.locations`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a location with the given uuid
        is found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_oscal_object(self, uuid, _seen=None)

Return the LIVE imported OSCAL document whose root UUID matches ``uuid``.

        Searches this document and its resolved imports depth-first, de-duplicating
        objects shared across multiple import paths (the same large catalog reached
        two ways is visited once). This underpins the import mechanism's object reuse
        and is the companion to :attr:`import_tree`: the tree carries each node's
        ``object_uuid``; pass one here to obtain the corresponding live instance.

        Unlike the model getters, this returns the LIVE object (not a copy) — it is a
        document handle meant for working with that instance through its own methods.

        Args:
            uuid (str, required): The root UUID of the document to locate.
            _seen (set | None, optional): Object ids already visited; used internally
                for cycle-safety. Defaults to None.

        Returns:
            OSCAL | None: The matching live document, or None if not found.

def get_parameter_by_id(self, param_id: 'str', with_source: 'bool' = False) -> 'Optional[dict]'

Return a parameter defined anywhere in scope, or None.

        Searches this document and its import tree for a ``param`` with the given id —
        covering parameters defined at control, group, or catalog level (and reached
        through imported catalogs/profiles). Subclasses may override to prefer resolved
        content. See :meth:`_lookup_in_scope` for the ``with_source`` locator form.

def get_party_by_uuid(self, party_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``party`` (by uuid) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a party with the given uuid is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def get_resource_by_uuid(self, resource_uuid: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a back-matter resource defined anywhere in scope, or None.

        Looks in THIS document's ``back-matter`` first; on a local miss the search
        cascades out through the immediately imported documents and continues depth-first
        along every branch (de-duplicated and cycle-safe) until a ``resource`` whose
        ``uuid`` matches is found or every branch is exhausted. This lets a cross-reference
        (``href="#uuid"``) resolve even when the resource is defined in an imported
        document rather than locally. Subclasses may override to prefer resolved content.

        Args:
            resource_uuid (str, required): The bare resource UUID to resolve (no ``#``).
            with_source (bool, optional): Return the full locator (element + owning
                ``object_uuid``/``href``) instead of the bare resource; useful for
                resolving the resource's relative rlink hrefs. Defaults to False.
            local_only (bool, optional): Search only THIS document, never imports.
                Defaults to False.

        Returns:
            Optional[dict]: A safe copy of the matching resource (or locator), or None.

def get_responsible_party_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``responsible-party`` (by role-id) in scope, or None.

        A ``responsible-party`` is keyed by the ``role-id`` it fulfills. Looks in THIS
        document's ``metadata.responsible-parties`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until one with the given role-id is
        found or every branch is exhausted. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

        Note: this targets metadata-level ``responsible-parties`` only. The
        ``responsible-role`` assemblies embedded throughout implementation/assessment
        models are model-specific and handled by a separate, later cascade.

def get_role_by_id(self, role_id: 'str', with_source: 'bool' = False, local_only: 'bool' = False) -> 'Optional[dict]'

Return a metadata ``role`` (by id) defined anywhere in scope, or None.

        Looks in THIS document's ``metadata.roles`` first, then (unless ``local_only``)
        cascades depth-first through the import tree until a role with the given id is
        found or every branch is exhausted — so a ``role-id`` reference resolves even when
        the role is defined in an imported document. See :meth:`_lookup_in_scope` for
        ``with_source`` and ``local_only``.

def ignore_import(self, href: 'str') -> 'bool'

Mark an import as intentionally ignored.

        The entry remains in import_list with status IGNORED.  Like DUPLICATE,
        IGNORED entries are treated as non-blocking: once all remaining entries
        are READY, DUPLICATE, or IGNORED, content_state advances to
        IMPORTS_RESOLVED.

        Typical use: the caller presents a DUPLICATE entry to the user and the
        user explicitly chooses to ignore it rather than supply a replacement.

        The same priority ordering used by retry_import applies when multiple
        entries share the same href — DUPLICATE and INVALID are preferred over
        READY.

        Args:
            href: Any href that identifies the entry (href_original, href_valid,
                  failure.uri, or an href_list item href).

        Returns:
            True if an entry was found and updated, False if no match was found.

property import_tree

Recursive import tree built lazily on first access and cached.

        Returns a root node dict representing this document, with an ``imports`` key
        holding the first-level imports; each import is a node of the same shape,
        recursively. The tree is a SAFE COPY of the cached structure — mutating it does
        not affect the cache; use :meth:`rebuild_import_tree` to force a fresh traversal.

        The tree carries no live OSCAL objects, so it stays small and safe to
        serialize/transmit. Instead, each node identifies its document by UUID and
        summary metadata; call :meth:`get_oscal_object` with ``object_uuid`` to obtain
        the live instance when one is actually needed.

        Each node (root and every import) has these keys:

        * ``href_original`` (str): the import href as written in the source document.
        * ``href_valid`` (str): the resolved href actually loaded, if any.
        * ``href_list`` (list[dict]): every href attempted, with per-attempt status.
        * ``status`` (ImportState): READY, INVALID, DUPLICATE, or IGNORED.
        * ``is_valid`` / ``is_local`` / ``is_remote`` / ``is_cached`` (bool): provenance.
        * ``object_uuid`` (str | None): the imported document's root UUID, or None when
          it was not acquired. Pass to :meth:`get_oscal_object` for the live object.
        * ``model`` (str): the imported document's model type (e.g. "catalog").
        * ``title`` (str): the imported document's metadata title.
        * ``oscal_version`` (str): the OSCAL version (no ``v`` prefix, e.g. "1.1.3").
        * ``version`` (str): the document's own metadata version.
        * ``published`` (str): the metadata publication timestamp (RFC-3339), if present.
        * ``last_modified`` (str): the metadata last-modified timestamp (RFC-3339).
        * ``failure`` (ImportFailure | None): the failure record when ``status`` is
          INVALID, else None.
        * ``imports`` (list[dict]): child import nodes (empty when none or unacquired).

        The six summary fields (``model``, ``title``, ``oscal_version``, ``version``,
        ``published``, ``last_modified``) are populated only when the object was
        successfully acquired; otherwise each is an empty string ``""``.

        This is the single source of truth for the node/entry field schema. The import
        getters :attr:`failed_imports`, :attr:`duplicate_imports`, and
        :attr:`unresolved_imports` return these same per-entry fields as flat lists
        (without the recursive ``imports`` key). Note that those getters only ever hold
        failed or duplicate entries, which carry no loaded document — so in their results
        ``object_uuid`` is always ``None`` and the six summary fields are always ``""``.

property imports_resolved

bool: True when all imports resolved (``content_state >= IMPORTS_RESOLVED``).

def initial_validation(self, content: 'str') -> 'bool'

Perform initial validation of content and advance the content state.

        Detects the format, checks that the content is a recognized, well-formed
        OSCAL format (XML, JSON, or YAML), identifies the model/version and extracts
        summary metadata, then invokes full OSCAL schema validation. Updates
        ``self.content_state`` progressively as each stage passes.

        Args:
            content (str, required): The raw OSCAL content to validate.

        Returns:
            bool: True if initial validation is successful, False otherwise.

property is_acquired

bool: True once content has been acquired (``content_state >= ACQUIRED``).

property is_cache_expired

True when remote cached content has exceeded its TTL.

property is_editable

Can this content be modified?

property is_fresh

True when content is local or cached and within its TTL.

property is_read_only

bool: True when the content may not be mutated (most-restrictive-wins).

        Read-only when any of these hold: the underlying writable flag is set,
        the content is canonical/published (``is_canonical``), or the document is
        write-locked by a *different* actor in its workspace (see
        :meth:`_locked_by_other`). Because every mutation gate checks this property,
        canonical status and workspace locks are enforced uniformly.

property is_remote

bool: True when the content originates from a remote source (not a local file).

property is_stale

True when remote cached content has exceeded its TTL.

property is_valid

bool: True when content passes OSCAL validation (``content_state >= VALID``).

property is_well_formed

bool: True when content is well-formed (``content_state >= WELL_FORMED``).

property json

Return the content as a JSON string in canonical key order.

def json_query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using JSON key name syntax (via :class:`NativePath`).

        Steps use the actual JSON key names (``controls``, ``props``, ``parts``, …)
        with no metaschema translation required.  Arrays are iterated
        transparently, so ``//controls[id='ac-2.2']`` navigates directly into
        any ``controls`` array at any depth.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits.

        Parameters
        ----------
        path : str
            Path expression using JSON key names, e.g.
            ``"//controls[id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def json_query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`json_query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using JSON key names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

classmethod load(cls, source: 'str | os.PathLike | _ReadableSource', *, href: 'str | None' = None)

Initialize an instance from a local file path or file-like object.

        Aligns with Python's conventional ``load(...)`` behavior (cf. ``json.load`` /
        ``pickle.load``): the source is a **local** path or a file-like object. Use
        ``loads(...)`` for in-memory strings/dicts, and ``acquire(...)`` for URI/reference
        sources (``http``/``https``/``file``/``ftp``/…) — ``load`` does not fetch remotely.

        Args:
            source (str | os.PathLike | file-like, required): A filesystem path or an
                object with a ``read()`` method.
            href (str | None, optional): URI label identifying the source; defaults to
                the path or the object's ``name``. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the loaded content.

        Raises:
            TypeError: If ``source`` is neither path-like nor file-like.

classmethod loads(cls, content: 'str | dict', *, href: 'str | None' = None)

Initialize an instance from in-memory OSCAL content.

        Args:
            content (str | dict, required): OSCAL content already in memory, as a
                serialized string or a dict.
            href (str | None, optional): URI identifying the original content
                source. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance populated from the content.

classmethod new(cls, title: 'str', version: 'str' = '', published: 'str' = '')

Create a new OSCAL document from a template.

        Must be called on a specific model class (``Catalog.new()``,
        ``Profile.new()``, etc.), not on ``OSCAL`` directly.

        Args:
            title (str, required): Document title (stored in metadata).
            version (str, optional): Document version (stored in metadata).
                Defaults to "".
            published (str, optional): Publication date (stored in metadata).
                Defaults to "".

        Returns:
            OSCAL: A new editable instance of the model subclass.

        Raises:
            TypeError: If called on the ``OSCAL`` base class instead of a subclass.

classmethod open(cls, source: 'str | os.PathLike | dict | OscalRef | list | _ReadableSource', *, href: 'str | None' = None)

Universal constructor — inspects the source type and delegates to
        the appropriate loader.

        Delegates to:
            load()    — file-like objects (anything with .read()), PathLike
                        objects, and bare string paths (no URI scheme)
            acquire() — URI strings (http/https/file/ftp/...), OscalRef,
                        reference dicts, and fallback lists

        Args:
            source (str | os.PathLike | dict | OscalRef | list | file-like, required):
                Any supported OSCAL source. String values with a URI scheme are
                acquired; bare paths and file-like objects are loaded.
            href (str | None, optional): URI label passed through to ``load()`` when
                applicable. Keyword-only. Defaults to None.

        Returns:
            OSCAL: A new instance from the appropriate loader.

property origin_state

Computed from is_local, is_cached, and TTL. Changes over time for cached remote content.

def put(self, path: 'str', value, mode: "Literal['replace', 'insert']" = 'replace', *, validate: 'bool' = False, check_refs: 'bool' = False) -> 'bool'

Write a value into the JSON content at a slash-separated path.

        This is the shared, guarded entry point for JSON mutations. It centralizes the
        defensive behavior that would otherwise be repeated at every call site:
        the read-only / content guard (:meth:`_can_mutate`), auto-creation of missing
        intermediate objects and optional OSCAL arrays, and dirty-state bookkeeping
        (``is_unsaved`` / ``last_modified``).

        Path segments are ``'/'`` separated and relative to the model root (e.g.
        ``"metadata/title"`` or ``"metadata/roles/0/title"``). A numeric segment
        indexes a list; any other segment names a dict key. Missing intermediate dict
        keys are created automatically.

        Args:
            path (str, required): Slash-separated path relative to the model root.
            value (Any, required): The JSON-compatible value to write.
            mode (str, optional): ``"replace"`` (default) sets the value at ``path``;
                ``"insert"`` treats the leaf as an optional array — creating it if
                absent — and appends ``value`` to it.
            validate (bool, optional): When True, run metaschema-driven datatype/regex
                and allowed-value checks before writing (see :meth:`_validate_write`).
                Currently a permissive extension point. Defaults to False.
            check_refs (bool, optional): When True, run referential-integrity checks
                before writing (see :meth:`_check_referential_integrity`). Currently a
                permissive extension point. Defaults to False.

        Returns:
            bool: True on success, False on any failure (guard, bad path/index,
                validation, or type mismatch). No mutation occurs on failure.

def query(self, path: 'str', context: 'dict | None' = None) -> 'list'

Query the JSON content using XML element name syntax (via :class:`OSCALPath`).

        Steps use OSCAL XML element names (``control``, ``prop``, ``part``, …)
        and the metaschema index translates them to the correct JSON keys
        (``controls``, ``props``, ``parts``, …) including array/BY_KEY grouping.

        The returned list contains SAFE COPIES — mutating a result does not change the
        document; use the model's mutation methods for persistent edits. A single deep
        copy of the whole result set preserves internal identity between overlapping
        matches.

        Parameters
        ----------
        path : str
            Path expression using XML element names, e.g.
            ``"//control[@id='ac-2.2']"`` or ``"/*/metadata/title"``.
        context : dict, optional
            Sub-dict to query within.  Defaults to the full document dict
            (``self._dict``).

        Returns a list of matching JSON values (as copies), or ``[]`` on error / no match.

def query_one(self, path: 'str', context: 'dict | None' = None, default=None)

Return the first result of :meth:`query` as a safe copy, or ``default``.

        Args:
            path (str, required): Path expression using OSCAL XML element names.
            context (dict | None, optional): Sub-dict to query within. Defaults to the
                full document dict.
            default (Any, optional): Value to return when there is no match. Returned
                as-is (not copied). Defaults to None.

        Returns:
            Any: A safe copy of the first matching JSON value, or ``default``.

def reachable_ids(self, _seen=None) -> 'set'

Return every ``id``/``uuid`` value in this document and its import tree.

        Used to decide whether a cross-reference resolves somewhere in scope. The walk
        is de-duplicated and cycle-safe across the import graph.

def rebuild_import_tree(self) -> 'dict'

Discard the cached import tree and rebuild it from the current import_list.

        Returns:
            dict: The freshly built root node of the recursive import tree.

def remove_import(self, href: 'str') -> 'bool'

Remove a first-level import statement from this document.

        Operates only on this document's own imports, never on descendants. The
        import *statement* is deleted from ``self._dict``; any back-matter resource
        it referenced via a URI fragment (``href="#uuid"``) is intentionally
        preserved. The affected part of the import tree is refreshed and any
        model-specific derived state (e.g. a Profile's resolved catalog) is reset
        via :meth:`_after_imports_changed`.

        Cardinality is enforced from :data:`_IMPORT_SPEC`:

        * Models with no top-level import (catalog, mapping-collection) — invalid.
        * Fixed-cardinality models where ``min == max`` (SSP/AP/AR require exactly
          one) — invalid; change that import with :meth:`retry_import` instead.
        * Otherwise the removal is rejected when it would drop the count of *real*
          imports (non-empty, non-``"#"`` href) below the model's minimum. Removing
          an empty placeholder is always allowed for variable-cardinality models.

        Args:
            href: Any href that identifies the import — its literal href (including
                a ``"#uuid"`` fragment or an empty ``""``/``"#"`` placeholder), or
                the resolved target href of the back-matter resource it references.

        Returns:
            True if an import was found and removed; False if not found, the
            cardinality forbids removal, or the content is read-only.

def resolve_imports(self, base_path: 'str' = '', *, cache_directive: "'CacheDirective | None'" = None) -> 'list'

Discover and load every OSCAL document referenced by this document's
        import declarations.  Populates (and returns) self.import_list.

        Because ``validate()`` resolves imports, loading a document cascades this
        depth-first down the whole import tree. Two guards prevent runaway on shared
        or circular graphs: the object registry ensures a file loaded via multiple
        branches (a diamond) is held once, and an import that resolves back to an
        ancestor still being resolved (a cycle) is marked ``ImportState.CYCLIC`` and
        not loaded — the ancestor stays valid and recursion stops there.

        A ``cache_directive`` is applied to this document's direct imports; a
        ``refresh`` or ``CACHE_NEVER`` directive bypasses the in-memory registry so
        the imported content is genuinely reloaded rather than reused.

        Recognised import locations by model:
            profile                    → import/@href
            component-definition       → import-component-definition/@href,
                                         component/control-implementation/@source,
                                         capability/control-implementation/@source
            system-security-plan       → import-profile/@href
            assessment-plan            → import-ssp/@href
            plan-of-action-and-milestones → import-ssp/@href
            assessment-results         → import-ap/@href
            mapping-collection         → mapping/source/@href,
                                         mapping/target/@href

        Args:
            base_path (str, optional): Directory used to resolve relative hrefs.
                Defaults to the directory of this document's own href.
            cache_directive (CacheDirective | None, optional): Caching directive
                applied to this document's direct import fetches. Keyword-only.
                Defaults to the standard 24h behavior.

        Returns:
            list[dict]: self.import_list, one entry per discovered reference.

def retry_import(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Retry a failed import identified by href, using a replacement source.

        The failed import is matched by href (original or previously resolved),
        then re-attempted using ``replacement_href`` (resolved relative to this
        document's location).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def retry_imports(self, failed_href: 'str', replacement_href: 'str') -> 'bool'

Compatibility alias for :meth:`retry_import` (plural method name).

        Args:
            failed_href (str, required): The href of the failed import to retry.
            replacement_href (str, required): The replacement href to attempt.

        Returns:
            bool: True if the import was successfully resolved on retry, False otherwise.

def set_metadata(self, content: 'dict' = {}) -> 'bool'

Set simple metadata fields on the OSCAL content's ``metadata`` section.

        Complex metadata collections (revisions, roles, parties, links, props, etc.)
        are not yet supported and are skipped with a warning.

        Args:
            content (dict, optional): Mapping of metadata field name to value to set.
                Defaults to an empty dict.

        Returns:
            bool: True on success, or None when the content cannot be mutated.

def supports(self, name: 'str') -> 'bool'

Return True if this model exposes ``name`` as a method or attribute.

        Lets callers look before they leap when an operation is only valid for
        some OSCAL models::

            if doc.supports("add_control"):
                doc.add_control(...)

        Only class-level (shared, model-defined) members count; per-instance
        attributes set at runtime are ignored, so the check reflects the model's
        capabilities rather than incidental state.

        Args:
            name (str, required): The method/attribute name to test.

        Returns:
            bool: True if the model defines ``name``.

property unresolved_imports

Return import_list entries that still warrant user attention.

        Includes failed imports (INVALID) and duplicates (DUPLICATE).  Excludes
        READY (resolved) and IGNORED (explicitly dismissed by the caller).

        This is the signal a UI should use to decide whether to keep showing
        import-resolution affordances.  It stays non-empty while there is still
        something the user can act on — even when ``imports_resolved`` is already
        True because the only remaining items are non-blocking duplicates.
        Once every entry is READY or IGNORED, this list is empty and the
        resolution UI can close.

        Each entry is a safe copy shaped like an :attr:`import_tree` node (same field
        schema, but a flat list without the recursive ``imports`` key). Exception:
        unresolved entries (failed or duplicate) never carry a loaded document, so for
        every entry here ``object_uuid`` is always ``None`` and the six summary fields
        (``model``, ``title``, ``oscal_version``, ``version``, ``published``,
        ``last_modified``) are always ``""``.

def update_import(self, *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None, new_resource: 'bool' = True) -> 'ImportResult'

Modify the single import of a one-import model (SSP, AP, AR, or POA&M).

        These models carry exactly one import (POA&M: at most one), so :meth:`add_import`
        and :meth:`remove_import` do not apply — this is how their import is changed. It
        takes the same resource fields as :meth:`update_resource` (``title``,
        ``description``, ``props``, ``rlinks``, ``remarks`` — ``None`` leaves a field
        unchanged; arrays replace wholesale) plus ``new_resource``. The behavior depends
        on what the existing import points at:

        * **No import yet** (only possible for POA&M): the call is forwarded to
          :meth:`add_import` (``new_resource`` does not apply and is omitted); the import
          target's href is taken from the first supplied ``rlink``.
        * **Import is a direct URI** (or an empty ``""``/``"#"`` placeholder): a new
          back-matter resource is created (via :meth:`append_resource`) — its ``rlink`` is
          the supplied ``rlinks`` when given, otherwise the existing URI — and the import's
          href is repointed to that resource's ``#uuid``. (``new_resource`` does not apply:
          there is no backing resource to update.) Status: "replaced".
        * **Import is a ``#uuid`` fragment**:

          - ``new_resource=True`` (default): a brand-new resource with a new UUID is
            created via :meth:`append_resource` from the supplied fields, and the import's
            href is repointed to it. The prior resource is **left in place** — it is not
            deleted, because other content may reference it (including OSCAL documents not
            currently loaded that import this one and cite that resource by UUID). Because
            the new resource is built only from the fields you pass, supply ``rlinks`` (and
            any props) for the new target; read the old resource first with
            :meth:`get_resource_by_uuid` if you want to carry values forward. Status:
            "replaced".
          - ``new_resource=False``: the existing resource is edited in place via
            :meth:`update_resource` (same wholesale array-replacement semantics and
            data-loss caveats — see that method). The import's href is unchanged. Status:
            "updated".

        Args:
            title (str | None, optional): Resource title; ``""`` removes it.
            description (str | None, optional): Resource description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). Also the source of the import target's
                href when creating a resource or bootstrapping a POA&M import.
            remarks (str | None, optional): Resource remarks (markdown); ``""`` removes it.
            new_resource (bool, optional): When the import already references a ``#uuid``
                resource, True (default) creates a new resource and repoints; False edits
                the existing resource in place. Ignored for the direct-URI/placeholder and
                no-import cases. Defaults to True.

        Returns:
            ImportResult: ``status`` "added"/"replaced"/"updated" on success (``ok`` True),
                or "invalid"/"error" otherwise, carrying the import ``entry`` and the
                created/updated ``resource``.

def update_resource(self, uuid: 'str', *, title: 'Optional[str]' = None, description: 'Optional[str]' = None, props: 'Optional[list]' = None, rlinks: 'Optional[list]' = None, remarks: 'Optional[str]' = None) -> 'dict | None'

Update fields of an existing ``back-matter`` resource, selected by ``uuid``.

        Only a resource defined in THIS document's own ``back-matter`` is editable
        (imported resources are not). Each field is optional and independent:

        * ``None`` (the default) leaves that field untouched.
        * A scalar (``title``/``description``/``remarks``) replaces the current value;
          an empty string removes the field entirely.
        * An array (``props``/``rlinks``) **replaces the existing array wholesale** — the
          old list is discarded and the supplied list becomes the new one. Passing an
          empty list removes the field.

        .. warning::
            Array replacement is destructive and total, not a merge. Whatever you pass
            for ``props`` or ``rlinks`` becomes the *complete* new list; every entry not
            present in your list is permanently dropped. A resource frequently carries
            entries you did not author and may not be aware of — for example multiple
            ``rlinks`` pointing at format variants (``.xml``/``.json``) of the same file,
            ``rlinks`` bearing ``hashes`` for integrity, a ``base64`` payload, or ``props``
            added by other tools or pipelines. Supplying a partial list here silently
            deletes all of those. There is no undo.

            **Recommended pattern:** read the current resource first with
            :meth:`get_resource_by_uuid`, mutate the copy it returns (append to / edit the
            existing ``props``/``rlinks`` rather than rebuilding them from scratch), then
            pass those full arrays back to this method. That way you extend the resource
            instead of overwriting it, and nothing you did not intend to touch is lost::

                res = doc.get_resource_by_uuid(uuid)          # safe copy of the whole resource
                res["rlinks"].append({"href": "catalog.json",
                                      "media-type": "application/json"})
                doc.update_resource(uuid, rlinks=res["rlinks"])   # full list, nothing dropped

        Args:
            uuid (str, required): UUID of the local back-matter resource to update.
            title (str | None, optional): New title; ``""`` removes it. ``None`` = unchanged.
            description (str | None, optional): New description; ``""`` removes it.
            props (list | None, optional): Replacement property dicts (see
                :func:`append_props`). ``[]`` removes all props. ``None`` = unchanged.
            rlinks (list | None, optional): Replacement ``rlink`` dicts (``href`` plus
                optional ``media-type``/``hashes``). ``[]`` removes all rlinks.
            remarks (str | None, optional): New remarks (markdown); ``""`` removes them.

        Returns:
            dict | None: A safe copy of the updated resource, or None when the content is
                read-only, ``uuid`` is empty, or no local resource with that UUID exists.

def validate(self, format: 'str' = '') -> 'bool'

Validate OSCAL content against the metaschema index in sequenced phases.

        Phases (each recorded in ``validation_status``):
          structure      – all required fields and hierarchy are present
          data-types     – every leaf value matches its declared OSCAL datatype
          allowed-values – every constrained value is within its enumerated set
          cardinality    – every array satisfies its min-occurs/max-occurs bounds
          choice         – every choice is mutually exclusive (at most one member present) and has a member when one is required

        ``validation_status["well-formed"]`` is set by ``initial_validation()``, not here.
        All phases always run regardless of earlier failures, giving a complete picture
        of issues in a single call.  The format argument is accepted for API
        compatibility but does not alter the validation path — ``_dict`` is always the
        authoritative representation.

        Returns True only when every phase passes (content_state reaches VALID).

def walk_imports(self, visitor_fn, depth=0, _seen=None, *, scope='successful')

Walk the import tree depth-first, calling ``visitor_fn(entry, depth)`` for each entry.

        Args:
            visitor_fn (Callable, required): Callable receiving ``(entry_dict, depth_int)``.
            depth (int, optional): Current recursion depth; used internally. Defaults to 0.
            _seen (set | None, optional): Object ids already visited; used internally to
                prevent cycles. Defaults to None.
            scope (str, optional): Keyword-only. Which entries to visit — "successful"
                (default) visits only READY imports and recurses into them; "failed" visits
                only INVALID/NOT_LOADED imports without recursion; "all" visits every entry,
                recursing only into READY imports.

        Returns:
            None

property xml

Return the content as an XML string in canonical element order.

        Rebuilds from the current dict via the metaschema converter (reflecting
        the latest edits, in schema-required element order) and retains no tree.

property yaml

Return the content as a YAML string in canonical key order.

module oscal.oscal_registry

oscal_registry — process-shared identity map for loaded OSCAL objects.

Ensures a given OSCAL document is held in memory once and reused across branches
of an import tree (and across separate resolves in the same process), so two
references to the same file share a single object instead of loading it twice.

Objects are keyed by a composite **content identity** — ``(root-uuid,
last-modified, published)`` — which treats the same content as identical
regardless of format or location, with a **canonicalized href** as a pre-fetch
fast path. Values are held via weak references (``WeakValueDictionary``), so an
object stays registered only while some importer still holds it and is dropped
automatically once no longer referenced.

The default registry is a process-global singleton (``get_registry()``). The
``ObjectRegistry`` class is injectable so a future Workspace/session can own an
isolated instance.

Module constants:
    (none exported)

class ObjectRegistry

An identity map of loaded OSCAL objects, keyed by content identity and href.

    Lookups check the canonical href first (cheap, pre-fetch), then the composite
    content-identity key. Stale entries — objects whose own TTL has expired
    (``is_cache_expired``) — are treated as misses and dropped so the caller
    reloads. Thread-safe via an internal lock.

def __init__(self) -> None

Initialize an empty registry (weak identity/href maps and a resolution stack).

def alias_href(self, href: str, obj: Any) -> None

Point an additional canonical href at an already-registered object.

        Args:
            href (str, required): The canonical href to alias.
            obj (Any, required): The object the href should resolve to.

def clear(self) -> None

Drop all entries (primarily for test isolation).

def enter_resolving(self, href: str) -> None

Mark a canonical href as currently being resolved (push onto the DFS stack).

def exit_resolving(self, href: str) -> None

Unmark a canonical href once its resolution completes (pop from the stack).

def forget(self, obj: Any) -> None

Remove every reference to ``obj`` from the registry (thread-safe).

        Used to drop a document that was only transiently registered (e.g. a root
        registered by identity key for the duration of its import resolution).

def get(self, *, key: Optional[tuple] = None, href: str = '') -> Optional[Any]

Return a live, fresh object matching ``href`` (checked first) or ``key``.

        Args:
            key (tuple | None, optional): Composite content-identity key.
            href (str, optional): Canonicalized href.

        Returns:
            Any | None: The registered object, or None on miss or when the match is
                stale (its ``is_cache_expired`` is True), in which case it is dropped.

def is_resolving(self, href: str) -> bool

Return True when ``href`` is an ancestor currently being resolved (a cycle).

def register(self, obj: Any, *, key: Optional[tuple] = None, href: str = '') -> Any

Register ``obj`` under its content-identity key and/or canonical href.

        Args:
            obj (Any, required): The object to register.
            key (tuple | None, optional): Composite content-identity key.
            href (str, optional): Canonicalized href.

        Returns:
            Any: The registered object (``obj``).

def get_registry() -> oscal.oscal_registry.ObjectRegistry

Return the currently active object registry.

    Returns the registry activated by :func:`use_registry` (e.g. a Workspace's own
    registry) when one is in effect on the current context, otherwise the
    process-global default. Because a document load cascades synchronously, every
    object created during the load picks up whichever registry is active.

    Returns:
        ObjectRegistry: The active registry, or the process-global default.

def use_registry(registry: oscal.oscal_registry.ObjectRegistry)

Activate ``registry`` for the duration of the ``with`` block.

    Objects created while this context is active (including transitively-loaded
    imports) use ``registry`` instead of the process-global default.

    Args:
        registry (ObjectRegistry, required): The registry to activate.

    Yields:
        ObjectRegistry: The activated registry.

module oscal.oscal_cache

oscal_cache — on-disk cache of remote OSCAL content.

Provides a persistent, cross-session cache of content fetched from remote URLs so
the same remote document is not downloaded repeatedly. It reuses the shared
``filecache`` file-store schema (the same table the support database uses) in a
separate ``local_cache.db`` located alongside the support database. The database
is created lazily on first use, not at startup.

Cached content is keyed by its (canonicalized) remote URL via the ``filecache``
``original_location`` column, with the fetch time stored in ``acquired``; an entry
is served only while it is within ``LOCAL_CACHE_TTL`` seconds of that time,
otherwise it is refetched and the entry refreshed.

This complements the in-memory object registry (``oscal_registry``): the registry
avoids re-loading/parsing a live object, while this cache avoids the network round
trip across process runs.

Caching is controlled per fetch by a :class:`CacheDirective`. The directive is
applied first, then the fetch is evaluated for local reuse vs. refresh. Because
the directive's TTL is compared against the entry's last-fetch time, changing the
TTL re-evaluates freshness against that time (e.g. an entry fetched 6h ago is
still fresh under a new 12h TTL). ``CACHE_NEVER`` purges any copy and always
fetches remotely; ``CACHE_FOREVER`` reuses a copy of any age; ``refresh`` forces a
refetch now.

Module constants:
    LOCAL_CACHE_TTL (int): Default seconds a cached item stays fresh (86400 = 24h).
    CACHE_FOREVER (int): TTL sentinel — never expires (reuse a copy of any age).
    CACHE_NEVER (int): TTL sentinel — do not cache (purge and always fetch remotely).
    LOCAL_CACHE_FILENAME (str): Filename of the cache database ("local_cache.db").

class CacheDirective

A per-fetch instruction for how the remote-content cache should behave.

    The directive is applied first, then the fetch is evaluated: the (possibly
    overridden) TTL is compared against the cached entry's last-fetch time to decide
    whether the local copy is reused or the content is refetched.

    Attributes:
        ttl (int): Freshness window in seconds, or a sentinel — ``CACHE_FOREVER``
            (reuse a copy of any age) or ``CACHE_NEVER`` (purge and always fetch).
            Defaults to ``LOCAL_CACHE_TTL`` (24h).
        refresh (bool): When True, force a refetch now regardless of freshness
            (the refreshed content replaces the cached copy). Defaults to False.

classmethod default(cls) -> 'CacheDirective'

Default behavior: 24h TTL, no forced refresh.

        Returns:
            CacheDirective: A directive with the default TTL and no refresh.

classmethod forever(cls) -> 'CacheDirective'

Keep the cached copy until manually purged or refreshed.

        Returns:
            CacheDirective: A directive with ``ttl=CACHE_FOREVER``.

classmethod never(cls) -> 'CacheDirective'

Never cache: purge any existing copy and always fetch remotely.

        Returns:
            CacheDirective: A directive with ``ttl=CACHE_NEVER``.

classmethod of(cls, seconds: int) -> 'CacheDirective'

Cache with a specific TTL.

        Args:
            seconds (int, required): Freshness window in seconds.

        Returns:
            CacheDirective: A directive with ``ttl=seconds``.

classmethod refresh_now(cls, ttl: int = 86400) -> 'CacheDirective'

Force a refetch now, then cache the result.

        Args:
            ttl (int, optional): TTL to apply to the refreshed copy. Defaults to
                ``LOCAL_CACHE_TTL`` (24h).

        Returns:
            CacheDirective: A directive with ``refresh=True`` and the given ``ttl``.

class LocalCache

Persistent cache of remote content, backed by a ``filecache`` table.

    The backing ``local_cache.db`` is opened/created lazily on first access. Entries
    are keyed by remote URL and expire ``LOCAL_CACHE_TTL`` seconds after they were
    fetched.

def __init__(self, db_path: str = '') -> None

Initialize the cache.

        Args:
            db_path (str, optional): Explicit path to the cache database. When empty,
                the path is resolved lazily to ``local_cache.db`` beside the support
                database.

def clear(self) -> None

Remove all cached entries (primarily for maintenance/tests).

def get(self, url: str, directive: Optional[oscal.oscal_cache.CacheDirective] = None) -> Optional[str]

Apply ``directive``, then return cached content for ``url`` if reusable.

        The directive is applied first: ``CACHE_NEVER`` purges any copy; ``refresh``
        forces a miss. Freshness is then evaluated by comparing the directive's TTL
        against the entry's last-fetch time (``CACHE_FOREVER`` reuses any age).

        Args:
            url (str, required): The (canonicalized) remote URL key.
            directive (CacheDirective | None, optional): Caching directive; defaults
                to :meth:`CacheDirective.default` (24h, no refresh).

        Returns:
            Optional[str]: The cached content to reuse, or None to fetch remotely.

def purge(self, url: str) -> None

Remove the cached entry for a single ``url`` (manual deletion).

        Args:
            url (str, required): The (canonicalized) remote URL key.

def put(self, url: str, content, directive: Optional[oscal.oscal_cache.CacheDirective] = None) -> bool

Store or refresh cached content for ``url``, resetting its last-fetch time.

        A ``CACHE_NEVER`` directive stores nothing (the content is used but not cached).

        Args:
            url (str, required): The (canonicalized) remote URL key.
            content (str | bytes, required): The fetched content to cache.
            directive (CacheDirective | None, optional): Caching directive; defaults
                to :meth:`CacheDirective.default`.

        Returns:
            bool: True when stored, False when skipped or on error.

def get_local_cache() -> oscal.oscal_cache.LocalCache

Return the process-global default remote-content cache.

    Returns:
        LocalCache: The shared cache instance (its database is created on first use).

module oscal.oscal_workspace

oscal_workspace — a Workspace that owns a set of related OSCAL documents.

A ``Workspace`` is the entry point for opening/creating OSCAL content as a project.
It owns an isolated in-memory object registry (so two workspaces are independent
object graphs) and injects that registry into every document it loads — including
transitively-loaded imports — via :func:`oscal.oscal_registry.use_registry`.

Within one workspace, opening the same file twice returns the **same** object
(root documents are shared, keyed by their source path/href), which is the basis
for multi-view editing. The remote-content disk cache remains process-global
(shared across workspaces).

A workspace can be **saved to a single SQLite project file** (content + state,
reusing the shared ``filecache`` schema) and reloaded self-contained, without
refetching. The project file also carries project-level metadata (title, path,
last-modified, remarks, and an extensible attributes bag) and is the intended
substrate for future multi-view / multi-user (locking, sync) support.

Module constants:
    WORKSPACE_META_TABLE (dict): Schema for the ``workspace_meta`` key/value table.
    WORKSPACE_DOCS_TABLE (dict): Schema for the ``workspace_documents`` table.

class Workspace

A named set of related OSCAL documents with an isolated object registry.

    Documents opened through the workspace share one registry (imports dedup within
    the workspace) and one document identity map (opening the same source twice
    returns the same object). Carries project metadata and can be persisted to a
    single SQLite project file.

def __init__(self, title: str = '', path: str = '', registry: Optional[oscal.oscal_registry.ObjectRegistry] = None) -> None

Create a workspace.

        Args:
            title (str, optional): Project title.
            path (str, optional): Default path for the workspace's project file.
            registry (ObjectRegistry | None, optional): Registry to use; a fresh
                isolated one is created when omitted.

def as_actor(self, actor: str)

Context manager that attributes mutations in the block to ``actor``.

        Args:
            actor (str, required): The actor (view/session) id.

        Returns:
            A context manager activating ``actor`` as the current actor.

def close(self, doc: oscal.oscal_content.OSCAL) -> None

Stop tracking a document (releasing the workspace's strong reference and lock).

def close_all(self) -> None

Release all tracked documents and their locks.

property documents

list: The workspace's open root documents.

def is_locked(self, doc: oscal.oscal_content.OSCAL) -> bool

Return True when ``doc`` is write-locked by any actor.

classmethod load(cls, path: str) -> 'Workspace'

Load a workspace from its SQLite project file (self-contained; no refetch).

        Args:
            path (str, required): The workspace project file.

        Returns:
            Workspace: The reconstructed workspace, with documents rehydrated and
                their import trees rewired from the persisted content and state.

def loads(self, content: str, *, href: Optional[str] = None) -> oscal.oscal_content.OSCAL

Open in-memory content into the workspace.

        Args:
            content (str, required): Serialized OSCAL content.
            href (str | None, optional): Source URI to key/track the document by.

        Returns:
            OSCAL: The opened document.

def lock(self, doc: oscal.oscal_content.OSCAL, actor: Optional[str] = None) -> bool

Acquire the write lock on ``doc`` for ``actor`` (exclusive editing).

        While held, the document is read-only to every other actor. Re-locking by
        the same actor succeeds (idempotent).

        Args:
            doc (OSCAL, required): The document to lock.
            actor (str | None, optional): The actor; defaults to the current actor.

        Returns:
            bool: True if the lock is held by ``actor`` afterward, False if another
                actor already holds it.

        Raises:
            ValueError: When no actor is given and none is active.

def lock_holder(self, doc: oscal.oscal_content.OSCAL) -> Optional[str]

Return the actor holding the write lock on ``doc``, or None.

        Args:
            doc (OSCAL, required): The document.

        Returns:
            Optional[str]: The lock-holding actor, or None when unlocked.

def new(self, model_cls, title: str, **kwargs) -> oscal.oscal_content.OSCAL

Create a new document in the workspace.

        Args:
            model_cls (type, required): A model class (e.g. ``Catalog``).
            title (str, required): Document title.
            **kwargs: Passed through to ``model_cls.new``.

        Returns:
            OSCAL: The new document, tracked by the workspace.

def open(self, source) -> oscal.oscal_content.OSCAL

Open a document into the workspace (loading it under the workspace registry).

        Re-opening the same source returns the already-open document (shared root).

        Args:
            source (str, required): A path or URI to load.

        Returns:
            OSCAL: The (possibly already-open) document.

property registry

ObjectRegistry: This workspace's isolated object registry.

def save(self, path: str = '') -> bool

Save the workspace (content + state + project metadata) to a SQLite file.

        Every reachable document (roots and their resolved imports) is serialized as
        JSON into the shared ``filecache`` table, with its state and import edges
        recorded in ``workspace_documents``; project metadata goes in
        ``workspace_meta``. Reusing ``filecache`` means no schema change to the
        support database.

        Args:
            path (str, optional): Destination path. Defaults to ``self.path``.

        Returns:
            bool: True on success.

def unlock(self, doc: oscal.oscal_content.OSCAL, actor: Optional[str] = None) -> bool

Release the write lock on ``doc``.

        Args:
            doc (OSCAL, required): The document to unlock.
            actor (str | None, optional): The actor; defaults to the current actor.
                A caller may only release its own lock (unless ``actor`` is None-held).

        Returns:
            bool: True when the document is unlocked afterward; False when the lock
                is held by a different actor and cannot be released.

module oscal.metaschema_parser

metaschema_parser — parse NIST resolved-metaschema XML into a structural index.

Parses OSCAL resolved-metaschema XML files into a dictionary representation of the
metaschema structure (assemblies, fields, flags, attributes, child elements, and
allowed-value constraints). The resulting index drives XML↔JSON conversion and
validation elsewhere in the library.

While there is some defensive coding, this module assumes metaschema files are
valid; it does not validate metaschema structure or content. It ignores unexpected
structures and logs a WARNING when it encounters expected but unhandled structures.

Module constants:
    SUPPRESS_XPATH_NOT_FOUND_WARNINGS (bool): Suppress warnings when an XPath yields
        no match.
    RUNAWAY_LIMIT (int): Maximum recursion/iteration count before aborting as a
        runaway.
    DEBUG_OBJECT (str): Name of a definition to trace for debugging ("" disables).
    PRUNE_JSON (bool): Remove None values and empty arrays from the resolved JSON output.
    OSCAL_DEFAULT_NAMESPACE (str): The NIST OSCAL namespace URI.
    METASCHEMA_DEFAULT_NAMESPACE (str): The NIST Metaschema namespace URI.
    METASCHEMA_TOP_IGNNORE (list): Top-level metaschema elements to ignore.
    METASCHEMA_TOP_KEEP (list): Top-level metaschema elements to process.
    METASCHEMA_PROPS_HANDLED (list): Metaschema ``prop`` names handled on definitions.
    METASCHEMA_RULE_PROPS_HANDLED (list): Metaschema ``prop`` names handled on rules.
    METASCHEMA_INDEX_PROPS_HANDLED (list): Metaschema ``prop`` names handled on indexes.
    METASCHEMA_ROP_NAMESPACE (list): Recognized metaschema property namespace URIs.
    METASCHEMA_ROOT_ELEMENT (str): Root element name of a metaschema document
        ("METASCHEMA").
    CONSTRAINT_ROOT_ELEMENT (str): Root element name of a meta-constraints document.
    CONSTRAINT_TOP_IGNORE (list): Top-level constraint elements to ignore.
    CONSTRAINT_TOP_KEEP (list): Top-level constraint elements to process.
    GREEN, BLUE, YELLOW, RED, ORANGE, MAGENTA, CYAN, PURPLE, BOLD, RESET (str):
        ANSI terminal escape codes used for colorized diagnostic output.

class MetaschemaParser

Parses a single OSCAL resolved-metaschema XML document into a structural index.

    Holds the parsed metaschema tree and namespace/model context, resolves imported
    metaschemas, and walks assemblies, fields, and flags to build the nested index
    (nodes, attributes, allowed-value constraints) consumed by the converter and
    validator. Prefer the :meth:`create` classmethod to construct instances.

def __init__(self, metaschema, support, oscal_version='', import_registry=None)

Initialize a parser for one metaschema document.

        Args:
            metaschema (str, required): The resolved-metaschema XML content to parse.
            support (OSCALSupport, required): The OSCAL support object used to fetch
                imported metaschemas and store results.
            oscal_version (str, optional): The OSCAL version this metaschema belongs
                to. Defaults to "".
            import_registry (dict, optional): Shared registry of already-parsed imports
                (``href -> MetaschemaParser``), so a metaschema imported by more than one
                model is parsed once and reused. Endures across all models of one OSCAL
                version and is cleared when that version finishes. Defaults to ``None``
                (a fresh, isolated registry) — never a mutable default.

def build_metaschema_tree(self)

Build the full structural index for this metaschema's model.

        Recursively walks the root assembly to produce the node tree, applies
        constraints against a synthesized XML skeleton, prunes empty values, and
        annotates namespace conditions and JSON paths.

        Returns:
            dict: The metaschema index — model metadata plus a ``nodes`` tree — or an
                empty dict on error or when the root assembly cannot be found.

classmethod create(cls, metaschema, support, oscal_version='', import_registry=None)

Construct a ``MetaschemaParser`` (preferred factory over direct instantiation).

        Args:
            metaschema (str, required): The resolved-metaschema XML content to parse.
            support (OSCALSupport, required): The OSCAL support object.
            oscal_version (str, optional): The OSCAL version. Defaults to "".
            import_registry (dict, optional): Shared per-version import registry (see
                :meth:`__init__`). Defaults to ``None`` — a fresh registry per top-level
                parse. Recursive import parsing threads the parent's registry down.

        Returns:
            MetaschemaParser: A new parser instance.

def get_markup_content(self, xExpr, context=None)

Run an XPath query and return its markup content as a string.

        Handles results that are either plain strings or nodes containing HTML
        (markup) formatting, returning a string in either case.

        Args:
            xExpr (str, required): An XPath expression.
            context (Element, optional): Node to evaluate the expression against.
                Defaults to None (whole document).

        Returns:
            str: The matched content as a string (markup preserved as HTML).

def graceful_accumulate(self, current_value, xExpr, context=None)

Prepend a resolved markup value onto an accumulating list of values.

        Used where a field/assembly reference's values must be added to (rather than
        replace) any values already defined on the referenced define-field/assembly.

        Args:
            current_value (list, required): The existing accumulated values; wrapped in
                a list if not already one.
            xExpr (str, required): XPath expression yielding the markup value to add.
            context (Element, optional): Node to evaluate against. Defaults to None.

        Returns:
            list: ``current_value`` with the resolved value inserted at the front (when
                non-empty).

def graceful_override(self, current_value, xExpr, context=None)

Return an overriding value when present, otherwise keep the current value.

        Used where a field/assembly reference's value must replace any value already
        defined on the referenced define-field/assembly.

        Args:
            current_value (Any, required): The existing value to keep if no override
                is found.
            xExpr (str, required): XPath expression yielding the overriding value.
            context (Element, optional): Node to evaluate against. Defaults to None.

        Returns:
            Any: The resolved override value if non-empty, otherwise ``current_value``.

def handle_attributes(self, metaschema_node, definition_obj: 'ET.Element', structure_type, name, parent)

Map an XML definition's attributes onto a metaschema node.

        Translates attributes such as ``as-type`` (datatype), ``required``,
        ``min-occurs``/``max-occurs`` (cardinality), ``collapsible``, ``deprecated``,
        ``default``, and ``in-xml`` (XML wrapping) into node fields. Unhandled
        attributes are logged as warnings.

        Args:
            metaschema_node (dict, required): The node being built; updated in place.
            definition_obj (ET.Element, required): The XML definition element.
            structure_type (str, required): The definition's structure type.
            name (str, required): The definition name.
            parent (str, required): The parent path.

        Returns:
            dict: The updated ``metaschema_node``.

def handle_children(self, name, structure_type, metaschema_node, context, handle_choice=0)

Resolve the child model of an assembly (fields, assemblies, choices, any).

        Walks the assembly's ``model`` (or a specific ``choice`` group), recursing to
        build each child node and constructing synthetic nodes for ``choice``/``any``.

        Args:
            name (str, required): The definition name being processed.
            structure_type (str, required): "define-assembly" or "choice".
            metaschema_node (dict, required): The parent node (provides path/source).
            context (Element, required): The XML context to search within.
            handle_choice (int, optional): 1-based index of the choice group to process
                when ``structure_type`` is "choice". Defaults to 0.

        Returns:
            list: The resolved child node dicts.

def handle_constraints(self, metaschema_node, definition_obj, structure_type, name, parent)

Process ``<constraint><allowed-values>`` elements from a definition object.

        Targets are handled as follows: ``.`` or absent applies to the current node;
        ``@flag-name`` applies to the named flag child; complex Metapath targets are
        resolved against the XML skeleton (or stored with the unresolved target
        preserved). Multiple allowed-values sets for the same target are cumulative;
        ``allow-other`` conflicts resolve with 'yes' winning and emit a warning.

        Args:
            metaschema_node (dict, required): The node being built; updated in place.
            definition_obj (ET.Element, required): The XML definition element.
            structure_type (str, required): The definition's structure type.
            name (str, required): The definition name.
            parent (str, required): The parent path.

        Returns:
            dict: The updated ``metaschema_node``.

def handle_flags(self, metaschema_node, definition_obj, structure_type, name, parent)

Resolve the flags defined or referenced by a field or assembly.

        Finds each ``define-flag``/``flag`` child, recurses to build its node, and
        collects the results.

        Args:
            metaschema_node (dict, required): The parent node being built (used for
                path context).
            definition_obj (ET.Element, required): The field/assembly XML definition.
            structure_type (str, required): The parent's structure type.
            name (str, required): The parent definition name.
            parent (str, required): The parent path.

        Returns:
            list: The resolved flag node dicts (empty when none are present).

def handle_group_as(self, metaschema_node, definition_obj: 'ET.Element', structure_type, name, parent)

Apply a definition's ``group-as`` element to a metaschema node.

        Reads the ``group-as`` name and its ``in-xml``/``in-json`` grouping
        attributes and records them (and XML wrapping) on the node.

        Args:
            metaschema_node (dict, required): The node being built; updated in place.
            definition_obj (ET.Element, required): The XML definition element.
            structure_type (str, required): The definition's structure type.
            name (str, required): The definition name (for logging).
            parent (str, required): The parent path (used to build wrapped paths).

        Returns:
            dict: The updated ``metaschema_node``.

def handle_props(self, metaschema_node, definition_obj, structure_type, name, parent)

Map a definition's ``prop`` elements onto a metaschema node.

        Recognized props (``METASCHEMA_PROPS_HANDLED`` in the OSCAL namespace) are
        promoted to dedicated node keys; any other prop is appended to the node's
        ``props`` list as ``{"name", "value", "namespace"}``.

        Args:
            metaschema_node (dict, required): The node being built; updated in place.
            definition_obj (ET.Element, required): The XML definition element.
            structure_type (str, required): The definition's structure type.
            name (str, required): The definition name.
            parent (str, required): The parent path.

        Returns:
            dict: The updated ``metaschema_node``.

def initialize_metaschema_index(self)

Create a new, fully-keyed metaschema index-constraint dict with default values.

        Called before each index constraint is populated, to guarantee a consistent
        key set (id, level, name, target, handled props, etc.).

        Returns:
            dict: A new index dict with all expected keys initialized.

def initialize_metaschema_node(self)

Create a new, fully-keyed metaschema index node with default (empty) values.

        Called as each node is created, including the top-level node, to guarantee a
        consistent key set (path, name, datatype, cardinality, children, constraints,
        handled props, etc.).

        Returns:
            dict: A new node dict with all expected keys initialized.

def initialize_metaschema_rule(self)

Create a new, fully-keyed metaschema rule with default (empty) values.

        Called before each rule (e.g. an allowed-values constraint) is populated, to
        guarantee a consistent key set (id, level, datatype, allowed-values,
        allow-other, test, message, cardinality, etc.).

        Returns:
            dict: A new rule dict with all expected keys initialized.

def look_in_imports(self, name, structure_type, parent='', ignore_local=False, already_searched=None)

Search imported metaschemas for a definition by name and structure type.

        Args:
            name (str, required): The definition name to find.
            structure_type (str, required): The structure type to match
                (e.g. "define-assembly", "define-field", "define-flag").
            parent (str, optional): Parent path for the resolved node. Defaults to "".
            ignore_local (bool, optional): Passed through to recursion; ignore local
                definitions in the imported metaschema. Defaults to False.
            already_searched (list | None, optional): Definition names already visited,
                to prevent cycles. Defaults to None.

        Returns:
            dict | None: The resolved node from the imported metaschema, or None if not
                found.

def recurse_metaschema(self, name, structure_type='define-assembly', parent='', ignore_local=False, already_searched=None, context=None, skip_children=False, use_name=None)

Recursively build a metaschema index node and its descendants.

        Processes the XML definition for ``name`` and extracts a node dict describing
        its attributes, flags, and child elements, recursing into referenced
        definitions.

        Args:
            name (str, required): The definition/element name to process (e.g. a model
                or field name).
            structure_type (str, optional): The kind of definition — "define-assembly",
                "define-field", "define-flag", or an inline assembly/field/flag.
                Defaults to "define-assembly".
            parent (str, optional): Name of the parent definition, for logging/paths.
                Defaults to "".
            ignore_local (bool, optional): When True, ignore local (non-exported)
                definitions; set True when recursing into an imported metaschema so its
                private locals are not exposed. Defaults to False.
            already_searched (list | None, optional): Definition names already visited,
                to prevent infinite recursion. Defaults to None.
            context (Element, optional): XML context node to search within.
                Defaults to None.
            skip_children (bool, optional): When True, do not recurse into child
                elements. Defaults to False.
            use_name (str | None, optional): Override for the node's effective
                (use-)name. Defaults to None.

        Returns:
            dict: The metaschema index node for ``name`` (with nested children).

def set_default_values(self, metaschema_node, definition_obj, structure_type, name, parent)

Fill in default node values required by the metaschema specification.

        Applies spec defaults for any unset attributes — datatype ("string"),
        cardinality (0..1, or 1..1 for the root), ``json-collapsible``,
        ``deprecated``, ``default``, and XML wrapping for fields/assemblies.

        Args:
            metaschema_node (dict, required): The node being built; updated in place.
            definition_obj (ET.Element, required): The XML definition element.
            structure_type (str, required): The definition's structure type.
            name (str, required): The definition name.
            parent (str, required): The parent path; an empty value marks the root node.

        Returns:
            dict: The updated ``metaschema_node``.

def setup_imports(self)

Identify ``import`` elements and load each as a nested ``MetaschemaParser``.

        Imported metaschemas are fetched from the support database and stored in
        ``self.imports`` keyed by model name for later cross-metaschema lookups. Each
        import is also recorded as a child of this parser's ``import_inventory`` tree
        node, so the full import-relationship graph is captured.

        Duplicate imports (the same metaschema reached from more than one model, or via
        more than one path) are parsed only once: an :attr:`import_registry`, shared for
        the whole OSCAL version, is consulted first and its parsed object reused.

        Returns:
            None

def str_node(self, node)

Build a human-readable summary of a parsed metaschema index node.

        Args:
            node (dict, required): An index node produced by the parser, carrying
                keys such as ``formal-name``, ``use-name``, ``min-occurs``,
                ``max-occurs``, ``datatype``, ``children``, and ``constraints``.

        Returns:
            str: A multi-line, human-readable description of the node.

def top_pass(self)

Perform the first parsing pass: deserialize XML and read top-level metadata.

        Parses the metaschema content, then extracts the model name, schema name,
        OSCAL version, namespace, and JSON base URI, and sets up imports.

        Returns:
            bool: True if the XML was well-formed and parsed, False otherwise.

def xpath(self, xExpr, context=None) -> 'ET.Element | list[ET.Element] | None'

Run an XPath query and return the matching element(s).

        Args:
            xExpr (str, required): An XPath expression.
            context (Element, optional): Node to evaluate the expression against.
                When None, the expression runs against the whole document.
                Defaults to None.

        Returns:
            ET.Element | list[ET.Element] | None: A single element, a list of
                elements, or None on error / no match.

def xpath_atomic(self, xExpr, context=None)

Run an XPath query and return the first result as a string.

        Args:
            xExpr (str, required): An XPath expression.
            context (Element, optional): Node to evaluate the expression against.
                When None, the expression runs against the whole document.
                Defaults to None.

        Returns:
            str: The first matching result as a string, or "" on error / no match.

def clean_none_values_recursive(dictionary)

Recursively drop None values and empty containers from a dict.

    Removes key/value pairs whose value is None, and prunes empty nested dicts and
    lists (including dicts nested inside lists), returning a new cleaned dict.

    Args:
        dictionary (dict, required): The dictionary to clean.

    Returns:
        dict: A new dictionary with None values and empty containers removed.

def parse_metaschema(support=None, oscal_version=None, save_to_fs=False) -> 'int'

Parse and store the OSCAL metaschema index for one or all supported versions.

    Args:
        support (OSCALSupport, optional): The OSCAL support object. Currently the
            shared instance is fetched internally via ``get_support()`` regardless
            of this argument. Defaults to None.
        oscal_version (str, optional): The OSCAL version to parse. When None, all
            supported versions are processed. Defaults to None.
        save_to_fs (bool, optional): When True, also write each model index (and the
            parse report) to the local file system. Defaults to False (database only).

    Returns:
        int: 0 on success, 1 on error (process-style exit code).

def parse_metaschema_specific(support, oscal_version, save_to_fs=False)

Parse and store every model index for a specific OSCAL version.

    Each model index is stored in the support database as
    ``(version, model, "processed")``. When ``save_to_fs`` is True it is also written
    to ``support/<version>/<model>.json`` alongside the support database.

    Args:
        support (OSCALSupport, required): The OSCAL support object providing
            metaschema assets and asset storage.
        oscal_version (str, required): The OSCAL version to parse.
        save_to_fs (bool, optional): When True, also write each model index (and the
            parse report) to the local file system. Defaults to False (database only).

    Returns:
        bool: True if all models parsed and stored successfully, False otherwise.